tim.py
author Eugen Sawin <sawine@me73.com>
Mon, 18 Jul 2011 01:03:37 +0200
changeset 14 1b45f8231179
permissions -rw-r--r--
Renamed and added default config file.
     1 #!/usr/local/bin/python
     2 # -*- coding: utf-8 -*-
     3 
     4 """
     5 Name: tim - A time recording tool, because time is money.
     6 Author: Eugen Sawin <sawine@me73.com>
     7 Dependencies: Python 2.7 and libsqlite3-dev
     8 """
     9 
    10 import os
    11 import sys
    12 import argparse
    13 from datetime import datetime
    14 
    15 import db
    16 
    17 def main():
    18 	global config
    19 	config = read_config()
    20 	parser = argparse.ArgumentParser(description="Records time.")
    21 	subs = parser.add_subparsers()
    22 
    23 	sub_init = subs.add_parser("init")
    24 	sub_init.add_argument("working_path")
    25 	sub_init.set_defaults(func=init)
    26 
    27 	sub_begin = subs.add_parser("begin")
    28 	sub_begin.add_argument("label")
    29 	sub_begin.set_defaults(func=begin)
    30 
    31 	sub_end = subs.add_parser("end")
    32 	sub_end.add_argument("label", nargs="?", default="all:all")
    33 	sub_end.add_argument("-m")
    34 	sub_end.set_defaults(func=end)
    35 
    36 	sub_pause = subs.add_parser("pause")
    37 	sub_pause.set_defaults(func=pause)
    38 
    39 	sub_resume = subs.add_parser("resume")
    40 	sub_resume.add_argument("-m")
    41 	sub_resume.set_defaults(func=resume)
    42 
    43 	sub_add = subs.add_parser("add")
    44 	sub_add_subs = sub_add.add_subparsers()
    45 
    46 	sub_add_task = sub_add_subs.add_parser("task")
    47 	sub_add_task.add_argument("label")
    48 	sub_add_task.add_argument("begin")
    49 	sub_add_task.add_argument("end")
    50 	sub_add_task.add_argument("-m")
    51 	sub_add_task.set_defaults(func=add_task)
    52 	
    53 	sub_add_break = sub_add_subs.add_parser("break")
    54 	sub_add_break.add_argument("begin")
    55 	sub_add_break.add_argument("end")
    56 	sub_add_break.add_argument("-m")
    57 	sub_add_break.set_defaults(func=add_break)
    58 
    59 	sub_set = subs.add_parser("set")
    60 	sub_set_subs = sub_set.add_subparsers()
    61 
    62 	sub_set_customer = sub_set_subs.add_parser("customer")
    63 	sub_set_customer.add_argument("project")
    64 	sub_set_customer.add_argument("customer")
    65 	sub_set_customer.set_defaults(func=set_customer)
    66 
    67 	sub_set_rate = sub_set_subs.add_parser("rate")
    68 	sub_set_rate.add_argument("label")
    69 	sub_set_rate.add_argument("rate", type=float)
    70 	sub_set_rate.add_argument("currency")
    71 	sub_set_rate.add_argument("type")
    72 	sub_set_rate.set_defaults(func=set_rate)
    73 
    74 	sub_status = subs.add_parser("status")
    75 	sub_status.set_defaults(func=status)
    76 
    77 	args = parser.parse_args()
    78 	args.func(args)
    79 
    80 WD = "working_path"
    81 CONFIG = {WD: str}
    82 CONFIG_SEP = "="
    83 DB_FILE = "tim.db"
    84 
    85 CURRENCIES = {("EUR", "EURO", "€"): "EUR",
    86 ("US DOLLAR", "USD", "$"): "USD"}
    87 
    88 RATE_TYPES = {("h", "hourly"): "hourly"}
    89 
    90 config = {}
    91 
    92 # Try to get the user's home directory path
    93 try: # Windows
    94 	from win32com.shell import shellcon, shell
    95 	HOMEDIR	= shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA, 0, 0)
    96 except ImportError: # Linux (hopefully)
    97 	HOMEDIR = os.path.expanduser("~")
    98 
    99 CONFIG_FILE = "%s/.timrc" % HOMEDIR
   100 
   101 DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
   102 
   103 def read_config():
   104 	config = {}
   105 	if not os.path.isfile(CONFIG_FILE):
   106 		write_config(config)
   107 	with open(CONFIG_FILE, "r") as config_stream:
   108 		config_lines = [l.split(CONFIG_SEP) for l in config_stream.readlines() 
   109 						if CONFIG_SEP in l]
   110 		for key, value in config_lines:
   111 			key = key.strip().lower()
   112 			value = value.strip()
   113 			if key in CONFIG:
   114 				config[key] = CONFIG[key](value)
   115 	return config
   116 
   117 def write_config(config):
   118 	with open(CONFIG_FILE, "w") as config_input:
   119 		config_input.write("\n".join([CONFIG_SEP.join((k, v)) 
   120 							for k, v in config.iteritems() if k in CONFIG]))
   121 
   122 def db_file():
   123 	global config
   124 	if WD not in config:
   125 		print "Working directory path is not configured. \
   126 Please use the init command."
   127 		return None
   128 	return config[WD] + "/" + DB_FILE
   129 	
   130 def init(args):
   131 	global config
   132 	last_wd = None
   133 	if WD in config:
   134 		last_wd = config[WD]
   135 	config[WD] = args.working_path
   136 	write_config(config)
   137 	path_exists = os.path.exists(config[WD]) and os.path.isdir(config[WD])
   138 	db_exists = path_exists and os.path.exists(db_file())\
   139 				and os.path.isfile(db_file())
   140 	if last_wd != config[WD]:		
   141 		print "Changed working directory from %s to %s." % (last_wd, config[WD])
   142 	else:
   143 		print "Working directory remains at %s." % config[WD]
   144 	if not path_exists:
   145 		print "Warning: working directory %s does not exist." % config[WD]
   146 		os.makedirs(config[WD])
   147 		print "Working directory %s created." % config[WD]
   148 	elif db_exists:
   149 		print "Database file %s already exists, please delete it before \
   150 initiating a new database at this location." % db_file()
   151 	if not db_exists:
   152 		db.init(db_file())	
   153 
   154 def parse_label(label):
   155 	label = label.strip()
   156 	if ":" in label:
   157 		project, activity = label.split(":")
   158 	else:
   159 		project, activity = (label, None)
   160 	return project, activity
   161 
   162 def begin(args):
   163 	project, activity = parse_label(args.label)
   164 	time = datetime.now()
   165 	db.resume(db_file(), None, None, time)
   166 	db.begin(db_file(), project, activity, time)	
   167     
   168 def end(args):
   169 	project, activity = parse_label(args.label)
   170 	log = args.m	
   171 	time = datetime.now()
   172 	db.resume(db_file(), db.find_active_task(db_file()), time, log)
   173 	db.end(db_file(), project, activity, time, log)	
   174 
   175 def pause(args):
   176 	db.pause(db_file(), datetime.now())	 
   177 
   178 def resume(args):
   179 	db.resume(db_file(), None, datetime.now(), args.m)	
   180 
   181 def add_task(args):
   182 	project, activity = parse_label(args.label)
   183 	log = args.m
   184 	begin = datetime.strptime(args.begin, DATETIME_FORMAT)
   185 	end = datetime.strptime(args.end, DATETIME_FORMAT)
   186 	db.begin(db_file(), project, activity, begin)
   187 	db.end(db_file(), project, activity, end, log)
   188 
   189 def add_break(args):
   190 	log = args.m
   191 	begin = datetime.strptime(args.begin, DATETIME_FORMAT)
   192 	end = datetime.strptime(args.end, DATETIME_FORMAT)
   193 	db.pause(db_file(), begin)
   194 	db.resume(db_file(), db.find_active_task(db_file(), begin), end, log)
   195 
   196 def set_customer(args):
   197 	db.update_where(db_file(), "projects", "name", args.project, "customer", 
   198 				args.customer)
   199 
   200 def map_name(map_base, name):
   201 	mapped = None
   202 	if name not in map_base.itervalues():
   203 		for v in map_base.iterkeys():
   204 			if name in v:
   205 				mapped = map_base[v]
   206 	else:
   207 		mapped = name
   208 	return mapped
   209 
   210 def set_rate(args):
   211 	project, activity = parse_label(args.label)
   212 	currency = map_name(CURRENCIES, args.currency)
   213 	type = map_name(RATE_TYPES, args.type)
   214 	print "setting rate for %s at %f %s %s" % (project, args.rate, currency, type)
   215 	db.set_rate(db_file(), project, activity, args.rate, currency, type)
   216 
   217 def status(args):
   218 	task = db.status(db_file())
   219 	if task:
   220 		print "Currently active task"
   221 		print "Project: %s Activity: %s" % task
   222 	else:
   223 		print "There is no active task."
   224 
   225 if __name__ == "__main__":
   226 	main()