Merge branch 'release/2.0.0'

This commit is contained in:
Nathan Chapman 2022-08-03 09:49:16 -06:00
commit a1e7da1d99
151 changed files with 34823 additions and 3291 deletions

View File

@ -25,3 +25,9 @@ class User(AbstractUser):
choices=TIMEZONE_CHOICES, choices=TIMEZONE_CHOICES,
default=MOUNTAIN 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

View File

@ -1,72 +1,23 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %}
{% block head_title %}Profile |{% endblock %}
{% block content %} {% block content %}
<article class="panel"> <article class="detail">
<header> <header class="detail__header">
<h1 class="greeting"><em>Welcome {{user.first_name}} {{user.last_name }}</em> <div>
<br> <h1>{{ user }}</h1>
Here's what's going on today <p><small>Joined: <time datetime="{{ user.date_joined|date:'Y-m-d' }}">{{ user.date_joined|date:'M d Y' }}</time></p>
</h1> </div>
<a href="{% url 'account-update' user.pk %}" class="action-button">Edit</a>
</header> </header>
<section> <section class="detail__info">
<h3 class="domain__heading">Birthdays</h3> <dl class="detail__datalist">
<ul> <dt>Username</dt>
{% for student in birthdays %} <dd>{{ user.username }}</dd>
<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> <dt>Email address</dt>
{% empty %} <dd>{{ user.email }}</dd>
<p>No Birthdays this next week.</p> </dl>
{% 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> </section>
</article> </article>
{% endblock %} {% endblock %}

View File

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

View File

@ -2,10 +2,21 @@ from django.urls import path, include
from . import views from . import views
urlpatterns = [ urlpatterns = [
path('', views.AccountListView.as_view(), name='account-list'),
path('<int:pk>/', include([ path('<int:pk>/', include([
path('', views.AccountDetailView.as_view(), name='account-detail'), path(
path('update/', views.AccountUpdateView.as_view(), name='account-update'), '',
path('delete/', views.AccountDeleteView.as_view(), name='account-delete'), 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'
),
])), ])),
] ]

View File

@ -12,48 +12,14 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.forms import PasswordChangeForm 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 .models import User
from .forms import AccountUpdateForm from .forms import AccountUpdateForm
class AccountListView(LoginRequiredMixin, ListView):
model = User
template_name = 'accounts/account_list.html'
class AccountDetailView(LoginRequiredMixin, DetailView): class AccountDetailView(LoginRequiredMixin, DetailView):
model = User model = User
template_name = 'accounts/account_detail.html' 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): class AccountUpdateView(LoginRequiredMixin, UpdateView):
model = User model = User

View File

@ -1,6 +0,0 @@
from django.contrib import admin
from .models import Day, Entry
admin.site.register(Day)
admin.site.register(Entry)

View File

@ -1,6 +0,0 @@
from django.apps import AppConfig
class AttendanceConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'attendance'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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">&laquo; first</a>
<a class="minor-action-button" href="?page={{ page_obj.previous_page_number }}">&lsaquo; 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 &rsaquo;</a>
<a class="minor-action-button" href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
{% endif %}
</span>
</section>
</article>
{% endblock %}

View File

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

View File

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

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

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

View File

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

View File

@ -1,3 +1,23 @@
from django.contrib import admin 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)

View File

@ -0,0 +1,8 @@
from .models import SchoolYear
from django.shortcuts import get_object_or_404
def current_year(request):
return {
'current_year': SchoolYear.objects.latest('year')
}

View File

