From 242143f8aed64b08ee0b13f41a9314d5c579b206 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Campos Date: Thu, 1 Oct 2015 09:23:07 +0200 Subject: [PATCH 1/5] Show the duration of every task in the stories text It's shown as (HH:MM story) making it possible to know the duration of each task done in a particular day. --- phpreport-report | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpreport-report b/phpreport-report index 8bc4102..83aa806 100755 --- a/phpreport-report +++ b/phpreport-report @@ -318,7 +318,7 @@ class DetailedReport(Report): def get_stories_for_day_and_user(self, user, date): tasks_for_day = self.time_period.filter_tasks(date=date, user=user) - all_stories = " ".join([task.text + " " + task.story for task in tasks_for_day]) + all_stories = " ".join([task.text + " (" + DateUtils.format_delta(task.length()) + " " + task.story + ")" for task in tasks_for_day]) # Strip out duplicated whitespace return re.compile(r'\s+').sub(' ', all_stories).strip() From a7d96c360960b853a3e58bcf75c0f87a14c8fe3e Mon Sep 17 00:00:00 2001 From: Carlos Garcia Campos Date: Thu, 1 Oct 2015 09:33:58 +0200 Subject: [PATCH 2/5] Start every task in a new line in text reports It makes easier to read for days having several tasks. --- phpreport-report | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/phpreport-report b/phpreport-report index 83aa806..7d9081f 100755 --- a/phpreport-report +++ b/phpreport-report @@ -276,6 +276,9 @@ class AggregateReport(Report): class DetailedReport(Report): + + EOS = "PhpReportReportEOS" + def __init__(self, time_period, parent, formatter): if parent: header = "{0} for {1}".format(time_period, parent.header) @@ -318,7 +321,7 @@ class DetailedReport(Report): def get_stories_for_day_and_user(self, user, date): tasks_for_day = self.time_period.filter_tasks(date=date, user=user) - all_stories = " ".join([task.text + " (" + DateUtils.format_delta(task.length()) + " " + task.story + ")" for task in tasks_for_day]) + all_stories = " ".join([task.text + " (" + DateUtils.format_delta(task.length()) + " " + task.story + ")" + self.EOS for task in tasks_for_day]) # Strip out duplicated whitespace return re.compile(r'\s+').sub(' ', all_stories).strip() @@ -328,7 +331,7 @@ class DetailedReport(Report): all_dates = self.time_period.get_all_dates() contents = [(date.strftime("%A"), self.get_stories_for_day_and_user(user, date)) for date in all_dates] - self.formatter.generate_aligned_list(contents) + self.formatter.generate_aligned_list(contents, self.EOS) def generate_report(self): self.pieces = [] @@ -365,19 +368,21 @@ class TextFormatter(object): for row in table[1:]: self.generate_table_row(row, lengths) - def generate_aligned_list(self, contents): + def generate_aligned_list(self, contents, separator = None): first_column_size = max([len(content[0]) for content in contents]) format_string = "%%%i.%is: %%s\n" % (first_column_size, first_column_size) indent = (first_column_size + 2) * ' ' # Enough to account for the day name offset. width = 80 - len(indent) for content in contents: - second_column = textwrap.fill(content[1], - break_long_words=False, # Don't break URLs. - width=width, - initial_indent=indent, - subsequent_indent=indent).strip() - self.pieces.append(format_string % (content[0], second_column)) + second_column = [] + for item in content[1].split(separator): + second_column.append(textwrap.fill(item.strip(), + break_long_words=False, # Don't break URLs. + width=width, + initial_indent=indent, + subsequent_indent=indent)) + self.pieces.append(format_string % (content[0], "\n".join(second_column).strip())) def generate_header(self, header): self.pieces.append("\n%s\n" % header) @@ -430,9 +435,13 @@ class TwikiFormatter(TextFormatter): def generate_section_header(self, header): self.pieces.append("\n---++++%s\n" % header) - def generate_aligned_list(self, contents): + def generate_aligned_list(self, contents, separator = None): for content in contents: - self.pieces.append(" * *%s* - %s\n" % (content[0], content[1])) + if separator is not None: + text = content[1].replace(separator, "") + else: + text = content[1] + self.pieces.append(" * *%s* - %s\n" % (content[0], text)) def send_url_request(request): From a252e2b2f4491787ca50404d3c3694a21e45b958 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Campos Date: Thu, 1 Oct 2015 09:40:57 +0200 Subject: [PATCH 3/5] Do not show days with no tasks for the detailed stories in text reports --- phpreport-report | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/phpreport-report b/phpreport-report index 7d9081f..a45e602 100755 --- a/phpreport-report +++ b/phpreport-report @@ -382,7 +382,9 @@ class TextFormatter(object): width=width, initial_indent=indent, subsequent_indent=indent)) - self.pieces.append(format_string % (content[0], "\n".join(second_column).strip())) + text = "\n".join(second_column).strip() + if text: + self.pieces.append(format_string % (content[0], text)) def generate_header(self, header): self.pieces.append("\n%s\n" % header) From 8d2a6ce371b4443618b96f4b132b67d1f3ed2cfe Mon Sep 17 00:00:00 2001 From: Carlos Garcia Campos Date: Tue, 3 Nov 2015 10:45:46 +0100 Subject: [PATCH 4/5] Add total hours per story in aggregate report It helps a lot to find mistakes. --- phpreport-report | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/phpreport-report b/phpreport-report index a45e602..8e3188f 100755 --- a/phpreport-report +++ b/phpreport-report @@ -142,6 +142,14 @@ class PeriodOfWork(object): self.filter_tasks(date, day_offset, user, only_onsite)], datetime.timedelta()) + def stories(self): + stories = {} + self.get_tasks_if_necessary() + for task in self.tasks: + stories.setdefault(task.story, datetime.timedelta()) + stories[task.story] += task.length() + return stories + @staticmethod def fetch_tasks_for_all(periods): filters = [period.filter for period in periods] @@ -261,11 +269,23 @@ class AggregateReport(Report): table_contents = [] total = datetime.timedelta() total_onsite = datetime.timedelta() + total_stories = {} for period in self.time_periods: (time, time_onsite) = self.generate_report_for_period(period, table_contents) total += time total_onsite += time_onsite + stories = period.stories() + for story in stories: + total_stories.setdefault(story, datetime.timedelta()) + total_stories[story] += stories[story] + + self.formatter.generate_table(table_contents, has_headers=False) + + self.formatter.generate_header("Total hours per story") + table_contents = [] + for story in total_stories: + table_contents.append([story, "%s" % DateUtils.format_delta(total_stories[story])]) self.formatter.generate_table(table_contents, has_headers=False) self.formatter.generate_header( From b889c1c4ec4d067538b42f9f4f5b2e7062485abc Mon Sep 17 00:00:00 2001 From: Carlos Garcia Campos Date: Mon, 18 Dec 2017 10:42:54 +0100 Subject: [PATCH 5/5] Do not show a space when task doesn't have a story --- phpreport-report | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/phpreport-report b/phpreport-report index 8e3188f..7383ae0 100755 --- a/phpreport-report +++ b/phpreport-report @@ -340,8 +340,15 @@ class DetailedReport(Report): self.formatter.generate_large_text("Onsite hours worked: %s" % DateUtils.format_delta(onsite_time)) def get_stories_for_day_and_user(self, user, date): + def story_text(task): + text = " (" + DateUtils.format_delta(task.length()) + if task.story: + text += " " + task.story + text += ")" + return text + tasks_for_day = self.time_period.filter_tasks(date=date, user=user) - all_stories = " ".join([task.text + " (" + DateUtils.format_delta(task.length()) + " " + task.story + ")" + self.EOS for task in tasks_for_day]) + all_stories = " ".join([task.text + story_text(task) + self.EOS for task in tasks_for_day]) # Strip out duplicated whitespace return re.compile(r'\s+').sub(' ', all_stories).strip()