Workout With Friends
Stay fit with a little motivation
 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 #
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 #
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 
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 
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 
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 
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