@ -16,22 +16,56 @@ from .models import (
class SchoolYearCreateForm(forms.ModelForm): class SchoolYearCreateForm(forms.ModelForm):
class Meta: class Meta:
model = SchoolYear model = SchoolYear
fields = [ fields = ['year']
'year'
]
class StudentForm(forms.ModelForm): class StudentForm(forms.ModelForm):
class Meta: class Meta:
model = Student model = Student
fields = ( fields = [
'student_id', 'student_id',
'first_name', 'first_name',
'last_name', 'last_name',
'address', 'address',
'dob', 'dob',
) 'allergies',
'tags'
]
labels = { labels = {
'student_id': 'Student ID', 'student_id': 'Student ID',
'dob': 'DOB', 'dob': 'DOB'
}
class ComponentCreateForm(forms.ModelForm):
class Meta:
model = Component
fields = [
'name',
'category',
'due_date',
'grade_total'
]
widgets = {
'due_date': forms.DateInput(attrs={'type': 'date'})
}
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()
} }

View File

@ -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 from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
@ -9,16 +9,41 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('contenttypes', '0002_remove_content_type_name'),
] ]
operations = [ 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( migrations.CreateModel(
name='SchoolYear', name='SchoolYear',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('start_date', models.DateField()), ('year', models.PositiveIntegerField(unique=True)),
('end_date', models.DateField()), ('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( migrations.CreateModel(
@ -26,6 +51,8 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)), ('name', models.CharField(max_length=50)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
], ],
options={ options={
'ordering': ['name'], 'ordering': ['name'],
@ -37,21 +64,14 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=250)), ('name', models.CharField(max_length=250)),
('description', models.CharField(blank=True, 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')), ('school_year', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.schoolyear')),
], ],
options={ options={
'ordering': ['name'], '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( migrations.CreateModel(
name='Student', name='Student',
fields=[ fields=[
@ -61,44 +81,58 @@ class Migration(migrations.Migration):
('last_name', models.CharField(max_length=50)), ('last_name', models.CharField(max_length=50)),
('address', models.TextField(blank=True)), ('address', models.TextField(blank=True)),
('dob', models.DateField()), ('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')), ('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={ options={
'ordering': ['student_id', 'first_name'], '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( migrations.CreateModel(
name='SchoolDay', name='SchoolDay',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField()), ('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')), ('school_year', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.schoolyear')),
], ],
options={ options={
'ordering': ['-date'], 'ordering': ['-date'],
}, },
), ),
migrations.CreateModel( migrations.AddField(
name='Component', model_name='component',
fields=[ name='subject',
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.subject'),
('name', models.CharField(max_length=50)), ),
('category', models.CharField(choices=[('QUIZ', 'Quiz'), ('ASSIGNMENT', 'Assignment'), ('TEST', 'Test')], default='ASSIGNMENT', max_length=255)), migrations.AddField(
('due_date', models.DateField()), model_name='component',
('grade_total', models.PositiveIntegerField()), name='tags',
('finished_grading', models.BooleanField(default=False)), field=models.ManyToManyField(blank=True, to='core.tag'),
('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.CreateModel( migrations.CreateModel(
name='AttendanceEntry', name='AttendanceEntry',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('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)), ('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')), ('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')), ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.student')),
], ],
@ -107,8 +141,4 @@ class Migration(migrations.Migration):
'ordering': ['student'], 'ordering': ['student'],
}, },
), ),
migrations.AddIndex(
model_name='studenttag',
index=models.Index(fields=['content_type', 'object_id'], name='core_studen_content_a7305d_idx'),
),
] ]

View File

@ -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),
),
]

View File

@ -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,
),
]

View File

@ -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),
),
]

View File

@ -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),
),
]

View File

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

View File

