Add base system

This commit is contained in:
Nathan Chapman 2022-07-01 14:13:37 -06:00
parent cd502df19a
commit 4c477ac16c
28 changed files with 1029 additions and 112 deletions

View File

@ -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')
}

View File

@ -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'}),
}

View File

@ -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']

View File

@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block content %}
<h1>Delete Component</h1>
<form method="POST" action="{% url 'core:component-delete' component.pk %}">
{% csrf_token %}
<p>Are you sure you want to delete "{{ component }}"?</p>
{{ form.as_p }}
<p>
<input type="submit" value="Delete"> or <a href="{% url 'core:component-detail' component.pk %}">cancel</a>
</p>
</form>
{% endblock %}

View File

@ -0,0 +1,28 @@
{% extends 'base.html' %}
{% block head_title %}New component | {% endblock head_title %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<menu>
<li><strong><a href="{% url 'core:schoolyear-detail' subject.school_year.year %}">{{ subject.school_year.year }}</a></strong></li>
<span></span>
<li><a href="{% url 'core:subject-list' subject.school_year.year %}">Subjects</a></li>
<span></span>
<li><a href="{% url 'core:subject-detail' subject.school_year.year subject.pk %}">{{ subject.name }}</a></li>
</menu>
</div>
{% endblock breadcrumbs %}
{% block content %}
<article class="form">
<h1>+ New Component</h1>
<form method="POST" action="{% url 'core:component-create' school_year.year subject.pk %}">
{% csrf_token %}
{{ form.as_p }}
<p>
<input type="submit" value="Create"> or <a href="{% url 'core:subject-detail' school_year.year subject.pk %}">cancel</a>
</p>
</form>
</article>
{% endblock %}

View File

@ -0,0 +1,88 @@
{% extends "base.html" %}
{% load helpers %}
{% block head_title %}{{ component.name }} | {% endblock head_title %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<menu>
<li><strong><a href="{% url 'core:schoolyear-detail' component.subject.school_year.year %}">{{ component.subject.school_year.year }}</a></strong></li>
<span></span>
<li><a href="{% url 'core:subject-list' component.subject.school_year.year %}">Subjects</a></li>
<span></span>
<li><a href="{% url 'core:subject-detail' component.subject.school_year.year component.subject.pk %}">{{ component.subject.name }}</a></li>
</menu>
</div>
{% endblock breadcrumbs %}
{% block content %}
<article class="detail">
<header class="detail__header">
<div>
<h1>{{component.name}} &ensp; - &ensp; {{ component.get_category_display }}{% if component.finished_grading %} &ensp; &mdash; &ensp; <span class="tag tag__success">✓ Graded</span>{% endif %}</h1>
<p><small>Added: <time datetime="{{ component.created_at|date:'Y-m-d' }}">{{ component.created_at|date:'M d Y' }}</time><br>
Last updated: <time datetime="{{ component.updated_at|date:'Y-m-d' }}">{{ component.updated_at|date:'M d Y' }}</time></small></p>
</div>
<a href="{% url 'component-update' component.subject.pk component.pk %}" class="action-button">Edit</a>
</header>
{% if component.tags.count > 0 %}
<section>
<span>
{% for tag in component.tags.all %}
<a class="tag__item" href="{% url 'tag-detail' tag.pk %}">{{tag.name}}</a>
{% endfor %}
</span>
</section>
{% endif %}
<section class="detail__info">
<dl class="detail__datalist">
<dt>Due Date</dt>
<dd>{{component.due_date}}</dd>
<dt>Category</dt>
<dd>{{component.get_category_display}}</dd>
<dt>Grade Total</dt>
<dd>{{component.grade_total}}</dd>
</dl>
</section>
<section>
<header class="list__header">
<div class="list__title">
<h2>Scores</h2>
<a class="action-button" href="{% url 'core:component-manager' component.subject.school_year.year component.subject.pk component.pk %}">Enter Scores &rarr;</a>
</div>
</header>
<table>
<thead>
<tr>
<th>Student</th>
<th colspan="2">Score</th>
</tr>
</thead>
<tbody>
{% for score in component.score_set.all %}
<tr>
<td><a href="{% url 'core:student-detail' component.subject.school_year.year score.student.pk %}">{{score.student.student_id}} &mdash; {{score.student}}</a></td>
<td>{{score.value}} / {{component.grade_total}}</td>
<td><a href="{% url 'core:score-update' component.subject.school_year.year score.pk %}">Edit score</a></td>
</tr>
{% endfor %}
<tr>
<td><strong>Avg. Score</strong></td>
<td colspan="2"><strong>{{component.grade_avg_pre|floatformat:2}} / {{component.grade_total}}</strong></td>
</tr>
</tbody>
</table>
</section>
{% if scoreless %}
<section>
<h3>Scoreless</h3>
<ul>
{% for student in scoreless %}
<li>{{student}}</li>
{% endfor %}
</ul>
</section>
{% endif %}
</article>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<h1>Update Component</h1>
<form method="POST" action="{% url 'core:component-update' component.pk %}">
{% csrf_token %}
{{ form.as_p }}
<p>
<input type="submit" value="Save changes"> or <a href="{% url 'core:component-detail' component.pk %}">cancel</a>
</p>
</form>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% block content %}
<h1>Components</h1>
<table>
<tbody>
{% for component in component_list %}
<tr>
<dt>{{ component }}</dt>
</tr>
{% empty %}
<tr>No components yet.</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1,56 @@
{% extends "base.html" %}
{% load helpers %}
{% block head_title %}Enter Scores {{ component.name }} | {% endblock head_title %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<menu>
<li><strong><a href="{% url 'core:schoolyear-detail' component.subject.school_year.year %}">{{ component.subject.school_year.year }}</a></strong></li>
<span></span>
<li><a href="{% url 'core:subject-list' component.subject.school_year.year %}">Subjects</a></li>
<span></span>
<li><a href="{% url 'core:subject-detail' component.subject.school_year.year component.subject.pk %}">{{ component.subject.name }}</a></li>
<span></span>
<li><a href="{% url 'core:component-detail' component.subject.school_year.year component.subject.pk component.pk %}">{{ component.name }}</a></li>
</menu>
</div>
{% endblock breadcrumbs %}
{% block content %}
<article class="form">
<h1>{{component}}</h1>
<section>
<form action="{% url 'core:component-manager' component.subject.school_year.year component.subject.pk component.pk %}" method="POST">
{% csrf_token %}
<h3>Enter Scores</h3>
<table>
<thead>
<tr>
<th>Student</th>
<th>Score</th>
{% if formset.errors %}
<th>Errors</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for student in student_list %}
<tr>
<td>{{student.full_name}}</td>
<td>
<input type="number" name="student_{{student.pk}}" min="0" max="{{component.grade_total}}" value="{{student.cscore}}">
&ensp; / &ensp; {{component.grade_total}}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{form.as_p}}
<p>
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'core:component-detail' component.subject.school_year.year component.subject.pk component.pk %}">cancel</a>
</p>
</form>
</section>
</article>
{% endblock %}

