def action_stop(colorizer, time): data = get_data_store().load() ensure_working(data) current_entry = data['work'][-1] ensure_end_after_start(current_entry, time) current_entry['end'] = time get_data_store().dump(data) print('You stopped working on ' + colorizer.red(current_entry['name']) + ' at ' + colorizer.yellow(formatted_str_for_isotime_str(time, '%H:%M')) + '.')
def action_note(colorizer, content): data = get_data_store().load() ensure_working(data) current = data['work'][-1] if 'notes' not in current: current['notes'] = [content] else: current['notes'].append(content) get_data_store().dump(data) print('Yep, noted to ' + colorizer.yellow(current['name']) + '.')
def action_edit(): if "EDITOR" not in os.environ: raise NoEditor("Please set the 'EDITOR' environment variable") store = get_data_store() data = store.load() yml = yaml.safe_dump(data, default_flow_style=False, allow_unicode=True) cmd = os.getenv('EDITOR') fd, temp_path = tempfile.mkstemp(suffix='.yml', prefix='tt.') with open(temp_path, "r+") as f: f.write(yml.replace('\n- ', '\n\n- ')) f.seek(0) subprocess.check_call(cmd + ' ' + temp_path, shell=True) yml = f.read() f.truncate() f.close os.close(fd) os.remove(temp_path) try: data = yaml.load(yml, Loader=yaml.SafeLoader) except yaml.YAMLError as exc: raise InvalidYAML("Oops, that YAML doesn't appear to be valid!") store.dump(data)
def action_tag(tags): data = get_data_store().load() ensure_working(data) current = data['work'][-1] current['tags'] = set(current.get('tags') or []) current['tags'].update(tags) current['tags'] = list(current['tags']) get_data_store().dump(data) tag_count = len(tags) print("Okay, tagged current work with %d tag%s." % (tag_count, "s" if tag_count > 1 else ""))
def action_start(colorizer, name, time): data = get_data_store().load() work = data['work'] if work and 'end' not in work[-1]: raise AlreadyOn("You are already working on %s. Stop it or use a " "different sheet." % (colorizer.yellow(work[-1]['name']), )) entry = { 'name': name, 'start': time, } work.append(entry) get_data_store().dump(data) print('Started working on ' + colorizer.green(name) + ' at ' + colorizer.yellow(formatted_str_for_isotime_str(time, '%H:%M')) + '.')
def generate_day_based_report(): data = get_data_store().load() work = data['work'] report = dict() for item in work: if 'end' in item: day = reportingutils.extract_day(item['start']) duration = parse_isotime(item['end']) - parse_isotime( item['start']) try: report[day] except KeyError: report[day] = defaultdict(lambda: timedelta()) report[day][item['name']] += duration #print ('report[', day, "][", item['name'], "]=" , report[day][item['name']]) return report
def action_log(period): data = get_data_store().load() work = data['work'] log = defaultdict(lambda: {'delta': timedelta()}) current = None for item in work: start_time = parse_isotime(item['start']) if 'end' in item: log[item['name']]['delta'] += ( parse_isotime(item['end']) - start_time) else: log[item['name']]['delta'] += datetime.utcnow() - start_time current = item['name'] name_col_len = 0 for name, item in log.items(): name_col_len = max(name_col_len, len(strip_color(name))) secs = item['delta'].total_seconds() tmsg = [] if secs > 3600: hours = int(secs // 3600) secs -= hours * 3600 tmsg.append(str(hours) + ' hour' + ('s' if hours > 1 else '')) if secs > 60: mins = int(secs // 60) secs -= mins * 60 tmsg.append(str(mins) + ' minute' + ('s' if mins > 1 else '')) if secs: tmsg.append(str(secs) + ' second' + ('s' if secs > 1 else '')) print(tmsg) log[name]['tmsg'] = ', '.join(tmsg)[::-1].replace(',', '& ', 1)[::-1] for name, item in sorted(log.items(), key=(lambda x: x[0]), reverse=True): print(ljust_with_color(name, name_col_len), ' :: ', item['tmsg'], end=' <- working\n' if current == name else '\n')
def action_csv(): sep = '|' data = get_data_store().load() work = data['work'] for item in work: if 'end' in item: notes = reportingutils.get_notes_from_workitem(item) duration = parse_isotime(item['end']) - parse_isotime( item['start']) duration_total = reportingutils.remove_seconds(duration) date = reportingutils.extract_day(item['start']) name = item['name'] start = format_csv_time(item['start']) end = format_csv_time(item['end']) tags = '' if 'tags' in item: tags = item['tags'] print_elements(date, name, start, end, duration_total, notes, tags, sep)
def action_status(colorizer): data = get_data_store().load() ensure_working(data) current = data['work'][-1] start_time = parse_isotime(current['start']) diff = timegap(start_time, datetime.utcnow()) isotime_local = isotime_utc_to_local(current['start']) start_h_m = isotime_local.strftime('%H:%M') now_time_str = datetime.now().strftime('%H:%M'); print('You have been working on {0} for {1}, since {2}; It is now {3}.' .format(colorizer.green(current['name']), colorizer.yellow(diff), colorizer.yellow(start_h_m), colorizer.yellow(now_time_str))) if 'notes' in current: for note in current['notes']: print(' * ', note)
def action_report(colorizer, activity): print('Displaying all entries for ', colorizer.yellow(activity), ' grouped by day:', sep='') print() sep = ' | ' data = get_data_store().load() work = data['work'] report = defaultdict( lambda: { 'sum': timedelta(), 'notes': '', 'weekday': '', 'start_time': None, 'end_time': None }) total_time = 0 for item in work: if item['name'] == activity and 'end' in item: start_time = parse_isotime(item['start']) end_time = parse_isotime(item['end']) day = reportingutils.extract_day(item['start']) duration = parse_isotime(item['end']) - parse_isotime( item['start']) report[day]['sum'] += duration report[day]['notes'] += reportingutils.get_notes_from_workitem( item) report[day][ 'weekday'] = reportingutils.extract_day_custom_formatter( item['start'], '%a') report[day]['start_time'] = get_min_date(report[day]['start_time'], start_time) report[day]['end_time'] = get_max_date(report[day]['end_time'], end_time) total_time += duration.seconds print('weekday', sep, 'date', sep, 'total duration', sep, 'start time', sep, 'end time', sep, 'break', sep, 'description', sep) for date, details in sorted(report.items()): start_time = utc_to_local(details['start_time']).strftime("%H:%M") end_time = utc_to_local(details['end_time']).strftime("%H:%M") break_duration = get_break_duration(details['start_time'], details['end_time'], details['sum']) print(details['weekday'], sep, date, sep, start_time, sep, end_time, sep, format_time(break_duration, colorizer), sep, format_time(details['sum'], colorizer), sep, details['notes'], sep="") should_hours = 8 * len(report.items()) should_hours_str = str(should_hours) + ':00' print() print('Based on your current entries, you should have logged ', colorizer.green(should_hours_str), ' ; you instead logged ', format_time_seconds(total_time, colorizer), sep='')