@ -5,6 +5,9 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import ( from django.contrib.contenttypes.fields import (
GenericForeignKey, GenericRelation GenericForeignKey, GenericRelation
) )
from django.db.models import (
Exists, OuterRef, Prefetch, Subquery, Count, Sum, Avg, F, Q, Value
)
class SchoolYear(models.Model): class SchoolYear(models.Model):
@ -27,21 +30,13 @@ class SchoolYear(models.Model):
class StudentTag(models.Model): class StudentTag(models.Model):
tag = models.SlugField() name = models.CharField(max_length=255)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
def __str__(self): def __str__(self):
return self.tag return self.name
class Meta:
indexes = [
models.Index(fields=['content_type', 'object_id']),
]
# class StudentManager(models.Manager): # class StudentManager(models.Manager):
@ -66,8 +61,9 @@ class Student(models.Model):
last_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50)
address = models.TextField(blank=True) address = models.TextField(blank=True)
dob = models.DateField() 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) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
@ -89,7 +85,7 @@ class Student(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('core:student-detail', kwargs={ return reverse('core:student-detail', kwargs={
'year': self.school_year.year, 'year': self.school_year.year,
'student_pk': self.pk 'pk': self.pk
}) })
class Meta: class Meta:
@ -114,7 +110,10 @@ class Subject(models.Model):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
return reverse('subject-detail', kwargs={'pk': self.pk}) return reverse('core:subject-detail', kwargs={
'year': self.school_year.year,
'pk': self.pk
})
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
@ -180,7 +179,11 @@ class Component(models.Model):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
return reverse('component-detail', kwargs={'pk': self.pk}) return reverse('core:component-detail', kwargs={
'year': self.school_year.year,
'pk': self.subject.pk,
'component_pk': self.pk
})
class Meta: class Meta:
ordering = ['due_date'] ordering = ['due_date']
@ -203,7 +206,11 @@ class Score(models.Model):
return f'{self.student} scored: {self.value} / {self.component.grade_total}' return f'{self.student} scored: {self.value} / {self.component.grade_total}'
def get_absolute_url(self): def get_absolute_url(self):
return reverse('score-detail', kwargs={'pk': self.pk}) return reverse('core:component-detail', kwargs={
'year': self.component.subject.school_year.year,
'pk': self.component.subject.pk,
'component_pk': self.component.pk
})
class Meta: class Meta:
ordering = ['student'] ordering = ['student']
@ -222,6 +229,12 @@ class SchoolDay(models.Model):
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=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): def __str__(self):
return f'{self.date}' return f'{self.date}'
@ -251,8 +264,15 @@ class AttendanceEntry(models.Model):
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=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): def __str__(self):
return f"{self.day} | {self.student} | {self.status}" return f"{self.school_day} | {self.student} | {self.status}"
class Meta: class Meta:
verbose_name_plural = 'entries' verbose_name_plural = 'entries'

View 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 %}

View 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 %}

View 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 %}

View 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 %}

View 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 %}

View 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 %}

View File

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

View File

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

View File

@ -0,0 +1,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 %}

View File

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

View File

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

View File

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

View 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 %}

View 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 }} &ensp; / &ensp; {{ 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 %}

View 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 %}

View 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 }} &ensp; / &ensp; {{ 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 %}

View 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 &rarr;</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 %}

View File

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

View File

@ -1,34 +1,31 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block head_title %}Project {{ schoolyear.name }} | {% endblock head_title %} {% block head_title %}Year {{ schoolyear.year }} | {% endblock head_title %}
{% block content %} {% block content %}
<article class="detail"> <article class="detail">
<header class="detail__header"> <header class="detail__header">
<div> <div>
<h1>{{ schoolyear }}</h1> <h1>{{ schoolyear }}</h1>
<p><small>Created <time datetime="{{ schoolyear.created_at|date:'Y-m-d' }}">{{ schoolyear.created_at }}</time></small></p>
</div> </div>
{% if perms.core.can_change_schoolyear %}
<a href="{% url 'core:schoolyear-update' schoolyear.year %}" class="action-button">Edit</a> <a href="{% url 'core:schoolyear-update' schoolyear.year %}" class="action-button">Edit</a>
{% endif %}
</header> </header>
<div class="tools"> <section class="tools">
<a href="{% url 'core:student-list' schoolyear.year %}" class="tool"> <a href="{% url 'core:student-list' schoolyear.year %}" class="tool">
<h3>Students</h3> <h3>Students</h3>
<p>Tool Description</p> <p>Tool Description</p>
<span>&rarr;</span> <span>&rarr;</span>
</a> </a>
<a href="" class="tool"> <a href="{% url 'core:schoolday-list' schoolyear.year %}" class="tool">
<h3>Attendence</h3> <h3>Attendence</h3>
<p>Tool Description</p> <p>Tool Description</p>
<span>&rarr;</span> <span>&rarr;</span>
</a> </a>
<a href="" class="tool"> <a href="{% url 'core:subject-list' schoolyear.year %}" class="tool">
<h3>Subjects</h3> <h3>Subjects</h3>
<p>Tool Description</p> <p>Tool Description</p>
<span>&rarr;</span> <span>&rarr;</span>
</a> </a>
</div> </section>
</article> </article>
{% endblock %} {% endblock %}

