# HG changeset patch # User Björn Ricks # Date 1323681592 -3600 # Node ID 804211b988aa2516b79e7b8045c84c0d44268a28 # Parent efff8b9e704631c91684e21c2f3ec0379e8c7d5d Move zeiterfassung and zeitsort to an own module in getan Both scripts can be installed via setup.py diff -r efff8b9e7046 -r 804211b988aa contrib/zeiterfassung --- a/contrib/zeiterfassung Mon Dec 12 09:58:17 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,272 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# zeiterfassung -# ------------- -# (c) 2008 by Sascha L. Teichmann -# -# 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, datetime - -DEFAULT_DATABASE = "time.db" - -TYPE_OF_ENTRY = "?" - -WORKPACKAGE = re.compile("^\[(\w*)(\s|\])") - -USAGE = '''usage: %s - with - [--user=|-u ] : Name of user, default: $USER - [--database=|-d ]: getan database, default: time.db - [--project=|-p ] : Key of output project, default: all - [--encoding=|-e encoding] : encoding of output, default: none - [--week=]|-w ] : week of year - [--year=]|-y ] : year - [--list|-l] : list all projects - [--help|-h] : This text - [--emtpy|-m] : show empty projects - [--lastweek|-c] : entries of last working week''' - -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:mc', - ['database=', 'user=', 'project=', 'encoding=', 'help', 'list', 'week=', - 'year=', 'empty', 'lastweek']) - - 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 ("--lastweek", "-c") and not week: - currentweek = datetime.now().isocalendar()[1] - lastweek = currentweek - 1 - if lastweek <= 0: - lastweek = 52 - week = str(lastweek) - 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: diff -r efff8b9e7046 -r 804211b988aa contrib/zeitsort --- a/contrib/zeitsort Mon Dec 12 09:58:17 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# zeitsort -# -------- -# (c) 2008 by Sascha L. Teichmann -# -# Simple script which sorts lines of zeiterfassung.txt files by date. -# -# This is Free Software licensed under the terms of GPLv3 or later. -# For details see LICENSE coming with the source of 'getan'. -# - -import sys -import re - -from datetime import date - -DATE = re.compile("(\d\d)\.(\d\d)\.(\d\d\d\d)") - -def date_cmp(a, b): - ma = DATE.search(a) - mb = DATE.search(b) - if not ma and not mb: return cmp(a, b) - if ma and not mb: return -1 - if not ma and mb: return +1 - da = date(int(ma.group(3)), int(ma.group(2)), int(ma.group(1))) - db = date(int(mb.group(3)), int(mb.group(2)), int(mb.group(1))) - return cmp(da, db) - -def main(): - all = [] - while True: - line = sys.stdin.readline() - if not line: break - if not DATE.search(line): - # handle multi lines - if not all: all.append(line) - else: all[-1] += line - else: - all.append(line) - all.sort(date_cmp) - sys.stdout.write(''.join(all)) - sys.stdout.flush() - -if __name__ == '__main__': - main() - -# vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: diff -r efff8b9e7046 -r 804211b988aa getan/contrib/zeiterfassung.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/getan/contrib/zeiterfassung.py Mon Dec 12 10:19:52 2011 +0100 @@ -0,0 +1,272 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# zeiterfassung +# ------------- +# (c) 2008 by Sascha L. Teichmann +# +# 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, datetime + +DEFAULT_DATABASE = "time.db" + +TYPE_OF_ENTRY = "?" + +WORKPACKAGE = re.compile("^\[(\w*)(\s|\])") + +USAGE = '''usage: %s + with + [--user=|-u ] : Name of user, default: $USER + [--database=|-d ]: getan database, default: time.db + [--project=|-p ] : Key of output project, default: all + [--encoding=|-e encoding] : encoding of output, default: none + [--week=]|-w ] : week of year + [--year=]|-y ] : year + [--list|-l] : list all projects + [--help|-h] : This text + [--emtpy|-m] : show empty projects + [--lastweek|-c] : entries of last working week''' + +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:mc', + ['database=', 'user=', 'project=', 'encoding=', 'help', 'list', 'week=', + 'year=', 'empty', 'lastweek']) + + 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 ("--lastweek", "-c") and not week: + currentweek = datetime.now().isocalendar()[1] + lastweek = currentweek - 1 + if lastweek <= 0: + lastweek = 52 + week = str(lastweek) + 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: diff -r efff8b9e7046 -r 804211b988aa getan/contrib/zeitsort.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/getan/contrib/zeitsort.py Mon Dec 12 10:19:52 2011 +0100 @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# zeitsort +# -------- +# (c) 2008 by Sascha L. Teichmann +# +# Simple script which sorts lines of zeiterfassung.txt files by date. +# +# This is Free Software licensed under the terms of GPLv3 or later. +# For details see LICENSE coming with the source of 'getan'. +# + +import sys +import re + +from datetime import date + +DATE = re.compile("(\d\d)\.(\d\d)\.(\d\d\d\d)") + +def date_cmp(a, b): + ma = DATE.search(a) + mb = DATE.search(b) + if not ma and not mb: return cmp(a, b) + if ma and not mb: return -1 + if not ma and mb: return +1 + da = date(int(ma.group(3)), int(ma.group(2)), int(ma.group(1))) + db = date(int(mb.group(3)), int(mb.group(2)), int(mb.group(1))) + return cmp(da, db) + +def main(): + all = [] + while True: + line = sys.stdin.readline() + if not line: break + if not DATE.search(line): + # handle multi lines + if not all: all.append(line) + else: all[-1] += line + else: + all.append(line) + all.sort(date_cmp) + sys.stdout.write(''.join(all)) + sys.stdout.flush() + +if __name__ == '__main__': + main() + +# vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: diff -r efff8b9e7046 -r 804211b988aa setup.py --- a/setup.py Mon Dec 12 09:58:17 2011 +0100 +++ b/setup.py Mon Dec 12 10:19:52 2011 +0100 @@ -29,7 +29,10 @@ package_data = {"" : ["*.txt"], }, entry_points = { "console_scripts": ["getan=getan.main:main", - "getan-classic=getan.classic.getan:main"] }, + "getan-classic=getan.classic.getan:main", + "zeiterfassung=getan.contrib.zeiterfassung:main", + "zeitsort=getan.contrib.zeitsort:main", + ] }, classifiers=[ "Topic :: Utilities", "Environment :: Console",