Changeset 26:0bcab03222d0 in stamper
- Timestamp:
- Jul 30, 2014, 4:31:10 PM (10 years ago)
- Branch:
- default
- Phase:
- public
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
stamper/stamper.py
r25 r26 3 3 import re 4 4 import pygal 5 from datetime import datetime, timedelta5 from datetime import datetime, date, timedelta 6 6 from os.path import expanduser, exists 7 7 from collections import OrderedDict … … 60 60 Validate a given filter. Filters can have the following notation: 61 61 62 - %Y-%m-%d: Times recorded at a given date 63 62 64 - %Y-%m-%d--%Y-%m-%d: Times recorded between two dates 63 65 64 - _%Y-%m-%d: Times recorded before agiven date65 66 - +%Y-%m-%d: Times recorded aftera given date66 - *%Y-%m-%d: Times recorded up to a given date 67 68 - %Y-%m-%d*: Times recorded from a given date 67 69 68 70 - N...N[d|w|m|y]: Times recorded N...N days/weeks/months/years ago 71 72 Important: all date comparisons are made on datetime objects, using 73 00:00 as the time (first second of the given day). This means that 74 for range filters, the first day is included, but the second day is not 69 75 """ 70 # First try the date filters, one by one 71 matches = ['%Y-%m-%d', '_%Y-%m-%d', '+%Y-%m-%d'] 72 for match in matches: 73 try: 74 if '--' in stamp_filter: 75 filter_from, filter_to = stamp_filter.split('--') 76 filter_from = datetime.strptime(filter_from, match) 77 filter_to = datetime.strptime(filter_to, match) 78 else: 79 valid_filter = datetime.strptime(stamp_filter, match) 80 except ValueError: 81 pass 82 else: 83 return stamp_filter 84 85 valid_filter = re.search(r'(\d+[dwmyDWMY]{1})', stamp_filter) 86 if valid_filter: 87 return stamp_filter 88 89 # Not a valid filter 90 return None 76 filter_from = None 77 filter_to = None 78 79 if stamp_filter is None: 80 return filter_from, filter_to 81 82 if '--' in stamp_filter: 83 filter_from, filter_to = stamp_filter.split('--') 84 filter_from = datetime.strptime(filter_from, DATE_FORMAT) 85 filter_to = datetime.strptime(filter_to, DATE_FORMAT) 86 87 elif stamp_filter.startswith('*'): 88 filter_to = datetime.strptime(stamp_filter, '*'+DATE_FORMAT) 89 filter_to = filter_to.replace(hour=0, minute=0, second=0) 90 91 elif stamp_filter.endswith('*'): 92 filter_from = datetime.strptime(stamp_filter, DATE_FORMAT+'*') 93 filter_from = filter_from.replace(hour=0, minute=0, second=0) 94 95 elif re.search(r'(\d+[dD]{1})', stamp_filter): 96 number = int(stamp_filter.lower().replace('d', '')) 97 delta = timedelta(days=number) 98 filter_from = datetime.today() - delta 99 filter_from = filter_from.replace(hour=0, minute=0, second=0) 100 101 elif re.search(r'(\d+[wW]{1})', stamp_filter): 102 number = int(stamp_filter.lower().replace('w', '')) * 7 103 delta = timedelta(days=number) 104 filter_from = datetime.today() - delta 105 filter_from = filter_from.replace(hour=0, minute=0, second=0) 106 107 elif re.search(r'(\d+[mM]{1})', stamp_filter): 108 number = int(stamp_filter.lower().replace('m', '')) 109 past = date.today() 110 # start travelling in time, back to N months ago 111 for n in range(number): 112 past = past.replace(day=1) - timedelta(days=1) 113 # Now use the year/month from the past + the current day to set 114 # the proper date 115 filter_from = datetime(past.year, past.month, date.today().day) 116 117 elif re.search(r'(\d+[yY]{1})', stamp_filter): 118 number = int(stamp_filter.lower().replace('y', '')) 119 today = date.today() 120 filter_from = datetime(today.year - number, today.month, today.day) 121 122 else: 123 # maybe they are giving us a fixed date 124 filter_from = datetime.strptime(stamp_filter, DATE_FORMAT) 125 filter_from = filter_from.replace(hour=0, minute=0, second=0) 126 filter_to = filter_from + timedelta(days=1) 127 128 return filter_from, filter_to 91 129 92 130 def customers(self): … … 98 136 return customers 99 137 100 def totals(self, stamp_filter=None):138 def totals(self, filter_from=None, filter_to=None): 101 139 totals = {} 102 140 for stamp in self.stamps: 103 141 customer = stamp['customer'] 142 # customer will be None for "start" stamps, having no end time 104 143 if customer: 105 # c will be None for "start" stamps, having no end time 144 start = datetime.strptime(stamp['start'], DATETIME_FORMAT) 145 end = datetime.strptime(stamp['end'], DATETIME_FORMAT) 146 if filter_from and start < filter_from: 147 # if there is a filter setting a starting date for the 148 # report and the current stamp is from an earlier date, do 149 # not add it to the totals 150 continue 151 if filter_to and start > filter_to: 152 # similar for the end date 153 continue 106 154 if customer not in totals: 107 155 totals[customer] = 0 … … 109 157 return totals 110 158 111 def details(self ):159 def details(self, filter_customer=None, filter_from=None, filter_to=None): 112 160 details = OrderedDict() 113 161 totals = OrderedDict() 114 162 total_customer = OrderedDict() 115 163 for stamp in self.stamps: 116 if stamp['customer']: 164 customer = stamp['customer'] 165 if customer: 166 if filter_customer and customer != filter_customer: 167 # we are getting the details for only one customer, if this 168 # stamp is not for that customer, simply move on and ignore 169 # it 170 continue 171 start = datetime.strptime(stamp['start'], DATETIME_FORMAT) 172 start_day = start.strftime('%Y-%m-%d') 173 end = datetime.strptime(stamp['end'], DATETIME_FORMAT) 174 if filter_from and start < filter_from: 175 # if there is a filter setting a starting date for the 176 # report and the current stamp is from an earlier date, do 177 # not add it to the totals 178 continue 179 if filter_to and start > filter_to: 180 # similar for the end date 181 continue 117 182 # avoid "start" stamps 118 start_day = stamp['start'].split(' ')[0]119 183 if start_day not in details: 120 184 details[start_day] = [] … … 125 189 ' -> %(worktime)s %(customer)s %(action)s' % { 126 190 'worktime': str(timedelta(seconds=worktime)), 127 'customer': stamp['customer'],191 'customer': customer, 128 192 'action': stamp['action'] 129 193 }) 130 customer = stamp['customer']131 194 totals[start_day] += worktime 132 195 if start_day not in total_customer: … … 139 202 return details, totals, total_customer 140 203 141 def details_by_customer(self, customer):142 details = OrderedDict()143 totals = OrderedDict()144 for stamp in self.stamps:145 if stamp['customer'] == customer:146 start_day = stamp['start'].split(' ')[0]147 if start_day not in details:148 details[start_day] = []149 if start_day not in totals:150 totals[start_day] = 0151 worktime = self.worktime(stamp['start'], stamp['end'])152 details[start_day].append(153 ' -> %(worktime)s %(customer)s %(action)s' % {154 'worktime': str(timedelta(seconds=worktime)),155 'customer': stamp['customer'],156 'action': stamp['action']157 })158 totals[start_day] += worktime159 for day in totals:160 totals[day] = str(timedelta(seconds=totals[day]))161 return details, totals162 163 204 def show_stamps(self, customer=None, stamp_filter=None, verbose=False, 164 205 sum=False, graph=False): 165 if stamp_filter: 166 stamp_filter = self.validate_filter(stamp_filter) 167 168 totals = self.totals(stamp_filter) 169 206 207 filter_from, filter_to = self.validate_filter(stamp_filter) 208 209 # If the user asks for verbose information, show it before the 210 # totals (mimicing what the original stamp tool does) 211 if verbose: 212 details, totals, total_customer = self.details(customer, 213 filter_from, 214 filter_to) 215 for day in details: 216 print('------ %(day)s ------' % {'day': day}) 217 for line in details[day]: 218 print(line) 219 print(' Total: %(total)s' % {'total': totals[day]}) 220 print '-'*79 221 222 # now calculate the totals and show them 223 totals = self.totals(filter_from, filter_to) 170 224 if customer: 171 225 seconds=totals.get(customer, 0) … … 179 233 print(' %(customer)s: %(total)s' % {'customer': c, 180 234 'total': total}) 181 182 if verbose:183 if customer:184 details, totals = self.details_by_customer(customer)185 else:186 details, totals, total_customer = self.details()187 for day in details:188 print('------ %(day)s ------' % {'day': day})189 for line in details[day]:190 print(line)191 print(' Total: %(total)s' % {'total': totals[day]})192 235 193 236 if sum:
Note:
See TracChangeset
for help on using the changeset viewer.