View File

@ -13,7 +13,7 @@
<a href="{% url 'core:schoolyear-update' schoolyear.year %}" class="action-button">Edit</a>
{% endif %}
</header>
<div class="tools">
<section class="tools">
<a href="{% url 'core:student-list' schoolyear.year %}" class="tool">
<h3>Students</h3>
<p>Tool Description</p>
@ -24,11 +24,11 @@
<p>Tool Description</p>
<span>&rarr;</span>
</a>
<a href="" class="tool">
<a href="{% url 'core:subject-list' schoolyear.year %}" class="tool">
<h3>Subjects</h3>
<p>Tool Description</p>
<span>&rarr;</span>
</a>
</div>
</section>
</article>
{% endblock %}

View File

@ -14,32 +14,17 @@
</div>
</header>
<table class="schoolyears list__table">
<thead>
<tr>
<th>Created</th>
<th>Year</th>
<th>Students</th>
<th>Components</th>
<th>Last Updated</th>
</tr>
</thead>
<tbody class="schoolyears__list">
<section class="schoolyears__list">
{% for schoolyear in schoolyear_list %}
<tr class="schoolyear has-link" onclick="document.location='{% url 'core:schoolyear-detail' schoolyear.year %}'">
<td>{{ schoolyear.created_at|date:'m/d/Y' }}</td>
<td><h5>{{ schoolyear.year }}</h5></td>
<td></td>
<td></td>
<td></td>
</tr>
<a href="{% url 'core:schoolyear-detail' schoolyear.year %}" class="schoolyear">
<h2>{{ schoolyear.year }}</h2>
<h5>Students: () &emsp; Subjects: ()</h5>
<span>&rarr;</span>
</a>
{% empty %}
<tr>
<td colspan="4">No schoolyears yet.</td>
</tr>
<h3>No school years yet</h3>
{% endfor %}
</tbody>
</table>
</section>
{% include 'core/partials/pagination.html' %}
</article>

