workout/gym/views.py

576 lines
19 KiB
Python

import datetime
import plotly
import plotly.graph_objs as go
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.views import generic
from gym.forms import RoundForm
from gym.mixins import SessionResetMixin, QuickActionsMixin
from gym.models import Room, Equipment, Setting, Session, Round, TheoreticalMax
class RoomListView(LoginRequiredMixin, SessionResetMixin, generic.ListView):
queryset = Room.objects.all().order_by("name")
context_object_name = "rooms"
template_name = "gym/rooms.html"
class RoomDetailView(
LoginRequiredMixin, QuickActionsMixin, SessionResetMixin, generic.DetailView
):
model = Room
context_object_name = "room"
template_name = "gym/room.html"
def get_quick_actions(self):
quick_actions = [
{
"url": reverse("rooms-list"),
"category": "secondary",
"display": "Liste des salles",
},
{
"url": "{}?room={}".format(reverse("session-start"), self.object.pk),
"category": "primary",
"display": "Commencer une séance",
},
{
"url": "{}?room={}".format(reverse("equipment-create"), self.object.pk),
"category": "success",
"display": "Ajouter une machine",
},
]
if self.object.latitude and self.object.longitude:
from user_agents import parse
user_agent = parse(self.request.META.get("HTTP_USER_AGENT"))
if user_agent.is_mobile:
url = f"waze://?ll={self.object.latitude},{self.object.longitude}&navigate=yes"
name = "Waze"
quick_actions.append(
{
"url": f"here-location://{self.object.latitude},{self.object.longitude}",
"category": "info",
"display": "Here WeGo",
"icon": "fas fa-location-arrow",
}
)
else:
url = (
f"https://www.google.com/maps/place/{self.object.latitude},{self.object.longitude}"
f"/@{self.object.latitude},{self.object.longitude},18z"
)
name = "Google Maps"
quick_actions.append(
{
"url": url,
"category": "info",
"display": name,
"icon": "fas fa-location-arrow",
}
)
return quick_actions
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["sessions"] = self.object.sessions.all().order_by("-start")
return context
class EquipmentDetailView(LoginRequiredMixin, QuickActionsMixin, generic.DetailView):
model = Equipment
context_object_name = "equipment"
template_name = "gym/equipment.html"
session = None
def get_quick_actions(self):
lst = []
if self.session:
lst.extend(
[
{
"url": reverse("session-detail", args=(self.session.pk,)),
"category": "secondary",
"display": "Retourner à la séance",
},
{
"url": "{}?equipment={}".format(
reverse("round-create"), self.object.pk
),
"category": "primary",
"display": "Commencer une série",
},
]
)
else:
lst.append(
{
"url": reverse("room-detail", args=(self.object.room.pk,)),
"category": "secondary",
"display": "Retourner à la salle",
}
)
lst.extend(
[
{
"url": "{}?equipment={}".format(
reverse("setting-create"), self.object.pk
),
"category": "success",
"display": "Ajouter un réglage",
},
{
"url": "{}?equipment={}".format(
reverse("theoretical-max-create"), self.object.pk
),
"category": "success",
"display": "Ajouter un max théorique",
},
]
)
if self.request.user.is_staff:
lst.append(
{
"url": reverse(
"admin:gym_equipment_change", args=(self.object.pk,)
),
"category": "warning",
"display": "Éditer la machine",
}
)
return lst
def dispatch(self, request, *args, **kwargs):
session_pk = self.request.session.get("session_pk")
if session_pk:
self.session = Session.objects.get(pk=session_pk)
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.session:
context["session"] = self.session.pk
context["rounds"] = Round.objects.filter(
session=self.session, equipment=self.get_object()
)
return context
class EquipmentCreateView(LoginRequiredMixin, QuickActionsMixin, generic.CreateView):
model = Equipment
fields = ["room", "name", "default_work_form", "unit", "default_repetition_number"]
template_name = "gym/equipment_edit.html"
room = None
def get_quick_actions(self):
return [
{
"url": reverse("room-detail", args=(self.room.pk,)),
"category": "secondary",
"display": "Retourner à la salle",
}
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["room"] = self.room
return context
def get_success_url(self):
return reverse("equipment-detail", kwargs={"pk": self.object.pk})
def dispatch(self, request, *args, **kwargs):
default_room_id = self.request.GET.get("room")
if default_room_id:
self.room = get_object_or_404(Room, pk=default_room_id)
return super().dispatch(request, *args, **kwargs)
def get_initial(self):
initial = super().get_initial()
initial["room"] = self.room
return initial
class SettingCreateView(LoginRequiredMixin, QuickActionsMixin, generic.CreateView):
model = Setting
fields = ["equipment", "name", "value"]
template_name = "gym/setting_edit.html"
equipment = None
def get_quick_actions(self):
return [
{
"url": reverse("equipment-detail", args=(self.equipment.pk,)),
"category": "secondary",
"display": "Retourner à la machine",
}
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = "Ajouter"
context["equipment"] = self.equipment
return context
def dispatch(self, request, *args, **kwargs):
default_equipment_id = self.request.GET.get("equipment")
if default_equipment_id:
self.equipment = get_object_or_404(Equipment, pk=default_equipment_id)
return super().dispatch(request, *args, **kwargs)
def get_initial(self):
initial = super().get_initial()
initial["equipment"] = self.equipment
return initial
def get_success_url(self):
return reverse("equipment-detail", kwargs={"pk": self.object.equipment.pk})
class SettingUpdateView(LoginRequiredMixin, QuickActionsMixin, generic.UpdateView):
model = Setting
fields = ["equipment", "name", "value"]
template_name = "gym/setting_edit.html"
def get_quick_actions(self):
return [
{
"url": reverse("equipment-detail", args=(self.object.equipment.pk,)),
"category": "secondary",
"display": "Retourner à la machine",
}
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["edit"] = True
context["title"] = "Modifier"
context["equipment"] = self.object.equipment
return context
def get_success_url(self):
return reverse("equipment-detail", kwargs={"pk": self.object.equipment.pk})
class SettingDeleteView(LoginRequiredMixin, generic.DeleteView):
model = Setting
template_name = "gym/confirm_delete.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["article"] = "une"
context["name"] = "série"
context["message"] = (
"Êtes-vous sûr de vouloir supprimer le réglage "
'<code>{}</code> pour la machine "{}" ?'.format(
self.object, self.object.equipment
)
)
return context
def get_success_url(self):
return reverse("equipment-detail", kwargs={"pk": self.object.equipment.pk})
class TheoreticalMaxCreateView(
LoginRequiredMixin, QuickActionsMixin, generic.CreateView
):
model = TheoreticalMax
fields = ["equipment", "date", "value"]
template_name = "gym/theoretical_max_edit.html"
equipment = None
def get_quick_actions(self):
return [
{
"url": reverse("equipment-detail", args=(self.equipment.pk,)),
"category": "secondary",
"display": "Retourner à la machine",
}
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = "Ajouter"
context["equipment"] = self.equipment
return context
def dispatch(self, request, *args, **kwargs):
default_equipment_id = self.request.GET.get("equipment")
if default_equipment_id:
self.equipment = get_object_or_404(Equipment, pk=default_equipment_id)
return super().dispatch(request, *args, **kwargs)
def get_initial(self):
initial = super().get_initial()
initial["equipment"] = self.equipment
initial["date"] = datetime.date.today()
return initial
def get_success_url(self):
return reverse("theoretical-max-list", kwargs={"pk": self.object.equipment.pk})
class TheoreticalMaxListView(LoginRequiredMixin, QuickActionsMixin, generic.ListView):
model = TheoreticalMax
template_name = "gym/theoretical_max_list.html"
context_object_name = "maxs"
equipment = None
def dispatch(self, request, *args, **kwargs):
pk = self.kwargs.pop("pk")
self.equipment = get_object_or_404(Equipment, pk=pk)
return super().dispatch(request, *args, **kwargs)
def get_queryset(self):
return TheoreticalMax.objects.filter(equipment=self.equipment).order_by("date")
def get_quick_actions(self):
return [
{
"url": reverse("equipment-detail", args=(self.equipment.pk,)),
"category": "secondary",
"display": "Retourner à la machine",
},
{
"url": "{}?equipment={}".format(
reverse("theoretical-max-create"), self.equipment.pk
),
"category": "success",
"display": "Ajouter un max théorique",
},
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
data = [
go.Scatter(
x=[max.date for max in self.get_queryset()],
y=[max.value for max in self.get_queryset()],
)
]
layout = go.Layout(
title="Évolution du max théorique",
yaxis={"title": "Charge ({})".format(self.equipment.unit.name)},
xaxis={"title": "Date"},
)
figure = {"data": data, "layout": layout}
graph = plotly.offline.plot(figure, auto_open=False, output_type="div")
context["graph"] = graph
context["equipment"] = self.equipment
return context
class SessionCreateView(LoginRequiredMixin, QuickActionsMixin, generic.CreateView):
model = Session
fields = ["room", "start", "default_theoretical_max_percentage"]
template_name = "gym/session_edit.html"
room = None
def get_quick_actions(self):
room_pk = self.room.pk
return [
{
"url": reverse("room-detail", args=(room_pk,)),
"category": "secondary",
"display": "Retourner à la salle",
}
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = "Démarrer"
context["room"] = self.room
return context
def get_success_url(self):
return reverse("session-detail", kwargs={"pk": self.object.pk})
def dispatch(self, request, *args, **kwargs):
self.room = get_object_or_404(Room, pk=self.request.GET.get("room"))
return super().dispatch(request, *args, **kwargs)
def get_initial(self):
initial = super().get_initial()
initial["room"] = self.room
initial["start"] = datetime.datetime.now()
return initial
class SessionDetailView(LoginRequiredMixin, QuickActionsMixin, generic.DetailView):
model = Session
context_object_name = "session"
template_name = "gym/session_detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
session = self.get_object()
context["used_equipments"] = {
k: Round.objects.filter(equipment=k, session=session).count()
for k in Equipment.objects.filter(rounds__session=self.get_object())
}
return context
def get_quick_actions(self):
room_pk = self.get_object().room.pk
return [
{
"url": reverse("room-detail", args=(room_pk,)),
"category": "secondary",
"display": "Retourner à la salle",
},
{
"url": "{}?room={}".format(reverse("equipment-create"), room_pk),
"category": "success",
"display": "Ajouter une machine",
},
]
def dispatch(self, request, *args, **kwargs):
request.session["session_pk"] = self.get_object().pk
return super().dispatch(request, *args, **kwargs)
class RoundCreateView(LoginRequiredMixin, QuickActionsMixin, generic.CreateView):
model = Round
template_name = "gym/round_edit.html"
equipment = None
session = None
form_class = RoundForm
def get_quick_actions(self):
return [
{
"url": reverse("equipment-detail", args=(self.equipment.pk,)),
"category": "secondary",
"display": "Retourner à la machine",
}
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = "Commencer"
context["equipment"] = self.equipment
return context
def dispatch(self, request, *args, **kwargs):
self.equipment = get_object_or_404(
Equipment, pk=self.request.GET.get("equipment")
)
session_pk = self.request.session.get("session_pk")
if session_pk:
self.session = Session.objects.get(pk=session_pk)
return super().dispatch(request, *args, **kwargs)
def get_initial(self):
theoretical_max_percentage = self.session.default_theoretical_max_percentage
initial = super().get_initial()
initial["equipment"] = self.equipment
initial["session"] = self.session
last_round = self.equipment.rounds.filter(
session=self.session
).last() # type: Round
initial["theoretical_max_percentage"] = theoretical_max_percentage
if last_round:
initial["repetition_number"] = last_round.repetition_number
initial["work_form"] = last_round.work_form
initial["chosen_weight"] = last_round.chosen_weight
else:
initial["repetition_number"] = (
self.equipment.default_repetition_number or 12
)
initial["work_form"] = self.equipment.default_work_form
proposed_weight = 0
theoretical_max = self.equipment.last_theoretical_max
if theoretical_max:
proposed_weight = (
theoretical_max.value * theoretical_max_percentage / 100
)
initial["chosen_weight"] = proposed_weight
return initial
def get_success_url(self):
return reverse("equipment-detail", kwargs={"pk": self.object.equipment.pk})
class RoundUpdateView(LoginRequiredMixin, QuickActionsMixin, generic.UpdateView):
model = Round
fields = [
"equipment",
"session",
"theoretical_max_percentage",
"chosen_weight",
"repetition_number",
"work_form",
"notes",
]
template_name = "gym/round_edit.html"
def get_quick_actions(self):
return [
{
"url": reverse("equipment-detail", args=(self.object.equipment.pk,)),
"category": "secondary",
"display": "Retourner à la machine",
}
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["edit"] = True
context["title"] = "Modifier"
context["equipment"] = self.object.equipment
return context
def get_success_url(self):
return reverse("equipment-detail", kwargs={"pk": self.object.equipment.pk})
class RoundDeleteView(LoginRequiredMixin, generic.DeleteView):
model = Round
template_name = "gym/confirm_delete.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["article"] = "une"
context["name"] = "série"
context["message"] = (
"Êtes-vous sûr de vouloir supprimer "
'la série <code>{}, {}x{}{}</code> pour la séance "{}" ?'.format(
self.object.equipment,
self.object.repetition_number,
self.object.chosen_weight,
self.object.equipment.unit,
self.object.session,
)
)
return context
def get_success_url(self):
return reverse("equipment-detail", kwargs={"pk": self.object.equipment.pk})