700 lines
20 KiB
Python
700 lines
20 KiB
Python
import datetime as dt
|
|
from django.conf import settings
|
|
from django.utils import timezone
|
|
from django.db import models
|
|
from django.shortcuts import render, reverse, redirect, get_object_or_404
|
|
from django.urls import reverse, reverse_lazy
|
|
from django.views.generic.base import TemplateView
|
|
from django.views.generic.detail import DetailView
|
|
from django.views.generic.list import ListView
|
|
from django.views.generic.dates import YearArchiveView
|
|
from django.views.generic.edit import (
|
|
FormView, CreateView, UpdateView, DeleteView, FormMixin
|
|
)
|
|
from django.contrib import messages
|
|
from django.contrib.auth.mixins import (
|
|
LoginRequiredMixin, PermissionRequiredMixin
|
|
)
|
|
from django.contrib.messages.views import SuccessMessageMixin
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.db.models import (
|
|
Exists, OuterRef, Prefetch, Subquery, Count, Sum, Avg, F, Q, Value
|
|
)
|
|
|
|
from .models import (
|
|
SchoolYear,
|
|
StudentTag,
|
|
Student,
|
|
Subject,
|
|
Tag,
|
|
Component,
|
|
Score,
|
|
SchoolDay,
|
|
AttendanceEntry,
|
|
)
|
|
|
|
from .forms import (
|
|
SchoolYearCreateForm,
|
|
ComponentCreateForm,
|
|
SchoolDayForm,
|
|
AttendanceEntryForm
|
|
)
|
|
|
|
|
|
class TodayView(LoginRequiredMixin, TemplateView):
|
|
template_name = 'core/today.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
today = timezone.localtime(timezone.now()).date()
|
|
enddate = today + dt.timedelta(days=7)
|
|
|
|
context['birthdays'] = Student.objects.filter(
|
|
dob__month=today.month,
|
|
dob__day__range=[today.day, enddate.day]
|
|
).order_by('dob')
|
|
|
|
context['components'] = Component.objects.filter(
|
|
due_date=today
|
|
).select_related('subject')
|
|
|
|
context['attendance'] = SchoolDay.objects.filter(
|
|
date=today
|
|
).prefetch_related('attendanceentry_set', 'attendanceentry_set__student')
|
|
|
|
context['ungraded_components'] = Component.objects.filter(
|
|
due_date__lte=today,
|
|
finished_grading=False
|
|
).select_related('subject')
|
|
|
|
return context
|
|
|
|
|
|
class SchoolYearListView(LoginRequiredMixin, ListView):
|
|
model = SchoolYear
|
|
paginate_by = 10
|
|
|
|
def get_queryset(self):
|
|
queryset = SchoolYear.objects.annotate(
|
|
models.Count('student', distinct=True),
|
|
models.Count('subject', distinct=True)
|
|
).order_by('-year')
|
|
return queryset
|
|
|
|
|
|
class SchoolYearCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|
model = SchoolYear
|
|
success_message = 'SchoolYear created.'
|
|
form_class = SchoolYearCreateForm
|
|
template_name_suffix = '_create_form'
|
|
|
|
|
|
class SchoolYearDetailView(LoginRequiredMixin, DetailView):
|
|
model = SchoolYear
|
|
slug_url_kwarg = 'year'
|
|
slug_field = 'year'
|
|
|
|
|
|
class SchoolYearUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|
model = SchoolYear
|
|
slug_url_kwarg = 'year'
|
|
slug_field = 'year'
|
|
success_message = 'SchoolYear saved.'
|
|
fields = '__all__'
|
|
|
|
|
|
class StudentListView(LoginRequiredMixin, ListView):
|
|
model = Student
|
|
paginate_by = 50
|
|
|
|
def get_queryset(self):
|
|
queryset = Student.objects.filter(
|
|
school_year__year=self.kwargs['year']
|
|
)
|
|
return queryset
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
|
|
class StudentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|
model = Student
|
|
success_message = 'Student created.'
|
|
template_name_suffix = '_create_form'
|
|
fields = [
|
|
'student_id',
|
|
'first_name',
|
|
'last_name',
|
|
'address',
|
|
'dob',
|
|
]
|
|
|
|
def form_valid(self, form):
|
|
form.instance.school_year = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return super().form_valid(form)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(SchoolYear, year=self.kwargs['year'])
|
|
return context
|
|
|
|
|
|
class StudentDetailView(LoginRequiredMixin, DetailView):
|
|
model = Student
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
|
|
context['score_list'] = Score.objects.select_related(
|
|
'student'
|
|
).prefetch_related(
|
|
'component'
|
|
).select_related(
|
|
'component__subject'
|
|
).filter(
|
|
student=self.object
|
|
).order_by(
|
|
'component__subject',
|
|
'-component__due_date'
|
|
)
|
|
|
|
context['subject_list'] = Subject.objects.filter(
|
|
component__score__student=self.object
|
|
).annotate(
|
|
grade=Sum(F('component__score__value')),
|
|
grade_total=Sum('component__grade_total')
|
|
)
|
|
|
|
context['entry_list'] = AttendanceEntry.objects.select_related(
|
|
'school_day'
|
|
).filter(
|
|
student=self.object
|
|
).order_by('status', '-school_day__date').select_related('student')
|
|
|
|
return context
|
|
|
|
|
|
class StudentUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|
model = Student
|
|
success_message = 'Student saved.'
|
|
fields = '__all__'
|
|
|
|
|
|
class StudentDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
|
model = Student
|
|
success_message = 'Student deleted.'
|
|
success_url = reverse_lazy('student-list')
|
|
|
|
|
|
class StudentTagListView(LoginRequiredMixin, ListView):
|
|
model = StudentTag
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
|
|
class StudentTagCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|
model = StudentTag
|
|
success_message = 'Tag created.'
|
|
template_name_suffix = '_create_form'
|
|
fields = '__all__'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
def get_success_url(self):
|
|
return reverse('core:stag-list', kwargs={
|
|
'year': self.kwargs['year']
|
|
})
|
|
|
|
|
|
class StudentTagDetailView(LoginRequiredMixin, DetailView):
|
|
model = StudentTag
|
|
pk_url_kwarg = 'stag_pk'
|
|
context_object_name = 'tag'
|
|
|
|
def get_object(self):
|
|
queryset = StudentTag.objects.filter(
|
|
pk=self.kwargs.get(self.pk_url_kwarg)
|
|
).prefetch_related(
|
|
Prefetch(
|
|
'student_set',
|
|
queryset=Student.objects.filter(
|
|
school_year__year=self.kwargs['year']
|
|
)
|
|
)
|
|
)
|
|
obj = queryset.get()
|
|
return obj
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
|
|
class StudentTagUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|
model = StudentTag
|
|
pk_url_kwarg = 'stag_pk'
|
|
context_object_name = 'tag'
|
|
success_message = 'Tag saved.'
|
|
fields = '__all__'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
def get_success_url(self):
|
|
return reverse('core:stag-list', kwargs={
|
|
'year': self.kwargs['year']
|
|
})
|
|
|
|
|
|
class StudentTagDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
|
model = StudentTag
|
|
pk_url_kwarg = 'stag_pk'
|
|
context_object_name = 'tag'
|
|
success_message = 'Tag deleted.'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
def get_success_url(self):
|
|
return reverse('core:stag-list', kwargs={
|
|
'year': self.kwargs['year']
|
|
})
|
|
|
|
|
|
class SubjectListView(LoginRequiredMixin, ListView):
|
|
model = Subject
|
|
paginate_by = 50
|
|
|
|
def get_queryset(self):
|
|
queryset = Subject.objects.filter(
|
|
school_year__year=self.kwargs['year']
|
|
).annotate(
|
|
models.Count('component')
|
|
).order_by('name')
|
|
return queryset
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
|
|
class SubjectCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|
model = Subject
|
|
success_message = 'Subject created.'
|
|
template_name_suffix = '_create_form'
|
|
fields = [
|
|
'name',
|
|
'description'
|
|
]
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
form.instance.school_year = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return super().form_valid(form)
|
|
|
|
|
|
class SubjectDetailView(LoginRequiredMixin, DetailView):
|
|
model = Subject
|
|
|
|
def get_object(self):
|
|
queryset = Subject.objects.filter(
|
|
pk=self.kwargs.get(self.pk_url_kwarg)
|
|
).prefetch_related(
|
|
Prefetch(
|
|
'component_set',
|
|
queryset=Component.objects.prefetch_related(
|
|
'score_set'
|
|
).annotate(
|
|
grade_avg_pre=Avg('score__value')
|
|
)
|
|
)
|
|
)
|
|
obj = queryset.get()
|
|
return obj
|
|
|
|
|
|
class SubjectUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|
model = Subject
|
|
success_message = 'Subject saved.'
|
|
fields = '__all__'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
|
|
class SubjectDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
|
model = Subject
|
|
success_message = 'Subject deleted.'
|
|
success_url = reverse_lazy('subject-list')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
|
|
class ComponentListView(LoginRequiredMixin, ListView):
|
|
model = Component
|
|
|
|
|
|
class ComponentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|
model = Component
|
|
success_message = 'Component created.'
|
|
template_name_suffix = '_create_form'
|
|
form_class = ComponentCreateForm
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
context['subject'] = get_object_or_404(
|
|
Subject, pk=self.kwargs['pk']
|
|
)
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
form.instance.school_year = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
form.instance.subject = get_object_or_404(
|
|
Subject, pk=self.kwargs['pk']
|
|
)
|
|
return super().form_valid(form)
|
|
|
|
|
|
class ComponentDetailView(LoginRequiredMixin, DetailView):
|
|
model = Component
|
|
pk_url_kwarg = 'component_pk'
|
|
|
|
def get_object(self):
|
|
queryset = Component.objects.filter(
|
|
pk=self.kwargs.get(self.pk_url_kwarg)
|
|
).prefetch_related(
|
|
Prefetch(
|
|
'score_set',
|
|
queryset=Score.objects.select_related('student')
|
|
),
|
|
'tags'
|
|
).annotate(
|
|
grade_avg_pre=Avg('score__value')
|
|
)
|
|
obj = queryset.get()
|
|
return obj
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
cscores = Score.objects.filter(
|
|
component=self.object,
|
|
student=OuterRef('pk')
|
|
)
|
|
context['scoreless'] = Student.objects.filter(
|
|
school_year__year=self.kwargs['year']
|
|
).exclude(
|
|
score__in=cscores
|
|
)
|
|
return context
|
|
|
|
|
|
class ComponentUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|
model = Component
|
|
pk_url_kwarg = 'component_pk'
|
|
success_message = 'Component saved.'
|
|
fields = '__all__'
|
|
|
|
def get_success_url(self):
|
|
return reverse('core:component-detail', kwargs={
|
|
'year': self.object.subject.school_year.year,
|
|
'pk': self.object.subject.pk,
|
|
'component_pk': self.object.pk
|
|
})
|
|
|
|
|
|
class ComponentDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
|
model = Component
|
|
pk_url_kwarg = 'component_pk'
|
|
success_message = 'Component deleted.'
|
|
success_url = reverse_lazy('core:component-list')
|
|
|
|
|
|
class ComponentManagerView(LoginRequiredMixin, UpdateView):
|
|
model = Component
|
|
pk_url_kwarg = 'component_pk'
|
|
template_name_suffix = '_manager'
|
|
fields = ['finished_grading']
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
cscores = Score.objects.filter(
|
|
component=self.object,
|
|
student=OuterRef('pk')
|
|
)
|
|
context['student_list'] = Student.objects.filter(
|
|
school_year__year=self.kwargs['year']
|
|
).annotate(
|
|
cscore=Subquery(cscores.values('value')),
|
|
cscore_pk=Subquery(cscores.values('pk'))
|
|
)
|
|
return context
|
|
|
|
def get_success_url(self):
|
|
return reverse('core:component-detail', kwargs={
|
|
'year': self.object.subject.school_year.year,
|
|
'pk': self.object.subject.pk,
|
|
'component_pk': self.object.pk
|
|
})
|
|
|
|
def form_valid(self, form):
|
|
form.save()
|
|
for key, value in self.request.POST.items():
|
|
if 'student' in key and value:
|
|
s_pk = key.split('_')[1]
|
|
obj, created = Score.objects.update_or_create(
|
|
component=self.object,
|
|
student=Student.objects.get(pk=s_pk),
|
|
defaults={'value': value}
|
|
)
|
|
return super().form_valid(form)
|
|
|
|
|
|
class ScoreListView(LoginRequiredMixin, ListView):
|
|
model = Score
|
|
|
|
|
|
class ScoreCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|
model = Score
|
|
success_message = 'Score created.'
|
|
template_name_suffix = '_create_form'
|
|
fields = '__all__'
|
|
|
|
|
|
class ScoreDetailView(LoginRequiredMixin, DetailView):
|
|
model = Score
|
|
|
|
|
|
class ScoreUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|
model = Score
|
|
success_message = 'Score saved.'
|
|
fields = [
|
|
'component',
|
|
'value'
|
|
]
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
|
|
class ScoreDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
|
model = Score
|
|
success_message = 'Score deleted.'
|
|
|
|
def get_success_url(self):
|
|
return reverse('core:schoolyear-detail', kwargs={
|
|
'year': self.kwargs['year']
|
|
})
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
|
|
class SchoolDayListView(LoginRequiredMixin, ListView):
|
|
model = SchoolDay
|
|
paginate_by = 7
|
|
|
|
def get_queryset(self):
|
|
queryset = SchoolDay.objects.filter(
|
|
school_year__year=self.kwargs['year']
|
|
)
|
|
return queryset
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
|
|
class SchoolDayCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|
model = SchoolDay
|
|
success_message = 'SchoolDay created.'
|
|
template_name_suffix = '_create_form'
|
|
form_class = SchoolDayForm
|
|
|
|
def get_initial(self):
|
|
today = timezone.localtime(timezone.now()).date()
|
|
initial = {
|
|
'date': today,
|
|
}
|
|
return initial
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['student_list'] = Student.objects.filter(
|
|
school_year__year=self.kwargs['year']
|
|
)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
form.instance.school_year = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
form.save()
|
|
for key, value in self.request.POST.items():
|
|
if 'student' in key:
|
|
s = key.split('_')[1]
|
|
AttendanceEntry.objects.create(
|
|
school_day=form.instance,
|
|
student=Student.objects.get(pk=s),
|
|
status=value,
|
|
)
|
|
return super().form_valid(form)
|
|
|
|
|
|
class SchoolDayDetailView(LoginRequiredMixin, DetailView):
|
|
model = SchoolDay
|
|
|
|
|
|
class SchoolDayUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|
model = SchoolDay
|
|
success_message = 'SchoolDay saved.'
|
|
form_class = SchoolDayForm
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
form.save()
|
|
for key, value in self.request.POST.items():
|
|
if 'student' in key:
|
|
s = key.split('_')[1]
|
|
AttendanceEntry.objects.filter(
|
|
school_day=self.object,
|
|
student=Student.objects.get(pk=s)
|
|
).update(status=value)
|
|
return super().form_valid(form)
|
|
|
|
|
|
class SchoolDayDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
|
model = SchoolDay
|
|
success_message = 'SchoolDay deleted.'
|
|
|
|
def get_success_url(self):
|
|
return reverse('core:schoolday-list', kwargs={
|
|
'year': self.kwargs['year']
|
|
})
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['school_year'] = get_object_or_404(
|
|
SchoolYear, year=self.kwargs['year']
|
|
)
|
|
return context
|
|
|
|
|
|
class AttendanceEntryListView(LoginRequiredMixin, ListView):
|
|
model = AttendanceEntry
|
|
|
|
|
|
class AttendanceEntryCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|
model = AttendanceEntry
|
|
success_message = 'Entry created.'
|
|
template_name_suffix = '_create_form'
|
|
fields = '__all__'
|
|
|
|
|
|
class AttendanceEntryDetailView(LoginRequiredMixin, DetailView):
|
|
model = AttendanceEntry
|
|
pk_url_kwarg = 'entry_pk'
|
|
context_object_name = 'entry'
|
|
|
|
|
|
class AttendanceEntryUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|
model = AttendanceEntry
|
|
pk_url_kwarg = 'entry_pk'
|
|
context_object_name = 'entry'
|
|
success_message = 'Entry saved.'
|
|
form_class = AttendanceEntryForm
|
|
|
|
def get_success_url(self):
|
|
return reverse('core:schoolday-detail', kwargs={
|
|
'year': self.kwargs['year'],
|
|
'pk': self.kwargs['pk']
|
|
})
|
|
|
|
|
|
class AttendanceEntryDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
|
model = AttendanceEntry
|
|
pk_url_kwarg = 'entry_pk'
|
|
context_object_name = 'entry'
|
|
success_message = 'Entry deleted.'
|
|
|
|
def get_success_url(self):
|
|
return reverse('core:schoolday-detail', kwargs={
|
|
'year': self.kwargs['year'],
|
|
'pk': self.kwargs['pk']
|
|
})
|