3
# Copyright (C) 2008 Toms Bauģis <toms.baugis at gmail.com>
4
# Copyright (C) 2008 Nathan Samson <nathansamson at gmail dot com>
5
# Copyright (C) 2008 Giorgos Logiotatidis <seadog at sealabs dot net>
7
# This file is part of Project Hamster.
9
# Project Hamster is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU General Public License as published by
11
# the Free Software Foundation, either version 3 of the License, or
12
# (at your option) any later version.
14
# Project Hamster is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# GNU General Public License for more details.
19
# You should have received a copy of the GNU General Public License
20
# along with Project Hamster. If not, see <http://www.gnu.org/licenses/>.
24
from xml.dom.minidom import Document
26
from hamster.i18n import C_
29
def simple(facts, start_date, end_date, format, path):
30
facts = copy.deepcopy(facts) # dont want to do anything bad to the input
31
report_path = stuff.locale_from_utf8(path)
34
writer = TSVWriter(report_path)
36
writer = XMLWriter(report_path)
37
elif format == "ical":
38
writer = ICalWriter(report_path)
39
else: #default to HTML
40
writer = HTMLWriter(report_path, start_date, end_date)
42
writer.write_report(facts)
45
class ReportWriter(object):
46
#a tiny bit better than repeating the code all the time
47
def __init__(self, path, datetime_format = "%Y-%m-%d %H:%M:%S"):
48
self.file = open(path, "w")
49
self.datetime_format = datetime_format
51
def write_report(self, facts):
54
fact["name"]= fact["name"].encode('utf-8')
55
fact["description"] = (fact["description"] or u"").encode('utf-8')
56
fact["category"] = (fact["category"] or _("Unsorted")).encode('utf-8')
58
if self.datetime_format:
59
fact["start_time"] = fact["start_time"].strftime(self.datetime_format)
62
fact["end_time"] = fact["end_time"].strftime(self.datetime_format)
65
fact["delta"] = fact["delta"].seconds / 60 + fact["delta"].days * 24 * 60
66
fact["tags"] = ", ".join(fact["tags"])
68
self._write_fact(self.file, fact)
70
self._finish(self.file, facts)
74
def _start(self, file, facts):
75
raise NotImplementedError
77
def _write_fact(self, file, fact):
78
raise NotImplementedError
80
def _finish(self, file, facts):
81
raise NotImplementedError
83
class ICalWriter(ReportWriter):
84
"""a lame ical writer, could not be bothered with finding a library"""
85
def __init__(self, path):
86
ReportWriter.__init__(self, path, datetime_format = "%Y%m%dT%H%M%S")
87
self.file.write("BEGIN:VCALENDAR\nVERSION:1.0\n")
90
def _write_fact(self, file, fact):
91
#for now we will skip ongoing facts
92
if not fact["end_time"]: return
94
if fact["category"] == _("Unsorted"):
95
fact["category"] = None
97
self.file.write("""BEGIN:VEVENT
98
CATEGORIES:%(category)s
99
DTSTART:%(start_time)s
102
DESCRIPTION:%(description)s
106
def _finish(self, file, facts):
107
self.file.write("END:VCALENDAR\n")
109
class TSVWriter(ReportWriter):
110
def __init__(self, path):
111
ReportWriter.__init__(self, path)
112
self.csv_writer = csv.writer(self.file, dialect='excel-tab')
114
headers = [# column title in the TSV export format
116
# column title in the TSV export format
118
# column title in the TSV export format
120
# column title in the TSV export format
121
_("duration minutes"),
122
# column title in the TSV export format
124
# column title in the TSV export format
126
# column title in the TSV export format
128
self.csv_writer.writerow([h.encode('utf-8') for h in headers])
130
def _write_fact(self, file, fact):
131
self.csv_writer.writerow([fact[key] for key in ["name", "start_time",
132
"end_time", "delta", "category", "description",
134
def _finish(self, file, facts):
137
class XMLWriter(ReportWriter):
138
def __init__(self, path):
139
ReportWriter.__init__(self, path)
140
self.doc = Document()
141
self.activity_list = self.doc.createElement("activities")
143
def _write_fact(self, file, fact):
144
activity = self.doc.createElement("activity")
145
activity.setAttribute("name", fact["name"])
146
activity.setAttribute("start_time", fact["start_time"])
147
activity.setAttribute("end_time", fact["end_time"])
148
activity.setAttribute("duration_minutes", str(fact["delta"]))
149
activity.setAttribute("category", fact["category"])
150
activity.setAttribute("description", fact["description"])
151
activity.setAttribute("tags", fact["tags"])
152
self.activity_list.appendChild(activity)
154
def _finish(self, file, facts):
155
self.doc.appendChild(self.activity_list)
156
file.write(self.doc.toxml())
160
class HTMLWriter(ReportWriter):
161
def __init__(self, path, start_date, end_date):
162
ReportWriter.__init__(self, path, datetime_format = None)
164
dates_dict = stuff.dateDict(start_date, "start_")
165
dates_dict.update(stuff.dateDict(end_date, "end_"))
166
if start_date.year != end_date.year:
167
self.title = _(u"Overview for %(start_B)s %(start_d)s, %(start_Y)s – %(end_B)s %(end_d)s, %(end_Y)s") % dates_dict
168
elif start_date.month != end_date.month:
169
self.title = _(u"Overview for %(start_B)s %(start_d)s – %(end_B)s %(end_d)s, %(end_Y)s") % dates_dict
171
self.title = _(u"Overview for %(start_B)s %(start_d)s – %(end_d)s, %(end_Y)s") % dates_dict
173
if start_date == end_date:
174
self.title = _(u"Overview for %(start_B)s %(start_d)s, %(start_Y)s") % dates_dict
179
"""TODO bring template to external file or write to PDF"""
181
self.file.write("""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
182
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
183
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
185
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
186
<meta name="author" content="hamster-applet" />
187
<title>%(title)s</title>
188
<style type="text/css">
190
font-family: "sans-serif";
197
border-bottom: 2px solid #303030;
202
border-bottom: 2px solid #303030;
211
background-color: #eee;
215
background-color: #ffffff;
221
<h1>%(title)s</h1>""" % {"title": self.title} + """
224
<th>""" + _("Date") + """</th>
225
<th>""" + _("Activity") + """</th>
226
<th>""" + _("Category") + """</th>
227
<th>""" + _("Tags") + """</th>
228
<th>""" + _("Start") + """</th>
229
<th>""" + _("End") + """</th>
230
<th>""" + _("Duration") + """</th>
231
<th>""" + _("Description") + """</th>
234
def _write_fact(self, report, fact):
235
end_time = fact["end_time"]
237
# ongoing task in current day
240
end_time_str = end_time.strftime('%H:%M')
243
if fact["category"] != _("Unsorted"): #do not print "unsorted" in list
244
category = fact["category"]
246
description = fact["description"] or ""
248
# fact date column in HTML report
249
report.write("""<tr class="row%d">
259
""" % (int(self.even_row),
260
fact["start_time"].strftime(
261
# date column format for each row in HTML report
262
# Using python datetime formatting syntax. See:
263
# http://docs.python.org/library/time.html#time.strftime
264
C_("html report","%b %d, %Y")),
268
fact["start_time"].strftime('%H:%M'),
270
stuff.format_duration(fact["delta"]) or "",
273
self.even_row = not self.even_row
276
# save data for summary table
278
id_string = "<td class=\"smallCell\">%s</td><td class=\"largeCell\">%s</td>" % (fact["category"], fact["name"])
279
self.sum_time[id_string] = self.sum_time.get(id_string, 0) + fact["delta"]
281
def _finish(self, report, facts):
282
report.write("</table>")
285
report.write("\n<h2>%s</h2>\n" % _("Totals"))
286
report.write("""<table>
288
<th>""" + _("Category") + """</th>
289
<th>""" + _("Activity") + """</th>
290
<th>""" + _("Duration") + """</th>
294
for key in sorted(self.sum_time.keys()):
295
report.write(" <tr class=\"row%d\">%s<td class=\"smallCell\">%s</td></tr>\n" % (int(even_row), key, stuff.format_duration(self.sum_time[key])))
296
tot_time += self.sum_time[key]
298
even_row = not even_row
300
report.write(" <tr><th colspan=\"2\" style=\"text-align:right;\">" + _("Total Time") + ":</th><th>%s</th></tr>\n" % (stuff.format_duration(tot_time)))
301
report.write("</table>\n")
303
report.write("</body>\n</html>")