View File

@ -1,12 +1,18 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block head_title %}Edit Year {{ schoolyear.year }} | {% endblock head_title %}
{% block content %} {% block content %}
<h1>Update Schoolyear</h1> <article class="form">
<form method="POST" action="{% url 'core:schoolyear-update' schoolyear.year %}"> <header class="form_header">
{% csrf_token %} <h1>Edit Schoolyear</h1>
{{ form.as_p }} </header>
<p> <form method="POST" action="{% url 'core:schoolyear-update' schoolyear.year %}">
<input type="submit" value="Save changes"> or <a href="{% url 'core:schoolyear-detail' schoolyear.year %}">cancel</a> {% csrf_token %}
</p> {{ form.as_p }}
</form> <p>
<input type="submit" value="Save changes"> or <a href="{% url 'core:schoolyear-detail' schoolyear.year %}">cancel</a>
</p>
</form>
</article>
{% endblock %} {% endblock %}

View File

@ -8,38 +8,21 @@
<header class="list__header"> <header class="list__header">
<div class="list__title"> <div class="list__title">
<h1>School Years</h1> <h1>School Years</h1>
{% if perms.core.can_add_schoolyear %}
<a href="{% url 'core:schoolyear-create' %}" class="action-button">+ New Year</a> <a href="{% url 'core:schoolyear-create' %}" class="action-button">+ New Year</a>
{% endif %}
</div> </div>
</header> </header>
<table class="schoolyears list__table"> <section class="schoolyears__list">
<thead> {% for schoolyear in schoolyear_list %}
<tr> <a href="{% url 'core:schoolyear-detail' schoolyear.year %}" class="schoolyear">
<th>Created</th> <h2>{{ schoolyear.year }}</h2>
<th>Year</th> <h5>Students: ({{ schoolyear.student__count }}) &emsp; Subjects: ({{ schoolyear.subject__count }})</h5>
<th>Students</th> <span>&rarr;</span>
<th>Components</th> </a>
<th>Last Updated</th> {% empty %}
</tr> <h3>No school years yet</h3>
</thead> {% endfor %}
<tbody class="schoolyears__list"> </section>
{% 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>
{% include 'core/partials/pagination.html' %} {% include 'core/partials/pagination.html' %}
</article> </article>

View File

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

View File

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

View File

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

View File

@ -0,0 +1,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 }}&mdash; <em>{{ score.component.get_category_display }}</em>: {{ score.component }}</h2>
</div>
<p><a href="{% url 'core:score-delete' school_year.year score.pk %}" class="action-button action-delete">Delete</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 %}

