class WorkoutSession(models.Model): """ Model for a workout session """ # Note: values hardcoded in manager.helpers.WorkoutCalendar IMPRESSION_BAD = '1' IMPRESSION_NEUTRAL = '2' IMPRESSION_GOOD = '3' IMPRESSION = ( (IMPRESSION_BAD, _("Bad")), (IMPRESSION_NEUTRAL, _('Neutral')), (IMPRESSION_GOOD, _('Good')), ) user = models.ForeignKey( User, verbose_name=_('User'), on_delete=models.CASCADE, ) """ The user the workout session belongs to See note in weight.models.WeightEntry about why this is not editable=False """ workout = models.ForeignKey( Workout, verbose_name=_('Workout'), on_delete=models.CASCADE, ) """ The workout the session belongs to """ date = Html5DateField(verbose_name=_('Date')) """ The date the workout session was performed """ notes = models.TextField( verbose_name=_('Notes'), null=True, blank=True, help_text=_('Any notes you might want to save about this workout ' 'session.') ) """ User notes about the workout """ impression = models.CharField( verbose_name=_('General impression'), max_length=2, choices=IMPRESSION, default=IMPRESSION_NEUTRAL, help_text=_( 'Your impression about this workout session. ' 'Did you exercise as well as you could?' ) ) """ The user's general impression of workout """ time_start = models.TimeField(verbose_name=_('Start time'), blank=True, null=True) """ Time the workout session started """ time_end = models.TimeField(verbose_name=_('Finish time'), blank=True, null=True) """ Time the workout session ended """ def __str__(self): """ Return a more human-readable representation """ return "{0} - {1}".format(self.workout, self.date) class Meta: """ Set other properties """ ordering = [ "date", ] unique_together = ("date", "user") def clean(self): """ Perform some additional validations """ if (not self.time_end and self.time_start) or (self.time_end and not self.time_start): raise ValidationError(_("If you enter a time, you must enter both start and end time.")) if self.time_end and self.time_start and self.time_start > self.time_end: raise ValidationError(_("The start time cannot be after the end time.")) def get_owner_object(self): """ Returns the object that has owner information """ return self def save(self, *args, **kwargs): """ Reset cache """ reset_workout_log(self.user_id, self.date.year, self.date.month) super(WorkoutSession, self).save(*args, **kwargs) def delete(self, *args, **kwargs): """ Reset cache """ reset_workout_log(self.user_id, self.date.year, self.date.month) super(WorkoutSession, self).delete(*args, **kwargs)
class WorkoutLog(models.Model): """ A log entry for an exercise """ user = models.ForeignKey(User, verbose_name=_('User'), editable=False, on_delete=models.CASCADE) exercise = models.ForeignKey(Exercise, verbose_name=_('Exercise'), on_delete=models.CASCADE) workout = models.ForeignKey(Workout, verbose_name=_('Workout'), on_delete=models.CASCADE) repetition_unit = models.ForeignKey(RepetitionUnit, verbose_name=_('Unit'), default=1, on_delete=models.CASCADE) """ The unit of the log. This can be e.g. a repetition, a minute, etc. """ reps = models.IntegerField(verbose_name=_('Repetitions'), validators=[MinValueValidator(0)]) """ Amount of repetitions, minutes, etc. Note that since adding the unit field, the name is no longer correct, but is kept for compatibility reasons (specially for the REST API). """ weight = models.DecimalField(decimal_places=2, max_digits=5, verbose_name=_('Weight'), validators=[MinValueValidator(0)]) weight_unit = models.ForeignKey(WeightUnit, verbose_name=_('Unit'), default=1, on_delete=models.CASCADE) """ The weight unit of the log. This can be e.g. kg, lb, km/h, etc. """ date = Html5DateField(verbose_name=_('Date')) rir = models.DecimalField(verbose_name=_('RiR'), decimal_places=1, max_digits=3, blank=True, null=True, choices=RIR_OPTIONS) """ Reps in reserve, RiR. The amount of reps that could realistically still be done in the set. """ # Metaclass to set some other properties class Meta: ordering = ["date", "reps"] def __str__(self): """ Return a more human-readable representation """ return "Log entry: {0} - {1} kg on {2}".format(self.reps, self.weight, self.date) def get_owner_object(self): """ Returns the object that has owner information """ return self def get_workout_session(self, date=None): """ Returns the corresponding workout session :return the WorkoutSession object or None if nothing was found """ if not date: date = self.date try: return WorkoutSession.objects.filter(user=self.user).get(date=date) except WorkoutSession.DoesNotExist: return None def save(self, *args, **kwargs): """ Reset cache """ reset_workout_log(self.user_id, self.date.year, self.date.month, self.date.day) # If the user selected "Until Failure", do only 1 "repetition", # everythin else doesn't make sense. if self.repetition_unit == 2: self.reps = 1 super(WorkoutLog, self).save(*args, **kwargs) def delete(self, *args, **kwargs): """ Reset cache """ reset_workout_log(self.user_id, self.date.year, self.date.month, self.date.day) super(WorkoutLog, self).delete(*args, **kwargs)
class WorkoutLog(models.Model): ''' A log entry for an exercise ''' user = models.ForeignKey(User, verbose_name=_('User'), editable=False) exercise = models.ForeignKey(Exercise, verbose_name=_('Exercise')) workout = models.ForeignKey(Workout, verbose_name=_('Workout')) reps = models.IntegerField(verbose_name=_('Repetitions'), validators=[MinValueValidator(0)]) weight = models.DecimalField(decimal_places=2, max_digits=5, verbose_name=_('Weight'), validators=[MinValueValidator(0)]) date = Html5DateField(verbose_name=_('Date')) # Metaclass to set some other properties class Meta: ordering = ["date", "reps"] def __str__(self): ''' Return a more human-readable representation ''' return u"Log entry: {0} - {1} kg on {2}".format( self.reps, self.weight, self.date) def get_owner_object(self): ''' Returns the object that has owner information ''' return self def get_workout_session(self, date=None): ''' Returns the corresponding workout session :return the WorkoutSession object or None if nothing was found ''' if not date: date = self.date try: return WorkoutSession.objects.filter(user=self.user).get(date=date) except WorkoutSession.DoesNotExist: return None def save(self, *args, **kwargs): ''' Reset cache ''' reset_workout_log(self.user_id, self.date.year, self.date.month, self.date.day) super(WorkoutLog, self).save(*args, **kwargs) def delete(self, *args, **kwargs): ''' Reset cache ''' reset_workout_log(self.user_id, self.date.year, self.date.month, self.date.day) super(WorkoutLog, self).delete(*args, **kwargs)
class Schedule(models.Model): """ Model for a workout schedule. A schedule is a collection of workous that are done for a certain time. E.g. workouts A, B, C, A, B, C, and so on. """ objects = ScheduleManager() """Custom manager""" user = models.ForeignKey(User, verbose_name=_('User'), editable=False, on_delete=models.CASCADE) """ The user this schedule belongs to. This could be accessed through a step that points to a workout, that points to a user, but this is more straight forward and performant """ name = models.CharField(verbose_name=_('Name'), max_length=100, help_text=_("Name or short description of the schedule. " "For example 'Program XYZ'.")) """Name or short description of the schedule.""" start_date = Html5DateField(verbose_name=_('Start date'), default=datetime.date.today) """The start date of this schedule""" is_active = models.BooleanField(verbose_name=_('Schedule active'), default=True, help_text=_("Tick the box if you want to mark this schedule " "as your active one (will be shown e.g. on your " "dashboard). All other schedules will then be " "marked as inactive")) """A flag indicating whether the schedule is active (needed for dashboard)""" is_loop = models.BooleanField(verbose_name=_('Is a loop'), default=False, help_text=_("Tick the box if you want to repeat the schedules " "in a loop (i.e. A, B, C, A, B, C, and so on)")) """A flag indicating whether the schedule should act as a loop""" def __str__(self): """ Return a more human-readable representation """ return self.name def get_absolute_url(self): return reverse('manager:schedule:view', kwargs={'pk': self.id}) def get_owner_object(self): """ Returns the object that has owner information """ return self def save(self, *args, **kwargs): """ Only one schedule can be marked as active at a time """ if self.is_active: Schedule.objects.filter(user=self.user).update(is_active=False) self.is_active = True super(Schedule, self).save(*args, **kwargs) def get_current_scheduled_workout(self): """ Returns the currently active schedule step for a user """ steps = self.schedulestep_set.all() start_date = self.start_date found = False if not steps: return False while not found: for step in steps: current_limit = start_date + datetime.timedelta(weeks=step.duration) if current_limit >= datetime.date.today(): found = True return step start_date = current_limit # If it's not a loop, there's no workout that matches, return if not self.is_loop: return False def get_end_date(self): """ Calculates the date when the schedule is over or None is the schedule is a loop. """ if self.is_loop: return None end_date = self.start_date for step in self.schedulestep_set.all(): end_date = end_date + datetime.timedelta(weeks=step.duration) return end_date
class WorkoutSession(models.Model): ''' Model for a workout session ''' IMPRESSION_BAD = '1' IMPRESSION_NEUTRAL = '2' IMPRESSION_GOOD = '3' IMPRESSION = ( (IMPRESSION_BAD, _("Bad")), (IMPRESSION_NEUTRAL, _('Neutral')), (IMPRESSION_GOOD, _('Good')), ) user = models.ForeignKey(User, verbose_name=_('User')) ''' The user the workout session belongs to See note in weight.models.WeightEntry about why this is not editable=False ''' workout = models.ForeignKey(Workout, verbose_name=_('Workout')) ''' The workout the session belongs to ''' date = Html5DateField(verbose_name=_('Date')) ''' The date the workout session was performed ''' notes = models.TextField( verbose_name=_('Notes'), null=True, blank=True, help_text=_('Any notes you might want to save about this workout ' 'session.')) ''' User notes about the workout ''' impression = models.CharField( verbose_name=_('General impression'), max_length=2, choices=IMPRESSION, default=IMPRESSION_NEUTRAL, help_text=_('Your impression about this workout session. ' 'Did you exercise as well as you could?')) ''' The user's general impression of workout ''' time_start = models.TimeField(verbose_name=_('Start time'), blank=True, null=True) ''' Time the workout session started ''' time_end = models.TimeField(verbose_name=_('Finish time'), blank=True, null=True) ''' Time the workout session ended ''' def __unicode__(self): ''' Return a more human-readable representation ''' return u"{0} - {1}".format(self.workout, self.date) class Meta: ''' Set other properties ''' ordering = [ "date", ] unique_together = ("date", "user") def clean(self): ''' Perform some additional validations ''' if (not self.time_end and self.time_start) or (self.time_end and not self.time_start): raise ValidationError( _("If you enter a time, you must enter both start and end time." )) if self.time_end and self.time_start and self.time_start > self.time_end: raise ValidationError( _("The start time cannot be after the end time.")) def get_owner_object(self): ''' Returns the object that has owner information ''' return self