Skip to content

Commit

Permalink
Merge pull request #375 from ederag/fix_cmdline_export
Browse files Browse the repository at this point in the history
Fix command line export (PR #375)

Fix issue #373.
Export to ical, tsv, xml and html work.

- Now `Fact.delta` is calculated upon access.
  This fixes durations that were not updated in the overview
  for the running activity.
- Pass dates to reports, not datetimes.
- Remove the `ReportWriter.export` function.
- Create Fact.copy()
  • Loading branch information
ederag authored Jan 12, 2019
2 parents 7d2a8b4 + 7dfdd6d commit 2be328f
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 36 deletions.
3 changes: 1 addition & 2 deletions src/hamster-cli
Original file line number Diff line number Diff line change
Expand Up @@ -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=""):
Expand Down
2 changes: 0 additions & 2 deletions src/hamster/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
)

Expand Down
15 changes: 12 additions & 3 deletions src/hamster/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand All @@ -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.
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/hamster/lib/stuff.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
51 changes: 23 additions & 28 deletions src/hamster/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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):
Expand All @@ -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)
Expand All @@ -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)])
Expand All @@ -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)
Expand Down Expand Up @@ -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,

Expand Down

0 comments on commit 2be328f

Please sign in to comment.