sawine@6
|
1 |
#!/usr/local/bin/python
|
sawine@0
|
2 |
|
sawine@0
|
3 |
"""
|
sawine@7
|
4 |
tim - A time recording tool, because time is money.
|
sawine@0
|
5 |
Author: Eugen Sawin (sawine@me73.com)
|
sawine@7
|
6 |
Dependencies: Python 2.7 and libsqlite3-dev
|
sawine@0
|
7 |
"""
|
sawine@0
|
8 |
|
sawine@1
|
9 |
import os
|
sawine@2
|
10 |
import sys
|
sawine@0
|
11 |
import argparse
|
sawine@2
|
12 |
from datetime import datetime
|
sawine@3
|
13 |
|
sawine@3
|
14 |
import db
|
sawine@0
|
15 |
|
sawine@1
|
16 |
WD = "working_path"
|
sawine@1
|
17 |
CONFIG = {WD: str}
|
sawine@1
|
18 |
CONFIG_SEP = "="
|
sawine@2
|
19 |
DB_FILE = "cronrec.db"
|
sawine@1
|
20 |
|
sawine@1
|
21 |
config = {}
|
sawine@1
|
22 |
|
sawine@7
|
23 |
# Try to get the user's home directory path
|
sawine@1
|
24 |
try: # Windows
|
sawine@1
|
25 |
from win32com.shell import shellcon, shell
|
sawine@1
|
26 |
HOMEDIR = shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA, 0, 0)
|
sawine@2
|
27 |
except ImportError: # Linux (hopefully)
|
sawine@1
|
28 |
HOMEDIR = os.path.expanduser("~")
|
sawine@1
|
29 |
|
sawine@1
|
30 |
CONFIG_FILE = "%s/.cronrecrc" % HOMEDIR
|
sawine@1
|
31 |
|
sawine@10
|
32 |
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
sawine@10
|
33 |
|
sawine@1
|
34 |
def read_config():
|
sawine@1
|
35 |
config = {}
|
sawine@1
|
36 |
with open(CONFIG_FILE, "r") as config_stream:
|
sawine@1
|
37 |
config_lines = [l.split(CONFIG_SEP) for l in config_stream.readlines()
|
sawine@1
|
38 |
if CONFIG_SEP in l]
|
sawine@1
|
39 |
for key, value in config_lines:
|
sawine@1
|
40 |
key = key.strip().lower()
|
sawine@1
|
41 |
value = value.strip()
|
sawine@1
|
42 |
if key in CONFIG:
|
sawine@1
|
43 |
config[key] = CONFIG[key](value)
|
sawine@1
|
44 |
return config
|
sawine@1
|
45 |
|
sawine@1
|
46 |
def write_config(config):
|
sawine@1
|
47 |
with open(CONFIG_FILE, "w") as config_input:
|
sawine@1
|
48 |
config_input.write("\n".join([CONFIG_SEP.join((k, v))
|
sawine@1
|
49 |
for k, v in config.iteritems() if k in CONFIG]))
|
sawine@1
|
50 |
|
sawine@3
|
51 |
def db_file():
|
sawine@3
|
52 |
global config
|
sawine@3
|
53 |
if WD not in config:
|
sawine@3
|
54 |
print "Working directory path is not configured. \
|
sawine@3
|
55 |
Please use the init command."
|
sawine@3
|
56 |
return None
|
sawine@3
|
57 |
return config[WD] + "/" + DB_FILE
|
sawine@3
|
58 |
|
sawine@1
|
59 |
def init(args):
|
sawine@2
|
60 |
global config
|
sawine@1
|
61 |
last_wd = None
|
sawine@1
|
62 |
if WD in config:
|
sawine@1
|
63 |
last_wd = config[WD]
|
sawine@2
|
64 |
config[WD] = args.working_path
|
sawine@1
|
65 |
write_config(config)
|
sawine@2
|
66 |
path_exists = os.path.exists(config[WD]) and os.path.isdir(config[WD])
|
sawine@3
|
67 |
db_exists = path_exists and os.path.exists(db_file())\
|
sawine@3
|
68 |
and os.path.isfile(db_file())
|
sawine@2
|
69 |
if last_wd != config[WD]:
|
sawine@2
|
70 |
print "Changed working directory from %s to %s." % (last_wd, config[WD])
|
sawine@1
|
71 |
else:
|
sawine@2
|
72 |
print "Working directory remains at %s." % config[WD]
|
sawine@2
|
73 |
if not path_exists:
|
sawine@2
|
74 |
print "Warning: working directory %s does not exist." % config[WD]
|
sawine@2
|
75 |
os.makedirs(config[WD])
|
sawine@2
|
76 |
print "Working directory %s created." % config[WD]
|
sawine@2
|
77 |
elif db_exists:
|
sawine@3
|
78 |
print "Database file %s already exists, please delete it before \
|
sawine@3
|
79 |
initiating a new database at this location." % db_file()
|
sawine@2
|
80 |
if not db_exists:
|
sawine@3
|
81 |
db.init(db_file())
|
sawine@1
|
82 |
|
sawine@10
|
83 |
def parse_label(label):
|
sawine@10
|
84 |
label = label.strip()
|
sawine@2
|
85 |
if ":" in label:
|
sawine@2
|
86 |
project, activity = label.split(":")
|
sawine@2
|
87 |
else:
|
sawine@10
|
88 |
project, activity = (label, None)
|
sawine@10
|
89 |
return project, activity
|
sawine@10
|
90 |
|
sawine@10
|
91 |
def begin(args):
|
sawine@10
|
92 |
project, activity = parse_label(args.label)
|
sawine@8
|
93 |
time = datetime.now()
|
sawine@8
|
94 |
db.resume(db_file(), None, None, time)
|
sawine@8
|
95 |
db.begin(db_file(), project, activity, time)
|
sawine@0
|
96 |
|
sawine@0
|
97 |
def end(args):
|
sawine@10
|
98 |
project, activity = parse_label(args.label)
|
sawine@10
|
99 |
log = args.m
|
sawine@8
|
100 |
time = datetime.now()
|
sawine@9
|
101 |
db.resume(db_file(), db.find_active_task(db_file()), time, log)
|
sawine@9
|
102 |
db.end(db_file(), project, activity, time, log)
|
sawine@8
|
103 |
|
sawine@8
|
104 |
def pause(args):
|
sawine@8
|
105 |
db.pause(db_file(), None, None, datetime.now())
|
sawine@8
|
106 |
|
sawine@8
|
107 |
def resume(args):
|
sawine@9
|
108 |
db.resume(db_file(), None, None, datetime.now(), args.m)
|
sawine@2
|
109 |
|
sawine@10
|
110 |
def add_task(args):
|
sawine@10
|
111 |
project, activity = parse_label(args.label)
|
sawine@10
|
112 |
log = args.m
|
sawine@10
|
113 |
begin = datetime.strptime(args.begin, DATETIME_FORMAT)
|
sawine@10
|
114 |
end = datetime.strptime(args.end, DATETIME_FORMAT)
|
sawine@10
|
115 |
db.begin(db_file(), project, activity, begin)
|
sawine@10
|
116 |
db.end(db_file(), project, activity, end, log)
|
sawine@10
|
117 |
|
sawine@7
|
118 |
def status(args):
|
sawine@8
|
119 |
task = db.status(db_file())
|
sawine@8
|
120 |
if task:
|
sawine@8
|
121 |
print "Currently active task"
|
sawine@8
|
122 |
print "Project: %s Activity: %s" % task
|
sawine@7
|
123 |
else:
|
sawine@8
|
124 |
print "There is no active task."
|
sawine@7
|
125 |
|
sawine@2
|
126 |
def main():
|
sawine@2
|
127 |
global config
|
sawine@1
|
128 |
config = read_config()
|
sawine@1
|
129 |
parser = argparse.ArgumentParser(description="Records time.")
|
sawine@1
|
130 |
subs = parser.add_subparsers()
|
sawine@10
|
131 |
|
sawine@10
|
132 |
sub_init = subs.add_parser("init")
|
sawine@10
|
133 |
sub_init.add_argument("working_path", type=str)
|
sawine@10
|
134 |
sub_init.set_defaults(func=init)
|
sawine@0
|
135 |
|
sawine@1
|
136 |
sub_begin = subs.add_parser("begin")
|
sawine@2
|
137 |
sub_begin.add_argument("label", type=str)
|
sawine@1
|
138 |
sub_begin.set_defaults(func=begin)
|
sawine@0
|
139 |
|
sawine@1
|
140 |
sub_end = subs.add_parser("end")
|
sawine@7
|
141 |
sub_end.add_argument("label", type=str, nargs="?", default="all:all")
|
sawine@9
|
142 |
sub_end.add_argument("-m", type=str)
|
sawine@1
|
143 |
sub_end.set_defaults(func=end)
|
sawine@0
|
144 |
|
sawine@10
|
145 |
sub_pause = subs.add_parser("pause")
|
sawine@10
|
146 |
sub_pause.set_defaults(func=pause)
|
sawine@8
|
147 |
|
sawine@10
|
148 |
sub_resume = subs.add_parser("resume")
|
sawine@10
|
149 |
sub_resume.add_argument("-m", type=str)
|
sawine@10
|
150 |
sub_resume.set_defaults(func=resume)
|
sawine@8
|
151 |
|
sawine@10
|
152 |
sub_add = subs.add_parser("add")
|
sawine@10
|
153 |
sub_add_subs = sub_add.add_subparsers()
|
sawine@10
|
154 |
sub_add_task = sub_add_subs.add_parser("task")
|
sawine@10
|
155 |
sub_add_task.add_argument("label", type=str)
|
sawine@10
|
156 |
sub_add_task.add_argument("begin", type=str)
|
sawine@10
|
157 |
sub_add_task.add_argument("end", type=str)
|
sawine@10
|
158 |
sub_add_task.add_argument("-m", type=str)
|
sawine@10
|
159 |
sub_add_task.set_defaults(func=add_task)
|
sawine@10
|
160 |
|
sawine@10
|
161 |
sub_status = subs.add_parser("status")
|
sawine@10
|
162 |
sub_status.set_defaults(func=status)
|
sawine@7
|
163 |
|
sawine@1
|
164 |
args = parser.parse_args()
|
sawine@1
|
165 |
args.func(args)
|
sawine@2
|
166 |
|
sawine@2
|
167 |
if __name__ == "__main__":
|
sawine@2
|
168 |
main()
|