View File

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

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load helpers %} {% 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 %} {% block breadcrumbs %}
<div class="breadcrumbs"> <div class="breadcrumbs">
@ -23,77 +23,87 @@
</div> </div>
<a href="{% url 'core:student-update' student.school_year.year student.pk %}" class="action-button">Edit</a> <a href="{% url 'core:student-update' student.school_year.year student.pk %}" class="action-button">Edit</a>
</header> </header>
<section class="student__details"> <section class="detail__info">
<dl> <dl class="detail__datalist">
{% if student.allergies %}
<dt>Allergies</dt>
<dd>{{ student.allergies }}</dd>
{% endif %}
<dt>Birthday</dt> <dt>Birthday</dt>
<dd>{{ student.dob }}</dd> <dd>{{ student.dob }}</dd>
<dt>Age</dt> <dt>Age</dt>
<dd>{{ student.age }}</dd> <dd>{{ student.age }}</dd>
<dt>Allergies</dt>
<dd>{% if student.allergies %}<span class="tag tag__warning">{{ student.allergies }}</span>{% endif %}</dd>
{% if student.address %} {% if student.address %}
<dt>Address</dt> <dt>Address</dt>
<dd> <dd>
<address>{{ student.address|linebreaksbr }}</address> <address>{{ student.address|linebreaksbr }}</address>
</dd> </dd>
{% endif %} {% 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> </dl>
</section> </section>
<section class="grades"> <section class="grades">
<h2>Grades</h2> <h2>Grades</h2>
<table> <table>
<thead> <thead>
<th>Subject</th> <tr>
<th>Grade</th> <th>Subject</th>
<th>Grade</th>
</tr>
</thead> </thead>
<tbody> <tbody>
{% for subject in subject_list %} {% for subject in subject_list %}
<tr> <tr>
<td class="grade"><em>{{subject}}</em></td> <td class="grade"><h5>{{ subject }}</h5> {{ subject.description }}</td>
<td class="grade">{{subject.grade|grade_as_percentage:subject.grade_total}}%</td> <td class="grade">{{ subject.grade|grade_as_percentage:subject.grade_total }}%</td>
</tr> </tr>
{% empty %} {% empty %}
<tr> <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> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</section> </section>
<section> <section>
<h2>Components</h2> <h2>Scores</h2>
<div> <div class="student__components">
{% regroup score_list by component.subject as score_list %} {% regroup score_list by component.subject as score_list %}
{% for subject in score_list %} {% for subject in score_list %}
<h4>{{subject.grouper}}</h4> <div class="student__component">
<table> <h4>{{ subject.grouper }}</h4>
<thead> <table>
<tr> <thead>
<td>Due Date</td> <tr>
<td>Component</td> <th>Due Date</th>
<td>Category</td> <th>Component</th>
<td>Score</td> <th>Category</th>
<td>Total</td> <th>Score</th>
<td colspan="2">Percentage</td> <th>Total</th>
</tr> <th colspan="2">Percentage</th>
</thead> </tr>
<tbody> </thead>
{% for score in subject.list %} <tbody>
<tr> {% for score in subject.list %}
<td>{{score.component.due_date}}</td> <tr>
<td><a href="{% url 'component-detail' score.component.subject.pk score.component.pk %}">{{score.component}}</a></td> <td>{{ score.component.due_date }}</td>
<td>{{score.component.get_category_display}}</td> <td>{{ score.component }}</td>
<td>{{score.value}}</td> <td>{{ score.component.get_category_display }}</td>
<td>{{score.component.grade_total}}</td> <td>{{ score.value }}</td>
<td>{{score.grade_as_percentage}}%</td> <td>{{ score.component.grade_total }}</td>
<td><a href="{% url 'score-update' score.pk %}?return_to={{request.get_full_path}}">Change score</a></td> <td>{{ score.grade_as_percentage }}%</td>
</tr> <td><a href="{% url 'core:score-update' student.school_year.year score.pk %}">Edit</a></td>
{% endfor %} </tr>
</tbody> {% endfor %}
</table> </tbody>
</table>
</div>
{% empty %} {% empty %}
<p>No components graded yet.</p> <p>No components graded yet.</p>
{% endfor %} {% endfor %}
@ -109,15 +119,14 @@
<table> <table>
<thead> <thead>
<tr> <tr>
<td>Date</td> <th colspan="2">Date</th>
<td colspan="2">Status</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for entry in status.list %} {% for entry in status.list %}
<tr> <tr>
<td>{{entry.day.date}}</td> <td>{{ entry.school_day.date }}</td>
<td><a href="{% url 'entry-update' entry.pk %}">Update</a></td> <td><a href="{% url 'core:entry-update' entry.school_day.school_year.year entry.school_day.pk entry.pk %}">Edit</a></td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

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

