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',
|
'student_id': 'Student ID',
|
||||||
'dob': 'DOB',
|
'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 (
|
from django.contrib.contenttypes.fields import (
|
||||||
GenericForeignKey, GenericRelation
|
GenericForeignKey, GenericRelation
|
||||||
)
|
)
|
||||||
|
from django.db.models import (
|
||||||
|
Exists, OuterRef, Prefetch, Subquery, Count, Sum, Avg, F, Q, Value
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SchoolYear(models.Model):
|
class SchoolYear(models.Model):
|
||||||
@ -89,7 +92,7 @@ class Student(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('core:student-detail', kwargs={
|
return reverse('core:student-detail', kwargs={
|
||||||
'year': self.school_year.year,
|
'year': self.school_year.year,
|
||||||
'student_pk': self.pk
|
'pk': self.pk
|
||||||
})
|
})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -114,7 +117,10 @@ class Subject(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_absolute_url(self):
|
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:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
@ -180,7 +186,11 @@ class Component(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_absolute_url(self):
|
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:
|
class Meta:
|
||||||
ordering = ['due_date']
|
ordering = ['due_date']
|
||||||
@ -203,7 +213,11 @@ class Score(models.Model):
|
|||||||
return f'{self.student} scored: {self.value} / {self.component.grade_total}'
|
return f'{self.student} scored: {self.value} / {self.component.grade_total}'
|
||||||
|
|
||||||
def get_absolute_url(self):
|
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:
|
class Meta:
|
||||||
ordering = ['student']
|
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>
|
<a href="{% url 'core:schoolyear-update' schoolyear.year %}" class="action-button">Edit</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</header>
|
</header>
|
||||||
<div class="tools">
|
<section class="tools">
|
||||||
<a href="{% url 'core:student-list' schoolyear.year %}" class="tool">
|
<a href="{% url 'core:student-list' schoolyear.year %}" class="tool">
|
||||||
<h3>Students</h3>
|
<h3>Students</h3>
|
||||||
<p>Tool Description</p>
|
<p>Tool Description</p>
|
||||||
@ -24,11 +24,11 @@
|
|||||||
<p>Tool Description</p>
|
<p>Tool Description</p>
|
||||||
<span>→</span>
|
<span>→</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="" class="tool">
|
<a href="{% url 'core:subject-list' schoolyear.year %}" class="tool">
|
||||||
<h3>Subjects</h3>
|
<h3>Subjects</h3>
|
||||||
<p>Tool Description</p>
|
<p>Tool Description</p>
|
||||||
<span>→</span>
|
<span>→</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -14,32 +14,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<table class="schoolyears list__table">
|
<section class="schoolyears__list">
|
||||||
<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 %}
|
{% for schoolyear in schoolyear_list %}
|
||||||
<tr class="schoolyear has-link" onclick="document.location='{% url 'core:schoolyear-detail' schoolyear.year %}'">
|
<a href="{% url 'core:schoolyear-detail' schoolyear.year %}" class="schoolyear">
|
||||||
<td>{{ schoolyear.created_at|date:'m/d/Y' }}</td>
|
<h2>{{ schoolyear.year }}</h2>
|
||||||
<td><h5>{{ schoolyear.year }}</h5></td>
|
<h5>Students: ()   Subjects: ()</h5>
|
||||||
<td></td>
|
<span>→</span>
|
||||||
<td></td>
|
</a>
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr>
|
<h3>No school years yet</h3>
|
||||||
<td colspan="4">No schoolyears yet.</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</section>
|
||||||
</table>
|
|
||||||
{% include 'core/partials/pagination.html' %}
|
{% include 'core/partials/pagination.html' %}
|
||||||
</article>
|
</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>
|
</div>
|
||||||
<a href="{% url 'core:student-update' student.school_year.year student.pk %}" class="action-button">Edit</a>
|
<a href="{% url 'core:student-update' student.school_year.year student.pk %}" class="action-button">Edit</a>
|
||||||
</header>
|
</header>
|
||||||
<section class="student__details">
|
<section class="detail__info">
|
||||||
<dl>
|
<dl class="detail__datalist">
|
||||||
{% if student.allergies %}
|
{% if student.allergies %}
|
||||||
<dt>Allergies</dt>
|
<dt>Allergies</dt>
|
||||||
<dd>{{ student.allergies }}</dd>
|
<dd>{{ student.allergies }}</dd>
|
||||||
@ -51,7 +51,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for subject in subject_list %}
|
{% for subject in subject_list %}
|
||||||
<tr>
|
<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>
|
<td class="grade">{{subject.grade|grade_as_percentage:subject.grade_total}}%</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
@ -65,26 +65,28 @@
|
|||||||
<section>
|
<section>
|
||||||
<h2>Components</h2>
|
<h2>Components</h2>
|
||||||
|
|
||||||
<div>
|
<div class="student__components">
|
||||||
{% regroup score_list by component.subject as score_list %}
|
{% regroup score_list by component.subject as score_list %}
|
||||||
{% for subject in score_list %}
|
{% for subject in score_list %}
|
||||||
|
<div class="student__component">
|
||||||
<h4>{{subject.grouper}}</h4>
|
<h4>{{subject.grouper}}</h4>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Due Date</td>
|
<th>Due Date</th>
|
||||||
<td>Component</td>
|
<th>Component</th>
|
||||||
<td>Category</td>
|
<th>Category</th>
|
||||||
<td>Score</td>
|
<th>Score</th>
|
||||||
<td>Total</td>
|
<th>Total</th>
|
||||||
<td colspan="2">Percentage</td>
|
<th colspan="2">Percentage</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for score in subject.list %}
|
{% for score in subject.list %}
|
||||||
|
<tr class="subject has-link" onclick="document.location='{% url 'component-detail' score.component.subject.pk score.component.pk %}'">
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{score.component.due_date}}</td>
|
<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.component.get_category_display}}</td>
|
||||||
<td>{{score.value}}</td>
|
<td>{{score.value}}</td>
|
||||||
<td>{{score.component.grade_total}}</td>
|
<td>{{score.component.grade_total}}</td>
|
||||||
@ -94,6 +96,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<p>No components graded yet.</p>
|
<p>No components graded yet.</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -109,8 +112,8 @@
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Date</td>
|
<th>Date</th>
|
||||||
<td colspan="2">Status</td>
|
<th colspan="2">Status</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@ -1,12 +1,29 @@
|
|||||||
{% extends 'base.html' %}
|
{% 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 %}
|
{% block content %}
|
||||||
|
<article class="form">
|
||||||
<h1>Update Student</h1>
|
<h1>Update Student</h1>
|
||||||
<form method="POST" action="{% url 'core:student-update' student.pk %}">
|
<form method="POST" action="{% url 'core:student-update' student.school_year.year student.pk %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<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>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
</article>
|
||||||
{% endblock %}
|
{% 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(),
|
views.StudentCreateView.as_view(),
|
||||||
name='student-create'
|
name='student-create'
|
||||||
),
|
),
|
||||||
path('<int:student_pk>/', include([
|
path('<int:pk>/', include([
|
||||||
path(
|
path(
|
||||||
'',
|
'',
|
||||||
views.StudentDetailView.as_view(),
|
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 (
|
from .forms import (
|
||||||
SchoolYearCreateForm
|
SchoolYearCreateForm,
|
||||||
|
ComponentCreateForm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SchoolYearListView(ListView):
|
class SchoolYearListView(ListView):
|
||||||
model = SchoolYear
|
model = SchoolYear
|
||||||
|
paginate_by = 10
|
||||||
|
|
||||||
class SchoolYearDetailView(DetailView):
|
|
||||||
model = SchoolYear
|
|
||||||
slug_url_kwarg = 'year'
|
|
||||||
slug_field = 'year'
|
|
||||||
|
|
||||||
|
|
||||||
class SchoolYearCreateView(SuccessMessageMixin, CreateView):
|
class SchoolYearCreateView(SuccessMessageMixin, CreateView):
|
||||||
@ -54,6 +50,12 @@ class SchoolYearCreateView(SuccessMessageMixin, CreateView):
|
|||||||
template_name_suffix = '_create_form'
|
template_name_suffix = '_create_form'
|
||||||
|
|
||||||
|
|
||||||
|
class SchoolYearDetailView(DetailView):
|
||||||
|
model = SchoolYear
|
||||||
|
slug_url_kwarg = 'year'
|
||||||
|
slug_field = 'year'
|
||||||
|
|
||||||
|
|
||||||
class SchoolYearUpdateView(SuccessMessageMixin, UpdateView):
|
class SchoolYearUpdateView(SuccessMessageMixin, UpdateView):
|
||||||
model = SchoolYear
|
model = SchoolYear
|
||||||
slug_url_kwarg = 'year'
|
slug_url_kwarg = 'year'
|
||||||
@ -80,9 +82,32 @@ class StudentListView(ListView):
|
|||||||
return context
|
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):
|
class StudentDetailView(DetailView):
|
||||||
model = Student
|
model = Student
|
||||||
pk_url_kwarg = 'student_pk'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
@ -116,37 +141,261 @@ class StudentDetailView(DetailView):
|
|||||||
return context
|
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):
|
class StudentUpdateView(SuccessMessageMixin, UpdateView):
|
||||||
model = Student
|
model = Student
|
||||||
pk_url_kwarg = 'student_pk'
|
|
||||||
success_message = 'Student saved.'
|
success_message = 'Student saved.'
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
class StudentDeleteView(SuccessMessageMixin, DeleteView):
|
class StudentDeleteView(SuccessMessageMixin, DeleteView):
|
||||||
model = Student
|
model = Student
|
||||||
pk_url_kwarg = 'student_pk'
|
|
||||||
success_message = 'Student deleted.'
|
success_message = 'Student deleted.'
|
||||||
success_url = reverse_lazy('student-list')
|
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.template.context_processors.request',
|
||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
'core.context_processors.current_year',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -146,7 +147,7 @@ STATICFILES_FINDERS = (
|
|||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
AUTH_USER_MODEL = 'accounts.User'
|
AUTH_USER_MODEL = 'accounts.User'
|
||||||
LOGIN_REDIRECT_URL = reverse_lazy('student-list')
|
LOGIN_REDIRECT_URL = reverse_lazy('core:schoolyear-list')
|
||||||
|
|
||||||
# Decimal settings
|
# Decimal settings
|
||||||
DEFAULT_DECIMAL_PLACES = 2
|
DEFAULT_DECIMAL_PLACES = 2
|
||||||
|
|||||||
@ -136,7 +136,7 @@ thead a {
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
tr:nth-child(even) {
|
tbody tr:nth-child(even) {
|
||||||
background-color: var(--color-light-gray);
|
background-color: var(--color-light-gray);
|
||||||
}
|
}
|
||||||
th, td {
|
th, td {
|
||||||
@ -159,6 +159,11 @@ tbody tr.has-link {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td h5 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
/* Forms
|
/* Forms
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
form {
|
form {
|
||||||
@ -198,6 +203,7 @@ input[type=search]:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
button,
|
button,
|
||||||
|
input[type=submit],
|
||||||
.action-button {
|
.action-button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: none;
|
border: none;
|
||||||
@ -211,6 +217,10 @@ button,
|
|||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-delete {
|
||||||
|
background-color: var(--color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
form progress {
|
form progress {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -367,6 +377,10 @@ article > header {
|
|||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
article > section:not(:last-child) {
|
||||||
|
margin-bottom: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* List
|
/* List
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
|
|
||||||
@ -409,6 +423,16 @@ article > header {
|
|||||||
/*justify-self: end;*/
|
/*justify-self: end;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Form
|
||||||
|
========================================================================== */
|
||||||
|
.form__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Breadcrumbs
|
/* Breadcrumbs
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
.breadcrumbs {
|
.breadcrumbs {
|
||||||
@ -462,8 +486,41 @@ article > header {
|
|||||||
background-color: var(--color-yellow);
|
background-color: var(--color-yellow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool *:last-child {
|
.tool > *:last-child {
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
align-self: end;
|
align-self: end;
|
||||||
font-size: 2rem;
|
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 %}>
|
{% endif %}>
|
||||||
</form>
|
</form>
|
||||||
<menu>
|
<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 '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 'day-list' %}">Attendance</a></li>
|
||||||
<li><a href="{% url 'subject-list' %}">Curricula</a></li>
|
<li><a href="{% url 'core:subject-list' current_year.year %}">Subjects</a></li>
|
||||||
<li><a href="{% url 'thread-list' %}">Communications</a></li>
|
|
||||||
</menu>
|
</menu>
|
||||||
<menu class="nav__account">
|
<menu class="nav__account">
|
||||||
<li><a href="{% url 'account-detail' user.pk %}">Profile</a></li>
|
<li><a href="{% url 'account-detail' user.pk %}">Profile</a></li>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user