podusowski/spartan

View on GitHub
statistics/goals.py

Summary

Maintainability
A
45 mins
Test Coverage
import collections
import enum
from typing import List

from django.contrib.auth.models import User
from django.utils import timezone

from training import units
from training import dates
from statistics import models
from statistics.statistics import Statistics


Forecast = enum.Enum('Forecast', ['AHEAD', 'ON_TRACK', 'BEHIND', 'DONE'])
Goal = collections.namedtuple('Goal', ['name', 'volume', 'progress', 'percent', 'left', 'forecast'])


class Goals:
    def __init__(self, user: User) -> None:
        self.user = user
        self.statistics = Statistics(user)

    def set(self, name: str, volume: int) -> None:
        models.Goal.objects.update_or_create(user=self.user, name=name, defaults={'volume': volume})

    def delete(self, name: str) -> None:
        models.Goal.objects.filter(user=self.user, name=name).delete()

    def get(self, name) -> Goal:
        for goal in self.all():
            if goal.name == name:
                return goal
        return None

    def _has_goal(self, name: str) -> bool:
        return True if self.get(name) else False

    def workouts_not_having_goal(self):
        workouts = self.statistics.most_popular_workouts()
        return [workout.name for workout in workouts if not self._has_goal(workout.name)]

    def all(self, now=timezone.now()) -> List[Goal]:
        volumes = {f.name: f.volume for f in self.statistics.favourites_this_month()}

        def calculate_forecast(percent):
            date_progress = dates.this_month(now=now).progress(now)

            if percent >= 100:
                return Forecast.DONE

            if percent > date_progress + 10:
                return Forecast.AHEAD

            if percent < date_progress - 10:
                return Forecast.BEHIND

            return Forecast.ON_TRACK

        def make_goal(goal):
            current = volumes.get(goal.name, units.Volume(0))

            percent = 0
            if goal.volume > 0:
                percent = round(current.number() / goal.volume * 100)

            return Goal(name=goal.name,
                        volume=goal.volume,
                        progress=current,
                        percent=percent,
                        forecast=calculate_forecast(percent),
                        left=current.left_to(goal.volume))

        return [make_goal(g) for g in models.Goal.objects.filter(user=self.user)]