mirror of
https://github.com/ejwa/gitinspector.git
synced 2025-03-26 02:01:27 +01:00
Converting space indentation back to tabs
This commit is contained in:
parent
7911fcf93e
commit
7b9d394791
30 changed files with 2216 additions and 2311 deletions
PipfilePipfile.lock
gitinspector
basedir.pyblame.pychanges.pyclone.pycomment.pyconfig.pyextensions.pyfiltering.pyformat.pygitinspector.pygravatar.pyhelp.pyinterval.pylocalization.pymetrics.pyoptval.py
output
blameoutput.pychangesoutput.pyextensionsoutput.pyfilteringoutput.pymetricsoutput.pyoutputable.pyresponsibilitiesoutput.pytimelineoutput.py
responsibilities.pyterminal.pytimeline.pyversion.py
2
Pipfile
2
Pipfile
|
@ -4,11 +4,11 @@ verify_ssl = true
|
|||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
black-but-with-tabs-instead-of-spaces = "*"
|
||||
|
||||
[dev-packages]
|
||||
pytest = "*"
|
||||
flake8 = "*"
|
||||
black = "*"
|
||||
twine = "*"
|
||||
coverage = "*"
|
||||
coveralls = "*"
|
||||
|
|
427
Pipfile.lock
generated
427
Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "eeaad7bc007adaa51ede465d1ccad2bf56d6ba3c6feef74d7218a91a2ceb4074"
|
||||
"sha256": "87e9949234210245765703c4d654f0f7205eec399e5d2acba50242218b858a45"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
|
@ -13,8 +13,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"default": {},
|
||||
"develop": {
|
||||
"default": {
|
||||
"appdirs": {
|
||||
"hashes": [
|
||||
"sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
|
||||
|
@ -27,20 +26,152 @@
|
|||
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
|
||||
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==21.2.0"
|
||||
},
|
||||
"black": {
|
||||
"black-but-with-tabs-instead-of-spaces": {
|
||||
"hashes": [
|
||||
"sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"
|
||||
"sha256:01b00ac677000874b86c6f22efc965ab2cc16645a27b86b01bac2fed68a5a12e",
|
||||
"sha256:bd5dd0842cef0a2c6714bd7381c8ead9106f68c64c64c706679a6a7fabb7ba48"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==20.8b1"
|
||||
"version": "==19.11"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a",
|
||||
"sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==8.0.1"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||
],
|
||||
"version": "==0.4.3"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd",
|
||||
"sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"
|
||||
],
|
||||
"version": "==0.8.1"
|
||||
},
|
||||
"regex": {
|
||||
"hashes": [
|
||||
"sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5",
|
||||
"sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79",
|
||||
"sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31",
|
||||
"sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500",
|
||||
"sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11",
|
||||
"sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14",
|
||||
"sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3",
|
||||
"sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439",
|
||||
"sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c",
|
||||
"sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82",
|
||||
"sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711",
|
||||
"sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093",
|
||||
"sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a",
|
||||
"sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb",
|
||||
"sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8",
|
||||
"sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17",
|
||||
"sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000",
|
||||
"sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d",
|
||||
"sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480",
|
||||
"sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc",
|
||||
"sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0",
|
||||
"sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9",
|
||||
"sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765",
|
||||
"sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e",
|
||||
"sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a",
|
||||
"sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07",
|
||||
"sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f",
|
||||
"sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac",
|
||||
"sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7",
|
||||
"sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed",
|
||||
"sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968",
|
||||
"sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7",
|
||||
"sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2",
|
||||
"sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4",
|
||||
"sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87",
|
||||
"sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8",
|
||||
"sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10",
|
||||
"sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29",
|
||||
"sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605",
|
||||
"sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6",
|
||||
"sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"
|
||||
],
|
||||
"version": "==2021.4.4"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.10.2"
|
||||
},
|
||||
"typed-ast": {
|
||||
"hashes": [
|
||||
"sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace",
|
||||
"sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff",
|
||||
"sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266",
|
||||
"sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528",
|
||||
"sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6",
|
||||
"sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808",
|
||||
"sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4",
|
||||
"sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363",
|
||||
"sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341",
|
||||
"sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04",
|
||||
"sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41",
|
||||
"sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e",
|
||||
"sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3",
|
||||
"sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899",
|
||||
"sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805",
|
||||
"sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c",
|
||||
"sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c",
|
||||
"sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39",
|
||||
"sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a",
|
||||
"sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3",
|
||||
"sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7",
|
||||
"sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f",
|
||||
"sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075",
|
||||
"sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0",
|
||||
"sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40",
|
||||
"sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428",
|
||||
"sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927",
|
||||
"sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3",
|
||||
"sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f",
|
||||
"sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"
|
||||
],
|
||||
"version": "==1.4.3"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
|
||||
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
|
||||
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
|
||||
],
|
||||
"version": "==3.10.0.0"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
|
||||
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==21.2.0"
|
||||
},
|
||||
"bleach": {
|
||||
"hashes": [
|
||||
"sha256:6123ddc1052673e52bab52cdc955bcb57a015264a1c57d37bea2f6b817af0125",
|
||||
"sha256:98b3170739e5e83dd9dc19633f074727ad848cbedb6026708c8ac2d3b697a433"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==3.3.0"
|
||||
},
|
||||
"certifi": {
|
||||
|
@ -50,79 +181,20 @@
|
|||
],
|
||||
"version": "==2021.5.30"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813",
|
||||
"sha256:04c468b622ed31d408fea2346bec5bbffba2cc44226302a0de1ade9f5ea3d373",
|
||||
"sha256:06d7cd1abac2ffd92e65c0609661866709b4b2d82dd15f611e602b9b188b0b69",
|
||||
"sha256:06db6321b7a68b2bd6df96d08a5adadc1fa0e8f419226e25b2a5fbf6ccc7350f",
|
||||
"sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06",
|
||||
"sha256:0f861a89e0043afec2a51fd177a567005847973be86f709bbb044d7f42fc4e05",
|
||||
"sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea",
|
||||
"sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee",
|
||||
"sha256:1bf1ac1984eaa7675ca8d5745a8cb87ef7abecb5592178406e55858d411eadc0",
|
||||
"sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396",
|
||||
"sha256:24a570cd11895b60829e941f2613a4f79df1a27344cbbb82164ef2e0116f09c7",
|
||||
"sha256:24ec4ff2c5c0c8f9c6b87d5bb53555bf267e1e6f70e52e5a9740d32861d36b6f",
|
||||
"sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73",
|
||||
"sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315",
|
||||
"sha256:293e7ea41280cb28c6fcaaa0b1aa1f533b8ce060b9e701d78511e1e6c4a1de76",
|
||||
"sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1",
|
||||
"sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49",
|
||||
"sha256:3c3f39fa737542161d8b0d680df2ec249334cd70a8f420f71c9304bd83c3cbed",
|
||||
"sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892",
|
||||
"sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482",
|
||||
"sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058",
|
||||
"sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5",
|
||||
"sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53",
|
||||
"sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045",
|
||||
"sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3",
|
||||
"sha256:681d07b0d1e3c462dd15585ef5e33cb021321588bebd910124ef4f4fb71aef55",
|
||||
"sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5",
|
||||
"sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e",
|
||||
"sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c",
|
||||
"sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369",
|
||||
"sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827",
|
||||
"sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053",
|
||||
"sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa",
|
||||
"sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4",
|
||||
"sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322",
|
||||
"sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132",
|
||||
"sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62",
|
||||
"sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa",
|
||||
"sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0",
|
||||
"sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396",
|
||||
"sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e",
|
||||
"sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991",
|
||||
"sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6",
|
||||
"sha256:cc5a8e069b9ebfa22e26d0e6b97d6f9781302fe7f4f2b8776c3e1daea35f1adc",
|
||||
"sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1",
|
||||
"sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406",
|
||||
"sha256:df5052c5d867c1ea0b311fb7c3cd28b19df469c056f7fdcfe88c7473aa63e333",
|
||||
"sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d",
|
||||
"sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"
|
||||
],
|
||||
"version": "==1.14.5"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
|
||||
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==4.0.0"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a",
|
||||
"sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"
|
||||
],
|
||||
"version": "==8.0.1"
|
||||
},
|
||||
"colorama": {
|
||||
"hashes": [
|
||||
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
|
||||
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==0.4.4"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -185,28 +257,11 @@
|
|||
},
|
||||
"coveralls": {
|
||||
"hashes": [
|
||||
"sha256:7bd173b3425733661ba3063c88f180127cc2b20e9740686f86d2622b31b41385",
|
||||
"sha256:cbb942ae5ef3d2b55388cb5b43e93a269544911535f1e750e1c656aef019ce60"
|
||||
"sha256:172fb79c5f61c6ede60554f2cac46deff6d64ee735991fb2124fb414e188bdb4",
|
||||
"sha256:9b3236e086627340bf2c95f89f757d093cbed43d17179d3f4fb568c347e7d29a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d",
|
||||
"sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959",
|
||||
"sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6",
|
||||
"sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873",
|
||||
"sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2",
|
||||
"sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713",
|
||||
"sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1",
|
||||
"sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177",
|
||||
"sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250",
|
||||
"sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca",
|
||||
"sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d",
|
||||
"sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"
|
||||
],
|
||||
"version": "==3.4.7"
|
||||
"version": "==3.1.0"
|
||||
},
|
||||
"docopt": {
|
||||
"hashes": [
|
||||
|
@ -219,29 +274,32 @@
|
|||
"sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125",
|
||||
"sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==0.17.1"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839",
|
||||
"sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"
|
||||
"sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b",
|
||||
"sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.8.4"
|
||||
"version": "==3.9.2"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
|
||||
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.10"
|
||||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:960d52ba7c21377c990412aca380bf3642d734c2eaab78a2c39319f67c6a5786",
|
||||
"sha256:e592faad8de1bda9fe920cf41e15261e7131bcf266c30306eec00e8e225c1dd5"
|
||||
"sha256:833b26fb89d5de469b24a390e9df088d4e52e4ba33b01dc5e0e4f41b81a16c00",
|
||||
"sha256:b142cc1dd1342f31ff04bb7d022492b09920cb64fed867cd3ea6f80fe3ebd139"
|
||||
],
|
||||
"version": "==4.4.0"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==4.5.0"
|
||||
},
|
||||
"iniconfig": {
|
||||
"hashes": [
|
||||
|
@ -250,19 +308,12 @@
|
|||
],
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"jeepney": {
|
||||
"hashes": [
|
||||
"sha256:7d59b6622675ca9e993a6bd38de845051d315f8b0c72cca3aef733a20b648657",
|
||||
"sha256:aec56c0eb1691a841795111e184e13cad504f7703b9a64f63020816afa79a8ae"
|
||||
],
|
||||
"markers": "sys_platform == 'linux'",
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"keyring": {
|
||||
"hashes": [
|
||||
"sha256:045703609dd3fccfcdb27da201684278823b72af515aedec1a8515719a038cb8",
|
||||
"sha256:8f607d7d1cc502c43a932a275a56fe47db50271904513a379d39df1af277ac48"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==23.0.1"
|
||||
},
|
||||
"mccabe": {
|
||||
|
@ -272,27 +323,14 @@
|
|||
],
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||
],
|
||||
"version": "==0.4.3"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
|
||||
"sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==20.9"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd",
|
||||
"sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"
|
||||
],
|
||||
"version": "==0.8.1"
|
||||
},
|
||||
"pkginfo": {
|
||||
"hashes": [
|
||||
"sha256:029a70cb45c6171c329dfc890cde0879f8c52d6f3922794796e06f577bb03db4",
|
||||
|
@ -302,60 +340,59 @@
|
|||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:265a94bf44ca13662f12fcd1b074c14d4b269a712f051b6f644ef7e705d6735f",
|
||||
"sha256:467f0219e89bb5061a8429c6fc5cf055fa3983a0e68e84a1d205046306b37d9e"
|
||||
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
||||
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
||||
],
|
||||
"version": "==1.0.0.dev0"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.13.1"
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
|
||||
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.10.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
|
||||
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
|
||||
"sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068",
|
||||
"sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"
|
||||
],
|
||||
"version": "==2.6.0"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
|
||||
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
|
||||
],
|
||||
"version": "==2.20"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.7.0"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
|
||||
"sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
|
||||
"sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3",
|
||||
"sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"
|
||||
],
|
||||
"version": "==2.2.0"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.3.1"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f",
|
||||
"sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==2.9.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:1c6409312ce2ce2997896af5756753778d5f1603666dba5587804f09ad82ed27",
|
||||
"sha256:f4896b4cc085a1f8f8ae53a1a90db5a86b3825ff73eb974dffee3d9e701007f4"
|
||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
||||
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
||||
],
|
||||
"version": "==3.0.0b2"
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.4.7"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9",
|
||||
"sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"
|
||||
"sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b",
|
||||
"sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.2.2"
|
||||
"version": "==6.2.4"
|
||||
},
|
||||
"readme-renderer": {
|
||||
"hashes": [
|
||||
|
@ -364,57 +401,12 @@
|
|||
],
|
||||
"version": "==29.0"
|
||||
},
|
||||
"regex": {
|
||||
"hashes": [
|
||||
"sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5",
|
||||
"sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79",
|
||||
"sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31",
|
||||
"sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500",
|
||||
"sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11",
|
||||
"sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14",
|
||||
"sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3",
|
||||
"sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439",
|
||||
"sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c",
|
||||
"sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82",
|
||||
"sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711",
|
||||
"sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093",
|
||||
"sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a",
|
||||
"sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb",
|
||||
"sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8",
|
||||
"sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17",
|
||||
"sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000",
|
||||
"sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d",
|
||||
"sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480",
|
||||
"sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc",
|
||||
"sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0",
|
||||
"sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9",
|
||||
"sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765",
|
||||
"sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e",
|
||||
"sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a",
|
||||
"sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07",
|
||||
"sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f",
|
||||
"sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac",
|
||||
"sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7",
|
||||
"sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed",
|
||||
"sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968",
|
||||
"sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7",
|
||||
"sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2",
|
||||
"sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4",
|
||||
"sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87",
|
||||
"sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8",
|
||||
"sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10",
|
||||
"sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29",
|
||||
"sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605",
|
||||
"sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6",
|
||||
"sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"
|
||||
],
|
||||
"version": "==2021.4.4"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
|
||||
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==2.25.1"
|
||||
},
|
||||
"requests-toolbelt": {
|
||||
|
@ -431,19 +423,12 @@
|
|||
],
|
||||
"version": "==1.5.0"
|
||||
},
|
||||
"secretstorage": {
|
||||
"hashes": [
|
||||
"sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f",
|
||||
"sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"
|
||||
],
|
||||
"markers": "sys_platform == 'linux'",
|
||||
"version": "==3.3.1"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"toml": {
|
||||
|
@ -451,72 +436,31 @@
|
|||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.10.2"
|
||||
},
|
||||
"tqdm": {
|
||||
"hashes": [
|
||||
"sha256:736524215c690621b06fc89d0310a49822d75e599fcd0feb7cc742b98d692493",
|
||||
"sha256:cd5791b5d7c3f2f1819efc81d36eb719a38e0906a7380365c556779f585ea042"
|
||||
"sha256:24be966933e942be5f074c29755a95b315c69a91f839a29139bf26ffffe2d3fd",
|
||||
"sha256:aa0c29f03f298951ac6318f7c8ce584e48fa22ec26396e6411e43d038243bdb2"
|
||||
],
|
||||
"version": "==4.61.0"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==4.61.1"
|
||||
},
|
||||
"twine": {
|
||||
"hashes": [
|
||||
"sha256:2f6942ec2a17417e19d2dd372fc4faa424c87ee9ce49b4e20c427eb00a0f3f41",
|
||||
"sha256:fcffa8fc37e8083a5be0728371f299598870ee1eccc94e9a25cef7b1dcfa8297"
|
||||
"sha256:16f706f2f1687d7ce30e7effceee40ed0a09b7c33b9abb5ef6434e5551565d83",
|
||||
"sha256:a56c985264b991dc8a8f4234eb80c5af87fa8080d0c224ad8f2cd05a2c22e83b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.3.0"
|
||||
},
|
||||
"typed-ast": {
|
||||
"hashes": [
|
||||
"sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace",
|
||||
"sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff",
|
||||
"sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266",
|
||||
"sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528",
|
||||
"sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6",
|
||||
"sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808",
|
||||
"sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4",
|
||||
"sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363",
|
||||
"sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341",
|
||||
"sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04",
|
||||
"sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41",
|
||||
"sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e",
|
||||
"sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3",
|
||||
"sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899",
|
||||
"sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805",
|
||||
"sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c",
|
||||
"sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c",
|
||||
"sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39",
|
||||
"sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a",
|
||||
"sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3",
|
||||
"sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7",
|
||||
"sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f",
|
||||
"sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075",
|
||||
"sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0",
|
||||
"sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40",
|
||||
"sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428",
|
||||
"sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927",
|
||||
"sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3",
|
||||
"sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f",
|
||||
"sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"
|
||||
],
|
||||
"version": "==1.4.3"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
|
||||
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
|
||||
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
|
||||
],
|
||||
"version": "==3.10.0.0"
|
||||
"version": "==3.4.1"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c",
|
||||
"sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"version": "==1.26.5"
|
||||
},
|
||||
"webencodings": {
|
||||
|
@ -531,6 +475,7 @@
|
|||
"sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76",
|
||||
"sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.4.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,43 +23,43 @@ import sys
|
|||
|
||||
|
||||
def get_basedir():
|
||||
if hasattr(sys, "frozen"): # exists when running via py2exe
|
||||
return sys.prefix
|
||||
else:
|
||||
return os.path.dirname(os.path.realpath(__file__))
|
||||
if hasattr(sys, "frozen"): # exists when running via py2exe
|
||||
return sys.prefix
|
||||
else:
|
||||
return os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
def get_basedir_git(path=None):
|
||||
previous_directory = None
|
||||
previous_directory = None
|
||||
|
||||
if path is not None:
|
||||
previous_directory = os.getcwd()
|
||||
os.chdir(path)
|
||||
if path is not None:
|
||||
previous_directory = os.getcwd()
|
||||
os.chdir(path)
|
||||
|
||||
bare_command = subprocess.Popen(
|
||||
["git", "rev-parse", "--is-bare-repository"], stdout=subprocess.PIPE, stderr=open(os.devnull, "w")
|
||||
)
|
||||
bare_command = subprocess.Popen(
|
||||
["git", "rev-parse", "--is-bare-repository"], stdout=subprocess.PIPE, stderr=open(os.devnull, "w")
|
||||
)
|
||||
|
||||
isbare = bare_command.stdout.readlines()
|
||||
bare_command.wait()
|
||||
isbare = bare_command.stdout.readlines()
|
||||
bare_command.wait()
|
||||
|
||||
if bare_command.returncode != 0:
|
||||
sys.exit(_('Error processing git repository at "%s".' % os.getcwd()))
|
||||
if bare_command.returncode != 0:
|
||||
sys.exit(_('Error processing git repository at "%s".' % os.getcwd()))
|
||||
|
||||
isbare = isbare[0].decode("utf-8", "replace").strip() == "true"
|
||||
absolute_path = None
|
||||
isbare = isbare[0].decode("utf-8", "replace").strip() == "true"
|
||||
absolute_path = None
|
||||
|
||||
if isbare:
|
||||
absolute_path = subprocess.Popen(["git", "rev-parse", "--git-dir"], stdout=subprocess.PIPE).stdout
|
||||
else:
|
||||
absolute_path = subprocess.Popen(["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE).stdout
|
||||
if isbare:
|
||||
absolute_path = subprocess.Popen(["git", "rev-parse", "--git-dir"], stdout=subprocess.PIPE).stdout
|
||||
else:
|
||||
absolute_path = subprocess.Popen(["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE).stdout
|
||||
|
||||
absolute_path = absolute_path.readlines()
|
||||
absolute_path = absolute_path.readlines()
|
||||
|
||||
if len(absolute_path) == 0:
|
||||
sys.exit(_("Unable to determine absolute path of git repository."))
|
||||
if len(absolute_path) == 0:
|
||||
sys.exit(_("Unable to determine absolute path of git repository."))
|
||||
|
||||
if path is not None:
|
||||
os.chdir(previous_directory)
|
||||
if path is not None:
|
||||
os.chdir(previous_directory)
|
||||
|
||||
return absolute_path[0].decode("utf-8", "replace").strip()
|
||||
return absolute_path[0].decode("utf-8", "replace").strip()
|
||||
|
|
|
@ -31,9 +31,9 @@ NUM_THREADS = multiprocessing.cpu_count()
|
|||
|
||||
|
||||
class BlameEntry(object):
|
||||
rows = 0
|
||||
skew = 0 # Used when calculating average code age.
|
||||
comments = 0
|
||||
rows = 0
|
||||
skew = 0 # Used when calculating average code age.
|
||||
comments = 0
|
||||
|
||||
|
||||
__thread_lock__ = threading.BoundedSemaphore(NUM_THREADS)
|
||||
|
@ -43,175 +43,173 @@ AVG_DAYS_PER_MONTH = 30.4167
|
|||
|
||||
|
||||
class BlameThread(threading.Thread):
|
||||
def __init__(self, useweeks, changes, blame_command, extension, blames, filename):
|
||||
__thread_lock__.acquire() # Lock controlling the number of threads running
|
||||
threading.Thread.__init__(self)
|
||||
def __init__(self, useweeks, changes, blame_command, extension, blames, filename):
|
||||
__thread_lock__.acquire() # Lock controlling the number of threads running
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
self.useweeks = useweeks
|
||||
self.changes = changes
|
||||
self.blame_command = blame_command
|
||||
self.extension = extension
|
||||
self.blames = blames
|
||||
self.filename = filename
|
||||
self.useweeks = useweeks
|
||||
self.changes = changes
|
||||
self.blame_command = blame_command
|
||||
self.extension = extension
|
||||
self.blames = blames
|
||||
self.filename = filename
|
||||
|
||||
self.is_inside_comment = False
|
||||
self.is_inside_comment = False
|
||||
|
||||
def __clear_blamechunk_info__(self):
|
||||
self.blamechunk_email = None
|
||||
self.blamechunk_is_last = False
|
||||
self.blamechunk_is_prior = False
|
||||
self.blamechunk_revision = None
|
||||
self.blamechunk_time = None
|
||||
def __clear_blamechunk_info__(self):
|
||||
self.blamechunk_email = None
|
||||
self.blamechunk_is_last = False
|
||||
self.blamechunk_is_prior = False
|
||||
self.blamechunk_revision = None
|
||||
self.blamechunk_time = None
|
||||
|
||||
def __handle_blamechunk_content__(self, content):
|
||||
author = None
|
||||
(comments, self.is_inside_comment) = comment.handle_comment_block(self.is_inside_comment, self.extension, content)
|
||||
def __handle_blamechunk_content__(self, content):
|
||||
author = None
|
||||
(comments, self.is_inside_comment) = comment.handle_comment_block(self.is_inside_comment, self.extension, content)
|
||||
|
||||
if self.blamechunk_is_prior and interval.get_since():
|
||||
return
|
||||
try:
|
||||
author = self.changes.get_latest_author_by_email(self.blamechunk_email)
|
||||
except KeyError:
|
||||
return
|
||||
if self.blamechunk_is_prior and interval.get_since():
|
||||
return
|
||||
try:
|
||||
author = self.changes.get_latest_author_by_email(self.blamechunk_email)
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
if (
|
||||
not filtering.set_filtered(author, "author")
|
||||
and not filtering.set_filtered(self.blamechunk_email, "email")
|
||||
and not filtering.set_filtered(self.blamechunk_revision, "revision")
|
||||
):
|
||||
if (
|
||||
not filtering.set_filtered(author, "author")
|
||||
and not filtering.set_filtered(self.blamechunk_email, "email")
|
||||
and not filtering.set_filtered(self.blamechunk_revision, "revision")
|
||||
):
|
||||
|
||||
__blame_lock__.acquire() # Global lock used to protect calls from here...
|
||||
__blame_lock__.acquire() # Global lock used to protect calls from here...
|
||||
|
||||
if self.blames.get((author, self.filename), None) is None:
|
||||
self.blames[(author, self.filename)] = BlameEntry()
|
||||
if self.blames.get((author, self.filename), None) is None:
|
||||
self.blames[(author, self.filename)] = BlameEntry()
|
||||
|
||||
self.blames[(author, self.filename)].comments += comments
|
||||
self.blames[(author, self.filename)].rows += 1
|
||||
self.blames[(author, self.filename)].comments += comments
|
||||
self.blames[(author, self.filename)].rows += 1
|
||||
|
||||
if (self.blamechunk_time - self.changes.first_commit_date).days > 0:
|
||||
self.blames[(author, self.filename)].skew += (self.changes.last_commit_date - self.blamechunk_time).days / (
|
||||
7.0 if self.useweeks else AVG_DAYS_PER_MONTH
|
||||
)
|
||||
if (self.blamechunk_time - self.changes.first_commit_date).days > 0:
|
||||
self.blames[(author, self.filename)].skew += (self.changes.last_commit_date - self.blamechunk_time).days / (
|
||||
7.0 if self.useweeks else AVG_DAYS_PER_MONTH
|
||||
)
|
||||
|
||||
__blame_lock__.release() # ...to here.
|
||||
__blame_lock__.release() # ...to here.
|
||||
|
||||
def run(self):
|
||||
git_blame_r = subprocess.Popen(self.blame_command, stdout=subprocess.PIPE).stdout
|
||||
rows = git_blame_r.readlines()
|
||||
git_blame_r.close()
|
||||
def run(self):
|
||||
git_blame_r = subprocess.Popen(self.blame_command, stdout=subprocess.PIPE).stdout
|
||||
rows = git_blame_r.readlines()
|
||||
git_blame_r.close()
|
||||
|
||||
self.__clear_blamechunk_info__()
|
||||
self.__clear_blamechunk_info__()
|
||||
|
||||
# pylint: disable=W0201
|
||||
for j in range(0, len(rows)):
|
||||
row = rows[j].decode("utf-8", "replace").strip()
|
||||
keyval = row.split(" ", 2)
|
||||
# pylint: disable=W0201
|
||||
for j in range(0, len(rows)):
|
||||
row = rows[j].decode("utf-8", "replace").strip()
|
||||
keyval = row.split(" ", 2)
|
||||
|
||||
if self.blamechunk_is_last:
|
||||
self.__handle_blamechunk_content__(row)
|
||||
self.__clear_blamechunk_info__()
|
||||
elif keyval[0] == "boundary":
|
||||
self.blamechunk_is_prior = True
|
||||
elif keyval[0] == "author-mail":
|
||||
self.blamechunk_email = keyval[1].lstrip("<").rstrip(">")
|
||||
elif keyval[0] == "author-time":
|
||||
self.blamechunk_time = datetime.date.fromtimestamp(int(keyval[1]))
|
||||
elif keyval[0] == "filename":
|
||||
self.blamechunk_is_last = True
|
||||
elif Blame.is_revision(keyval[0]):
|
||||
self.blamechunk_revision = keyval[0]
|
||||
if self.blamechunk_is_last:
|
||||
self.__handle_blamechunk_content__(row)
|
||||
self.__clear_blamechunk_info__()
|
||||
elif keyval[0] == "boundary":
|
||||
self.blamechunk_is_prior = True
|
||||
elif keyval[0] == "author-mail":
|
||||
self.blamechunk_email = keyval[1].lstrip("<").rstrip(">")
|
||||
elif keyval[0] == "author-time":
|
||||
self.blamechunk_time = datetime.date.fromtimestamp(int(keyval[1]))
|
||||
elif keyval[0] == "filename":
|
||||
self.blamechunk_is_last = True
|
||||
elif Blame.is_revision(keyval[0]):
|
||||
self.blamechunk_revision = keyval[0]
|
||||
|
||||
__thread_lock__.release() # Lock controlling the number of threads running
|
||||
__thread_lock__.release() # Lock controlling the number of threads running
|
||||
|
||||
|
||||
PROGRESS_TEXT = N_("Checking how many rows belong to each author (2 of 2): {0:.0f}%")
|
||||
|
||||
|
||||
class Blame(object):
|
||||
def __init__(self, repo, hard, useweeks, changes):
|
||||
self.blames = {}
|
||||
ls_tree_p = subprocess.Popen(
|
||||
["git", "ls-tree", "--name-only", "-r", interval.get_ref()], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
lines = ls_tree_p.communicate()[0].splitlines()
|
||||
ls_tree_p.stdout.close()
|
||||
def __init__(self, repo, hard, useweeks, changes):
|
||||
self.blames = {}
|
||||
ls_tree_p = subprocess.Popen(
|
||||
["git", "ls-tree", "--name-only", "-r", interval.get_ref()], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
lines = ls_tree_p.communicate()[0].splitlines()
|
||||
ls_tree_p.stdout.close()
|
||||
|
||||
if ls_tree_p.returncode == 0:
|
||||
progress_text = _(PROGRESS_TEXT)
|
||||
if ls_tree_p.returncode == 0:
|
||||
progress_text = _(PROGRESS_TEXT)
|
||||
|
||||
if repo is not None:
|
||||
progress_text = "[%s] " % repo.name + progress_text
|
||||
if repo is not None:
|
||||
progress_text = "[%s] " % repo.name + progress_text
|
||||
|
||||
for i, row in enumerate(lines):
|
||||
row = row.strip().decode("unicode_escape", "ignore")
|
||||
row = row.encode("latin-1", "replace")
|
||||
row = row.decode("utf-8", "replace").strip('"').strip("'").strip()
|
||||
for i, row in enumerate(lines):
|
||||
row = row.strip().decode("unicode_escape", "ignore")
|
||||
row = row.encode("latin-1", "replace")
|
||||
row = row.decode("utf-8", "replace").strip('"').strip("'").strip()
|
||||
|
||||
if (
|
||||
FileDiff.get_extension(row) in extensions.get_located()
|
||||
and FileDiff.is_valid_extension(row)
|
||||
and not filtering.set_filtered(FileDiff.get_filename(row))
|
||||
):
|
||||
blame_command = [
|
||||
_f
|
||||
for _f in ["git", "blame", "--line-porcelain", "-w"]
|
||||
+ (["-C", "-C", "-M"] if hard else [])
|
||||
+ [interval.get_since(), interval.get_ref(), "--", row]
|
||||
if _f
|
||||
]
|
||||
thread = BlameThread(
|
||||
useweeks, changes, blame_command, FileDiff.get_extension(row), self.blames, row.strip()
|
||||
)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
if (
|
||||
FileDiff.get_extension(row) in extensions.get_located()
|
||||
and FileDiff.is_valid_extension(row)
|
||||
and not filtering.set_filtered(FileDiff.get_filename(row))
|
||||
):
|
||||
blame_command = [
|
||||
_f
|
||||
for _f in ["git", "blame", "--line-porcelain", "-w"]
|
||||
+ (["-C", "-C", "-M"] if hard else [])
|
||||
+ [interval.get_since(), interval.get_ref(), "--", row]
|
||||
if _f
|
||||
]
|
||||
thread = BlameThread(useweeks, changes, blame_command, FileDiff.get_extension(row), self.blames, row.strip())
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
if format.is_interactive_format():
|
||||
terminal.output_progress(progress_text, i, len(lines))
|
||||
if format.is_interactive_format():
|
||||
terminal.output_progress(progress_text, i, len(lines))
|
||||
|
||||
# Make sure all threads have completed.
|
||||
for i in range(0, NUM_THREADS):
|
||||
__thread_lock__.acquire()
|
||||
# Make sure all threads have completed.
|
||||
for i in range(0, NUM_THREADS):
|
||||
__thread_lock__.acquire()
|
||||
|
||||
# We also have to release them for future use.
|
||||
for i in range(0, NUM_THREADS):
|
||||
__thread_lock__.release()
|
||||
# We also have to release them for future use.
|
||||
for i in range(0, NUM_THREADS):
|
||||
__thread_lock__.release()
|
||||
|
||||
def __iadd__(self, other):
|
||||
try:
|
||||
self.blames.update(other.blames)
|
||||
return self
|
||||
except AttributeError:
|
||||
return other
|
||||
def __iadd__(self, other):
|
||||
try:
|
||||
self.blames.update(other.blames)
|
||||
return self
|
||||
except AttributeError:
|
||||
return other
|
||||
|
||||
@staticmethod
|
||||
def is_revision(string):
|
||||
revision = re.search("([0-9a-f]{40})", string)
|
||||
@staticmethod
|
||||
def is_revision(string):
|
||||
revision = re.search("([0-9a-f]{40})", string)
|
||||
|
||||
if revision is None:
|
||||
return False
|
||||
if revision is None:
|
||||
return False
|
||||
|
||||
return revision.group(1).strip()
|
||||
return revision.group(1).strip()
|
||||
|
||||
@staticmethod
|
||||
def get_stability(author, blamed_rows, changes):
|
||||
if author in changes.get_authorinfo_list():
|
||||
author_insertions = changes.get_authorinfo_list()[author].insertions
|
||||
return 100 if author_insertions == 0 else 100.0 * blamed_rows / author_insertions
|
||||
return 100
|
||||
@staticmethod
|
||||
def get_stability(author, blamed_rows, changes):
|
||||
if author in changes.get_authorinfo_list():
|
||||
author_insertions = changes.get_authorinfo_list()[author].insertions
|
||||
return 100 if author_insertions == 0 else 100.0 * blamed_rows / author_insertions
|
||||
return 100
|
||||
|
||||
@staticmethod
|
||||
def get_time(string):
|
||||
time = re.search(r" \(.*?(\d\d\d\d-\d\d-\d\d)", string)
|
||||
return time.group(1).strip()
|
||||
@staticmethod
|
||||
def get_time(string):
|
||||
time = re.search(r" \(.*?(\d\d\d\d-\d\d-\d\d)", string)
|
||||
return time.group(1).strip()
|
||||
|
||||
def get_summed_blames(self):
|
||||
summed_blames = {}
|
||||
for i in list(self.blames.items()):
|
||||
if summed_blames.get(i[0][0], None) is None:
|
||||
summed_blames[i[0][0]] = BlameEntry()
|
||||
def get_summed_blames(self):
|
||||
summed_blames = {}
|
||||
for i in list(self.blames.items()):
|
||||
if summed_blames.get(i[0][0], None) is None:
|
||||
summed_blames[i[0][0]] = BlameEntry()
|
||||
|
||||
summed_blames[i[0][0]].rows += i[1].rows
|
||||
summed_blames[i[0][0]].skew += i[1].skew
|
||||
summed_blames[i[0][0]].comments += i[1].comments
|
||||
summed_blames[i[0][0]].rows += i[1].rows
|
||||
summed_blames[i[0][0]].skew += i[1].skew
|
||||
summed_blames[i[0][0]].comments += i[1].comments
|
||||
|
||||
return summed_blames
|
||||
return summed_blames
|
||||
|
|
|
@ -35,289 +35,285 @@ __changes_lock__ = threading.Lock()
|
|||
|
||||
|
||||
class FileDiff(object):
|
||||
def __init__(self, string):
|
||||
commit_line = string.split("|")
|
||||
def __init__(self, string):
|
||||
commit_line = string.split("|")
|
||||
|
||||
if commit_line.__len__() == 2:
|
||||
self.name = commit_line[0].strip()
|
||||
self.insertions = commit_line[1].count("+")
|
||||
self.deletions = commit_line[1].count("-")
|
||||
if commit_line.__len__() == 2:
|
||||
self.name = commit_line[0].strip()
|
||||
self.insertions = commit_line[1].count("+")
|
||||
self.deletions = commit_line[1].count("-")
|
||||
|
||||
@staticmethod
|
||||
def is_filediff_line(string):
|
||||
string = string.split("|")
|
||||
return string.__len__() == 2 and string[1].find("Bin") == -1 and ("+" in string[1] or "-" in string[1])
|
||||
@staticmethod
|
||||
def is_filediff_line(string):
|
||||
string = string.split("|")
|
||||
return string.__len__() == 2 and string[1].find("Bin") == -1 and ("+" in string[1] or "-" in string[1])
|
||||
|
||||
@staticmethod
|
||||
def get_extension(string):
|
||||
string = string.split("|")[0].strip().strip("{}").strip('"').strip("'")
|
||||
return os.path.splitext(string)[1][1:]
|
||||
@staticmethod
|
||||
def get_extension(string):
|
||||
string = string.split("|")[0].strip().strip("{}").strip('"').strip("'")
|
||||
return os.path.splitext(string)[1][1:]
|
||||
|
||||
@staticmethod
|
||||
def get_filename(string):
|
||||
return string.split("|")[0].strip().strip("{}").strip('"').strip("'")
|
||||
@staticmethod
|
||||
def get_filename(string):
|
||||
return string.split("|")[0].strip().strip("{}").strip('"').strip("'")
|
||||
|
||||
@staticmethod
|
||||
def is_valid_extension(string):
|
||||
extension = FileDiff.get_extension(string)
|
||||
@staticmethod
|
||||
def is_valid_extension(string):
|
||||
extension = FileDiff.get_extension(string)
|
||||
|
||||
for i in extensions.get():
|
||||
if (extension == "" and i == "*") or extension == i or i == "**":
|
||||
return True
|
||||
return False
|
||||
for i in extensions.get():
|
||||
if (extension == "" and i == "*") or extension == i or i == "**":
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Commit(object):
|
||||
def __init__(self, string):
|
||||
self.filediffs = []
|
||||
commit_line = string.split("|")
|
||||
def __init__(self, string):
|
||||
self.filediffs = []
|
||||
commit_line = string.split("|")
|
||||
|
||||
if commit_line.__len__() == 5:
|
||||
self.timestamp = commit_line[0]
|
||||
self.date = commit_line[1]
|
||||
self.sha = commit_line[2]
|
||||
self.author = commit_line[3].strip()
|
||||
self.email = commit_line[4].strip()
|
||||
if commit_line.__len__() == 5:
|
||||
self.timestamp = commit_line[0]
|
||||
self.date = commit_line[1]
|
||||
self.sha = commit_line[2]
|
||||
self.author = commit_line[3].strip()
|
||||
self.email = commit_line[4].strip()
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.timestamp.__lt__(other.timestamp) # only used for sorting; we just consider the timestamp.
|
||||
def __lt__(self, other):
|
||||
return self.timestamp.__lt__(other.timestamp) # only used for sorting; we just consider the timestamp.
|
||||
|
||||
def add_filediff(self, filediff):
|
||||
self.filediffs.append(filediff)
|
||||
def add_filediff(self, filediff):
|
||||
self.filediffs.append(filediff)
|
||||
|
||||
def get_filediffs(self):
|
||||
return self.filediffs
|
||||
def get_filediffs(self):
|
||||
return self.filediffs
|
||||
|
||||
@staticmethod
|
||||
def get_author_and_email(string):
|
||||
commit_line = string.split("|")
|
||||
@staticmethod
|
||||
def get_author_and_email(string):
|
||||
commit_line = string.split("|")
|
||||
|
||||
if commit_line.__len__() == 5:
|
||||
return (commit_line[3].strip(), commit_line[4].strip())
|
||||
if commit_line.__len__() == 5:
|
||||
return (commit_line[3].strip(), commit_line[4].strip())
|
||||
|
||||
@staticmethod
|
||||
def is_commit_line(string):
|
||||
return string.split("|").__len__() == 5
|
||||
@staticmethod
|
||||
def is_commit_line(string):
|
||||
return string.split("|").__len__() == 5
|
||||
|
||||
|
||||
class AuthorInfo(object):
|
||||
email = None
|
||||
insertions = 0
|
||||
deletions = 0
|
||||
commits = 0
|
||||
email = None
|
||||
insertions = 0
|
||||
deletions = 0
|
||||
commits = 0
|
||||
|
||||
|
||||
class ChangesThread(threading.Thread):
|
||||
def __init__(self, hard, changes, first_hash, second_hash, offset):
|
||||
__thread_lock__.acquire() # Lock controlling the number of threads running
|
||||
threading.Thread.__init__(self)
|
||||
def __init__(self, hard, changes, first_hash, second_hash, offset):
|
||||
__thread_lock__.acquire() # Lock controlling the number of threads running
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
self.hard = hard
|
||||
self.changes = changes
|
||||
self.first_hash = first_hash
|
||||
self.second_hash = second_hash
|
||||
self.offset = offset
|
||||
self.hard = hard
|
||||
self.changes = changes
|
||||
self.first_hash = first_hash
|
||||
self.second_hash = second_hash
|
||||
self.offset = offset
|
||||
|
||||
@staticmethod
|
||||
def create(hard, changes, first_hash, second_hash, offset):
|
||||
thread = ChangesThread(hard, changes, first_hash, second_hash, offset)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
@staticmethod
|
||||
def create(hard, changes, first_hash, second_hash, offset):
|
||||
thread = ChangesThread(hard, changes, first_hash, second_hash, offset)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
def run(self):
|
||||
git_log_r = subprocess.Popen(
|
||||
[
|
||||
_f
|
||||
for _f in [
|
||||
"git",
|
||||
"log",
|
||||
"--reverse",
|
||||
"--pretty=%ct|%cd|%H|%aN|%aE",
|
||||
"--stat=100000,8192",
|
||||
"--no-merges",
|
||||
"-w",
|
||||
interval.get_since(),
|
||||
interval.get_until(),
|
||||
"--date=short",
|
||||
]
|
||||
+ (["-C", "-C", "-M"] if self.hard else [])
|
||||
+ [self.first_hash + self.second_hash]
|
||||
if _f
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
).stdout
|
||||
lines = git_log_r.readlines()
|
||||
git_log_r.close()
|
||||
def run(self):
|
||||
git_log_r = subprocess.Popen(
|
||||
[
|
||||
_f
|
||||
for _f in [
|
||||
"git",
|
||||
"log",
|
||||
"--reverse",
|
||||
"--pretty=%ct|%cd|%H|%aN|%aE",
|
||||
"--stat=100000,8192",
|
||||
"--no-merges",
|
||||
"-w",
|
||||
interval.get_since(),
|
||||
interval.get_until(),
|
||||
"--date=short",
|
||||
]
|
||||
+ (["-C", "-C", "-M"] if self.hard else [])
|
||||
+ [self.first_hash + self.second_hash]
|
||||
if _f
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
).stdout
|
||||
lines = git_log_r.readlines()
|
||||
git_log_r.close()
|
||||
|
||||
commit = None
|
||||
found_valid_extension = False
|
||||
is_filtered = False
|
||||
commits = []
|
||||
commit = None
|
||||
found_valid_extension = False
|
||||
is_filtered = False
|
||||
commits = []
|
||||
|
||||
__changes_lock__.acquire() # Global lock used to protect calls from here...
|
||||
__changes_lock__.acquire() # Global lock used to protect calls from here...
|
||||
|
||||
for i in lines:
|
||||
j = i.strip().decode("unicode_escape", "ignore")
|
||||
j = j.encode("latin-1", "replace")
|
||||
j = j.decode("utf-8", "replace")
|
||||
for i in lines:
|
||||
j = i.strip().decode("unicode_escape", "ignore")
|
||||
j = j.encode("latin-1", "replace")
|
||||
j = j.decode("utf-8", "replace")
|
||||
|
||||
if Commit.is_commit_line(j):
|
||||
(author, email) = Commit.get_author_and_email(j)
|
||||
self.changes.emails_by_author[author] = email
|
||||
self.changes.authors_by_email[email] = author
|
||||
if Commit.is_commit_line(j):
|
||||
(author, email) = Commit.get_author_and_email(j)
|
||||
self.changes.emails_by_author[author] = email
|
||||
self.changes.authors_by_email[email] = author
|
||||
|
||||
if Commit.is_commit_line(j) or i is lines[-1]:
|
||||
if found_valid_extension:
|
||||
bisect.insort(commits, commit)
|
||||
if Commit.is_commit_line(j) or i is lines[-1]:
|
||||
if found_valid_extension:
|
||||
bisect.insort(commits, commit)
|
||||
|
||||
found_valid_extension = False
|
||||
is_filtered = False
|
||||
commit = Commit(j)
|
||||
found_valid_extension = False
|
||||
is_filtered = False
|
||||
commit = Commit(j)
|
||||
|
||||
if Commit.is_commit_line(j) and (
|
||||
filtering.set_filtered(commit.author, "author")
|
||||
or filtering.set_filtered(commit.email, "email")
|
||||
or filtering.set_filtered(commit.sha, "revision")
|
||||
or filtering.set_filtered(commit.sha, "message")
|
||||
):
|
||||
is_filtered = True
|
||||
if Commit.is_commit_line(j) and (
|
||||
filtering.set_filtered(commit.author, "author")
|
||||
or filtering.set_filtered(commit.email, "email")
|
||||
or filtering.set_filtered(commit.sha, "revision")
|
||||
or filtering.set_filtered(commit.sha, "message")
|
||||
):
|
||||
is_filtered = True
|
||||
|
||||
if FileDiff.is_filediff_line(j) and not filtering.set_filtered(FileDiff.get_filename(j)) and not is_filtered:
|
||||
extensions.add_located(FileDiff.get_extension(j))
|
||||
if FileDiff.is_filediff_line(j) and not filtering.set_filtered(FileDiff.get_filename(j)) and not is_filtered:
|
||||
extensions.add_located(FileDiff.get_extension(j))
|
||||
|
||||
if FileDiff.is_valid_extension(j):
|
||||
found_valid_extension = True
|
||||
filediff = FileDiff(j)
|
||||
commit.add_filediff(filediff)
|
||||
if FileDiff.is_valid_extension(j):
|
||||
found_valid_extension = True
|
||||
filediff = FileDiff(j)
|
||||
commit.add_filediff(filediff)
|
||||
|
||||
self.changes.commits[self.offset // CHANGES_PER_THREAD] = commits
|
||||
__changes_lock__.release() # ...to here.
|
||||
__thread_lock__.release() # Lock controlling the number of threads running
|
||||
self.changes.commits[self.offset // CHANGES_PER_THREAD] = commits
|
||||
__changes_lock__.release() # ...to here.
|
||||
__thread_lock__.release() # Lock controlling the number of threads running
|
||||
|
||||
|
||||
PROGRESS_TEXT = N_("Fetching and calculating primary statistics (1 of 2): {0:.0f}%")
|
||||
|
||||
|
||||
class Changes(object):
|
||||
authors = {}
|
||||
authors_dateinfo = {}
|
||||
authors_by_email = {}
|
||||
emails_by_author = {}
|
||||
authors = {}
|
||||
authors_dateinfo = {}
|
||||
authors_by_email = {}
|
||||
emails_by_author = {}
|
||||
|
||||
def __init__(self, repo, hard):
|
||||
self.commits = []
|
||||
interval.set_ref("HEAD")
|
||||
git_rev_list_p = subprocess.Popen(
|
||||
[
|
||||
_f
|
||||
for _f in ["git", "rev-list", "--reverse", "--no-merges", interval.get_since(), interval.get_until(), "HEAD"]
|
||||
if _f
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
lines = git_rev_list_p.communicate()[0].splitlines()
|
||||
git_rev_list_p.stdout.close()
|
||||
def __init__(self, repo, hard):
|
||||
self.commits = []
|
||||
interval.set_ref("HEAD")
|
||||
git_rev_list_p = subprocess.Popen(
|
||||
[_f for _f in ["git", "rev-list", "--reverse", "--no-merges", interval.get_since(), interval.get_until(), "HEAD"] if _f],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
lines = git_rev_list_p.communicate()[0].splitlines()
|
||||
git_rev_list_p.stdout.close()
|
||||
|
||||
if git_rev_list_p.returncode == 0 and len(lines) > 0:
|
||||
progress_text = _(PROGRESS_TEXT)
|
||||
if repo is not None:
|
||||
progress_text = "[%s] " % repo.name + progress_text
|
||||
if git_rev_list_p.returncode == 0 and len(lines) > 0:
|
||||
progress_text = _(PROGRESS_TEXT)
|
||||
if repo is not None:
|
||||
progress_text = "[%s] " % repo.name + progress_text
|
||||
|
||||
chunks = len(lines) // CHANGES_PER_THREAD
|
||||
self.commits = [None] * (chunks if len(lines) % CHANGES_PER_THREAD == 0 else chunks + 1)
|
||||
first_hash = ""
|
||||
chunks = len(lines) // CHANGES_PER_THREAD
|
||||
self.commits = [None] * (chunks if len(lines) % CHANGES_PER_THREAD == 0 else chunks + 1)
|
||||
first_hash = ""
|
||||
|
||||
for i, entry in enumerate(lines):
|
||||
if i % CHANGES_PER_THREAD == CHANGES_PER_THREAD - 1:
|
||||
entry = entry.decode("utf-8", "replace").strip()
|
||||
second_hash = entry
|
||||
ChangesThread.create(hard, self, first_hash, second_hash, i)
|
||||
first_hash = entry + ".."
|
||||
for i, entry in enumerate(lines):
|
||||
if i % CHANGES_PER_THREAD == CHANGES_PER_THREAD - 1:
|
||||
entry = entry.decode("utf-8", "replace").strip()
|
||||
second_hash = entry
|
||||
ChangesThread.create(hard, self, first_hash, second_hash, i)
|
||||
first_hash = entry + ".."
|
||||
|
||||
if format.is_interactive_format():
|
||||
terminal.output_progress(progress_text, i, len(lines))
|
||||
else:
|
||||
if CHANGES_PER_THREAD - 1 != i % CHANGES_PER_THREAD:
|
||||
entry = entry.decode("utf-8", "replace").strip()
|
||||
second_hash = entry
|
||||
ChangesThread.create(hard, self, first_hash, second_hash, i)
|
||||
if format.is_interactive_format():
|
||||
terminal.output_progress(progress_text, i, len(lines))
|
||||
else:
|
||||
if CHANGES_PER_THREAD - 1 != i % CHANGES_PER_THREAD:
|
||||
entry = entry.decode("utf-8", "replace").strip()
|
||||
second_hash = entry
|
||||
ChangesThread.create(hard, self, first_hash, second_hash, i)
|
||||
|
||||
# Make sure all threads have completed.
|
||||
for i in range(0, NUM_THREADS):
|
||||
__thread_lock__.acquire()
|
||||
# Make sure all threads have completed.
|
||||
for i in range(0, NUM_THREADS):
|
||||
__thread_lock__.acquire()
|
||||
|
||||
# We also have to release them for future use.
|
||||
for i in range(0, NUM_THREADS):
|
||||
__thread_lock__.release()
|
||||
# We also have to release them for future use.
|
||||
for i in range(0, NUM_THREADS):
|
||||
__thread_lock__.release()
|
||||
|
||||
self.commits = [item for sublist in self.commits for item in sublist]
|
||||
self.commits = [item for sublist in self.commits for item in sublist]
|
||||
|
||||
if len(self.commits) > 0:
|
||||
if interval.has_interval():
|
||||
interval.set_ref(self.commits[-1].sha)
|
||||
if len(self.commits) > 0:
|
||||
if interval.has_interval():
|
||||
interval.set_ref(self.commits[-1].sha)
|
||||
|
||||
self.first_commit_date = datetime.date(
|
||||
int(self.commits[0].date[0:4]), int(self.commits[0].date[5:7]), int(self.commits[0].date[8:10])
|
||||
)
|
||||
self.last_commit_date = datetime.date(
|
||||
int(self.commits[-1].date[0:4]), int(self.commits[-1].date[5:7]), int(self.commits[-1].date[8:10])
|
||||
)
|
||||
self.first_commit_date = datetime.date(
|
||||
int(self.commits[0].date[0:4]), int(self.commits[0].date[5:7]), int(self.commits[0].date[8:10])
|
||||
)
|
||||
self.last_commit_date = datetime.date(
|
||||
int(self.commits[-1].date[0:4]), int(self.commits[-1].date[5:7]), int(self.commits[-1].date[8:10])
|
||||
)
|
||||
|
||||
def __iadd__(self, other):
|
||||
try:
|
||||
self.authors.update(other.authors)
|
||||
self.authors_dateinfo.update(other.authors_dateinfo)
|
||||
self.authors_by_email.update(other.authors_by_email)
|
||||
self.emails_by_author.update(other.emails_by_author)
|
||||
def __iadd__(self, other):
|
||||
try:
|
||||
self.authors.update(other.authors)
|
||||
self.authors_dateinfo.update(other.authors_dateinfo)
|
||||
self.authors_by_email.update(other.authors_by_email)
|
||||
self.emails_by_author.update(other.emails_by_author)
|
||||
|
||||
for commit in other.commits:
|
||||
bisect.insort(self.commits, commit)
|
||||
if not self.commits and not other.commits:
|
||||
self.commits = []
|
||||
for commit in other.commits:
|
||||
bisect.insort(self.commits, commit)
|
||||
if not self.commits and not other.commits:
|
||||
self.commits = []
|
||||
|
||||
return self
|
||||
except AttributeError:
|
||||
return other
|
||||
return self
|
||||
except AttributeError:
|
||||
return other
|
||||
|
||||
def get_commits(self):
|
||||
return self.commits
|
||||
def get_commits(self):
|
||||
return self.commits
|
||||
|
||||
@staticmethod
|
||||
def modify_authorinfo(authors, key, commit):
|
||||
if authors.get(key, None) is None:
|
||||
authors[key] = AuthorInfo()
|
||||
@staticmethod
|
||||
def modify_authorinfo(authors, key, commit):
|
||||
if authors.get(key, None) is None:
|
||||
authors[key] = AuthorInfo()
|
||||
|
||||
if commit.get_filediffs():
|
||||
authors[key].commits += 1
|
||||
if commit.get_filediffs():
|
||||
authors[key].commits += 1
|
||||
|
||||
for j in commit.get_filediffs():
|
||||
authors[key].insertions += j.insertions
|
||||
authors[key].deletions += j.deletions
|
||||
for j in commit.get_filediffs():
|
||||
authors[key].insertions += j.insertions
|
||||
authors[key].deletions += j.deletions
|
||||
|
||||
def get_authorinfo_list(self):
|
||||
if not self.authors:
|
||||
for i in self.commits:
|
||||
Changes.modify_authorinfo(self.authors, i.author, i)
|
||||
def get_authorinfo_list(self):
|
||||
if not self.authors:
|
||||
for i in self.commits:
|
||||
Changes.modify_authorinfo(self.authors, i.author, i)
|
||||
|
||||
return self.authors
|
||||
return self.authors
|
||||
|
||||
def get_authordateinfo_list(self):
|
||||
if not self.authors_dateinfo:
|
||||
for i in self.commits:
|
||||
Changes.modify_authorinfo(self.authors_dateinfo, (i.date, i.author), i)
|
||||
def get_authordateinfo_list(self):
|
||||
if not self.authors_dateinfo:
|
||||
for i in self.commits:
|
||||
Changes.modify_authorinfo(self.authors_dateinfo, (i.date, i.author), i)
|
||||
|
||||
return self.authors_dateinfo
|
||||
return self.authors_dateinfo
|
||||
|
||||
def get_latest_author_by_email(self, name):
|
||||
if not hasattr(name, "decode"):
|
||||
name = str.encode(name)
|
||||
try:
|
||||
name = name.decode("unicode_escape", "ignore")
|
||||
except UnicodeEncodeError:
|
||||
pass
|
||||
def get_latest_author_by_email(self, name):
|
||||
if not hasattr(name, "decode"):
|
||||
name = str.encode(name)
|
||||
try:
|
||||
name = name.decode("unicode_escape", "ignore")
|
||||
except UnicodeEncodeError:
|
||||
pass
|
||||
|
||||
return self.authors_by_email[name]
|
||||
return self.authors_by_email[name]
|
||||
|
||||
def get_latest_email_by_author(self, name):
|
||||
return self.emails_by_author[name]
|
||||
def get_latest_email_by_author(self, name):
|
||||
return self.emails_by_author[name]
|
||||
|
|
|
@ -25,41 +25,41 @@ import sys
|
|||
import tempfile
|
||||
|
||||
try:
|
||||
from urllib.parse import urlparse
|
||||
from urllib.parse import urlparse
|
||||
except:
|
||||
from urllib.parse import urlparse
|
||||
from urllib.parse import urlparse
|
||||
|
||||
__cloned_paths__ = []
|
||||
|
||||
|
||||
def create(url):
|
||||
class Repository(object):
|
||||
def __init__(self, name, location):
|
||||
self.name = name
|
||||
self.location = location
|
||||
class Repository(object):
|
||||
def __init__(self, name, location):
|
||||
self.name = name
|
||||
self.location = location
|
||||
|
||||
parsed_url = urlparse(url)
|
||||
parsed_url = urlparse(url)
|
||||
|
||||
if (
|
||||
parsed_url.scheme == "file"
|
||||
or parsed_url.scheme == "git"
|
||||
or parsed_url.scheme == "http"
|
||||
or parsed_url.scheme == "https"
|
||||
or parsed_url.scheme == "ssh"
|
||||
):
|
||||
path = tempfile.mkdtemp(suffix=".gitinspector")
|
||||
git_clone = subprocess.Popen(["git", "clone", url, path], stdout=sys.stderr)
|
||||
git_clone.wait()
|
||||
if (
|
||||
parsed_url.scheme == "file"
|
||||
or parsed_url.scheme == "git"
|
||||
or parsed_url.scheme == "http"
|
||||
or parsed_url.scheme == "https"
|
||||
or parsed_url.scheme == "ssh"
|
||||
):
|
||||
path = tempfile.mkdtemp(suffix=".gitinspector")
|
||||
git_clone = subprocess.Popen(["git", "clone", url, path], stdout=sys.stderr)
|
||||
git_clone.wait()
|
||||
|
||||
if git_clone.returncode != 0:
|
||||
sys.exit(git_clone.returncode)
|
||||
if git_clone.returncode != 0:
|
||||
sys.exit(git_clone.returncode)
|
||||
|
||||
__cloned_paths__.append(path)
|
||||
return Repository(os.path.basename(parsed_url.path), path)
|
||||
__cloned_paths__.append(path)
|
||||
return Repository(os.path.basename(parsed_url.path), path)
|
||||
|
||||
return Repository(None, os.path.abspath(url))
|
||||
return Repository(None, os.path.abspath(url))
|
||||
|
||||
|
||||
def delete():
|
||||
for path in __cloned_paths__:
|
||||
shutil.rmtree(path, ignore_errors=True)
|
||||
for path in __cloned_paths__:
|
||||
shutil.rmtree(path, ignore_errors=True)
|
||||
|
|
|
@ -19,138 +19,138 @@
|
|||
|
||||
|
||||
__comment_begining__ = {
|
||||
"java": "/*",
|
||||
"c": "/*",
|
||||
"cc": "/*",
|
||||
"cpp": "/*",
|
||||
"cs": "/*",
|
||||
"h": "/*",
|
||||
"hh": "/*",
|
||||
"hpp": "/*",
|
||||
"hs": "{-",
|
||||
"html": "<!--",
|
||||
"php": "/*",
|
||||
"py": '"""',
|
||||
"glsl": "/*",
|
||||
"rb": "=begin",
|
||||
"js": "/*",
|
||||
"jspx": "<!--",
|
||||
"scala": "/*",
|
||||
"sql": "/*",
|
||||
"tex": "\\begin{comment}",
|
||||
"xhtml": "<!--",
|
||||
"xml": "<!--",
|
||||
"ml": "(*",
|
||||
"mli": "(*",
|
||||
"go": "/*",
|
||||
"ly": "%{",
|
||||
"ily": "%{",
|
||||
"java": "/*",
|
||||
"c": "/*",
|
||||
"cc": "/*",
|
||||
"cpp": "/*",
|
||||
"cs": "/*",
|
||||
"h": "/*",
|
||||
"hh": "/*",
|
||||
"hpp": "/*",
|
||||
"hs": "{-",
|
||||
"html": "<!--",
|
||||
"php": "/*",
|
||||
"py": '"""',
|
||||
"glsl": "/*",
|
||||
"rb": "=begin",
|
||||
"js": "/*",
|
||||
"jspx": "<!--",
|
||||
"scala": "/*",
|
||||
"sql": "/*",
|
||||
"tex": "\\begin{comment}",
|
||||
"xhtml": "<!--",
|
||||
"xml": "<!--",
|
||||
"ml": "(*",
|
||||
"mli": "(*",
|
||||
"go": "/*",
|
||||
"ly": "%{",
|
||||
"ily": "%{",
|
||||
}
|
||||
|
||||
__comment_end__ = {
|
||||
"java": "*/",
|
||||
"c": "*/",
|
||||
"cc": "*/",
|
||||
"cpp": "*/",
|
||||
"cs": "*/",
|
||||
"h": "*/",
|
||||
"hh": "*/",
|
||||
"hpp": "*/",
|
||||
"hs": "-}",
|
||||
"html": "-->",
|
||||
"php": "*/",
|
||||
"py": '"""',
|
||||
"glsl": "*/",
|
||||
"rb": "=end",
|
||||
"js": "*/",
|
||||
"jspx": "-->",
|
||||
"scala": "*/",
|
||||
"sql": "*/",
|
||||
"tex": "\\end{comment}",
|
||||
"xhtml": "-->",
|
||||
"xml": "-->",
|
||||
"ml": "*)",
|
||||
"mli": "*)",
|
||||
"go": "*/",
|
||||
"ly": "%}",
|
||||
"ily": "%}",
|
||||
"java": "*/",
|
||||
"c": "*/",
|
||||
"cc": "*/",
|
||||
"cpp": "*/",
|
||||
"cs": "*/",
|
||||
"h": "*/",
|
||||
"hh": "*/",
|
||||
"hpp": "*/",
|
||||
"hs": "-}",
|
||||
"html": "-->",
|
||||
"php": "*/",
|
||||
"py": '"""',
|
||||
"glsl": "*/",
|
||||
"rb": "=end",
|
||||
"js": "*/",
|
||||
"jspx": "-->",
|
||||
"scala": "*/",
|
||||
"sql": "*/",
|
||||
"tex": "\\end{comment}",
|
||||
"xhtml": "-->",
|
||||
"xml": "-->",
|
||||
"ml": "*)",
|
||||
"mli": "*)",
|
||||
"go": "*/",
|
||||
"ly": "%}",
|
||||
"ily": "%}",
|
||||
}
|
||||
|
||||
__comment__ = {
|
||||
"java": "//",
|
||||
"c": "//",
|
||||
"cc": "//",
|
||||
"cpp": "//",
|
||||
"cs": "//",
|
||||
"h": "//",
|
||||
"hh": "//",
|
||||
"hpp": "//",
|
||||
"hs": "--",
|
||||
"pl": "#",
|
||||
"php": "//",
|
||||
"py": "#",
|
||||
"glsl": "//",
|
||||
"rb": "#",
|
||||
"robot": "#",
|
||||
"rs": "//",
|
||||
"rlib": "//",
|
||||
"js": "//",
|
||||
"scala": "//",
|
||||
"sql": "--",
|
||||
"tex": "%",
|
||||
"ada": "--",
|
||||
"ads": "--",
|
||||
"adb": "--",
|
||||
"pot": "#",
|
||||
"po": "#",
|
||||
"go": "//",
|
||||
"ly": "%",
|
||||
"ily": "%",
|
||||
"java": "//",
|
||||
"c": "//",
|
||||
"cc": "//",
|
||||
"cpp": "//",
|
||||
"cs": "//",
|
||||
"h": "//",
|
||||
"hh": "//",
|
||||
"hpp": "//",
|
||||
"hs": "--",
|
||||
"pl": "#",
|
||||
"php": "//",
|
||||
"py": "#",
|
||||
"glsl": "//",
|
||||
"rb": "#",
|
||||
"robot": "#",
|
||||
"rs": "//",
|
||||
"rlib": "//",
|
||||
"js": "//",
|
||||
"scala": "//",
|
||||
"sql": "--",
|
||||
"tex": "%",
|
||||
"ada": "--",
|
||||
"ads": "--",
|
||||
"adb": "--",
|
||||
"pot": "#",
|
||||
"po": "#",
|
||||
"go": "//",
|
||||
"ly": "%",
|
||||
"ily": "%",
|
||||
}
|
||||
|
||||
__comment_markers_must_be_at_begining__ = {"tex": True}
|
||||
|
||||
|
||||
def __has_comment_begining__(extension, string):
|
||||
if __comment_markers_must_be_at_begining__.get(extension, None):
|
||||
return string.find(__comment_begining__[extension]) == 0
|
||||
elif __comment_begining__.get(extension, None) is not None and string.find(__comment_end__[extension], 2) == -1:
|
||||
return string.find(__comment_begining__[extension]) != -1
|
||||
if __comment_markers_must_be_at_begining__.get(extension, None):
|
||||
return string.find(__comment_begining__[extension]) == 0
|
||||
elif __comment_begining__.get(extension, None) is not None and string.find(__comment_end__[extension], 2) == -1:
|
||||
return string.find(__comment_begining__[extension]) != -1
|
||||
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
def __has_comment_end__(extension, string):
|
||||
if __comment_markers_must_be_at_begining__.get(extension, None):
|
||||
return string.find(__comment_end__[extension]) == 0
|
||||
elif __comment_end__.get(extension, None) is not None:
|
||||
return string.find(__comment_end__[extension]) != -1
|
||||
if __comment_markers_must_be_at_begining__.get(extension, None):
|
||||
return string.find(__comment_end__[extension]) == 0
|
||||
elif __comment_end__.get(extension, None) is not None:
|
||||
return string.find(__comment_end__[extension]) != -1
|
||||
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
def is_comment(extension, string):
|
||||
if __comment_begining__.get(extension, None) is not None and string.strip().startswith(__comment_begining__[extension]):
|
||||
return True
|
||||
if __comment_end__.get(extension, None) is not None and string.strip().endswith(__comment_end__[extension]):
|
||||
return True
|
||||
if __comment__.get(extension, None) is not None and string.strip().startswith(__comment__[extension]):
|
||||
return True
|
||||
if __comment_begining__.get(extension, None) is not None and string.strip().startswith(__comment_begining__[extension]):
|
||||
return True
|
||||
if __comment_end__.get(extension, None) is not None and string.strip().endswith(__comment_end__[extension]):
|
||||
return True
|
||||
if __comment__.get(extension, None) is not None and string.strip().startswith(__comment__[extension]):
|
||||
return True
|
||||
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
def handle_comment_block(is_inside_comment, extension, content):
|
||||
comments = 0
|
||||
comments = 0
|
||||
|
||||
if is_comment(extension, content):
|
||||
comments += 1
|
||||
if is_inside_comment:
|
||||
if __has_comment_end__(extension, content):
|
||||
is_inside_comment = False
|
||||
else:
|
||||
comments += 1
|
||||
elif __has_comment_begining__(extension, content) and not __has_comment_end__(extension, content):
|
||||
is_inside_comment = True
|
||||
if is_comment(extension, content):
|
||||
comments += 1
|
||||
if is_inside_comment:
|
||||
if __has_comment_end__(extension, content):
|
||||
is_inside_comment = False
|
||||
else:
|
||||
comments += 1
|
||||
elif __has_comment_begining__(extension, content) and not __has_comment_end__(extension, content):
|
||||
is_inside_comment = True
|
||||
|
||||
return (comments, is_inside_comment)
|
||||
return (comments, is_inside_comment)
|
||||
|
|
|
@ -24,73 +24,73 @@ from . import extensions, filtering, format, interval, optval
|
|||
|
||||
|
||||
class GitConfig(object):
|
||||
def __init__(self, run, repo, global_only=False):
|
||||
self.run = run
|
||||
self.repo = repo
|
||||
self.global_only = global_only
|
||||
def __init__(self, run, repo, global_only=False):
|
||||
self.run = run
|
||||
self.repo = repo
|
||||
self.global_only = global_only
|
||||
|
||||
def __read_git_config__(self, variable):
|
||||
previous_directory = os.getcwd()
|
||||
os.chdir(self.repo)
|
||||
setting = subprocess.Popen(
|
||||
[_f for _f in ["git", "config", "--global" if self.global_only else "", "inspector." + variable] if _f],
|
||||
stdout=subprocess.PIPE,
|
||||
).stdout
|
||||
os.chdir(previous_directory)
|
||||
def __read_git_config__(self, variable):
|
||||
previous_directory = os.getcwd()
|
||||
os.chdir(self.repo)
|
||||
setting = subprocess.Popen(
|
||||
[_f for _f in ["git", "config", "--global" if self.global_only else "", "inspector." + variable] if _f],
|
||||
stdout=subprocess.PIPE,
|
||||
).stdout
|
||||
os.chdir(previous_directory)
|
||||
|
||||
try:
|
||||
setting = setting.readlines()[0]
|
||||
setting = setting.decode("utf-8", "replace").strip()
|
||||
except IndexError:
|
||||
setting = ""
|
||||
try:
|
||||
setting = setting.readlines()[0]
|
||||
setting = setting.decode("utf-8", "replace").strip()
|
||||
except IndexError:
|
||||
setting = ""
|
||||
|
||||
return setting
|
||||
return setting
|
||||
|
||||
def __read_git_config_bool__(self, variable):
|
||||
try:
|
||||
variable = self.__read_git_config__(variable)
|
||||
return optval.get_boolean_argument(False if variable == "" else variable)
|
||||
except optval.InvalidOptionArgument:
|
||||
return False
|
||||
def __read_git_config_bool__(self, variable):
|
||||
try:
|
||||
variable = self.__read_git_config__(variable)
|
||||
return optval.get_boolean_argument(False if variable == "" else variable)
|
||||
except optval.InvalidOptionArgument:
|
||||
return False
|
||||
|
||||
def __read_git_config_string__(self, variable):
|
||||
string = self.__read_git_config__(variable)
|
||||
return (True, string) if len(string) > 0 else (False, None)
|
||||
def __read_git_config_string__(self, variable):
|
||||
string = self.__read_git_config__(variable)
|
||||
return (True, string) if len(string) > 0 else (False, None)
|
||||
|
||||
def read(self):
|
||||
var = self.__read_git_config_string__("file-types")
|
||||
if var[0]:
|
||||
extensions.define(var[1])
|
||||
def read(self):
|
||||
var = self.__read_git_config_string__("file-types")
|
||||
if var[0]:
|
||||
extensions.define(var[1])
|
||||
|
||||
var = self.__read_git_config_string__("exclude")
|
||||
if var[0]:
|
||||
filtering.add(var[1])
|
||||
var = self.__read_git_config_string__("exclude")
|
||||
if var[0]:
|
||||
filtering.add(var[1])
|
||||
|
||||
var = self.__read_git_config_string__("format")
|
||||
if var[0] and not format.select(var[1]):
|
||||
raise format.InvalidFormatError(_("specified output format not supported."))
|
||||
var = self.__read_git_config_string__("format")
|
||||
if var[0] and not format.select(var[1]):
|
||||
raise format.InvalidFormatError(_("specified output format not supported."))
|
||||
|
||||
self.run.hard = self.__read_git_config_bool__("hard")
|
||||
self.run.list_file_types = self.__read_git_config_bool__("list-file-types")
|
||||
self.run.localize_output = self.__read_git_config_bool__("localize-output")
|
||||
self.run.metrics = self.__read_git_config_bool__("metrics")
|
||||
self.run.responsibilities = self.__read_git_config_bool__("responsibilities")
|
||||
self.run.useweeks = self.__read_git_config_bool__("weeks")
|
||||
self.run.hard = self.__read_git_config_bool__("hard")
|
||||
self.run.list_file_types = self.__read_git_config_bool__("list-file-types")
|
||||
self.run.localize_output = self.__read_git_config_bool__("localize-output")
|
||||
self.run.metrics = self.__read_git_config_bool__("metrics")
|
||||
self.run.responsibilities = self.__read_git_config_bool__("responsibilities")
|
||||
self.run.useweeks = self.__read_git_config_bool__("weeks")
|
||||
|
||||
var = self.__read_git_config_string__("since")
|
||||
if var[0]:
|
||||
interval.set_since(var[1])
|
||||
var = self.__read_git_config_string__("since")
|
||||
if var[0]:
|
||||
interval.set_since(var[1])
|
||||
|
||||
var = self.__read_git_config_string__("until")
|
||||
if var[0]:
|
||||
interval.set_until(var[1])
|
||||
var = self.__read_git_config_string__("until")
|
||||
if var[0]:
|
||||
interval.set_until(var[1])
|
||||
|
||||
self.run.timeline = self.__read_git_config_bool__("timeline")
|
||||
self.run.timeline = self.__read_git_config_bool__("timeline")
|
||||
|
||||
if self.__read_git_config_bool__("grading"):
|
||||
self.run.hard = True
|
||||
self.run.list_file_types = True
|
||||
self.run.metrics = True
|
||||
self.run.responsibilities = True
|
||||
self.run.timeline = True
|
||||
self.run.useweeks = True
|
||||
if self.__read_git_config_bool__("grading"):
|
||||
self.run.hard = True
|
||||
self.run.list_file_types = True
|
||||
self.run.metrics = True
|
||||
self.run.responsibilities = True
|
||||
self.run.timeline = True
|
||||
self.run.useweeks = True
|
||||
|
|
|
@ -25,20 +25,20 @@ __located_extensions__ = set()
|
|||
|
||||
|
||||
def get():
|
||||
return __extensions__
|
||||
return __extensions__
|
||||
|
||||
|
||||
def define(string):
|
||||
global __extensions__
|
||||
__extensions__ = string.split(",")
|
||||
global __extensions__
|
||||
__extensions__ = string.split(",")
|
||||
|
||||
|
||||
def add_located(string):
|
||||
if len(string) == 0:
|
||||
__located_extensions__.add("*")
|
||||
else:
|
||||
__located_extensions__.add(string)
|
||||
if len(string) == 0:
|
||||
__located_extensions__.add("*")
|
||||
else:
|
||||
__located_extensions__.add(string)
|
||||
|
||||
|
||||
def get_located():
|
||||
return __located_extensions__
|
||||
return __located_extensions__
|
||||
|
|
|
@ -22,83 +22,83 @@ import re
|
|||
import subprocess
|
||||
|
||||
__filters__ = {
|
||||
"file": [set(), set()],
|
||||
"author": [set(), set()],
|
||||
"email": [set(), set()],
|
||||
"revision": [set(), set()],
|
||||
"message": [set(), None],
|
||||
"file": [set(), set()],
|
||||
"author": [set(), set()],
|
||||
"email": [set(), set()],
|
||||
"revision": [set(), set()],
|
||||
"message": [set(), None],
|
||||
}
|
||||
|
||||
|
||||
class InvalidRegExpError(ValueError):
|
||||
def __init__(self, msg):
|
||||
super(InvalidRegExpError, self).__init__(msg)
|
||||
self.msg = msg
|
||||
def __init__(self, msg):
|
||||
super(InvalidRegExpError, self).__init__(msg)
|
||||
self.msg = msg
|
||||
|
||||
|
||||
def get():
|
||||
return __filters__
|
||||
return __filters__
|
||||
|
||||
|
||||
def __add_one__(string):
|
||||
for i in __filters__:
|
||||
if (i + ":").lower() == string[0 : len(i) + 1].lower():
|
||||
__filters__[i][0].add(string[len(i) + 1 :])
|
||||
return
|
||||
__filters__["file"][0].add(string)
|
||||
for i in __filters__:
|
||||
if (i + ":").lower() == string[0 : len(i) + 1].lower():
|
||||
__filters__[i][0].add(string[len(i) + 1 :])
|
||||
return
|
||||
__filters__["file"][0].add(string)
|
||||
|
||||
|
||||
def add(string):
|
||||
rules = string.split(",")
|
||||
for rule in rules:
|
||||
__add_one__(rule)
|
||||
rules = string.split(",")
|
||||
for rule in rules:
|
||||
__add_one__(rule)
|
||||
|
||||
|
||||
def clear():
|
||||
for i in __filters__:
|
||||
__filters__[i][0] = set()
|
||||
for i in __filters__:
|
||||
__filters__[i][0] = set()
|
||||
|
||||
|
||||
def get_filered(filter_type="file"):
|
||||
return __filters__[filter_type][1]
|
||||
return __filters__[filter_type][1]
|
||||
|
||||
|
||||
def has_filtered():
|
||||
for i in __filters__:
|
||||
if __filters__[i][1]:
|
||||
return True
|
||||
return False
|
||||
for i in __filters__:
|
||||
if __filters__[i][1]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def __find_commit_message__(sha):
|
||||
git_show_r = subprocess.Popen(
|
||||
[_f for _f in ["git", "show", "-s", "--pretty=%B", "-w", sha] if _f], stdout=subprocess.PIPE
|
||||
).stdout
|
||||
git_show_r = subprocess.Popen(
|
||||
[_f for _f in ["git", "show", "-s", "--pretty=%B", "-w", sha] if _f], stdout=subprocess.PIPE
|
||||
).stdout
|
||||
|
||||
commit_message = git_show_r.read()
|
||||
git_show_r.close()
|
||||
commit_message = git_show_r.read()
|
||||
git_show_r.close()
|
||||
|
||||
commit_message = commit_message.strip().decode("unicode_escape", "ignore")
|
||||
commit_message = commit_message.encode("latin-1", "replace")
|
||||
return commit_message.decode("utf-8", "replace")
|
||||
commit_message = commit_message.strip().decode("unicode_escape", "ignore")
|
||||
commit_message = commit_message.encode("latin-1", "replace")
|
||||
return commit_message.decode("utf-8", "replace")
|
||||
|
||||
|
||||
def set_filtered(string, filter_type="file"):
|
||||
string = string.strip()
|
||||
string = string.strip()
|
||||
|
||||
if len(string) > 0:
|
||||
for i in __filters__[filter_type][0]:
|
||||
search_for = string
|
||||
if len(string) > 0:
|
||||
for i in __filters__[filter_type][0]:
|
||||
search_for = string
|
||||
|
||||
if filter_type == "message":
|
||||
search_for = __find_commit_message__(string)
|
||||
try:
|
||||
if re.search(i, search_for) is not None:
|
||||
if filter_type == "message":
|
||||
__add_one__("revision:" + string)
|
||||
else:
|
||||
__filters__[filter_type][1].add(string)
|
||||
return True
|
||||
except:
|
||||
raise InvalidRegExpError(_("invalid regular expression specified"))
|
||||
return False
|
||||
if filter_type == "message":
|
||||
search_for = __find_commit_message__(string)
|
||||
try:
|
||||
if re.search(i, search_for) is not None:
|
||||
if filter_type == "message":
|
||||
__add_one__("revision:" + string)
|
||||
else:
|
||||
__filters__[filter_type][1].add(string)
|
||||
return True
|
||||
except:
|
||||
raise InvalidRegExpError(_("invalid regular expression specified"))
|
||||
return False
|
||||
|
|
|
@ -34,41 +34,41 @@ __selected_format__ = DEFAULT_FORMAT
|
|||
|
||||
|
||||
class InvalidFormatError(Exception):
|
||||
def __init__(self, msg):
|
||||
super(InvalidFormatError, self).__init__(msg)
|
||||
self.msg = msg
|
||||
def __init__(self, msg):
|
||||
super(InvalidFormatError, self).__init__(msg)
|
||||
self.msg = msg
|
||||
|
||||
|
||||
def select(format):
|
||||
global __selected_format__
|
||||
__selected_format__ = format
|
||||
global __selected_format__
|
||||
__selected_format__ = format
|
||||
|
||||
return format in __available_formats__
|
||||
return format in __available_formats__
|
||||
|
||||
|
||||
def get_selected():
|
||||
return __selected_format__
|
||||
return __selected_format__
|
||||
|
||||
|
||||
def is_interactive_format():
|
||||
return __selected_format__ == "text"
|
||||
return __selected_format__ == "text"
|
||||
|
||||
|
||||
def __output_html_template__(name):
|
||||
template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), name)
|
||||
file_r = open(template_path, "rb")
|
||||
template = file_r.read().decode("utf-8", "replace")
|
||||
template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), name)
|
||||
file_r = open(template_path, "rb")
|
||||
template = file_r.read().decode("utf-8", "replace")
|
||||
|
||||
file_r.close()
|
||||
return template
|
||||
file_r.close()
|
||||
return template
|
||||
|
||||
|
||||
def __get_zip_file_content__(name, file_name="/html/flot.zip"):
|
||||
zip_file = zipfile.ZipFile(basedir.get_basedir() + file_name, "r")
|
||||
content = zip_file.read(name)
|
||||
zip_file = zipfile.ZipFile(basedir.get_basedir() + file_name, "r")
|
||||
content = zip_file.read(name)
|
||||
|
||||
zip_file.close()
|
||||
return content.decode("utf-8", "replace")
|
||||
zip_file.close()
|
||||
return content.decode("utf-8", "replace")
|
||||
|
||||
|
||||
INFO_ONE_REPOSITORY = N_("Statistical information for the repository '{0}' was gathered on {1}.")
|
||||
|
@ -76,98 +76,96 @@ INFO_MANY_REPOSITORIES = N_("Statistical information for the repositories '{0}'
|
|||
|
||||
|
||||
def output_header(repos):
|
||||
repos_string = ", ".join([repo.name for repo in repos])
|
||||
repos_string = ", ".join([repo.name for repo in repos])
|
||||
|
||||
if __selected_format__ == "html" or __selected_format__ == "htmlembedded":
|
||||
base = basedir.get_basedir()
|
||||
html_header = __output_html_template__(base + "/html/html.header")
|
||||
tablesorter_js = __get_zip_file_content__("jquery.tablesorter.min.js", "/html/jquery.tablesorter.min.js.zip").encode(
|
||||
"latin-1", "replace"
|
||||
)
|
||||
tablesorter_js = tablesorter_js.decode("utf-8", "ignore")
|
||||
flot_js = __get_zip_file_content__("jquery.flot.js")
|
||||
pie_js = __get_zip_file_content__("jquery.flot.pie.js")
|
||||
resize_js = __get_zip_file_content__("jquery.flot.resize.js")
|
||||
if __selected_format__ == "html" or __selected_format__ == "htmlembedded":
|
||||
base = basedir.get_basedir()
|
||||
html_header = __output_html_template__(base + "/html/html.header")
|
||||
tablesorter_js = __get_zip_file_content__("jquery.tablesorter.min.js", "/html/jquery.tablesorter.min.js.zip").encode(
|
||||
"latin-1", "replace"
|
||||
)
|
||||
tablesorter_js = tablesorter_js.decode("utf-8", "ignore")
|
||||
flot_js = __get_zip_file_content__("jquery.flot.js")
|
||||
pie_js = __get_zip_file_content__("jquery.flot.pie.js")
|
||||
resize_js = __get_zip_file_content__("jquery.flot.resize.js")
|
||||
|
||||
logo_file = open(base + "/html/gitinspector_piclet.png", "rb")
|
||||
logo = logo_file.read()
|
||||
logo_file.close()
|
||||
logo = base64.b64encode(logo)
|
||||
logo_file = open(base + "/html/gitinspector_piclet.png", "rb")
|
||||
logo = logo_file.read()
|
||||
logo_file.close()
|
||||
logo = base64.b64encode(logo)
|
||||
|
||||
if __selected_format__ == "htmlembedded":
|
||||
jquery_js = ">" + __get_zip_file_content__("jquery.js")
|
||||
else:
|
||||
jquery_js = ' src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">'
|
||||
if __selected_format__ == "htmlembedded":
|
||||
jquery_js = ">" + __get_zip_file_content__("jquery.js")
|
||||
else:
|
||||
jquery_js = ' src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">'
|
||||
|
||||
print(
|
||||
html_header.format(
|
||||
title=_("Repository statistics for '{0}'").format(repos_string),
|
||||
jquery=jquery_js,
|
||||
jquery_tablesorter=tablesorter_js,
|
||||
jquery_flot=flot_js,
|
||||
jquery_flot_pie=pie_js,
|
||||
jquery_flot_resize=resize_js,
|
||||
logo=logo.decode("utf-8", "replace"),
|
||||
logo_text=_(
|
||||
"The output has been generated by {0} {1}. The statistical analysis tool" " for git repositories."
|
||||
).format('<a href="https://github.com/ejwa/gitinspector">gitinspector</a>', version.__version__),
|
||||
repo_text=_(INFO_ONE_REPOSITORY if len(repos) <= 1 else INFO_MANY_REPOSITORIES).format(
|
||||
repos_string, localization.get_date()
|
||||
),
|
||||
show_minor_authors=_("Show minor authors"),
|
||||
hide_minor_authors=_("Hide minor authors"),
|
||||
show_minor_rows=_("Show rows with minor work"),
|
||||
hide_minor_rows=_("Hide rows with minor work"),
|
||||
)
|
||||
)
|
||||
elif __selected_format__ == "json":
|
||||
print('{\n\t"gitinspector": {')
|
||||
print('\t\t"version": "' + version.__version__ + '",')
|
||||
print(
|
||||
html_header.format(
|
||||
title=_("Repository statistics for '{0}'").format(repos_string),
|
||||
jquery=jquery_js,
|
||||
jquery_tablesorter=tablesorter_js,
|
||||
jquery_flot=flot_js,
|
||||
jquery_flot_pie=pie_js,
|
||||
jquery_flot_resize=resize_js,
|
||||
logo=logo.decode("utf-8", "replace"),
|
||||
logo_text=_("The output has been generated by {0} {1}. The statistical analysis tool" " for git repositories.").format(
|
||||
'<a href="https://github.com/ejwa/gitinspector">gitinspector</a>', version.__version__
|
||||
),
|
||||
repo_text=_(INFO_ONE_REPOSITORY if len(repos) <= 1 else INFO_MANY_REPOSITORIES).format(
|
||||
repos_string, localization.get_date()
|
||||
),
|
||||
show_minor_authors=_("Show minor authors"),
|
||||
hide_minor_authors=_("Hide minor authors"),
|
||||
show_minor_rows=_("Show rows with minor work"),
|
||||
hide_minor_rows=_("Hide rows with minor work"),
|
||||
)
|
||||
)
|
||||
elif __selected_format__ == "json":
|
||||
print('{\n\t"gitinspector": {')
|
||||
print('\t\t"version": "' + version.__version__ + '",')
|
||||
|
||||
if len(repos) <= 1:
|
||||
print('\t\t"repository": "' + repos_string + '",')
|
||||
else:
|
||||
repos_json = '\t\t"repositories": [ '
|
||||
if len(repos) <= 1:
|
||||
print('\t\t"repository": "' + repos_string + '",')
|
||||
else:
|
||||
repos_json = '\t\t"repositories": [ '
|
||||
|
||||
for repo in repos:
|
||||
repos_json += '"' + repo.name + '", '
|
||||
for repo in repos:
|
||||
repos_json += '"' + repo.name + '", '
|
||||
|
||||
print(repos_json[:-2] + " ],")
|
||||
print(repos_json[:-2] + " ],")
|
||||
|
||||
print('\t\t"report_date": "' + time.strftime("%Y/%m/%d") + '",')
|
||||
print('\t\t"report_date": "' + time.strftime("%Y/%m/%d") + '",')
|
||||
|
||||
elif __selected_format__ == "xml":
|
||||
print("<gitinspector>")
|
||||
print("\t<version>" + version.__version__ + "</version>")
|
||||
elif __selected_format__ == "xml":
|
||||
print("<gitinspector>")
|
||||
print("\t<version>" + version.__version__ + "</version>")
|
||||
|
||||
if len(repos) <= 1:
|
||||
print("\t<repository>" + repos_string + "</repository>")
|
||||
else:
|
||||
print("\t<repositories>")
|
||||
if len(repos) <= 1:
|
||||
print("\t<repository>" + repos_string + "</repository>")
|
||||
else:
|
||||
print("\t<repositories>")
|
||||
|
||||
for repo in repos:
|
||||
print("\t\t<repository>" + repo.name + "</repository>")
|
||||
for repo in repos:
|
||||
print("\t\t<repository>" + repo.name + "</repository>")
|
||||
|
||||
print("\t</repositories>")
|
||||
print("\t</repositories>")
|
||||
|
||||
print("\t<report-date>" + time.strftime("%Y/%m/%d") + "</report-date>")
|
||||
else:
|
||||
print(
|
||||
textwrap.fill(
|
||||
_(INFO_ONE_REPOSITORY if len(repos) <= 1 else INFO_MANY_REPOSITORIES).format(
|
||||
repos_string, localization.get_date()
|
||||
),
|
||||
width=terminal.get_size()[0],
|
||||
)
|
||||
)
|
||||
print("\t<report-date>" + time.strftime("%Y/%m/%d") + "</report-date>")
|
||||
else:
|
||||
print(
|
||||
textwrap.fill(
|
||||
_(INFO_ONE_REPOSITORY if len(repos) <= 1 else INFO_MANY_REPOSITORIES).format(repos_string, localization.get_date()),
|
||||
width=terminal.get_size()[0],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def output_footer():
|
||||
if __selected_format__ == "html" or __selected_format__ == "htmlembedded":
|
||||
base = basedir.get_basedir()
|
||||
html_footer = __output_html_template__(base + "/html/html.footer")
|
||||
print(html_footer)
|
||||
elif __selected_format__ == "json":
|
||||
print("\n\t}\n}")
|
||||
elif __selected_format__ == "xml":
|
||||
print("</gitinspector>")
|
||||
if __selected_format__ == "html" or __selected_format__ == "htmlembedded":
|
||||
base = basedir.get_basedir()
|
||||
html_footer = __output_html_template__(base + "/html/html.footer")
|
||||
print(html_footer)
|
||||
elif __selected_format__ == "json":
|
||||
print("\n\t}\n}")
|
||||
elif __selected_format__ == "xml":
|
||||
print("</gitinspector>")
|
||||
|
|
|
@ -40,200 +40,200 @@ localization.init()
|
|||
|
||||
|
||||
class Runner(object):
|
||||
def __init__(self):
|
||||
self.hard = False
|
||||
self.include_metrics = False
|
||||
self.list_file_types = False
|
||||
self.localize_output = False
|
||||
self.responsibilities = False
|
||||
self.grading = False
|
||||
self.timeline = False
|
||||
self.useweeks = False
|
||||
def __init__(self):
|
||||
self.hard = False
|
||||
self.include_metrics = False
|
||||
self.list_file_types = False
|
||||
self.localize_output = False
|
||||
self.responsibilities = False
|
||||
self.grading = False
|
||||
self.timeline = False
|
||||
self.useweeks = False
|
||||
|
||||
def process(self, repos):
|
||||
localization.check_compatibility(version.__version__)
|
||||
def process(self, repos):
|
||||
localization.check_compatibility(version.__version__)
|
||||
|
||||
if not self.localize_output:
|
||||
localization.disable()
|
||||
if not self.localize_output:
|
||||
localization.disable()
|
||||
|
||||
terminal.skip_escapes(not sys.stdout.isatty())
|
||||
terminal.set_stdout_encoding()
|
||||
previous_directory = os.getcwd()
|
||||
summed_blames = Blame.__new__(Blame)
|
||||
summed_changes = Changes.__new__(Changes)
|
||||
summed_metrics = MetricsLogic.__new__(MetricsLogic)
|
||||
terminal.skip_escapes(not sys.stdout.isatty())
|
||||
terminal.set_stdout_encoding()
|
||||
previous_directory = os.getcwd()
|
||||
summed_blames = Blame.__new__(Blame)
|
||||
summed_changes = Changes.__new__(Changes)
|
||||
summed_metrics = MetricsLogic.__new__(MetricsLogic)
|
||||
|
||||
for repo in repos:
|
||||
os.chdir(repo.location)
|
||||
repo = repo if len(repos) > 1 else None
|
||||
changes = Changes(repo, self.hard)
|
||||
summed_blames += Blame(repo, self.hard, self.useweeks, changes)
|
||||
summed_changes += changes
|
||||
for repo in repos:
|
||||
os.chdir(repo.location)
|
||||
repo = repo if len(repos) > 1 else None
|
||||
changes = Changes(repo, self.hard)
|
||||
summed_blames += Blame(repo, self.hard, self.useweeks, changes)
|
||||
summed_changes += changes
|
||||
|
||||
if self.include_metrics:
|
||||
summed_metrics += MetricsLogic()
|
||||
if self.include_metrics:
|
||||
summed_metrics += MetricsLogic()
|
||||
|
||||
if sys.stdout.isatty() and format.is_interactive_format():
|
||||
terminal.clear_row()
|
||||
else:
|
||||
os.chdir(previous_directory)
|
||||
if sys.stdout.isatty() and format.is_interactive_format():
|
||||
terminal.clear_row()
|
||||
else:
|
||||
os.chdir(previous_directory)
|
||||
|
||||
format.output_header(repos)
|
||||
outputable.output(ChangesOutput(summed_changes))
|
||||
format.output_header(repos)
|
||||
outputable.output(ChangesOutput(summed_changes))
|
||||
|
||||
if summed_changes.get_commits():
|
||||
outputable.output(BlameOutput(summed_changes, summed_blames))
|
||||
if summed_changes.get_commits():
|
||||
outputable.output(BlameOutput(summed_changes, summed_blames))
|
||||
|
||||
if self.timeline:
|
||||
outputable.output(TimelineOutput(summed_changes, self.useweeks))
|
||||
if self.timeline:
|
||||
outputable.output(TimelineOutput(summed_changes, self.useweeks))
|
||||
|
||||
if self.include_metrics:
|
||||
outputable.output(MetricsOutput(summed_metrics))
|
||||
if self.include_metrics:
|
||||
outputable.output(MetricsOutput(summed_metrics))
|
||||
|
||||
if self.responsibilities:
|
||||
outputable.output(ResponsibilitiesOutput(summed_changes, summed_blames))
|
||||
if self.responsibilities:
|
||||
outputable.output(ResponsibilitiesOutput(summed_changes, summed_blames))
|
||||
|
||||
outputable.output(FilteringOutput())
|
||||
outputable.output(FilteringOutput())
|
||||
|
||||
if self.list_file_types:
|
||||
outputable.output(ExtensionsOutput())
|
||||
if self.list_file_types:
|
||||
outputable.output(ExtensionsOutput())
|
||||
|
||||
format.output_footer()
|
||||
os.chdir(previous_directory)
|
||||
format.output_footer()
|
||||
os.chdir(previous_directory)
|
||||
|
||||
|
||||
def __check_python_version__():
|
||||
if sys.version_info < (3, 6):
|
||||
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])
|
||||
sys.exit(_("gitinspector requires at least Python 3.6 to run (version {0} was found).").format(python_version))
|
||||
if sys.version_info < (3, 6):
|
||||
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])
|
||||
sys.exit(_("gitinspector requires at least Python 3.6 to run (version {0} was found).").format(python_version))
|
||||
|
||||
|
||||
def __get_validated_git_repos__(repos_relative):
|
||||
if not repos_relative:
|
||||
repos_relative = "."
|
||||
if not repos_relative:
|
||||
repos_relative = "."
|
||||
|
||||
repos = []
|
||||
repos = []
|
||||
|
||||
# Try to clone the repos or return the same directory and bail out.
|
||||
for repo in repos_relative:
|
||||
cloned_repo = clone.create(repo)
|
||||
# Try to clone the repos or return the same directory and bail out.
|
||||
for repo in repos_relative:
|
||||
cloned_repo = clone.create(repo)
|
||||
|
||||
if cloned_repo.name is None:
|
||||
cloned_repo.location = basedir.get_basedir_git(cloned_repo.location)
|
||||
cloned_repo.name = os.path.basename(cloned_repo.location)
|
||||
if cloned_repo.name is None:
|
||||
cloned_repo.location = basedir.get_basedir_git(cloned_repo.location)
|
||||
cloned_repo.name = os.path.basename(cloned_repo.location)
|
||||
|
||||
repos.append(cloned_repo)
|
||||
repos.append(cloned_repo)
|
||||
|
||||
return repos
|
||||
return repos
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
terminal.check_terminal_encoding()
|
||||
terminal.set_stdin_encoding()
|
||||
argv = terminal.convert_command_line_to_utf8() if argv is None else argv
|
||||
run = Runner()
|
||||
repos = []
|
||||
terminal.check_terminal_encoding()
|
||||
terminal.set_stdin_encoding()
|
||||
argv = terminal.convert_command_line_to_utf8() if argv is None else argv
|
||||
run = Runner()
|
||||
repos = []
|
||||
|
||||
try:
|
||||
opts, args = optval.gnu_getopt(
|
||||
argv[1:],
|
||||
"f:F:hHlLmrTwx:",
|
||||
[
|
||||
"exclude=",
|
||||
"file-types=",
|
||||
"format=",
|
||||
"hard:true",
|
||||
"help",
|
||||
"list-file-types:true",
|
||||
"localize-output:true",
|
||||
"metrics:true",
|
||||
"responsibilities:true",
|
||||
"since=",
|
||||
"grading:true",
|
||||
"timeline:true",
|
||||
"until=",
|
||||
"version",
|
||||
"weeks:true",
|
||||
],
|
||||
)
|
||||
repos = __get_validated_git_repos__(set(args))
|
||||
try:
|
||||
opts, args = optval.gnu_getopt(
|
||||
argv[1:],
|
||||
"f:F:hHlLmrTwx:",
|
||||
[
|
||||
"exclude=",
|
||||
"file-types=",
|
||||
"format=",
|
||||
"hard:true",
|
||||
"help",
|
||||
"list-file-types:true",
|
||||
"localize-output:true",
|
||||
"metrics:true",
|
||||
"responsibilities:true",
|
||||
"since=",
|
||||
"grading:true",
|
||||
"timeline:true",
|
||||
"until=",
|
||||
"version",
|
||||
"weeks:true",
|
||||
],
|
||||
)
|
||||
repos = __get_validated_git_repos__(set(args))
|
||||
|
||||
# We need the repos above to be set before we read the git config.
|
||||
GitConfig(run, repos[-1].location).read()
|
||||
clear_x_on_next_pass = True
|
||||
# We need the repos above to be set before we read the git config.
|
||||
GitConfig(run, repos[-1].location).read()
|
||||
clear_x_on_next_pass = True
|
||||
|
||||
for o, a in opts:
|
||||
if o in ("-h", "--help"):
|
||||
help.output()
|
||||
sys.exit(0)
|
||||
elif o in ("-f", "--file-types"):
|
||||
extensions.define(a)
|
||||
elif o in ("-F", "--format"):
|
||||
if not format.select(a):
|
||||
raise format.InvalidFormatError(_("specified output format not supported."))
|
||||
elif o == "-H":
|
||||
run.hard = True
|
||||
elif o == "--hard":
|
||||
run.hard = optval.get_boolean_argument(a)
|
||||
elif o == "-l":
|
||||
run.list_file_types = True
|
||||
elif o == "--list-file-types":
|
||||
run.list_file_types = optval.get_boolean_argument(a)
|
||||
elif o == "-L":
|
||||
run.localize_output = True
|
||||
elif o == "--localize-output":
|
||||
run.localize_output = optval.get_boolean_argument(a)
|
||||
elif o == "-m":
|
||||
run.include_metrics = True
|
||||
elif o == "--metrics":
|
||||
run.include_metrics = optval.get_boolean_argument(a)
|
||||
elif o == "-r":
|
||||
run.responsibilities = True
|
||||
elif o == "--responsibilities":
|
||||
run.responsibilities = optval.get_boolean_argument(a)
|
||||
elif o == "--since":
|
||||
interval.set_since(a)
|
||||
elif o == "--version":
|
||||
version.output()
|
||||
sys.exit(0)
|
||||
elif o == "--grading":
|
||||
grading = optval.get_boolean_argument(a)
|
||||
run.include_metrics = grading
|
||||
run.list_file_types = grading
|
||||
run.responsibilities = grading
|
||||
run.grading = grading
|
||||
run.hard = grading
|
||||
run.timeline = grading
|
||||
run.useweeks = grading
|
||||
elif o == "-T":
|
||||
run.timeline = True
|
||||
elif o == "--timeline":
|
||||
run.timeline = optval.get_boolean_argument(a)
|
||||
elif o == "--until":
|
||||
interval.set_until(a)
|
||||
elif o == "-w":
|
||||
run.useweeks = True
|
||||
elif o == "--weeks":
|
||||
run.useweeks = optval.get_boolean_argument(a)
|
||||
elif o in ("-x", "--exclude"):
|
||||
if clear_x_on_next_pass:
|
||||
clear_x_on_next_pass = False
|
||||
filtering.clear()
|
||||
filtering.add(a)
|
||||
for o, a in opts:
|
||||
if o in ("-h", "--help"):
|
||||
help.output()
|
||||
sys.exit(0)
|
||||
elif o in ("-f", "--file-types"):
|
||||
extensions.define(a)
|
||||
elif o in ("-F", "--format"):
|
||||
if not format.select(a):
|
||||
raise format.InvalidFormatError(_("specified output format not supported."))
|
||||
elif o == "-H":
|
||||
run.hard = True
|
||||
elif o == "--hard":
|
||||
run.hard = optval.get_boolean_argument(a)
|
||||
elif o == "-l":
|
||||
run.list_file_types = True
|
||||
elif o == "--list-file-types":
|
||||
run.list_file_types = optval.get_boolean_argument(a)
|
||||
elif o == "-L":
|
||||
run.localize_output = True
|
||||
elif o == "--localize-output":
|
||||
run.localize_output = optval.get_boolean_argument(a)
|
||||
elif o == "-m":
|
||||
run.include_metrics = True
|
||||
elif o == "--metrics":
|
||||
run.include_metrics = optval.get_boolean_argument(a)
|
||||
elif o == "-r":
|
||||
run.responsibilities = True
|
||||
elif o == "--responsibilities":
|
||||
run.responsibilities = optval.get_boolean_argument(a)
|
||||
elif o == "--since":
|
||||
interval.set_since(a)
|
||||
elif o == "--version":
|
||||
version.output()
|
||||
sys.exit(0)
|
||||
elif o == "--grading":
|
||||
grading = optval.get_boolean_argument(a)
|
||||
run.include_metrics = grading
|
||||
run.list_file_types = grading
|
||||
run.responsibilities = grading
|
||||
run.grading = grading
|
||||
run.hard = grading
|
||||
run.timeline = grading
|
||||
run.useweeks = grading
|
||||
elif o == "-T":
|
||||
run.timeline = True
|
||||
elif o == "--timeline":
|
||||
run.timeline = optval.get_boolean_argument(a)
|
||||
elif o == "--until":
|
||||
interval.set_until(a)
|
||||
elif o == "-w":
|
||||
run.useweeks = True
|
||||
elif o == "--weeks":
|
||||
run.useweeks = optval.get_boolean_argument(a)
|
||||
elif o in ("-x", "--exclude"):
|
||||
if clear_x_on_next_pass:
|
||||
clear_x_on_next_pass = False
|
||||
filtering.clear()
|
||||
filtering.add(a)
|
||||
|
||||
__check_python_version__()
|
||||
run.process(repos)
|
||||
__check_python_version__()
|
||||
run.process(repos)
|
||||
|
||||
except (filtering.InvalidRegExpError, format.InvalidFormatError, optval.InvalidOptionArgument, getopt.error) as exception:
|
||||
print(sys.argv[0], "\b:", exception.msg, file=sys.stderr)
|
||||
print(_("Try `{0} --help' for more information.").format(sys.argv[0]), file=sys.stderr)
|
||||
sys.exit(2)
|
||||
except (filtering.InvalidRegExpError, format.InvalidFormatError, optval.InvalidOptionArgument, getopt.error) as exception:
|
||||
print(sys.argv[0], "\b:", exception.msg, file=sys.stderr)
|
||||
print(_("Try `{0} --help' for more information.").format(sys.argv[0]), file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
@atexit.register
|
||||
def cleanup():
|
||||
clone.delete()
|
||||
clone.delete()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
|
|
@ -21,21 +21,21 @@
|
|||
import hashlib
|
||||
|
||||
try:
|
||||
from urllib.parse import urlencode
|
||||
from urllib.parse import urlencode
|
||||
except:
|
||||
from urllib.parse import urlencode
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from . import format
|
||||
|
||||
|
||||
def get_url(email, size=20):
|
||||
md5hash = hashlib.md5(email.encode("utf-8").lower().strip()).hexdigest()
|
||||
base_url = "https://www.gravatar.com/avatar/" + md5hash
|
||||
params = None
|
||||
md5hash = hashlib.md5(email.encode("utf-8").lower().strip()).hexdigest()
|
||||
base_url = "https://www.gravatar.com/avatar/" + md5hash
|
||||
params = None
|
||||
|
||||
if format.get_selected() == "html":
|
||||
params = {"default": "identicon", "size": size}
|
||||
elif format.get_selected() == "xml" or format.get_selected() == "json":
|
||||
params = {"default": "identicon"}
|
||||
if format.get_selected() == "html":
|
||||
params = {"default": "identicon", "size": size}
|
||||
elif format.get_selected() == "xml" or format.get_selected() == "json":
|
||||
params = {"default": "identicon"}
|
||||
|
||||
return base_url + "?" + urlencode(params)
|
||||
return base_url + "?" + urlencode(params)
|
||||
|
|
|
@ -24,7 +24,7 @@ from .format import __available_formats__
|
|||
|
||||
|
||||
__doc__ = _(
|
||||
"""Usage: {0} [OPTION]... [REPOSITORY]...
|
||||
"""Usage: {0} [OPTION]... [REPOSITORY]...
|
||||
List information about the repository in REPOSITORY. If no repository is
|
||||
specified, the current directory is used. If multiple repositories are
|
||||
given, information will be merged into a unified statistical report.
|
||||
|
@ -81,4 +81,4 @@ Report gitinspector bugs to gitinspector@ejwa.se."""
|
|||
|
||||
|
||||
def output():
|
||||
print(__doc__.format(sys.argv[0], ",".join(DEFAULT_EXTENSIONS), ",".join(__available_formats__)))
|
||||
print(__doc__.format(sys.argv[0], ",".join(DEFAULT_EXTENSIONS), ",".join(__available_formats__)))
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
|
||||
|
||||
try:
|
||||
from shlex import quote
|
||||
from shlex import quote
|
||||
except ImportError:
|
||||
from pipes import quote
|
||||
from pipes import quote
|
||||
|
||||
__since__ = ""
|
||||
|
||||
|
@ -31,31 +31,31 @@ __ref__ = "HEAD"
|
|||
|
||||
|
||||
def has_interval():
|
||||
return __since__ + __until__ != ""
|
||||
return __since__ + __until__ != ""
|
||||
|
||||
|
||||
def get_since():
|
||||
return __since__
|
||||
return __since__
|
||||
|
||||
|
||||
def set_since(since):
|
||||
global __since__
|
||||
__since__ = "--since=" + quote(since)
|
||||
global __since__
|
||||
__since__ = "--since=" + quote(since)
|
||||
|
||||
|
||||
def get_until():
|
||||
return __until__
|
||||
return __until__
|
||||
|
||||
|
||||
def set_until(until):
|
||||
global __until__
|
||||
__until__ = "--until=" + quote(until)
|
||||
global __until__
|
||||
__until__ = "--until=" + quote(until)
|
||||
|
||||
|
||||
def get_ref():
|
||||
return __ref__
|
||||
return __ref__
|
||||
|
||||
|
||||
def set_ref(ref):
|
||||
global __ref__
|
||||
__ref__ = ref
|
||||
global __ref__
|
||||
__ref__ = ref
|
||||
|
|
|
@ -33,81 +33,81 @@ __translation__ = None
|
|||
|
||||
# Dummy function used to handle string constants
|
||||
def N_(message):
|
||||
return message
|
||||
return message
|
||||
|
||||
|
||||
def init():
|
||||
global __enabled__
|
||||
global __installed__
|
||||
global __translation__
|
||||
global __enabled__
|
||||
global __installed__
|
||||
global __translation__
|
||||
|
||||
if not __installed__:
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
except locale.Error:
|
||||
__translation__ = gettext.NullTranslations()
|
||||
else:
|
||||
lang = locale.getlocale()
|
||||
if not __installed__:
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
except locale.Error:
|
||||
__translation__ = gettext.NullTranslations()
|
||||
else:
|
||||
lang = locale.getlocale()
|
||||
|
||||
# Fix for non-POSIX-compliant systems (Windows et al.).
|
||||
if os.getenv("LANG") is None:
|
||||
lang = locale.getdefaultlocale()
|
||||
# Fix for non-POSIX-compliant systems (Windows et al.).
|
||||
if os.getenv("LANG") is None:
|
||||
lang = locale.getdefaultlocale()
|
||||
|
||||
if lang[0]:
|
||||
os.environ["LANG"] = lang[0]
|
||||
if lang[0]:
|
||||
os.environ["LANG"] = lang[0]
|
||||
|
||||
if lang[0] is not None:
|
||||
filename = basedir.get_basedir() + "/translations/messages_%s.mo" % lang[0][0:2]
|
||||
if lang[0] is not None:
|
||||
filename = basedir.get_basedir() + "/translations/messages_%s.mo" % lang[0][0:2]
|
||||
|
||||
try:
|
||||
__translation__ = gettext.GNUTranslations(open(filename, "rb"))
|
||||
except IOError:
|
||||
__translation__ = gettext.NullTranslations()
|
||||
else:
|
||||
print("WARNING: Localization disabled because the system language could not be determined.", file=sys.stderr)
|
||||
__translation__ = gettext.NullTranslations()
|
||||
try:
|
||||
__translation__ = gettext.GNUTranslations(open(filename, "rb"))
|
||||
except IOError:
|
||||
__translation__ = gettext.NullTranslations()
|
||||
else:
|
||||
print("WARNING: Localization disabled because the system language could not be determined.", file=sys.stderr)
|
||||
__translation__ = gettext.NullTranslations()
|
||||
|
||||
__enabled__ = True
|
||||
__installed__ = True
|
||||
__translation__.install()
|
||||
__enabled__ = True
|
||||
__installed__ = True
|
||||
__translation__.install()
|
||||
|
||||
|
||||
def check_compatibility(version):
|
||||
if isinstance(__translation__, gettext.GNUTranslations):
|
||||
header_pattern = re.compile("^([^:\n]+): *(.*?) *$", re.MULTILINE)
|
||||
header_entries = dict(header_pattern.findall(_("")))
|
||||
if isinstance(__translation__, gettext.GNUTranslations):
|
||||
header_pattern = re.compile("^([^:\n]+): *(.*?) *$", re.MULTILINE)
|
||||
header_entries = dict(header_pattern.findall(_("")))
|
||||
|
||||
if header_entries["Project-Id-Version"] != "gitinspector {0}".format(version):
|
||||
print(
|
||||
"WARNING: The translation for your system locale is not up to date with the current gitinspector "
|
||||
"version. The current maintainer of this locale is {0}.".format(header_entries["Last-Translator"]),
|
||||
file=sys.stderr,
|
||||
)
|
||||
if header_entries["Project-Id-Version"] != "gitinspector {0}".format(version):
|
||||
print(
|
||||
"WARNING: The translation for your system locale is not up to date with the current gitinspector "
|
||||
"version. The current maintainer of this locale is {0}.".format(header_entries["Last-Translator"]),
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
|
||||
def get_date():
|
||||
if __enabled__ and isinstance(__translation__, gettext.GNUTranslations):
|
||||
date = time.strftime("%x")
|
||||
if __enabled__ and isinstance(__translation__, gettext.GNUTranslations):
|
||||
date = time.strftime("%x")
|
||||
|
||||
if hasattr(date, "decode"):
|
||||
date = date.decode("utf-8", "replace")
|
||||
if hasattr(date, "decode"):
|
||||
date = date.decode("utf-8", "replace")
|
||||
|
||||
return date
|
||||
else:
|
||||
return time.strftime("%Y/%m/%d")
|
||||
return date
|
||||
else:
|
||||
return time.strftime("%Y/%m/%d")
|
||||
|
||||
|
||||
def enable():
|
||||
if isinstance(__translation__, gettext.GNUTranslations):
|
||||
__translation__.install(True)
|
||||
if isinstance(__translation__, gettext.GNUTranslations):
|
||||
__translation__.install(True)
|
||||
|
||||
global __enabled__
|
||||
__enabled__ = True
|
||||
global __enabled__
|
||||
__enabled__ = True
|
||||
|
||||
|
||||
def disable():
|
||||
global __enabled__
|
||||
__enabled__ = False
|
||||
global __enabled__
|
||||
__enabled__ = False
|
||||
|
||||
if __installed__:
|
||||
gettext.NullTranslations().install()
|
||||
if __installed__:
|
||||
gettext.NullTranslations().install()
|
||||
|
|
|
@ -24,46 +24,46 @@ from .changes import FileDiff
|
|||
from . import comment, filtering, interval
|
||||
|
||||
__metric_eloc__ = {
|
||||
"java": 500,
|
||||
"c": 500,
|
||||
"cpp": 500,
|
||||
"cs": 500,
|
||||
"h": 300,
|
||||
"hpp": 300,
|
||||
"php": 500,
|
||||
"py": 500,
|
||||
"glsl": 1000,
|
||||
"rb": 500,
|
||||
"js": 500,
|
||||
"sql": 1000,
|
||||
"xml": 1000,
|
||||
"java": 500,
|
||||
"c": 500,
|
||||
"cpp": 500,
|
||||
"cs": 500,
|
||||
"h": 300,
|
||||
"hpp": 300,
|
||||
"php": 500,
|
||||
"py": 500,
|
||||
"glsl": 1000,
|
||||
"rb": 500,
|
||||
"js": 500,
|
||||
"sql": 1000,
|
||||
"xml": 1000,
|
||||
}
|
||||
|
||||
__metric_cc_tokens__ = [
|
||||
[
|
||||
["java", "js", "c", "cc", "cpp"],
|
||||
["else", r"for\s+\(.*\)", r"if\s+\(.*\)", r"case\s+\w+:", "default:", r"while\s+\(.*\)"],
|
||||
["assert", "break", "continue", "return"],
|
||||
],
|
||||
[
|
||||
["cs"],
|
||||
[
|
||||
"else",
|
||||
r"for\s+\(.*\)",
|
||||
r"foreach\s+\(.*\)",
|
||||
r"goto\s+\w+:",
|
||||
r"if\s+\(.*\)",
|
||||
r"case\s+\w+:",
|
||||
"default:",
|
||||
r"while\s+\(.*\)",
|
||||
],
|
||||
["assert", "break", "continue", "return"],
|
||||
],
|
||||
[
|
||||
["py"],
|
||||
[r"^\s+elif .*:$", r"^\s+else:$", r"^\s+for .*:", r"^\s+if .*:$", r"^\s+while .*:$"],
|
||||
[r"^\s+assert", "break", "continue", "return"],
|
||||
],
|
||||
[
|
||||
["java", "js", "c", "cc", "cpp"],
|
||||
["else", r"for\s+\(.*\)", r"if\s+\(.*\)", r"case\s+\w+:", "default:", r"while\s+\(.*\)"],
|
||||
["assert", "break", "continue", "return"],
|
||||
],
|
||||
[
|
||||
["cs"],
|
||||
[
|
||||
"else",
|
||||
r"for\s+\(.*\)",
|
||||
r"foreach\s+\(.*\)",
|
||||
r"goto\s+\w+:",
|
||||
r"if\s+\(.*\)",
|
||||
r"case\s+\w+:",
|
||||
"default:",
|
||||
r"while\s+\(.*\)",
|
||||
],
|
||||
["assert", "break", "continue", "return"],
|
||||
],
|
||||
[
|
||||
["py"],
|
||||
[r"^\s+elif .*:$", r"^\s+else:$", r"^\s+for .*:", r"^\s+if .*:$", r"^\s+while .*:$"],
|
||||
[r"^\s+assert", "break", "continue", "return"],
|
||||
],
|
||||
]
|
||||
|
||||
METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD = 50
|
||||
|
@ -71,89 +71,89 @@ METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD = 0.75
|
|||
|
||||
|
||||
class MetricsLogic(object):
|
||||
def __init__(self):
|
||||
self.eloc = {}
|
||||
self.cyclomatic_complexity = {}
|
||||
self.cyclomatic_complexity_density = {}
|
||||
def __init__(self):
|
||||
self.eloc = {}
|
||||
self.cyclomatic_complexity = {}
|
||||
self.cyclomatic_complexity_density = {}
|
||||
|
||||
ls_tree_p = subprocess.Popen(
|
||||
["git", "ls-tree", "--name-only", "-r", interval.get_ref()], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
lines = ls_tree_p.communicate()[0].splitlines()
|
||||
ls_tree_p.stdout.close()
|
||||
ls_tree_p = subprocess.Popen(
|
||||
["git", "ls-tree", "--name-only", "-r", interval.get_ref()], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
lines = ls_tree_p.communicate()[0].splitlines()
|
||||
ls_tree_p.stdout.close()
|
||||
|
||||
if ls_tree_p.returncode == 0:
|
||||
for i in lines:
|
||||
i = i.strip().decode("unicode_escape", "ignore")
|
||||
i = i.encode("latin-1", "replace")
|
||||
i = i.decode("utf-8", "replace").strip('"').strip("'").strip()
|
||||
if ls_tree_p.returncode == 0:
|
||||
for i in lines:
|
||||
i = i.strip().decode("unicode_escape", "ignore")
|
||||
i = i.encode("latin-1", "replace")
|
||||
i = i.decode("utf-8", "replace").strip('"').strip("'").strip()
|
||||
|
||||
if FileDiff.is_valid_extension(i) and not filtering.set_filtered(FileDiff.get_filename(i)):
|
||||
file_r = subprocess.Popen(
|
||||
["git", "show", interval.get_ref() + ":{0}".format(i.strip())], stdout=subprocess.PIPE
|
||||
).stdout.readlines()
|
||||
if FileDiff.is_valid_extension(i) and not filtering.set_filtered(FileDiff.get_filename(i)):
|
||||
file_r = subprocess.Popen(
|
||||
["git", "show", interval.get_ref() + ":{0}".format(i.strip())], stdout=subprocess.PIPE
|
||||
).stdout.readlines()
|
||||
|
||||
extension = FileDiff.get_extension(i)
|
||||
lines = MetricsLogic.get_eloc(file_r, extension)
|
||||
cycc = MetricsLogic.get_cyclomatic_complexity(file_r, extension)
|
||||
extension = FileDiff.get_extension(i)
|
||||
lines = MetricsLogic.get_eloc(file_r, extension)
|
||||
cycc = MetricsLogic.get_cyclomatic_complexity(file_r, extension)
|
||||
|
||||
if __metric_eloc__.get(extension, None) is not None and __metric_eloc__[extension] < lines:
|
||||
self.eloc[i.strip()] = lines
|
||||
if __metric_eloc__.get(extension, None) is not None and __metric_eloc__[extension] < lines:
|
||||
self.eloc[i.strip()] = lines
|
||||
|
||||
if METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD < cycc:
|
||||
self.cyclomatic_complexity[i.strip()] = cycc
|
||||
if METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD < cycc:
|
||||
self.cyclomatic_complexity[i.strip()] = cycc
|
||||
|
||||
if lines > 0 and METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD < cycc / float(lines):
|
||||
self.cyclomatic_complexity_density[i.strip()] = cycc / float(lines)
|
||||
if lines > 0 and METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD < cycc / float(lines):
|
||||
self.cyclomatic_complexity_density[i.strip()] = cycc / float(lines)
|
||||
|
||||
def __iadd__(self, other):
|
||||
try:
|
||||
self.eloc.update(other.eloc)
|
||||
self.cyclomatic_complexity.update(other.cyclomatic_complexity)
|
||||
self.cyclomatic_complexity_density.update(other.cyclomatic_complexity_density)
|
||||
return self
|
||||
except AttributeError:
|
||||
return other
|
||||
def __iadd__(self, other):
|
||||
try:
|
||||
self.eloc.update(other.eloc)
|
||||
self.cyclomatic_complexity.update(other.cyclomatic_complexity)
|
||||
self.cyclomatic_complexity_density.update(other.cyclomatic_complexity_density)
|
||||
return self
|
||||
except AttributeError:
|
||||
return other
|
||||
|
||||
@staticmethod
|
||||
def get_cyclomatic_complexity(file_r, extension):
|
||||
is_inside_comment = False
|
||||
cc_counter = 0
|
||||
@staticmethod
|
||||
def get_cyclomatic_complexity(file_r, extension):
|
||||
is_inside_comment = False
|
||||
cc_counter = 0
|
||||
|
||||
entry_tokens = None
|
||||
exit_tokens = None
|
||||
entry_tokens = None
|
||||
exit_tokens = None
|
||||
|
||||
for i in __metric_cc_tokens__:
|
||||
if extension in i[0]:
|
||||
entry_tokens = i[1]
|
||||
exit_tokens = i[2]
|
||||
for i in __metric_cc_tokens__:
|
||||
if extension in i[0]:
|
||||
entry_tokens = i[1]
|
||||
exit_tokens = i[2]
|
||||
|
||||
if entry_tokens or exit_tokens:
|
||||
for i in file_r:
|
||||
i = i.decode("utf-8", "replace")
|
||||
(_, is_inside_comment) = comment.handle_comment_block(is_inside_comment, extension, i)
|
||||
if entry_tokens or exit_tokens:
|
||||
for i in file_r:
|
||||
i = i.decode("utf-8", "replace")
|
||||
(_, is_inside_comment) = comment.handle_comment_block(is_inside_comment, extension, i)
|
||||
|
||||
if not is_inside_comment and not comment.is_comment(extension, i):
|
||||
for j in entry_tokens:
|
||||
if re.search(j, i, re.DOTALL):
|
||||
cc_counter += 2
|
||||
for j in exit_tokens:
|
||||
if re.search(j, i, re.DOTALL):
|
||||
cc_counter += 1
|
||||
return cc_counter
|
||||
if not is_inside_comment and not comment.is_comment(extension, i):
|
||||
for j in entry_tokens:
|
||||
if re.search(j, i, re.DOTALL):
|
||||
cc_counter += 2
|
||||
for j in exit_tokens:
|
||||
if re.search(j, i, re.DOTALL):
|
||||
cc_counter += 1
|
||||
return cc_counter
|
||||
|
||||
return -1
|
||||
return -1
|
||||
|
||||
@staticmethod
|
||||
def get_eloc(file_r, extension):
|
||||
is_inside_comment = False
|
||||
eloc_counter = 0
|
||||
@staticmethod
|
||||
def get_eloc(file_r, extension):
|
||||
is_inside_comment = False
|
||||
eloc_counter = 0
|
||||
|
||||
for i in file_r:
|
||||
i = i.decode("utf-8", "replace")
|
||||
(_, is_inside_comment) = comment.handle_comment_block(is_inside_comment, extension, i)
|
||||
for i in file_r:
|
||||
i = i.decode("utf-8", "replace")
|
||||
(_, is_inside_comment) = comment.handle_comment_block(is_inside_comment, extension, i)
|
||||
|
||||
if not is_inside_comment and not comment.is_comment(extension, i):
|
||||
eloc_counter += 1
|
||||
if not is_inside_comment and not comment.is_comment(extension, i):
|
||||
eloc_counter += 1
|
||||
|
||||
return eloc_counter
|
||||
return eloc_counter
|
||||
|
|
|
@ -22,51 +22,51 @@ import getopt
|
|||
|
||||
|
||||
class InvalidOptionArgument(Exception):
|
||||
def __init__(self, msg):
|
||||
super(InvalidOptionArgument, self).__init__(msg)
|
||||
self.msg = msg
|
||||
def __init__(self, msg):
|
||||
super(InvalidOptionArgument, self).__init__(msg)
|
||||
self.msg = msg
|
||||
|
||||
|
||||
def __find_arg_in_options__(arg, options):
|
||||
for opt in options:
|
||||
if opt[0].find(arg) == 0:
|
||||
return opt
|
||||
for opt in options:
|
||||
if opt[0].find(arg) == 0:
|
||||
return opt
|
||||
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def __find_options_to_extend__(long_options):
|
||||
options_to_extend = []
|
||||
options_to_extend = []
|
||||
|
||||
for num, arg in enumerate(long_options):
|
||||
arg = arg.split(":")
|
||||
if len(arg) == 2:
|
||||
long_options[num] = arg[0] + "="
|
||||
options_to_extend.append(("--" + arg[0], arg[1]))
|
||||
for num, arg in enumerate(long_options):
|
||||
arg = arg.split(":")
|
||||
if len(arg) == 2:
|
||||
long_options[num] = arg[0] + "="
|
||||
options_to_extend.append(("--" + arg[0], arg[1]))
|
||||
|
||||
return options_to_extend
|
||||
return options_to_extend
|
||||
|
||||
|
||||
# This is a duplicate of gnu_getopt, but with support for optional arguments in long options, in the form; "arg:default_value".
|
||||
|
||||
|
||||
def gnu_getopt(args, options, long_options):
|
||||
options_to_extend = __find_options_to_extend__(long_options)
|
||||
options_to_extend = __find_options_to_extend__(long_options)
|
||||
|
||||
for num, arg in enumerate(args):
|
||||
opt = __find_arg_in_options__(arg, options_to_extend)
|
||||
if opt:
|
||||
args[num] = arg + "=" + opt[1]
|
||||
for num, arg in enumerate(args):
|
||||
opt = __find_arg_in_options__(arg, options_to_extend)
|
||||
if opt:
|
||||
args[num] = arg + "=" + opt[1]
|
||||
|
||||
return getopt.gnu_getopt(args, options, long_options)
|
||||
return getopt.gnu_getopt(args, options, long_options)
|
||||
|
||||
|
||||
def get_boolean_argument(arg):
|
||||
if isinstance(arg, bool):
|
||||
return arg
|
||||
elif arg is None or arg.lower() == "false" or arg.lower() == "f" or arg == "0":
|
||||
return False
|
||||
elif arg.lower() == "true" or arg.lower() == "t" or arg == "1":
|
||||
return True
|
||||
if isinstance(arg, bool):
|
||||
return arg
|
||||
elif arg is None or arg.lower() == "false" or arg.lower() == "f" or arg == "0":
|
||||
return False
|
||||
elif arg.lower() == "true" or arg.lower() == "t" or arg == "1":
|
||||
return True
|
||||
|
||||
raise InvalidOptionArgument(_("The given option argument is not a valid boolean."))
|
||||
raise InvalidOptionArgument(_("The given option argument is not a valid boolean."))
|
||||
|
|
|
@ -27,159 +27,155 @@ from ..blame import Blame
|
|||
from .outputable import Outputable
|
||||
|
||||
BLAME_INFO_TEXT = N_(
|
||||
"Below are the number of rows from each author that have survived and are still " "intact in the current revision"
|
||||
"Below are the number of rows from each author that have survived and are still " "intact in the current revision"
|
||||
)
|
||||
|
||||
|
||||
class BlameOutput(Outputable):
|
||||
def __init__(self, changes, blame):
|
||||
if format.is_interactive_format():
|
||||
print("")
|
||||
def __init__(self, changes, blame):
|
||||
if format.is_interactive_format():
|
||||
print("")
|
||||
|
||||
self.changes = changes
|
||||
self.blame = blame
|
||||
Outputable.__init__(self)
|
||||
self.changes = changes
|
||||
self.blame = blame
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_html(self):
|
||||
blame_xml = '<div><div class="box">'
|
||||
blame_xml += "<p>" + _(BLAME_INFO_TEXT) + '.</p><div><table id="blame" class="git">'
|
||||
blame_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th> </tr></thead>".format(
|
||||
_("Author"), _("Rows"), _("Stability"), _("Age"), _("% in comments")
|
||||
)
|
||||
blame_xml += "<tbody>"
|
||||
chart_data = ""
|
||||
blames = sorted(self.blame.get_summed_blames().items())
|
||||
total_blames = 0
|
||||
def output_html(self):
|
||||
blame_xml = '<div><div class="box">'
|
||||
blame_xml += "<p>" + _(BLAME_INFO_TEXT) + '.</p><div><table id="blame" class="git">'
|
||||
blame_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th> </tr></thead>".format(
|
||||
_("Author"), _("Rows"), _("Stability"), _("Age"), _("% in comments")
|
||||
)
|
||||
blame_xml += "<tbody>"
|
||||
chart_data = ""
|
||||
blames = sorted(self.blame.get_summed_blames().items())
|
||||
total_blames = 0
|
||||
|
||||
for i in blames:
|
||||
total_blames += i[1].rows
|
||||
for i in blames:
|
||||
total_blames += i[1].rows
|
||||
|
||||
for i, entry in enumerate(blames):
|
||||
work_percentage = str("{0:.2f}".format(100.0 * entry[1].rows / total_blames))
|
||||
blame_xml += "<tr " + ('class="odd">' if i % 2 == 1 else ">")
|
||||
for i, entry in enumerate(blames):
|
||||
work_percentage = str("{0:.2f}".format(100.0 * entry[1].rows / total_blames))
|
||||
blame_xml += "<tr " + ('class="odd">' if i % 2 == 1 else ">")
|
||||
|
||||
if format.get_selected() == "html":
|
||||
author_email = self.changes.get_latest_email_by_author(entry[0])
|
||||
blame_xml += '<td><img src="{0}"/>{1}</td>'.format(gravatar.get_url(author_email), entry[0])
|
||||
else:
|
||||
blame_xml += "<td>" + entry[0] + "</td>"
|
||||
if format.get_selected() == "html":
|
||||
author_email = self.changes.get_latest_email_by_author(entry[0])
|
||||
blame_xml += '<td><img src="{0}"/>{1}</td>'.format(gravatar.get_url(author_email), entry[0])
|
||||
else:
|
||||
blame_xml += "<td>" + entry[0] + "</td>"
|
||||
|
||||
blame_xml += "<td>" + str(entry[1].rows) + "</td>"
|
||||
blame_xml += "<td>" + ("{0:.1f}".format(Blame.get_stability(entry[0], entry[1].rows, self.changes)) + "</td>")
|
||||
blame_xml += "<td>" + "{0:.1f}".format(float(entry[1].skew) / entry[1].rows) + "</td>"
|
||||
blame_xml += "<td>" + "{0:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "</td>"
|
||||
blame_xml += '<td style="display: none">' + work_percentage + "</td>"
|
||||
blame_xml += "</tr>"
|
||||
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry[0]), work_percentage)
|
||||
blame_xml += "<td>" + str(entry[1].rows) + "</td>"
|
||||
blame_xml += "<td>" + ("{0:.1f}".format(Blame.get_stability(entry[0], entry[1].rows, self.changes)) + "</td>")
|
||||
blame_xml += "<td>" + "{0:.1f}".format(float(entry[1].skew) / entry[1].rows) + "</td>"
|
||||
blame_xml += "<td>" + "{0:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "</td>"
|
||||
blame_xml += '<td style="display: none">' + work_percentage + "</td>"
|
||||
blame_xml += "</tr>"
|
||||
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry[0]), work_percentage)
|
||||
|
||||
if blames[-1] != entry:
|
||||
chart_data += ", "
|
||||
if blames[-1] != entry:
|
||||
chart_data += ", "
|
||||
|
||||
blame_xml += '<tfoot><tr> <td colspan="5"> </td> </tr></tfoot></tbody></table>'
|
||||
blame_xml += '<div class="chart" id="blame_chart"></div></div>'
|
||||
blame_xml += '<script type="text/javascript">'
|
||||
blame_xml += ' blame_plot = $.plot($("#blame_chart"), [{0}], {{'.format(chart_data)
|
||||
blame_xml += " series: {"
|
||||
blame_xml += " pie: {"
|
||||
blame_xml += " innerRadius: 0.4,"
|
||||
blame_xml += " show: true,"
|
||||
blame_xml += " combine: {"
|
||||
blame_xml += " threshold: 0.01,"
|
||||
blame_xml += ' label: "' + _("Minor Authors") + '"'
|
||||
blame_xml += " }"
|
||||
blame_xml += " }"
|
||||
blame_xml += " }, grid: {"
|
||||
blame_xml += " hoverable: true"
|
||||
blame_xml += " }"
|
||||
blame_xml += " });"
|
||||
blame_xml += "</script></div></div>"
|
||||
blame_xml += '<tfoot><tr> <td colspan="5"> </td> </tr></tfoot></tbody></table>'
|
||||
blame_xml += '<div class="chart" id="blame_chart"></div></div>'
|
||||
blame_xml += '<script type="text/javascript">'
|
||||
blame_xml += ' blame_plot = $.plot($("#blame_chart"), [{0}], {{'.format(chart_data)
|
||||
blame_xml += " series: {"
|
||||
blame_xml += " pie: {"
|
||||
blame_xml += " innerRadius: 0.4,"
|
||||
blame_xml += " show: true,"
|
||||
blame_xml += " combine: {"
|
||||
blame_xml += " threshold: 0.01,"
|
||||
blame_xml += ' label: "' + _("Minor Authors") + '"'
|
||||
blame_xml += " }"
|
||||
blame_xml += " }"
|
||||
blame_xml += " }, grid: {"
|
||||
blame_xml += " hoverable: true"
|
||||
blame_xml += " }"
|
||||
blame_xml += " });"
|
||||
blame_xml += "</script></div></div>"
|
||||
|
||||
print(blame_xml)
|
||||
print(blame_xml)
|
||||
|
||||
def output_json(self):
|
||||
message_json = '\t\t\t"message": "' + _(BLAME_INFO_TEXT) + '",\n'
|
||||
blame_json = ""
|
||||
def output_json(self):
|
||||
message_json = '\t\t\t"message": "' + _(BLAME_INFO_TEXT) + '",\n'
|
||||
blame_json = ""
|
||||
|
||||
for i in sorted(self.blame.get_summed_blames().items()):
|
||||
author_email = self.changes.get_latest_email_by_author(i[0])
|
||||
for i in sorted(self.blame.get_summed_blames().items()):
|
||||
author_email = self.changes.get_latest_email_by_author(i[0])
|
||||
|
||||
name_json = '\t\t\t\t"name": "' + i[0] + '",\n'
|
||||
email_json = '\t\t\t\t"email": "' + author_email + '",\n'
|
||||
gravatar_json = '\t\t\t\t"gravatar": "' + gravatar.get_url(author_email) + '",\n'
|
||||
rows_json = '\t\t\t\t"rows": ' + str(i[1].rows) + ",\n"
|
||||
stability_json = (
|
||||
'\t\t\t\t"stability": ' + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows, self.changes)) + ",\n"
|
||||
)
|
||||
age_json = '\t\t\t\t"age": ' + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + ",\n"
|
||||
percentage_in_comments_json = (
|
||||
'\t\t\t\t"percentage_in_comments": ' + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) + "\n"
|
||||
)
|
||||
blame_json += (
|
||||
"{\n"
|
||||
+ name_json
|
||||
+ email_json
|
||||
+ gravatar_json
|
||||
+ rows_json
|
||||
+ stability_json
|
||||
+ age_json
|
||||
+ percentage_in_comments_json
|
||||
+ "\t\t\t},"
|
||||
)
|
||||
else:
|
||||
blame_json = blame_json[:-1]
|
||||
name_json = '\t\t\t\t"name": "' + i[0] + '",\n'
|
||||
email_json = '\t\t\t\t"email": "' + author_email + '",\n'
|
||||
gravatar_json = '\t\t\t\t"gravatar": "' + gravatar.get_url(author_email) + '",\n'
|
||||
rows_json = '\t\t\t\t"rows": ' + str(i[1].rows) + ",\n"
|
||||
stability_json = '\t\t\t\t"stability": ' + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows, self.changes)) + ",\n"
|
||||
age_json = '\t\t\t\t"age": ' + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + ",\n"
|
||||
percentage_in_comments_json = (
|
||||
'\t\t\t\t"percentage_in_comments": ' + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) + "\n"
|
||||
)
|
||||
blame_json += (
|
||||
"{\n"
|
||||
+ name_json
|
||||
+ email_json
|
||||
+ gravatar_json
|
||||
+ rows_json
|
||||
+ stability_json
|
||||
+ age_json
|
||||
+ percentage_in_comments_json
|
||||
+ "\t\t\t},"
|
||||
)
|
||||
else:
|
||||
blame_json = blame_json[:-1]
|
||||
|
||||
print(',\n\t\t"blame": {\n' + message_json + '\t\t\t"authors": [\n\t\t\t' + blame_json + "]\n\t\t}", end="")
|
||||
print(',\n\t\t"blame": {\n' + message_json + '\t\t\t"authors": [\n\t\t\t' + blame_json + "]\n\t\t}", end="")
|
||||
|
||||
def output_text(self):
|
||||
if sys.stdout.isatty() and format.is_interactive_format():
|
||||
terminal.clear_row()
|
||||
def output_text(self):
|
||||
if sys.stdout.isatty() and format.is_interactive_format():
|
||||
terminal.clear_row()
|
||||
|
||||
print(textwrap.fill(_(BLAME_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
||||
terminal.printb(
|
||||
terminal.ljust(_("Author"), 21)
|
||||
+ terminal.rjust(_("Rows"), 10)
|
||||
+ terminal.rjust(_("Stability"), 15)
|
||||
+ terminal.rjust(_("Age"), 13)
|
||||
+ terminal.rjust(_("% in comments"), 20)
|
||||
)
|
||||
print(textwrap.fill(_(BLAME_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
||||
terminal.printb(
|
||||
terminal.ljust(_("Author"), 21)
|
||||
+ terminal.rjust(_("Rows"), 10)
|
||||
+ terminal.rjust(_("Stability"), 15)
|
||||
+ terminal.rjust(_("Age"), 13)
|
||||
+ terminal.rjust(_("% in comments"), 20)
|
||||
)
|
||||
|
||||
for i in sorted(self.blame.get_summed_blames().items()):
|
||||
print(terminal.ljust(i[0], 20)[0 : 20 - terminal.get_excess_column_count(i[0])], end=" ")
|
||||
print(str(i[1].rows).rjust(10), end=" ")
|
||||
print("{0:.1f}".format(Blame.get_stability(i[0], i[1].rows, self.changes)).rjust(14), end=" ")
|
||||
print("{0:.1f}".format(float(i[1].skew) / i[1].rows).rjust(12), end=" ")
|
||||
print("{0:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(19))
|
||||
for i in sorted(self.blame.get_summed_blames().items()):
|
||||
print(terminal.ljust(i[0], 20)[0 : 20 - terminal.get_excess_column_count(i[0])], end=" ")
|
||||
print(str(i[1].rows).rjust(10), end=" ")
|
||||
print("{0:.1f}".format(Blame.get_stability(i[0], i[1].rows, self.changes)).rjust(14), end=" ")
|
||||
print("{0:.1f}".format(float(i[1].skew) / i[1].rows).rjust(12), end=" ")
|
||||
print("{0:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(19))
|
||||
|
||||
def output_xml(self):
|
||||
message_xml = "\t\t<message>" + _(BLAME_INFO_TEXT) + "</message>\n"
|
||||
blame_xml = ""
|
||||
def output_xml(self):
|
||||
message_xml = "\t\t<message>" + _(BLAME_INFO_TEXT) + "</message>\n"
|
||||
blame_xml = ""
|
||||
|
||||
for i in sorted(self.blame.get_summed_blames().items()):
|
||||
author_email = self.changes.get_latest_email_by_author(i[0])
|
||||
for i in sorted(self.blame.get_summed_blames().items()):
|
||||
author_email = self.changes.get_latest_email_by_author(i[0])
|
||||
|
||||
name_xml = "\t\t\t\t<name>" + i[0] + "</name>\n"
|
||||
email_xml = "\t\t\t\t<email>" + author_email + "</email>\n"
|
||||
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||
rows_xml = "\t\t\t\t<rows>" + str(i[1].rows) + "</rows>\n"
|
||||
stability_xml = (
|
||||
"\t\t\t\t<stability>" + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows, self.changes)) + "</stability>\n"
|
||||
)
|
||||
age_xml = "\t\t\t\t<age>" + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + "</age>\n"
|
||||
percentage_in_comments_xml = (
|
||||
"\t\t\t\t<percentage-in-comments>"
|
||||
+ "{0:.2f}".format(100.0 * i[1].comments / i[1].rows)
|
||||
+ "</percentage-in-comments>\n"
|
||||
)
|
||||
blame_xml += (
|
||||
"\t\t\t<author>\n"
|
||||
+ name_xml
|
||||
+ email_xml
|
||||
+ gravatar_xml
|
||||
+ rows_xml
|
||||
+ stability_xml
|
||||
+ age_xml
|
||||
+ percentage_in_comments_xml
|
||||
+ "\t\t\t</author>\n"
|
||||
)
|
||||
name_xml = "\t\t\t\t<name>" + i[0] + "</name>\n"
|
||||
email_xml = "\t\t\t\t<email>" + author_email + "</email>\n"
|
||||
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||
rows_xml = "\t\t\t\t<rows>" + str(i[1].rows) + "</rows>\n"
|
||||
stability_xml = (
|
||||
"\t\t\t\t<stability>" + "{0:.1f}".format(Blame.get_stability(i[0], i[1].rows, self.changes)) + "</stability>\n"
|
||||
)
|
||||
age_xml = "\t\t\t\t<age>" + "{0:.1f}".format(float(i[1].skew) / i[1].rows) + "</age>\n"
|
||||
percentage_in_comments_xml = (
|
||||
"\t\t\t\t<percentage-in-comments>" + "{0:.2f}".format(100.0 * i[1].comments / i[1].rows) + "</percentage-in-comments>\n"
|
||||
)
|
||||
blame_xml += (
|
||||
"\t\t\t<author>\n"
|
||||
+ name_xml
|
||||
+ email_xml
|
||||
+ gravatar_xml
|
||||
+ rows_xml
|
||||
+ stability_xml
|
||||
+ age_xml
|
||||
+ percentage_in_comments_xml
|
||||
+ "\t\t\t</author>\n"
|
||||
)
|
||||
|
||||
print("\t<blame>\n" + message_xml + "\t\t<authors>\n" + blame_xml + "\t\t</authors>\n\t</blame>")
|
||||
print("\t<blame>\n" + message_xml + "\t\t<authors>\n" + blame_xml + "\t\t</authors>\n\t</blame>")
|
||||
|
|
|
@ -29,187 +29,185 @@ NO_COMMITED_FILES_TEXT = N_("No commited files with the specified extensions wer
|
|||
|
||||
|
||||
class ChangesOutput(Outputable):
|
||||
def __init__(self, changes):
|
||||
self.changes = changes
|
||||
Outputable.__init__(self)
|
||||
def __init__(self, changes):
|
||||
self.changes = changes
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_html(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
changes_xml = '<div><div class="box">'
|
||||
chart_data = ""
|
||||
def output_html(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
changes_xml = '<div><div class="box">'
|
||||
chart_data = ""
|
||||
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
|
||||
if authorinfo_list:
|
||||
changes_xml += "<p>" + _(HISTORICAL_INFO_TEXT) + '.</p><div><table id="changes" class="git">'
|
||||
changes_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th>".format(
|
||||
_("Author"), _("Commits"), _("Insertions"), _("Deletions"), _("% of changes")
|
||||
)
|
||||
changes_xml += "</tr></thead><tbody>"
|
||||
if authorinfo_list:
|
||||
changes_xml += "<p>" + _(HISTORICAL_INFO_TEXT) + '.</p><div><table id="changes" class="git">'
|
||||
changes_xml += "<thead><tr> <th>{0}</th> <th>{1}</th> <th>{2}</th> <th>{3}</th> <th>{4}</th>".format(
|
||||
_("Author"), _("Commits"), _("Insertions"), _("Deletions"), _("% of changes")
|
||||
)
|
||||
changes_xml += "</tr></thead><tbody>"
|
||||
|
||||
for i, entry in enumerate(sorted(authorinfo_list)):
|
||||
authorinfo = authorinfo_list.get(entry)
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
for i, entry in enumerate(sorted(authorinfo_list)):
|
||||
authorinfo = authorinfo_list.get(entry)
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
|
||||
changes_xml += "<tr " + ('class="odd">' if i % 2 == 1 else ">")
|
||||
changes_xml += "<tr " + ('class="odd">' if i % 2 == 1 else ">")
|
||||
|
||||
if format.get_selected() == "html":
|
||||
changes_xml += '<td><img src="{0}"/>{1}</td>'.format(
|
||||
gravatar.get_url(self.changes.get_latest_email_by_author(entry)), entry
|
||||
)
|
||||
else:
|
||||
changes_xml += "<td>" + entry + "</td>"
|
||||
if format.get_selected() == "html":
|
||||
changes_xml += '<td><img src="{0}"/>{1}</td>'.format(
|
||||
gravatar.get_url(self.changes.get_latest_email_by_author(entry)), entry
|
||||
)
|
||||
else:
|
||||
changes_xml += "<td>" + entry + "</td>"
|
||||
|
||||
changes_xml += "<td>" + str(authorinfo.commits) + "</td>"
|
||||
changes_xml += "<td>" + str(authorinfo.insertions) + "</td>"
|
||||
changes_xml += "<td>" + str(authorinfo.deletions) + "</td>"
|
||||
changes_xml += "<td>" + "{0:.2f}".format(percentage) + "</td>"
|
||||
changes_xml += "</tr>"
|
||||
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry), "{0:.2f}".format(percentage))
|
||||
changes_xml += "<td>" + str(authorinfo.commits) + "</td>"
|
||||
changes_xml += "<td>" + str(authorinfo.insertions) + "</td>"
|
||||
changes_xml += "<td>" + str(authorinfo.deletions) + "</td>"
|
||||
changes_xml += "<td>" + "{0:.2f}".format(percentage) + "</td>"
|
||||
changes_xml += "</tr>"
|
||||
chart_data += "{{label: {0}, data: {1}}}".format(json.dumps(entry), "{0:.2f}".format(percentage))
|
||||
|
||||
if sorted(authorinfo_list)[-1] != entry:
|
||||
chart_data += ", "
|
||||
if sorted(authorinfo_list)[-1] != entry:
|
||||
chart_data += ", "
|
||||
|
||||
changes_xml += '<tfoot><tr> <td colspan="5"> </td> </tr></tfoot></tbody></table>'
|
||||
changes_xml += '<div class="chart" id="changes_chart"></div></div>'
|
||||
changes_xml += '<script type="text/javascript">'
|
||||
changes_xml += ' changes_plot = $.plot($("#changes_chart"), [{0}], {{'.format(chart_data)
|
||||
changes_xml += " series: {"
|
||||
changes_xml += " pie: {"
|
||||
changes_xml += " innerRadius: 0.4,"
|
||||
changes_xml += " show: true,"
|
||||
changes_xml += " combine: {"
|
||||
changes_xml += " threshold: 0.01,"
|
||||
changes_xml += ' label: "' + _("Minor Authors") + '"'
|
||||
changes_xml += " }"
|
||||
changes_xml += " }"
|
||||
changes_xml += " }, grid: {"
|
||||
changes_xml += " hoverable: true"
|
||||
changes_xml += " }"
|
||||
changes_xml += " });"
|
||||
changes_xml += "</script>"
|
||||
else:
|
||||
changes_xml += "<p>" + _(NO_COMMITED_FILES_TEXT) + ".</p>"
|
||||
changes_xml += '<tfoot><tr> <td colspan="5"> </td> </tr></tfoot></tbody></table>'
|
||||
changes_xml += '<div class="chart" id="changes_chart"></div></div>'
|
||||
changes_xml += '<script type="text/javascript">'
|
||||
changes_xml += ' changes_plot = $.plot($("#changes_chart"), [{0}], {{'.format(chart_data)
|
||||
changes_xml += " series: {"
|
||||
changes_xml += " pie: {"
|
||||
changes_xml += " innerRadius: 0.4,"
|
||||
changes_xml += " show: true,"
|
||||
changes_xml += " combine: {"
|
||||
changes_xml += " threshold: 0.01,"
|
||||
changes_xml += ' label: "' + _("Minor Authors") + '"'
|
||||
changes_xml += " }"
|
||||
changes_xml += " }"
|
||||
changes_xml += " }, grid: {"
|
||||
changes_xml += " hoverable: true"
|
||||
changes_xml += " }"
|
||||
changes_xml += " });"
|
||||
changes_xml += "</script>"
|
||||
else:
|
||||
changes_xml += "<p>" + _(NO_COMMITED_FILES_TEXT) + ".</p>"
|
||||
|
||||
changes_xml += "</div></div>"
|
||||
print(changes_xml)
|
||||
changes_xml += "</div></div>"
|
||||
print(changes_xml)
|
||||
|
||||
def output_json(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
def output_json(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
|
||||
if authorinfo_list:
|
||||
message_json = '\t\t\t"message": "' + _(HISTORICAL_INFO_TEXT) + '",\n'
|
||||
changes_json = ""
|
||||
if authorinfo_list:
|
||||
message_json = '\t\t\t"message": "' + _(HISTORICAL_INFO_TEXT) + '",\n'
|
||||
changes_json = ""
|
||||
|
||||
for i in sorted(authorinfo_list):
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
authorinfo = authorinfo_list.get(i)
|
||||
for i in sorted(authorinfo_list):
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
authorinfo = authorinfo_list.get(i)
|
||||
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
name_json = '\t\t\t\t"name": "' + i + '",\n'
|
||||
email_json = '\t\t\t\t"email": "' + author_email + '",\n'
|
||||
gravatar_json = '\t\t\t\t"gravatar": "' + gravatar.get_url(author_email) + '",\n'
|
||||
commits_json = '\t\t\t\t"commits": ' + str(authorinfo.commits) + ",\n"
|
||||
insertions_json = '\t\t\t\t"insertions": ' + str(authorinfo.insertions) + ",\n"
|
||||
deletions_json = '\t\t\t\t"deletions": ' + str(authorinfo.deletions) + ",\n"
|
||||
percentage_json = '\t\t\t\t"percentage_of_changes": ' + "{0:.2f}".format(percentage) + "\n"
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
name_json = '\t\t\t\t"name": "' + i + '",\n'
|
||||
email_json = '\t\t\t\t"email": "' + author_email + '",\n'
|
||||
gravatar_json = '\t\t\t\t"gravatar": "' + gravatar.get_url(author_email) + '",\n'
|
||||
commits_json = '\t\t\t\t"commits": ' + str(authorinfo.commits) + ",\n"
|
||||
insertions_json = '\t\t\t\t"insertions": ' + str(authorinfo.insertions) + ",\n"
|
||||
deletions_json = '\t\t\t\t"deletions": ' + str(authorinfo.deletions) + ",\n"
|
||||
percentage_json = '\t\t\t\t"percentage_of_changes": ' + "{0:.2f}".format(percentage) + "\n"
|
||||
|
||||
changes_json += (
|
||||
"{\n"
|
||||
+ name_json
|
||||
+ email_json
|
||||
+ gravatar_json
|
||||
+ commits_json
|
||||
+ insertions_json
|
||||
+ deletions_json
|
||||
+ percentage_json
|
||||
+ "\t\t\t}"
|
||||
)
|
||||
changes_json += ","
|
||||
else:
|
||||
changes_json = changes_json[:-1]
|
||||
changes_json += (
|
||||
"{\n"
|
||||
+ name_json
|
||||
+ email_json
|
||||
+ gravatar_json
|
||||
+ commits_json
|
||||
+ insertions_json
|
||||
+ deletions_json
|
||||
+ percentage_json
|
||||
+ "\t\t\t}"
|
||||
)
|
||||
changes_json += ","
|
||||
else:
|
||||
changes_json = changes_json[:-1]
|
||||
|
||||
print('\t\t"changes": {\n' + message_json + '\t\t\t"authors": [\n\t\t\t' + changes_json + "]\n\t\t}", end="")
|
||||
else:
|
||||
print('\t\t"exception": "' + _(NO_COMMITED_FILES_TEXT) + '"')
|
||||
print('\t\t"changes": {\n' + message_json + '\t\t\t"authors": [\n\t\t\t' + changes_json + "]\n\t\t}", end="")
|
||||
else:
|
||||
print('\t\t"exception": "' + _(NO_COMMITED_FILES_TEXT) + '"')
|
||||
|
||||
def output_text(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
def output_text(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
|
||||
if authorinfo_list:
|
||||
print(textwrap.fill(_(HISTORICAL_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
||||
terminal.printb(
|
||||
terminal.ljust(_("Author"), 21)
|
||||
+ terminal.rjust(_("Commits"), 13)
|
||||
+ terminal.rjust(_("Insertions"), 14)
|
||||
+ terminal.rjust(_("Deletions"), 15)
|
||||
+ terminal.rjust(_("% of changes"), 16)
|
||||
)
|
||||
if authorinfo_list:
|
||||
print(textwrap.fill(_(HISTORICAL_INFO_TEXT) + ":", width=terminal.get_size()[0]) + "\n")
|
||||
terminal.printb(
|
||||
terminal.ljust(_("Author"), 21)
|
||||
+ terminal.rjust(_("Commits"), 13)
|
||||
+ terminal.rjust(_("Insertions"), 14)
|
||||
+ terminal.rjust(_("Deletions"), 15)
|
||||
+ terminal.rjust(_("% of changes"), 16)
|
||||
)
|
||||
|
||||
for i in sorted(authorinfo_list):
|
||||
authorinfo = authorinfo_list.get(i)
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
for i in sorted(authorinfo_list):
|
||||
authorinfo = authorinfo_list.get(i)
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
|
||||
print(terminal.ljust(i, 20)[0 : 20 - terminal.get_excess_column_count(i)], end=" ")
|
||||
print(str(authorinfo.commits).rjust(13), end=" ")
|
||||
print(str(authorinfo.insertions).rjust(13), end=" ")
|
||||
print(str(authorinfo.deletions).rjust(14), end=" ")
|
||||
print("{0:.2f}".format(percentage).rjust(15))
|
||||
else:
|
||||
print(_(NO_COMMITED_FILES_TEXT) + ".")
|
||||
print(terminal.ljust(i, 20)[0 : 20 - terminal.get_excess_column_count(i)], end=" ")
|
||||
print(str(authorinfo.commits).rjust(13), end=" ")
|
||||
print(str(authorinfo.insertions).rjust(13), end=" ")
|
||||
print(str(authorinfo.deletions).rjust(14), end=" ")
|
||||
print("{0:.2f}".format(percentage).rjust(15))
|
||||
else:
|
||||
print(_(NO_COMMITED_FILES_TEXT) + ".")
|
||||
|
||||
def output_xml(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
def output_xml(self):
|
||||
authorinfo_list = self.changes.get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
for i in authorinfo_list:
|
||||
total_changes += authorinfo_list.get(i).insertions
|
||||
total_changes += authorinfo_list.get(i).deletions
|
||||
|
||||
if authorinfo_list:
|
||||
message_xml = "\t\t<message>" + _(HISTORICAL_INFO_TEXT) + "</message>\n"
|
||||
changes_xml = ""
|
||||
if authorinfo_list:
|
||||
message_xml = "\t\t<message>" + _(HISTORICAL_INFO_TEXT) + "</message>\n"
|
||||
changes_xml = ""
|
||||
|
||||
for i in sorted(authorinfo_list):
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
authorinfo = authorinfo_list.get(i)
|
||||
for i in sorted(authorinfo_list):
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
authorinfo = authorinfo_list.get(i)
|
||||
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
name_xml = "\t\t\t\t<name>" + i + "</name>\n"
|
||||
email_xml = "\t\t\t\t<email>" + author_email + "</email>\n"
|
||||
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||
commits_xml = "\t\t\t\t<commits>" + str(authorinfo.commits) + "</commits>\n"
|
||||
insertions_xml = "\t\t\t\t<insertions>" + str(authorinfo.insertions) + "</insertions>\n"
|
||||
deletions_xml = "\t\t\t\t<deletions>" + str(authorinfo.deletions) + "</deletions>\n"
|
||||
percentage_xml = (
|
||||
"\t\t\t\t<percentage-of-changes>" + "{0:.2f}".format(percentage) + "</percentage-of-changes>\n"
|
||||
)
|
||||
percentage = 0 if total_changes == 0 else (authorinfo.insertions + authorinfo.deletions) / total_changes * 100
|
||||
name_xml = "\t\t\t\t<name>" + i + "</name>\n"
|
||||
email_xml = "\t\t\t\t<email>" + author_email + "</email>\n"
|
||||
gravatar_xml = "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||
commits_xml = "\t\t\t\t<commits>" + str(authorinfo.commits) + "</commits>\n"
|
||||
insertions_xml = "\t\t\t\t<insertions>" + str(authorinfo.insertions) + "</insertions>\n"
|
||||
deletions_xml = "\t\t\t\t<deletions>" + str(authorinfo.deletions) + "</deletions>\n"
|
||||
percentage_xml = "\t\t\t\t<percentage-of-changes>" + "{0:.2f}".format(percentage) + "</percentage-of-changes>\n"
|
||||
|
||||
changes_xml += (
|
||||
"\t\t\t<author>\n"
|
||||
+ name_xml
|
||||
+ email_xml
|
||||
+ gravatar_xml
|
||||
+ commits_xml
|
||||
+ insertions_xml
|
||||
+ deletions_xml
|
||||
+ percentage_xml
|
||||
+ "\t\t\t</author>\n"
|
||||
)
|
||||
changes_xml += (
|
||||
"\t\t\t<author>\n"
|
||||
+ name_xml
|
||||
+ email_xml
|
||||
+ gravatar_xml
|
||||
+ commits_xml
|
||||
+ insertions_xml
|
||||
+ deletions_xml
|
||||
+ percentage_xml
|
||||
+ "\t\t\t</author>\n"
|
||||
)
|
||||
|
||||
print("\t<changes>\n" + message_xml + "\t\t<authors>\n" + changes_xml + "\t\t</authors>\n\t</changes>")
|
||||
else:
|
||||
print("\t<changes>\n\t\t<exception>" + _(NO_COMMITED_FILES_TEXT) + "</exception>\n\t</changes>")
|
||||
print("\t<changes>\n" + message_xml + "\t\t<authors>\n" + changes_xml + "\t\t</authors>\n\t</changes>")
|
||||
else:
|
||||
print("\t<changes>\n\t\t<exception>" + _(NO_COMMITED_FILES_TEXT) + "</exception>\n\t</changes>")
|
||||
|
|
|
@ -29,91 +29,88 @@ EXTENSIONS_MARKED_TEXT = N_("(extensions used during statistical analysis are ma
|
|||
|
||||
|
||||
class ExtensionsOutput(Outputable):
|
||||
@staticmethod
|
||||
def is_marked(extension):
|
||||
if extension in extensions.__extensions__ or "**" in extensions.__extensions__:
|
||||
return True
|
||||
@staticmethod
|
||||
def is_marked(extension):
|
||||
if extension in extensions.__extensions__ or "**" in extensions.__extensions__:
|
||||
return True
|
||||
|
||||
return False
|
||||
return False
|
||||
|
||||
def output_html(self):
|
||||
if extensions.__located_extensions__:
|
||||
extensions_xml = '<div><div class="box">'
|
||||
extensions_xml += "<p>{0} {1}.</p><p>".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT))
|
||||
def output_html(self):
|
||||
if extensions.__located_extensions__:
|
||||
extensions_xml = '<div><div class="box">'
|
||||
extensions_xml += "<p>{0} {1}.</p><p>".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT))
|
||||
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
extensions_xml += "<strong>" + i + "</strong>"
|
||||
else:
|
||||
extensions_xml += i
|
||||
extensions_xml += " "
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
extensions_xml += "<strong>" + i + "</strong>"
|
||||
else:
|
||||
extensions_xml += i
|
||||
extensions_xml += " "
|
||||
|
||||
extensions_xml += "</p></div></div>"
|
||||
print(extensions_xml)
|
||||
extensions_xml += "</p></div></div>"
|
||||
print(extensions_xml)
|
||||
|
||||
def output_json(self):
|
||||
if extensions.__located_extensions__:
|
||||
message_json = '\t\t\t"message": "' + _(EXTENSIONS_INFO_TEXT) + '",\n'
|
||||
used_extensions_json = ""
|
||||
unused_extensions_json = ""
|
||||
def output_json(self):
|
||||
if extensions.__located_extensions__:
|
||||
message_json = '\t\t\t"message": "' + _(EXTENSIONS_INFO_TEXT) + '",\n'
|
||||
used_extensions_json = ""
|
||||
unused_extensions_json = ""
|
||||
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
used_extensions_json += '"' + i + '", '
|
||||
else:
|
||||
unused_extensions_json += '"' + i + '", '
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
used_extensions_json += '"' + i + '", '
|
||||
else:
|
||||
unused_extensions_json += '"' + i + '", '
|
||||
|
||||
used_extensions_json = used_extensions_json[:-2]
|
||||
unused_extensions_json = unused_extensions_json[:-2]
|
||||
used_extensions_json = used_extensions_json[:-2]
|
||||
unused_extensions_json = unused_extensions_json[:-2]
|
||||
|
||||
print(
|
||||
',\n\t\t"extensions": {\n'
|
||||
+ message_json
|
||||
+ '\t\t\t"used": [ '
|
||||
+ used_extensions_json
|
||||
+ ' ],\n\t\t\t"unused": [ '
|
||||
+ unused_extensions_json
|
||||
+ " ]\n"
|
||||
+ "\t\t}",
|
||||
end="",
|
||||
)
|
||||
print(
|
||||
',\n\t\t"extensions": {\n'
|
||||
+ message_json
|
||||
+ '\t\t\t"used": [ '
|
||||
+ used_extensions_json
|
||||
+ ' ],\n\t\t\t"unused": [ '
|
||||
+ unused_extensions_json
|
||||
+ " ]\n"
|
||||
+ "\t\t}",
|
||||
end="",
|
||||
)
|
||||
|
||||
def output_text(self):
|
||||
if extensions.__located_extensions__:
|
||||
print(
|
||||
"\n"
|
||||
+ textwrap.fill(
|
||||
"{0} {1}:".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT)), width=terminal.get_size()[0]
|
||||
)
|
||||
)
|
||||
def output_text(self):
|
||||
if extensions.__located_extensions__:
|
||||
print(
|
||||
"\n" + textwrap.fill("{0} {1}:".format(_(EXTENSIONS_INFO_TEXT), _(EXTENSIONS_MARKED_TEXT)), width=terminal.get_size()[0])
|
||||
)
|
||||
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
print("[" + terminal.__bold__ + i + terminal.__normal__ + "]", end=" ")
|
||||
else:
|
||||
print(i, end=" ")
|
||||
print("")
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
print("[" + terminal.__bold__ + i + terminal.__normal__ + "]", end=" ")
|
||||
else:
|
||||
print(i, end=" ")
|
||||
print("")
|
||||
|
||||
def output_xml(self):
|
||||
if extensions.__located_extensions__:
|
||||
message_xml = "\t\t<message>" + _(EXTENSIONS_INFO_TEXT) + "</message>\n"
|
||||
used_extensions_xml = ""
|
||||
unused_extensions_xml = ""
|
||||
def output_xml(self):
|
||||
if extensions.__located_extensions__:
|
||||
message_xml = "\t\t<message>" + _(EXTENSIONS_INFO_TEXT) + "</message>\n"
|
||||
used_extensions_xml = ""
|
||||
unused_extensions_xml = ""
|
||||
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
used_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
|
||||
else:
|
||||
unused_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
|
||||
for i in sorted(extensions.__located_extensions__):
|
||||
if ExtensionsOutput.is_marked(i):
|
||||
used_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
|
||||
else:
|
||||
unused_extensions_xml += "\t\t\t<extension>" + i + "</extension>\n"
|
||||
|
||||
print(
|
||||
"\t<extensions>\n"
|
||||
+ message_xml
|
||||
+ "\t\t<used>\n"
|
||||
+ used_extensions_xml
|
||||
+ "\t\t</used>\n"
|
||||
+ "\t\t<unused>\n"
|
||||
+ unused_extensions_xml
|
||||
+ "\t\t</unused>\n"
|
||||
+ "\t</extensions>"
|
||||
)
|
||||
print(
|
||||
"\t<extensions>\n"
|
||||
+ message_xml
|
||||
+ "\t\t<used>\n"
|
||||
+ used_extensions_xml
|
||||
+ "\t\t</used>\n"
|
||||
+ "\t\t<unused>\n"
|
||||
+ unused_extensions_xml
|
||||
+ "\t\t</unused>\n"
|
||||
+ "\t</extensions>"
|
||||
)
|
||||
|
|
|
@ -26,109 +26,105 @@ from .outputable import Outputable
|
|||
|
||||
FILTERING_INFO_TEXT = N_("The following files were excluded from the statistics due to the specified exclusion patterns")
|
||||
FILTERING_AUTHOR_INFO_TEXT = N_(
|
||||
"The following authors were excluded from the statistics due to the specified exclusion patterns"
|
||||
"The following authors were excluded from the statistics due to the specified exclusion patterns"
|
||||
)
|
||||
FILTERING_EMAIL_INFO_TEXT = N_(
|
||||
"The authors with the following emails were excluded from the statistics due to the specified " "exclusion patterns"
|
||||
"The authors with the following emails were excluded from the statistics due to the specified " "exclusion patterns"
|
||||
)
|
||||
FILTERING_COMMIT_INFO_TEXT = N_(
|
||||
"The following commit revisions were excluded from the statistics due to the specified " "exclusion patterns"
|
||||
"The following commit revisions were excluded from the statistics due to the specified " "exclusion patterns"
|
||||
)
|
||||
|
||||
|
||||
class FilteringOutput(Outputable):
|
||||
@staticmethod
|
||||
def __output_html_section__(info_string, filtered):
|
||||
filtering_xml = ""
|
||||
@staticmethod
|
||||
def __output_html_section__(info_string, filtered):
|
||||
filtering_xml = ""
|
||||
|
||||
if filtered:
|
||||
filtering_xml += "<p>" + info_string + "." + "</p>"
|
||||
if filtered:
|
||||
filtering_xml += "<p>" + info_string + "." + "</p>"
|
||||
|
||||
for i in filtered:
|
||||
filtering_xml += "<p>" + i + "</p>"
|
||||
for i in filtered:
|
||||
filtering_xml += "<p>" + i + "</p>"
|
||||
|
||||
return filtering_xml
|
||||
return filtering_xml
|
||||
|
||||
def output_html(self):
|
||||
if has_filtered():
|
||||
filtering_xml = '<div><div class="box">'
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1])
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1])
|
||||
filtering_xml += "</div></div>"
|
||||
def output_html(self):
|
||||
if has_filtered():
|
||||
filtering_xml = '<div><div class="box">'
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1])
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
|
||||
FilteringOutput.__output_html_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1])
|
||||
filtering_xml += "</div></div>"
|
||||
|
||||
print(filtering_xml)
|
||||
print(filtering_xml)
|
||||
|
||||
@staticmethod
|
||||
def __output_json_section__(info_string, filtered, container_tagname):
|
||||
if filtered:
|
||||
message_json = '\t\t\t\t"message": "' + info_string + '",\n'
|
||||
filtering_json = ""
|
||||
@staticmethod
|
||||
def __output_json_section__(info_string, filtered, container_tagname):
|
||||
if filtered:
|
||||
message_json = '\t\t\t\t"message": "' + info_string + '",\n'
|
||||
filtering_json = ""
|
||||
|
||||
for i in filtered:
|
||||
filtering_json += '\t\t\t\t\t"' + i + '",\n'
|
||||
else:
|
||||
filtering_json = filtering_json[:-3]
|
||||
for i in filtered:
|
||||
filtering_json += '\t\t\t\t\t"' + i + '",\n'
|
||||
else:
|
||||
filtering_json = filtering_json[:-3]
|
||||
|
||||
return (
|
||||
'\n\t\t\t"{0}": {{\n'.format(container_tagname)
|
||||
+ message_json
|
||||
+ '\t\t\t\t"entries": [\n'
|
||||
+ filtering_json
|
||||
+ '"\n\t\t\t\t]\n\t\t\t},'
|
||||
)
|
||||
return (
|
||||
'\n\t\t\t"{0}": {{\n'.format(container_tagname)
|
||||
+ message_json
|
||||
+ '\t\t\t\t"entries": [\n'
|
||||
+ filtering_json
|
||||
+ '"\n\t\t\t\t]\n\t\t\t},'
|
||||
)
|
||||
|
||||
return ""
|
||||
return ""
|
||||
|
||||
def output_json(self):
|
||||
if has_filtered():
|
||||
output = ',\n\t\t"filtering": {'
|
||||
output += FilteringOutput.__output_json_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1], "files")
|
||||
output += FilteringOutput.__output_json_section__(
|
||||
_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1], "authors"
|
||||
)
|
||||
output += FilteringOutput.__output_json_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1], "emails")
|
||||
output += FilteringOutput.__output_json_section__(
|
||||
_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision"
|
||||
)
|
||||
output = output[:-1]
|
||||
output += "\n\t\t}"
|
||||
print(output, end="")
|
||||
def output_json(self):
|
||||
if has_filtered():
|
||||
output = ',\n\t\t"filtering": {'
|
||||
output += FilteringOutput.__output_json_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1], "files")
|
||||
output += FilteringOutput.__output_json_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1], "authors")
|
||||
output += FilteringOutput.__output_json_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1], "emails")
|
||||
output += FilteringOutput.__output_json_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision")
|
||||
output = output[:-1]
|
||||
output += "\n\t\t}"
|
||||
print(output, end="")
|
||||
|
||||
@staticmethod
|
||||
def __output_text_section__(info_string, filtered):
|
||||
if filtered:
|
||||
print("\n" + textwrap.fill(info_string + ":", width=terminal.get_size()[0]))
|
||||
@staticmethod
|
||||
def __output_text_section__(info_string, filtered):
|
||||
if filtered:
|
||||
print("\n" + textwrap.fill(info_string + ":", width=terminal.get_size()[0]))
|
||||
|
||||
for i in filtered:
|
||||
(width, _unused) = terminal.get_size()
|
||||
print("...%s" % i[-width + 3 :] if len(i) > width else i)
|
||||
for i in filtered:
|
||||
(width, _unused) = terminal.get_size()
|
||||
print("...%s" % i[-width + 3 :] if len(i) > width else i)
|
||||
|
||||
def output_text(self):
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1])
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1])
|
||||
def output_text(self):
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1])
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1])
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1])
|
||||
FilteringOutput.__output_text_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1])
|
||||
|
||||
@staticmethod
|
||||
def __output_xml_section__(info_string, filtered, container_tagname):
|
||||
if filtered:
|
||||
message_xml = "\t\t\t<message>" + info_string + "</message>\n"
|
||||
filtering_xml = ""
|
||||
@staticmethod
|
||||
def __output_xml_section__(info_string, filtered, container_tagname):
|
||||
if filtered:
|
||||
message_xml = "\t\t\t<message>" + info_string + "</message>\n"
|
||||
filtering_xml = ""
|
||||
|
||||
for i in filtered:
|
||||
filtering_xml += "\t\t\t\t<entry>" + i + "</entry>\n"
|
||||
for i in filtered:
|
||||
filtering_xml += "\t\t\t\t<entry>" + i + "</entry>\n"
|
||||
|
||||
print("\t\t<{0}>".format(container_tagname))
|
||||
print(message_xml + "\t\t\t<entries>\n" + filtering_xml + "\t\t\t</entries>\n")
|
||||
print("\t\t</{0}>".format(container_tagname))
|
||||
print("\t\t<{0}>".format(container_tagname))
|
||||
print(message_xml + "\t\t\t<entries>\n" + filtering_xml + "\t\t\t</entries>\n")
|
||||
print("\t\t</{0}>".format(container_tagname))
|
||||
|
||||
def output_xml(self):
|
||||
if has_filtered():
|
||||
print("\t<filtering>")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1], "files")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1], "authors")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1], "emails")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision")
|
||||
print("\t</filtering>")
|
||||
def output_xml(self):
|
||||
if has_filtered():
|
||||
print("\t<filtering>")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_INFO_TEXT), __filters__["file"][1], "files")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_AUTHOR_INFO_TEXT), __filters__["author"][1], "authors")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_EMAIL_INFO_TEXT), __filters__["email"][1], "emails")
|
||||
FilteringOutput.__output_xml_section__(_(FILTERING_COMMIT_INFO_TEXT), __filters__["revision"][1], "revision")
|
||||
print("\t</filtering>")
|
||||
|
|
|
@ -26,7 +26,7 @@ from .outputable import Outputable
|
|||
ELOC_INFO_TEXT = N_("The following files are suspiciously big (in order of severity)")
|
||||
CYCLOMATIC_COMPLEXITY_TEXT = N_("The following files have an elevated cyclomatic complexity (in order of severity)")
|
||||
CYCLOMATIC_COMPLEXITY_DENSITY_TEXT = N_(
|
||||
"The following files have an elevated cyclomatic complexity density " "(in order of severity)"
|
||||
"The following files have an elevated cyclomatic complexity density " "(in order of severity)"
|
||||
)
|
||||
METRICS_MISSING_INFO_TEXT = N_("No metrics violations were found in the repository")
|
||||
|
||||
|
@ -34,152 +34,144 @@ METRICS_VIOLATION_SCORES = [[1.0, "minimal"], [1.25, "minor"], [1.5, "medium"],
|
|||
|
||||
|
||||
def __get_metrics_score__(ceiling, value):
|
||||
for i in reversed(METRICS_VIOLATION_SCORES):
|
||||
if value > ceiling * i[0]:
|
||||
return i[1]
|
||||
for i in reversed(METRICS_VIOLATION_SCORES):
|
||||
if value > ceiling * i[0]:
|
||||
return i[1]
|
||||
|
||||
|
||||
class MetricsOutput(Outputable):
|
||||
def __init__(self, metrics):
|
||||
self.metrics = metrics
|
||||
Outputable.__init__(self)
|
||||
def __init__(self, metrics):
|
||||
self.metrics = metrics
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_text(self):
|
||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||
print("\n" + _(METRICS_MISSING_INFO_TEXT) + ".")
|
||||
def output_text(self):
|
||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||
print("\n" + _(METRICS_MISSING_INFO_TEXT) + ".")
|
||||
|
||||
if self.metrics.eloc:
|
||||
print("\n" + _(ELOC_INFO_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True):
|
||||
print(_("{0} ({1} estimated lines of code)").format(i[1], str(i[0])))
|
||||
if self.metrics.eloc:
|
||||
print("\n" + _(ELOC_INFO_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True):
|
||||
print(_("{0} ({1} estimated lines of code)").format(i[1], str(i[0])))
|
||||
|
||||
if self.metrics.cyclomatic_complexity:
|
||||
print("\n" + _(CYCLOMATIC_COMPLEXITY_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True):
|
||||
print(_("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0])))
|
||||
if self.metrics.cyclomatic_complexity:
|
||||
print("\n" + _(CYCLOMATIC_COMPLEXITY_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True):
|
||||
print(_("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0])))
|
||||
|
||||
if self.metrics.cyclomatic_complexity_density:
|
||||
print("\n" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + ":")
|
||||
for i in sorted(
|
||||
set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True
|
||||
):
|
||||
print(_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]))
|
||||
if self.metrics.cyclomatic_complexity_density:
|
||||
print("\n" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + ":")
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True):
|
||||
print(_("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0]))
|
||||
|
||||
def output_html(self):
|
||||
metrics_xml = '<div><div class="box" id="metrics">'
|
||||
def output_html(self):
|
||||
metrics_xml = '<div><div class="box" id="metrics">'
|
||||
|
||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||
metrics_xml += "<p>" + _(METRICS_MISSING_INFO_TEXT) + ".</p>"
|
||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||
metrics_xml += "<p>" + _(METRICS_MISSING_INFO_TEXT) + ".</p>"
|
||||
|
||||
if self.metrics.eloc:
|
||||
metrics_xml += "<div><h4>" + _(ELOC_INFO_TEXT) + ".</h4>"
|
||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True)):
|
||||
metrics_xml += (
|
||||
'<div class="'
|
||||
+ __get_metrics_score__(__metric_eloc__[FileDiff.get_extension(i[1])], i[0])
|
||||
+ (' odd">' if num % 2 == 1 else '">')
|
||||
+ _("{0} ({1} estimated lines of code)").format(i[1], str(i[0]))
|
||||
+ "</div>"
|
||||
)
|
||||
metrics_xml += "</div>"
|
||||
if self.metrics.eloc:
|
||||
metrics_xml += "<div><h4>" + _(ELOC_INFO_TEXT) + ".</h4>"
|
||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True)):
|
||||
metrics_xml += (
|
||||
'<div class="'
|
||||
+ __get_metrics_score__(__metric_eloc__[FileDiff.get_extension(i[1])], i[0])
|
||||
+ (' odd">' if num % 2 == 1 else '">')
|
||||
+ _("{0} ({1} estimated lines of code)").format(i[1], str(i[0]))
|
||||
+ "</div>"
|
||||
)
|
||||
metrics_xml += "</div>"
|
||||
|
||||
if self.metrics.cyclomatic_complexity:
|
||||
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_TEXT) + "</h4>"
|
||||
for num, i in enumerate(
|
||||
sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True)
|
||||
):
|
||||
metrics_xml += (
|
||||
'<div class="'
|
||||
+ __get_metrics_score__(METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD, i[0])
|
||||
+ (' odd">' if num % 2 == 1 else '">')
|
||||
+ _("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0]))
|
||||
+ "</div>"
|
||||
)
|
||||
metrics_xml += "</div>"
|
||||
if self.metrics.cyclomatic_complexity:
|
||||
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_TEXT) + "</h4>"
|
||||
for num, i in enumerate(sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True)):
|
||||
metrics_xml += (
|
||||
'<div class="'
|
||||
+ __get_metrics_score__(METRIC_CYCLOMATIC_COMPLEXITY_THRESHOLD, i[0])
|
||||
+ (' odd">' if num % 2 == 1 else '">')
|
||||
+ _("{0} ({1} in cyclomatic complexity)").format(i[1], str(i[0]))
|
||||
+ "</div>"
|
||||
)
|
||||
metrics_xml += "</div>"
|
||||
|
||||
if self.metrics.cyclomatic_complexity_density:
|
||||
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + "</h4>"
|
||||
for num, i in enumerate(
|
||||
sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True)
|
||||
):
|
||||
metrics_xml += (
|
||||
'<div class="'
|
||||
+ __get_metrics_score__(METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD, i[0])
|
||||
+ (' odd">' if num % 2 == 1 else '">')
|
||||
+ _("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0])
|
||||
+ "</div>"
|
||||
)
|
||||
metrics_xml += "</div>"
|
||||
if self.metrics.cyclomatic_complexity_density:
|
||||
metrics_xml += "<div><h4>" + _(CYCLOMATIC_COMPLEXITY_DENSITY_TEXT) + "</h4>"
|
||||
for num, i in enumerate(
|
||||
sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True)
|
||||
):
|
||||
metrics_xml += (
|
||||
'<div class="'
|
||||
+ __get_metrics_score__(METRIC_CYCLOMATIC_COMPLEXITY_DENSITY_THRESHOLD, i[0])
|
||||
+ (' odd">' if num % 2 == 1 else '">')
|
||||
+ _("{0} ({1:.3f} in cyclomatic complexity density)").format(i[1], i[0])
|
||||
+ "</div>"
|
||||
)
|
||||
metrics_xml += "</div>"
|
||||
|
||||
metrics_xml += "</div></div>"
|
||||
print(metrics_xml)
|
||||
metrics_xml += "</div></div>"
|
||||
print(metrics_xml)
|
||||
|
||||
def output_json(self):
|
||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||
print(',\n\t\t"metrics": {\n\t\t\t"message": "' + _(METRICS_MISSING_INFO_TEXT) + '"\n\t\t}', end="")
|
||||
else:
|
||||
eloc_json = ""
|
||||
def output_json(self):
|
||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||
print(',\n\t\t"metrics": {\n\t\t\t"message": "' + _(METRICS_MISSING_INFO_TEXT) + '"\n\t\t}', end="")
|
||||
else:
|
||||
eloc_json = ""
|
||||
|
||||
if self.metrics.eloc:
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True):
|
||||
eloc_json += '{\n\t\t\t\t"type": "estimated-lines-of-code",\n'
|
||||
eloc_json += '\t\t\t\t"file_name": "' + i[1] + '",\n'
|
||||
eloc_json += '\t\t\t\t"value": ' + str(i[0]) + "\n"
|
||||
eloc_json += "\t\t\t},"
|
||||
else:
|
||||
if not self.metrics.cyclomatic_complexity:
|
||||
eloc_json = eloc_json[:-1]
|
||||
if self.metrics.eloc:
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True):
|
||||
eloc_json += '{\n\t\t\t\t"type": "estimated-lines-of-code",\n'
|
||||
eloc_json += '\t\t\t\t"file_name": "' + i[1] + '",\n'
|
||||
eloc_json += '\t\t\t\t"value": ' + str(i[0]) + "\n"
|
||||
eloc_json += "\t\t\t},"
|
||||
else:
|
||||
if not self.metrics.cyclomatic_complexity:
|
||||
eloc_json = eloc_json[:-1]
|
||||
|
||||
if self.metrics.cyclomatic_complexity:
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True):
|
||||
eloc_json += '{\n\t\t\t\t"type": "cyclomatic-complexity",\n'
|
||||
eloc_json += '\t\t\t\t"file_name": "' + i[1] + '",\n'
|
||||
eloc_json += '\t\t\t\t"value": ' + str(i[0]) + "\n"
|
||||
eloc_json += "\t\t\t},"
|
||||
else:
|
||||
if not self.metrics.cyclomatic_complexity_density:
|
||||
eloc_json = eloc_json[:-1]
|
||||
if self.metrics.cyclomatic_complexity:
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True):
|
||||
eloc_json += '{\n\t\t\t\t"type": "cyclomatic-complexity",\n'
|
||||
eloc_json += '\t\t\t\t"file_name": "' + i[1] + '",\n'
|
||||
eloc_json += '\t\t\t\t"value": ' + str(i[0]) + "\n"
|
||||
eloc_json += "\t\t\t},"
|
||||
else:
|
||||
if not self.metrics.cyclomatic_complexity_density:
|
||||
eloc_json = eloc_json[:-1]
|
||||
|
||||
if self.metrics.cyclomatic_complexity_density:
|
||||
for i in sorted(
|
||||
set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True
|
||||
):
|
||||
eloc_json += '{\n\t\t\t\t"type": "cyclomatic-complexity-density",\n'
|
||||
eloc_json += '\t\t\t\t"file_name": "' + i[1] + '",\n'
|
||||
eloc_json += '\t\t\t\t"value": {0:.3f}\n'.format(i[0])
|
||||
eloc_json += "\t\t\t},"
|
||||
else:
|
||||
eloc_json = eloc_json[:-1]
|
||||
if self.metrics.cyclomatic_complexity_density:
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True):
|
||||
eloc_json += '{\n\t\t\t\t"type": "cyclomatic-complexity-density",\n'
|
||||
eloc_json += '\t\t\t\t"file_name": "' + i[1] + '",\n'
|
||||
eloc_json += '\t\t\t\t"value": {0:.3f}\n'.format(i[0])
|
||||
eloc_json += "\t\t\t},"
|
||||
else:
|
||||
eloc_json = eloc_json[:-1]
|
||||
|
||||
print(',\n\t\t"metrics": {\n\t\t\t"violations": [\n\t\t\t' + eloc_json + "]\n\t\t}", end="")
|
||||
print(',\n\t\t"metrics": {\n\t\t\t"violations": [\n\t\t\t' + eloc_json + "]\n\t\t}", end="")
|
||||
|
||||
def output_xml(self):
|
||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||
print("\t<metrics>\n\t\t<message>" + _(METRICS_MISSING_INFO_TEXT) + "</message>\n\t</metrics>")
|
||||
else:
|
||||
eloc_xml = ""
|
||||
def output_xml(self):
|
||||
if not self.metrics.eloc and not self.metrics.cyclomatic_complexity and not self.metrics.cyclomatic_complexity_density:
|
||||
print("\t<metrics>\n\t\t<message>" + _(METRICS_MISSING_INFO_TEXT) + "</message>\n\t</metrics>")
|
||||
else:
|
||||
eloc_xml = ""
|
||||
|
||||
if self.metrics.eloc:
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True):
|
||||
eloc_xml += "\t\t\t<estimated-lines-of-code>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>" + str(i[0]) + "</value>\n"
|
||||
eloc_xml += "\t\t\t</estimated-lines-of-code>\n"
|
||||
if self.metrics.eloc:
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.eloc.items())]), reverse=True):
|
||||
eloc_xml += "\t\t\t<estimated-lines-of-code>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>" + str(i[0]) + "</value>\n"
|
||||
eloc_xml += "\t\t\t</estimated-lines-of-code>\n"
|
||||
|
||||
if self.metrics.cyclomatic_complexity:
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True):
|
||||
eloc_xml += "\t\t\t<cyclomatic-complexity>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>" + str(i[0]) + "</value>\n"
|
||||
eloc_xml += "\t\t\t</cyclomatic-complexity>\n"
|
||||
if self.metrics.cyclomatic_complexity:
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity.items())]), reverse=True):
|
||||
eloc_xml += "\t\t\t<cyclomatic-complexity>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>" + str(i[0]) + "</value>\n"
|
||||
eloc_xml += "\t\t\t</cyclomatic-complexity>\n"
|
||||
|
||||
if self.metrics.cyclomatic_complexity_density:
|
||||
for i in sorted(
|
||||
set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True
|
||||
):
|
||||
eloc_xml += "\t\t\t<cyclomatic-complexity-density>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>{0:.3f}</value>\n".format(i[0])
|
||||
eloc_xml += "\t\t\t</cyclomatic-complexity-density>\n"
|
||||
if self.metrics.cyclomatic_complexity_density:
|
||||
for i in sorted(set([(j, i) for (i, j) in list(self.metrics.cyclomatic_complexity_density.items())]), reverse=True):
|
||||
eloc_xml += "\t\t\t<cyclomatic-complexity-density>\n"
|
||||
eloc_xml += "\t\t\t\t<file-name>" + i[1] + "</file-name>\n"
|
||||
eloc_xml += "\t\t\t\t<value>{0:.3f}</value>\n".format(i[0])
|
||||
eloc_xml += "\t\t\t</cyclomatic-complexity-density>\n"
|
||||
|
||||
print("\t<metrics>\n\t\t<violations>\n" + eloc_xml + "\t\t</violations>\n\t</metrics>")
|
||||
print("\t<metrics>\n\t\t<violations>\n" + eloc_xml + "\t\t</violations>\n\t</metrics>")
|
||||
|
|
|
@ -22,25 +22,25 @@ from .. import format
|
|||
|
||||
|
||||
class Outputable(object):
|
||||
def output_html(self):
|
||||
raise NotImplementedError(_("HTML output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||
def output_html(self):
|
||||
raise NotImplementedError(_("HTML output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||
|
||||
def output_json(self):
|
||||
raise NotImplementedError(_("JSON output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||
def output_json(self):
|
||||
raise NotImplementedError(_("JSON output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||
|
||||
def output_text(self):
|
||||
raise NotImplementedError(_("Text output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||
def output_text(self):
|
||||
raise NotImplementedError(_("Text output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||
|
||||
def output_xml(self):
|
||||
raise NotImplementedError(_("XML output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||
def output_xml(self):
|
||||
raise NotImplementedError(_("XML output not yet supported in") + ' "' + self.__class__.__name__ + '".')
|
||||
|
||||
|
||||
def output(outputable):
|
||||
if format.get_selected() == "html" or format.get_selected() == "htmlembedded":
|
||||
outputable.output_html()
|
||||
elif format.get_selected() == "json":
|
||||
outputable.output_json()
|
||||
elif format.get_selected() == "text":
|
||||
outputable.output_text()
|
||||
else:
|
||||
outputable.output_xml()
|
||||
if format.get_selected() == "html" or format.get_selected() == "htmlembedded":
|
||||
outputable.output_html()
|
||||
elif format.get_selected() == "json":
|
||||
outputable.output_json()
|
||||
elif format.get_selected() == "text":
|
||||
outputable.output_text()
|
||||
else:
|
||||
outputable.output_xml()
|
||||
|
|
|
@ -25,123 +25,121 @@ from .. import responsibilities as resp
|
|||
from .outputable import Outputable
|
||||
|
||||
RESPONSIBILITIES_INFO_TEXT = N_(
|
||||
"The following responsibilities, by author, were found in the current "
|
||||
"revision of the repository (comments are excluded from the line count, "
|
||||
"if possible)"
|
||||
"The following responsibilities, by author, were found in the current "
|
||||
"revision of the repository (comments are excluded from the line count, "
|
||||
"if possible)"
|
||||
)
|
||||
MOSTLY_RESPONSIBLE_FOR_TEXT = N_("is mostly responsible for")
|
||||
|
||||
|
||||
class ResponsibilitiesOutput(Outputable):
|
||||
def __init__(self, changes, blame):
|
||||
self.changes = changes
|
||||
self.blame = blame
|
||||
Outputable.__init__(self)
|
||||
def __init__(self, changes, blame):
|
||||
self.changes = changes
|
||||
self.blame = blame
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_text(self):
|
||||
print("\n" + textwrap.fill(_(RESPONSIBILITIES_INFO_TEXT) + ":", width=terminal.get_size()[0]))
|
||||
def output_text(self):
|
||||
print("\n" + textwrap.fill(_(RESPONSIBILITIES_INFO_TEXT) + ":", width=terminal.get_size()[0]))
|
||||
|
||||
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.blame, i)), reverse=True)
|
||||
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.blame, i)), reverse=True)
|
||||
|
||||
if responsibilities:
|
||||
print("\n" + i, _(MOSTLY_RESPONSIBLE_FOR_TEXT) + ":")
|
||||
if responsibilities:
|
||||
print("\n" + i, _(MOSTLY_RESPONSIBLE_FOR_TEXT) + ":")
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
(width, _unused) = terminal.get_size()
|
||||
width -= 7
|
||||
for j, entry in enumerate(responsibilities):
|
||||
(width, _unused) = terminal.get_size()
|
||||
width -= 7
|
||||
|
||||
print(str(entry[0]).rjust(6), end=" ")
|
||||
print("...%s" % entry[1][-width + 3 :] if len(entry[1]) > width else entry[1])
|
||||
print(str(entry[0]).rjust(6), end=" ")
|
||||
print("...%s" % entry[1][-width + 3 :] if len(entry[1]) > width else entry[1])
|
||||
|
||||
if j >= 9:
|
||||
break
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
def output_html(self):
|
||||
resp_xml = '<div><div class="box" id="responsibilities">'
|
||||
resp_xml += "<p>" + _(RESPONSIBILITIES_INFO_TEXT) + ".</p>"
|
||||
def output_html(self):
|
||||
resp_xml = '<div><div class="box" id="responsibilities">'
|
||||
resp_xml += "<p>" + _(RESPONSIBILITIES_INFO_TEXT) + ".</p>"
|
||||
|
||||
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.blame, i)), reverse=True)
|
||||
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.blame, i)), reverse=True)
|
||||
|
||||
if responsibilities:
|
||||
resp_xml += "<div>"
|
||||
if responsibilities:
|
||||
resp_xml += "<div>"
|
||||
|
||||
if format.get_selected() == "html":
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
resp_xml += '<h3><img src="{0}"/>{1} {2}</h3>'.format(
|
||||
gravatar.get_url(author_email, size=32), i, _(MOSTLY_RESPONSIBLE_FOR_TEXT)
|
||||
)
|
||||
else:
|
||||
resp_xml += "<h3>{0} {1}</h3>".format(i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
|
||||
if format.get_selected() == "html":
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
resp_xml += '<h3><img src="{0}"/>{1} {2}</h3>'.format(
|
||||
gravatar.get_url(author_email, size=32), i, _(MOSTLY_RESPONSIBLE_FOR_TEXT)
|
||||
)
|
||||
else:
|
||||
resp_xml += "<h3>{0} {1}</h3>".format(i, _(MOSTLY_RESPONSIBLE_FOR_TEXT))
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_xml += (
|
||||
"<div" + (' class="odd">' if j % 2 == 1 else ">") + entry[1] + " (" + str(entry[0]) + " eloc)</div>"
|
||||
)
|
||||
if j >= 9:
|
||||
break
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_xml += "<div" + (' class="odd">' if j % 2 == 1 else ">") + entry[1] + " (" + str(entry[0]) + " eloc)</div>"
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
resp_xml += "</div>"
|
||||
resp_xml += "</div></div>"
|
||||
print(resp_xml)
|
||||
resp_xml += "</div>"
|
||||
resp_xml += "</div></div>"
|
||||
print(resp_xml)
|
||||
|
||||
def output_json(self):
|
||||
message_json = '\t\t\t"message": "' + _(RESPONSIBILITIES_INFO_TEXT) + '",\n'
|
||||
resp_json = ""
|
||||
def output_json(self):
|
||||
message_json = '\t\t\t"message": "' + _(RESPONSIBILITIES_INFO_TEXT) + '",\n'
|
||||
resp_json = ""
|
||||
|
||||
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.blame, i)), reverse=True)
|
||||
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.blame, i)), reverse=True)
|
||||
|
||||
if responsibilities:
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
if responsibilities:
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
|
||||
resp_json += "{\n"
|
||||
resp_json += '\t\t\t\t"name": "' + i + '",\n'
|
||||
resp_json += '\t\t\t\t"email": "' + author_email + '",\n'
|
||||
resp_json += '\t\t\t\t"gravatar": "' + gravatar.get_url(author_email) + '",\n'
|
||||
resp_json += '\t\t\t\t"files": [\n\t\t\t\t'
|
||||
resp_json += "{\n"
|
||||
resp_json += '\t\t\t\t"name": "' + i + '",\n'
|
||||
resp_json += '\t\t\t\t"email": "' + author_email + '",\n'
|
||||
resp_json += '\t\t\t\t"gravatar": "' + gravatar.get_url(author_email) + '",\n'
|
||||
resp_json += '\t\t\t\t"files": [\n\t\t\t\t'
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_json += "{\n"
|
||||
resp_json += '\t\t\t\t\t"name": "' + entry[1] + '",\n'
|
||||
resp_json += '\t\t\t\t\t"rows": ' + str(entry[0]) + "\n"
|
||||
resp_json += "\t\t\t\t},"
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_json += "{\n"
|
||||
resp_json += '\t\t\t\t\t"name": "' + entry[1] + '",\n'
|
||||
resp_json += '\t\t\t\t\t"rows": ' + str(entry[0]) + "\n"
|
||||
resp_json += "\t\t\t\t},"
|
||||
|
||||
if j >= 9:
|
||||
break
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
resp_json = resp_json[:-1]
|
||||
resp_json += "]\n\t\t\t},"
|
||||
resp_json = resp_json[:-1]
|
||||
resp_json += "]\n\t\t\t},"
|
||||
|
||||
resp_json = resp_json[:-1]
|
||||
print(',\n\t\t"responsibilities": {\n' + message_json + '\t\t\t"authors": [\n\t\t\t' + resp_json + "]\n\t\t}", end="")
|
||||
resp_json = resp_json[:-1]
|
||||
print(',\n\t\t"responsibilities": {\n' + message_json + '\t\t\t"authors": [\n\t\t\t' + resp_json + "]\n\t\t}", end="")
|
||||
|
||||
def output_xml(self):
|
||||
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\n"
|
||||
resp_xml = ""
|
||||
def output_xml(self):
|
||||
message_xml = "\t\t<message>" + _(RESPONSIBILITIES_INFO_TEXT) + "</message>\n"
|
||||
resp_xml = ""
|
||||
|
||||
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.blame, i)), reverse=True)
|
||||
if responsibilities:
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
for i in sorted(set(i[0] for i in self.blame.blames)):
|
||||
responsibilities = sorted(((i[1], i[0]) for i in resp.Responsibilities.get(self.blame, i)), reverse=True)
|
||||
if responsibilities:
|
||||
author_email = self.changes.get_latest_email_by_author(i)
|
||||
|
||||
resp_xml += "\t\t\t<author>\n"
|
||||
resp_xml += "\t\t\t\t<name>" + i + "</name>\n"
|
||||
resp_xml += "\t\t\t\t<email>" + author_email + "</email>\n"
|
||||
resp_xml += "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||
resp_xml += "\t\t\t\t<files>\n"
|
||||
resp_xml += "\t\t\t<author>\n"
|
||||
resp_xml += "\t\t\t\t<name>" + i + "</name>\n"
|
||||
resp_xml += "\t\t\t\t<email>" + author_email + "</email>\n"
|
||||
resp_xml += "\t\t\t\t<gravatar>" + gravatar.get_url(author_email) + "</gravatar>\n"
|
||||
resp_xml += "\t\t\t\t<files>\n"
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_xml += "\t\t\t\t\t<file>\n"
|
||||
resp_xml += "\t\t\t\t\t\t<name>" + entry[1] + "</name>\n"
|
||||
resp_xml += "\t\t\t\t\t\t<rows>" + str(entry[0]) + "</rows>\n"
|
||||
resp_xml += "\t\t\t\t\t</file>\n"
|
||||
for j, entry in enumerate(responsibilities):
|
||||
resp_xml += "\t\t\t\t\t<file>\n"
|
||||
resp_xml += "\t\t\t\t\t\t<name>" + entry[1] + "</name>\n"
|
||||
resp_xml += "\t\t\t\t\t\t<rows>" + str(entry[0]) + "</rows>\n"
|
||||
resp_xml += "\t\t\t\t\t</file>\n"
|
||||
|
||||
if j >= 9:
|
||||
break
|
||||
if j >= 9:
|
||||
break
|
||||
|
||||
resp_xml += "\t\t\t\t</files>\n"
|
||||
resp_xml += "\t\t\t</author>\n"
|
||||
resp_xml += "\t\t\t\t</files>\n"
|
||||
resp_xml += "\t\t\t</author>\n"
|
||||
|
||||
print("\t<responsibilities>\n" + message_xml + "\t\t<authors>\n" + resp_xml + "\t\t</authors>\n\t</responsibilities>")
|
||||
print("\t<responsibilities>\n" + message_xml + "\t\t<authors>\n" + resp_xml + "\t\t</authors>\n\t</responsibilities>")
|
||||
|
|
|
@ -28,193 +28,184 @@ MODIFIED_ROWS_TEXT = N_("Modified Rows:")
|
|||
|
||||
|
||||
def __output_row__text__(timeline_data, periods, names):
|
||||
print("\n" + terminal.__bold__ + terminal.ljust(_("Author"), 20), end=" ")
|
||||
print("\n" + terminal.__bold__ + terminal.ljust(_("Author"), 20), end=" ")
|
||||
|
||||
for period in periods:
|
||||
print(terminal.rjust(period, 10), end=" ")
|
||||
for period in periods:
|
||||
print(terminal.rjust(period, 10), end=" ")
|
||||
|
||||
print(terminal.__normal__)
|
||||
print(terminal.__normal__)
|
||||
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_periods(periods, name[0]):
|
||||
print(terminal.ljust(name[0], 20)[0 : 20 - terminal.get_excess_column_count(name[0])], end=" ")
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_periods(periods, name[0]):
|
||||
print(terminal.ljust(name[0], 20)[0 : 20 - terminal.get_excess_column_count(name[0])], end=" ")
|
||||
|
||||
for period in periods:
|
||||
multiplier = timeline_data.get_multiplier(period, 9)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = signs[1] * "-" + signs[0] * "+"
|
||||
print(
|
||||
("." if timeline_data.is_author_in_period(period, name[0]) and len(signs_str) == 0 else signs_str).rjust(
|
||||
10
|
||||
),
|
||||
end=" ",
|
||||
)
|
||||
print("")
|
||||
for period in periods:
|
||||
multiplier = timeline_data.get_multiplier(period, 9)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = signs[1] * "-" + signs[0] * "+"
|
||||
print(
|
||||
("." if timeline_data.is_author_in_period(period, name[0]) and len(signs_str) == 0 else signs_str).rjust(10), end=" ",
|
||||
)
|
||||
print("")
|
||||
|
||||
print(terminal.__bold__ + terminal.ljust(_(MODIFIED_ROWS_TEXT), 20) + terminal.__normal__, end=" ")
|
||||
print(terminal.__bold__ + terminal.ljust(_(MODIFIED_ROWS_TEXT), 20) + terminal.__normal__, end=" ")
|
||||
|
||||
for period in periods:
|
||||
total_changes = str(timeline_data.get_total_changes_in_period(period)[2])
|
||||
for period in periods:
|
||||
total_changes = str(timeline_data.get_total_changes_in_period(period)[2])
|
||||
|
||||
if hasattr(total_changes, "decode"):
|
||||
total_changes = total_changes.decode("utf-8", "replace")
|
||||
if hasattr(total_changes, "decode"):
|
||||
total_changes = total_changes.decode("utf-8", "replace")
|
||||
|
||||
print(terminal.rjust(total_changes, 10), end=" ")
|
||||
print(terminal.rjust(total_changes, 10), end=" ")
|
||||
|
||||
print("")
|
||||
print("")
|
||||
|
||||
|
||||
def __output_row__html__(timeline_data, periods, names):
|
||||
timeline_xml = '<table class="git full"><thead><tr><th>' + _("Author") + "</th>"
|
||||
timeline_xml = '<table class="git full"><thead><tr><th>' + _("Author") + "</th>"
|
||||
|
||||
for period in periods:
|
||||
timeline_xml += "<th>" + str(period) + "</th>"
|
||||
for period in periods:
|
||||
timeline_xml += "<th>" + str(period) + "</th>"
|
||||
|
||||
timeline_xml += "</tr></thead><tbody>"
|
||||
i = 0
|
||||
timeline_xml += "</tr></thead><tbody>"
|
||||
i = 0
|
||||
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_periods(periods, name[0]):
|
||||
timeline_xml += "<tr" + (' class="odd">' if i % 2 == 1 else ">")
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_periods(periods, name[0]):
|
||||
timeline_xml += "<tr" + (' class="odd">' if i % 2 == 1 else ">")
|
||||
|
||||
if format.get_selected() == "html":
|
||||
timeline_xml += '<td><img src="{0}"/>{1}</td>'.format(gravatar.get_url(name[1]), name[0])
|
||||
else:
|
||||
timeline_xml += "<td>" + name[0] + "</td>"
|
||||
if format.get_selected() == "html":
|
||||
timeline_xml += '<td><img src="{0}"/>{1}</td>'.format(gravatar.get_url(name[1]), name[0])
|
||||
else:
|
||||
timeline_xml += "<td>" + name[0] + "</td>"
|
||||
|
||||
for period in periods:
|
||||
multiplier = timeline_data.get_multiplier(period, 18)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = signs[1] * '<div class="remove"> </div>' + signs[0] * '<div class="insert"> </div>'
|
||||
for period in periods:
|
||||
multiplier = timeline_data.get_multiplier(period, 18)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = signs[1] * '<div class="remove"> </div>' + signs[0] * '<div class="insert"> </div>'
|
||||
|
||||
timeline_xml += "<td>" + (
|
||||
"." if timeline_data.is_author_in_period(period, name[0]) and len(signs_str) == 0 else signs_str
|
||||
)
|
||||
timeline_xml += "</td>"
|
||||
timeline_xml += "</tr>"
|
||||
i = i + 1
|
||||
timeline_xml += "<td>" + ("." if timeline_data.is_author_in_period(period, name[0]) and len(signs_str) == 0 else signs_str)
|
||||
timeline_xml += "</td>"
|
||||
timeline_xml += "</tr>"
|
||||
i = i + 1
|
||||
|
||||
timeline_xml += "<tfoot><tr><td><strong>" + _(MODIFIED_ROWS_TEXT) + "</strong></td>"
|
||||
timeline_xml += "<tfoot><tr><td><strong>" + _(MODIFIED_ROWS_TEXT) + "</strong></td>"
|
||||
|
||||
for period in periods:
|
||||
total_changes = timeline_data.get_total_changes_in_period(period)
|
||||
timeline_xml += "<td>" + str(total_changes[2]) + "</td>"
|
||||
for period in periods:
|
||||
total_changes = timeline_data.get_total_changes_in_period(period)
|
||||
timeline_xml += "<td>" + str(total_changes[2]) + "</td>"
|
||||
|
||||
timeline_xml += "</tr></tfoot></tbody></table>"
|
||||
print(timeline_xml)
|
||||
timeline_xml += "</tr></tfoot></tbody></table>"
|
||||
print(timeline_xml)
|
||||
|
||||
|
||||
class TimelineOutput(Outputable):
|
||||
def __init__(self, changes, useweeks):
|
||||
self.changes = changes
|
||||
self.useweeks = useweeks
|
||||
Outputable.__init__(self)
|
||||
def __init__(self, changes, useweeks):
|
||||
self.changes = changes
|
||||
self.useweeks = useweeks
|
||||
Outputable.__init__(self)
|
||||
|
||||
def output_text(self):
|
||||
if self.changes.get_commits():
|
||||
print("\n" + textwrap.fill(_(TIMELINE_INFO_TEXT) + ":", width=terminal.get_size()[0]))
|
||||
def output_text(self):
|
||||
if self.changes.get_commits():
|
||||
print("\n" + textwrap.fill(_(TIMELINE_INFO_TEXT) + ":", width=terminal.get_size()[0]))
|
||||
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
(width, _unused) = terminal.get_size()
|
||||
max_periods_per_row = int((width - 21) / 11)
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
(width, _unused) = terminal.get_size()
|
||||
max_periods_per_row = int((width - 21) / 11)
|
||||
|
||||
for i in range(0, len(periods), max_periods_per_row):
|
||||
__output_row__text__(timeline_data, periods[i : i + max_periods_per_row], names)
|
||||
for i in range(0, len(periods), max_periods_per_row):
|
||||
__output_row__text__(timeline_data, periods[i : i + max_periods_per_row], names)
|
||||
|
||||
def output_html(self):
|
||||
if self.changes.get_commits():
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
max_periods_per_row = 8
|
||||
def output_html(self):
|
||||
if self.changes.get_commits():
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
max_periods_per_row = 8
|
||||
|
||||
timeline_xml = '<div><div id="timeline" class="box">'
|
||||
timeline_xml += "<p>" + _(TIMELINE_INFO_TEXT) + ".</p>"
|
||||
print(timeline_xml)
|
||||
timeline_xml = '<div><div id="timeline" class="box">'
|
||||
timeline_xml += "<p>" + _(TIMELINE_INFO_TEXT) + ".</p>"
|
||||
print(timeline_xml)
|
||||
|
||||
for i in range(0, len(periods), max_periods_per_row):
|
||||
__output_row__html__(timeline_data, periods[i : i + max_periods_per_row], names)
|
||||
for i in range(0, len(periods), max_periods_per_row):
|
||||
__output_row__html__(timeline_data, periods[i : i + max_periods_per_row], names)
|
||||
|
||||
timeline_xml = "</div></div>"
|
||||
print(timeline_xml)
|
||||
timeline_xml = "</div></div>"
|
||||
print(timeline_xml)
|
||||
|
||||
def output_json(self):
|
||||
if self.changes.get_commits():
|
||||
message_json = '\t\t\t"message": "' + _(TIMELINE_INFO_TEXT) + '",\n'
|
||||
timeline_json = ""
|
||||
periods_json = '\t\t\t"period_length": "{0}",\n'.format("week" if self.useweeks else "month")
|
||||
periods_json += '\t\t\t"periods": [\n\t\t\t'
|
||||
def output_json(self):
|
||||
if self.changes.get_commits():
|
||||
message_json = '\t\t\t"message": "' + _(TIMELINE_INFO_TEXT) + '",\n'
|
||||
timeline_json = ""
|
||||
periods_json = '\t\t\t"period_length": "{0}",\n'.format("week" if self.useweeks else "month")
|
||||
periods_json += '\t\t\t"periods": [\n\t\t\t'
|
||||
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
|
||||
for period in periods:
|
||||
name_json = '\t\t\t\t"name": "' + str(period) + '",\n'
|
||||
authors_json = '\t\t\t\t"authors": [\n\t\t\t\t'
|
||||
for period in periods:
|
||||
name_json = '\t\t\t\t"name": "' + str(period) + '",\n'
|
||||
authors_json = '\t\t\t\t"authors": [\n\t\t\t\t'
|
||||
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_period(period, name[0]):
|
||||
multiplier = timeline_data.get_multiplier(period, 24)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = signs[1] * "-" + signs[0] * "+"
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_period(period, name[0]):
|
||||
multiplier = timeline_data.get_multiplier(period, 24)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = signs[1] * "-" + signs[0] * "+"
|
||||
|
||||
if len(signs_str) == 0:
|
||||
signs_str = "."
|
||||
if len(signs_str) == 0:
|
||||
signs_str = "."
|
||||
|
||||
authors_json += '{\n\t\t\t\t\t"name": "' + name[0] + '",\n'
|
||||
authors_json += '\t\t\t\t\t"email": "' + name[1] + '",\n'
|
||||
authors_json += '\t\t\t\t\t"gravatar": "' + gravatar.get_url(name[1]) + '",\n'
|
||||
authors_json += '\t\t\t\t\t"work": "' + signs_str + '"\n\t\t\t\t},'
|
||||
else:
|
||||
authors_json = authors_json[:-1]
|
||||
authors_json += '{\n\t\t\t\t\t"name": "' + name[0] + '",\n'
|
||||
authors_json += '\t\t\t\t\t"email": "' + name[1] + '",\n'
|
||||
authors_json += '\t\t\t\t\t"gravatar": "' + gravatar.get_url(name[1]) + '",\n'
|
||||
authors_json += '\t\t\t\t\t"work": "' + signs_str + '"\n\t\t\t\t},'
|
||||
else:
|
||||
authors_json = authors_json[:-1]
|
||||
|
||||
authors_json += "],\n"
|
||||
modified_rows_json = (
|
||||
'\t\t\t\t"modified_rows": ' + str(timeline_data.get_total_changes_in_period(period)[2]) + "\n"
|
||||
)
|
||||
timeline_json += "{\n" + name_json + authors_json + modified_rows_json + "\t\t\t},"
|
||||
else:
|
||||
timeline_json = timeline_json[:-1]
|
||||
authors_json += "],\n"
|
||||
modified_rows_json = '\t\t\t\t"modified_rows": ' + str(timeline_data.get_total_changes_in_period(period)[2]) + "\n"
|
||||
timeline_json += "{\n" + name_json + authors_json + modified_rows_json + "\t\t\t},"
|
||||
else:
|
||||
timeline_json = timeline_json[:-1]
|
||||
|
||||
print(',\n\t\t"timeline": {\n' + message_json + periods_json + timeline_json + "]\n\t\t}", end="")
|
||||
print(',\n\t\t"timeline": {\n' + message_json + periods_json + timeline_json + "]\n\t\t}", end="")
|
||||
|
||||
def output_xml(self):
|
||||
if self.changes.get_commits():
|
||||
message_xml = "\t\t<message>" + _(TIMELINE_INFO_TEXT) + "</message>\n"
|
||||
timeline_xml = ""
|
||||
periods_xml = '\t\t<periods length="{0}">\n'.format("week" if self.useweeks else "month")
|
||||
def output_xml(self):
|
||||
if self.changes.get_commits():
|
||||
message_xml = "\t\t<message>" + _(TIMELINE_INFO_TEXT) + "</message>\n"
|
||||
timeline_xml = ""
|
||||
periods_xml = '\t\t<periods length="{0}">\n'.format("week" if self.useweeks else "month")
|
||||
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
timeline_data = timeline.TimelineData(self.changes, self.useweeks)
|
||||
periods = timeline_data.get_periods()
|
||||
names = timeline_data.get_authors()
|
||||
|
||||
for period in periods:
|
||||
name_xml = "\t\t\t\t<name>" + str(period) + "</name>\n"
|
||||
authors_xml = "\t\t\t\t<authors>\n"
|
||||
for period in periods:
|
||||
name_xml = "\t\t\t\t<name>" + str(period) + "</name>\n"
|
||||
authors_xml = "\t\t\t\t<authors>\n"
|
||||
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_period(period, name[0]):
|
||||
multiplier = timeline_data.get_multiplier(period, 24)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = signs[1] * "-" + signs[0] * "+"
|
||||
for name in names:
|
||||
if timeline_data.is_author_in_period(period, name[0]):
|
||||
multiplier = timeline_data.get_multiplier(period, 24)
|
||||
signs = timeline_data.get_author_signs_in_period(name[0], period, multiplier)
|
||||
signs_str = signs[1] * "-" + signs[0] * "+"
|
||||
|
||||
if len(signs_str) == 0:
|
||||
signs_str = "."
|
||||
if len(signs_str) == 0:
|
||||
signs_str = "."
|
||||
|
||||
authors_xml += "\t\t\t\t\t<author>\n\t\t\t\t\t\t<name>" + name[0] + "</name>\n"
|
||||
authors_xml += "\t\t\t\t\t\t<email>" + name[1] + "</email>\n"
|
||||
authors_xml += "\t\t\t\t\t\t<gravatar>" + gravatar.get_url(name[1]) + "</gravatar>\n"
|
||||
authors_xml += "\t\t\t\t\t\t<work>" + signs_str + "</work>\n\t\t\t\t\t</author>\n"
|
||||
authors_xml += "\t\t\t\t\t<author>\n\t\t\t\t\t\t<name>" + name[0] + "</name>\n"
|
||||
authors_xml += "\t\t\t\t\t\t<email>" + name[1] + "</email>\n"
|
||||
authors_xml += "\t\t\t\t\t\t<gravatar>" + gravatar.get_url(name[1]) + "</gravatar>\n"
|
||||
authors_xml += "\t\t\t\t\t\t<work>" + signs_str + "</work>\n\t\t\t\t\t</author>\n"
|
||||
|
||||
authors_xml += "\t\t\t\t</authors>\n"
|
||||
modified_rows_xml = (
|
||||
"\t\t\t\t<modified_rows>"
|
||||
+ str(timeline_data.get_total_changes_in_period(period)[2])
|
||||
+ "</modified_rows>\n"
|
||||
)
|
||||
timeline_xml += "\t\t\t<period>\n" + name_xml + authors_xml + modified_rows_xml + "\t\t\t</period>\n"
|
||||
authors_xml += "\t\t\t\t</authors>\n"
|
||||
modified_rows_xml = (
|
||||
"\t\t\t\t<modified_rows>" + str(timeline_data.get_total_changes_in_period(period)[2]) + "</modified_rows>\n"
|
||||
)
|
||||
timeline_xml += "\t\t\t<period>\n" + name_xml + authors_xml + modified_rows_xml + "\t\t\t</period>\n"
|
||||
|
||||
print("\t<timeline>\n" + message_xml + periods_xml + timeline_xml + "\t\t</periods>\n\t</timeline>")
|
||||
print("\t<timeline>\n" + message_xml + periods_xml + timeline_xml + "\t\t</periods>\n\t</timeline>")
|
||||
|
|
|
@ -19,18 +19,18 @@
|
|||
|
||||
|
||||
class ResponsibiltyEntry(object):
|
||||
blames = {}
|
||||
blames = {}
|
||||
|
||||
|
||||
class Responsibilities(object):
|
||||
@staticmethod
|
||||
def get(blame, author_name):
|
||||
author_blames = {}
|
||||
@staticmethod
|
||||
def get(blame, author_name):
|
||||
author_blames = {}
|
||||
|
||||
for i in list(blame.blames.items()):
|
||||
if author_name == i[0][0]:
|
||||
total_rows = i[1].rows - i[1].comments
|
||||
if total_rows > 0:
|
||||
author_blames[i[0][1]] = total_rows
|
||||
for i in list(blame.blames.items()):
|
||||
if author_name == i[0][0]:
|
||||
total_rows = i[1].rows - i[1].comments
|
||||
if total_rows > 0:
|
||||
author_blames[i[0][1]] = total_rows
|
||||
|
||||
return sorted(author_blames.items())
|
||||
return sorted(author_blames.items())
|
||||
|
|
|
@ -31,149 +31,149 @@ DEFAULT_TERMINAL_SIZE = (80, 25)
|
|||
|
||||
|
||||
def __get_size_windows__():
|
||||
res = None
|
||||
try:
|
||||
from ctypes import windll, create_string_buffer
|
||||
res = None
|
||||
try:
|
||||
from ctypes import windll, create_string_buffer
|
||||
|
||||
handler = windll.kernel32.GetStdHandle(-12) # stderr
|
||||
csbi = create_string_buffer(22)
|
||||
res = windll.kernel32.GetConsoleScreenBufferInfo(handler, csbi)
|
||||
except:
|
||||
return DEFAULT_TERMINAL_SIZE
|
||||
handler = windll.kernel32.GetStdHandle(-12) # stderr
|
||||
csbi = create_string_buffer(22)
|
||||
res = windll.kernel32.GetConsoleScreenBufferInfo(handler, csbi)
|
||||
except:
|
||||
return DEFAULT_TERMINAL_SIZE
|
||||
|
||||
if res:
|
||||
import struct
|
||||
if res:
|
||||
import struct
|
||||
|
||||
(_, _, _, _, _, left, top, right, bottom, _, _) = struct.unpack("hhhhHhhhhhh", csbi.raw)
|
||||
sizex = right - left + 1
|
||||
sizey = bottom - top + 1
|
||||
return sizex, sizey
|
||||
else:
|
||||
return DEFAULT_TERMINAL_SIZE
|
||||
(_, _, _, _, _, left, top, right, bottom, _, _) = struct.unpack("hhhhHhhhhhh", csbi.raw)
|
||||
sizex = right - left + 1
|
||||
sizey = bottom - top + 1
|
||||
return sizex, sizey
|
||||
else:
|
||||
return DEFAULT_TERMINAL_SIZE
|
||||
|
||||
|
||||
def __get_size_linux__():
|
||||
def ioctl_get_window_size(file_descriptor):
|
||||
try:
|
||||
import fcntl, termios, struct
|
||||
def ioctl_get_window_size(file_descriptor):
|
||||
try:
|
||||
import fcntl, termios, struct
|
||||
|
||||
size = struct.unpack("hh", fcntl.ioctl(file_descriptor, termios.TIOCGWINSZ, "1234"))
|
||||
except:
|
||||
return DEFAULT_TERMINAL_SIZE
|
||||
size = struct.unpack("hh", fcntl.ioctl(file_descriptor, termios.TIOCGWINSZ, "1234"))
|
||||
except:
|
||||
return DEFAULT_TERMINAL_SIZE
|
||||
|
||||
return size
|
||||
return size
|
||||
|
||||
size = ioctl_get_window_size(0) or ioctl_get_window_size(1) or ioctl_get_window_size(2)
|
||||
size = ioctl_get_window_size(0) or ioctl_get_window_size(1) or ioctl_get_window_size(2)
|
||||
|
||||
if not size:
|
||||
try:
|
||||
file_descriptor = os.open(os.ctermid(), os.O_RDONLY)
|
||||
size = ioctl_get_window_size(file_descriptor)
|
||||
os.close(file_descriptor)
|
||||
except:
|
||||
pass
|
||||
if not size:
|
||||
try:
|
||||
size = (os.environ["LINES"], os.environ["COLUMNS"])
|
||||
except:
|
||||
return DEFAULT_TERMINAL_SIZE
|
||||
if not size:
|
||||
try:
|
||||
file_descriptor = os.open(os.ctermid(), os.O_RDONLY)
|
||||
size = ioctl_get_window_size(file_descriptor)
|
||||
os.close(file_descriptor)
|
||||
except:
|
||||
pass
|
||||
if not size:
|
||||
try:
|
||||
size = (os.environ["LINES"], os.environ["COLUMNS"])
|
||||
except:
|
||||
return DEFAULT_TERMINAL_SIZE
|
||||
|
||||
return int(size[1]), int(size[0])
|
||||
return int(size[1]), int(size[0])
|
||||
|
||||
|
||||
def clear_row():
|
||||
print("\r", end="")
|
||||
print("\r", end="")
|
||||
|
||||
|
||||
def skip_escapes(skip):
|
||||
if skip:
|
||||
global __bold__
|
||||
global __normal__
|
||||
__bold__ = ""
|
||||
__normal__ = ""
|
||||
if skip:
|
||||
global __bold__
|
||||
global __normal__
|
||||
__bold__ = ""
|
||||
__normal__ = ""
|
||||
|
||||
|
||||
def printb(string):
|
||||
print(__bold__ + string + __normal__)
|
||||
print(__bold__ + string + __normal__)
|
||||
|
||||
|
||||
def get_size():
|
||||
width = 0
|
||||
height = 0
|
||||
width = 0
|
||||
height = 0
|
||||
|
||||
if sys.stdout.isatty():
|
||||
current_os = platform.system()
|
||||
if sys.stdout.isatty():
|
||||
current_os = platform.system()
|
||||
|
||||
if current_os == "Windows":
|
||||
(width, height) = __get_size_windows__()
|
||||
elif current_os == "Linux" or current_os == "Darwin" or current_os.startswith("CYGWIN"):
|
||||
(width, height) = __get_size_linux__()
|
||||
if current_os == "Windows":
|
||||
(width, height) = __get_size_windows__()
|
||||
elif current_os == "Linux" or current_os == "Darwin" or current_os.startswith("CYGWIN"):
|
||||
(width, height) = __get_size_linux__()
|
||||
|
||||
if width > 0:
|
||||
return (width, height)
|
||||
if width > 0:
|
||||
return (width, height)
|
||||
|
||||
return DEFAULT_TERMINAL_SIZE
|
||||
return DEFAULT_TERMINAL_SIZE
|
||||
|
||||
|
||||
def set_stdout_encoding():
|
||||
if not sys.stdout.isatty() and sys.version_info < (3,):
|
||||
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
|
||||
if not sys.stdout.isatty() and sys.version_info < (3,):
|
||||
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
|
||||
|
||||
|
||||
def set_stdin_encoding():
|
||||
if not sys.stdin.isatty() and sys.version_info < (3,):
|
||||
sys.stdin = codecs.getreader("utf-8")(sys.stdin)
|
||||
if not sys.stdin.isatty() and sys.version_info < (3,):
|
||||
sys.stdin = codecs.getreader("utf-8")(sys.stdin)
|
||||
|
||||
|
||||
def convert_command_line_to_utf8():
|
||||
try:
|
||||
argv = []
|
||||
try:
|
||||
argv = []
|
||||
|
||||
for arg in sys.argv:
|
||||
argv.append(arg.decode(sys.stdin.encoding, "replace"))
|
||||
for arg in sys.argv:
|
||||
argv.append(arg.decode(sys.stdin.encoding, "replace"))
|
||||
|
||||
return argv
|
||||
except AttributeError:
|
||||
return sys.argv
|
||||
return argv
|
||||
except AttributeError:
|
||||
return sys.argv
|
||||
|
||||
|
||||
def check_terminal_encoding():
|
||||
if sys.stdout.isatty() and (sys.stdout.encoding is None or sys.stdin.encoding is None):
|
||||
print(
|
||||
_(
|
||||
"WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. "
|
||||
"The encoding can be configured with the environment variable 'PYTHONIOENCODING'."
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
if sys.stdout.isatty() and (sys.stdout.encoding is None or sys.stdin.encoding is None):
|
||||
print(
|
||||
_(
|
||||
"WARNING: The terminal encoding is not correctly configured. gitinspector might malfunction. "
|
||||
"The encoding can be configured with the environment variable 'PYTHONIOENCODING'."
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
|
||||
def get_excess_column_count(string):
|
||||
width_mapping = {"F": 2, "H": 1, "W": 2, "Na": 1, "N": 1, "A": 1}
|
||||
result = 0
|
||||
width_mapping = {"F": 2, "H": 1, "W": 2, "Na": 1, "N": 1, "A": 1}
|
||||
result = 0
|
||||
|
||||
for i in string:
|
||||
width = unicodedata.east_asian_width(i)
|
||||
result += width_mapping[width]
|
||||
for i in string:
|
||||
width = unicodedata.east_asian_width(i)
|
||||
result += width_mapping[width]
|
||||
|
||||
return result - len(string)
|
||||
return result - len(string)
|
||||
|
||||
|
||||
def ljust(string, pad):
|
||||
return string.ljust(pad - get_excess_column_count(string))
|
||||
return string.ljust(pad - get_excess_column_count(string))
|
||||
|
||||
|
||||
def rjust(string, pad):
|
||||
return string.rjust(pad - get_excess_column_count(string))
|
||||
return string.rjust(pad - get_excess_column_count(string))
|
||||
|
||||
|
||||
def output_progress(text, pos, length):
|
||||
if sys.stdout.isatty():
|
||||
(width, _unused) = get_size()
|
||||
progress_text = text.format(100 * pos / length)
|
||||
if sys.stdout.isatty():
|
||||
(width, _unused) = get_size()
|
||||
progress_text = text.format(100 * pos / length)
|
||||
|
||||
if len(progress_text) > width:
|
||||
progress_text = "...%s" % progress_text[-width + 3 :]
|
||||
if len(progress_text) > width:
|
||||
progress_text = "...%s" % progress_text[-width + 3 :]
|
||||
|
||||
print("\r{0}\r{1}".format(" " * width, progress_text), end="")
|
||||
sys.stdout.flush()
|
||||
print("\r{0}\r{1}".format(" " * width, progress_text), end="")
|
||||
sys.stdout.flush()
|
||||
|
|
|
@ -22,79 +22,79 @@ import datetime
|
|||
|
||||
|
||||
class TimelineData(object):
|
||||
def __init__(self, changes, useweeks):
|
||||
authordateinfo_list = sorted(changes.get_authordateinfo_list().items())
|
||||
self.changes = changes
|
||||
self.entries = {}
|
||||
self.total_changes_by_period = {}
|
||||
self.useweeks = useweeks
|
||||
def __init__(self, changes, useweeks):
|
||||
authordateinfo_list = sorted(changes.get_authordateinfo_list().items())
|
||||
self.changes = changes
|
||||
self.entries = {}
|
||||
self.total_changes_by_period = {}
|
||||
self.useweeks = useweeks
|
||||
|
||||
for i in authordateinfo_list:
|
||||
key = None
|
||||
for i in authordateinfo_list:
|
||||
key = None
|
||||
|
||||
if useweeks:
|
||||
yearweek = datetime.date(int(i[0][0][0:4]), int(i[0][0][5:7]), int(i[0][0][8:10])).isocalendar()
|
||||
key = (i[0][1], str(yearweek[0]) + "W" + "{0:02d}".format(yearweek[1]))
|
||||
else:
|
||||
key = (i[0][1], i[0][0][0:7])
|
||||
if useweeks:
|
||||
yearweek = datetime.date(int(i[0][0][0:4]), int(i[0][0][5:7]), int(i[0][0][8:10])).isocalendar()
|
||||
key = (i[0][1], str(yearweek[0]) + "W" + "{0:02d}".format(yearweek[1]))
|
||||
else:
|
||||
key = (i[0][1], i[0][0][0:7])
|
||||
|
||||
if self.entries.get(key, None) is None:
|
||||
self.entries[key] = i[1]
|
||||
else:
|
||||
self.entries[key].insertions += i[1].insertions
|
||||
self.entries[key].deletions += i[1].deletions
|
||||
if self.entries.get(key, None) is None:
|
||||
self.entries[key] = i[1]
|
||||
else:
|
||||
self.entries[key].insertions += i[1].insertions
|
||||
self.entries[key].deletions += i[1].deletions
|
||||
|
||||
for period in self.get_periods():
|
||||
total_insertions = 0
|
||||
total_deletions = 0
|
||||
for period in self.get_periods():
|
||||
total_insertions = 0
|
||||
total_deletions = 0
|
||||
|
||||
for author in self.get_authors():
|
||||
entry = self.entries.get((author[0], period), None)
|
||||
if entry is not None:
|
||||
total_insertions += entry.insertions
|
||||
total_deletions += entry.deletions
|
||||
for author in self.get_authors():
|
||||
entry = self.entries.get((author[0], period), None)
|
||||
if entry is not None:
|
||||
total_insertions += entry.insertions
|
||||
total_deletions += entry.deletions
|
||||
|
||||
self.total_changes_by_period[period] = (total_insertions, total_deletions, total_insertions + total_deletions)
|
||||
self.total_changes_by_period[period] = (total_insertions, total_deletions, total_insertions + total_deletions)
|
||||
|
||||
def get_periods(self):
|
||||
return sorted(set([i[1] for i in self.entries]))
|
||||
def get_periods(self):
|
||||
return sorted(set([i[1] for i in self.entries]))
|
||||
|
||||
def get_total_changes_in_period(self, period):
|
||||
return self.total_changes_by_period[period]
|
||||
def get_total_changes_in_period(self, period):
|
||||
return self.total_changes_by_period[period]
|
||||
|
||||
def get_authors(self):
|
||||
return sorted(set([(i[0][0], self.changes.get_latest_email_by_author(i[0][0])) for i in list(self.entries.items())]))
|
||||
def get_authors(self):
|
||||
return sorted(set([(i[0][0], self.changes.get_latest_email_by_author(i[0][0])) for i in list(self.entries.items())]))
|
||||
|
||||
def get_author_signs_in_period(self, author, period, multiplier):
|
||||
authorinfo = self.entries.get((author, period), None)
|
||||
total = float(self.total_changes_by_period[period][2])
|
||||
def get_author_signs_in_period(self, author, period, multiplier):
|
||||
authorinfo = self.entries.get((author, period), None)
|
||||
total = float(self.total_changes_by_period[period][2])
|
||||
|
||||
if authorinfo:
|
||||
i = multiplier * (self.entries[(author, period)].insertions / total)
|
||||
j = multiplier * (self.entries[(author, period)].deletions / total)
|
||||
return (int(i), int(j))
|
||||
else:
|
||||
return (0, 0)
|
||||
if authorinfo:
|
||||
i = multiplier * (self.entries[(author, period)].insertions / total)
|
||||
j = multiplier * (self.entries[(author, period)].deletions / total)
|
||||
return (int(i), int(j))
|
||||
else:
|
||||
return (0, 0)
|
||||
|
||||
def get_multiplier(self, period, max_width):
|
||||
multiplier = 0
|
||||
def get_multiplier(self, period, max_width):
|
||||
multiplier = 0
|
||||
|
||||
while True:
|
||||
for i in self.entries:
|
||||
entry = self.entries.get(i)
|
||||
while True:
|
||||
for i in self.entries:
|
||||
entry = self.entries.get(i)
|
||||
|
||||
if period == i[1]:
|
||||
changes_in_period = float(self.total_changes_by_period[i[1]][2])
|
||||
if multiplier * (entry.insertions + entry.deletions) / changes_in_period > max_width:
|
||||
return multiplier
|
||||
if period == i[1]:
|
||||
changes_in_period = float(self.total_changes_by_period[i[1]][2])
|
||||
if multiplier * (entry.insertions + entry.deletions) / changes_in_period > max_width:
|
||||
return multiplier
|
||||
|
||||
multiplier += 0.25
|
||||
multiplier += 0.25
|
||||
|
||||
def is_author_in_period(self, period, author):
|
||||
return self.entries.get((author, period), None) is not None
|
||||
def is_author_in_period(self, period, author):
|
||||
return self.entries.get((author, period), None) is not None
|
||||
|
||||
def is_author_in_periods(self, periods, author):
|
||||
for period in periods:
|
||||
if self.is_author_in_period(period, author):
|
||||
return True
|
||||
return False
|
||||
def is_author_in_periods(self, periods, author):
|
||||
for period in periods:
|
||||
if self.is_author_in_period(period, author):
|
||||
return True
|
||||
return False
|
||||
|
|
|
@ -25,7 +25,7 @@ localization.init()
|
|||
__version__ = "0.5.0dev"
|
||||
|
||||
__doc__ = _(
|
||||
"""Copyright © 2012-2015 Ejwa Software. All rights reserved.
|
||||
"""Copyright © 2012-2015 Ejwa Software. All rights reserved.
|
||||
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.
|
||||
|
@ -35,4 +35,4 @@ Written by Adam Waldenberg."""
|
|||
|
||||
|
||||
def output():
|
||||
print("gitinspector {0}\n".format(__version__) + __doc__)
|
||||
print("gitinspector {0}\n".format(__version__) + __doc__)
|
||||
|
|
Loading…
Add table
Reference in a new issue