From 9983cffa87865b3bf5a0cef7044ed94a0a3ebdc4 Mon Sep 17 00:00:00 2001 From: Maksym Sobolyev Date: Wed, 5 Mar 2014 14:02:25 -0700 Subject: [PATCH 1/6] Add new "-M" option, which when used in conjunction with "-w" will cause gitinspector output *both* monthly and weekly stats for the period in question. --- gitinspector/gitinspector.py | 7 ++++++- gitinspector/output/timelineoutput.py | 16 +++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/gitinspector/gitinspector.py b/gitinspector/gitinspector.py index b725fb23..6cbea24a 100644 --- a/gitinspector/gitinspector.py +++ b/gitinspector/gitinspector.py @@ -50,6 +50,7 @@ def __init__(self): self.grading = False self.timeline = False self.useweeks = False + self.forcemonths = False def process(self, repos): localization.check_compatibility(version.__version__) @@ -86,6 +87,8 @@ def process(self, repos): outputable.output(BlameOutput(summed_changes, summed_blames)) if self.timeline: + if self.useweeks and self.forcemonths: + outputable.output(TimelineOutput(summed_changes, False)) outputable.output(TimelineOutput(summed_changes, self.useweeks)) if self.include_metrics: @@ -133,7 +136,7 @@ def main(): repos = [] try: - opts, args = optval.gnu_getopt(argv[1:], "f:F:hHlLmrTwx:", ["exclude=", "file-types=", "format=", + opts, args = optval.gnu_getopt(argv[1:], "f:F:hHlLmrTwMx:", ["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"]) @@ -194,6 +197,8 @@ def main(): interval.set_until(a) elif o == "-w": run.useweeks = True + elif o == "-M": + run.forcemonths = True elif o == "--weeks": run.useweeks = optval.get_boolean_argument(a) elif o in("-x", "--exclude"): diff --git a/gitinspector/output/timelineoutput.py b/gitinspector/output/timelineoutput.py index f76228bf..dc8301cf 100644 --- a/gitinspector/output/timelineoutput.py +++ b/gitinspector/output/timelineoutput.py @@ -24,7 +24,8 @@ from .. import format, gravatar, terminal, timeline from .outputable import Outputable -TIMELINE_INFO_TEXT = N_("The following history timeline has been gathered from the repository") +TIMELINE_INFO_TEXT_M = N_("The following monthly history timeline has been gathered from the repository") +TIMELINE_INFO_TEXT_W = N_("The following weekly history timeline has been gathered from the repository") MODIFIED_ROWS_TEXT = N_("Modified Rows:") def __output_row__text__(timeline_data, periods, names): @@ -102,9 +103,14 @@ def __init__(self, changes, useweeks): self.useweeks = useweeks Outputable.__init__(self) + def get_tinfo_txt(self): + if self.useweeks: + return (_(TIMELINE_INFO_TEXT_W)) + return (_(TIMELINE_INFO_TEXT_M)) + def output_text(self): if self.changes.get_commits(): - print("\n" + textwrap.fill(_(TIMELINE_INFO_TEXT) + ":", width=terminal.get_size()[0])) + print("\n" + textwrap.fill(self.get_tinfo_txt() + ":", width=terminal.get_size()[0])) timeline_data = timeline.TimelineData(self.changes, self.useweeks) periods = timeline_data.get_periods() @@ -123,7 +129,7 @@ def output_html(self): max_periods_per_row = 8 timeline_xml = "
" - timeline_xml += "

" + _(TIMELINE_INFO_TEXT) + ".

" + timeline_xml += "

" + self.get_tinfo_txt() + ".

" print(timeline_xml) for i in range(0, len(periods), max_periods_per_row): @@ -134,7 +140,7 @@ def output_html(self): def output_json(self): if self.changes.get_commits(): - message_json = "\t\t\t\"message\": \"" + _(TIMELINE_INFO_TEXT) + "\",\n" + message_json = "\t\t\t\"message\": \"" + self.get_tinfo_txt() + "\",\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" @@ -174,7 +180,7 @@ def output_json(self): def output_xml(self): if self.changes.get_commits(): - message_xml = "\t\t" + _(TIMELINE_INFO_TEXT) + "\n" + message_xml = "\t\t" + self.get_tinfo_txt() + "\n" timeline_xml = "" periods_xml = "\t\t\n".format("week" if self.useweeks else "month") From 364be357ccc9d520b9b781110641404bbe3b365a Mon Sep 17 00:00:00 2001 From: Maksym Sobolyev Date: Tue, 9 Feb 2016 10:34:01 -0800 Subject: [PATCH 2/6] Add --forcemonths option. Suggested by: @adam-waldenberg --- gitinspector/config.py | 1 + gitinspector/gitinspector.py | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gitinspector/config.py b/gitinspector/config.py index 4eaf15c6..12da453b 100644 --- a/gitinspector/config.py +++ b/gitinspector/config.py @@ -73,6 +73,7 @@ def read(self): 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.forcemonths = self.__read_git_config_bool__("forcemonths") var = self.__read_git_config_string__("since") if var[0]: diff --git a/gitinspector/gitinspector.py b/gitinspector/gitinspector.py index 6cbea24a..10c480ca 100644 --- a/gitinspector/gitinspector.py +++ b/gitinspector/gitinspector.py @@ -139,7 +139,7 @@ def main(): opts, args = optval.gnu_getopt(argv[1:], "f:F:hHlLmrTwMx:", ["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"]) + "timeline:true", "until=", "version", "weeks:true", "forcemonths:false"]) repos = __get_validated_git_repos__(set(args)) #We need the repos above to be set before we read the git config. @@ -197,10 +197,12 @@ def main(): interval.set_until(a) elif o == "-w": run.useweeks = True - elif o == "-M": - run.forcemonths = True elif o == "--weeks": run.useweeks = optval.get_boolean_argument(a) + elif o == "-M": + run.forcemonths = True + elif o == "--forcemonths": + run.forcemonths = optval.get_boolean_argument(a) elif o in("-x", "--exclude"): if clear_x_on_next_pass: clear_x_on_next_pass = False From 92f8b1a4d028739771254eeed9e0ba82ecad4be3 Mon Sep 17 00:00:00 2001 From: Maksym Sobolyev Date: Tue, 9 Feb 2016 12:14:41 -0800 Subject: [PATCH 3/6] Output both age in months and age in weeks when -wM is specified. Suggested by: @adam-waldenberg --- gitinspector/blame.py | 25 ++++++++++--- gitinspector/gitinspector.py | 2 +- gitinspector/output/blameoutput.py | 60 +++++++++++++++++++++++------- 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/gitinspector/blame.py b/gitinspector/blame.py index 3c964cbe..76797b48 100644 --- a/gitinspector/blame.py +++ b/gitinspector/blame.py @@ -32,8 +32,18 @@ class BlameEntry(object): rows = 0 - skew = 0 # Used when calculating average code age. + skew_w = 0 # Used when calculating average code age. + skew_m = 0 # Used when calculating average code age. comments = 0 + useweeks = None + + def __init__(self, useweeks): + self.useweeks = useweeks + + def get_skew(self, forcemonths = False): + if not self.useweeks or forcemonths: + return self.skew_m + return self.skew_w __thread_lock__ = threading.BoundedSemaphore(NUM_THREADS) __blame_lock__ = threading.Lock() @@ -79,14 +89,15 @@ def __handle_blamechunk_content__(self, content): __blame_lock__.acquire() # Global lock used to protect calls from here... if self.blames.get((author, self.filename), None) == None: - self.blames[(author, self.filename)] = BlameEntry() + self.blames[(author, self.filename)] = BlameEntry(self.useweeks) 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)) + skew = (self.changes.last_commit_date - self.blamechunk_time).days; + self.blames[(author, self.filename)].skew_w += (skew / 7.0) + self.blames[(author, self.filename)].skew_m += (skew / AVG_DAYS_PER_MONTH) __blame_lock__.release() # ...to here. @@ -123,6 +134,7 @@ def run(self): class Blame(object): def __init__(self, repo, hard, useweeks, changes): self.blames = {} + self.useweeks = useweeks ls_tree_r = subprocess.Popen(["git", "ls-tree", "--name-only", "-r", interval.get_ref()], bufsize=1, stdout=subprocess.PIPE).stdout lines = ls_tree_r.readlines() @@ -190,10 +202,11 @@ def get_summed_blames(self): summed_blames = {} for i in self.blames.items(): if summed_blames.get(i[0][0], None) == None: - summed_blames[i[0][0]] = BlameEntry() + summed_blames[i[0][0]] = BlameEntry(self.useweeks) summed_blames[i[0][0]].rows += i[1].rows - summed_blames[i[0][0]].skew += i[1].skew + summed_blames[i[0][0]].skew_w += i[1].skew_w + summed_blames[i[0][0]].skew_m += i[1].skew_m summed_blames[i[0][0]].comments += i[1].comments return summed_blames diff --git a/gitinspector/gitinspector.py b/gitinspector/gitinspector.py index 10c480ca..aa570b52 100644 --- a/gitinspector/gitinspector.py +++ b/gitinspector/gitinspector.py @@ -84,7 +84,7 @@ def process(self, repos): outputable.output(ChangesOutput(summed_changes)) if changes.get_commits(): - outputable.output(BlameOutput(summed_changes, summed_blames)) + outputable.output(BlameOutput(summed_changes, summed_blames, self.forcemonths)) if self.timeline: if self.useweeks and self.forcemonths: diff --git a/gitinspector/output/blameoutput.py b/gitinspector/output/blameoutput.py index f49ee63b..63e09adc 100644 --- a/gitinspector/output/blameoutput.py +++ b/gitinspector/output/blameoutput.py @@ -31,19 +31,25 @@ "intact in the current revision") class BlameOutput(Outputable): - def __init__(self, changes, blame): + def __init__(self, changes, blame, forcemonths): if format.is_interactive_format(): print("") self.changes = changes self.blame = blame + self.forcemonths = forcemonths Outputable.__init__(self) def output_html(self): blame_xml = "
" blame_xml += "

