diff --git a/src/core/context_processors.py b/src/core/context_processors.py
new file mode 100644
index 0000000..fc630b0
--- /dev/null
+++ b/src/core/context_processors.py
@@ -0,0 +1,8 @@
+from .models import SchoolYear
+from django.shortcuts import get_object_or_404
+
+
+def current_year(request):
+ return {
+ 'current_year': SchoolYear.objects.latest('year')
+ }
diff --git a/src/core/forms.py b/src/core/forms.py
index 7a137b5..431c045 100644
--- a/src/core/forms.py
+++ b/src/core/forms.py
@@ -35,3 +35,17 @@ class StudentForm(forms.ModelForm):
'student_id': 'Student ID',
'dob': 'DOB',
}
+
+
+class ComponentCreateForm(forms.ModelForm):
+ class Meta:
+ model = Component
+ fields = (
+ 'name',
+ 'category',
+ 'due_date',
+ 'grade_total',
+ )
+ widgets = {
+ 'due_date': forms.DateInput(attrs={'type': 'date'}),
+ }
diff --git a/src/core/models.py b/src/core/models.py
index 5a54a2c..c23034e 100644
--- a/src/core/models.py
+++ b/src/core/models.py
@@ -5,6 +5,9 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import (
GenericForeignKey, GenericRelation
)
+from django.db.models import (
+ Exists, OuterRef, Prefetch, Subquery, Count, Sum, Avg, F, Q, Value
+)
class SchoolYear(models.Model):
@@ -89,7 +92,7 @@ class Student(models.Model):
def get_absolute_url(self):
return reverse('core:student-detail', kwargs={
'year': self.school_year.year,
- 'student_pk': self.pk
+ 'pk': self.pk
})
class Meta:
@@ -114,7 +117,10 @@ class Subject(models.Model):
return self.name
def get_absolute_url(self):
- return reverse('subject-detail', kwargs={'pk': self.pk})
+ return reverse('core:subject-detail', kwargs={
+ 'year': self.school_year.year,
+ 'pk': self.pk
+ })
class Meta:
ordering = ['name']
@@ -180,7 +186,11 @@ class Component(models.Model):
return self.name
def get_absolute_url(self):
- return reverse('component-detail', kwargs={'pk': self.pk})
+ return reverse('core:component-detail', kwargs={
+ 'year': self.school_year.year,
+ 'pk': self.subject.pk,
+ 'component_pk': self.pk
+ })
class Meta:
ordering = ['due_date']
@@ -203,7 +213,11 @@ class Score(models.Model):
return f'{self.student} scored: {self.value} / {self.component.grade_total}'
def get_absolute_url(self):
- return reverse('score-detail', kwargs={'pk': self.pk})
+ return reverse('core:component-detail', kwargs={
+ 'year': self.component.subject.school_year.year,
+ 'pk': self.component.subject.pk,
+ 'component_pk': self.component.pk
+ })
class Meta:
ordering = ['student']
diff --git a/src/core/templates/core/component_confirm_delete.html b/src/core/templates/core/component_confirm_delete.html
new file mode 100644
index 0000000..e654aca
--- /dev/null
+++ b/src/core/templates/core/component_confirm_delete.html
@@ -0,0 +1,13 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
Delete Component
+
+{% endblock %}
diff --git a/src/core/templates/core/component_create_form.html b/src/core/templates/core/component_create_form.html
new file mode 100644
index 0000000..2d64eef
--- /dev/null
+++ b/src/core/templates/core/component_create_form.html
@@ -0,0 +1,28 @@
+{% extends 'base.html' %}
+
+{% block head_title %}New component | {% endblock head_title %}
+
+{% block breadcrumbs %}
+
+{% endblock breadcrumbs %}
+
+{% block content %}
+
+ + New Component
+
+
+{% endblock %}
diff --git a/src/core/templates/core/component_detail.html b/src/core/templates/core/component_detail.html
new file mode 100644
index 0000000..3ca5d54
--- /dev/null
+++ b/src/core/templates/core/component_detail.html
@@ -0,0 +1,88 @@
+{% extends "base.html" %}
+{% load helpers %}
+
+{% block head_title %}{{ component.name }} | {% endblock head_title %}
+
+{% block breadcrumbs %}
+
+{% endblock breadcrumbs %}
+
+{% block content %}
+
+
+ {% if component.tags.count > 0 %}
+
+
+ {% for tag in component.tags.all %}
+ {{tag.name}}
+ {% endfor %}
+
+
+ {% endif %}
+
+
+ - Due Date
+ - {{component.due_date}}
+ - Category
+ - {{component.get_category_display}}
+ - Grade Total
+ - {{component.grade_total}}
+
+
+
+
+
+
+
+ | Student |
+ Score |
+
+
+
+ {% for score in component.score_set.all %}
+
+ | {{score.student.student_id}} — {{score.student}} |
+ {{score.value}} / {{component.grade_total}} |
+ Edit score |
+
+ {% endfor %}
+
+ | Avg. Score |
+ {{component.grade_avg_pre|floatformat:2}} / {{component.grade_total}} |
+
+
+
+
+ {% if scoreless %}
+
+
+ Scoreless
+
+ {% for student in scoreless %}
+ - {{student}}
+ {% endfor %}
+
+
+ {% endif %}
+
+{% endblock %}
diff --git a/src/core/templates/core/component_form.html b/src/core/templates/core/component_form.html
new file mode 100644
index 0000000..ea2259c
--- /dev/null
+++ b/src/core/templates/core/component_form.html
@@ -0,0 +1,12 @@
+{% extends 'base.html' %}
+
+{% block content %}
+Update Component
+
+{% endblock %}
diff --git a/src/core/templates/core/component_list.html b/src/core/templates/core/component_list.html
new file mode 100644
index 0000000..003f836
--- /dev/null
+++ b/src/core/templates/core/component_list.html
@@ -0,0 +1,16 @@
+{% extends 'base.html' %}
+
+{% block content %}
+Components
+
+
+ {% for component in component_list %}
+
+ {{ component }}
+
+ {% empty %}
+ No components yet.
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/src/core/templates/core/component_manager.html b/src/core/templates/core/component_manager.html
new file mode 100644
index 0000000..c950284
--- /dev/null
+++ b/src/core/templates/core/component_manager.html
@@ -0,0 +1,56 @@
+{% extends "base.html" %}
+{% load helpers %}
+
+{% block head_title %}Enter Scores {{ component.name }} | {% endblock head_title %}
+
+{% block breadcrumbs %}
+
+{% endblock breadcrumbs %}
+
+{% block content %}
+
+ {{component}}
+
+
+{% endblock %}
diff --git a/src/core/templates/core/schoolyear_detail.html b/src/core/templates/core/schoolyear_detail.html
index 0a02ad2..4cd4719 100644
--- a/src/core/templates/core/schoolyear_detail.html
+++ b/src/core/templates/core/schoolyear_detail.html
@@ -13,7 +13,7 @@
Edit
{% endif %}
-
+
{% endblock %}
diff --git a/src/core/templates/core/schoolyear_list.html b/src/core/templates/core/schoolyear_list.html
index ca8583c..ac49187 100644
--- a/src/core/templates/core/schoolyear_list.html
+++ b/src/core/templates/core/schoolyear_list.html
@@ -14,32 +14,17 @@
-
-
-
- | Created |
- Year |
- Students |
- Components |
- Last Updated |
-
-
-
- {% for schoolyear in schoolyear_list %}
-
- | {{ schoolyear.created_at|date:'m/d/Y' }} |
- {{ schoolyear.year }} |
- |
- |
- |
-
- {% empty %}
-
- | No schoolyears yet. |
-
- {% endfor %}
-
-
+
{% include 'core/partials/pagination.html' %}
diff --git a/src/core/templates/core/score_confirm_delete.html b/src/core/templates/core/score_confirm_delete.html
new file mode 100644
index 0000000..9fa1792
--- /dev/null
+++ b/src/core/templates/core/score_confirm_delete.html
@@ -0,0 +1,31 @@
+{% extends "base.html" %}
+
+{% block head_title %}Delete Score {{ score.pk }} | {% endblock head_title %}
+
+{% block breadcrumbs %}
+
+{% endblock breadcrumbs %}
+
+{% block content %}
+
+ Delete Score
+
+
+{% endblock %}
diff --git a/src/core/templates/core/score_create_form.html b/src/core/templates/core/score_create_form.html
new file mode 100644
index 0000000..1366c99
--- /dev/null
+++ b/src/core/templates/core/score_create_form.html
@@ -0,0 +1,12 @@
+{% extends 'base.html' %}
+
+{% block content %}
++ New Score
+
+{% endblock %}
diff --git a/src/core/templates/core/score_detail.html b/src/core/templates/core/score_detail.html
new file mode 100644
index 0000000..7dcadeb
--- /dev/null
+++ b/src/core/templates/core/score_detail.html
@@ -0,0 +1,10 @@
+{% extends 'base.html' %}
+
+{% block content %}
+Score
+
+ Edit
+ Delete
+
+{{ score }}
+{% endblock %}
diff --git a/src/core/templates/core/score_form.html b/src/core/templates/core/score_form.html
new file mode 100644
index 0000000..026eba1
--- /dev/null
+++ b/src/core/templates/core/score_form.html
@@ -0,0 +1,40 @@
+{% extends "base.html" %}
+
+{% block head_title %}Edit score {{ score.pk }} | {% endblock head_title %}
+
+{% block breadcrumbs %}
+
+{% endblock breadcrumbs %}
+
+{% block content %}
+
+
+
+
+{% endblock %}
diff --git a/src/core/templates/core/score_list.html b/src/core/templates/core/score_list.html
new file mode 100644
index 0000000..f9a81d5
--- /dev/null
+++ b/src/core/templates/core/score_list.html
@@ -0,0 +1,16 @@
+{% extends 'base.html' %}
+
+{% block content %}
+Scores
+
+
+ {% for score in score_list %}
+
+ {{ score }}
+
+ {% empty %}
+ No scores yet.
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/src/core/templates/core/student_detail.html b/src/core/templates/core/student_detail.html
index 906a91f..64ae008 100644
--- a/src/core/templates/core/student_detail.html
+++ b/src/core/templates/core/student_detail.html
@@ -23,8 +23,8 @@
Edit
-
-
+
+
{% if student.allergies %}
- Allergies
- {{ student.allergies }}
@@ -51,7 +51,7 @@
{% for subject in subject_list %}
- | {{subject}} |
+ {{subject}} |
{{subject.grade|grade_as_percentage:subject.grade_total}}% |
{% empty %}
@@ -65,35 +65,38 @@
Components
-
+
{% regroup score_list by component.subject as score_list %}
{% for subject in score_list %}
-
{{subject.grouper}}
-
-
-
- | Due Date |
- Component |
- Category |
- Score |
- Total |
- Percentage |
-
-
-
- {% for score in subject.list %}
-
- | {{score.component.due_date}} |
- {{score.component}} |
- {{score.component.get_category_display}} |
- {{score.value}} |
- {{score.component.grade_total}} |
- {{score.grade_as_percentage}}% |
- Change score |
-
- {% endfor %}
-
-
+
+
{{subject.grouper}}
+
+
+
+ | Due Date |
+ Component |
+ Category |
+ Score |
+ Total |
+ Percentage |
+
+
+
+ {% for score in subject.list %}
+
+
+ | {{score.component.due_date}} |
+ {{score.component}} |
+ {{score.component.get_category_display}} |
+ {{score.value}} |
+ {{score.component.grade_total}} |
+ {{score.grade_as_percentage}}% |
+ Change score |
+
+ {% endfor %}
+
+
+
{% empty %}
No components graded yet.
{% endfor %}
@@ -109,8 +112,8 @@
- | Date |
- Status |
+ Date |
+ Status |
diff --git a/src/core/templates/core/student_form.html b/src/core/templates/core/student_form.html
index 534a797..556dbdc 100644
--- a/src/core/templates/core/student_form.html
+++ b/src/core/templates/core/student_form.html
@@ -1,12 +1,29 @@
{% extends 'base.html' %}
+{% load helpers %}
+
+{% block head_title %}Edit Student {{ student.student_id }} | {% endblock head_title %}
+
+{% block breadcrumbs %}
+
+{% endblock breadcrumbs %}
{% block content %}
-Update Student
-
+
+ Update Student
+
+
{% endblock %}
diff --git a/src/core/templates/core/subject_confirm_delete.html b/src/core/templates/core/subject_confirm_delete.html
new file mode 100644
index 0000000..490ee4b
--- /dev/null
+++ b/src/core/templates/core/subject_confirm_delete.html
@@ -0,0 +1,13 @@
+{% extends 'base.html' %}
+
+{% block content %}
+Delete Subject
+
+{% endblock %}
diff --git a/src/core/templates/core/subject_create_form.html b/src/core/templates/core/subject_create_form.html
new file mode 100644
index 0000000..fa330d0
--- /dev/null
+++ b/src/core/templates/core/subject_create_form.html
@@ -0,0 +1,14 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+ + New Subject
+
+
+{% endblock %}
diff --git a/src/core/templates/core/subject_detail.html b/src/core/templates/core/subject_detail.html
new file mode 100644
index 0000000..ce396e1
--- /dev/null
+++ b/src/core/templates/core/subject_detail.html
@@ -0,0 +1,62 @@
+{% extends 'base.html' %}
+{% load helpers %}
+
+{% block head_title %}Subject {{ subject.subject_id }} | {% endblock head_title %}
+
+{% block breadcrumbs %}
+
+{% endblock breadcrumbs %}
+
+{% block content %}
+
+
+
+
+
+
+
+ | Due Date |
+ Description |
+ Category |
+ Grade Total |
+ Avg Score |
+
+
+
+ {% for component in subject.component_set.all %}
+
+ | {{component.due_date}} |
+ {{component.name}} |
+ {{component.get_category_display}} |
+ {{component.grade_total}} |
+ {{component.grade_avg_pre|floatformat:2}} |
+
+ {% empty %}
+
+ | No components yet. |
+
+ {% endfor %}
+
+
+
+
+{% endblock %}
diff --git a/src/core/templates/core/subject_form.html b/src/core/templates/core/subject_form.html
new file mode 100644
index 0000000..0ce4497
--- /dev/null
+++ b/src/core/templates/core/subject_form.html
@@ -0,0 +1,12 @@
+{% extends 'base.html' %}
+
+{% block content %}
+Update Subject
+
+{% endblock %}
diff --git a/src/core/templates/core/subject_list.html b/src/core/templates/core/subject_list.html
new file mode 100644
index 0000000..173275b
--- /dev/null
+++ b/src/core/templates/core/subject_list.html
@@ -0,0 +1,48 @@
+{% extends 'base.html' %}
+{% load static %}
+
+{% block head_title %}Subjects | {% endblock head_title %}
+
+{% block breadcrumbs %}
+
+{% endblock breadcrumbs %}
+
+{% block content %}
+
+
+
+
+
+ | Name |
+ Components |
+
+
+
+ {% for subject in subject_list %}
+
+
+ {{ subject.name }}
+ {{ subject.description }}
+ |
+ (componenet count) |
+
+ {% empty %}
+
+ | No subjects yet. |
+
+ {% endfor %}
+
+
+ {% include 'core/partials/pagination.html' %}
+
+{% endblock %}
diff --git a/src/core/urls.py b/src/core/urls.py
index 650e8fb..a4a811e 100644
--- a/src/core/urls.py
+++ b/src/core/urls.py
@@ -38,7 +38,7 @@ urlpatterns = [
views.StudentCreateView.as_view(),
name='student-create'
),
- path('/', include([
+ path('/', include([
path(
'',
views.StudentDetailView.as_view(),
@@ -56,6 +56,104 @@ urlpatterns = [
),
])),
])),
+
+ # Subjects
+ path('subjects/', include([
+ path(
+ '',
+ views.SubjectListView.as_view(),
+ name='subject-list'
+ ),
+ path(
+ 'new/',
+ views.SubjectCreateView.as_view(),
+ name='subject-create'
+ ),
+ path('/', include([
+ path(
+ '',
+ views.SubjectDetailView.as_view(),
+ name='subject-detail'
+ ),
+ path(
+ 'update/',
+ views.SubjectUpdateView.as_view(),
+ name='subject-update'
+ ),
+ path(
+ 'delete/',
+ views.SubjectDeleteView.as_view(),
+ name='subject-delete'
+ ),
+
+ # Components
+ path('components/', include([
+ path(
+ '',
+ views.ComponentListView.as_view(),
+ name='component-list'
+ ),
+ path(
+ 'new/',
+ views.ComponentCreateView.as_view(),
+ name='component-create'
+ ),
+ path('/', include([
+ path(
+ '',
+ views.ComponentDetailView.as_view(),
+ name='component-detail'
+ ),
+ path(
+ 'update/',
+ views.ComponentUpdateView.as_view(),
+ name='component-update'
+ ),
+ path(
+ 'delete/',
+ views.ComponentDeleteView.as_view(),
+ name='component-delete'
+ ),
+ path(
+ 'manage/',
+ views.ComponentManagerView.as_view(),
+ name='component-manager'
+ ),
+ ])),
+ ])),
+ ])),
+ ])),
+
+ # Scores
+ path('scores/', include([
+ path(
+ '',
+ views.ScoreListView.as_view(),
+ name='score-list'
+ ),
+ path(
+ 'new/',
+ views.ScoreCreateView.as_view(),
+ name='score-create'
+ ),
+ path('/', include([
+ path(
+ '',
+ views.ScoreDetailView.as_view(),
+ name='score-detail'
+ ),
+ path(
+ 'update/',
+ views.ScoreUpdateView.as_view(),
+ name='score-update'
+ ),
+ path(
+ 'delete/',
+ views.ScoreDeleteView.as_view(),
+ name='score-delete'
+ ),
+ ])),
+ ])),
])),
])),
]
diff --git a/src/core/views.py b/src/core/views.py
index 352f73e..346e175 100644
--- a/src/core/views.py
+++ b/src/core/views.py
@@ -33,18 +33,14 @@ from .models import (
)
from .forms import (
- SchoolYearCreateForm
+ SchoolYearCreateForm,
+ ComponentCreateForm
)
class SchoolYearListView(ListView):
model = SchoolYear
-
-
-class SchoolYearDetailView(DetailView):
- model = SchoolYear
- slug_url_kwarg = 'year'
- slug_field = 'year'
+ paginate_by = 10
class SchoolYearCreateView(SuccessMessageMixin, CreateView):
@@ -54,6 +50,12 @@ class SchoolYearCreateView(SuccessMessageMixin, CreateView):
template_name_suffix = '_create_form'
+class SchoolYearDetailView(DetailView):
+ model = SchoolYear
+ slug_url_kwarg = 'year'
+ slug_field = 'year'
+
+
class SchoolYearUpdateView(SuccessMessageMixin, UpdateView):
model = SchoolYear
slug_url_kwarg = 'year'
@@ -80,9 +82,32 @@ class StudentListView(ListView):
return context
+class StudentCreateView(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(DetailView):
model = Student
- pk_url_kwarg = 'student_pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@@ -116,37 +141,261 @@ class StudentDetailView(DetailView):
return context
-class StudentCreateView(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 StudentUpdateView(SuccessMessageMixin, UpdateView):
model = Student
- pk_url_kwarg = 'student_pk'
success_message = 'Student saved.'
fields = '__all__'
class StudentDeleteView(SuccessMessageMixin, DeleteView):
model = Student
- pk_url_kwarg = 'student_pk'
success_message = 'Student deleted.'
success_url = reverse_lazy('student-list')
+
+
+class SubjectListView(ListView):
+ model = Subject
+ paginate_by = 50
+
+ def get_queryset(self):
+ queryset = Subject.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 SubjectCreateView(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(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(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(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(ListView):
+ model = Component
+
+
+class ComponentCreateView(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(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.exclude(
+ score__in=cscores
+ )
+ return context
+
+
+class ComponentUpdateView(SuccessMessageMixin, UpdateView):
+ model = Component
+ pk_url_kwarg = 'component_pk'
+ success_message = 'Component saved.'
+ fields = '__all__'
+
+
+class ComponentDeleteView(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.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(ListView):
+ model = Score
+
+
+class ScoreCreateView(SuccessMessageMixin, CreateView):
+ model = Score
+ success_message = 'Score created.'
+ template_name_suffix = '_create_form'
+ fields = '__all__'
+
+
+class ScoreDetailView(DetailView):
+ model = Score
+
+
+class ScoreUpdateView(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(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
diff --git a/src/indici/settings.py b/src/indici/settings.py
index 3b7ea10..358beee 100644
--- a/src/indici/settings.py
+++ b/src/indici/settings.py
@@ -65,6 +65,7 @@ TEMPLATES = [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
+ 'core.context_processors.current_year',
],
},
},
@@ -146,7 +147,7 @@ STATICFILES_FINDERS = (
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = 'accounts.User'
-LOGIN_REDIRECT_URL = reverse_lazy('student-list')
+LOGIN_REDIRECT_URL = reverse_lazy('core:schoolyear-list')
# Decimal settings
DEFAULT_DECIMAL_PLACES = 2
diff --git a/src/static/styles/main.css b/src/static/styles/main.css
index c825ea9..aeb1c06 100644
--- a/src/static/styles/main.css
+++ b/src/static/styles/main.css
@@ -136,7 +136,7 @@ thead a {
color: inherit;
text-decoration: none;
}
-tr:nth-child(even) {
+tbody tr:nth-child(even) {
background-color: var(--color-light-gray);
}
th, td {
@@ -159,6 +159,11 @@ tbody tr.has-link {
cursor: pointer;
}
+td h5 {
+ margin-bottom: 0;
+ line-height: 1.5;
+}
+
/* Forms
========================================================================== */
form {
@@ -198,6 +203,7 @@ input[type=search]:focus {
}
button,
+input[type=submit],
.action-button {
cursor: pointer;
border: none;
@@ -211,6 +217,10 @@ button,
border-radius: 0.5rem;
}
+.action-delete {
+ background-color: var(--color-danger);
+}
+
form progress {
display: none;
}
@@ -367,6 +377,10 @@ article > header {
margin-bottom: 1.5rem;
}
+article > section:not(:last-child) {
+ margin-bottom: 4rem;
+}
+
/* List
========================================================================== */
@@ -409,6 +423,16 @@ article > header {
/*justify-self: end;*/
}
+
+/* Form
+ ========================================================================== */
+.form__header {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+}
+
+
/* Breadcrumbs
========================================================================== */
.breadcrumbs {
@@ -462,8 +486,41 @@ article > header {
background-color: var(--color-yellow);
}
-.tool *:last-child {
+.tool > *:last-child {
justify-self: end;
align-self: end;
font-size: 2rem;
}
+
+/* SchoolYear
+ ========================================================================== */
+.schoolyears__list {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 2rem;
+}
+
+.schoolyear {
+ display: grid;
+ grid-template-rows: repeat(3, auto);
+ min-height: 4rem;
+ border: var(--default-border);
+ padding: 1rem;
+ border-radius: 0.5rem;
+ text-decoration: none;
+ color: var(--color-primary);
+}
+
+.schoolyear:hover {
+ background-color: var(--color-yellow);
+}
+
+.schoolyear > *:last-child {
+ justify-self: end;
+ align-self: end;
+ font-size: 2rem;
+}
+
+.student__component {
+ margin-bottom: 2rem;
+}
diff --git a/src/templates/base.html b/src/templates/base.html
index b967abf..99562cb 100644
--- a/src/templates/base.html
+++ b/src/templates/base.html
@@ -29,11 +29,11 @@
{% endif %}>