source: OpenWorkouts-current/ow/tests/models/test_user.py @ 2f8a48f

currentfeature/docs
Last change on this file since 2f8a48f was 2f8a48f, checked in by borja <borja@…>, 5 years ago

(#7) Added several methods to the User model to gather some stats (yearly,

monthly, weekly).

Added two new utilities:

  • timedelta_to_hms (so we can print timedelta objects properly in template code)
  • get_week_days (returns a list of datetime objects for the days in the same week as a given day)

Added a template_helpers module, containing code that affects template
rendering.

Added timedelta_to_hms as a global to the default template rendering context

Refactored some code in the Workout model so it uses timedelta_to_hms instead
of running the same code twice.

  • Property mode set to 100644
File size: 12.4 KB
Line 
1from decimal import Decimal
2from datetime import datetime, timedelta, timezone
3
4import pytest
5from pyramid.security import Allow
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 = [(Allow, uid, 'edit'), (Allow, uid, 'view')]
36        assert root['john'].__acl__() == permissions
37
38    def test__str__(self, root):
39        email = root['john'].email
40        uid = str(root['john'].uid)
41        assert root['john'].__str__() == u'User: ' + email + ' (' + uid + ')'
42
43    def test_fullname(self, root):
44        assert root['john'].fullname == 'John Doe'
45
46    def test_password_is_encrypted(self, root):
47        assert root['john'].password != 's3cr3t'
48
49    def test_check_wrong_password(self, root):
50        assert not root['john'].check_password('badpass')
51
52    def test_check_good_password(self, root):
53        assert root['john'].check_password('s3cr3t')
54
55    def test_add_workout(self, root, workouts):
56        # First add all workouts at once
57        for workout in workouts:
58            root['john'].add_workout(workout)
59        # Then check they are there, in the correct position/number
60        for workout in workouts:
61            index = workouts.index(workout) + 1
62            assert root['john'][str(index)] == workout
63
64    def test_workouts(self, root, workouts):
65        # First add all workouts at once
66        for workout in workouts:
67            root['john'].add_workout(workout)
68        # workouts() will return the workouts sorted from newest to oldest
69        workouts.reverse()
70        assert list(root['john'].workouts()) == workouts
71        assert list(root['john'].workout_ids()) == ['1', '2', '3']
72        assert root['john'].num_workouts == len(workouts)
73
74    def test_activity_dates_tree(self, root):
75        # first an empty test
76        assert root['john'].activity_dates_tree == {}
77        # now add a cycling workout in a given date (25/11/2018)
78        workout = Workout(
79            start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc),
80            duration=timedelta(minutes=(60*4)),
81            distance=115,
82            sport='cycling')
83        root['john'].add_workout(workout)
84        assert root['john'].activity_dates_tree == {
85            2018: {11: {'cycling': 1}}
86        }
87        # add a running workout on the same date
88        workout = Workout(
89            start=datetime(2018, 11, 25, 16, 30, tzinfo=timezone.utc),
90            duration=timedelta(minutes=60),
91            distance=12,
92            sport='running')
93        root['john'].add_workout(workout)
94        assert root['john'].activity_dates_tree == {
95            2018: {11: {'cycling': 1, 'running': 1}}
96        }
97        # add a swimming workout on a different date, same year
98        workout = Workout(
99            start=datetime(2018, 8, 15, 11, 30, tzinfo=timezone.utc),
100            duration=timedelta(minutes=30),
101            distance=2,
102            sport='swimming')
103        root['john'].add_workout(workout)
104        assert root['john'].activity_dates_tree == {
105            2018: {8: {'swimming': 1},
106                   11: {'cycling': 1, 'running': 1}}
107        }
108        # now add some more cycling in a different year
109        # add a swimming workout on a different date, same year
110        workout = Workout(
111            start=datetime(2017, 4, 15, 15, 00, tzinfo=timezone.utc),
112            duration=timedelta(minutes=(60*3)),
113            distance=78,
114            sport='cycling')
115        root['john'].add_workout(workout)
116        assert root['john'].activity_dates_tree == {
117            2017: {4: {'cycling': 1}},
118            2018: {8: {'swimming': 1},
119                   11: {'cycling': 1, 'running': 1}}
120        }
121
122    def test_stats(self, root):
123        expected_no_stats = {
124            'workouts': 0,
125            'time': timedelta(seconds=0),
126            'distance': Decimal(0),
127            'elevation': Decimal(0),
128            'sports': {}
129        }
130        # no stats
131        assert root['john'].stats() == expected_no_stats
132        # add a cycling workout
133        workout = Workout(
134            start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc),
135            duration=timedelta(minutes=(60*4)),
136            distance=115,
137            sport='cycling')
138        root['john'].add_workout(workout)
139        # asking for a different year, future
140        assert root['john'].stats(2019) == expected_no_stats
141        # asking for a different year, past
142        assert root['john'].stats(2016) == expected_no_stats
143        # asking fot the year the workout is in
144        assert root['john'].stats(2018) == {
145            'workouts': 1,
146            'time': timedelta(minutes=(60*4)),
147            'distance': Decimal(115),
148            'elevation': Decimal(0),
149            'sports': {
150                'cycling': {
151                    'workouts': 1,
152                    'time': timedelta(minutes=(60*4)),
153                    'distance': Decimal(115),
154                    'elevation': Decimal(0),
155                }
156            }
157        }
158        # add a second cycling workout
159        workout = Workout(
160            start=datetime(2018, 11, 26, 10, 00, tzinfo=timezone.utc),
161            duration=timedelta(minutes=(60*3)),
162            distance=100,
163            sport='cycling')
164        root['john'].add_workout(workout)
165        assert root['john'].stats(2018) == {
166            'workouts': 2,
167            'time': timedelta(minutes=(60*7)),
168            'distance': Decimal(215),
169            'elevation': Decimal(0),
170            'sports': {
171                'cycling': {
172                    'workouts': 2,
173                    'time': timedelta(minutes=(60*7)),
174                    'distance': Decimal(215),
175                    'elevation': Decimal(0),
176                }
177            }
178        }
179        # add a running workout
180        workout = Workout(
181            start=datetime(2018, 11, 26, 16, 00, tzinfo=timezone.utc),
182            duration=timedelta(minutes=(60)),
183            distance=10,
184            sport='running')
185        root['john'].add_workout(workout)
186        assert root['john'].stats(2018) == {
187            'workouts': 3,
188            'time': timedelta(minutes=(60*8)),
189            'distance': Decimal(225),
190            'elevation': Decimal(0),
191            'sports': {
192                'cycling': {
193                    'workouts': 2,
194                    'time': timedelta(minutes=(60*7)),
195                    'distance': Decimal(215),
196                    'elevation': Decimal(0),
197                },
198                'running': {
199                    'workouts': 1,
200                    'time': timedelta(minutes=(60)),
201                    'distance': Decimal(10),
202                    'elevation': Decimal(0),
203                }
204            }
205        }
206        # ensure the stats for future/past years did not change after
207        # adding those workouts
208        assert root['john'].stats(2019) == expected_no_stats
209        assert root['john'].stats(2016) == expected_no_stats
210
211    def test_get_week_stats(self, root):
212        expected_no_stats_per_day = {
213            'workouts': 0,
214            'time': timedelta(0),
215            'distance': Decimal(0),
216            'elevation': Decimal(0),
217            'sports': {}
218        }
219
220        expected_no_stats = {}
221        for i in range(19, 26):
222            day = datetime(2018, 11, i, 10, 00, tzinfo=timezone.utc)
223            expected_no_stats[day] = expected_no_stats_per_day
224
225        day = datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc)
226        assert root['john'].get_week_stats(day) == expected_no_stats
227
228        # add a cycling workout
229        workout = Workout(
230            start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc),
231            duration=timedelta(minutes=(60*4)),
232            distance=115,
233            sport='cycling')
234        root['john'].add_workout(workout)
235
236        # check a week in the future
237        day = datetime(2019, 11, 25, 10, 00, tzinfo=timezone.utc)
238        week_stats = root['john'].get_week_stats(day)
239        for day in week_stats:
240            assert week_stats[day] == expected_no_stats_per_day
241
242        # check a week in the past
243        day = datetime(2017, 11, 25, 10, 00, tzinfo=timezone.utc)
244        week_stats = root['john'].get_week_stats(day)
245        for day in week_stats:
246            assert week_stats[day] == expected_no_stats_per_day
247
248        # Check the week where the workout is
249        day = datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc)
250        week_stats = root['john'].get_week_stats(day)
251        for day in week_stats:
252            if day.day == 25:
253                # this is the day where we have a workout
254                assert week_stats[day] == {
255                    'workouts': 1,
256                    'time': timedelta(minutes=(60*4)),
257                    'distance': Decimal(115),
258                    'elevation': Decimal(0),
259                    'sports': {
260                        'cycling': {
261                            'workouts': 1,
262                            'time': timedelta(minutes=(60*4)),
263                            'distance': Decimal(115),
264                            'elevation': Decimal(0)
265                        }
266                    }
267                }
268            else:
269                # day without workout
270                assert week_stats[day] == expected_no_stats_per_day
271
272        # add a second cycling workout
273        workout = Workout(
274            start=datetime(2018, 11, 23, 10, 00, tzinfo=timezone.utc),
275            duration=timedelta(minutes=(60*3)),
276            distance=100,
277            sport='cycling')
278        root['john'].add_workout(workout)
279        day = datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc)
280        week_stats = root['john'].get_week_stats(day)
281        for day in week_stats:
282            if day.day == 25:
283                # this is the day where we have a workout
284                assert week_stats[day] == {
285                    'workouts': 1,
286                    'time': timedelta(minutes=(60*4)),
287                    'distance': Decimal(115),
288                    'elevation': Decimal(0),
289                    'sports': {
290                        'cycling': {
291                            'workouts': 1,
292                            'time': timedelta(minutes=(60*4)),
293                            'distance': Decimal(115),
294                            'elevation': Decimal(0)
295                        }
296                    }
297                }
298            elif day.day == 23:
299                # this is the day where we have a workout
300                assert week_stats[day] == {
301                    'workouts': 1,
302                    'time': timedelta(minutes=(60*3)),
303                    'distance': Decimal(100),
304                    'elevation': Decimal(0),
305                    'sports': {
306                        'cycling': {
307                            'workouts': 1,
308                            'time': timedelta(minutes=(60*3)),
309                            'distance': Decimal(100),
310                            'elevation': Decimal(0)
311                        }
312                    }
313                }
314            else:
315                # day without workout
316                assert week_stats[day] == expected_no_stats_per_day
317
318    def test_week_stats(self, root):
319        expected_no_stats_per_day = {
320            'workouts': 0,
321            'time': timedelta(0),
322            'distance': Decimal(0),
323            'elevation': Decimal(0),
324            'sports': {}
325        }
326
327        # no workouts for the current week (this tests is for coverage
328        # purposes mostly, as the main logic is tested in test_get_week_stats)
329        day = datetime.now(timezone.utc)
330        week_stats = root['john'].get_week_stats(day)
331        for day in week_stats:
332            assert week_stats[day] == expected_no_stats_per_day
333
334    def test_week_totals(self, root):
335        # no data, empty totals
336        assert root['john'].week_totals == {
337            'distance': Decimal(0),
338            'time': timedelta(0)
339        }
Note: See TracBrowser for help on using the repository browser.