Mercurial > getan
view contrib/zeiterfassung @ 104:36204a42bd79
Allow to select projects with keys > 1 letters while moving entries
author | Björn Ricks <bjoern.ricks@intevation.de> |
---|---|
date | Mon, 12 Sep 2011 14:32:20 +0200 |
parents | 4b58763e75c1 |
children | e7548b8c6dcc |
line wrap: on
line source
#!/usr/bin/env python # -*- coding: utf-8 -*- # # zeiterfassung # ------------- # (c) 2008 by Sascha L. Teichmann <sascha.teichmann@intevation.de> # # Simple script which generates lines for zeiterfassung.txt files. # # This is Free Software licensed under the terms of GPLv3 or later. # For details see LICENSE coming with the source of 'getan'. # import os import os.path import sys import getopt import re import codecs from pysqlite2 import dbapi2 as db from datetime import date DEFAULT_DATABASE = "time.db" TYPE_OF_ENTRY = "?" WORKPACKAGE = re.compile("^\[(\w*)(\s|\])") USAGE = '''usage: %s <options> with <options> [--user=|-u <user>] : Name of user, default: $USER [--database=|-d <database>]: getan database, default: time.db [--project=|-p <key>] : Key of output project, default: all [--encoding=|-e encoding] : encoding of output, default: none [--week=]|-w <week>] : week of year [--year=]|-y <year>] : year [--list|-l] : list all projects [--help|-h] : This text [--emtpy|-m] : show empty projects''' LIST_PROJECTS = ''' SELECT key, description, active FROM projects ''' PROJECT_ID_BY_KEY = ''' SELECT id, description FROM projects where key = :key ''' ALL_PROJECT_IDS = ''' SELECT id, key, description FROM projects ''' WEEK_ENTRIES = ''' SELECT date(start_time) AS t, sum(strftime('%s', stop_time) - strftime('%s', start_time)), 'no description' AS description FROM entries WHERE project_id = :project_id AND (strftime('%Y', start_time) ) = :year AND (description IS NULL or length(description) = 0) -- trim() function is missing AND (strftime('%W', start_time) = :week OR strftime('%W', stop_time) = :week) GROUP BY round(julianday(start_time)) UNION SELECT date(start_time) AS s, strftime('%s', stop_time) - strftime('%s', start_time), description FROM entries WHERE project_id = :project_id AND (strftime('%Y', start_time) ) = :year AND description IS NOT NULL AND length(description) > 0 AND (strftime('%W', start_time) = :week OR strftime('%W', stop_time) = :week) ORDER BY t ''' ENTRIES = ''' SELECT date(start_time), sum(strftime('%s', stop_time) - strftime('%s', start_time)), 'no description' AS description FROM entries WHERE project_id = :project_id AND (description IS NULL or length(description) = 0) -- trim() function is missing GROUP BY round(julianday(start_time)) UNION SELECT date(start_time), strftime('%s', stop_time) - strftime('%s', start_time), description FROM entries WHERE project_id = :project_id AND description IS NOT NULL AND length(description) > 0 ''' ENTRIES_YEAR = ''' SELECT date(start_time), sum(strftime('%s', stop_time) - strftime('%s', start_time)), 'no description' AS description FROM entries WHERE project_id = :project_id AND (description IS NULL or length(description) = 0) -- trim() function is missing GROUP BY round(julianday(start_time)) UNION SELECT date(start_time), strftime('%s', stop_time) - strftime('%s', start_time), description FROM entries WHERE project_id = :project_id AND (strftime('%Y', start_time) ) = :year AND description IS NOT NULL AND length(description) > 0 ''' def human_time(s): h = s / 3600 m = (s % 3600) / 60 if (s % 60) >= 30: m += 1 if m == 60: m = 0 h += 1 return "%2d:%02d" % (h, m) class TermError(Exception): def __init__(self, msg): Exception.__init__(self) self.msg = msg def __str__(self): return repr(self.msg) def tolerantClose(c): if c: try: c.close() except: pass def usage(exit_code = 0): print USAGE % sys.argv[0] sys.exit(exit_code) def main(): database = DEFAULT_DATABASE user = None list_projects = False project = None encoding = None week = None year = None empty_proj = False opts, args = getopt.getopt( sys.argv[1:], 'd:u:p:e:hl:w:y:m', ['database=', 'user=', 'project=', 'encoding=', 'help', 'list', 'week=', 'year=', 'empty']) for opt, val in opts: if opt in ("--database", "-d"): database = val elif opt in ("--user", "-u"): user = val elif opt in ("--project", "-p"): project = val elif opt in ("--encoding", "-e"): encoding = val elif opt in ("--help", "-h"): usage() elif opt in ("--list", "-l"): list_projects = True elif opt in ("--week", "-w"): week = val elif opt in ("--year", "-y"): year = val elif opt in ("--empty", "-m"): empty_proj = True if not user: user = os.getenv("USER") proj_year = year if not proj_year: proj_year = date.today().strftime("%Y") if encoding: Writer = codecs.getwriter(encoding) sys.stdout = Writer(sys.stdout) if not os.path.isfile(database): print >> sys.stderr, "'%s' does not exist or is not a file." % database sys.exit(1) con, cur = None, None try: try: con = db.connect(database) if encoding: con.text_factory = lambda s: s.decode(encoding) cur = con.cursor() if list_projects: cur.execute(LIST_PROJECTS) while True: row = cur.fetchone() if not row: break print "%s %s %s" % (row[0], row[2] and "*" or "-", row[1]) else: if project: cur.execute(PROJECT_ID_BY_KEY, { 'key': project }) row = cur.fetchone() if row is None: raise TermError("There is no project with key '%s'" % project) project_ids = [[row[0], project, row[1]]] else: cur.execute(ALL_PROJECT_IDS); project_ids = cur.fetchall() total = 0 for project_id, project, proj_desc in project_ids: if not week is None: cur.execute(WEEK_ENTRIES, {'project_id': project_id, 'week': week, 'year' : proj_year}) elif not year: cur.execute(ENTRIES, {'project_id': project_id}) else: cur.execute(ENTRIES_YEAR, {'project_id': project_id, 'year':proj_year}) total_proj = 0 row = cur.fetchone() if row or empty_proj: print "# project: %s (%s)" % (project, proj_desc) while row: d = date(*map(int, row[0].split('-'))) t = max(60, row[1]) c = row[2] total_proj += t workpackage = "----" if c: m = WORKPACKAGE.match(c) if m: workpackage = m.group(1) c = c[m.end():].strip() c = c.replace('\x1b', '') print "%s %sh %s %-3s [%s] %s" % ( d.strftime("%d.%m.%Y"), human_time(t), TYPE_OF_ENTRY, user, workpackage, c) row = cur.fetchone() total += total_proj if empty_proj or total_proj > 0: print "# total: %sh\n\n" % human_time(total_proj) print "# total all projects: %sh\n\n" % human_time(total) finally: tolerantClose(cur) tolerantClose(con) except TermError, e: print >> sys.stderr, "error: %s" % e.msg if __name__ == '__main__': main() # vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: