From 3ea214783c24d77c87dcf8e9aae40212e80ca1ae Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Thu, 7 Apr 2016 12:32:39 +0200 Subject: [PATCH 1/4] Add summary option. --- changelog | 6 +++ lib/ctt/report.py | 100 +++++++++++++++++++++++++++++++++------------- scripts/ctt | 2 + 3 files changed, 81 insertions(+), 27 deletions(-) diff --git a/changelog b/changelog index 99211e2..b94e516 100644 --- a/changelog +++ b/changelog @@ -4,6 +4,12 @@ Changelog * Changes are always commented with their author in (braces) * Exception: No braces means author == Nico Schottelius +Next: + * Added -s, --summary option (Darko Poljak) + * No args error (Darko Poljak) + * Report project name as file path basename (Darko Poljak) + * Report globbing (Darko Poljak) + 1.0: 2014-07-01 * Added installer (Oz Nahum) * Bugfix in listprojects (Oz Nahum) diff --git a/lib/ctt/report.py b/lib/ctt/report.py index d69186d..ac11374 100755 --- a/lib/ctt/report.py +++ b/lib/ctt/report.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- # # 2012 Nico Schottelius (nico-ctt at schottelius.org) +# 2016 Darko Poljak (darko.poljak at gmail.com) # # This file is part of ctt. # @@ -70,14 +71,50 @@ class Report(object): projects.extend(fnames) total_time = 0 + entries = {} for project in projects: - report = cls(project, args.start, args.end, args.output_format, args.regexp, args.ignore_case) - report.report() + report = cls(project=project, start_date=args.start, + end_date=args.end, output_format=args.output_format, + regexp=args.regexp, ignore_case=args.ignore_case) + project_report = report.report(args.summary) + if args.summary: + cls.update_summary_report(entries, project_report) + else: + report.print_report(project_report, args.output_format) total_time = total_time + report.total_time + if args.summary: + cls.print_summary_report(entries, args.output_format) cls.summary(total_time) + @staticmethod + def update_summary_report(report, entry): + for time in entry: + if not time in report: + report[time] = [] + report[time].extend(entry[time]) + + + @staticmethod + def print_summary_report(report, output_format): + Report.print_report_entries(report, output_format, sorted_keys=True) + + @staticmethod + def print_report_entries(report, output_format, sorted_keys=False): + if sorted_keys: + keys = sorted(report.keys()) + else: + keys = report.keys() + for time in keys: + entries = report[time] + for entry in entries: + print(output_format.format_map(entry)) + + def print_report(self, report, output_format): + self.header() + self.print_report_entries(report, output_format) + def _init_date(self, start_date, end_date): """Setup date - either default or user given values""" @@ -154,9 +191,8 @@ class Report(object): else: log.debug("Skipping: %s" % dirname) - def report(self): - self.header() - self.list_entries() + def report(self, summary=False): + return self.list_entries(summary) def header(self): project_name = os.path.basename(self.project) @@ -183,29 +219,39 @@ class Report(object): return count - def list_entries(self): + def _get_report_entry(self, time, entry): + report = {} + start_datetime = datetime.datetime.strptime(time, ctt.DATETIMEFORMAT) + delta = datetime.timedelta(seconds=int(float(entry['delta']))) + end_datetime = (start_datetime + delta).replace(microsecond = 0) + + report['start_datetime'] = start_datetime.strftime(ctt.DATETIMEFORMAT) + report['end_datetime'] = end_datetime.strftime(ctt.DATETIMEFORMAT) + + report['delta'] = delta + report['delta_seconds'] = int(float(entry['delta'])) + report['delta_minutes'] = int(report['delta_seconds']/60) + + if 'comment' in entry: + report['comment'] = entry['comment'] + else: + report['comment'] = False + return report + + + def list_entries(self, summary=False): """Return total time tracked""" - sorted_times = sorted(self._report_db.keys()) + entries = {} + if summary: + time_keys = self._report_db.keys() + else: + time_keys = sorted(self._report_db.keys()) - for time in sorted_times: + for time in time_keys: entry = self._report_db[time] - report = {} - - start_datetime = datetime.datetime.strptime(time, ctt.DATETIMEFORMAT) - delta = datetime.timedelta(seconds=int(float(entry['delta']))) - end_datetime = (start_datetime + delta).replace(microsecond = 0) - - report['start_datetime'] = start_datetime.strftime(ctt.DATETIMEFORMAT) - report['end_datetime'] = end_datetime.strftime(ctt.DATETIMEFORMAT) - - report['delta'] = delta - report['delta_seconds'] = int(float(entry['delta'])) - report['delta_minutes'] = int(report['delta_seconds']/60) - - if 'comment' in entry: - report['comment'] = entry['comment'] - else: - report['comment'] = False - - print(self.output_format.format_map(report)) + report = self._get_report_entry(time, entry) + if not time in entries: + entries[time] = [] + entries[time].append(report) + return entries diff --git a/scripts/ctt b/scripts/ctt index 8dd8292..a585206 100755 --- a/scripts/ctt +++ b/scripts/ctt @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- # # 2012-2015 Nico Schottelius (nico-ctt at schottelius.org) +# 2016 Darko Poljak (darko.poljak at gmail.com) # # This file is part of ctt. # @@ -78,6 +79,7 @@ def parse_argv(argv, version): parser['report'].add_argument("-i", "--ignore-case", help="ignore case distinctions", action="store_true") parser['report'].add_argument("-f", "--format", help="output format (default: %s)" % ctt.REPORTFORMAT, default=ctt.REPORTFORMAT, dest="output_format") + parser['report'].add_argument("-s", "--summary", help="hie project names and list time entries in chronological order", action="store_true") #parser['track'].add_argument("-t", "--tag", help="Add tags", # action="store_true") From 4663003b697590560f6a54e731509e68a24fd01c Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Thu, 7 Apr 2016 18:26:34 +0200 Subject: [PATCH 2/4] Remove unnecessary code. --- lib/ctt/report.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/ctt/report.py b/lib/ctt/report.py index ac11374..b219a3e 100755 --- a/lib/ctt/report.py +++ b/lib/ctt/report.py @@ -192,7 +192,7 @@ class Report(object): log.debug("Skipping: %s" % dirname) def report(self, summary=False): - return self.list_entries(summary) + return self.list_entries() def header(self): project_name = os.path.basename(self.project) @@ -239,15 +239,11 @@ class Report(object): return report - def list_entries(self, summary=False): + def list_entries(self): """Return total time tracked""" entries = {} - if summary: - time_keys = self._report_db.keys() - else: - time_keys = sorted(self._report_db.keys()) - + time_keys = self._report_db.keys() for time in time_keys: entry = self._report_db[time] report = self._get_report_entry(time, entry) From f5123d33284ae6a62d128fb901a43f94e78c6f5e Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Thu, 7 Apr 2016 22:22:56 +0200 Subject: [PATCH 3/4] Code improvements. --- lib/ctt/report.py | 81 +++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/lib/ctt/report.py b/lib/ctt/report.py index b219a3e..6418cb0 100755 --- a/lib/ctt/report.py +++ b/lib/ctt/report.py @@ -32,6 +32,7 @@ import os.path import re import sys import glob +import collections import ctt import ctt.listprojects @@ -71,49 +72,61 @@ class Report(object): projects.extend(fnames) total_time = 0 - entries = {} + reports = collections.OrderedDict() for project in projects: report = cls(project=project, start_date=args.start, end_date=args.end, output_format=args.output_format, regexp=args.regexp, ignore_case=args.ignore_case) - project_report = report.report(args.summary) - if args.summary: - cls.update_summary_report(entries, project_report) - else: - report.print_report(project_report, args.output_format) + report_data = report.report() + reports[report.project] = (report, report_data) total_time = total_time + report.total_time - if args.summary: - cls.print_summary_report(entries, args.output_format) + cls.print_reports(reports, args.output_format, args.summary) cls.summary(total_time) @staticmethod - def update_summary_report(report, entry): - for time in entry: - if not time in report: - report[time] = [] - report[time].extend(entry[time]) - - - @staticmethod - def print_summary_report(report, output_format): - Report.print_report_entries(report, output_format, sorted_keys=True) - - @staticmethod - def print_report_entries(report, output_format, sorted_keys=False): - if sorted_keys: - keys = sorted(report.keys()) + def print_report_time_entries(report_data, output_format, summary): + ''' Print time entries from report_data report using output_format. + + If summary is True then the order of times (keys) is + sorted. + ''' + if summary: + keys = sorted(report_data.keys()) else: - keys = report.keys() + keys = report_data.keys() for time in keys: - entries = report[time] + entries = report_data[time] for entry in entries: print(output_format.format_map(entry)) - def print_report(self, report, output_format): - self.header() - self.print_report_entries(report, output_format) + @staticmethod + def print_reports(reports, output_format, summary): + ''' Print reports using output_format for each entry. + + If summary is True then all time entries from all + projects is extracted to one report dict. + Otherwise, all time entries by each project is printed. + ''' + if summary: + summary_report = {} + for project in reports: + report, report_data = reports[project] + for time in report_data: + if summary: + if not time in summary_report: + summary_report[time] = report_data[time] + else: + summary_report[time].extend(report_data[time]) + else: + report.header() + Report.print_report_time_entries(report_data, + output_format, summary) + if summary: + Report.print_report_time_entries(summary_report, + output_format, summary) + def _init_date(self, start_date, end_date): """Setup date - either default or user given values""" @@ -191,9 +204,6 @@ class Report(object): else: log.debug("Skipping: %s" % dirname) - def report(self, summary=False): - return self.list_entries() - def header(self): project_name = os.path.basename(self.project) print("Report for %s between %s and %s" % @@ -220,6 +230,8 @@ class Report(object): def _get_report_entry(self, time, entry): + ''' Get one time entry data. + ''' report = {} start_datetime = datetime.datetime.strptime(time, ctt.DATETIMEFORMAT) delta = datetime.timedelta(seconds=int(float(entry['delta']))) @@ -239,7 +251,7 @@ class Report(object): return report - def list_entries(self): + def report(self): """Return total time tracked""" entries = {} @@ -248,6 +260,7 @@ class Report(object): entry = self._report_db[time] report = self._get_report_entry(time, entry) if not time in entries: - entries[time] = [] - entries[time].append(report) + entries[time] = [report] + else: + entries[time].append(report) return entries From 0c90b7b2e7a5453daac874cb8b2815b2ecc939a1 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Fri, 8 Apr 2016 08:47:39 +0200 Subject: [PATCH 4/4] Update .gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 8a1d86d..2f4a08b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ +__pycache__/ lib/ctt/__pycache__/ +*.pyc + +# -vim +.*.swp # Manpages *.1