from django.shortcuts import render, reverse from django.db.models import Avg, Count, Min, Sum from django.db.models.functions import Round from django.urls import reverse_lazy from django.views.generic.base import TemplateView from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView from django.views.generic.detail import DetailView from django.views.generic.list import ListView from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.utils import timezone from django.contrib import messages from django.contrib.auth.models import User from accounts.models import Instructor, Student from .models import Code, Period from .forms import AttendanceUpdateForm, PeriodForm # EXAMPLE PERMISSION MIXIN # class MyView(PermissionRequiredMixin, View): # permission_required = 'polls.add_choice' # # Or multiple of permissions: # permission_required = ('polls.view_choice', 'polls.change_choice') # OVERVIEW class AttendanceOverview(LoginRequiredMixin, TemplateView): template_name = 'attendance/attendance_overview.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['user'] = User.objects.get(pk=self.request.user.id) if hasattr(self.request.user, 'instructor'): context['student_list'] = Student.objects.filter( department=self.request.user.instructor.department ).annotate(total_hours=Sum('period__duration')) context['period_list'] = Period.objects.order_by('-clocked_in') elif hasattr(self.request.user, 'student'): student = self.request.user.student # sum all duration fields for student periods_duration_sum = Period.objects.filter( student = student ).filter( clocked_in__year=timezone.now().year ).filter( clocked_in__month=timezone.now().month ).aggregate(total_duration=Sum('duration')) hours = 0 # Convert to hours floating point if periods_duration_sum['total_duration'] != None: hours = round((periods_duration_sum['total_duration'].total_seconds() / 3600), 2) context['period_list'] = Period.objects.filter( student=student ).filter( clocked_in__year=timezone.now().year ).filter( clocked_in__month=timezone.now().month ).order_by('-clocked_in') context['period_total'] = hours if student.current_period_id != None: context['current_period'] = Period.objects.get(pk=student.current_period_id) return context class AttendanceUpdateView(LoginRequiredMixin, FormView): template_name = 'attendance/attendance_form.html' form_class = AttendanceUpdateForm def form_valid(self, form): # update checked in student_number = form.cleaned_data['qr_string'].split(':')[0] station_number = form.cleaned_data['qr_string'].split(':')[1] student = Student.objects.get(student_number=student_number) if student.is_clocked_in: student.is_clocked_in=False period = student.period_set.get(pk=student.current_period_id) period.clocked_out=timezone.now() student.save() period.save() messages.add_message(self.request, messages.INFO, f'{student.user.first_name} {student.user.last_name} clocked out.') else: c_p = student.period_set.create(student=student, clocked_in=timezone.now(), station_number=station_number) student.current_period_id = c_p.id student.is_clocked_in=True student.save() messages.add_message(self.request, messages.INFO, f'{student.user.first_name} {student.user.last_name} clocked in.') return super().form_valid(form) def get_success_url(self): return reverse('attendance-update') # PERIODS class PeriodCreateView(LoginRequiredMixin, CreateView): model = Period form_class = PeriodForm template_name = 'attendance/period_form.html' class PeriodDetailView(LoginRequiredMixin, DetailView): model = Period template_name = 'attendance/period_detail.html' class PeriodUpdateView(LoginRequiredMixin, UpdateView): model = Period form_class = PeriodForm template_name = 'attendance/period_form.html' # When editing a period, check if it matches the current period of the student and clock them out def form_valid(self, form): student = form.instance.student if form.instance.id == student.current_period_id: student.is_clocked_in = False student.save() return super().form_valid(form) def get_success_url(self): pk = self.kwargs["pk"] return reverse('period-detail', kwargs={'pk': pk}) class PeriodDeleteView(LoginRequiredMixin, DeleteView): model = Period success_url = '/attendance' # CODES class CodeCreateView(LoginRequiredMixin, CreateView): model = Code fields = ['station_number'] template_name = 'attendance/code_form.html' def form_valid(self, form): form.instance.student = self.request.user.student return super().form_valid(form) class CodeDetailView(LoginRequiredMixin, DetailView): model = Code template_name = 'attendance/code_detail.html' class CodeUpdateView(LoginRequiredMixin, UpdateView): model = Code fields = ['station_number'] template_name = 'attendance/code_update_form.html' def get_success_url(self): pk = self.kwargs["pk"] return reverse('code-detail', kwargs={'pk': pk})