Add base system
This commit is contained in:
parent
cd502df19a
commit
4c477ac16c
8
src/core/context_processors.py
Normal file
8
src/core/context_processors.py
Normal 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')
|
||||
}
|
||||
@ -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'}),
|
||||
}
|
||||
|
||||
@ -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']
|
||||
|
||||
13
src/core/templates/core/component_confirm_delete.html
Normal file
13
src/core/templates/core/component_confirm_delete.html
Normal 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 %}
|
||||
28
src/core/templates/core/component_create_form.html
Normal file
28
src/core/templates/core/component_create_form.html
Normal 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 %}
|
||||
88
src/core/templates/core/component_detail.html
Normal file
88
src/core/templates/core/component_detail.html
Normal 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}}   -   {{ component.get_category_display }}{% if component.finished_grading %}   —   <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 →</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}} — {{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 %}
|
||||
12
src/core/templates/core/component_form.html
Normal file
12
src/core/templates/core/component_form.html
Normal 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 %}
|
||||
16
src/core/templates/core/component_list.html
Normal file
16
src/core/templates/core/component_list.html
Normal 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 %}
|
||||
56
src/core/templates/core/component_manager.html
Normal file
56
src/core/templates/core/component_manager.html
Normal 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}}">
|
||||
  /   {{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 %}
|
||||
@ -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>→</span>
|
||||
</a>
|
||||
<a href="" class="tool">
|
||||
<a href="{% url 'core:subject-list' schoolyear.year %}" class="tool">
|
||||
<h3>Subjects</h3>
|
||||
<p>Tool Description</p>
|
||||
<span>→</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
|
||||
@ -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">
|
||||
{% 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>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="4">No schoolyears yet.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<section class="schoolyears__list">
|
||||
{% for schoolyear in schoolyear_list %}
|
||||
<a href="{% url 'core:schoolyear-detail' schoolyear.year %}" class="schoolyear">
|
||||
<h2>{{ schoolyear.year }}</h2>
|
||||
<h5>Students: ()   Subjects: ()</h5>
|
||||
<span>→</span>
|
||||
</a>
|
||||
{% empty %}
|
||||
<h3>No school years yet</h3>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% include 'core/partials/pagination.html' %}
|
||||
</article>
|
||||
|
||||
|
||||
31
src/core/templates/core/score_confirm_delete.html
Normal file
31
src/core/templates/core/score_confirm_delete.html
Normal 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 %}
|
||||
12
src/core/templates/core/score_create_form.html
Normal file
12
src/core/templates/core/score_create_form.html
Normal 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 %}
|
||||
10
src/core/templates/core/score_detail.html
Normal file
10
src/core/templates/core/score_detail.html
Normal 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 %}
|
||||
40
src/core/templates/core/score_form.html
Normal file
40
src/core/templates/core/score_form.html
Normal 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}}— <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 %}
|
||||
16
src/core/templates/core/score_list.html
Normal file
16
src/core/templates/core/score_list.html
Normal 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 %}
|
||||
@ -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,35 +65,38 @@
|
||||
<section>
|
||||
<h2>Components</h2>
|
||||
|
||||
<div>
|
||||
<div class="student__components">
|
||||
{% regroup score_list by component.subject as score_list %}
|
||||
{% for subject in score_list %}
|
||||
<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>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for score in subject.list %}
|
||||
<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.get_category_display}}</td>
|
||||
<td>{{score.value}}</td>
|
||||
<td>{{score.component.grade_total}}</td>
|
||||
<td>{{score.grade_as_percentage}}%</td>
|
||||
<td><a href="{% url 'score-update' score.pk %}?return_to={{request.get_full_path}}">Change score</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="student__component">
|
||||
<h4>{{subject.grouper}}</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<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>{{score.component}}</td>
|
||||
<td>{{score.component.get_category_display}}</td>
|
||||
<td>{{score.value}}</td>
|
||||
<td>{{score.component.grade_total}}</td>
|
||||
<td>{{score.grade_as_percentage}}%</td>
|
||||
<td><a href="{% url 'score-update' score.pk %}?return_to={{request.get_full_path}}">Change score</a></td>
|
||||
</tr>
|
||||
{% 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>
|
||||
|
||||
@ -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 %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Save changes"> or <a href="{% url 'core:student-detail' student.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
<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.school_year.year student.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
|
||||
13
src/core/templates/core/subject_confirm_delete.html
Normal file
13
src/core/templates/core/subject_confirm_delete.html
Normal 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 %}
|
||||
14
src/core/templates/core/subject_create_form.html
Normal file
14
src/core/templates/core/subject_create_form.html
Normal 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 %}
|
||||
62
src/core/templates/core/subject_detail.html
Normal file
62
src/core/templates/core/subject_detail.html
Normal 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 %}
|
||||
12
src/core/templates/core/subject_form.html
Normal file
12
src/core/templates/core/subject_form.html
Normal 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 %}
|
||||
48
src/core/templates/core/subject_list.html
Normal file
48
src/core/templates/core/subject_list.html
Normal 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 →</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 %}
|
||||
100
src/core/urls.py
100
src/core/urls.py
@ -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'
|
||||
),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
]
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user