Merge branch 'release/2.0.0'
This commit is contained in:
commit
a1e7da1d99
@ -25,3 +25,9 @@ class User(AbstractUser):
|
||||
choices=TIMEZONE_CHOICES,
|
||||
default=MOUNTAIN
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
if self.first_name and self.last_name:
|
||||
return f'{self.first_name} {self.last_name}'
|
||||
else:
|
||||
return self.username
|
||||
|
||||
@ -1,72 +1,23 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block head_title %}Profile |{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<header>
|
||||
<h1 class="greeting"><em>Welcome {{user.first_name}} {{user.last_name }}</em>
|
||||
<br>
|
||||
Here's what's going on today
|
||||
</h1>
|
||||
<article class="detail">
|
||||
<header class="detail__header">
|
||||
<div>
|
||||
<h1>{{ user }}</h1>
|
||||
<p><small>Joined: <time datetime="{{ user.date_joined|date:'Y-m-d' }}">{{ user.date_joined|date:'M d Y' }}</time></p>
|
||||
</div>
|
||||
<a href="{% url 'account-update' user.pk %}" class="action-button">Edit</a>
|
||||
</header>
|
||||
<section>
|
||||
<h3 class="domain__heading">Birthdays</h3>
|
||||
<ul>
|
||||
{% for student in birthdays %}
|
||||
<li><strong><a href="{% url 'student-detail' student.pk %}">{{student}}</a></strong> is turning {{student.age|add:1}} on {{student.dob|date:"M j"}}</li>
|
||||
{% empty %}
|
||||
<p>No Birthdays this next week.</p>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h3 class="domain__heading">Today's Assignments</h3>
|
||||
<ul>
|
||||
{% for component in components %}
|
||||
<li>
|
||||
{{component.subject}}, <a href="{% url 'component-detail' component.subject.pk component.pk %}">{{component}}</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<p>Nothing for today.</p>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h3 class="domain__heading">Today's Attendance</h3>
|
||||
{% for day in attendance %}
|
||||
<p><strong><a href="{% url 'day-update' day.pk %}">{{day.date}}</a></strong></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Student</td>
|
||||
<td colspan="2">Status</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in day.entry_set.all %}
|
||||
<tr>
|
||||
<td>{{entry.student}}</td>
|
||||
<td>{{entry.get_status_display}}</td>
|
||||
<td><a href="{% url 'entry-update' entry.pk %}">Update</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% empty %}
|
||||
<p class="greeting">No attendance taken yet: <a href="{% url 'day-create' %}" class="action-button">Take attendance</a></p>
|
||||
{% endfor %}
|
||||
</section>
|
||||
<section>
|
||||
<h3 class="domain__heading">Assignments to be graded</h3>
|
||||
<ul>
|
||||
{% for component in ungraded_components %}
|
||||
<li>
|
||||
{{component.subject}}, <a href="{% url 'component-detail' component.subject.pk component.pk %}">{{component}}</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<p>Everything is graded to far.</p>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<section class="detail__info">
|
||||
<dl class="detail__datalist">
|
||||
<dt>Username</dt>
|
||||
<dd>{{ user.username }}</dd>
|
||||
<dt>Email address</dt>
|
||||
<dd>{{ user.email }}</dd>
|
||||
</dl>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<section class="panel">
|
||||
<h1>Users</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<th>Username</th>
|
||||
<th>Name</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in user_list %}
|
||||
<tr>
|
||||
<td>{{ user.username }}</td>
|
||||
<td><a href="{% url 'account-detail' user.id %}">{{user.first_name}} {{user.last_name}}</a></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td>No users yet.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@ -2,10 +2,21 @@ from django.urls import path, include
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.AccountListView.as_view(), name='account-list'),
|
||||
path('<int:pk>/', include([
|
||||
path('', views.AccountDetailView.as_view(), name='account-detail'),
|
||||
path('update/', views.AccountUpdateView.as_view(), name='account-update'),
|
||||
path('delete/', views.AccountDeleteView.as_view(), name='account-delete'),
|
||||
path(
|
||||
'',
|
||||
views.AccountDetailView.as_view(),
|
||||
name='account-detail'
|
||||
),
|
||||
path(
|
||||
'update/',
|
||||
views.AccountUpdateView.as_view(),
|
||||
name='account-update'
|
||||
),
|
||||
path(
|
||||
'delete/',
|
||||
views.AccountDeleteView.as_view(),
|
||||
name='account-delete'
|
||||
),
|
||||
])),
|
||||
]
|
||||
|
||||
@ -12,48 +12,14 @@ from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
|
||||
from students.models import Student
|
||||
from gradebook.models import Component
|
||||
from attendance.models import Day, Entry
|
||||
|
||||
from .models import User
|
||||
from .forms import AccountUpdateForm
|
||||
|
||||
|
||||
class AccountListView(LoginRequiredMixin, ListView):
|
||||
model = User
|
||||
template_name = 'accounts/account_list.html'
|
||||
|
||||
|
||||
class AccountDetailView(LoginRequiredMixin, DetailView):
|
||||
model = User
|
||||
template_name = 'accounts/account_detail.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
today = timezone.localtime(timezone.now()).date()
|
||||
enddate = today + dt.timedelta(days=7)
|
||||
|
||||
context['birthdays'] = Student.objects.filter(
|
||||
dob__month=today.month,
|
||||
dob__day__range=[today.day, enddate.day]
|
||||
).order_by('dob')
|
||||
|
||||
context['components'] = Component.objects.filter(
|
||||
due_date=today
|
||||
).select_related('subject')
|
||||
|
||||
context['attendance'] = Day.objects.filter(
|
||||
date=today
|
||||
).prefetch_related('entry_set', 'entry_set__student')
|
||||
|
||||
context['ungraded_components'] = Component.objects.filter(
|
||||
due_date__lte=today,
|
||||
finished_grading=False
|
||||
).select_related('subject')
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class AccountUpdateView(LoginRequiredMixin, UpdateView):
|
||||
model = User
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Day, Entry
|
||||
|
||||
admin.site.register(Day)
|
||||
admin.site.register(Entry)
|
||||
@ -1,6 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AttendanceConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'attendance'
|
||||
@ -1,24 +0,0 @@
|
||||
from django import forms
|
||||
from django.utils import timezone
|
||||
from .models import Day, Entry
|
||||
|
||||
from students.models import Student
|
||||
|
||||
|
||||
class DayForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Day
|
||||
fields = ('date',)
|
||||
widgets = {
|
||||
'date': forms.DateInput(attrs = {
|
||||
'type': 'date',
|
||||
}),
|
||||
}
|
||||
|
||||
class EntryForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Entry
|
||||
fields = ('day', 'student', 'status')
|
||||
widgets = {
|
||||
'student': forms.HiddenInput()
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
# Generated by Django 3.2.7 on 2021-09-01 15:26
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('students', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Day',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField()),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-date',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Entry',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('status', models.CharField(choices=[('P', 'Present'), ('T', 'Tardy'), ('A', 'Absent')], default='P', max_length=1)),
|
||||
('day', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='attendance.day')),
|
||||
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='students.student')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'entries',
|
||||
'ordering': ('student',),
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -1,33 +0,0 @@
|
||||
from django.db import models
|
||||
from students.models import Student
|
||||
|
||||
class Day(models.Model):
|
||||
class Meta:
|
||||
ordering = ('-date',)
|
||||
|
||||
date = models.DateField()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.date}"
|
||||
|
||||
class Entry(models.Model):
|
||||
class Meta:
|
||||
verbose_name_plural = 'entries'
|
||||
ordering = ('student',)
|
||||
|
||||
STATUS_CHOICES = [
|
||||
('P', 'Present'),
|
||||
('T', 'Tardy'),
|
||||
('A', 'Absent'),
|
||||
]
|
||||
|
||||
day = models.ForeignKey(Day, on_delete=models.CASCADE)
|
||||
student = models.ForeignKey(Student, on_delete=models.CASCADE)
|
||||
status = models.CharField(
|
||||
max_length=1,
|
||||
choices=STATUS_CHOICES,
|
||||
default='P'
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.day} | {self.student} | {self.status}"
|
||||
@ -1,13 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Delete {{day}}</h1>
|
||||
<form method="post" action="{% url 'day-delete' day.pk %}">
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{day}}"> or <a href="{% url 'day-detail' day.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,34 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<form action="{% url 'day-create' %}" method="POST">
|
||||
{% csrf_token %}
|
||||
<header>
|
||||
<h1>Take Attendance</h1>
|
||||
{{form.as_p}}
|
||||
</header>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Student</td>
|
||||
<td>Present</td>
|
||||
<td>Tardy</td>
|
||||
<td>Absent</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for student in student_list %}
|
||||
<tr>
|
||||
<td>{{student.full_name}}</td>
|
||||
<td><input type="radio" name="students_{{student.pk}}" value="P" checked></td>
|
||||
<td><input type="radio" name="students_{{student.pk}}" value="T"></td>
|
||||
<td><input type="radio" name="students_{{student.pk}}" value="A"></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<input class="action-button" type="submit" value="Save attendance"> or <a href="{% url 'day-list' %}">cancel</a>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,34 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<form action="{% url 'day-update' day.pk %}" method="POST">
|
||||
{% csrf_token %}
|
||||
<header>
|
||||
<h1>Take Attendance</h1>
|
||||
{{form.as_p}}
|
||||
</header>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Student</td>
|
||||
<td>Present</td>
|
||||
<td>Tardy</td>
|
||||
<td>Absent</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in day.entry_set.all %}
|
||||
<tr>
|
||||
<td>{{entry.student.full_name}}</td>
|
||||
<td><input type="radio" name="students_{{entry.student.pk}}" value="P" {% if entry.status == "P" %}checked{% endif %}></td>
|
||||
<td><input type="radio" name="students_{{entry.student.pk}}" value="T" {% if entry.status == "T" %}checked{% endif %}></td>
|
||||
<td><input type="radio" name="students_{{entry.student.pk}}" value="A" {% if entry.status == "A" %}checked{% endif %}></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'day-list' %}">cancel</a>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,51 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Attendance</h1>
|
||||
<p>
|
||||
<a href="{% url 'day-create' %}" class="action-button">Take attendance</a>
|
||||
</p>
|
||||
<section>
|
||||
{% for day in day_list %}
|
||||
<h4><a href="{% url 'day-update' day.pk %}">{{day.date}}</a></h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Student</td>
|
||||
<td colspan="2">Status</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in day.entry_set.all %}
|
||||
<tr>
|
||||
<td>{{entry.student}}</td>
|
||||
<td>{{entry.get_status_display}}</td>
|
||||
<td><a href="{% url 'entry-update' entry.pk %}">Update</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% empty %}
|
||||
<p>No attendance taken yet.</p>
|
||||
{% endfor %}
|
||||
</section>
|
||||
<section class="pagination">
|
||||
<span class="step-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a class="minor-action-button" href="?page=1">« first</a>
|
||||
<a class="minor-action-button" href="?page={{ page_obj.previous_page_number }}">‹ previous</a>
|
||||
{% endif %}
|
||||
|
||||
<span class="current">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
|
||||
</span>
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<a class="minor-action-button" href="?page={{ page_obj.next_page_number }}">next ›</a>
|
||||
<a class="minor-action-button" href="?page={{ page_obj.paginator.num_pages }}">last »</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,13 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Delete {{entry}}</h1>
|
||||
<form method="post" action="{% url 'entry-delete' entry.pk %}">
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{entry}}"> or <a href="{% url 'day-detail' entry.day.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,20 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<div class="generic__header">
|
||||
<h1>Update Entry</h1>
|
||||
<a class="action-button action-delete" href="{% url 'entry-delete' entry.pk %}">Delete {{entry}}</a>
|
||||
</div>
|
||||
<p>For <strong>{{student}}</strong></p>
|
||||
<section>
|
||||
<form action="{% url 'entry-update' entry.pk %}" method="POST">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p>
|
||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'student-detail' entry.student.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@ -1,20 +0,0 @@
|
||||
from django.urls import path, include
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('days/', views.DayListView.as_view(), name='day-list'),
|
||||
path('days/new/', views.DayCreateView.as_view(), name='day-create'),
|
||||
path('days/<int:pk>/', include([
|
||||
path('', views.DayDetailView.as_view(), name='day-detail'),
|
||||
path('update/', views.DayUpdateView.as_view(), name='day-update'),
|
||||
path('delete/', views.DayDeleteView.as_view(), name='day-delete'),
|
||||
])),
|
||||
|
||||
path('entries/', views.EntryListView.as_view(), name='entry-list'),
|
||||
path('entries/new/', views.EntryCreateView.as_view(), name='entry-create'),
|
||||
path('entries/<int:pk>/', include([
|
||||
path('', views.EntryDetailView.as_view(), name='entry-detail'),
|
||||
path('update/', views.EntryUpdateView.as_view(), name='entry-update'),
|
||||
path('delete/', views.EntryDeleteView.as_view(), name='entry-delete'),
|
||||
])),
|
||||
]
|
||||
@ -1,98 +0,0 @@
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse_lazy, reverse
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.list import ListView
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.utils import timezone
|
||||
|
||||
from django.db.models import Prefetch, Subquery, Count, Sum, F, Q, Value
|
||||
from django.db.models.functions import Length, Upper
|
||||
|
||||
from students.models import Student
|
||||
from .models import Day, Entry
|
||||
from .forms import DayForm, EntryForm
|
||||
|
||||
|
||||
# Days
|
||||
class DayListView(LoginRequiredMixin, ListView):
|
||||
model = Day
|
||||
paginate_by = 7
|
||||
|
||||
def get_queryset(self):
|
||||
object_list = Day.objects.all().prefetch_related('entry_set', 'entry_set__student')
|
||||
return object_list
|
||||
|
||||
class DayCreateView(LoginRequiredMixin, CreateView):
|
||||
model = Day
|
||||
template_name_suffix = '_create_form'
|
||||
form_class = DayForm
|
||||
success_url = reverse_lazy('profile-detail')
|
||||
|
||||
def get_initial(self):
|
||||
today = timezone.localtime(timezone.now()).date()
|
||||
initial = {
|
||||
'date': today,
|
||||
}
|
||||
return initial
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['student_list'] = Student.objects.all()
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
form.save()
|
||||
for key, value in self.request.POST.items():
|
||||
if 'student' in key:
|
||||
s = key.split('_')[1]
|
||||
Entry.objects.create(
|
||||
day=form.instance,
|
||||
student=Student.objects.get(pk=s),
|
||||
status=value,
|
||||
)
|
||||
return super().form_valid(form)
|
||||
|
||||
class DayDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Day
|
||||
|
||||
class DayUpdateView(LoginRequiredMixin, UpdateView):
|
||||
model = Day
|
||||
form_class = DayForm
|
||||
success_url = reverse_lazy('day-list')
|
||||
|
||||
def form_valid(self, form):
|
||||
form.save()
|
||||
for key, value in self.request.POST.items():
|
||||
if 'student' in key:
|
||||
s = key.split('_')[1]
|
||||
Entry.objects.filter(day=self.object, student=s).update(status=value)
|
||||
return super().form_valid(form)
|
||||
|
||||
class DayDeleteView(LoginRequiredMixin, DeleteView):
|
||||
model = Day
|
||||
success_url = reverse_lazy('day-list')
|
||||
|
||||
|
||||
# Entries
|
||||
class EntryListView(LoginRequiredMixin, ListView):
|
||||
model = Entry
|
||||
|
||||
class EntryCreateView(LoginRequiredMixin, CreateView):
|
||||
model = Entry
|
||||
template_name_suffix = '_create_form'
|
||||
fields = ('__all__')
|
||||
|
||||
class EntryDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Entry
|
||||
|
||||
class EntryUpdateView(LoginRequiredMixin, UpdateView):
|
||||
model = Entry
|
||||
form_class = EntryForm
|
||||
success_url = reverse_lazy('day-list')
|
||||
|
||||
class EntryDeleteView(LoginRequiredMixin, DeleteView):
|
||||
model = Entry
|
||||
success_url = reverse_lazy('day-list')
|
||||
@ -1,3 +1,23 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
from .models import (
|
||||
SchoolYear,
|
||||
StudentTag,
|
||||
Student,
|
||||
Subject,
|
||||
Tag,
|
||||
Component,
|
||||
Score,
|
||||
SchoolDay,
|
||||
AttendanceEntry,
|
||||
)
|
||||
|
||||
admin.site.register(SchoolYear)
|
||||
admin.site.register(StudentTag)
|
||||
admin.site.register(Student)
|
||||
admin.site.register(Subject)
|
||||
admin.site.register(Tag)
|
||||
admin.site.register(Component)
|
||||
admin.site.register(Score)
|
||||
admin.site.register(SchoolDay)
|
||||
admin.site.register(AttendanceEntry)
|
||||
|
||||
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')
|
||||
}
|
||||
@ -16,22 +16,56 @@ from .models import (
|
||||
class SchoolYearCreateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = SchoolYear
|
||||
fields = [
|
||||
'year'
|
||||
]
|
||||
fields = ['year']
|
||||
|
||||
|
||||
class StudentForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Student
|
||||
fields = (
|
||||
fields = [
|
||||
'student_id',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'address',
|
||||
'dob',
|
||||
)
|
||||
'allergies',
|
||||
'tags'
|
||||
]
|
||||
labels = {
|
||||
'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'})
|
||||
}
|
||||
|
||||
|
||||
class SchoolDayForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = SchoolDay
|
||||
fields = ['date']
|
||||
widgets = {
|
||||
'date': forms.DateInput(attrs={
|
||||
'type': 'date',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
class AttendanceEntryForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = AttendanceEntry
|
||||
fields = ['status']
|
||||
widgets = {
|
||||
'status': forms.RadioSelect()
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Generated by Django 4.0.5 on 2022-06-29 18:50
|
||||
# Generated by Django 4.0.5 on 2022-07-28 20:46
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
@ -9,16 +9,41 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Component',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('category', models.CharField(choices=[('QUIZ', 'Quiz'), ('ASSIGNMENT', 'Assignment'), ('TEST', 'Test')], default='ASSIGNMENT', max_length=255)),
|
||||
('due_date', models.DateField()),
|
||||
('grade_total', models.PositiveIntegerField()),
|
||||
('finished_grading', models.BooleanField(default=False)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['due_date'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SchoolYear',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('start_date', models.DateField()),
|
||||
('end_date', models.DateField()),
|
||||
('year', models.PositiveIntegerField(unique=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StudentTag',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
@ -26,6 +51,8 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
@ -37,21 +64,14 @@ class Migration(migrations.Migration):
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=250)),
|
||||
('description', models.CharField(blank=True, max_length=250)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('school_year', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.schoolyear')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StudentTag',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('tag', models.SlugField()),
|
||||
('object_id', models.PositiveIntegerField()),
|
||||
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Student',
|
||||
fields=[
|
||||
@ -61,44 +81,58 @@ class Migration(migrations.Migration):
|
||||
('last_name', models.CharField(max_length=50)),
|
||||
('address', models.TextField(blank=True)),
|
||||
('dob', models.DateField()),
|
||||
('allergies', models.CharField(blank=True, max_length=255)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('school_year', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.schoolyear')),
|
||||
('tags', models.ManyToManyField(blank=True, to='core.studenttag')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['student_id', 'first_name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Score',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('value', models.PositiveIntegerField()),
|
||||
('component', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.component')),
|
||||
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.student')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['student'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SchoolDay',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField()),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('school_year', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.schoolyear')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-date'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Component',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('category', models.CharField(choices=[('QUIZ', 'Quiz'), ('ASSIGNMENT', 'Assignment'), ('TEST', 'Test')], default='ASSIGNMENT', max_length=255)),
|
||||
('due_date', models.DateField()),
|
||||
('grade_total', models.PositiveIntegerField()),
|
||||
('finished_grading', models.BooleanField(default=False)),
|
||||
('subject', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.subject')),
|
||||
('tags', models.ManyToManyField(blank=True, to='core.tag')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['due_date'],
|
||||
},
|
||||
migrations.AddField(
|
||||
model_name='component',
|
||||
name='subject',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.subject'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='component',
|
||||
name='tags',
|
||||
field=models.ManyToManyField(blank=True, to='core.tag'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AttendanceEntry',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('status', models.CharField(choices=[('P', 'Present'), ('T', 'Tardy'), ('A', 'Absent')], default='P', max_length=1)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('school_day', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.schoolday')),
|
||||
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.student')),
|
||||
],
|
||||
@ -107,8 +141,4 @@ class Migration(migrations.Migration):
|
||||
'ordering': ['student'],
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='studenttag',
|
||||
index=models.Index(fields=['content_type', 'object_id'], name='core_studen_content_a7305d_idx'),
|
||||
),
|
||||
]
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
# Generated by Django 4.0.5 on 2022-06-29 20:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='schoolyear',
|
||||
name='start_date',
|
||||
field=models.DateField(unique_for_year=True),
|
||||
),
|
||||
]
|
||||
@ -1,27 +0,0 @@
|
||||
# Generated by Django 4.0.5 on 2022-06-29 20:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0002_alter_schoolyear_start_date'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='schoolyear',
|
||||
name='end_date',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='schoolyear',
|
||||
name='start_date',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='schoolyear',
|
||||
name='year',
|
||||
field=models.PositiveIntegerField(default=2021),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@ -1,18 +0,0 @@
|
||||
# Generated by Django 4.0.5 on 2022-06-29 20:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0003_remove_schoolyear_end_date_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='schoolyear',
|
||||
name='year',
|
||||
field=models.PositiveIntegerField(unique=True),
|
||||
),
|
||||
]
|
||||
@ -1,104 +0,0 @@
|
||||
# Generated by Django 4.0.5 on 2022-06-29 21:27
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
from django.utils.timezone import utc
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0004_alter_schoolyear_year'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='attendanceentry',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='attendanceentry',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='component',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='component',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='schoolday',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='schoolday',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='schoolyear',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='schoolyear',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='student',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2022, 6, 29, 21, 26, 47, 547231, tzinfo=utc)),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='student',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='studenttag',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='studenttag',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subject',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subject',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='tag',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='tag',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
]
|
||||
@ -1,26 +0,0 @@
|
||||
# Generated by Django 4.0.5 on 2022-06-30 20:03
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0005_attendanceentry_created_at_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Score',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('value', models.PositiveIntegerField()),
|
||||
('component', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.component')),
|
||||
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.student')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['student'],
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -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):
|
||||
@ -27,21 +30,13 @@ class SchoolYear(models.Model):
|
||||
|
||||
|
||||
class StudentTag(models.Model):
|
||||
tag = models.SlugField()
|
||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey('content_type', 'object_id')
|
||||
name = models.CharField(max_length=255)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.tag
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=['content_type', 'object_id']),
|
||||
]
|
||||
return self.name
|
||||
|
||||
|
||||
# class StudentManager(models.Manager):
|
||||
@ -66,8 +61,9 @@ class Student(models.Model):
|
||||
last_name = models.CharField(max_length=50)
|
||||
address = models.TextField(blank=True)
|
||||
dob = models.DateField()
|
||||
allergies = models.CharField(max_length=255, blank=True)
|
||||
|
||||
tags = GenericRelation(StudentTag)
|
||||
tags = models.ManyToManyField(StudentTag, blank=True)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
@ -89,7 +85,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 +110,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 +179,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 +206,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']
|
||||
@ -222,6 +229,12 @@ class SchoolDay(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('core:schoolday-detail', kwargs={
|
||||
'year': self.school_year.year,
|
||||
'pk': self.pk
|
||||
})
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.date}'
|
||||
|
||||
@ -251,8 +264,15 @@ class AttendanceEntry(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('core:schoolday-detail', kwargs={
|
||||
'year': self.school_day.school_year.year,
|
||||
'pk': self.school_day.pk,
|
||||
'entry_pk': self.pk
|
||||
})
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.day} | {self.student} | {self.status}"
|
||||
return f"{self.school_day} | {self.student} | {self.status}"
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'entries'
|
||||
|
||||
31
src/core/templates/core/attendanceentry_confirm_delete.html
Normal file
31
src/core/templates/core/attendanceentry_confirm_delete.html
Normal file
@ -0,0 +1,31 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Edit Attendance | {% endblock head_title %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<menu>
|
||||
<li><strong><a href="{% url 'core:schoolyear-detail' entry.school_day.school_year.year %}">{{ entry.school_day.school_year.year }}</a></strong></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:schoolday-list' entry.school_day.school_year.year %}">Attendance</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:schoolday-detail' entry.school_day.school_year.year entry.school_day.pk %}">{{ entry.school_day }}</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<header class="form__header">
|
||||
<h1>Delete Attendanceentry</h1>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:entry-delete' entry.school_day.school_year.year entry.school_day.pk entry.pk %}">
|
||||
{% csrf_token %}
|
||||
<p>Are you sure you want to delete "{{ attendanceentry }}"?</p>
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Delete"> or <a href="{% url 'core:schoolday-detail' entry.school_day.school_year.year entry.school_day.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
12
src/core/templates/core/attendanceentry_create_form.html
Normal file
12
src/core/templates/core/attendanceentry_create_form.html
Normal file
@ -0,0 +1,12 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>+ New Attendanceentry</h1>
|
||||
<form method="POST" action="{% url 'core:attendanceentry-create' %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Create"> or <a href="{% url 'core:attendanceentry-list' %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
||||
33
src/core/templates/core/attendanceentry_detail.html
Normal file
33
src/core/templates/core/attendanceentry_detail.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Attendance | {% endblock head_title %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<menu>
|
||||
<li><strong><a href="{% url 'core:schoolyear-detail' entry.schoolday.school_year.year %}">{{ entry.schoolday.school_year.year }}</a></strong></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:schoolday-list' entry.schoolday.school_year.year %}">Attendance</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:schoolday-detail' entry.schoolday.school_year.year entry.schoolday.pk %}">{{ entry.schoolday }}</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<header class="form__header">
|
||||
<div>
|
||||
<h1>Edit Attendance</h1>
|
||||
</div>
|
||||
<p><a href="{% url 'core:schoolday-delete' entry.schoolday.school_year.year entry.schoolday.pk %}" class="action-button action-delete">Delete</a></p>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:schoolday-update' school_year.year entry.schoolday.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Save changes"> or <a href="{% url 'core:schoolday-list' school_year.year %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
33
src/core/templates/core/attendanceentry_form.html
Normal file
33
src/core/templates/core/attendanceentry_form.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Edit Attendance | {% endblock head_title %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<menu>
|
||||
<li><strong><a href="{% url 'core:schoolyear-detail' entry.school_day.school_year.year %}">{{ entry.school_day.school_year.year }}</a></strong></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:schoolday-list' entry.school_day.school_year.year %}">Attendance</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:schoolday-detail' entry.school_day.school_year.year entry.school_day.pk %}">{{ entry.school_day }}</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<header class="form__header">
|
||||
<div>
|
||||
<h1>Edit Attendance</h1>
|
||||
</div>
|
||||
<p><a href="{% url 'core:entry-delete' entry.school_day.school_year.year entry.school_day.pk entry.pk %}" class="action-button action-delete">Delete</a></p>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:entry-update' entry.school_day.school_year.year entry.school_day.pk entry.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Save changes"> or <a href="{% url 'core:schoolday-detail' entry.school_day.school_year.year entry.school_day.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
16
src/core/templates/core/attendanceentry_list.html
Normal file
16
src/core/templates/core/attendanceentry_list.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Attendanceentrys</h1>
|
||||
<table>
|
||||
<tbody>
|
||||
{% for attendanceentry in attendanceentry_list %}
|
||||
<tr>
|
||||
<dt>{{ attendanceentry }}</dt>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>No attendanceentrys yet.</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
36
src/core/templates/core/component_confirm_delete.html
Normal file
36
src/core/templates/core/component_confirm_delete.html
Normal file
@ -0,0 +1,36 @@
|
||||
{% 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>
|
||||
<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">
|
||||
<header class="form__header">
|
||||
<h1>Delete Component</h1>
|
||||
<p>
|
||||
<a href="{% url 'core:component-delete' component.subject.school_year.year component.subject.pk component.pk %}" class="action-button action-delete">Delete</a>
|
||||
</p>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:component-delete' component.subject.school_year.year component.subject.pk component.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Delete"> or <a href="{% url 'core:component-detail' component.subject.school_year.year component.subject.pk component.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% 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 'core:component-update' component.subject.school_year.year 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 tag__info" href="{% url 'core:tag-detail' component.subject.school_year.year 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</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 %}
|
||||
36
src/core/templates/core/component_form.html
Normal file
36
src/core/templates/core/component_form.html
Normal file
@ -0,0 +1,36 @@
|
||||
{% 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>
|
||||
<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">
|
||||
<header class="form__header">
|
||||
<h1>Edit Component</h1>
|
||||
<p>
|
||||
<a href="{% url 'core:component-delete' component.subject.school_year.year component.subject.pk component.pk %}" class="action-button action-delete">Delete</a>
|
||||
</p>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:component-update' component.subject.school_year.year component.subject.pk component.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input 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>
|
||||
</article>
|
||||
{% 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 %}
|
||||
9
src/core/templates/core/home.html
Normal file
9
src/core/templates/core/home.html
Normal file
@ -0,0 +1,9 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Grades & Attendance Management Software |{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<h1>Grades and Attendance Management Software</h1>
|
||||
</article>
|
||||
{% endblock %}
|
||||
29
src/core/templates/core/schoolday_confirm_delete.html
Normal file
29
src/core/templates/core/schoolday_confirm_delete.html
Normal file
@ -0,0 +1,29 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block head_title %}Delete Attendance | {% 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:schoolday-list' school_year.year %}">Attendance</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:schoolday-detail' school_year.year schoolday.pk %}">{{ schoolday.date }}</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<h1>Delete Attendance</h1>
|
||||
<form method="POST" action="{% url 'core:schoolday-delete' school_year.year schoolday.pk %}">
|
||||
{% csrf_token %}
|
||||
<p>Are you sure you want to delete "{{ schoolday }}"?</p>
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Delete" class="action-button action-delete"> or <a href="{% url 'core:schoolday-detail' school_year.year schoolday.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
46
src/core/templates/core/schoolday_create_form.html
Normal file
46
src/core/templates/core/schoolday_create_form.html
Normal file
@ -0,0 +1,46 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Attendance | {% 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:schoolday-list' school_year.year %}">Attendance</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<h1>Take Attendance</h1>
|
||||
<form method="POST" action="{% url 'core:schoolday-create' school_year.year %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Student</td>
|
||||
<td>Present</td>
|
||||
<td>Tardy</td>
|
||||
<td>Absent</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for student in student_list %}
|
||||
<tr>
|
||||
<td><h5>{{ student.student_id }}   /   {{ student.full_name }}</h5></td>
|
||||
<td><input type="radio" name="students_{{student.pk}}" value="P" checked></td>
|
||||
<td><input type="radio" name="students_{{student.pk}}" value="T"></td>
|
||||
<td><input type="radio" name="students_{{student.pk}}" value="A"></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<input type="submit" value="Save attendance"> or <a href="{% url 'core:schoolday-list' school_year.year %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
46
src/core/templates/core/schoolday_detail.html
Normal file
46
src/core/templates/core/schoolday_detail.html
Normal file
@ -0,0 +1,46 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block head_title %}Attendance {{ schoolday.date }} | {% endblock head_title %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<menu>
|
||||
<li><strong><a href="{% url 'core:schoolyear-detail' schoolday.school_year.year %}">{{ schoolday.school_year.year }}</a></strong></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:schoolday-list' schoolday.school_year.year %}">Attendance</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="detail">
|
||||
<header class="detail__header">
|
||||
<div>
|
||||
<h1>Attendance {{ schoolday.date }}</h1>
|
||||
<p><small>Added: <time datetime="{{ schoolday.created_at|date:'Y-m-d' }}">{{ schoolday.created_at|date:'M d Y' }}</time><br>
|
||||
Last updated: <time datetime="{{ schoolday.updated_at|date:'Y-m-d' }}">{{ schoolday.updated_at|date:'M d Y' }}</time></small></p>
|
||||
</div>
|
||||
<a href="{% url 'core:schoolday-update' schoolday.school_year.year schoolday.pk %}" class="action-button">Edit</a>
|
||||
</header>
|
||||
<section>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Student</td>
|
||||
<td colspan="2">Status</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in schoolday.attendanceentry_set.all %}
|
||||
<tr>
|
||||
<td>{{entry.student}}</td>
|
||||
<td>{{entry.get_status_display}}</td>
|
||||
<td><a href="{% url 'core:entry-update' schoolday.school_year.year schoolday.pk entry.pk %}">Edit</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
53
src/core/templates/core/schoolday_form.html
Normal file
53
src/core/templates/core/schoolday_form.html
Normal file
@ -0,0 +1,53 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Edit Attendance | {% endblock head_title %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<menu>
|
||||
<li><strong><a href="{% url 'core:schoolyear-detail' schoolday.school_year.year %}">{{ schoolday.school_year.year }}</a></strong></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:schoolday-list' schoolday.school_year.year %}">Attendance</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:schoolday-detail' schoolday.school_year.year schoolday.pk %}">{{ schoolday }}</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<header class="form__header">
|
||||
<div>
|
||||
<h1>Edit Attendance</h1>
|
||||
</div>
|
||||
<p><a href="{% url 'core:schoolday-delete' schoolday.school_year.year schoolday.pk %}" class="action-button action-delete">Delete</a></p>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:schoolday-update' school_year.year schoolday.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Student</td>
|
||||
<td>Present</td>
|
||||
<td>Tardy</td>
|
||||
<td>Absent</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in schoolday.attendanceentry_set.all %}
|
||||
<tr>
|
||||
<td><h5>{{ entry.student.student_id }}   /   {{ entry.student.full_name }}</h5></td>
|
||||
<td><input type="radio" name="students_{{ entry.student.pk }}" value="P" {% if entry.status == "P" %}checked{% endif %}></td>
|
||||
<td><input type="radio" name="students_{{ entry.student.pk }}" value="T" {% if entry.status == "T" %}checked{% endif %}></td>
|
||||
<td><input type="radio" name="students_{{ entry.student.pk }}" value="A" {% if entry.status == "A" %}checked{% endif %}></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<input type="submit" value="Save changes"> or <a href="{% url 'core:schoolday-detail' school_year.year schoolday.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
54
src/core/templates/core/schoolday_list.html
Normal file
54
src/core/templates/core/schoolday_list.html
Normal file
@ -0,0 +1,54 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block head_title %}Attendance | {% 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>Attendance</h1>
|
||||
<a href="{% url 'core:schoolday-create' school_year.year %}" class="action-button">Take attendance →</a>
|
||||
</div>
|
||||
</header>
|
||||
{% for schoolday in schoolday_list %}
|
||||
<section>
|
||||
<h4><a href="{% url 'core:schoolday-detail' school_year.year schoolday.pk %}">{{ schoolday.date }}</a></h4>
|
||||
<table class="list__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Student</th>
|
||||
<th colspan="2">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in schoolday.attendanceentry_set.all %}
|
||||
<tr onclick="document.location=''">
|
||||
<td>{{entry.student}}</td>
|
||||
<td>{{entry.get_status_display}}</td>
|
||||
<td><a href="{% url 'core:entry-update' school_year.year schoolday.pk entry.pk %}">Edit</a></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="3">No attendance yet.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
{% empty %}
|
||||
<section>
|
||||
<p>No attendance yet.</p>
|
||||
</section>
|
||||
{% endfor %}
|
||||
{% include 'core/partials/pagination.html' %}
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,12 +1,14 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>+ New Schoolyear</h1>
|
||||
<form method="POST" action="{% url 'core:schoolyear-create' %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Create"> or <a href="{% url 'core:schoolyear-list' %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
<article class="form">
|
||||
<h1>+ New School year</h1>
|
||||
<form method="POST" action="{% url 'core:schoolyear-create' %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Create"> or <a href="{% url 'core:schoolyear-list' %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,34 +1,31 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Project {{ schoolyear.name }} | {% endblock head_title %}
|
||||
{% block head_title %}Year {{ schoolyear.year }} | {% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<article class="detail">
|
||||
<header class="detail__header">
|
||||
<div>
|
||||
<h1>{{ schoolyear }}</h1>
|
||||
<p><small>Created <time datetime="{{ schoolyear.created_at|date:'Y-m-d' }}">{{ schoolyear.created_at }}</time></small></p>
|
||||
</div>
|
||||
{% if perms.core.can_change_schoolyear %}
|
||||
<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>
|
||||
<span>→</span>
|
||||
</a>
|
||||
<a href="" class="tool">
|
||||
<a href="{% url 'core:schoolday-list' schoolyear.year %}" class="tool">
|
||||
<h3>Attendence</h3>
|
||||
<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 %}
|
||||
|
||||
@ -1,12 +1,18 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Edit Year {{ schoolyear.year }} | {% endblock head_title %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Update Schoolyear</h1>
|
||||
<form method="POST" action="{% url 'core:schoolyear-update' schoolyear.year %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Save changes"> or <a href="{% url 'core:schoolyear-detail' schoolyear.year %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
<article class="form">
|
||||
<header class="form_header">
|
||||
<h1>Edit Schoolyear</h1>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:schoolyear-update' schoolyear.year %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Save changes"> or <a href="{% url 'core:schoolyear-detail' schoolyear.year %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
|
||||
@ -8,38 +8,21 @@
|
||||
<header class="list__header">
|
||||
<div class="list__title">
|
||||
<h1>School Years</h1>
|
||||
{% if perms.core.can_add_schoolyear %}
|
||||
<a href="{% url 'core:schoolyear-create' %}" class="action-button">+ New Year</a>
|
||||
{% endif %}
|
||||
</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: ({{ schoolyear.student__count }})   Subjects: ({{ schoolyear.subject__count }})</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 %}
|
||||
39
src/core/templates/core/score_form.html
Normal file
39
src/core/templates/core/score_form.html
Normal file
@ -0,0 +1,39 @@
|
||||
{% 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>Edit 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</a></p>
|
||||
</header>
|
||||
<section>
|
||||
<form action="{% url 'core:score-update' school_year.year score.pk %}" method="POST">
|
||||
{% csrf_token %}
|
||||
<h3>{{ score.student }}</h3>
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'core:component-detail' school_year.year score.component.subject.pk score.component.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 %}
|
||||
@ -1,7 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block head_title %}Student {{ student.student_id }} | {% endblock head_title %}
|
||||
{% block head_title %}{{ student.student_id }} / {{ student.full_name }} | {% endblock head_title %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
@ -23,77 +23,87 @@
|
||||
</div>
|
||||
<a href="{% url 'core:student-update' student.school_year.year student.pk %}" class="action-button">Edit</a>
|
||||
</header>
|
||||
<section class="student__details">
|
||||
<dl>
|
||||
{% if student.allergies %}
|
||||
<dt>Allergies</dt>
|
||||
<dd>{{ student.allergies }}</dd>
|
||||
{% endif %}
|
||||
<section class="detail__info">
|
||||
<dl class="detail__datalist">
|
||||
<dt>Birthday</dt>
|
||||
<dd>{{ student.dob }}</dd>
|
||||
<dt>Age</dt>
|
||||
<dd>{{ student.age }}</dd>
|
||||
<dt>Allergies</dt>
|
||||
<dd>{% if student.allergies %}<span class="tag tag__warning">{{ student.allergies }}</span>{% endif %}</dd>
|
||||
{% if student.address %}
|
||||
<dt>Address</dt>
|
||||
<dd>
|
||||
<address>{{ student.address|linebreaksbr }}</address>
|
||||
</dd>
|
||||
{% endif %}
|
||||
{% if student.tags.count > 0 %}
|
||||
<dt>Tags</dt>
|
||||
<div>
|
||||
{% for tag in student.tags.all %}
|
||||
<dd><a href="{% url 'core:stag-detail' school_year.year tag.pk %}" class="tag tag__info">{{ tag }}</a></dd>{% if not forloop.last %}<br>{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</section>
|
||||
<section class="grades">
|
||||
<h2>Grades</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<th>Subject</th>
|
||||
<th>Grade</th>
|
||||
<tr>
|
||||
<th>Subject</th>
|
||||
<th>Grade</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for subject in subject_list %}
|
||||
<tr>
|
||||
<td class="grade"><em>{{subject}}</em></td>
|
||||
<td class="grade">{{subject.grade|grade_as_percentage:subject.grade_total}}%</td>
|
||||
<td class="grade"><h5>{{ subject }}</h5> {{ subject.description }}</td>
|
||||
<td class="grade">{{ subject.grade|grade_as_percentage:subject.grade_total }}%</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="2">No grades yet. To add a grade you will need to enter a score for this student on a component.</td>
|
||||
<td colspan="2">No grades yet. You will need to enter a score for this student on a component.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Components</h2>
|
||||
<h2>Scores</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>
|
||||
<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 'core:score-update' student.school_year.year score.pk %}">Edit</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p>No components graded yet.</p>
|
||||
{% endfor %}
|
||||
@ -109,15 +119,14 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Date</td>
|
||||
<td colspan="2">Status</td>
|
||||
<th colspan="2">Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in status.list %}
|
||||
<tr>
|
||||
<td>{{entry.day.date}}</td>
|
||||
<td><a href="{% url 'entry-update' entry.pk %}">Update</a></td>
|
||||
<td>{{ entry.school_day.date }}</td>
|
||||
<td><a href="{% url 'core:entry-update' entry.school_day.school_year.year entry.school_day.pk entry.pk %}">Edit</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</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>Edit 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 %}
|
||||
|
||||
@ -18,13 +18,16 @@
|
||||
<h1>Students</h1>
|
||||
<a href="{% url 'core:student-create' school_year.year %}" class="action-button">+ New Student</a>
|
||||
</div>
|
||||
<a href="">Student Tags →</a>
|
||||
<a href="{% url 'core:stag-list' school_year.year %}">Student Tags →</a>
|
||||
</header>
|
||||
<table class="list__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="?order_by=record_num&direction=asc">Student No. ↕</a></th>
|
||||
<th><a href="?order_by=record_num&sort=asc">Student No. ↕</a></th>
|
||||
<th>Name</th>
|
||||
<th>Age</th>
|
||||
<th>Birthday</th>
|
||||
<th>Allergies</th>
|
||||
<th>Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -32,12 +35,19 @@
|
||||
{% for student in student_list %}
|
||||
<tr class="has-link" onclick="document.location='{% url 'core:student-detail' school_year.year student.pk %}'">
|
||||
<td>{{ student.student_id }}</td>
|
||||
<td>{{ student }}</td>
|
||||
<td>(tags)</td>
|
||||
<td><h5>{{ student }}</h5></td>
|
||||
<td>{{ student.age }}</td>
|
||||
<td>{{ student.dob }}</td>
|
||||
<td>{% if student.allergies %}<span class="tag tag__warning">{{ student.allergies }}</span>{% endif %}</td>
|
||||
<td>
|
||||
{% for tag in student.tags.all %}
|
||||
<a href="{% url 'core:stag-detail' school_year.year tag.pk %}" class="tag tag__info">{{ tag }}</a>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="7">No students yet.</td>
|
||||
<td colspan="6">No students yet.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
33
src/core/templates/core/studenttag_confirm_delete.html
Normal file
33
src/core/templates/core/studenttag_confirm_delete.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Delete Tag | {% 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:student-list' school_year.year %}">Students</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:stag-list' school_year.year %}">Student Tags</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:stag-detail' school_year.year tag.pk %}">{{ tag }}</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<header class="form__header">
|
||||
<h1>Delete Tag</h1>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:stag-delete' school_year.year tag.pk %}">
|
||||
{% csrf_token %}
|
||||
<p>Are you sure you want to delete "{{ tag }}"?</p>
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Delete" class="action-delete"> or <a href="{% url 'core:stag-update' school_year.year tag.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
28
src/core/templates/core/studenttag_create_form.html
Normal file
28
src/core/templates/core/studenttag_create_form.html
Normal file
@ -0,0 +1,28 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}New Tag | {% 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:student-list' school_year.year %}">Students</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:stag-list' school_year.year %}">Student Tags</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<h1>+ New Tag</h1>
|
||||
<form method="POST" action="{% url 'core:stag-create' school_year.year %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Create"> or <a href="{% url 'core:stag-list' school_year.year %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
50
src/core/templates/core/studenttag_detail.html
Normal file
50
src/core/templates/core/studenttag_detail.html
Normal file
@ -0,0 +1,50 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block head_title %}Tag {{ tag }} | {% 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:student-list' school_year.year %}">Students</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:stag-list' school_year.year %}">Student Tags</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="detail">
|
||||
<header class="detail__header">
|
||||
<div class="detail__title">
|
||||
<h1>{{ tag }}</h1>
|
||||
</div>
|
||||
<a href="{% url 'core:stag-update' school_year.year tag.pk %}" class="action-button">Edit</a>
|
||||
</header>
|
||||
<section>
|
||||
<h3>Students with this tag</h3>
|
||||
<table class="list__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="?order_by=id&sort=asc">Student No. ↕</a></th>
|
||||
<th>Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for student in tag.student_set.all %}
|
||||
<tr class="has-link" onclick="document.location='{% url 'core:student-detail' school_year.year student.pk %}'">
|
||||
<td>{{ student.student_id }}</td>
|
||||
<td>{{ student }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="2">No students tagged yet.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
33
src/core/templates/core/studenttag_form.html
Normal file
33
src/core/templates/core/studenttag_form.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Edit Tag | {% 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:student-list' school_year.year %}">Students</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:stag-list' school_year.year %}">Student Tags</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<header class="form__header">
|
||||
<h1>Edit Tag</h1>
|
||||
<p>
|
||||
<a href="{% url 'core:stag-delete' school_year.year tag.pk %}" class="action-button action-delete">Delete</a>
|
||||
</p>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:stag-update' school_year.year tag.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Save changes"> or <a href="{% url 'core:stag-detail' school_year.year tag.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
32
src/core/templates/core/studenttag_list.html
Normal file
32
src/core/templates/core/studenttag_list.html
Normal file
@ -0,0 +1,32 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block head_title %}Student Tags | {% 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:student-list' school_year.year %}">Students</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="list">
|
||||
<header class="list__header">
|
||||
<div class="list__title">
|
||||
<h1>Student Tags</h1>
|
||||
<a href="{% url 'core:stag-create' school_year.year %}" class="action-button">+ New Tag</a>
|
||||
</div>
|
||||
</header>
|
||||
<section>
|
||||
{% for tag in studenttag_list %}
|
||||
<a href="{% url 'core:stag-detail' school_year.year tag.pk %}" class="tag tag__info">{{ tag }}</a>
|
||||
{% empty %}
|
||||
<p>No tags yet.</p>
|
||||
{% endfor %}
|
||||
</section>
|
||||
</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 %}
|
||||
28
src/core/templates/core/subject_create_form.html
Normal file
28
src/core/templates/core/subject_create_form.html
Normal file
@ -0,0 +1,28 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}New Subject | {% 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>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<header class="form__header">
|
||||
<h1>+ New Subject</h1>
|
||||
</header>
|
||||
<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 }} | {% 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 %}
|
||||
30
src/core/templates/core/subject_form.html
Normal file
30
src/core/templates/core/subject_form.html
Normal file
@ -0,0 +1,30 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Edit Subject | {% 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 }}</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<header>
|
||||
<h1>Edit Subject</h1>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:subject-update' subject.school_year.year subject.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Save changes"> or <a href="{% url 'core:subject-detail' subject.school_year.year subject.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% 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="{% url 'core:tag-list' school_year.year %}">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>{{ subject.component__count }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="3">No subjects yet.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% include 'core/partials/pagination.html' %}
|
||||
</article>
|
||||
{% endblock %}
|
||||
33
src/core/templates/core/tag_confirm_delete.html
Normal file
33
src/core/templates/core/tag_confirm_delete.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Delete Tag | {% 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:tag-list' school_year.year %}">Tags</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:tag-detail' school_year.year tag.pk %}">{{ tag }}</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<header class="form__header">
|
||||
<h1>Delete Tag</h1>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:tag-delete' school_year.year tag.pk %}">
|
||||
{% csrf_token %}
|
||||
<p>Are you sure you want to delete "{{ tag }}"?</p>
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Delete" class="action-delete"> or <a href="{% url 'core:tag-update' school_year.year tag.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
28
src/core/templates/core/tag_create_form.html
Normal file
28
src/core/templates/core/tag_create_form.html
Normal file
@ -0,0 +1,28 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}New Tag | {% 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:tag-list' school_year.year %}">Tags</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<h1>+ New Tag</h1>
|
||||
<form method="POST" action="{% url 'core:tag-create' school_year.year %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Create"> or <a href="{% url 'core:tag-list' school_year.year %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
50
src/core/templates/core/tag_detail.html
Normal file
50
src/core/templates/core/tag_detail.html
Normal file
@ -0,0 +1,50 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block head_title %}Tag {{ tag }} | {% 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:tag-list' school_year.year %}">Tags</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="detail">
|
||||
<header class="detail__header">
|
||||
<div class="detail__title">
|
||||
<h1>{{ tag }}</h1>
|
||||
</div>
|
||||
<a href="{% url 'core:tag-update' school_year.year tag.pk %}" class="action-button">Edit</a>
|
||||
</header>
|
||||
<section>
|
||||
<h3>Subjects with this tag</h3>
|
||||
<table class="list__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Subject</th>
|
||||
<th>Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for component in tag.component_set.all %}
|
||||
<tr class="has-link" onclick="document.location='{% url 'core:component-detail' school_year.year component.subject.pk component.pk %}'">
|
||||
<td>{{ component.subject.name }}</td>
|
||||
<td>{{ component }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="2">No components tagged yet.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
35
src/core/templates/core/tag_form.html
Normal file
35
src/core/templates/core/tag_form.html
Normal file
@ -0,0 +1,35 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block head_title %}Edit Tag | {% 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:tag-list' school_year.year %}">Tags</a></li>
|
||||
<span>›</span>
|
||||
<li><a href="{% url 'core:tag-detail' school_year.year tag.pk %}">{{ tag }}</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="form">
|
||||
<header class="form__header">
|
||||
<h1>Edit Tag</h1>
|
||||
<p>
|
||||
<a href="{% url 'core:tag-delete' school_year.year tag.pk %}" class="action-button action-delete">Delete</a>
|
||||
</p>
|
||||
</header>
|
||||
<form method="POST" action="{% url 'core:tag-update' school_year.year tag.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p>
|
||||
<input type="submit" value="Save changes"> or <a href="{% url 'core:tag-detail' school_year.year tag.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
32
src/core/templates/core/tag_list.html
Normal file
32
src/core/templates/core/tag_list.html
Normal file
@ -0,0 +1,32 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block head_title %}Tags | {% 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>
|
||||
</menu>
|
||||
</div>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
<article class="list">
|
||||
<header class="list__header">
|
||||
<div class="list__title">
|
||||
<h1>Tags</h1>
|
||||
<a href="{% url 'core:tag-create' school_year.year %}" class="action-button">+ New Tag</a>
|
||||
</div>
|
||||
</header>
|
||||
<section>
|
||||
{% for tag in tag_list %}
|
||||
<a href="{% url 'core:tag-detail' school_year.year tag.pk %}" class="tag tag__info">{{ tag }}</a>
|
||||
{% empty %}
|
||||
<p>No tags yet.</p>
|
||||
{% endfor %}
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,19 +1,18 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block head_title %}Today |{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<header>
|
||||
<h1 class="greeting"><em>Welcome {{profile.user.first_name}} {{profile.user.last_name }}</em>
|
||||
<br>
|
||||
Here's what's going on today
|
||||
</h1>
|
||||
<h1>Here's what's going on today</h1>
|
||||
</header>
|
||||
<section>
|
||||
<h3 class="domain__heading">Birthdays</h3>
|
||||
<h3 class="domain__heading">Birthdays this week</h3>
|
||||
<ul>
|
||||
{% for student in birthdays %}
|
||||
<li><strong><a href="{% url 'student-detail' student.pk %}">{{student}}</a></strong> is turning {{student.age|add:1}} on {{student.dob|date:"M j"}}</li>
|
||||
<li><strong><a href="{% url 'core:student-detail' current_year.year student.pk %}">{{ student }}</a></strong> is turning {{ student.age|add:1 }} on {{ student.dob|date:"M j" }}</li>
|
||||
{% empty %}
|
||||
<p>No Birthdays this next week.</p>
|
||||
{% endfor %}
|
||||
@ -24,7 +23,7 @@
|
||||
<ul>
|
||||
{% for component in components %}
|
||||
<li>
|
||||
{{component.subject}}, <a href="{% url 'component-detail' component.subject.pk component.pk %}">{{component}}</a>
|
||||
{{component.subject}}, <a href="{% url 'core:component-detail' component.subject.pk component.pk %}">{{component}}</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<p>Nothing for today.</p>
|
||||
@ -34,7 +33,7 @@
|
||||
<section>
|
||||
<h3 class="domain__heading">Today's Attendance</h3>
|
||||
{% for day in attendance %}
|
||||
<p><strong><a href="{% url 'day-update' day.pk %}">{{day.date}}</a></strong></p>
|
||||
<h4><a href="{% url 'core:schoolday-update' current_year.year day.pk %}">{{day.date}}</a></h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@ -43,17 +42,17 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in day.entry_set.all %}
|
||||
{% for entry in day.attendanceentry_set.all %}
|
||||
<tr>
|
||||
<td>{{entry.student}}</td>
|
||||
<td>{{entry.get_status_display}}</td>
|
||||
<td><a href="{% url 'entry-update' entry.pk %}">Update</a></td>
|
||||
<td><a href="{% url 'core:entry-update' current_year.year entry.school_day.pk entry.pk %}">Edit</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% empty %}
|
||||
<p class="greeting">No attendance taken yet: <a href="{% url 'day-create' %}" class="action-button">Take attendance</a></p>
|
||||
<p class="greeting">No attendance taken yet: <a href="{% url 'core:schoolday-create' current_year.year %}" class="action-button">Take attendance →</a></p>
|
||||
{% endfor %}
|
||||
</section>
|
||||
<section>
|
||||
@ -61,7 +60,7 @@
|
||||
<ul>
|
||||
{% for component in ungraded_components %}
|
||||
<li>
|
||||
{{component.subject}}, <a href="{% url 'component-detail' component.subject.pk component.pk %}">{{component}}</a>
|
||||
{{component.subject}}, <a href="{% url 'core:component-detail' current_year.year component.subject.pk component.pk %}">{{component}}</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<p>Everything is graded to far.</p>
|
||||
227
src/core/urls.py
227
src/core/urls.py
@ -2,6 +2,8 @@ from django.urls import path, include
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.HomeView.as_view(), name='home'),
|
||||
|
||||
# SchoolYears
|
||||
path('years/', include([
|
||||
path(
|
||||
@ -25,6 +27,7 @@ urlpatterns = [
|
||||
views.SchoolYearUpdateView.as_view(),
|
||||
name='schoolyear-update'
|
||||
),
|
||||
path('today/', views.TodayView.as_view(), name='today'),
|
||||
|
||||
# Students
|
||||
path('students/', include([
|
||||
@ -38,7 +41,7 @@ urlpatterns = [
|
||||
views.StudentCreateView.as_view(),
|
||||
name='student-create'
|
||||
),
|
||||
path('<int:student_pk>/', include([
|
||||
path('<int:pk>/', include([
|
||||
path(
|
||||
'',
|
||||
views.StudentDetailView.as_view(),
|
||||
@ -55,6 +58,228 @@ urlpatterns = [
|
||||
name='student-delete'
|
||||
),
|
||||
])),
|
||||
|
||||
# StudentTags
|
||||
path('tags/', include([
|
||||
path(
|
||||
'',
|
||||
views.StudentTagListView.as_view(),
|
||||
name='stag-list'
|
||||
),
|
||||
path(
|
||||
'new/',
|
||||
views.StudentTagCreateView.as_view(),
|
||||
name='stag-create'
|
||||
),
|
||||
path('<int:stag_pk>/', include([
|
||||
path(
|
||||
'',
|
||||
views.StudentTagDetailView.as_view(),
|
||||
name='stag-detail'
|
||||
),
|
||||
path(
|
||||
'update/',
|
||||
views.StudentTagUpdateView.as_view(),
|
||||
name='stag-update'
|
||||
),
|
||||
path(
|
||||
'delete/',
|
||||
views.StudentTagDeleteView.as_view(),
|
||||
name='stag-delete'
|
||||
),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
|
||||
# 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'
|
||||
),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
|
||||
# Tags
|
||||
path('tags/', include([
|
||||
path(
|
||||
'',
|
||||
views.TagListView.as_view(),
|
||||
name='tag-list'
|
||||
),
|
||||
path(
|
||||
'new/',
|
||||
views.TagCreateView.as_view(),
|
||||
name='tag-create'
|
||||
),
|
||||
path('<int:tag_pk>/', include([
|
||||
path(
|
||||
'',
|
||||
views.TagDetailView.as_view(),
|
||||
name='tag-detail'
|
||||
),
|
||||
path(
|
||||
'update/',
|
||||
views.TagUpdateView.as_view(),
|
||||
name='tag-update'
|
||||
),
|
||||
path(
|
||||
'delete/',
|
||||
views.TagDeleteView.as_view(),
|
||||
name='tag-delete'
|
||||
),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
|
||||
# SchoolDays
|
||||
path('schooldays/', include([
|
||||
path(
|
||||
'',
|
||||
views.SchoolDayListView.as_view(),
|
||||
name='schoolday-list'
|
||||
),
|
||||
path(
|
||||
'new/',
|
||||
views.SchoolDayCreateView.as_view(),
|
||||
name='schoolday-create'
|
||||
),
|
||||
path('<int:pk>/', include([
|
||||
path(
|
||||
'',
|
||||
views.SchoolDayDetailView.as_view(),
|
||||
name='schoolday-detail'
|
||||
),
|
||||
path(
|
||||
'update/',
|
||||
views.SchoolDayUpdateView.as_view(),
|
||||
name='schoolday-update'
|
||||
),
|
||||
path(
|
||||
'delete/',
|
||||
views.SchoolDayDeleteView.as_view(),
|
||||
name='schoolday-delete'
|
||||
),
|
||||
|
||||
# AttendanceEntries
|
||||
path('entries/', include([
|
||||
path(
|
||||
'',
|
||||
views.AttendanceEntryListView.as_view(),
|
||||
name='entry-list'
|
||||
),
|
||||
path(
|
||||
'new/',
|
||||
views.AttendanceEntryCreateView.as_view(),
|
||||
name='entry-create'
|
||||
),
|
||||
path('<int:entry_pk>/', include([
|
||||
path(
|
||||
'',
|
||||
views.AttendanceEntryDetailView.as_view(),
|
||||
name='entry-detail'
|
||||
),
|
||||
path(
|
||||
'update/',
|
||||
views.AttendanceEntryUpdateView.as_view(),
|
||||
name='entry-update'
|
||||
),
|
||||
path(
|
||||
'delete/',
|
||||
views.AttendanceEntryDeleteView.as_view(),
|
||||
name='entry-delete'
|
||||
),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
|
||||
# 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'
|
||||
),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import datetime as dt
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.db import models
|
||||
@ -33,28 +34,94 @@ from .models import (
|
||||
)
|
||||
|
||||
from .forms import (
|
||||
SchoolYearCreateForm
|
||||
StudentForm,
|
||||
SchoolYearCreateForm,
|
||||
ComponentCreateForm,
|
||||
SchoolDayForm,
|
||||
AttendanceEntryForm
|
||||
)
|
||||
|
||||
# SEARCH
|
||||
# # Look up Q objects for combining different fields in a single query
|
||||
# from django.db.models import Q
|
||||
# people = Person.objects.filter(Q(first_name__contains=query) | Q(last_name__contains=query)
|
||||
# restaurants = Restaurant.objects.filter(restaurant_name__contains=query)
|
||||
# pizzas = Pizza.objects.filter(pizza_name__contains=query)
|
||||
|
||||
class SchoolYearListView(ListView):
|
||||
# Then combine the results, if you want
|
||||
|
||||
# from itertools import chain
|
||||
# results = chain(people, restaurants, pizzas)
|
||||
|
||||
|
||||
class HomeView(TemplateView):
|
||||
template_name = 'core/home.html'
|
||||
|
||||
|
||||
class TodayView(LoginRequiredMixin, TemplateView):
|
||||
template_name = 'core/today.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
today = timezone.localtime(timezone.now()).date()
|
||||
enddate = today + dt.timedelta(days=7)
|
||||
|
||||
context['birthdays'] = Student.objects.filter(
|
||||
school_year__year=self.kwargs['year'],
|
||||
dob__month=today.month,
|
||||
dob__day__range=[today.day, enddate.day]
|
||||
).order_by('dob')
|
||||
|
||||
context['components'] = Component.objects.filter(
|
||||
due_date=today
|
||||
).select_related('subject')
|
||||
|
||||
context['attendance'] = SchoolDay.objects.filter(
|
||||
date=today
|
||||
).prefetch_related(
|
||||
'attendanceentry_set',
|
||||
'attendanceentry_set__student'
|
||||
)
|
||||
|
||||
context['ungraded_components'] = Component.objects.filter(
|
||||
subject__school_year__year=self.kwargs['year'],
|
||||
due_date__lte=today,
|
||||
finished_grading=False
|
||||
).select_related('subject')
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class SchoolYearListView(LoginRequiredMixin, ListView):
|
||||
model = SchoolYear
|
||||
paginate_by = 10
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = SchoolYear.objects.annotate(
|
||||
models.Count('student', distinct=True),
|
||||
models.Count('subject', distinct=True)
|
||||
).order_by('-year')
|
||||
return queryset
|
||||
|
||||
|
||||
class SchoolYearDetailView(DetailView):
|
||||
model = SchoolYear
|
||||
slug_url_kwarg = 'year'
|
||||
slug_field = 'year'
|
||||
|
||||
|
||||
class SchoolYearCreateView(SuccessMessageMixin, CreateView):
|
||||
class SchoolYearCreateView(
|
||||
LoginRequiredMixin, SuccessMessageMixin, CreateView
|
||||
):
|
||||
model = SchoolYear
|
||||
success_message = 'SchoolYear created.'
|
||||
form_class = SchoolYearCreateForm
|
||||
template_name_suffix = '_create_form'
|
||||
|
||||
|
||||
class SchoolYearUpdateView(SuccessMessageMixin, UpdateView):
|
||||
class SchoolYearDetailView(LoginRequiredMixin, DetailView):
|
||||
model = SchoolYear
|
||||
slug_url_kwarg = 'year'
|
||||
slug_field = 'year'
|
||||
|
||||
|
||||
class SchoolYearUpdateView(
|
||||
LoginRequiredMixin, SuccessMessageMixin, UpdateView
|
||||
):
|
||||
model = SchoolYear
|
||||
slug_url_kwarg = 'year'
|
||||
slug_field = 'year'
|
||||
@ -62,7 +129,7 @@ class SchoolYearUpdateView(SuccessMessageMixin, UpdateView):
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class StudentListView(ListView):
|
||||
class StudentListView(LoginRequiredMixin, ListView):
|
||||
model = Student
|
||||
paginate_by = 50
|
||||
|
||||
@ -80,12 +147,35 @@ class StudentListView(ListView):
|
||||
return context
|
||||
|
||||
|
||||
class StudentDetailView(DetailView):
|
||||
class StudentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = Student
|
||||
pk_url_kwarg = 'student_pk'
|
||||
success_message = 'Student created.'
|
||||
template_name_suffix = '_create_form'
|
||||
form_class = StudentForm
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.school_year = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class StudentDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Student
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
|
||||
context['score_list'] = Score.objects.select_related(
|
||||
'student'
|
||||
@ -116,37 +206,619 @@ class StudentDetailView(DetailView):
|
||||
return context
|
||||
|
||||
|
||||
class StudentCreateView(SuccessMessageMixin, CreateView):
|
||||
class StudentUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
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):
|
||||
class StudentDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = Student
|
||||
pk_url_kwarg = 'student_pk'
|
||||
success_message = 'Student deleted.'
|
||||
success_url = reverse_lazy('student-list')
|
||||
|
||||
|
||||
class StudentTagListView(LoginRequiredMixin, ListView):
|
||||
model = StudentTag
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class StudentTagCreateView(
|
||||
LoginRequiredMixin, SuccessMessageMixin, CreateView
|
||||
):
|
||||
model = StudentTag
|
||||
success_message = 'Tag created.'
|
||||
template_name_suffix = '_create_form'
|
||||
fields = '__all__'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:stag-list', kwargs={
|
||||
'year': self.kwargs['year']
|
||||
})
|
||||
|
||||
|
||||
class StudentTagDetailView(LoginRequiredMixin, DetailView):
|
||||
model = StudentTag
|
||||
pk_url_kwarg = 'stag_pk'
|
||||
context_object_name = 'tag'
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = StudentTag.objects.filter(
|
||||
pk=self.kwargs.get(self.pk_url_kwarg)
|
||||
).prefetch_related(
|
||||
Prefetch(
|
||||
'student_set',
|
||||
queryset=Student.objects.filter(
|
||||
school_year__year=self.kwargs['year']
|
||||
)
|
||||
)
|
||||
)
|
||||
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 StudentTagUpdateView(
|
||||
LoginRequiredMixin, SuccessMessageMixin, UpdateView
|
||||
):
|
||||
model = StudentTag
|
||||
pk_url_kwarg = 'stag_pk'
|
||||
context_object_name = 'tag'
|
||||
success_message = 'Tag saved.'
|
||||
fields = '__all__'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:stag-list', kwargs={
|
||||
'year': self.kwargs['year']
|
||||
})
|
||||
|
||||
|
||||
class StudentTagDeleteView(
|
||||
LoginRequiredMixin, SuccessMessageMixin, DeleteView
|
||||
):
|
||||
model = StudentTag
|
||||
pk_url_kwarg = 'stag_pk'
|
||||
context_object_name = 'tag'
|
||||
success_message = 'Tag deleted.'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:stag-list', kwargs={
|
||||
'year': self.kwargs['year']
|
||||
})
|
||||
|
||||
|
||||
class SubjectListView(LoginRequiredMixin, ListView):
|
||||
model = Subject
|
||||
paginate_by = 50
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Subject.objects.filter(
|
||||
school_year__year=self.kwargs['year']
|
||||
).annotate(
|
||||
models.Count('component')
|
||||
).order_by('name')
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class SubjectCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = Subject
|
||||
success_message = 'Subject created.'
|
||||
template_name_suffix = '_create_form'
|
||||
fields = [
|
||||
'name',
|
||||
'description'
|
||||
]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.school_year = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class SubjectDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Subject
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Subject.objects.filter(
|
||||
pk=self.kwargs.get(self.pk_url_kwarg)
|
||||
).select_related(
|
||||
'school_year'
|
||||
).prefetch_related(
|
||||
Prefetch(
|
||||
'component_set',
|
||||
queryset=Component.objects.prefetch_related(
|
||||
'score_set'
|
||||
).annotate(
|
||||
grade_avg_pre=Avg('score__value')
|
||||
)
|
||||
)
|
||||
)
|
||||
return queryset
|
||||
|
||||
|
||||
class SubjectUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = Subject
|
||||
success_message = 'Subject saved.'
|
||||
fields = '__all__'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class SubjectDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = Subject
|
||||
success_message = 'Subject deleted.'
|
||||
success_url = reverse_lazy('subject-list')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class TagListView(LoginRequiredMixin, ListView):
|
||||
model = Tag
|
||||
|
||||
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 TagCreateView(
|
||||
LoginRequiredMixin, SuccessMessageMixin, CreateView
|
||||
):
|
||||
model = Tag
|
||||
success_message = 'Tag created.'
|
||||
template_name_suffix = '_create_form'
|
||||
fields = '__all__'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:tag-list', kwargs={
|
||||
'year': self.kwargs['year']
|
||||
})
|
||||
|
||||
|
||||
class TagDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Tag
|
||||
pk_url_kwarg = 'tag_pk'
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Tag.objects.filter(
|
||||
pk=self.kwargs.get(self.pk_url_kwarg)
|
||||
).prefetch_related(
|
||||
Prefetch(
|
||||
'component_set',
|
||||
queryset=Component.objects.filter(
|
||||
subject__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 TagUpdateView(
|
||||
LoginRequiredMixin, SuccessMessageMixin, UpdateView
|
||||
):
|
||||
model = Tag
|
||||
pk_url_kwarg = 'tag_pk'
|
||||
success_message = 'Tag saved.'
|
||||
fields = '__all__'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:tag-list', kwargs={
|
||||
'year': self.kwargs['year']
|
||||
})
|
||||
|
||||
|
||||
class TagDeleteView(
|
||||
LoginRequiredMixin, SuccessMessageMixin, DeleteView
|
||||
):
|
||||
model = Tag
|
||||
pk_url_kwarg = 'tag_pk'
|
||||
success_message = 'Tag deleted.'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:tag-list', kwargs={
|
||||
'year': self.kwargs['year']
|
||||
})
|
||||
|
||||
|
||||
class ComponentListView(LoginRequiredMixin, ListView):
|
||||
model = Component
|
||||
|
||||
|
||||
class ComponentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = Component
|
||||
success_message = 'Component created.'
|
||||
template_name_suffix = '_create_form'
|
||||
form_class = ComponentCreateForm
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
context['subject'] = get_object_or_404(
|
||||
Subject, pk=self.kwargs['pk']
|
||||
)
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.school_year = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
form.instance.subject = get_object_or_404(
|
||||
Subject, pk=self.kwargs['pk']
|
||||
)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ComponentDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Component
|
||||
pk_url_kwarg = 'component_pk'
|
||||
|
||||
def get_queryset(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')
|
||||
)
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
cscores = Score.objects.filter(
|
||||
component=self.object,
|
||||
student=OuterRef('pk')
|
||||
)
|
||||
context['scoreless'] = Student.objects.filter(
|
||||
school_year__year=self.kwargs['year']
|
||||
).exclude(
|
||||
score__in=cscores
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class ComponentUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = Component
|
||||
pk_url_kwarg = 'component_pk'
|
||||
success_message = 'Component saved.'
|
||||
fields = '__all__'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:component-detail', kwargs={
|
||||
'year': self.object.subject.school_year.year,
|
||||
'pk': self.object.subject.pk,
|
||||
'component_pk': self.object.pk
|
||||
})
|
||||
|
||||
|
||||
class ComponentDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = Component
|
||||
pk_url_kwarg = 'component_pk'
|
||||
success_message = 'Component deleted.'
|
||||
success_url = reverse_lazy('core:component-list')
|
||||
|
||||
|
||||
class ComponentManagerView(LoginRequiredMixin, UpdateView):
|
||||
model = Component
|
||||
pk_url_kwarg = 'component_pk'
|
||||
template_name_suffix = '_manager'
|
||||
fields = ['finished_grading']
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
cscores = Score.objects.filter(
|
||||
component=self.object,
|
||||
student=OuterRef('pk')
|
||||
)
|
||||
context['student_list'] = Student.objects.filter(
|
||||
school_year__year=self.kwargs['year']
|
||||
).annotate(
|
||||
cscore=Subquery(cscores.values('value')),
|
||||
cscore_pk=Subquery(cscores.values('pk'))
|
||||
)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:component-detail', kwargs={
|
||||
'year': self.object.subject.school_year.year,
|
||||
'pk': self.object.subject.pk,
|
||||
'component_pk': self.object.pk
|
||||
})
|
||||
|
||||
def form_valid(self, form):
|
||||
form.save()
|
||||
for key, value in self.request.POST.items():
|
||||
if 'student' in key and value:
|
||||
s_pk = key.split('_')[1]
|
||||
obj, created = Score.objects.update_or_create(
|
||||
component=self.object,
|
||||
student=Student.objects.get(pk=s_pk),
|
||||
defaults={'value': value}
|
||||
)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ScoreListView(LoginRequiredMixin, ListView):
|
||||
model = Score
|
||||
|
||||
|
||||
class ScoreCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = Score
|
||||
success_message = 'Score created.'
|
||||
template_name_suffix = '_create_form'
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class ScoreDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Score
|
||||
|
||||
|
||||
class ScoreUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = Score
|
||||
success_message = 'Score saved.'
|
||||
fields = [
|
||||
'component',
|
||||
'value'
|
||||
]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class ScoreDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = Score
|
||||
success_message = 'Score deleted.'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:schoolyear-detail', kwargs={
|
||||
'year': self.kwargs['year']
|
||||
})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class SchoolDayListView(LoginRequiredMixin, ListView):
|
||||
model = SchoolDay
|
||||
paginate_by = 7
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = SchoolDay.objects.filter(
|
||||
school_year__year=self.kwargs['year']
|
||||
)
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class SchoolDayCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = SchoolDay
|
||||
success_message = 'SchoolDay created.'
|
||||
template_name_suffix = '_create_form'
|
||||
form_class = SchoolDayForm
|
||||
|
||||
def get_initial(self):
|
||||
today = timezone.localtime(timezone.now()).date()
|
||||
initial = {
|
||||
'date': today,
|
||||
}
|
||||
return initial
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['student_list'] = Student.objects.filter(
|
||||
school_year__year=self.kwargs['year']
|
||||
)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.school_year = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
form.save()
|
||||
for key, value in self.request.POST.items():
|
||||
if 'student' in key:
|
||||
s = key.split('_')[1]
|
||||
AttendanceEntry.objects.create(
|
||||
school_day=form.instance,
|
||||
student=Student.objects.get(pk=s),
|
||||
status=value,
|
||||
)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class SchoolDayDetailView(LoginRequiredMixin, DetailView):
|
||||
model = SchoolDay
|
||||
|
||||
|
||||
class SchoolDayUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = SchoolDay
|
||||
success_message = 'SchoolDay saved.'
|
||||
form_class = SchoolDayForm
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
form.save()
|
||||
for key, value in self.request.POST.items():
|
||||
if 'student' in key:
|
||||
s = key.split('_')[1]
|
||||
AttendanceEntry.objects.filter(
|
||||
school_day=self.object,
|
||||
student=Student.objects.get(pk=s)
|
||||
).update(status=value)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class SchoolDayDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = SchoolDay
|
||||
success_message = 'SchoolDay deleted.'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:schoolday-list', kwargs={
|
||||
'year': self.kwargs['year']
|
||||
})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['school_year'] = get_object_or_404(
|
||||
SchoolYear, year=self.kwargs['year']
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class AttendanceEntryListView(LoginRequiredMixin, ListView):
|
||||
model = AttendanceEntry
|
||||
|
||||
|
||||
class AttendanceEntryCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = AttendanceEntry
|
||||
success_message = 'Entry created.'
|
||||
template_name_suffix = '_create_form'
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class AttendanceEntryDetailView(LoginRequiredMixin, DetailView):
|
||||
model = AttendanceEntry
|
||||
pk_url_kwarg = 'entry_pk'
|
||||
context_object_name = 'entry'
|
||||
|
||||
|
||||
class AttendanceEntryUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = AttendanceEntry
|
||||
pk_url_kwarg = 'entry_pk'
|
||||
context_object_name = 'entry'
|
||||
success_message = 'Entry saved.'
|
||||
form_class = AttendanceEntryForm
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:schoolday-detail', kwargs={
|
||||
'year': self.kwargs['year'],
|
||||
'pk': self.kwargs['pk']
|
||||
})
|
||||
|
||||
|
||||
class AttendanceEntryDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = AttendanceEntry
|
||||
pk_url_kwarg = 'entry_pk'
|
||||
context_object_name = 'entry'
|
||||
success_message = 'Entry deleted.'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('core:schoolday-detail', kwargs={
|
||||
'year': self.kwargs['year'],
|
||||
'pk': self.kwargs['pk']
|
||||
})
|
||||
|
||||
32184
src/db.json
Normal file
32184
src/db.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,12 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from .models import (
|
||||
Tag,
|
||||
Subject,
|
||||
Component,
|
||||
Score,
|
||||
)
|
||||
|
||||
admin.site.register(Tag)
|
||||
admin.site.register(Subject)
|
||||
admin.site.register(Component)
|
||||
admin.site.register(Score)
|
||||
@ -1,6 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class GradebookConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'gradebook'
|
||||
@ -1,43 +0,0 @@
|
||||
from django import forms
|
||||
from .models import Component, Score, Tag
|
||||
|
||||
from students.models import Student
|
||||
|
||||
class ComponentForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Component
|
||||
fields = (
|
||||
'name',
|
||||
'category',
|
||||
'due_date',
|
||||
'grade_total',
|
||||
)
|
||||
widgets = {
|
||||
'due_date': forms.DateInput(attrs = {
|
||||
'type': 'date'
|
||||
}),
|
||||
}
|
||||
|
||||
class ComponentUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Component
|
||||
fields = (
|
||||
'subject',
|
||||
'name',
|
||||
'category',
|
||||
'due_date',
|
||||
'grade_total',
|
||||
'tags',
|
||||
)
|
||||
widgets = {
|
||||
'due_date': forms.DateInput(attrs = {
|
||||
'type': 'date'
|
||||
}),
|
||||
}
|
||||
|
||||
class TagForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Tag
|
||||
fields = (
|
||||
'name',
|
||||
)
|
||||
@ -1,69 +0,0 @@
|
||||
# Generated by Django 3.2.7 on 2021-09-01 15:26
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('students', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Component',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('category', models.CharField(choices=[('QZ', 'Quiz'), ('AS', 'Assignment'), ('TS', 'Test')], default='AS', max_length=2)),
|
||||
('due_date', models.DateField()),
|
||||
('grade_total', models.PositiveIntegerField()),
|
||||
],
|
||||
options={
|
||||
'ordering': ['due_date'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Subject',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=250)),
|
||||
('description', models.CharField(blank=True, max_length=250)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Tag',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Score',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('value', models.PositiveIntegerField()),
|
||||
('component', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='gradebook.component')),
|
||||
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='students.student')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('student',),
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='component',
|
||||
name='subject',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='gradebook.subject'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='component',
|
||||
name='tags',
|
||||
field=models.ManyToManyField(blank=True, to='gradebook.Tag'),
|
||||
),
|
||||
]
|
||||
@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.2.7 on 2021-09-16 23:21
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gradebook', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='component',
|
||||
name='finished_grading',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@ -1,100 +0,0 @@
|
||||
from datetime import datetime, date
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
|
||||
from django.db.models import Count, Sum, Avg, F, Value
|
||||
from django.db.models.functions import Length, Upper
|
||||
|
||||
from students.models import Student
|
||||
|
||||
class Tag(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('tag-detail', kwargs={'pk': self.pk})
|
||||
|
||||
|
||||
class Subject(models.Model):
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
name = models.CharField(max_length=250)
|
||||
description = models.CharField(max_length=250, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('subject-detail', kwargs={'pk': self.pk})
|
||||
|
||||
|
||||
class Component(models.Model):
|
||||
class Meta:
|
||||
ordering = ['due_date']
|
||||
|
||||
CATEGORY_CHOICES = [
|
||||
('QZ', 'Quiz'),
|
||||
('AS', 'Assignment'),
|
||||
('TS', 'Test'),
|
||||
]
|
||||
|
||||
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
category = models.CharField(
|
||||
max_length = 2,
|
||||
choices = CATEGORY_CHOICES,
|
||||
default='AS',
|
||||
)
|
||||
due_date = models.DateField()
|
||||
grade_total = models.PositiveIntegerField()
|
||||
tags = models.ManyToManyField(Tag, blank=True)
|
||||
finished_grading = models.BooleanField(default=False)
|
||||
|
||||
@property
|
||||
def is_due(self):
|
||||
if self.due_date < date.today():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def grade_avg(self):
|
||||
avg = Score.objects.filter(component=self.pk).aggregate(
|
||||
Avg('value')
|
||||
)
|
||||
if avg['value__avg'] is not None:
|
||||
return round(avg['value__avg'], 2)
|
||||
else:
|
||||
return "No scores yet."
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('component-detail', kwargs={'pk': self.pk})
|
||||
|
||||
|
||||
class Score(models.Model):
|
||||
class Meta:
|
||||
ordering = ('student',)
|
||||
|
||||
component = models.ForeignKey(Component, on_delete=models.CASCADE)
|
||||
student = models.ForeignKey(Student, on_delete=models.CASCADE)
|
||||
value = models.PositiveIntegerField()
|
||||
|
||||
@property
|
||||
def grade(self):
|
||||
return f"{self.value} / {self.component.grade_total}"
|
||||
|
||||
@property
|
||||
def grade_as_percentage(self):
|
||||
return round(self.value / self.component.grade_total * 100, 2)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.student} scored: {self.value} / {self.component.grade_total}"
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('score-detail', kwargs={'pk': self.pk})
|
||||
@ -1,13 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Delete {{component}}</h1>
|
||||
<form method="post" action="{% url 'component-delete' component.subject.pk component.pk %}">
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{component}}"> or <a href="{% url 'component-detail' component.subject.pk component.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,17 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>{{subject}}</h1>
|
||||
<h2>Create Component</h2>
|
||||
<section>
|
||||
<form action="{% url 'component-create' subject.pk %}" method="POST">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p>
|
||||
<input class="action-button" type="submit" value="Create component"> or <a href="{% url 'subject-detail' subject.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,77 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<header class="generic__header">
|
||||
<h1>{{component.name}}</h1>
|
||||
<a href="{% url 'component-update' component.subject.pk component.pk %}" class="action-button">Update Component</a>
|
||||
</header>
|
||||
{% if component.finished_grading %}
|
||||
<section>
|
||||
<p>
|
||||
<span class="component__grade--graded">✓ Graded</span>
|
||||
</p>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% 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>
|
||||
<dl>
|
||||
<dt>Due Date</dt>
|
||||
<dd>{{component.due_date}}</dd>
|
||||
<dt>Description</dt>
|
||||
<dd>{{component.name}}</dd>
|
||||
<dt>Category</dt>
|
||||
<dd>{{component.get_category_display}}</dd>
|
||||
<dt>Grade Total</dt>
|
||||
<dd>{{component.grade_total}}</dd>
|
||||
</dl>
|
||||
</section>
|
||||
<section>
|
||||
<h3>Scores</h3>
|
||||
<p>
|
||||
<a class="action-button" href="{% url 'component-manager' component.subject.pk component.pk %}">Bulk Edit Scores</a>
|
||||
</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Student</td>
|
||||
<td colspan="2">Score</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for score in component.score_set.all %}
|
||||
<tr>
|
||||
<td><a href="{% url 'student-detail' score.student.pk %}">{{score.student.student_id}} — {{score.student}}</a></td>
|
||||
<td>{{score.value}}</td>
|
||||
<td><a href="{% url 'score-update' score.pk %}">Edit score</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td><strong>Avg Score</strong></td>
|
||||
<td><strong>{{component.grade_avg_pre|floatformat:2}}</strong></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% if scoreless %}
|
||||
<section>
|
||||
|
||||
<h3>Scoreless</h3>
|
||||
<ul>
|
||||
{% for student in scoreless %}
|
||||
<li>{{student}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,19 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<div class="generic__header">
|
||||
<h1>Update Component</h1>
|
||||
<a class="action-button action-delete" href="{% url 'component-delete' component.subject.pk component.pk %}">Delete Component</a>
|
||||
</div>
|
||||
<section>
|
||||
<form action="{% url 'component-update' component.subject.pk component.pk %}" method="POST">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p>
|
||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'component-detail' component.subject.pk component.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,41 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load gradebook_filters %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>{{component}}, <em>{{component.subject}}</em></h1>
|
||||
<section>
|
||||
<form action="{% url 'component-manager' component.subject.pk component.pk %}" method="POST">
|
||||
{% csrf_token %}
|
||||
<h3>Enter Scores</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Out of:</td>
|
||||
<td>{{component.grade_total}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Student</td>
|
||||
<td>Score</td>
|
||||
{% if formset.errors %}
|
||||
<td>Errors</td>
|
||||
{% 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}}"></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{form.as_p}}
|
||||
<p>
|
||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'component-detail' component.subject.pk component.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,13 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Delete {{score}}</h1>
|
||||
<form method="post" action="{% url 'score-delete' score.pk %}">
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{score}}"> or <a href="{% url 'score-detail' score.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,16 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Create Score</h1>
|
||||
<section>
|
||||
<form action="{% url 'score-create' %}" method="POST">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p>
|
||||
<input class="action-button" type="submit" value="Create Score"> or <a href="{% url 'subject-list' %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,19 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Update Score</h1>
|
||||
<h2>{{score.component.subject}}— <em>{{score.component.get_category_display}}</em>: {{score.component}}</h2>
|
||||
<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 %}
|
||||
@ -1,83 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Search Results</h1>
|
||||
{% if student_list.count > 0 %}
|
||||
<section>
|
||||
<h3>Students</h3>
|
||||
<ul>
|
||||
{% for student in student_list %}
|
||||
<li class="student">
|
||||
<a class="student__li" href="{% url 'student-detail' student.pk %}">{{student.student_id}} — {{student.full_name}}</a>
|
||||
{% if student.sit %}
|
||||
<span class="student__attribute">SIT: {{student.get_sit_display}}</span>
|
||||
{% endif %}
|
||||
{% if student.iep_behavioral %}
|
||||
<span class="student__attribute">IEP behavioral</span>
|
||||
{% endif %}
|
||||
{% if student.iep_math %}
|
||||
<span class="student__attribute">IEP math</span>
|
||||
{% endif %}
|
||||
{% if student.iep_ela %}
|
||||
<span class="student__attribute">IEP ELA</span>
|
||||
{% endif %}
|
||||
{% if student.parent__count > 0 %}
|
||||
<br>Parents:
|
||||
{% for parent in student.parent_set.all %}
|
||||
<a href="{% url 'parent-detail' parent.pk %}">{{parent.full_name}}</a>{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% if component_list.count > 0 %}
|
||||
<section>
|
||||
<h3>Components</h3>
|
||||
<ul class="search__results">
|
||||
<li class="search__result">
|
||||
<strong>Subject</strong>
|
||||
<strong>Component</strong>
|
||||
<strong>Due Date</strong>
|
||||
<strong>Tags</strong>
|
||||
</li>
|
||||
<hr>
|
||||
{% for component in component_list %}
|
||||
<li class="search__result">
|
||||
{{component.subject}}
|
||||
<a href="{% url 'component-detail' component.subject.pk component.pk %}">{{component}}</a>
|
||||
<span>{{component.due_date}}</span>
|
||||
<span>
|
||||
{% for tag in component.tags.all %}
|
||||
<a class="tag__item" href="{% url 'tag-detail' tag.pk %}">{{tag.name}}</a>
|
||||
{% endfor %}
|
||||
</span>
|
||||
</li>
|
||||
{% empty %}
|
||||
<p>No components by that name were found.</p>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% if message_list.count > 0 %}
|
||||
<section>
|
||||
<h3>Messages</h3>
|
||||
<ul class="search__results">
|
||||
{% for message in message_list %}
|
||||
<li class="search__mresult">
|
||||
<p>
|
||||
<a href="{% url 'thread-detail' message.thread.pk %}">{{message.thread.subject}}</a><br>
|
||||
{{message.content|truncatewords:25}}<br>
|
||||
<a class="" href="{% url 'thread-detail' message.thread.pk %}">Read more →</a>
|
||||
</p>
|
||||
</li>
|
||||
{% empty %}
|
||||
<p>No components by that name were found.</p>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,13 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Delete {{subject}} Subject</h1>
|
||||
<form method="post" action="{% url 'subject-delete' subject.pk %}">
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{subject}}"> or <a href="{% url 'subject-list' %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,16 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<h1>Create Subject</h1>
|
||||
<section>
|
||||
<form action="{% url 'subject-create' %}" method="POST">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p>
|
||||
<input class="action-button" type="submit" value="Create Subject"> or <a href="{% url 'subject-list' %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,41 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<div class="generic__header">
|
||||
<h1>{{subject.name}}</h1>
|
||||
<a class="action-button" href="{% url 'subject-update' subject.pk %}">Update subject details</a>
|
||||
</div>
|
||||
{% if subject.description %}
|
||||
<span>{{subject.description}}</span>
|
||||
{% endif %}
|
||||
<section>
|
||||
<h3>Syllabus</h3>
|
||||
<p>
|
||||
<a href="{% url 'component-create' subject.pk %}" class="action-button">+ New component</a>
|
||||
</p>
|
||||
<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>
|
||||
<td>{{component.due_date}}</td>
|
||||
<td><a href="{% url 'component-detail' subject.pk component.pk %}">{{component.name}}</a></td>
|
||||
<td>{{component.get_category_display}}</td>
|
||||
<td>{{component.grade_total}}</td>
|
||||
<td>{{component.grade_avg_pre|floatformat:2}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -1,19 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="panel">
|
||||
<div class="generic__header">
|
||||
<h1>Update Subject</h1>
|
||||
<a class="action-button action-delete" href="{% url 'subject-delete' subject.pk %}">Delete subject</a>
|
||||
</div>
|
||||
<section>
|
||||
<form action="{% url 'subject-update' subject.pk %}" method="POST">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p>
|
||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'subject-detail' subject.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user