bernhard@378: #!/usr/bin/env python3
bjoern@387: """
bjoern@387: write a daily report accessing a database from
bjoern@387: http://hg.intevation.de/getan 1.1dev3.
bjoern@387: """
bernhard@378: # hastily done, written to learn the getan database format and its manipulation
bernhard@378: # Free Software under GNU GPL v>=3
bernhard@378: # 20130109 bernhard@intevation.de
bernhard@378: # 20140103 bernhard@intevation.de:
bernhard@378: #   started from 2013/getan-writeout-timesorted.py
bernhard@378: #   ported to python3. Removed the dependency for functions from worklog.py.
bernhard@378: #   the timesorted variant can be uncommented in the code for now
bernhard@378: # 20140109 bernhard@intevation.de:
bernhard@378: #   Total time output format improved.
bernhard@378: # 20140120 bernhard@intevation.de:
bernhard@378: #   added command line options, requires argparse module now (e.g. Python>=3.2)
bernhard@378: # 20141104 bernhard@intevation.de:
bernhard@378: #   migration to argparse complete, added -t option
bjoern@387: # TODO:
bernhard@378: #  * use python v>=3.2 variants in the code where noted.
bernhard@378: 
bernhard@378: import argparse
bernhard@378: import datetime
bernhard@378: import logging
bernhard@378: import sqlite3
bernhard@378: 
bjoern@387: factor = {'privat': 0}
bernhard@378: 
bernhard@378: l = logging
bernhard@378: l.basicConfig(level=logging.INFO,
bjoern@387:               # l.basicConfig(level=logging.DEBUG,
bjoern@387:               format='%(message)s')
bernhard@378: #                    format='%(asctime)s %(levelname)s %(message)s')
bernhard@378: 
bjoern@387: 
bernhard@378: def hhhmm_from_timedelta(td):
bernhard@378:     """Return a string '-HHH:MM' from the timedelta parameter.
bernhard@378: 
bernhard@378:     Accounts for way the integer division works for negative numbers:
bernhard@378:         -2 // 60 == -1
bernhard@378:         -2 % 60 == 58
bjoern@387:     by first working on the positive number and then adding the minus
bernhard@378:     to the string.
bernhard@378: 
bernhard@378:     For python >=3.1. Successor of hhmm_from_timedelta() from
bernhard@378:     http://intevation.de/cgi-bin/viewcvs-misc.cgi/worklog.py/ .
bernhard@378:     """
bjoern@387:     total_minutes = abs(round(td.days * 24 * 60 + td.seconds / 60))
bernhard@378:     # variant for Python v>3.2:
bjoern@387:     # total_minutes = abs(round(td/datetime.timedelta(minutes=1)))
bernhard@378: 
bjoern@387:     hours = total_minutes // 60
bjoern@387:     minutes = total_minutes % 60
bernhard@378: 
bernhard@378:     h_string = "{}".format(hours)
bernhard@378: 
bjoern@387:     if(td.days < 0):
bernhard@378:         h_string = "-" + h_string
bernhard@378: 
bernhard@378:     return "{:>3s}:{:02d}".format(h_string, minutes)
bernhard@378: 
bjoern@387: 
bernhard@378: def self_test():
bernhard@378:     """Run some simple tests on hhhmm_from_timedelta().
bernhard@378: 
bernhard@378:     e.g. run like
bernhard@378:         python3 -c 'from getan_report_20140103 import *; self_test()'
bernhard@378:     """
bernhard@378:     l.info(hhhmm_from_timedelta(datetime.timedelta(minutes=1)))
bernhard@378:     l.info(hhhmm_from_timedelta(datetime.timedelta(minutes=-2)))
bernhard@378: 
bjoern@387: 
bernhard@378: def main():
bernhard@378:     parser = argparse.ArgumentParser(description=__doc__)
bernhard@378:     parser.add_argument("-t", action='store_true',
bjoern@387:                         help="timesorted output and default reportday to today")
bernhard@378:     parser.add_argument("dbfilename")
bjoern@387:     parser.add_argument("reportday", nargs='?',
bjoern@387:                         help="day to report  yyyy-mm-dd")
bernhard@378: 
bernhard@378:     args = parser.parse_args()
bernhard@378:     l.debug(args)
bernhard@378: 
bernhard@378:     if args.reportday:
bernhard@404:         try:
bernhard@404:             report_range_start = \
bernhard@404:                 datetime.datetime.strptime(args.reportday, "%Y-%m-%d")
bernhard@404:         except ValueError:
bernhard@404:             report_range_start = \
bernhard@404:                 datetime.datetime.strptime(args.reportday, "%Y%m%d")
bernhard@404: 
bernhard@378:     elif args.t:
bernhard@378:         # start with today 00:00
bernhard@378:         report_range_start = datetime.datetime.combine(
bjoern@387:             datetime.date.today(), datetime.time())
bernhard@378:     else:
bernhard@378:         # start with yesterday 00:00
bernhard@378:         report_range_start = datetime.datetime.combine(
bjoern@387:             datetime.date.today() - datetime.timedelta(days=1),
bjoern@387:             datetime.time())
bernhard@378:     report_range_end = report_range_start + datetime.timedelta(days=1)
bernhard@378: 
bernhard@378:     l.info("Opening sqlite3 database '%s'" % args.dbfilename)
bjoern@387:     conn = sqlite3.connect(
bjoern@387:         args.dbfilename,
bjoern@387:         detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES)
bernhard@378:     c = conn.cursor()
bernhard@378: 
bernhard@378:     tasks = {}
bernhard@378:     task_total = {}
bernhard@378: 
bernhard@378:     c.execute('select * from projects')
bernhard@378:     for t in c:
bernhard@378:         l.debug(t)
bernhard@378:         tasks[t[0]] = t[2]
bernhard@378:         task_total[t[0]] = datetime.timedelta()
bernhard@378: 
bernhard@378: # from getan 1.0 20130103
bjoern@387: # CREATE TABLE entries (
bernhard@378: #    id          INTEGER PRIMARY KEY AUTOINCREMENT,
bernhard@378: #    project_id  INTEGER REFERENCES projects(id),
bernhard@378: #    start_time  TIMESTAMP NOT NULL,
bernhard@378: #    stop_time   TIMESTAMP NOT NULL,
bernhard@378: #    description VARCHAR(256),
bernhard@378: #
bernhard@378: #    CHECK (strftime('%s', start_time) <= strftime('%s', stop_time))
bjoern@387: # );
bernhard@378: 
bernhard@378:     total_time = datetime.timedelta()
bernhard@378: 
bernhard@378:     if args.t:
bernhard@378:         c.execute('select * from entries order by start_time')
bernhard@378:     else:
bernhard@378:         c.execute('select * from entries order by project_id')
bernhard@378:     for e in c:
bernhard@378:         l.debug(e)
bernhard@402:         # let us ignore microseconds
bernhard@402:         start_time = e[2].replace(microsecond = 0)
bernhard@402:         stop_time = e[3].replace(microsecond = 0)
bernhard@402:         length = stop_time - start_time
bernhard@378: 
bernhard@378:         desc = tasks[e[1]]
bernhard@378: 
bernhard@402:         if start_time >= report_range_start and start_time < report_range_end:
bernhard@378:             if args.t:
bernhard@403:                 print("{0:%Y-%m-%d %H:%M}-\n"
bernhard@403:                       "{1:%Y-%m-%d %H:%M} {4}  {2}: {3}\n".
bernhard@402:                       format(start_time, stop_time, desc, e[4],
bjoern@387:                              hhhmm_from_timedelta(length)))
bernhard@378:             else:
bernhard@378:                 print("{0} {2}: {3} {4}".
bernhard@402:                       format(start_time, stop_time, desc, e[4],
bjoern@387:                              hhhmm_from_timedelta(length)))
bernhard@378:             if desc in factor:
bjoern@387:                 # python3.1 does not allow timedelta division.
bjoern@387:                 # TODO: Make python3.1 save or update to python3.2.
bjoern@387:                 # l.info("applying factor %f to entry %s" % (factor[desc], e))
bjoern@387:                 # length = (length * int(factor[desc]*1000))/1000
bjoern@387:                 # Until python3.2 we only honor a factor of zero:
bernhard@378:                 if factor[desc] == 0:
bernhard@378:                     length = datetime.timedelta(0)
bernhard@378:                     l.info("not counting {}".format(e))
bernhard@378:                 else:
bernhard@378:                     l.info("ignoring factor {}".factor[desc])
bernhard@378:             total_time += length
bernhard@378:             task_total[e[1]] += length
bernhard@378: 
bernhard@378:     print("(" + hhhmm_from_timedelta(total_time).strip() + ")")
bernhard@378:     for t in tasks:
bernhard@378:         if task_total[t] != datetime.timedelta(0):
bernhard@378:             print("\t" + tasks[t], hhhmm_from_timedelta(task_total[t]))
bernhard@378: 
bernhard@378:     c.close()
bernhard@378: 
bernhard@378: if __name__ == "__main__":
bernhard@378:     main()