" + _(BLAME_INFO_TEXT) + ".

" - blame_xml += "".format( - _("Author"), _("Rows"), _("Stability"), _("Age"), _("% in comments")) + if self.forcemonths and self.blame.useweeks: + formtup = (_("Author"), _("Rows"), _("Stability"), _("Age, months"), _("Age, weeks"), _("% in comments")) + formmkup = "" + else: + formtup = (_("Author"), _("Rows"), _("Stability"), _("Age"), _("% in comments")) + formmkup = "" + blame_xml += formmkup.format(*formtup) blame_xml += "" chart_data = "" blames = sorted(self.blame.get_summed_blames().items()) @@ -64,7 +70,9 @@ def output_html(self): blame_xml += "" blame_xml += "") - blame_xml += "" + if self.forcemonths and self.blame.useweeks: + blame_xml += "" + blame_xml += "" blame_xml += "" blame_xml += "" blame_xml += "" @@ -73,7 +81,7 @@ def output_html(self): if blames[-1] != entry: chart_data += ", " - blame_xml += "
{0} {1} {2} {3} {4}
{0} {1} {2} {3} {4} {5}
{0} {1} {2} {3} {4}
" + str(entry[1].rows) + "" + ("{0:.1f}".format(Blame.get_stability(entry[0], entry[1].rows, self.changes)) + "" + "{0:.1f}".format(float(entry[1].skew) / entry[1].rows) + "" + "{0:.1f}".format(float(entry[1].get_skew(True)) / entry[1].rows) + "" + "{0:.1f}".format(float(entry[1].get_skew()) / entry[1].rows) + "" + "{0:.2f}".format(100.0 * entry[1].comments / entry[1].rows) + "" + work_percentage + "
 
" + blame_xml += "   " % len(formtup) blame_xml += "
" blame_xml += "