Renamed and added default config file.
1.1 --- a/db.py Wed Oct 06 16:07:36 2010 +0200
1.2 +++ b/db.py Mon Jul 18 01:03:37 2011 +0200
1.3 @@ -1,5 +1,5 @@
1.4 """
1.5 -Database lib for cronrec.
1.6 +Description: Database lib for cronrec.
1.7 Author: Eugen Sawin (sawine@me73.com)
1.8 Dependencies: libsqlite3-dev
1.9 """
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/tim.py Mon Jul 18 01:03:37 2011 +0200
2.3 @@ -0,0 +1,226 @@
2.4 +#!/usr/local/bin/python
2.5 +# -*- coding: utf-8 -*-
2.6 +
2.7 +"""
2.8 +Name: tim - A time recording tool, because time is money.
2.9 +Author: Eugen Sawin <sawine@me73.com>
2.10 +Dependencies: Python 2.7 and libsqlite3-dev
2.11 +"""
2.12 +
2.13 +import os
2.14 +import sys
2.15 +import argparse
2.16 +from datetime import datetime
2.17 +
2.18 +import db
2.19 +
2.20 +def main():
2.21 + global config
2.22 + config = read_config()
2.23 + parser = argparse.ArgumentParser(description="Records time.")
2.24 + subs = parser.add_subparsers()
2.25 +
2.26 + sub_init = subs.add_parser("init")
2.27 + sub_init.add_argument("working_path")
2.28 + sub_init.set_defaults(func=init)
2.29 +
2.30 + sub_begin = subs.add_parser("begin")
2.31 + sub_begin.add_argument("label")
2.32 + sub_begin.set_defaults(func=begin)
2.33 +
2.34 + sub_end = subs.add_parser("end")
2.35 + sub_end.add_argument("label", nargs="?", default="all:all")
2.36 + sub_end.add_argument("-m")
2.37 + sub_end.set_defaults(func=end)
2.38 +
2.39 + sub_pause = subs.add_parser("pause")
2.40 + sub_pause.set_defaults(func=pause)
2.41 +
2.42 + sub_resume = subs.add_parser("resume")
2.43 + sub_resume.add_argument("-m")
2.44 + sub_resume.set_defaults(func=resume)
2.45 +
2.46 + sub_add = subs.add_parser("add")
2.47 + sub_add_subs = sub_add.add_subparsers()
2.48 +
2.49 + sub_add_task = sub_add_subs.add_parser("task")
2.50 + sub_add_task.add_argument("label")
2.51 + sub_add_task.add_argument("begin")
2.52 + sub_add_task.add_argument("end")
2.53 + sub_add_task.add_argument("-m")
2.54 + sub_add_task.set_defaults(func=add_task)
2.55 +
2.56 + sub_add_break = sub_add_subs.add_parser("break")
2.57 + sub_add_break.add_argument("begin")
2.58 + sub_add_break.add_argument("end")
2.59 + sub_add_break.add_argument("-m")
2.60 + sub_add_break.set_defaults(func=add_break)
2.61 +
2.62 + sub_set = subs.add_parser("set")
2.63 + sub_set_subs = sub_set.add_subparsers()
2.64 +
2.65 + sub_set_customer = sub_set_subs.add_parser("customer")
2.66 + sub_set_customer.add_argument("project")
2.67 + sub_set_customer.add_argument("customer")
2.68 + sub_set_customer.set_defaults(func=set_customer)
2.69 +
2.70 + sub_set_rate = sub_set_subs.add_parser("rate")
2.71 + sub_set_rate.add_argument("label")
2.72 + sub_set_rate.add_argument("rate", type=float)
2.73 + sub_set_rate.add_argument("currency")
2.74 + sub_set_rate.add_argument("type")
2.75 + sub_set_rate.set_defaults(func=set_rate)
2.76 +
2.77 + sub_status = subs.add_parser("status")
2.78 + sub_status.set_defaults(func=status)
2.79 +
2.80 + args = parser.parse_args()
2.81 + args.func(args)
2.82 +
2.83 +WD = "working_path"
2.84 +CONFIG = {WD: str}
2.85 +CONFIG_SEP = "="
2.86 +DB_FILE = "tim.db"
2.87 +
2.88 +CURRENCIES = {("EUR", "EURO", "€"): "EUR",
2.89 +("US DOLLAR", "USD", "$"): "USD"}
2.90 +
2.91 +RATE_TYPES = {("h", "hourly"): "hourly"}
2.92 +
2.93 +config = {}
2.94 +
2.95 +# Try to get the user's home directory path
2.96 +try: # Windows
2.97 + from win32com.shell import shellcon, shell
2.98 + HOMEDIR = shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA, 0, 0)
2.99 +except ImportError: # Linux (hopefully)
2.100 + HOMEDIR = os.path.expanduser("~")
2.101 +
2.102 +CONFIG_FILE = "%s/.timrc" % HOMEDIR
2.103 +
2.104 +DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
2.105 +
2.106 +def read_config():
2.107 + config = {}
2.108 + if not os.path.isfile(CONFIG_FILE):
2.109 + write_config(config)
2.110 + with open(CONFIG_FILE, "r") as config_stream:
2.111 + config_lines = [l.split(CONFIG_SEP) for l in config_stream.readlines()
2.112 + if CONFIG_SEP in l]
2.113 + for key, value in config_lines:
2.114 + key = key.strip().lower()
2.115 + value = value.strip()
2.116 + if key in CONFIG:
2.117 + config[key] = CONFIG[key](value)
2.118 + return config
2.119 +
2.120 +def write_config(config):
2.121 + with open(CONFIG_FILE, "w") as config_input:
2.122 + config_input.write("\n".join([CONFIG_SEP.join((k, v))
2.123 + for k, v in config.iteritems() if k in CONFIG]))
2.124 +
2.125 +def db_file():
2.126 + global config
2.127 + if WD not in config:
2.128 + print "Working directory path is not configured. \
2.129 +Please use the init command."
2.130 + return None
2.131 + return config[WD] + "/" + DB_FILE
2.132 +
2.133 +def init(args):
2.134 + global config
2.135 + last_wd = None
2.136 + if WD in config:
2.137 + last_wd = config[WD]
2.138 + config[WD] = args.working_path
2.139 + write_config(config)
2.140 + path_exists = os.path.exists(config[WD]) and os.path.isdir(config[WD])
2.141 + db_exists = path_exists and os.path.exists(db_file())\
2.142 + and os.path.isfile(db_file())
2.143 + if last_wd != config[WD]:
2.144 + print "Changed working directory from %s to %s." % (last_wd, config[WD])
2.145 + else:
2.146 + print "Working directory remains at %s." % config[WD]
2.147 + if not path_exists:
2.148 + print "Warning: working directory %s does not exist." % config[WD]
2.149 + os.makedirs(config[WD])
2.150 + print "Working directory %s created." % config[WD]
2.151 + elif db_exists:
2.152 + print "Database file %s already exists, please delete it before \
2.153 +initiating a new database at this location." % db_file()
2.154 + if not db_exists:
2.155 + db.init(db_file())
2.156 +
2.157 +def parse_label(label):
2.158 + label = label.strip()
2.159 + if ":" in label:
2.160 + project, activity = label.split(":")
2.161 + else:
2.162 + project, activity = (label, None)
2.163 + return project, activity
2.164 +
2.165 +def begin(args):
2.166 + project, activity = parse_label(args.label)
2.167 + time = datetime.now()
2.168 + db.resume(db_file(), None, None, time)
2.169 + db.begin(db_file(), project, activity, time)
2.170 +
2.171 +def end(args):
2.172 + project, activity = parse_label(args.label)
2.173 + log = args.m
2.174 + time = datetime.now()
2.175 + db.resume(db_file(), db.find_active_task(db_file()), time, log)
2.176 + db.end(db_file(), project, activity, time, log)
2.177 +
2.178 +def pause(args):
2.179 + db.pause(db_file(), datetime.now())
2.180 +
2.181 +def resume(args):
2.182 + db.resume(db_file(), None, datetime.now(), args.m)
2.183 +
2.184 +def add_task(args):
2.185 + project, activity = parse_label(args.label)
2.186 + log = args.m
2.187 + begin = datetime.strptime(args.begin, DATETIME_FORMAT)
2.188 + end = datetime.strptime(args.end, DATETIME_FORMAT)
2.189 + db.begin(db_file(), project, activity, begin)
2.190 + db.end(db_file(), project, activity, end, log)
2.191 +
2.192 +def add_break(args):
2.193 + log = args.m
2.194 + begin = datetime.strptime(args.begin, DATETIME_FORMAT)
2.195 + end = datetime.strptime(args.end, DATETIME_FORMAT)
2.196 + db.pause(db_file(), begin)
2.197 + db.resume(db_file(), db.find_active_task(db_file(), begin), end, log)
2.198 +
2.199 +def set_customer(args):
2.200 + db.update_where(db_file(), "projects", "name", args.project, "customer",
2.201 + args.customer)
2.202 +
2.203 +def map_name(map_base, name):
2.204 + mapped = None
2.205 + if name not in map_base.itervalues():
2.206 + for v in map_base.iterkeys():
2.207 + if name in v:
2.208 + mapped = map_base[v]
2.209 + else:
2.210 + mapped = name
2.211 + return mapped
2.212 +
2.213 +def set_rate(args):
2.214 + project, activity = parse_label(args.label)
2.215 + currency = map_name(CURRENCIES, args.currency)
2.216 + type = map_name(RATE_TYPES, args.type)
2.217 + print "setting rate for %s at %f %s %s" % (project, args.rate, currency, type)
2.218 + db.set_rate(db_file(), project, activity, args.rate, currency, type)
2.219 +
2.220 +def status(args):
2.221 + task = db.status(db_file())
2.222 + if task:
2.223 + print "Currently active task"
2.224 + print "Project: %s Activity: %s" % task
2.225 + else:
2.226 + print "There is no active task."
2.227 +
2.228 +if __name__ == "__main__":
2.229 + main()