-
Notifications
You must be signed in to change notification settings - Fork 60
/
pomostat
executable file
·205 lines (179 loc) · 6.82 KB
/
pomostat
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/usr/bin/env python
import sys
import os
from datetime import datetime
from datetime import timedelta
from sigtools import modifiers
from clize import run
import pandas as pd
import matplotlib.pyplot as plt
DATA_FILENAME = os.path.expanduser("~/.pomodoro")
weekmap = [
"Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", "Sunday"
]
day_format = '"%A %d. %B %Y"'
def load():
"""
Load a pomodoro stats file into pandas dataframe
Each row is a pomodoro 'work' session.
The 'rest' sessions are ignored.
The columns are:
- start : starting datetime
- end : ending datetime
- duration : duration in seconds
- day : starting date (without time)
Returns
-------
pandas DataFrame
"""
data = pd.read_csv(DATA_FILENAME, parse_dates=["start"])
data = (data.loc[data["work"] == "work"])
data["day"] = data["start"]
data["day"] = data["day"].apply(lambda x: x.date())
return data
def load_duration_per_day(data):
"""
Given a pandas dataframe `data` obtained with `load`, this
function returns a pandas series with the number of hours of
pomodoros per day. Each element of the series corresponds to
a day with its corresponding number of hours of pomodoros.
Parameters
----------
data : pandas DataFrame
Returns
-------
pandas Series
"""
duration_per_day = data.groupby("day").sum() / 60.
duration_per_day.index = pd.to_datetime(duration_per_day.index)
return duration_per_day
@modifiers.kwoargs('weekof')
def main(action='overall', weekof='today'):
"""
action can be :
overall : all pomodoros together in a plot.
week : nb of hours per day for a specific week.
it accepts as an option "weekof" to specify the week.
weekof can be any day of the week you want to visualize
formatted as a python date.
Example: --weekof='2018-01-01' will correspond to the week
which the first january 2018 belongs to.
days : mean nb of hours per day, globally.
thisweek : nb of hours per day this week.
lastweek : nb of hours per day last week.
stats : mean and standard deviation per day.
weeks : nb of hours per week.
today : nb hours today.
yesterday : nb hours yesterday.
Examples usage
--------------
> pomostat overall
> pomostat thisweek
> pomostat lastweek
> pomostat week --weekof='today' # equivalent to pomostat thisweek
> pomostat week --weekof='2018-01-01' # week of first january
> pomostat yesterday
"""
data = load()
if action == "overall":
duration_per_day = load_duration_per_day(data)
duration_per_day['duration(hours)'] = duration_per_day['duration']
duration_per_day.plot(y='duration(hours)', x_compat=True)
plt.show()
elif action == "days":
D = data.copy()
D = D.groupby(by="date").sum().reset_index()
D["day"] = D["date"].map(lambda x: x.weekday())
D["dayname"] = D["date"].map(lambda x: weekmap[x.weekday()])
D = D.groupby(by="dayname")
D = D.mean() / 60.
D['duration(hours)'] = D['duration']
D = D.reset_index()
D['weekday'] = D['dayname'].map(lambda d: weekmap.index(d))
D = D.sort_values(by='weekday')
print(D)
D = D.plot(
kind="bar",
title="hours per day",
figsize=(8, 8),
x="dayname", y="duration(hours)")
plt.title("Per day hours")
plt.show()
elif action in ("week", "thisweek", "lastweek"):
D = data.copy()
D["day"] = D["start"].map(lambda x: x.weekday())
D["dayname"] = D["start"].map(lambda x: weekmap[x.weekday()])
if action == 'thisweek':
weekof = 'today'
elif action == 'lastweek':
today = datetime.today()
last_monday = (today - timedelta(days=today.weekday()))
last_sunday = last_monday - timedelta(days=1)
weekof = str(last_sunday)
else:
pass # in that case just use weekof
day_of_week = pd.to_datetime(pd.to_datetime(weekof).date())
last_monday_of_week = (
day_of_week - timedelta(days=day_of_week.weekday()))
first_day_of_week = last_monday_of_week
last_day_of_week = first_day_of_week + timedelta(days=7)
D = D[D.start >= first_day_of_week]
D = D[D.start < last_day_of_week]
if len(D) == 0:
print('The week from {} to {} has no recorded pomodoro '
'sessions.'.format(
first_day_of_week.date(),
last_day_of_week.date()))
sys.exit(0)
D = D.groupby(by=["day", "dayname"])
D = D.sum() / 60.
total_week_hours = D['duration'].sum()
D['duration(hours)'] = D['duration']
D = D.reset_index().plot(
kind="bar",
title="hours per day",
figsize=(8, 8),
x="dayname", y="duration(hours)")
plt.title("Total in the week : {:.3f} hours. Week of {} to {}".format(
total_week_hours,
first_day_of_week.strftime(day_format),
last_day_of_week.strftime(day_format),
))
plt.show()
elif action == "stats":
per_day = (data.groupby(by="day")).sum() / 60.
print("per day : {}+/-{}".format(per_day.values.mean(),
per_day.values.std()))
elif action == "weeks":
def week(x):
return x.isocalendar()[1]
def year(x):
return x.isocalendar()[0]
data['week'] = data['day'].apply(week)
data['year'] = data['day'].apply(year)
data = data.groupby(('week', 'year')).sum() / 60
data = data.reset_index()
data = data.sort_values(by=['year', 'week'], ascending=[True, True])
data['weekyear'] = data['week'] + \
(data['year'] - data['year'].min()) * 52
data['duration(hours)'] = data['duration']
print(data)
data.plot(title="hours per week", x='weekyear', y='duration(hours)')
plt.axhline(y=data['duration'].max(), c='green', ls='dashed')
plt.show()
elif action == "today" or action == "yesterday":
d = 1 if action == "yesterday" else 0
day = datetime.today().date() - timedelta(d)
next_day = day + timedelta(1)
day = pd.to_datetime(day)
next_day = pd.to_datetime(next_day)
D = data[(data["start"] >= day) & (data["start"] < next_day)]
print("{:.3f} h".format(D["duration"].sum() / 60.))
else:
print('Action not recognized. It should be : "overall" or '
'"week" or "days" or "thisweek" or "lastweek" or'
'"stats" or "weeks" or "today" or "yesterday".')
sys.exit(1)
if __name__ == "__main__":
run(main)