source: stamper/stamper/stamper.py@ 19:3fe3553bab17

Last change on this file since 19:3fe3553bab17 was 19:3fe3553bab17, checked in by Borja Lopez <borja@…>, 10 years ago

Show the detailed times properly ordered

File size: 6.1 KB
Line 
1
2import json
3import re
4from datetime import datetime, timedelta
5from os.path import expanduser, exists
6from collections import OrderedDict
7
8
9STAMPS_FILE = expanduser('~/.workstamps.json')
10DATE_FORMAT = '%Y-%m-%d %H:%M'
11
12
13class Stamper(object):
14
15 def __init__(self, stamps_file=STAMPS_FILE):
16 self.stamps_file = STAMPS_FILE
17 self.ensure_stamps_file()
18 self.stamps = []
19
20 def ensure_stamps_file(self):
21 if not exists(self.stamps_file):
22 with open(self.stamps_file, 'w') as stamps_file:
23 stamps_file.write('')
24
25 def load_stamps(self):
26 with open(self.stamps_file, 'r') as stamps_file:
27 try:
28 self.stamps = json.load(stamps_file)
29 except ValueError:
30 self.stamps = []
31
32 def save_stamps(self):
33 with open(self.stamps_file, 'w') as stamps_file:
34 json.dump(self.stamps, stamps_file, indent=4)
35
36 def stamp(self, start, end, customer, action):
37 self.stamps.append({
38 'start': start,
39 'end': end,
40 'customer': customer,
41 'action': action,
42 })
43
44 def last_stamp(self):
45 if not self.stamps:
46 return None
47 return self.stamps[-1]
48
49 def worktime(self, start, end):
50 worktime = (datetime.strptime(end, DATE_FORMAT) -
51 datetime.strptime(start, DATE_FORMAT))
52 return worktime.seconds
53
54 def validate_filter(self, stamp_filter):
55 """
56 Validate a given filter. Filters can have the following notation:
57
58 - %Y-%m-%d--%Y-%m-%d: Times recorded between two dates
59
60 - -%Y-%m-%d: Times recorded before a given date
61
62 - +%Y-%m-%d: Times recorded after a given date
63
64 - N...N[d|w|m|y]: Times recorded N...N days/weeks/months/years ago
65 """
66 # First try the date filters, one by one
67 matches = ['%Y-%m-%d', '-%Y-%m-%d', '+%Y-%m-%d']
68 for match in matches:
69 try:
70 if '--' in stamp_filter:
71 filter_from, filter_to = stamp_filter.split('--')
72 filter_from = datetime.strptime(stamp_filter, match)
73 filter_to = datetime.strptime(stamp_filter, match)
74 else:
75 valid_filter = datetime.strptime(stamp_filter, match)
76 except ValueError:
77 pass
78 else:
79 return stamp_filter
80
81 valid_filter = re.search(r'(\d+[dwmyDWMY]{1})', stamp_filter)
82 if valid_filter:
83 return stamp_filter
84
85 # Not a valid filter
86 return None
87
88 def customers(self):
89 customers = []
90 for stamp in self.stamps:
91 if stamp['customer'] not in customers:
92 customers.append(stamp['customer'])
93 customers.remove(None)
94 return customers
95
96 def totals(self, stamp_filter=None):
97 totals = {}
98 for stamp in self.stamps:
99 customer = stamp['customer']
100 if customer:
101 # c will be None for "start" stamps, having no end time
102 if customer not in totals:
103 totals[customer] = 0
104 totals[customer] += self.worktime(stamp['start'], stamp['end'])
105 return totals
106
107 def details(self):
108 details = OrderedDict()
109 totals = OrderedDict()
110 for stamp in self.stamps:
111 if stamp['customer']:
112 # avoid "start" stamps
113 start_day = stamp['start'].split(' ')[0]
114 if start_day not in details:
115 details[start_day] = []
116 if start_day not in totals:
117 totals[start_day] = 0
118 worktime = self.worktime(stamp['start'], stamp['end'])
119 details[start_day].append(
120 ' -> %(worktime)s %(customer)s %(action)s' % {
121 'worktime': str(timedelta(seconds=worktime)),
122 'customer': stamp['customer'],
123 'action': stamp['action']
124 })
125 totals[start_day] += worktime
126 for day in totals:
127 totals[day] = str(timedelta(seconds=totals[day]))
128 return details, totals
129
130 def details_by_customer(self, customer):
131 details = OrderedDict()
132 totals = OrderedDict()
133 for stamp in self.stamps:
134 if stamp['customer'] == customer:
135 start_day = stamp['start'].split(' ')[0]
136 if start_day not in details:
137 details[start_day] = []
138 if start_day not in totals:
139 totals[start_day] = 0
140 worktime = self.worktime(stamp['start'], stamp['end'])
141 details[start_day].append(
142 ' -> %(worktime)s %(customer)s %(action)s' % {
143 'worktime': str(timedelta(seconds=worktime)),
144 'customer': stamp['customer'],
145 'action': stamp['action']
146 })
147 totals[start_day] += worktime
148 for day in totals:
149 totals[day] = str(timedelta(seconds=totals[day]))
150 return details, totals
151
152 def show_stamps(self, customer=None, stamp_filter=None, verbose=False):
153 if stamp_filter:
154 stamp_filter = self.validate_filter(stamp_filter)
155
156 totals = self.totals(stamp_filter)
157
158 if customer:
159 total = timedelta(seconds=totals.get(customer, 0))
160 print(' %(customer)s: %(total)s' % {'customer': customer,
161 'total': total})
162 else:
163 for c in totals:
164 total = timedelta(seconds=totals[c])
165 print(' %(customer)s: %(total)s' % {'customer': c,
166 'total': total})
167
168 if verbose:
169 if customer:
170 details, totals = self.details_by_customer(customer)
171 else:
172 details, totals = self.details()
173 for day in details:
174 print('------ %(day)s ------' % {'day': day})
175 for line in details[day]:
176 print(line)
177 print(' Total: %(total)s' % {'total': totals[day]})
Note: See TracBrowser for help on using the repository browser.