View File

@ -0,0 +1,31 @@
{% extends "base.html" %}
{% block head_title %}Delete Score {{ score.pk }} | {% endblock head_title %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<menu>
<li><strong><a href="{% url 'core:schoolyear-detail' school_year.year %}">{{ school_year.year }}</a></strong></li>
<span></span>
<li><a href="{% url 'core:subject-list' school_year.year %}">Subjects</a></li>
<span></span>
<li><a href="{% url 'core:subject-detail' school_year.year score.component.subject.pk %}">{{ score.component.subject.name }}</a></li>
<span></span>
<li><a href="{% url 'core:component-detail' school_year.year score.component.subject.pk score.component.pk %}">{{ score.component.name }}</a></li>
</menu>
</div>
{% endblock breadcrumbs %}
{% block content %}
<article class="form">
<h1>Delete Score</h1>
<form method="POST" action="{% url 'core:score-delete' school_year.year score.pk %}">
{% csrf_token %}
<p>Are you sure you want to delete "{{ score }}"?</p>
{{ form.as_p }}
<p>
<input type="submit" value="Delete" class="action-button action-delete"> or <a href="{% url 'core:component-detail' school_year.year score.component.subject.pk score.component.pk %}">cancel</a>
</p>
</form>
</article>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<h1>+ New Score</h1>
<form method="POST" action="{% url 'core:score-create' %}">
{% csrf_token %}
{{ form.as_p }}
<p>
<input type="submit" value="Create"> or <a href="{% url 'core:score-list' %}">cancel</a>
</p>
</form>
{% endblock %}

View File

@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% block content %}
<h1>Score</h1>
<p>
<a href="{% url 'core:score-update' score.pk %}">Edit</a>
<a href="{% url 'core:score-delete' score.pk %}">Delete</a>
</p>
<p>{{ score }}</p>
{% endblock %}

View File

@ -0,0 +1,40 @@
{% extends "base.html" %}
{% block head_title %}Edit score {{ score.pk }} | {% endblock head_title %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<menu>
<li><strong><a href="{% url 'core:schoolyear-detail' school_year.year %}">{{ school_year.year }}</a></strong></li>
<span></span>
<li><a href="{% url 'core:subject-list' school_year.year %}">Subjects</a></li>
<span></span>
<li><a href="{% url 'core:subject-detail' school_year.year score.component.subject.pk %}">{{ score.component.subject.name }}</a></li>
<span></span>
<li><a href="{% url 'core:component-detail' school_year.year score.component.subject.pk score.component.pk %}">{{ score.component.name }}</a></li>
</menu>
</div>
{% endblock breadcrumbs %}
{% block content %}
<article class="form">
<header class="form__header">
<div>
<h1>Update Score</h1>
<h2>{{score.component.subject}}&mdash; <em>{{score.component.get_category_display}}</em>: {{score.component}}</h2>
</div>
<p><a href="{% url 'core:score-delete' school_year.year score.pk %}" class="action-button action-delete">Delete score</a></p>
</header>
<section>
<form action="{% url 'score-update' score.pk %}" method="POST">
{% csrf_token %}
<input type="hidden" name="next" value="{{next}}">
<h3>{{score.student}}</h3>
{{form.as_p}}
<p>
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'student-detail' score.student.pk %}">cancel</a>
</p>
</form>
</section>
</article>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% block content %}
<h1>Scores</h1>
<table>
<tbody>
{% for score in score_list %}
<tr>
<dt>{{ score }}</dt>
</tr>
{% empty %}
<tr>No scores yet.</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -23,8 +23,8 @@
</div>
<a href="{% url 'core:student-update' student.school_year.year student.pk %}" class="action-button">Edit</a>
</header>
<section class="student__details">
<dl>
<section class="detail__info">
<dl class="detail__datalist">
{% if student.allergies %}
<dt>Allergies</dt>
<dd>{{ student.allergies }}</dd>
@ -51,7 +51,7 @@
<tbody>
{% for subject in subject_list %}
<tr>
<td class="grade"><em>{{subject}}</em></td>
<td class="grade"><h5>{{subject}}</h5></td>
<td class="grade">{{subject.grade|grade_as_percentage:subject.grade_total}}%</td>
</tr>
{% empty %}
@ -65,26 +65,28 @@
<section>
<h2>Components</h2>
<div>
<div class="student__components">
{% regroup score_list by component.subject as score_list %}
{% for subject in score_list %}
<div class="student__component">
<h4>{{subject.grouper}}</h4>
<table>
<thead>
<tr>
<td>Due Date</td>
<td>Component</td>
<td>Category</td>
<td>Score</td>
<td>Total</td>
<td colspan="2">Percentage</td>
<th>Due Date</th>
<th>Component</th>
<th>Category</th>
<th>Score</th>
<th>Total</th>
<th colspan="2">Percentage</th>
</tr>
</thead>
<tbody>
{% for score in subject.list %}
<tr class="subject has-link" onclick="document.location='{% url 'component-detail' score.component.subject.pk score.component.pk %}'">
<tr>
<td>{{score.component.due_date}}</td>
<td><a href="{% url 'component-detail' score.component.subject.pk score.component.pk %}">{{score.component}}</a></td>
<td>{{score.component}}</td>
<td>{{score.component.get_category_display}}</td>
<td>{{score.value}}</td>
<td>{{score.component.grade_total}}</td>
@ -94,6 +96,7 @@
{% endfor %}
</tbody>
</table>
</div>
{% empty %}
<p>No components graded yet.</p>
{% endfor %}
@ -109,8 +112,8 @@
<table>
<thead>
<tr>
<td>Date</td>
<td colspan="2">Status</td>
<th>Date</th>
<th colspan="2">Status</th>
</tr>
</thead>
<tbody>

