source: OpenWorkouts-current/ow/tests/models/test_user.py @ 778d53d

current
Last change on this file since 778d53d was 778d53d, checked in by Borja Lopez <borja@…>, 5 years ago

(#7) Show per-sport stats in the profile page:

  • Show a dropdown list of sports for which the user has activities. By default we choose the sport with most activities.
  • Show a dropdown list of years for which the user has activities. By default we show stats for the current year. If the user picks up a different year, we show the totals (distance, time, elevation, number of workouts) for that year.
  • Show the totals of all time for the chosen sport
  • Property mode set to 100644
File size: 27.7 KB
Line 
1from decimal import Decimal
2from datetime import datetime, timedelta, timezone
3
4import pytest
5from pyramid.security import Allow, Everyone, Deny, ALL_PERMISSIONS
6
7from ow.models.root import OpenWorkouts
8from ow.models.workout import Workout
9from ow.models.user import User
10
11
12class 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        }
Note: See TracBrowser for help on using the repository browser.