export/reports POC



Hello guys, as promise here is my first stab at the export/reports rewrite.

Please consider this is very alpha code and of poor quality control,
right now it works, but nothing more.

Some things I'm going to improve before summiting a real patch
- transform the "output" variable into a class with some handy methods.
- have a proper time measuring object*
- add data range support
- add totals per category

* I found a bug/omission with the time reporting, the code tends to
round up to minutes and pass strings around. shouldn't there be a
proper date/time/datetime class to calculate those values? I
implemented a method to read from timedelta objects. I think we should
have an object that will take care of all the time/date/datetime
conversions.

attached the patch. in order to apply it to the svn checkout you will do
$ patch -p1 < path/to/initial-export.patch

in order to make this run you will need to:
$ cd <your local svn checkout>
$ cp ~/.gnome2/hamster-applet/hamster.db .
$ python -i hamster/export.py
>>> console()
#pprint output of your db
>>> dump_to_file('something.txt',console)
# the above will create ~/something.txt, it is compatible with all functions
# if you want json output, install simplejson (python-simplejson in
debian & familly)
>>> json()
# and to get plain text and html you will need tempita.
$ cd hamster
$ svn co http://svn.pythonpaste.org/Tempita/trunk/tempita tempita
# so you will have tempita as a submodule of hamster
# then you can do
>>> text()
# and
>>> html()
diff -r 6ab2058db7eb hamster/db.py
--- a/hamster/db.py	Sun Oct 26 10:13:19 2008 -0600
+++ b/hamster/db.py	Tue Oct 28 17:04:30 2008 -0600
@@ -261,6 +261,10 @@ class Storage(hamster.storage.Storage):
         end_date = end_date or date        
 
         return self.fetchall(query, (_("Unsorted"), date, end_date))