View File

@ -18,13 +18,16 @@
<h1>Students</h1> <h1>Students</h1>
<a href="{% url 'core:student-create' school_year.year %}" class="action-button">+ New Student</a> <a href="{% url 'core:student-create' school_year.year %}" class="action-button">+ New Student</a>
</div> </div>
<a href="">Student Tags &rarr;</a> <a href="{% url 'core:stag-list' school_year.year %}">Student Tags &rarr;</a>
</header> </header>
<table class="list__table"> <table class="list__table">
<thead> <thead>
<tr> <tr>
<th><a href="?order_by=record_num&direction=asc">Student No. &varr;</a></th> <th><a href="?order_by=record_num&sort=asc">Student No. &varr;</a></th>
<th>Name</th> <th>Name</th>
<th>Age</th>
<th>Birthday</th>
<th>Allergies</th>
<th>Tags</th> <th>Tags</th>
</tr> </tr>
</thead> </thead>
@ -32,12 +35,19 @@
{% for student in student_list %} {% for student in student_list %}
<tr class="has-link" onclick="document.location='{% url 'core:student-detail' school_year.year student.pk %}'"> <tr class="has-link" onclick="document.location='{% url 'core:student-detail' school_year.year student.pk %}'">
<td>{{ student.student_id }}</td> <td>{{ student.student_id }}</td>
<td>{{ student }}</td> <td><h5>{{ student }}</h5></td>
<td>(tags)</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> </tr>
{% empty %} {% empty %}
<tr> <tr>
<td colspan="7">No students yet.</td> <td colspan="6">No students yet.</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View 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 %}

View 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 %}

View 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. &varr;</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 %}

View 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 %}

View 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 %}

View File

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

View File

@ -0,0 +1,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 %}

View 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 %}

View 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 %}

View File

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

View 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 %}

View 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 %}

View 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 %}

View 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 %}

View 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 %}

View File

