-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTrackedMonth.py
More file actions
123 lines (100 loc) · 5.54 KB
/
TrackedMonth.py
File metadata and controls
123 lines (100 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from datastructures import *
from utils import binary_question_input
from calendar import monthrange
import datetime
import numpy as np
import pandas as pd
from pathlib import Path
class TrackedMonth:
@classmethod
def from_file(cls, filepath: Path):
raw_data = pd.read_csv(filepath, sep=',', header=0)
# ensure the given file has the expected columns
TrackedMonth.validate_header(raw_data)
raw_data['date'] = raw_data['date'].apply(lambda d: datetime.datetime.strptime(d, '%d.%m.%Y').date())
raw_data['start'] = raw_data['start'].apply(Time.from_string)
raw_data['end'] = raw_data['end'].apply(Time.from_string)
raw_data['work time'] = raw_data['work time'].apply(Time.from_string)
raw_data['running total'] = raw_data['running total'].apply(Time.from_string)
month_tracked = datetime.date(raw_data['date'][0].year, raw_data['date'][0].month, 1)
return cls(raw_data, month_tracked)
@classmethod
def from_date(cls, d: datetime.date, target_dir=Path('./data')):
filepath = target_dir / '{}_{}_{}.csv'.format(d.year,
d.strftime('%m'),
d.strftime('%B'))
if not filepath.exists():
d = datetime.date(d.year, d.month, 1)
return cls(data=pd.DataFrame(columns=['date', 'start', 'end', 'work time', 'running total', 'description']),
month=d)
else:
return cls.from_file(filepath)
@staticmethod
def validate_header(data: pd.DataFrame):
expected_header = ['date', 'start', 'end', 'work time', 'running total', 'description']
assert np.array_equal(expected_header, data.columns)
def __init__(self, data: pd.DataFrame, month: datetime.date):
TrackedMonth.validate_header(data)
self._data = data
self._month = month
self.sort()
self.recalculate_running_total()
def save(self, target_dir: Path = Path('./data')):
filepath = target_dir / '{}_{}_{}.csv'.format(self._month.year,
self._month.strftime('%m'),
self._month.strftime('%B'))
filepath.parent.mkdir(parents=True, exist_ok=True)
self.sort()
self.recalculate_running_total()
output_df = self._data
output_df['date'] = output_df['date'].apply(lambda d: d.strftime('%d.%m.%Y'))
output_df.to_csv(filepath, sep=',', index=False)
def add_entry(self, date: datetime.date, start: Time, end: Time, description: str):
found_overlap, overlapping_entries = self.overlapping_entry_exists(date, start, end)
if found_overlap:
print('The work time entry {} starting {} and ending {} overlaps with the following entries:'
.format(date.strftime('%d.%m.%Y'), start.as_string(), end.as_string()))
overlap = self._data[overlapping_entries]
for el in overlap.iterrows():
print('{} start {} end {}'.format(el[1]['date'].strftime('%d.%m.%Y'), el[1]['start'], el[1]['end']))
if binary_question_input('Do you want to overwrite all of these entries? (y/n) '):
self._data = self._data[overlapping_entries.apply(lambda b: not b)]
else:
return
new_entry = pd.DataFrame(columns=['date', 'start', 'end', 'work time', 'running total', 'description'])
new_entry.loc[0] = [date, start, end, end - start, Time(0, 0, False), description]
self._data = self._data.append(new_entry)
self.sort()
self.recalculate_running_total()
def overlapping_entry_exists(self, date: datetime.date, start: Time, end: Time):
same_date_entries = self._data['date'] == date
df = self._data[same_date_entries]
overlapping_entries = same_date_entries
for el in df.iterrows():
idx = el[0]
dat = el[1]
overlapping_entries[idx] = overlapping_entries[idx] \
and (((start <= dat['start']) and (end > dat['start']))
or ((dat['start'] < start) and dat['end'] > start))
return overlapping_entries.any(), overlapping_entries
def sort(self):
self._data.sort_values(by=['date', 'start'], axis=0, inplace=True, ignore_index=True, kind='heapsort')
def recalculate_running_total(self):
running_total = Time(0, 0, False)
for el in self._data.iterrows():
running_total = running_total + el[1]['work time']
self._data['running total'][el[0]] = running_total
def get_month_summary(self, hours_per_week=None):
df = pd.DataFrame(columns=['month', 'time done', 'running total', 'time required', 'overtime done',
'total overtime', 'hours per week'])
if hours_per_week is None:
hpw = input('How many hours per week do you have to work in {} (as float)? '
.format(self._month.strftime('%B %Y')))
hours_per_week = float(hpw)
req_t = Time.required_time_calculation(monthrange(self._month.year, self._month.month)[1], hours_per_week)
total_t = self._data['running total'].max()
df.loc[0] = [Month(self._month.month, self._month.year), total_t, Time(0, 0, False), req_t, total_t - req_t,
Time(0, 0, False), hours_per_week]
return df
def month(self):
return Month(self._month.month, self._month.year)