Workout With Friends Stay fit with a little motivation

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Properties
workout.py
Go to the documentation of this file.
1 from __future__ import unicode_literals
2 from sqlalchemy.ext.declarative import declared_attr
3 from sqlalchemy.orm import backref, relationship
4 from sqlalchemy.schema import Column, ForeignKey
5 from sqlalchemy.types import DateTime, Integer, Numeric, SmallInteger
6 from wowf.lib.utils import current_timestamp
7 from wowf.models.lookup_tables import ChallengeType
8 from wowf.models.meta import Base
9
10
11 class Workout(Base):
12
13 __tablename__ = 'workouts'
14 __mapper_args__ = {'polymorphic_on': 'challenge_type_id', 'with_polymorphic': '*'}
15 id = Column(Integer(unsigned=True), primary_key=True)
16 user_id = Column(Integer(unsigned=True), ForeignKey('users.id'), nullable=False)
17 challenge_id = Column(Integer(unsigned=True), ForeignKey('challenges.id'), nullable=False)
18 challenge_type_id = Column(
19 Integer(unsigned=True), ForeignKey('challenge_types.id', ondelete='cascade'),
20 nullable=False)
21 points = Column(SmallInteger(unsigned=True), nullable=False)
22 created_at = Column(DateTime, nullable=False, default=current_timestamp)
23
24 user = relationship(
25 'User', lazy='joined', backref=backref('workouts', lazy='dynamic'))
26 challenge = relationship(
27 'Challenge', lazy='joined', backref=backref('workouts', lazy='dynamic'))
28
29
30 class _Workout(object):
31
32 @declared_attr
33 def id(cls):
34 return Column(
35 Integer(unsigned=True), ForeignKey('workouts.id', ondelete='cascade'),
36 primary_key=True)
37
38
39 ##
40 #
41 # Base class for all workouts which require the use of a heart rate device.
42 #
43 class DeviceWorkout(_Workout):
44
45 @classmethod
46 def create(cls, user, challenge, samples):
47 return super(DeviceWorkout, cls).create(
48 user=user, challenge=challenge, samples=samples)
49
50
51 ##
52 #
53 # Base class for all weight related workouts.
54 #
55 class WeightWorkout(_Workout):
56
57 repetitions = Column(SmallInteger(unsigned=True), nullable=False)
58
59 @classmethod
60 def create(cls, user, challenge, repetitions):
61 return super(WeightWorkout, cls).create(
62 user=user, challenge=challenge, repetitions=repetitions)
63
64
65 class SpeedWorkout(DeviceWorkout, Workout):
66
67 __tablename__ = 'speed_workouts'
68 __mapper_args__ = {'polymorphic_identity': ChallengeType.lookup_data.index('speed') + 1}
69 speed = Column(Numeric(4, 2), nullable=False, doc='speed in meters per second')
70
71 def __init__(self, user, challenge, samples):
72 self.user = user
73 self.challenge = challenge
74 total_distance = samples['distance'][-1]
75 if not total_distance >= challenge.distance:
76 # Did not run the full distance
77 self.points = self.speed = 0
78 return
79 distance_samples = zip(samples['timestamp'], samples['distance'])
80 distance = time = 0
81 for t, d in distance_samples:
82 if d >= challenge.distance:
83 time = t
84 distance = d
85 time = time / 1000.00 # Convert to seconds
86 self.speed = float(distance / time)
87 self.points = self.speed * 100
88 avg_speed = user.get_average_speed(challenge.distance)
89 if avg_speed:
90 delta = 100 * float(self.speed - avg_speed) / avg_speed
91 if delta >= 10:
92 # 10% increase
93 self.points *= 1.1
94 elif delta <= 10:
95 # 10% decline
96 self.points *= 0.9
97
98
99 class EnduranceWorkout(DeviceWorkout, Workout):
100
101 __tablename__ = 'endurance_workouts'
102 __mapper_args__ = {'polymorphic_identity': ChallengeType.lookup_data.index('endurance') + 1}
103 heart_rate = Column(SmallInteger(unsigned=True), nullable=False)
104 calories_burned = Column(SmallInteger(unsigned=True), nullable=False)
105
106 def __init__(self, user, challenge, samples):
107 self.user = user
108 self.challenge = challenge
109 total_duration = samples['timestamp'][-1] / 1000.0
110 total_duration = total_duration / 60.0
111 if not total_duration >= challenge.duration:
112 # Did not run the full duration
113 self.points = self.heart_rate = self.calories_burned = 0
114 return
115 duration = challenge.duration * 60.0 * 1000.0 # Convert to microseconds
116 heart_rate_samples = zip(samples['timestamp'], samples['heart_rate'])
117 calories_burned_samples = zip(samples['timestamp'], samples['calories_burned'])
118 heart_rate = []
119 calories_burned = 0
120 for t, h in heart_rate_samples:
121 heart_rate.append(h)
122 if t >= duration:
123 break
124 for t, c in calories_burned_samples:
125 if t >= duration:
126 calories_burned = c
127 self.heart_rate = float(sum(heart_rate)) / len(heart_rate)
128 self.calories_burned = calories_burned
129 self.points = self.heart_rate * 10 + self.calories_burned
130 avg_heart_rate = user.get_average_heart_rate(challenge.duration)
131 avg_calories_burned = user.get_average_calories_burned(challenge.duration)
132 if avg_heart_rate:
133 delta = 100 * float(self.heart_rate - avg_heart_rate) / avg_heart_rate
134 if delta >= 10:
135 # 10% increase
136 self.points *= 1.05
137 elif delta <= 10:
138 # 10% decline
139 self.points *= 0.95
140 if avg_calories_burned:
141 delta = 100 * float(self.calories_burned - avg_calories_burned) / avg_calories_burned
142 if delta >= 10:
143 # 10% increase
144 self.points *= 1.05
145 elif delta <= 10:
146 # 10% decline
147 self.points *= 0.95
148
149
150 class BenchPressWorkout(WeightWorkout, Workout):
151
152 __tablename__ = 'bench_press_workouts'
153 __mapper_args__ = {'polymorphic_identity': ChallengeType.lookup_data.index('bench_press') + 1}
154
155 def __init__(self, user, challenge, repetitions):
156 self.user = user
157 self.challenge = challenge
158 self.repetitions = repetitions
159 self.points = challenge.calculate_user_weight(user) * repetitions
160 avg_repetitions = user.get_average_bench_press_repetitions(challenge.percentage)
161 if avg_repetitions:
162 delta = 100 * float(self.repetitions - avg_repetitions) / avg_repetitions
163 if delta >= 10:
164 # 10% increase
165 self.points *= 1.1
166 elif delta <= 10:
167 # 10% decline
168 self.points *= 0.9
169 # Scale down for bench press
170 self.points *= 0.9
171
172
173 class SquatWorkout(WeightWorkout, Workout):
174
175 __tablename__ = 'squat_workouts'
176 __mapper_args__ = {'polymorphic_identity': ChallengeType.lookup_data.index('squat') + 1}
177
178 def __init__(self, user, challenge, repetitions):
179 self.user = user
180 self.challenge = challenge
181 self.repetitions = repetitions
182 self.points = challenge.calculate_user_weight(user) * repetitions
183 avg_repetitions = user.get_average_squat_repetitions(challenge.percentage)
184 if avg_repetitions:
185 delta = 100 * float(repetitions - avg_repetitions) / avg_repetitions
186 if delta >= 10:
187 # 10% increase
188 self.points *= 1.1
189 elif delta <= 10:
190 # 10% decline
191 self.points *= 0.9
192 # Scale down for squat
193 self.points *= 0.9
194