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