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.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 fields = ['equipment', 'session', 'theoretical_max_percentage', 'chosen_weight', 'repetition_number', 'work_form', 'notes'] template_name = 'gym/round_edit.html' equipment = None session = 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'] = '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})