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 " '{} 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 {}, {}x{}{} 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})