+
+    def __get_facts_by_activity(self,activity_id):
+        query = "select * from facts where activity_id = ?"
+        return self.fetchall(query,(activity_id,))
 
     def __remove_fact(self, fact_id):
         query = """
diff -r 6ab2058db7eb hamster/export.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hamster/export.py	Tue Oct 28 17:04:30 2008 -0600
@@ -0,0 +1,135 @@
+#dev version code stolen from hamster.hamster-applet 
+
+from os.path import *
+import sys
+
+# Allow to use uninstalled
+def _check(path):
+    return exists(path) and isdir(path) and isfile(path+"/AUTHORS")
+
+name = join(dirname(__file__), '..')
+if _check(name):
+    print 'Running uninstalled hamster, modifying PYTHONPATH'
+    sys.path.insert(0, abspath(name))
+else:
+    sys.path.insert(0, abspath("@PYTHONDIR@"))
+    print "Running installed hamster, using [ PYTHONDIR@:$PYTHONPATH]"
+
+import hamster
+from hamster.db import Storage
+print "using hamster from %s" % sys.modules['hamster']
+hamster.HAMSTER_DB = join(abspath(name),'hamster.db')
+print "using hamster.db from %s" % hamster.HAMSTER_DB
+s = Storage(None)
+
+def get_total_time_for_activity(activity_id):
+    import datetime
+    total_time = datetime.timedelta()
+    for fact in s.get_facts_by_activity(activity_id):
+        try:
+            delta = fact['end_time'] - fact['start_time']
+        except TypeError:
+            print fact
+        total_time+=delta
+    from hamster.stuff import format_timedelta
+    return format_timedelta(total_time)
+#    from hamster.stuff import format_duration
+#    duration = 24.0 * total_time.days + total_time.seconds / 60.0 + total_time.microseconds/(60.0*60.0)
+#    duration = 24 * total_time.days + total_time.seconds / 60 + total_time.microseconds/(60*60)
+#    return duration
+
+def export_tasks(include='all',exclude=None):
+    output = {}
+    for category in s.get_category_list():
+        current_category = output[category['name']] = {}
+        for activity in s.get_activities(category['id']):
+            current_category[activity['name']] = get_total_time_for_activity(activity['id'])
+    return output
+
+def console():
+    import pprint
+    pprint.pprint(export_tasks())
+
+def json():
+    import simplejson
+    return simplejson.dumps(export_tasks())
+
+def xml():
+    import xml.etree.ElementTree as ET
+
+def text():
+    import tempita
+    template = tempita.Template(
+    """
+    {{py:
+def dict_to_time(timedict):
+    return "%d:%d:%d:%d" % (timedict['days'],timedict['hours'],timedict['minutes'],timedict['seconds'])
+    }}
+    {{for category_name,categories in items.items() }}
+        {{category_name}}
+        {{for activity_name,activities in categories.items()}}
+            {{activity_name}},{{activities | dict_to_time}}
+        {{endfor}}
+    {{endfor}}
+    """)
+    return template.substitute(items = export_tasks())
+
+def html():
+    import tempita
+    template = tempita.HTMLTemplate(
+    """{{py:
+def dict_to_time(timedict):
+    return "%d:%d:%d:%d" % (timedict['days'],timedict['hours'],timedict['minutes'],timedict['seconds'])
+    }}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en">
+    <head>
+        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+        <meta name="author" content="hamster-applet" />
+    <title>%s</title>
+    <style>
+        body {
+        padding: 12px;
+        }
+        h1 {
+        border-bottom: 1px solid gray;
+        padding-bottom: 4px;
+        }
+        table {margin-left: 24px}
+        th {
+            text-align: left;
+        }
+        tr {padding: 6px;}
+        td {
+            padding: 2px;
+            padding-right: 24px;
+        }
+        </style>
+    </head>
+<body>
+    {{for category_name,categories in items.items() }}
+        <table>
+        <tr>
+            <th>activity</th>
+            <th>duration</th>
+        </tr>
+        {{category_name}}
+        {{for activity_name,activities in categories.items()}}
+            <tr><td>{{activity_name}}</td><td>{{activities | dict_to_time}}</td></tr>
+        {{endfor}}
+        </table>
+    {{endfor}}
+    </html>
+    """)
+    return template.substitute(items = export_tasks())
+
+
+def dump_to_file(filename,function):
+    report_path = join(expanduser("~"), filename)
+    report = open(report_path, "w")
+    report.write(function())
+    report.close()
+
+dump_to_file('test.html',html)
+
diff -r 6ab2058db7eb hamster/storage.py
--- a/hamster/storage.py	Sun Oct 26 10:13:19 2008 -0600
+++ b/hamster/storage.py	Tue Oct 28 17:04:30 2008 -0600
@@ -48,6 +48,9 @@ class Storage(object):
 
     def get_facts(self, date, end_date = None):
         return self.__get_facts(date, end_date)
+
+    def get_facts_by_activity(self, activity_id = None):
+        return self.__get_facts_by_activity(activity_id)
 
     def remove_fact(self, fact_id):
         fact = self.get_fact(fact_id)
@@ -104,7 +107,6 @@ class Storage(object):
         self.dispatch('activity_updated', ())
         return res
 
-
     def get_category_list(self):
         return self.__get_category_list()
         
diff -r 6ab2058db7eb hamster/stuff.py
--- a/hamster/stuff.py	Sun Oct 26 10:13:19 2008 -0600
+++ b/hamster/stuff.py	Tue Oct 28 17:04:30 2008 -0600
@@ -65,6 +65,28 @@ def format_duration(minutes):
     formatted_duration += "%02d:%02d" % (hours, minutes)
             
     return formatted_duration
+
+def format_timedelta(delta):
+    days = delta.days
+    hours = delta.seconds / 3600
+    remaining = delta.seconds % 3600
+    minutes = remaining / 60
+    seconds = remaining % 60
+    #XXX fix microseconds 
+    microseconds = delta.microseconds 
+    del delta
+    del remaining
+    return locals()
+
+def test_format_timedelta():
+    import datetime
+    bigdelta = datetime.datetime.now() - datetime.datetime(1,1,1)
+    timedict = format_timedelta(bigdelta)
+    newdelta = datetime.timedelta(days=timedict['days'],hours=timedict['hours'],minutes=timedict['minutes'],seconds=timedict['seconds'],microseconds=timedict['microseconds'])
+    print newdelta == bigdelta
+    assert false
+
+
 
 def dateDict(date, prefix):
     """converts date into dictionary, having prefix for all the keys"""


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]