diff --git a/src/hamster-cli b/src/hamster-cli index e856422d1..d30cb045f 100755 --- a/src/hamster-cli +++ b/src/hamster-cli @@ -215,8 +215,7 @@ class HamsterClient(object): end_time = end_time or start_time.replace(hour=23, minute=59, second=59) facts = self.storage.get_facts(start_time, end_time) - writer = reports.simple(facts, start_time, end_time, export_format) - print(writer.export()) + writer = reports.simple(facts, start_time.date(), end_time.date(), export_format) def _activities(self, search=""): diff --git a/src/hamster/client.py b/src/hamster/client.py index 1332ab1f1..e8a9fe5e6 100644 --- a/src/hamster/client.py +++ b/src/hamster/client.py @@ -36,8 +36,6 @@ def from_dbus_fact(fact): category = fact[6], tags = fact[7], date = dt.datetime.utcfromtimestamp(fact[8]).date(), - delta = dt.timedelta(days = fact[9] // (24 * 60 * 60), - seconds = fact[9] % (24 * 60 * 60)), id = fact[0] ) diff --git a/src/hamster/lib/__init__.py b/src/hamster/lib/__init__.py index b31fde5d9..2d24b0992 100644 --- a/src/hamster/lib/__init__.py +++ b/src/hamster/lib/__init__.py @@ -70,7 +70,7 @@ def figure_time(str_time): class Fact(object): def __init__(self, activity="", category = "", description = "", tags = "", - start_time = None, end_time = None, id = None, delta = None, + start_time = None, end_time = None, id = None, date = None, activity_id = None, initial_fact=None): """Homogeneous chunk of activity. The category, description and tags can be either passed in explicitly @@ -108,7 +108,6 @@ def __init__(self, activity="", category = "", description = "", tags = "", self.end_time = None self.id = id self.ponies = False - self.delta = delta self.activity_id = activity_id phase = "start_time" if date else "date" @@ -135,9 +134,13 @@ def as_dict(self): 'date': calendar.timegm(date.timetuple()) if date else "", 'start_time': self.start_time if isinstance(self.start_time, str) else calendar.timegm(self.start_time.timetuple()), 'end_time': self.end_time if isinstance(self.end_time, str) else calendar.timegm(self.end_time.timetuple()) if self.end_time else "", - 'delta': self.delta.seconds + self.delta.days * 24 * 60 * 60 if self.delta else "" #duration in seconds + 'delta': self.delta.total_seconds() # ugly, but needed for report.py } + def copy(self, **kwds): + """Return an independent copy, with overrides as keyword arguments.""" + return Fact(initial_fact=self, **kwds) + @property def date(self): """hamster day, determined from start_time. @@ -157,6 +160,12 @@ def date(self, value): if self.end_time: self.end_time = hamsterday_time_to_datetime(value, self.end_time.time()) + @property + def delta(self): + """Duration (datetime.timedelta).""" + end_time = self.end_time if self.end_time else dt.datetime.now() + return end_time - self.start_time + def serialized_name(self): res = self.activity diff --git a/src/hamster/lib/stuff.py b/src/hamster/lib/stuff.py index 43789a7f7..99687b6a3 100644 --- a/src/hamster/lib/stuff.py +++ b/src/hamster/lib/stuff.py @@ -172,7 +172,7 @@ def duration_minutes(duration): return duration_minutes(res) elif isinstance(duration, dt.timedelta): - return duration.seconds / 60 + duration.days * 24 * 60 + return duration.total_seconds() / 60 else: return duration diff --git a/src/hamster/reports.py b/src/hamster/reports.py index 69314c802..a29f8511f 100644 --- a/src/hamster/reports.py +++ b/src/hamster/reports.py @@ -28,6 +28,7 @@ import re import codecs from string import Template +from textwrap import dedent from hamster.lib.configuration import runtime from hamster.lib import stuff, trophies @@ -75,33 +76,25 @@ def simple(facts, start_date, end_date, format, path = None): class ReportWriter(object): #a tiny bit better than repeating the code all the time def __init__(self, path = None, datetime_format = "%Y-%m-%d %H:%M:%S"): - self.file = open(path, "w") if path else codecs.getwriter("utf8")(StringIO()) + # if path is empty or None, print to stdout + self.file = open(path, "w") if path else StringIO() + self.path = path self.datetime_format = datetime_format - def export(self): - return self.file.getvalue() - def write_report(self, facts): try: for fact in facts: - fact.activity= fact.activity fact.description = (fact.description or "") fact.category = (fact.category or _("Unsorted")) - if self.datetime_format: - fact.start_time = fact.start_time.strftime(self.datetime_format) - - if fact.end_time: - fact.end_time = fact.end_time.strftime(self.datetime_format) - else: - fact.end_time = "" - self._write_fact(fact) self._finish(facts) finally: - if isinstance(self.file, IOBase): - self.file.close() + if not self.path: + # print the full report to stdout + print(self.file.getvalue()) + self.file.close() def _start(self, facts): raise NotImplementedError @@ -112,6 +105,7 @@ def _write_fact(self, fact): def _finish(self, facts): raise NotImplementedError + class ICalWriter(ReportWriter): """a lame ical writer, could not be bothered with finding a library""" def __init__(self, path): @@ -126,18 +120,21 @@ def _write_fact(self, fact): if fact.category == _("Unsorted"): fact.category = None - self.file.write("""BEGIN:VEVENT -CATEGORIES:%(category)s -DTSTART:%(start_time)s -DTEND:%(end_time)s -SUMMARY:%(activity)s -DESCRIPTION:%(description)s -END:VEVENT -""" % dict(fact)) + event_str = f"""\ + BEGIN:VEVENT + CATEGORIES:{fact.category} + DTSTART:{fact.start_time} + DTEND:{fact.end_time} + SUMMARY:{fact.activity} + DESCRIPTION:{fact.description} + END:VEVENT + """ + self.file.write(dedent(event_str)) def _finish(self, facts): self.file.write("END:VCALENDAR\n") + class TSVWriter(ReportWriter): def __init__(self, path): ReportWriter.__init__(self, path) @@ -160,11 +157,10 @@ def __init__(self, path): self.csv_writer.writerow([h for h in headers]) def _write_fact(self, fact): - fact.delta = stuff.duration_minutes(fact.delta) self.csv_writer.writerow([fact.activity, fact.start_time, fact.end_time, - fact.delta, + str(stuff.duration_minutes(fact.delta)), fact.category, fact.description, ", ".join(fact.tags)]) @@ -180,8 +176,8 @@ def __init__(self, path): def _write_fact(self, fact): activity = self.doc.createElement("activity") activity.setAttribute("name", fact.activity) - activity.setAttribute("start_time", fact.start_time) - activity.setAttribute("end_time", fact.end_time) + activity.setAttribute("start_time", str(fact.start_time)) + activity.setAttribute("end_time", str(fact.end_time)) activity.setAttribute("duration_minutes", str(stuff.duration_minutes(fact.delta))) activity.setAttribute("category", fact.category) activity.setAttribute("description", fact.description) @@ -297,7 +293,6 @@ def _finish(self, facts): date_facts.append([str_date, by_date.get(date, [])]) date += dt.timedelta(days=1) - data = dict( title = self.title,