sawine@6: #!/usr/local/bin/python sawine@0: sawine@0: """ sawine@7: tim - A time recording tool, because time is money. sawine@0: Author: Eugen Sawin (sawine@me73.com) sawine@7: Dependencies: Python 2.7 and libsqlite3-dev sawine@0: """ sawine@0: sawine@1: import os sawine@2: import sys sawine@0: import argparse sawine@2: from datetime import datetime sawine@3: sawine@3: import db sawine@0: sawine@1: WD = "working_path" sawine@1: CONFIG = {WD: str} sawine@1: CONFIG_SEP = "=" sawine@12: DB_FILE = "tim.db" sawine@1: sawine@1: config = {} sawine@1: sawine@7: # Try to get the user's home directory path sawine@1: try: # Windows sawine@1: from win32com.shell import shellcon, shell sawine@1: HOMEDIR = shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA, 0, 0) sawine@2: except ImportError: # Linux (hopefully) sawine@1: HOMEDIR = os.path.expanduser("~") sawine@1: sawine@12: CONFIG_FILE = "%s/.timrc" % HOMEDIR sawine@1: sawine@10: DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" sawine@10: sawine@1: def read_config(): sawine@1: config = {} sawine@1: with open(CONFIG_FILE, "r") as config_stream: sawine@1: config_lines = [l.split(CONFIG_SEP) for l in config_stream.readlines() sawine@1: if CONFIG_SEP in l] sawine@1: for key, value in config_lines: sawine@1: key = key.strip().lower() sawine@1: value = value.strip() sawine@1: if key in CONFIG: sawine@1: config[key] = CONFIG[key](value) sawine@1: return config sawine@1: sawine@1: def write_config(config): sawine@1: with open(CONFIG_FILE, "w") as config_input: sawine@1: config_input.write("\n".join([CONFIG_SEP.join((k, v)) sawine@1: for k, v in config.iteritems() if k in CONFIG])) sawine@1: sawine@3: def db_file(): sawine@3: global config sawine@3: if WD not in config: sawine@3: print "Working directory path is not configured. \ sawine@3: Please use the init command." sawine@3: return None sawine@3: return config[WD] + "/" + DB_FILE sawine@3: sawine@1: def init(args): sawine@2: global config sawine@1: last_wd = None sawine@1: if WD in config: sawine@1: last_wd = config[WD] sawine@2: config[WD] = args.working_path sawine@1: write_config(config) sawine@2: path_exists = os.path.exists(config[WD]) and os.path.isdir(config[WD]) sawine@3: db_exists = path_exists and os.path.exists(db_file())\ sawine@3: and os.path.isfile(db_file()) sawine@2: if last_wd != config[WD]: sawine@2: print "Changed working directory from %s to %s." % (last_wd, config[WD]) sawine@1: else: sawine@2: print "Working directory remains at %s." % config[WD] sawine@2: if not path_exists: sawine@2: print "Warning: working directory %s does not exist." % config[WD] sawine@2: os.makedirs(config[WD]) sawine@2: print "Working directory %s created." % config[WD] sawine@2: elif db_exists: sawine@3: print "Database file %s already exists, please delete it before \ sawine@3: initiating a new database at this location." % db_file() sawine@2: if not db_exists: sawine@3: db.init(db_file()) sawine@1: sawine@10: def parse_label(label): sawine@10: label = label.strip() sawine@2: if ":" in label: sawine@2: project, activity = label.split(":") sawine@2: else: sawine@10: project, activity = (label, None) sawine@10: return project, activity sawine@10: sawine@10: def begin(args): sawine@10: project, activity = parse_label(args.label) sawine@8: time = datetime.now() sawine@8: db.resume(db_file(), None, None, time) sawine@8: db.begin(db_file(), project, activity, time) sawine@0: sawine@0: def end(args): sawine@10: project, activity = parse_label(args.label) sawine@10: log = args.m sawine@8: time = datetime.now() sawine@9: db.resume(db_file(), db.find_active_task(db_file()), time, log) sawine@9: db.end(db_file(), project, activity, time, log) sawine@8: sawine@8: def pause(args): sawine@11: db.pause(db_file(), datetime.now()) sawine@8: sawine@8: def resume(args): sawine@11: db.resume(db_file(), None, datetime.now(), args.m) sawine@2: sawine@10: def add_task(args): sawine@10: project, activity = parse_label(args.label) sawine@10: log = args.m sawine@10: begin = datetime.strptime(args.begin, DATETIME_FORMAT) sawine@10: end = datetime.strptime(args.end, DATETIME_FORMAT) sawine@10: db.begin(db_file(), project, activity, begin) sawine@10: db.end(db_file(), project, activity, end, log) sawine@10: sawine@11: def add_break(args): sawine@11: log = args.m sawine@11: begin = datetime.strptime(args.begin, DATETIME_FORMAT) sawine@11: end = datetime.strptime(args.end, DATETIME_FORMAT) sawine@11: db.pause(db_file(), begin) sawine@11: db.resume(db_file(), db.find_active_task(db_file(), begin), end, log) sawine@11: sawine@12: def set_customer(args): sawine@12: db.update_where(db_file(), "projects", "name", args.project, "customer", sawine@12: args.customer) sawine@12: sawine@7: def status(args): sawine@8: task = db.status(db_file()) sawine@8: if task: sawine@8: print "Currently active task" sawine@8: print "Project: %s Activity: %s" % task sawine@7: else: sawine@8: print "There is no active task." sawine@7: sawine@2: def main(): sawine@2: global config sawine@1: config = read_config() sawine@1: parser = argparse.ArgumentParser(description="Records time.") sawine@1: subs = parser.add_subparsers() sawine@10: sawine@10: sub_init = subs.add_parser("init") sawine@12: sub_init.add_argument("working_path") sawine@10: sub_init.set_defaults(func=init) sawine@0: sawine@1: sub_begin = subs.add_parser("begin") sawine@12: sub_begin.add_argument("label") sawine@1: sub_begin.set_defaults(func=begin) sawine@0: sawine@1: sub_end = subs.add_parser("end") sawine@12: sub_end.add_argument("label", nargs="?", default="all:all") sawine@12: sub_end.add_argument("-m") sawine@1: sub_end.set_defaults(func=end) sawine@0: sawine@10: sub_pause = subs.add_parser("pause") sawine@10: sub_pause.set_defaults(func=pause) sawine@8: sawine@10: sub_resume = subs.add_parser("resume") sawine@12: sub_resume.add_argument("-m") sawine@10: sub_resume.set_defaults(func=resume) sawine@8: sawine@10: sub_add = subs.add_parser("add") sawine@10: sub_add_subs = sub_add.add_subparsers() sawine@11: sawine@10: sub_add_task = sub_add_subs.add_parser("task") sawine@12: sub_add_task.add_argument("label") sawine@12: sub_add_task.add_argument("begin") sawine@12: sub_add_task.add_argument("end") sawine@12: sub_add_task.add_argument("-m") sawine@10: sub_add_task.set_defaults(func=add_task) sawine@11: sawine@11: sub_add_break = sub_add_subs.add_parser("break") sawine@12: sub_add_break.add_argument("begin") sawine@12: sub_add_break.add_argument("end") sawine@12: sub_add_break.add_argument("-m") sawine@11: sub_add_break.set_defaults(func=add_break) sawine@10: sawine@12: sub_set = subs.add_parser("set") sawine@12: sub_set_subs = sub_set.add_subparsers() sawine@12: sawine@12: sub_set_customer = sub_set_subs.add_parser("customer") sawine@12: sub_set_customer.add_argument("project") sawine@12: sub_set_customer.add_argument("customer") sawine@12: sub_set_customer.set_defaults(func=set_customer) sawine@12: sawine@10: sub_status = subs.add_parser("status") sawine@10: sub_status.set_defaults(func=status) sawine@7: sawine@1: args = parser.parse_args() sawine@1: args.func(args) sawine@2: sawine@2: if __name__ == "__main__": sawine@2: main()