View File

@ -1,12 +1,29 @@
{% extends 'base.html' %}
{% load helpers %}
{% block head_title %}Edit Student {{ student.student_id }} | {% endblock head_title %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<menu>
<li><strong><a href="{% url 'core:schoolyear-detail' student.school_year.year %}">{{ student.school_year.year }}</a></strong></li>
<span></span>
<li><a href="{% url 'core:student-list' student.school_year.year %}">Students</a></li>
<span></span>
<li><a href="{% url 'core:student-detail' student.school_year.year student.pk %}">{{ student.full_name }}</a></li>
</menu>
</div>
{% endblock breadcrumbs %}
{% block content %}
<h1>Update Student</h1>
<form method="POST" action="{% url 'core:student-update' student.pk %}">
<article class="form">
<h1>Update Student</h1>
<form method="POST" action="{% url 'core:student-update' student.school_year.year student.pk %}">
{% csrf_token %}
{{ form.as_p }}
<p>
<input type="submit" value="Save changes"> or <a href="{% url 'core:student-detail' student.pk %}">cancel</a>
<input type="submit" value="Save changes"> or <a href="{% url 'core:student-detail' student.school_year.year student.pk %}">cancel</a>
</p>
</form>
</form>
</article>
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block content %}
<h1>Delete Subject</h1>
<form method="POST" action="{% url 'core:subject-delete' subject.pk %}">
{% csrf_token %}
<p>Are you sure you want to delete "{{ subject }}"?</p>
{{ form.as_p }}
<p>
<input type="submit" value="Delete"> or <a href="{% url 'core:subject-detail' subject.pk %}">cancel</a>
</p>
</form>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends 'base.html' %}
{% block content %}
<article class="form">
<h1>+ New Subject</h1>
<form method="POST" action="{% url 'core:subject-create' school_year.year %}">
{% csrf_token %}
{{ form.as_p }}
<p>
<input type="submit" value="Create"> or <a href="{% url 'core:subject-list' school_year.year %}">cancel</a>
</p>
</form>
</article>
{% endblock %}

View File

