1 | from decimal import Decimal |
---|
2 | from datetime import datetime, timedelta, timezone |
---|
3 | |
---|
4 | import pytest |
---|
5 | from pyramid.security import Allow, Everyone, Deny, ALL_PERMISSIONS |
---|
6 | |
---|
7 | from ow.models.root import OpenWorkouts |
---|
8 | from ow.models.workout import Workout |
---|
9 | from ow.models.user import User |
---|
10 | |
---|
11 | |
---|
12 | class TestUser(object): |
---|
13 | @pytest.fixture |
---|
14 | def root(self): |
---|
15 | root = OpenWorkouts() |
---|
16 | root['john'] = User(firstname='John', lastname='Doe', |
---|
17 | email='john.doe@example.net') |
---|
18 | root['john'].password = 's3cr3t' |
---|
19 | return root |
---|
20 | |
---|
21 | @pytest.fixture |
---|
22 | def workouts(self): |
---|
23 | workouts = [Workout(sport='running'), |
---|
24 | Workout(sport='cycling'), |
---|
25 | Workout(sport='swimming')] |
---|
26 | return workouts |
---|
27 | |
---|
28 | def test_user_attrs(self, root): |
---|
29 | assert root['john'].firstname == 'John' |
---|
30 | assert root['john'].lastname == 'Doe' |
---|
31 | assert root['john'].email == 'john.doe@example.net' |
---|
32 | |
---|
33 | def test__acl__(self, root): |
---|
34 | uid = str(root['john'].uid) |
---|
35 | permissions = [ |
---|
36 | (Allow, uid, 'view'), |
---|
37 | (Allow, uid, 'edit'), |
---|
38 | (Deny, Everyone, ALL_PERMISSIONS), |
---|
39 | ] |
---|
40 | assert root['john'].__acl__() == permissions |
---|
41 | |
---|
42 | def test__str__(self, root): |
---|
43 | email = root['john'].email |
---|
44 | uid = str(root['john'].uid) |
---|
45 | assert root['john'].__str__() == u'User: ' + uid + ' (' + email + ')' |
---|
46 | |
---|
47 | def test__repr__(self, root): |
---|
48 | email = root['john'].email |
---|
49 | uid = str(root['john'].uid) |
---|
50 | expected = u'<ow.models.user.User: ' + uid + ' (' + email + ')>' |
---|
51 | assert root['john'].__repr__() == expected |
---|
52 | |
---|
53 | def test_fullname(self, root): |
---|
54 | assert root['john'].fullname == 'John Doe' |
---|
55 | |
---|
56 | def test_password_is_encrypted(self, root): |
---|
57 | assert root['john'].password != 's3cr3t' |
---|
58 | |
---|
59 | def test_check_wrong_password(self, root): |
---|
60 | assert not root['john'].check_password('badpass') |
---|
61 | |
---|
62 | def test_check_good_password(self, root): |
---|
63 | assert root['john'].check_password('s3cr3t') |
---|
64 | |
---|
65 | def test_add_workout(self, root, workouts): |
---|
66 | # First add all workouts at once |
---|
67 | for workout in workouts: |
---|
68 | root['john'].add_workout(workout) |
---|
69 | # Then check they are there, in the correct position/number |
---|
70 | for workout in workouts: |
---|
71 | index = workouts.index(workout) + 1 |
---|
72 | assert root['john'][str(index)] == workout |
---|
73 | |
---|
74 | def test_workouts(self, root, workouts): |
---|
75 | # First add all workouts at once |
---|
76 | for workout in workouts: |
---|
77 | root['john'].add_workout(workout) |
---|
78 | # workouts() will return the workouts sorted from newest to oldest |
---|
79 | workouts.reverse() |
---|
80 | assert list(root['john'].workouts()) == workouts |
---|
81 | assert list(root['john'].workout_ids()) == ['1', '2', '3'] |
---|
82 | assert root['john'].num_workouts == len(workouts) |
---|
83 | |
---|
84 | def test_favorite_sport(self, root): |
---|
85 | assert root['john'].favorite_sport is None |
---|
86 | # add a cycling workout |
---|
87 | workout = Workout( |
---|
88 | sport='cycling', |
---|
89 | start=datetime.now(timezone.utc), |
---|
90 | duration=timedelta(minutes=120), |
---|
91 | distance=66, |
---|
92 | ) |
---|
93 | root['john'].add_workout(workout) |
---|
94 | assert root['john'].favorite_sport == 'cycling' |
---|
95 | # add a running workout, both sports have same amount of workouts, |
---|
96 | # favorite is picked up reversed alphabetically |
---|
97 | workout = Workout( |
---|
98 | sport='running', |
---|
99 | start=datetime.now(timezone.utc), |
---|
100 | duration=timedelta(minutes=45), |
---|
101 | distance=5, |
---|
102 | ) |
---|
103 | root['john'].add_workout(workout) |
---|
104 | assert root['john'].favorite_sport == 'running' |
---|
105 | # add another cycling workout, now that is the favorite sport |
---|
106 | workout = Workout( |
---|
107 | sport='cycling', |
---|
108 | start=datetime.now(timezone.utc), |
---|
109 | duration=timedelta(minutes=60), |
---|
110 | distance=30, |
---|
111 | ) |
---|
112 | root['john'].add_workout(workout) |
---|
113 | assert root['john'].favorite_sport == 'cycling' |
---|
114 | |
---|
115 | def test_activity_sports(self, root): |
---|
116 | assert root['john'].activity_sports == [] |
---|
117 | workout = Workout( |
---|
118 | sport='cycling', |
---|
119 | start=datetime.now(timezone.utc), |
---|
120 | duration=timedelta(minutes=120), |
---|
121 | distance=66, |
---|
122 | ) |
---|
123 | root['john'].add_workout(workout) |
---|
124 | assert root['john'].activity_sports == ['cycling'] |
---|
125 | workout = Workout( |
---|
126 | sport='running', |
---|
127 | start=datetime.now(timezone.utc), |
---|
128 | duration=timedelta(minutes=45), |
---|
129 | distance=5, |
---|
130 | ) |
---|
131 | root['john'].add_workout(workout) |
---|
132 | assert root['john'].activity_sports == ['cycling', 'running'] |
---|
133 | |
---|
134 | def test_activity_years(self, root): |
---|
135 | assert root['john'].activity_years == [] |
---|
136 | workout = Workout( |
---|
137 | sport='cycling', |
---|
138 | start=datetime.now(timezone.utc), |
---|
139 | duration=timedelta(minutes=120), |
---|
140 | distance=66, |
---|
141 | ) |
---|
142 | root['john'].add_workout(workout) |
---|
143 | assert root['john'].activity_years == [datetime.now(timezone.utc).year] |
---|
144 | workout = Workout( |
---|
145 | sport='running', |
---|
146 | start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc), |
---|
147 | duration=timedelta(minutes=45), |
---|
148 | distance=5, |
---|
149 | ) |
---|
150 | root['john'].add_workout(workout) |
---|
151 | assert root['john'].activity_years == [ |
---|
152 | datetime.now(timezone.utc).year, |
---|
153 | 2018 |
---|
154 | ] |
---|
155 | |
---|
156 | def test_activity_months(self, root): |
---|
157 | # we have to pass a year parameter |
---|
158 | with pytest.raises(TypeError): |
---|
159 | root['john'].activity_months() |
---|
160 | now = datetime.now(timezone.utc) |
---|
161 | assert root['john'].activity_months(now.year) == [] |
---|
162 | workout = Workout( |
---|
163 | sport='cycling', |
---|
164 | start=datetime.now(timezone.utc), |
---|
165 | duration=timedelta(minutes=120), |
---|
166 | distance=66, |
---|
167 | ) |
---|
168 | root['john'].add_workout(workout) |
---|
169 | assert root['john'].activity_months(now.year) == [now.month] |
---|
170 | assert root['john'].activity_months(now.year-1) == [] |
---|
171 | assert root['john'].activity_months(now.year+1) == [] |
---|
172 | workout = Workout( |
---|
173 | sport='running', |
---|
174 | start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc), |
---|
175 | duration=timedelta(minutes=45), |
---|
176 | distance=5, |
---|
177 | ) |
---|
178 | root['john'].add_workout(workout) |
---|
179 | assert root['john'].activity_months(now.year) == [now.month] |
---|
180 | assert root['john'].activity_months(2018) == [11] |
---|
181 | assert root['john'].activity_months(now.year+1) == [] |
---|
182 | |
---|
183 | def test_activity_dates_tree(self, root): |
---|
184 | # first an empty test |
---|
185 | assert root['john'].activity_dates_tree == {} |
---|
186 | # now add a cycling workout in a given date (25/11/2018) |
---|
187 | workout = Workout( |
---|
188 | start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc), |
---|
189 | duration=timedelta(minutes=(60*4)), |
---|
190 | distance=115, |
---|
191 | sport='cycling') |
---|
192 | root['john'].add_workout(workout) |
---|
193 | assert root['john'].activity_dates_tree == { |
---|
194 | 2018: {11: {'cycling': 1}} |
---|
195 | } |
---|
196 | # add a running workout on the same date |
---|
197 | workout = Workout( |
---|
198 | start=datetime(2018, 11, 25, 16, 30, tzinfo=timezone.utc), |
---|
199 | duration=timedelta(minutes=60), |
---|
200 | distance=12, |
---|
201 | sport='running') |
---|
202 | root['john'].add_workout(workout) |
---|
203 | assert root['john'].activity_dates_tree == { |
---|
204 | 2018: {11: {'cycling': 1, 'running': 1}} |
---|
205 | } |
---|
206 | # add a swimming workout on a different date, same year |
---|
207 | workout = Workout( |
---|
208 | start=datetime(2018, 8, 15, 11, 30, tzinfo=timezone.utc), |
---|
209 | duration=timedelta(minutes=30), |
---|
210 | distance=2, |
---|
211 | sport='swimming') |
---|
212 | root['john'].add_workout(workout) |
---|
213 | assert root['john'].activity_dates_tree == { |
---|
214 | 2018: {8: {'swimming': 1}, |
---|
215 | 11: {'cycling': 1, 'running': 1}} |
---|
216 | } |
---|
217 | # now add some more cycling in a different year |
---|
218 | # add a swimming workout on a different date, same year |
---|
219 | workout = Workout( |
---|
220 | start=datetime(2017, 4, 15, 15, 00, tzinfo=timezone.utc), |
---|
221 | duration=timedelta(minutes=(60*3)), |
---|
222 | distance=78, |
---|
223 | sport='cycling') |
---|
224 | root['john'].add_workout(workout) |
---|
225 | assert root['john'].activity_dates_tree == { |
---|
226 | 2017: {4: {'cycling': 1}}, |
---|
227 | 2018: {8: {'swimming': 1}, |
---|
228 | 11: {'cycling': 1, 'running': 1}} |
---|
229 | } |
---|
230 | |
---|
231 | def test_stats(self, root): |
---|
232 | expected_no_stats = { |
---|
233 | 'workouts': 0, |
---|
234 | 'time': timedelta(seconds=0), |
---|
235 | 'distance': Decimal(0), |
---|
236 | 'elevation': Decimal(0), |
---|
237 | 'sports': {} |
---|
238 | } |
---|
239 | # no stats |
---|
240 | assert root['john'].stats() == expected_no_stats |
---|
241 | # add a cycling workout |
---|
242 | workout = Workout( |
---|
243 | start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc), |
---|
244 | duration=timedelta(minutes=(60*4)), |
---|
245 | distance=115, |
---|
246 | sport='cycling') |
---|
247 | root['john'].add_workout(workout) |
---|
248 | # asking for a different year, future |
---|
249 | assert root['john'].stats(2019) == expected_no_stats |
---|
250 | # asking for a different year, past |
---|
251 | assert root['john'].stats(2016) == expected_no_stats |
---|
252 | # asking fot the year the workout is in |
---|
253 | assert root['john'].stats(2018) == { |
---|
254 | 'workouts': 1, |
---|
255 | 'time': timedelta(minutes=(60*4)), |
---|
256 | 'distance': Decimal(115), |
---|
257 | 'elevation': Decimal(0), |
---|
258 | 'sports': { |
---|
259 | 'cycling': { |
---|
260 | 'workouts': 1, |
---|
261 | 'time': timedelta(minutes=(60*4)), |
---|
262 | 'distance': Decimal(115), |
---|
263 | 'elevation': Decimal(0), |
---|
264 | } |
---|
265 | } |
---|
266 | } |
---|
267 | # add a second cycling workout |
---|
268 | workout = Workout( |
---|
269 | start=datetime(2018, 11, 26, 10, 00, tzinfo=timezone.utc), |
---|
270 | duration=timedelta(minutes=(60*3)), |
---|
271 | distance=100, |
---|
272 | sport='cycling') |
---|
273 | root['john'].add_workout(workout) |
---|
274 | assert root['john'].stats(2018) == { |
---|
275 | 'workouts': 2, |
---|
276 | 'time': timedelta(minutes=(60*7)), |
---|
277 | 'distance': Decimal(215), |
---|
278 | 'elevation': Decimal(0), |
---|
279 | 'sports': { |
---|
280 | 'cycling': { |
---|
281 | 'workouts': 2, |
---|
282 | 'time': timedelta(minutes=(60*7)), |
---|
283 | 'distance': Decimal(215), |
---|
284 | 'elevation': Decimal(0), |
---|
285 | } |
---|
286 | } |
---|
287 | } |
---|
288 | # add a running workout |
---|
289 | workout = Workout( |
---|
290 | start=datetime(2018, 11, 26, 16, 00, tzinfo=timezone.utc), |
---|
291 | duration=timedelta(minutes=(60)), |
---|
292 | distance=10, |
---|
293 | sport='running') |
---|
294 | root['john'].add_workout(workout) |
---|
295 | assert root['john'].stats(2018) == { |
---|
296 | 'workouts': 3, |
---|
297 | 'time': timedelta(minutes=(60*8)), |
---|
298 | 'distance': Decimal(225), |
---|
299 | 'elevation': Decimal(0), |
---|
300 | 'sports': { |
---|
301 | 'cycling': { |
---|
302 | 'workouts': 2, |
---|
303 | 'time': timedelta(minutes=(60*7)), |
---|
304 | 'distance': Decimal(215), |
---|
305 | 'elevation': Decimal(0), |
---|
306 | }, |
---|
307 | 'running': { |
---|
308 | 'workouts': 1, |
---|
309 | 'time': timedelta(minutes=(60)), |
---|
310 | 'distance': Decimal(10), |
---|
311 | 'elevation': Decimal(0), |
---|
312 | } |
---|
313 | } |
---|
314 | } |
---|
315 | # ensure the stats for future/past years did not change after |
---|
316 | # adding those workouts |
---|
317 | assert root['john'].stats(2019) == expected_no_stats |
---|
318 | assert root['john'].stats(2016) == expected_no_stats |
---|
319 | |
---|
320 | def test_get_week_stats(self, root): |
---|
321 | expected_no_stats_per_day = { |
---|
322 | 'workouts': 0, |
---|
323 | 'time': timedelta(0), |
---|
324 | 'distance': Decimal(0), |
---|
325 | 'elevation': Decimal(0), |
---|
326 | 'sports': {} |
---|
327 | } |
---|
328 | |
---|
329 | expected_no_stats = {} |
---|
330 | for i in range(19, 26): |
---|
331 | day = datetime(2018, 11, i, 10, 00, tzinfo=timezone.utc) |
---|
332 | expected_no_stats[day] = expected_no_stats_per_day |
---|
333 | |
---|
334 | day = datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc) |
---|
335 | assert root['john'].get_week_stats(day) == expected_no_stats |
---|
336 | |
---|
337 | # add a cycling workout |
---|
338 | workout = Workout( |
---|
339 | start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc), |
---|
340 | duration=timedelta(minutes=(60*4)), |
---|
341 | distance=115, |
---|
342 | sport='cycling') |
---|
343 | root['john'].add_workout(workout) |
---|
344 | |
---|
345 | # check a week in the future |
---|
346 | day = datetime(2019, 11, 25, 10, 00, tzinfo=timezone.utc) |
---|
347 | week_stats = root['john'].get_week_stats(day) |
---|
348 | for day in week_stats: |
---|
349 | assert week_stats[day] == expected_no_stats_per_day |
---|
350 | |
---|
351 | # check a week in the past |
---|
352 | day = datetime(2017, 11, 25, 10, 00, tzinfo=timezone.utc) |
---|
353 | week_stats = root['john'].get_week_stats(day) |
---|
354 | for day in week_stats: |
---|
355 | assert week_stats[day] == expected_no_stats_per_day |
---|
356 | |
---|
357 | # Check the week where the workout is |
---|
358 | day = datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc) |
---|
359 | week_stats = root['john'].get_week_stats(day) |
---|
360 | for day in week_stats: |
---|
361 | if day.day == 25: |
---|
362 | # this is the day where we have a workout |
---|
363 | assert week_stats[day] == { |
---|
364 | 'workouts': 1, |
---|
365 | 'time': timedelta(minutes=(60*4)), |
---|
366 | 'distance': Decimal(115), |
---|
367 | 'elevation': Decimal(0), |
---|
368 | 'sports': { |
---|
369 | 'cycling': { |
---|
370 | 'workouts': 1, |
---|
371 | 'time': timedelta(minutes=(60*4)), |
---|
372 | 'distance': Decimal(115), |
---|
373 | 'elevation': Decimal(0) |
---|
374 | } |
---|
375 | } |
---|
376 | } |
---|
377 | else: |
---|
378 | # day without workout |
---|
379 | assert week_stats[day] == expected_no_stats_per_day |
---|
380 | |
---|
381 | # add a second cycling workout |
---|
382 | workout = Workout( |
---|
383 | start=datetime(2018, 11, 23, 10, 00, tzinfo=timezone.utc), |
---|
384 | duration=timedelta(minutes=(60*3)), |
---|
385 | distance=100, |
---|
386 | sport='cycling') |
---|
387 | root['john'].add_workout(workout) |
---|
388 | day = datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc) |
---|
389 | week_stats = root['john'].get_week_stats(day) |
---|
390 | for day in week_stats: |
---|
391 | if day.day == 25: |
---|
392 | # this is the day where we have a workout |
---|
393 | assert week_stats[day] == { |
---|
394 | 'workouts': 1, |
---|
395 | 'time': timedelta(minutes=(60*4)), |
---|
396 | 'distance': Decimal(115), |
---|
397 | 'elevation': Decimal(0), |
---|
398 | 'sports': { |
---|
399 | 'cycling': { |
---|
400 | 'workouts': 1, |
---|
401 | 'time': timedelta(minutes=(60*4)), |
---|
402 | 'distance': Decimal(115), |
---|
403 | 'elevation': Decimal(0) |
---|
404 | } |
---|
405 | } |
---|
406 | } |
---|
407 | elif day.day == 23: |
---|
408 | # this is the day where we have a workout |
---|
409 | assert week_stats[day] == { |
---|
410 | 'workouts': 1, |
---|
411 | 'time': timedelta(minutes=(60*3)), |
---|
412 | 'distance': Decimal(100), |
---|
413 | 'elevation': Decimal(0), |
---|
414 | 'sports': { |
---|
415 | 'cycling': { |
---|
416 | 'workouts': 1, |
---|
417 | 'time': timedelta(minutes=(60*3)), |
---|
418 | 'distance': Decimal(100), |
---|
419 | 'elevation': Decimal(0) |
---|
420 | } |
---|
421 | } |
---|
422 | } |
---|
423 | else: |
---|
424 | # day without workout |
---|
425 | assert week_stats[day] == expected_no_stats_per_day |
---|
426 | |
---|
427 | def test_week_stats(self, root): |
---|
428 | expected_no_stats_per_day = { |
---|
429 | 'workouts': 0, |
---|
430 | 'time': timedelta(0), |
---|
431 | 'distance': Decimal(0), |
---|
432 | 'elevation': Decimal(0), |
---|
433 | 'sports': {} |
---|
434 | } |
---|
435 | |
---|
436 | # no workouts for the current week (this tests is for coverage |
---|
437 | # purposes mostly, as the main logic is tested in test_get_week_stats) |
---|
438 | day = datetime.now(timezone.utc) |
---|
439 | week_stats = root['john'].get_week_stats(day) |
---|
440 | for day in week_stats: |
---|
441 | assert week_stats[day] == expected_no_stats_per_day |
---|
442 | |
---|
443 | def test_week_totals(self, root): |
---|
444 | # no data, empty totals |
---|
445 | assert root['john'].week_totals == { |
---|
446 | 'distance': Decimal(0), |
---|
447 | 'time': timedelta(0) |
---|
448 | } |
---|
449 | |
---|
450 | def test_yearly_stats(self, root): |
---|
451 | expected_no_stats_per_month = { |
---|
452 | 'workouts': 0, |
---|
453 | 'time': timedelta(0), |
---|
454 | 'distance': Decimal(0), |
---|
455 | 'elevation': Decimal(0), |
---|
456 | 'sports': {} |
---|
457 | } |
---|
458 | |
---|
459 | yearly_stats = root['john'].yearly_stats |
---|
460 | for month, stats in yearly_stats.items(): |
---|
461 | assert stats == expected_no_stats_per_month |
---|
462 | |
---|
463 | # add a cycling workout |
---|
464 | start_date = datetime.now(timezone.utc) |
---|
465 | workout = Workout( |
---|
466 | start=start_date, |
---|
467 | duration=timedelta(minutes=(60*4)), |
---|
468 | uphill=1200, |
---|
469 | distance=115, |
---|
470 | sport='cycling') |
---|
471 | root['john'].add_workout(workout) |
---|
472 | |
---|
473 | yearly_stats = root['john'].yearly_stats |
---|
474 | for month, stats in yearly_stats.items(): |
---|
475 | if month == (start_date.year, start_date.month): |
---|
476 | assert stats == { |
---|
477 | 'workouts': 1, |
---|
478 | 'time': timedelta(minutes=(60*4)), |
---|
479 | 'distance': Decimal(115), |
---|
480 | 'elevation': Decimal(1200), |
---|
481 | 'sports': { |
---|
482 | 'cycling': {'distance': Decimal(115), |
---|
483 | 'elevation': Decimal(1200), |
---|
484 | 'time': timedelta(minutes=(60*4)), |
---|
485 | 'workouts': 1} |
---|
486 | } |
---|
487 | } |
---|
488 | else: |
---|
489 | assert stats == expected_no_stats_per_month |
---|
490 | |
---|
491 | # add a second cycling workout |
---|
492 | workout = Workout( |
---|
493 | start=start_date, |
---|
494 | duration=timedelta(minutes=(30)), |
---|
495 | uphill=500, |
---|
496 | distance=20, |
---|
497 | sport='cycling') |
---|
498 | root['john'].add_workout(workout) |
---|
499 | |
---|
500 | yearly_stats = root['john'].yearly_stats |
---|
501 | for month, stats in yearly_stats.items(): |
---|
502 | if month == (start_date.year, start_date.month): |
---|
503 | assert stats == { |
---|
504 | 'workouts': 2, |
---|
505 | 'time': timedelta(minutes=((60*4)+30)), |
---|
506 | 'distance': Decimal(115+20), |
---|
507 | 'elevation': Decimal(1200+500), |
---|
508 | 'sports': { |
---|
509 | 'cycling': {'distance': Decimal(115+20), |
---|
510 | 'elevation': Decimal(1200+500), |
---|
511 | 'time': timedelta(minutes=((60*4)+30)), |
---|
512 | 'workouts': 2} |
---|
513 | } |
---|
514 | } |
---|
515 | else: |
---|
516 | assert stats == expected_no_stats_per_month |
---|
517 | |
---|
518 | # add a running workout |
---|
519 | workout = Workout( |
---|
520 | start=start_date, |
---|
521 | duration=timedelta(minutes=(60)), |
---|
522 | uphill=200, |
---|
523 | distance=5, |
---|
524 | sport='running') |
---|
525 | root['john'].add_workout(workout) |
---|
526 | |
---|
527 | yearly_stats = root['john'].yearly_stats |
---|
528 | for month, stats in yearly_stats.items(): |
---|
529 | if month == (start_date.year, start_date.month): |
---|
530 | assert stats == { |
---|
531 | 'workouts': 3, |
---|
532 | 'time': timedelta(minutes=((60*4)+30+60)), |
---|
533 | 'distance': Decimal(115+20+5), |
---|
534 | 'elevation': Decimal(1200+500+200), |
---|
535 | 'sports': { |
---|
536 | 'cycling': {'distance': Decimal(115+20), |
---|
537 | 'elevation': Decimal(1200+500), |
---|
538 | 'time': timedelta(minutes=((60*4)+30)), |
---|
539 | 'workouts': 2}, |
---|
540 | 'running': {'distance': Decimal(5), |
---|
541 | 'elevation': Decimal(200), |
---|
542 | 'time': timedelta(minutes=(60)), |
---|
543 | 'workouts': 1} |
---|
544 | } |
---|
545 | } |
---|
546 | else: |
---|
547 | assert stats == expected_no_stats_per_month |
---|
548 | |
---|
549 | def test_weekly_year_stats(self, root): |
---|
550 | expected_no_stats_per_week = { |
---|
551 | 'workouts': 0, |
---|
552 | 'time': timedelta(0), |
---|
553 | 'distance': Decimal(0), |
---|
554 | 'elevation': Decimal(0), |
---|
555 | 'sports': {} |
---|
556 | } |
---|
557 | |
---|
558 | weekly_year_stats = root['john'].weekly_year_stats |
---|
559 | for week, stats in weekly_year_stats.items(): |
---|
560 | assert stats == expected_no_stats_per_week |
---|
561 | |
---|
562 | # add a cycling workout |
---|
563 | start_date = datetime.now(timezone.utc) |
---|
564 | workout = Workout( |
---|
565 | start=start_date, |
---|
566 | duration=timedelta(minutes=(60*4)), |
---|
567 | uphill=1200, |
---|
568 | distance=115, |
---|
569 | sport='cycling') |
---|
570 | root['john'].add_workout(workout) |
---|
571 | |
---|
572 | weekly_year_stats = root['john'].weekly_year_stats |
---|
573 | workout_week = (start_date.year, start_date.month, |
---|
574 | start_date.isocalendar()[1]) |
---|
575 | for week, stats in weekly_year_stats.items(): |
---|
576 | if week[:3] == workout_week: |
---|
577 | assert stats == { |
---|
578 | 'workouts': 1, |
---|
579 | 'time': timedelta(minutes=(60*4)), |
---|
580 | 'distance': Decimal(115), |
---|
581 | 'elevation': Decimal(1200), |
---|
582 | 'sports': { |
---|
583 | 'cycling': {'distance': Decimal(115), |
---|
584 | 'elevation': Decimal(1200), |
---|
585 | 'time': timedelta(minutes=(60*4)), |
---|
586 | 'workouts': 1} |
---|
587 | } |
---|
588 | } |
---|
589 | else: |
---|
590 | assert stats == expected_no_stats_per_week |
---|
591 | |
---|
592 | # add a second cycling workout |
---|
593 | workout = Workout( |
---|
594 | start=start_date, |
---|
595 | duration=timedelta(minutes=(30)), |
---|
596 | uphill=500, |
---|
597 | distance=20, |
---|
598 | sport='cycling') |
---|
599 | root['john'].add_workout(workout) |
---|
600 | |
---|
601 | weekly_year_stats = root['john'].weekly_year_stats |
---|
602 | workout_week = (start_date.year, start_date.month, |
---|
603 | start_date.isocalendar()[1]) |
---|
604 | for week, stats in weekly_year_stats.items(): |
---|
605 | if week[:3] == workout_week: |
---|
606 | assert stats == { |
---|
607 | 'workouts': 2, |
---|
608 | 'time': timedelta(minutes=((60*4)+30)), |
---|
609 | 'distance': Decimal(115+20), |
---|
610 | 'elevation': Decimal(1200+500), |
---|
611 | 'sports': { |
---|
612 | 'cycling': {'distance': Decimal(115+20), |
---|
613 | 'elevation': Decimal(1200+500), |
---|
614 | 'time': timedelta(minutes=((60*4)+30)), |
---|
615 | 'workouts': 2} |
---|
616 | } |
---|
617 | } |
---|
618 | else: |
---|
619 | assert stats == expected_no_stats_per_week |
---|
620 | |
---|
621 | # add a running workout |
---|
622 | workout = Workout( |
---|
623 | start=start_date, |
---|
624 | duration=timedelta(minutes=(60)), |
---|
625 | uphill=200, |
---|
626 | distance=5, |
---|
627 | sport='running') |
---|
628 | root['john'].add_workout(workout) |
---|
629 | |
---|
630 | weekly_year_stats = root['john'].weekly_year_stats |
---|
631 | workout_week = (start_date.year, start_date.month, |
---|
632 | start_date.isocalendar()[1]) |
---|
633 | for week, stats in weekly_year_stats.items(): |
---|
634 | if week[:3] == workout_week: |
---|
635 | assert stats == { |
---|
636 | 'workouts': 3, |
---|
637 | 'time': timedelta(minutes=((60*4)+30+60)), |
---|
638 | 'distance': Decimal(115+20+5), |
---|
639 | 'elevation': Decimal(1200+500+200), |
---|
640 | 'sports': { |
---|
641 | 'cycling': {'distance': Decimal(115+20), |
---|
642 | 'elevation': Decimal(1200+500), |
---|
643 | 'time': timedelta(minutes=((60*4)+30)), |
---|
644 | 'workouts': 2}, |
---|
645 | 'running': {'distance': Decimal(5), |
---|
646 | 'elevation': Decimal(200), |
---|
647 | 'time': timedelta(minutes=(60)), |
---|
648 | 'workouts': 1} |
---|
649 | } |
---|
650 | } |
---|
651 | else: |
---|
652 | assert stats == expected_no_stats_per_week |
---|
653 | |
---|
654 | def test_sport_totals(self, root): |
---|
655 | # user has no workouts, so no totals |
---|
656 | assert root['john'].sport_totals() == { |
---|
657 | 'workouts': 0, |
---|
658 | 'time': timedelta(0), |
---|
659 | 'distance': Decimal(0), |
---|
660 | 'elevation': Decimal(0), |
---|
661 | } |
---|
662 | # add a cycling workout happening now |
---|
663 | workout = Workout( |
---|
664 | sport='cycling', |
---|
665 | start=datetime.now(timezone.utc), |
---|
666 | duration=timedelta(minutes=120), |
---|
667 | distance=66, |
---|
668 | ) |
---|
669 | root['john'].add_workout(workout) |
---|
670 | # only one workout, one sport, so the default will show totals |
---|
671 | # for that sport |
---|
672 | assert root['john'].sport_totals() == { |
---|
673 | 'workouts': 1, |
---|
674 | 'time': timedelta(minutes=120), |
---|
675 | 'distance': Decimal(66), |
---|
676 | 'elevation': Decimal(0), |
---|
677 | } |
---|
678 | # Add a running workout |
---|
679 | workout = Workout( |
---|
680 | sport='running', |
---|
681 | start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc), |
---|
682 | duration=timedelta(minutes=45), |
---|
683 | distance=5, |
---|
684 | ) |
---|
685 | root['john'].add_workout(workout) |
---|
686 | # the favorite sport is running now |
---|
687 | assert root['john'].sport_totals() == { |
---|
688 | 'workouts': 1, |
---|
689 | 'time': timedelta(minutes=45), |
---|
690 | 'distance': Decimal(5), |
---|
691 | 'elevation': Decimal(0), |
---|
692 | } |
---|
693 | # but we can get the totals for cycling too |
---|
694 | assert root['john'].sport_totals('cycling') == { |
---|
695 | 'workouts': 1, |
---|
696 | 'time': timedelta(minutes=120), |
---|
697 | 'distance': Decimal(66), |
---|
698 | 'elevation': Decimal(0), |
---|
699 | } |
---|
700 | # adding a new cycling workout, in a different year |
---|
701 | workout = Workout( |
---|
702 | sport='cycling', |
---|
703 | start=datetime(2017, 11, 25, 10, 00, tzinfo=timezone.utc), |
---|
704 | duration=timedelta(minutes=60), |
---|
705 | distance=32, |
---|
706 | ) |
---|
707 | root['john'].add_workout(workout) |
---|
708 | # now cycling is the favorite sport |
---|
709 | assert root['john'].sport_totals() == { |
---|
710 | 'workouts': 2, |
---|
711 | 'time': timedelta(minutes=180), |
---|
712 | 'distance': Decimal(98), |
---|
713 | 'elevation': Decimal(0), |
---|
714 | } |
---|
715 | # but we can get running stats too |
---|
716 | assert root['john'].sport_totals('running') == { |
---|
717 | 'workouts': 1, |
---|
718 | 'time': timedelta(minutes=45), |
---|
719 | 'distance': Decimal(5), |
---|
720 | 'elevation': Decimal(0), |
---|
721 | } |
---|
722 | # there are no running activities for 2016 |
---|
723 | assert root['john'].sport_totals('running', 2016) == { |
---|
724 | 'workouts': 0, |
---|
725 | 'time': timedelta(0), |
---|
726 | 'distance': Decimal(0), |
---|
727 | 'elevation': Decimal(0), |
---|
728 | } |
---|
729 | # and not activities for cycling in 2016 neither |
---|
730 | assert root['john'].sport_totals('cycling', 2016) == { |
---|
731 | 'workouts': 0, |
---|
732 | 'time': timedelta(0), |
---|
733 | 'distance': Decimal(0), |
---|
734 | 'elevation': Decimal(0), |
---|
735 | } |
---|
736 | # and we can get the separate totals for cycling in different years |
---|
737 | year = datetime.now(timezone.utc).year |
---|
738 | assert root['john'].sport_totals('cycling', year) == { |
---|
739 | 'workouts': 1, |
---|
740 | 'time': timedelta(minutes=120), |
---|
741 | 'distance': Decimal(66), |
---|
742 | 'elevation': Decimal(0), |
---|
743 | } |
---|
744 | assert root['john'].sport_totals('cycling', 2017) == { |
---|
745 | 'workouts': 1, |
---|
746 | 'time': timedelta(minutes=60), |
---|
747 | 'distance': Decimal(32), |
---|
748 | 'elevation': Decimal(0), |
---|
749 | } |
---|