@ -1,19 +1,18 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %} {% load static %}
{% block head_title %}Today |{% endblock %}
{% block content %} {% block content %}
<article class="panel"> <article class="panel">
<header> <header>
<h1 class="greeting"><em>Welcome {{profile.user.first_name}} {{profile.user.last_name }}</em> <h1>Here's what's going on today</h1>
<br>
Here's what's going on today
</h1>
</header> </header>
<section> <section>
<h3 class="domain__heading">Birthdays</h3> <h3 class="domain__heading">Birthdays this week</h3>
<ul> <ul>
{% for student in birthdays %} {% 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 %} {% empty %}
<p>No Birthdays this next week.</p> <p>No Birthdays this next week.</p>
{% endfor %} {% endfor %}
@ -24,7 +23,7 @@
<ul> <ul>
{% for component in components %} {% for component in components %}
<li> <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> </li>
{% empty %} {% empty %}
<p>Nothing for today.</p> <p>Nothing for today.</p>
@ -34,7 +33,7 @@
<section> <section>
<h3 class="domain__heading">Today's Attendance</h3> <h3 class="domain__heading">Today's Attendance</h3>
{% for day in attendance %} {% 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> <table>
<thead> <thead>
<tr> <tr>
@ -43,17 +42,17 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for entry in day.entry_set.all %} {% for entry in day.attendanceentry_set.all %}
<tr> <tr>
<td>{{entry.student}}</td> <td>{{entry.student}}</td>
<td>{{entry.get_status_display}}</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> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% empty %} {% 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 &rarr;</a></p>
{% endfor %} {% endfor %}
</section> </section>
<section> <section>
@ -61,7 +60,7 @@
<ul> <ul>
{% for component in ungraded_components %} {% for component in ungraded_components %}
<li> <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> </li>
{% empty %} {% empty %}
<p>Everything is graded to far.</p> <p>Everything is graded to far.</p>

View File

@ -2,6 +2,8 @@ from django.urls import path, include
from . import views from . import views
urlpatterns = [ urlpatterns = [
path('', views.HomeView.as_view(), name='home'),
# SchoolYears # SchoolYears
path('years/', include([ path('years/', include([
path( path(
@ -25,6 +27,7 @@ urlpatterns = [
views.SchoolYearUpdateView.as_view(), views.SchoolYearUpdateView.as_view(),
name='schoolyear-update' name='schoolyear-update'
), ),
path('today/', views.TodayView.as_view(), name='today'),
# Students # Students
path('students/', include([ path('students/', include([
@ -38,7 +41,7 @@ urlpatterns = [
views.StudentCreateView.as_view(), views.StudentCreateView.as_view(),
name='student-create' name='student-create'
), ),
path('<int:student_pk>/', include([ path('<int:pk>/', include([
path( path(
'', '',
views.StudentDetailView.as_view(), views.StudentDetailView.as_view(),
@ -55,6 +58,228 @@ urlpatterns = [
name='student-delete' 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'
),
])),
])), ])),
])), ])),
])), ])),

View File

@ -1,3 +1,4 @@
import datetime as dt
from django.conf import settings from django.conf import settings
from django.utils import timezone from django.utils import timezone
from django.db import models from django.db import models
@ -33,28 +34,94 @@ from .models import (
) )
from .forms 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 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): class SchoolYearCreateView(
model = SchoolYear LoginRequiredMixin, SuccessMessageMixin, CreateView
slug_url_kwarg = 'year' ):
slug_field = 'year'
class SchoolYearCreateView(SuccessMessageMixin, CreateView):
model = SchoolYear model = SchoolYear
success_message = 'SchoolYear created.' success_message = 'SchoolYear created.'
form_class = SchoolYearCreateForm form_class = SchoolYearCreateForm
template_name_suffix = '_create_form' 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 model = SchoolYear
slug_url_kwarg = 'year' slug_url_kwarg = 'year'
slug_field = 'year' slug_field = 'year'
@ -62,7 +129,7 @@ class SchoolYearUpdateView(SuccessMessageMixin, UpdateView):
fields = '__all__' fields = '__all__'
class StudentListView(ListView): class StudentListView(LoginRequiredMixin, ListView):
model = Student model = Student
paginate_by = 50 paginate_by = 50
@ -80,12 +147,35 @@ class StudentListView(ListView):
return context return context
class StudentDetailView(DetailView): class StudentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
model = Student 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): def get_context_data(self, **kwargs):
context = super().get_context_data(**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( context['score_list'] = Score.objects.select_related(
'student' 'student'
@ -116,37 +206,619 @@ class StudentDetailView(DetailView):
return context return context
class StudentCreateView(SuccessMessageMixin, CreateView): class StudentUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
model = Student 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.' success_message = 'Student saved.'
fields = '__all__' fields = '__all__'
class StudentDeleteView(SuccessMessageMixin, DeleteView): class StudentDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
model = Student model = Student
pk_url_kwarg = 'student_pk'
success_message = 'Student deleted.' success_message = 'Student deleted.'
success_url = reverse_lazy('student-list') success_url = reverse_lazy('student-list')
class 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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,6 +0,0 @@
from django.apps import AppConfig
class GradebookConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'gradebook'

View File

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

View File

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

View File

@ -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),
),
]

View File

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

View File

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

View File

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

View File

@ -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}} &mdash; {{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 %}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,19 +0,0 @@
{% extends "base.html" %}
{% block content %}
<article class="panel">
<h1>Update Score</h1>
<h2>{{score.component.subject}}&mdash; <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 %}

View File

@ -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}} &mdash; {{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 &rarr;</a>
</p>
</li>
{% empty %}
<p>No components by that name were found.</p>
{% endfor %}
</ul>
</section>
{% endif %}
</article>
{% endblock %}

View File

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

View File

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

View File

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

View File

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