@ -0,0 +1,62 @@
{% extends 'base.html' %}
{% load helpers %}
{% block head_title %}Subject {{ subject.subject_id }} | {% endblock head_title %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<menu>
<li><strong><a href="{% url 'core:schoolyear-detail' subject.school_year.year %}">{{ subject.school_year.year }}</a></strong></li>
<span></span>
<li><a href="{% url 'core:subject-list' subject.school_year.year %}">Subjects</a></li>
</menu>
</div>
{% endblock breadcrumbs %}
{% block content %}
<article class="detail">
<header class="detail__header">
<div>
<h1>{{ subject.name }}</h1>
<h5>{{ subject.description }}</h5>
<p><small>Added: <time datetime="{{ subject.created_at|date:'Y-m-d' }}">{{ subject.created_at|date:'M d Y' }}</time><br>
Last updated: <time datetime="{{ subject.updated_at|date:'Y-m-d' }}">{{ subject.updated_at|date:'M d Y' }}</time></small></p>
</div>
<a href="{% url 'core:subject-update' subject.school_year.year subject.pk %}" class="action-button">Edit</a>
</header>
<section>
<header class="list__header">
<div class="list__title">
<h3>Components</h3>
<a href="{% url 'core:component-create' subject.school_year.year subject.pk %}" class="action-button">+ New component</a>
</div>
</header>
<table>
<thead>
<tr>
<td>Due Date</td>
<td>Description</td>
<td>Category</td>
<td>Grade Total</td>
<td>Avg Score</td>
</tr>
</thead>
<tbody>
{% for component in subject.component_set.all %}
<tr class="component has-link" onclick="document.location='{% url 'core:component-detail' subject.school_year.year subject.pk component.pk %}'">
<td>{{component.due_date}}</td>
<td>{{component.name}}</td>
<td>{{component.get_category_display}}</td>
<td>{{component.grade_total}}</td>
<td>{{component.grade_avg_pre|floatformat:2}}</td>
</tr>
{% empty %}
<tr>
<td colspan="5">No components yet.</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
</article>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<h1>Update Subject</h1>
<form method="POST" action="{% url 'core:subject-update' subject.pk %}">
{% csrf_token %}
{{ form.as_p }}
<p>
<input type="submit" value="Save changes"> or <a href="{% url 'core:subject-detail' subject.pk %}">cancel</a>
</p>
</form>
{% endblock %}

View File

@ -0,0 +1,48 @@
{% extends 'base.html' %}
{% load static %}
{% block head_title %}Subjects | {% endblock head_title %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<menu>
<li><strong><a href="{% url 'core:schoolyear-detail' school_year.year %}">{{ school_year.year }}</a></strong></li>
</menu>
</div>
{% endblock breadcrumbs %}
{% block content %}
<article class="list">
<header class="list__header">
<div class="list__title">
<h1>Subjects</h1>
<a href="{% url 'core:subject-create' school_year.year %}" class="action-button">+ New Subject</a>
</div>
<a href="">Subject Tags &rarr;</a>
</header>
<table class="list__table">
<thead>
<tr>
<th>Name</th>
<th>Components</th>
</tr>
</thead>
<tbody>
{% for subject in subject_list %}
<tr class="subject has-link" onclick="document.location='{% url 'core:subject-detail' school_year.year subject.pk %}'">
<td>
<h5>{{ subject.name }}</h5>
<small>{{ subject.description }}</small>
</td>
<td>(componenet count)</td>
</tr>
{% empty %}
<tr>
<td colspan="3">No subjects yet.</td>
</tr>
{% endfor %}
</tbody>
</table>
{% include 'core/partials/pagination.html' %}
</article>
{% endblock %}

View File

@ -38,7 +38,7 @@ urlpatterns = [
views.StudentCreateView.as_view(),
name='student-create'
),
path('<int:student_pk>/', include([
path('<int:pk>/', 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('<int:pk>/', 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('<int:component_pk>/', 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('<int:pk>/', 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'
),
])),
])),
])),
])),
]

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -29,11 +29,11 @@
{% endif %}>
</form>
<menu>
<li><a href="{% url 'core:schoolyear-list' %}">Years</a></li>
<li><a href="{% url 'account-detail' user.pk %}">Today</a></li>
<li><a href="{% url 'student-list' %}">Students</a></li>
<li><a href="{% url 'core:student-list' current_year.year %}">Students</a></li>
<li><a href="{% url 'day-list' %}">Attendance</a></li>
<li><a href="{% url 'subject-list' %}">Curricula</a></li>
<li><a href="{% url 'thread-list' %}">Communications</a></li>
<li><a href="{% url 'core:subject-list' current_year.year %}">Subjects</a></li>
</menu>
<menu class="nav__account">
<li><a href="{% url 'account-detail' user.pk %}">Profile</a></li>