Delete old apps and refactor code
This commit is contained in:
parent
b1bbb2b1c2
commit
4ed3e1fd9d
@ -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
|
||||||
|
|||||||
@ -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 %}
|
||||||
|
|||||||
@ -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 %}
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<header>
|
|
||||||
<h1 class="greeting"><em>Welcome {{profile.user.first_name}} {{profile.user.last_name }}</em>
|
|
||||||
<br>
|
|
||||||
Here's what's going on today
|
|
||||||
</h1>
|
|
||||||
</header>
|
|
||||||
<section>
|
|
||||||
<h3 class="domain__heading">Birthdays</h3>
|
|
||||||
<ul>
|
|
||||||
{% for student in birthdays %}
|
|
||||||
<li><strong><a href="{% url 'student-detail' student.pk %}">{{student}}</a></strong> is turning {{student.age|add:1}} on {{student.dob|date:"M j"}}</li>
|
|
||||||
{% empty %}
|
|
||||||
<p>No Birthdays this next week.</p>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h3 class="domain__heading">Today's Assignments</h3>
|
|
||||||
<ul>
|
|
||||||
{% for component in components %}
|
|
||||||
<li>
|
|
||||||
{{component.subject}}, <a href="{% url 'component-detail' component.subject.pk component.pk %}">{{component}}</a>
|
|
||||||
</li>
|
|
||||||
{% empty %}
|
|
||||||
<p>Nothing for today.</p>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h3 class="domain__heading">Today's Attendance</h3>
|
|
||||||
{% for day in attendance %}
|
|
||||||
<p><strong><a href="{% url 'day-update' day.pk %}">{{day.date}}</a></strong></p>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td>
|
|
||||||
<td colspan="2">Status</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for entry in day.entry_set.all %}
|
|
||||||
<tr>
|
|
||||||
<td>{{entry.student}}</td>
|
|
||||||
<td>{{entry.get_status_display}}</td>
|
|
||||||
<td><a href="{% url 'entry-update' entry.pk %}">Update</a></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% empty %}
|
|
||||||
<p class="greeting">No attendance taken yet: <a href="{% url 'day-create' %}" class="action-button">Take attendance</a></p>
|
|
||||||
{% endfor %}
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h3 class="domain__heading">Assignments to be graded</h3>
|
|
||||||
<ul>
|
|
||||||
{% for component in ungraded_components %}
|
|
||||||
<li>
|
|
||||||
{{component.subject}}, <a href="{% url 'component-detail' component.subject.pk component.pk %}">{{component}}</a>
|
|
||||||
</li>
|
|
||||||
{% empty %}
|
|
||||||
<p>Everything is graded to far.</p>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -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'
|
||||||
|
),
|
||||||
])),
|
])),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
from .models import Day, Entry
|
|
||||||
|
|
||||||
admin.site.register(Day)
|
|
||||||
admin.site.register(Entry)
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class AttendanceConfig(AppConfig):
|
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
|
||||||
name = 'attendance'
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
from django import forms
|
|
||||||
from django.utils import timezone
|
|
||||||
from .models import Day, Entry
|
|
||||||
|
|
||||||
from students.models import Student
|
|
||||||
|
|
||||||
|
|
||||||
class DayForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Day
|
|
||||||
fields = ('date',)
|
|
||||||
widgets = {
|
|
||||||
'date': forms.DateInput(attrs = {
|
|
||||||
'type': 'date',
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
class EntryForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Entry
|
|
||||||
fields = ('day', 'student', 'status')
|
|
||||||
widgets = {
|
|
||||||
'student': forms.HiddenInput()
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
# Generated by Django 3.2.7 on 2021-09-01 15:26
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('students', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Day',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('date', models.DateField()),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'ordering': ('-date',),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Entry',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('status', models.CharField(choices=[('P', 'Present'), ('T', 'Tardy'), ('A', 'Absent')], default='P', max_length=1)),
|
|
||||||
('day', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='attendance.day')),
|
|
||||||
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='students.student')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name_plural': 'entries',
|
|
||||||
'ordering': ('student',),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
from django.db import models
|
|
||||||
from students.models import Student
|
|
||||||
|
|
||||||
class Day(models.Model):
|
|
||||||
class Meta:
|
|
||||||
ordering = ('-date',)
|
|
||||||
|
|
||||||
date = models.DateField()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.date}"
|
|
||||||
|
|
||||||
class Entry(models.Model):
|
|
||||||
class Meta:
|
|
||||||
verbose_name_plural = 'entries'
|
|
||||||
ordering = ('student',)
|
|
||||||
|
|
||||||
STATUS_CHOICES = [
|
|
||||||
('P', 'Present'),
|
|
||||||
('T', 'Tardy'),
|
|
||||||
('A', 'Absent'),
|
|
||||||
]
|
|
||||||
|
|
||||||
day = models.ForeignKey(Day, on_delete=models.CASCADE)
|
|
||||||
student = models.ForeignKey(Student, on_delete=models.CASCADE)
|
|
||||||
status = models.CharField(
|
|
||||||
max_length=1,
|
|
||||||
choices=STATUS_CHOICES,
|
|
||||||
default='P'
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.day} | {self.student} | {self.status}"
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Delete {{day}}</h1>
|
|
||||||
<form method="post" action="{% url 'day-delete' day.pk %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{day}}"> or <a href="{% url 'day-detail' day.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<form action="{% url 'day-create' %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
<header>
|
|
||||||
<h1>Take Attendance</h1>
|
|
||||||
{{form.as_p}}
|
|
||||||
</header>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td>
|
|
||||||
<td>Present</td>
|
|
||||||
<td>Tardy</td>
|
|
||||||
<td>Absent</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for student in student_list %}
|
|
||||||
<tr>
|
|
||||||
<td>{{student.full_name}}</td>
|
|
||||||
<td><input type="radio" name="students_{{student.pk}}" value="P" checked></td>
|
|
||||||
<td><input type="radio" name="students_{{student.pk}}" value="T"></td>
|
|
||||||
<td><input type="radio" name="students_{{student.pk}}" value="A"></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<input class="action-button" type="submit" value="Save attendance"> or <a href="{% url 'day-list' %}">cancel</a>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<form action="{% url 'day-update' day.pk %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
<header>
|
|
||||||
<h1>Take Attendance</h1>
|
|
||||||
{{form.as_p}}
|
|
||||||
</header>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td>
|
|
||||||
<td>Present</td>
|
|
||||||
<td>Tardy</td>
|
|
||||||
<td>Absent</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for entry in day.entry_set.all %}
|
|
||||||
<tr>
|
|
||||||
<td>{{entry.student.full_name}}</td>
|
|
||||||
<td><input type="radio" name="students_{{entry.student.pk}}" value="P" {% if entry.status == "P" %}checked{% endif %}></td>
|
|
||||||
<td><input type="radio" name="students_{{entry.student.pk}}" value="T" {% if entry.status == "T" %}checked{% endif %}></td>
|
|
||||||
<td><input type="radio" name="students_{{entry.student.pk}}" value="A" {% if entry.status == "A" %}checked{% endif %}></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'day-list' %}">cancel</a>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Attendance</h1>
|
|
||||||
<p>
|
|
||||||
<a href="{% url 'day-create' %}" class="action-button">Take attendance</a>
|
|
||||||
</p>
|
|
||||||
<section>
|
|
||||||
{% for day in day_list %}
|
|
||||||
<h4><a href="{% url 'day-update' day.pk %}">{{day.date}}</a></h4>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td>
|
|
||||||
<td colspan="2">Status</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for entry in day.entry_set.all %}
|
|
||||||
<tr>
|
|
||||||
<td>{{entry.student}}</td>
|
|
||||||
<td>{{entry.get_status_display}}</td>
|
|
||||||
<td><a href="{% url 'entry-update' entry.pk %}">Update</a></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% empty %}
|
|
||||||
<p>No attendance taken yet.</p>
|
|
||||||
{% endfor %}
|
|
||||||
</section>
|
|
||||||
<section class="pagination">
|
|
||||||
<span class="step-links">
|
|
||||||
{% if page_obj.has_previous %}
|
|
||||||
<a class="minor-action-button" href="?page=1">« first</a>
|
|
||||||
<a class="minor-action-button" href="?page={{ page_obj.previous_page_number }}">‹ previous</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<span class="current">
|
|
||||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{% if page_obj.has_next %}
|
|
||||||
<a class="minor-action-button" href="?page={{ page_obj.next_page_number }}">next ›</a>
|
|
||||||
<a class="minor-action-button" href="?page={{ page_obj.paginator.num_pages }}">last »</a>
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Delete {{entry}}</h1>
|
|
||||||
<form method="post" action="{% url 'entry-delete' entry.pk %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{entry}}"> or <a href="{% url 'day-detail' entry.day.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<div class="generic__header">
|
|
||||||
<h1>Update Entry</h1>
|
|
||||||
<a class="action-button action-delete" href="{% url 'entry-delete' entry.pk %}">Delete</a>
|
|
||||||
</div>
|
|
||||||
<p>For <strong>{{student}}</strong></p>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'entry-update' entry.pk %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'student-detail' entry.student.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
from django.urls import path, include
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('days/', views.DayListView.as_view(), name='day-list'),
|
|
||||||
path('days/new/', views.DayCreateView.as_view(), name='day-create'),
|
|
||||||
path('days/<int:pk>/', include([
|
|
||||||
path('', views.DayDetailView.as_view(), name='day-detail'),
|
|
||||||
path('update/', views.DayUpdateView.as_view(), name='day-update'),
|
|
||||||
path('delete/', views.DayDeleteView.as_view(), name='day-delete'),
|
|
||||||
])),
|
|
||||||
|
|
||||||
path('entries/', views.EntryListView.as_view(), name='entry-list'),
|
|
||||||
path('entries/new/', views.EntryCreateView.as_view(), name='entry-create'),
|
|
||||||
path('entries/<int:pk>/', include([
|
|
||||||
path('', views.EntryDetailView.as_view(), name='entry-detail'),
|
|
||||||
path('update/', views.EntryUpdateView.as_view(), name='entry-update'),
|
|
||||||
path('delete/', views.EntryDeleteView.as_view(), name='entry-delete'),
|
|
||||||
])),
|
|
||||||
]
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
from django.shortcuts import render
|
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.urls import reverse_lazy, reverse
|
|
||||||
from django.views.generic.base import TemplateView
|
|
||||||
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
|
|
||||||
from django.views.generic.detail import DetailView
|
|
||||||
from django.views.generic.list import ListView
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
from django.db.models import Prefetch, Subquery, Count, Sum, F, Q, Value
|
|
||||||
from django.db.models.functions import Length, Upper
|
|
||||||
|
|
||||||
from students.models import Student
|
|
||||||
from .models import Day, Entry
|
|
||||||
from .forms import DayForm, EntryForm
|
|
||||||
|
|
||||||
|
|
||||||
# Days
|
|
||||||
class DayListView(LoginRequiredMixin, ListView):
|
|
||||||
model = Day
|
|
||||||
paginate_by = 7
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
object_list = Day.objects.all().prefetch_related('entry_set', 'entry_set__student')
|
|
||||||
return object_list
|
|
||||||
|
|
||||||
class DayCreateView(LoginRequiredMixin, CreateView):
|
|
||||||
model = Day
|
|
||||||
template_name_suffix = '_create_form'
|
|
||||||
form_class = DayForm
|
|
||||||
success_url = reverse_lazy('profile-detail')
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
today = timezone.localtime(timezone.now()).date()
|
|
||||||
initial = {
|
|
||||||
'date': today,
|
|
||||||
}
|
|
||||||
return initial
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['student_list'] = Student.objects.all()
|
|
||||||
return context
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
form.save()
|
|
||||||
for key, value in self.request.POST.items():
|
|
||||||
if 'student' in key:
|
|
||||||
s = key.split('_')[1]
|
|
||||||
Entry.objects.create(
|
|
||||||
day=form.instance,
|
|
||||||
student=Student.objects.get(pk=s),
|
|
||||||
status=value,
|
|
||||||
)
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
class DayDetailView(LoginRequiredMixin, DetailView):
|
|
||||||
model = Day
|
|
||||||
|
|
||||||
class DayUpdateView(LoginRequiredMixin, UpdateView):
|
|
||||||
model = Day
|
|
||||||
form_class = DayForm
|
|
||||||
success_url = reverse_lazy('day-list')
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
form.save()
|
|
||||||
for key, value in self.request.POST.items():
|
|
||||||
if 'student' in key:
|
|
||||||
s = key.split('_')[1]
|
|
||||||
Entry.objects.filter(day=self.object, student=s).update(status=value)
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
class DayDeleteView(LoginRequiredMixin, DeleteView):
|
|
||||||
model = Day
|
|
||||||
success_url = reverse_lazy('day-list')
|
|
||||||
|
|
||||||
|
|
||||||
# Entries
|
|
||||||
class EntryListView(LoginRequiredMixin, ListView):
|
|
||||||
model = Entry
|
|
||||||
|
|
||||||
class EntryCreateView(LoginRequiredMixin, CreateView):
|
|
||||||
model = Entry
|
|
||||||
template_name_suffix = '_create_form'
|
|
||||||
fields = ('__all__')
|
|
||||||
|
|
||||||
class EntryDetailView(LoginRequiredMixin, DetailView):
|
|
||||||
model = Entry
|
|
||||||
|
|
||||||
class EntryUpdateView(LoginRequiredMixin, UpdateView):
|
|
||||||
model = Entry
|
|
||||||
form_class = EntryForm
|
|
||||||
success_url = reverse_lazy('day-list')
|
|
||||||
|
|
||||||
class EntryDeleteView(LoginRequiredMixin, DeleteView):
|
|
||||||
model = Entry
|
|
||||||
success_url = reverse_lazy('day-list')
|
|
||||||
@ -28,6 +28,7 @@ class StudentForm(forms.ModelForm):
|
|||||||
'last_name',
|
'last_name',
|
||||||
'address',
|
'address',
|
||||||
'dob',
|
'dob',
|
||||||
|
'allergies',
|
||||||
'tags'
|
'tags'
|
||||||
]
|
]
|
||||||
labels = {
|
labels = {
|
||||||
|
|||||||
@ -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'),
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 4.0.5 on 2022-06-29 20:35
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('core', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='schoolyear',
|
|
||||||
name='start_date',
|
|
||||||
field=models.DateField(unique_for_year=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
# Generated by Django 4.0.5 on 2022-06-29 20:47
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('core', '0002_alter_schoolyear_start_date'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='schoolyear',
|
|
||||||
name='end_date',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='schoolyear',
|
|
||||||
name='start_date',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='schoolyear',
|
|
||||||
name='year',
|
|
||||||
field=models.PositiveIntegerField(default=2021),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 4.0.5 on 2022-06-29 20:48
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('core', '0003_remove_schoolyear_end_date_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='schoolyear',
|
|
||||||
name='year',
|
|
||||||
field=models.PositiveIntegerField(unique=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
# Generated by Django 4.0.5 on 2022-06-29 21:27
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
from django.db import migrations, models
|
|
||||||
from django.utils.timezone import utc
|
|
||||||
import django.utils.timezone
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('core', '0004_alter_schoolyear_year'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='attendanceentry',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='attendanceentry',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='component',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='component',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='schoolday',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='schoolday',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='schoolyear',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='schoolyear',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='student',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2022, 6, 29, 21, 26, 47, 547231, tzinfo=utc)),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='student',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='studenttag',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='studenttag',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='subject',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='subject',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='tag',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='tag',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
# Generated by Django 4.0.5 on 2022-06-30 20:03
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('core', '0005_attendanceentry_created_at_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Score',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('value', models.PositiveIntegerField()),
|
|
||||||
('component', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.component')),
|
|
||||||
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.student')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'ordering': ['student'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
# Generated by Django 4.0.5 on 2022-07-27 19:55
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('core', '0006_score'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveIndex(
|
|
||||||
model_name='studenttag',
|
|
||||||
name='core_studen_content_a7305d_idx',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='studenttag',
|
|
||||||
name='content_type',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='studenttag',
|
|
||||||
name='object_id',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='student',
|
|
||||||
name='tags',
|
|
||||||
field=models.ManyToManyField(blank=True, to='core.studenttag'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 4.0.5 on 2022-07-27 19:57
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('core', '0007_remove_studenttag_core_studen_content_a7305d_idx_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='studenttag',
|
|
||||||
name='tag',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='studenttag',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(default='Allergy: Seasonal/Hay', max_length=255),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -61,6 +61,7 @@ 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 = models.ManyToManyField(StudentTag, blank=True)
|
tags = models.ManyToManyField(StudentTag, blank=True)
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,31 @@
|
|||||||
{% extends 'base.html' %}
|
{% 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 %}
|
{% block content %}
|
||||||
|
<article class="form">
|
||||||
|
<header class="form__header">
|
||||||
<h1>Delete Attendanceentry</h1>
|
<h1>Delete Attendanceentry</h1>
|
||||||
<form method="POST" action="{% url 'core:attendanceentry-delete' attendanceentry.pk %}">
|
</header>
|
||||||
|
<form method="POST" action="{% url 'core:entry-delete' entry.school_day.school_year.year entry.school_day.pk entry.pk %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<p>Are you sure you want to delete "{{ attendanceentry }}"?</p>
|
<p>Are you sure you want to delete "{{ attendanceentry }}"?</p>
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<p>
|
<p>
|
||||||
<input type="submit" value="Delete"> or <a href="{% url 'core:attendanceentry-detail' attendanceentry.pk %}">cancel</a>
|
<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>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
</article>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block head_title %}Attendance | {% endblock head_title %}
|
{% block head_title %}Edit Attendance | {% endblock head_title %}
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
|
|||||||
9
src/core/templates/core/home.html
Normal file
9
src/core/templates/core/home.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block head_title %}Grades & Attendance Management Software |{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<h1>Grades and Attendance Management Software</h1>
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
@ -30,7 +30,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for student in student_list %}
|
{% for student in student_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><h5>{{student.full_name}}</h5></td>
|
<td><h5>{{ student.student_id }}   /   {{ student.full_name }}</h5></td>
|
||||||
<td><input type="radio" name="students_{{student.pk}}" value="P" checked></td>
|
<td><input type="radio" name="students_{{student.pk}}" value="P" checked></td>
|
||||||
<td><input type="radio" name="students_{{student.pk}}" value="T"></td>
|
<td><input type="radio" name="students_{{student.pk}}" value="T"></td>
|
||||||
<td><input type="radio" name="students_{{student.pk}}" value="A"></td>
|
<td><input type="radio" name="students_{{student.pk}}" value="A"></td>
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for entry in schoolday.attendanceentry_set.all %}
|
{% for entry in schoolday.attendanceentry_set.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><h5>{{entry.student.full_name}}</h5></td>
|
<td><h5>{{ entry.student.student_id }}   /   {{ entry.student.full_name }}</h5></td>
|
||||||
<td><input type="radio" name="students_{{ entry.student.pk }}" value="P" {% if entry.status == "P" %}checked{% endif %}></td>
|
<td><input type="radio" name="students_{{ entry.student.pk }}" value="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="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>
|
<td><input type="radio" name="students_{{ entry.student.pk }}" value="A" {% if entry.status == "A" %}checked{% endif %}></td>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<article class="form">
|
||||||
<h1>+ New School year</h1>
|
<h1>+ New School year</h1>
|
||||||
<form method="POST" action="{% url 'core:schoolyear-create' %}">
|
<form method="POST" action="{% url 'core:schoolyear-create' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@ -9,4 +10,5 @@
|
|||||||
<input type="submit" value="Create"> or <a href="{% url 'core:schoolyear-list' %}">cancel</a>
|
<input type="submit" value="Create"> or <a href="{% url 'core:schoolyear-list' %}">cancel</a>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
</article>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -8,9 +8,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h1>{{ schoolyear }}</h1>
|
<h1>{{ schoolyear }}</h1>
|
||||||
</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>
|
||||||
<section 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">
|
||||||
|
|||||||
@ -8,9 +8,7 @@
|
|||||||
<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>
|
||||||
|
|
||||||
|
|||||||
@ -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">
|
||||||
@ -29,6 +29,8 @@
|
|||||||
<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>
|
||||||
@ -57,7 +59,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for subject in subject_list %}
|
{% for subject in subject_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="grade"><h5>{{ subject }}</h5></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 %}
|
||||||
|
|||||||
@ -25,6 +25,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th><a href="?order_by=record_num&sort=asc">Student No. ↕</a></th>
|
<th><a href="?order_by=record_num&sort=asc">Student No. ↕</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>
|
||||||
@ -33,6 +36,9 @@
|
|||||||
<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><h5>{{ student }}</h5></td>
|
<td><h5>{{ student }}</h5></td>
|
||||||
|
<td>{{ student.age }}</td>
|
||||||
|
<td>{{ student.dob }}</td>
|
||||||
|
<td>{% if student.allergies %}<span class="tag tag__warning">{{ student.allergies }}</span>{% endif %}</td>
|
||||||
<td>
|
<td>
|
||||||
{% for tag in student.tags.all %}
|
{% for tag in student.tags.all %}
|
||||||
<a href="{% url 'core:stag-detail' school_year.year tag.pk %}" class="tag tag__info">{{ tag }}</a>
|
<a href="{% url 'core:stag-detail' school_year.year tag.pk %}" class="tag tag__info">{{ tag }}</a>
|
||||||
@ -41,7 +47,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="3">No students yet.</td>
|
<td colspan="6">No students yet.</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -1,8 +1,22 @@
|
|||||||
{% extends 'base.html' %}
|
{% 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 %}
|
{% block content %}
|
||||||
<article class="form">
|
<article class="form">
|
||||||
|
<header class="form__header">
|
||||||
<h1>+ New Subject</h1>
|
<h1>+ New Subject</h1>
|
||||||
|
</header>
|
||||||
<form method="POST" action="{% url 'core:subject-create' school_year.year %}">
|
<form method="POST" action="{% url 'core:subject-create' school_year.year %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block head_title %}Subject {{ subject.subject_id }} | {% endblock head_title %}
|
{% block head_title %}{{ subject }} | {% endblock head_title %}
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
<h1>Subjects</h1>
|
<h1>Subjects</h1>
|
||||||
<a href="{% url 'core:subject-create' school_year.year %}" class="action-button">+ New Subject</a>
|
<a href="{% url 'core:subject-create' school_year.year %}" class="action-button">+ New Subject</a>
|
||||||
</div>
|
</div>
|
||||||
<a href="">Subject Tags →</a>
|
<a href="">Tags →</a>
|
||||||
</header>
|
</header>
|
||||||
<table class="list__table">
|
<table class="list__table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
{% 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>
|
||||||
@ -50,7 +52,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<p class="greeting">No attendance taken yet: <a href="{% url 'core:day-create' %}" class="action-button">Take attendance</a></p>
|
<p class="greeting">No attendance taken yet: <a href="{% url 'core:schoolday-create' current_year.year %}" class="action-button">Take attendance →</a></p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from django.urls import path, include
|
|||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('today/', views.TodayView.as_view(), name='today'),
|
path('', views.HomeView.as_view(), name='home'),
|
||||||
|
|
||||||
# SchoolYears
|
# SchoolYears
|
||||||
path('years/', include([
|
path('years/', include([
|
||||||
@ -27,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([
|
||||||
|
|||||||
@ -34,12 +34,29 @@ from .models import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .forms import (
|
from .forms import (
|
||||||
|
StudentForm,
|
||||||
SchoolYearCreateForm,
|
SchoolYearCreateForm,
|
||||||
ComponentCreateForm,
|
ComponentCreateForm,
|
||||||
SchoolDayForm,
|
SchoolDayForm,
|
||||||
AttendanceEntryForm
|
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)
|
||||||
|
|
||||||
|
# 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):
|
class TodayView(LoginRequiredMixin, TemplateView):
|
||||||
template_name = 'core/today.html'
|
template_name = 'core/today.html'
|
||||||
@ -50,6 +67,7 @@ class TodayView(LoginRequiredMixin, TemplateView):
|
|||||||
enddate = today + dt.timedelta(days=7)
|
enddate = today + dt.timedelta(days=7)
|
||||||
|
|
||||||
context['birthdays'] = Student.objects.filter(
|
context['birthdays'] = Student.objects.filter(
|
||||||
|
school_year__year=self.kwargs['year'],
|
||||||
dob__month=today.month,
|
dob__month=today.month,
|
||||||
dob__day__range=[today.day, enddate.day]
|
dob__day__range=[today.day, enddate.day]
|
||||||
).order_by('dob')
|
).order_by('dob')
|
||||||
@ -60,9 +78,13 @@ class TodayView(LoginRequiredMixin, TemplateView):
|
|||||||
|
|
||||||
context['attendance'] = SchoolDay.objects.filter(
|
context['attendance'] = SchoolDay.objects.filter(
|
||||||
date=today
|
date=today
|
||||||
).prefetch_related('attendanceentry_set', 'attendanceentry_set__student')
|
).prefetch_related(
|
||||||
|
'attendanceentry_set',
|
||||||
|
'attendanceentry_set__student'
|
||||||
|
)
|
||||||
|
|
||||||
context['ungraded_components'] = Component.objects.filter(
|
context['ungraded_components'] = Component.objects.filter(
|
||||||
|
subject__school_year__year=self.kwargs['year'],
|
||||||
due_date__lte=today,
|
due_date__lte=today,
|
||||||
finished_grading=False
|
finished_grading=False
|
||||||
).select_related('subject')
|
).select_related('subject')
|
||||||
@ -82,7 +104,9 @@ class SchoolYearListView(LoginRequiredMixin, ListView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class SchoolYearCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
class SchoolYearCreateView(
|
||||||
|
LoginRequiredMixin, SuccessMessageMixin, CreateView
|
||||||
|
):
|
||||||
model = SchoolYear
|
model = SchoolYear
|
||||||
success_message = 'SchoolYear created.'
|
success_message = 'SchoolYear created.'
|
||||||
form_class = SchoolYearCreateForm
|
form_class = SchoolYearCreateForm
|
||||||
@ -95,7 +119,9 @@ class SchoolYearDetailView(LoginRequiredMixin, DetailView):
|
|||||||
slug_field = 'year'
|
slug_field = 'year'
|
||||||
|
|
||||||
|
|
||||||
class SchoolYearUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
class SchoolYearUpdateView(
|
||||||
|
LoginRequiredMixin, SuccessMessageMixin, UpdateView
|
||||||
|
):
|
||||||
model = SchoolYear
|
model = SchoolYear
|
||||||
slug_url_kwarg = 'year'
|
slug_url_kwarg = 'year'
|
||||||
slug_field = 'year'
|
slug_field = 'year'
|
||||||
@ -125,13 +151,7 @@ class StudentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|||||||
model = Student
|
model = Student
|
||||||
success_message = 'Student created.'
|
success_message = 'Student created.'
|
||||||
template_name_suffix = '_create_form'
|
template_name_suffix = '_create_form'
|
||||||
fields = [
|
form_class = StudentForm
|
||||||
'student_id',
|
|
||||||
'first_name',
|
|
||||||
'last_name',
|
|
||||||
'address',
|
|
||||||
'dob',
|
|
||||||
]
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.school_year = get_object_or_404(
|
form.instance.school_year = get_object_or_404(
|
||||||
@ -141,7 +161,9 @@ class StudentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|||||||
|
|
||||||
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'])
|
context['school_year'] = get_object_or_404(
|
||||||
|
SchoolYear, year=self.kwargs['year']
|
||||||
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@ -207,7 +229,9 @@ class StudentTagListView(LoginRequiredMixin, ListView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class StudentTagCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
class StudentTagCreateView(
|
||||||
|
LoginRequiredMixin, SuccessMessageMixin, CreateView
|
||||||
|
):
|
||||||
model = StudentTag
|
model = StudentTag
|
||||||
success_message = 'Tag created.'
|
success_message = 'Tag created.'
|
||||||
template_name_suffix = '_create_form'
|
template_name_suffix = '_create_form'
|
||||||
@ -231,7 +255,7 @@ class StudentTagDetailView(LoginRequiredMixin, DetailView):
|
|||||||
pk_url_kwarg = 'stag_pk'
|
pk_url_kwarg = 'stag_pk'
|
||||||
context_object_name = 'tag'
|
context_object_name = 'tag'
|
||||||
|
|
||||||
def get_object(self):
|
def get_queryset(self):
|
||||||
queryset = StudentTag.objects.filter(
|
queryset = StudentTag.objects.filter(
|
||||||
pk=self.kwargs.get(self.pk_url_kwarg)
|
pk=self.kwargs.get(self.pk_url_kwarg)
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
@ -242,8 +266,7 @@ class StudentTagDetailView(LoginRequiredMixin, DetailView):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
obj = queryset.get()
|
return queryset
|
||||||
return obj
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
@ -253,7 +276,9 @@ class StudentTagDetailView(LoginRequiredMixin, DetailView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class StudentTagUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
class StudentTagUpdateView(
|
||||||
|
LoginRequiredMixin, SuccessMessageMixin, UpdateView
|
||||||
|
):
|
||||||
model = StudentTag
|
model = StudentTag
|
||||||
pk_url_kwarg = 'stag_pk'
|
pk_url_kwarg = 'stag_pk'
|
||||||
context_object_name = 'tag'
|
context_object_name = 'tag'
|
||||||
@ -273,7 +298,9 @@ class StudentTagUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class StudentTagDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
class StudentTagDeleteView(
|
||||||
|
LoginRequiredMixin, SuccessMessageMixin, DeleteView
|
||||||
|
):
|
||||||
model = StudentTag
|
model = StudentTag
|
||||||
pk_url_kwarg = 'stag_pk'
|
pk_url_kwarg = 'stag_pk'
|
||||||
context_object_name = 'tag'
|
context_object_name = 'tag'
|
||||||
@ -338,9 +365,11 @@ class SubjectCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|||||||
class SubjectDetailView(LoginRequiredMixin, DetailView):
|
class SubjectDetailView(LoginRequiredMixin, DetailView):
|
||||||
model = Subject
|
model = Subject
|
||||||
|
|
||||||
def get_object(self):
|
def get_queryset(self):
|
||||||
queryset = Subject.objects.filter(
|
queryset = Subject.objects.filter(
|
||||||
pk=self.kwargs.get(self.pk_url_kwarg)
|
pk=self.kwargs.get(self.pk_url_kwarg)
|
||||||
|
).select_related(
|
||||||
|
'school_year'
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
Prefetch(
|
Prefetch(
|
||||||
'component_set',
|
'component_set',
|
||||||
@ -351,8 +380,7 @@ class SubjectDetailView(LoginRequiredMixin, DetailView):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
obj = queryset.get()
|
return queryset
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
class SubjectUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
class SubjectUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||||
@ -415,7 +443,7 @@ class ComponentDetailView(LoginRequiredMixin, DetailView):
|
|||||||
model = Component
|
model = Component
|
||||||
pk_url_kwarg = 'component_pk'
|
pk_url_kwarg = 'component_pk'
|
||||||
|
|
||||||
def get_object(self):
|
def get_queryset(self):
|
||||||
queryset = Component.objects.filter(
|
queryset = Component.objects.filter(
|
||||||
pk=self.kwargs.get(self.pk_url_kwarg)
|
pk=self.kwargs.get(self.pk_url_kwarg)
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
@ -427,8 +455,7 @@ class ComponentDetailView(LoginRequiredMixin, DetailView):
|
|||||||
).annotate(
|
).annotate(
|
||||||
grade_avg_pre=Avg('score__value')
|
grade_avg_pre=Avg('score__value')
|
||||||
)
|
)
|
||||||
obj = queryset.get()
|
return queryset
|
||||||
return obj
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
|||||||
32184
src/db.json
Normal file
32184
src/db.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,12 +0,0 @@
|
|||||||
from django.contrib import admin
|
|
||||||
from .models import (
|
|
||||||
Tag,
|
|
||||||
Subject,
|
|
||||||
Component,
|
|
||||||
Score,
|
|
||||||
)
|
|
||||||
|
|
||||||
admin.site.register(Tag)
|
|
||||||
admin.site.register(Subject)
|
|
||||||
admin.site.register(Component)
|
|
||||||
admin.site.register(Score)
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class GradebookConfig(AppConfig):
|
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
|
||||||
name = 'gradebook'
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
from django import forms
|
|
||||||
from .models import Component, Score, Tag
|
|
||||||
|
|
||||||
from students.models import Student
|
|
||||||
|
|
||||||
class ComponentForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Component
|
|
||||||
fields = (
|
|
||||||
'name',
|
|
||||||
'category',
|
|
||||||
'due_date',
|
|
||||||
'grade_total',
|
|
||||||
)
|
|
||||||
widgets = {
|
|
||||||
'due_date': forms.DateInput(attrs = {
|
|
||||||
'type': 'date'
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
class ComponentUpdateForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Component
|
|
||||||
fields = (
|
|
||||||
'subject',
|
|
||||||
'name',
|
|
||||||
'category',
|
|
||||||
'due_date',
|
|
||||||
'grade_total',
|
|
||||||
'tags',
|
|
||||||
)
|
|
||||||
widgets = {
|
|
||||||
'due_date': forms.DateInput(attrs = {
|
|
||||||
'type': 'date'
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
class TagForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Tag
|
|
||||||
fields = (
|
|
||||||
'name',
|
|
||||||
)
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
# Generated by Django 3.2.7 on 2021-09-01 15:26
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('students', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Component',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=50)),
|
|
||||||
('category', models.CharField(choices=[('QZ', 'Quiz'), ('AS', 'Assignment'), ('TS', 'Test')], default='AS', max_length=2)),
|
|
||||||
('due_date', models.DateField()),
|
|
||||||
('grade_total', models.PositiveIntegerField()),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'ordering': ['due_date'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Subject',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=250)),
|
|
||||||
('description', models.CharField(blank=True, max_length=250)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'ordering': ['name'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Tag',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=50)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Score',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('value', models.PositiveIntegerField()),
|
|
||||||
('component', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='gradebook.component')),
|
|
||||||
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='students.student')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'ordering': ('student',),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='component',
|
|
||||||
name='subject',
|
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='gradebook.subject'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='component',
|
|
||||||
name='tags',
|
|
||||||
field=models.ManyToManyField(blank=True, to='gradebook.Tag'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 3.2.7 on 2021-09-16 23:21
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('gradebook', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='component',
|
|
||||||
name='finished_grading',
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
from datetime import datetime, date
|
|
||||||
from django.db import models
|
|
||||||
from django.urls import reverse
|
|
||||||
|
|
||||||
from django.db.models import Count, Sum, Avg, F, Value
|
|
||||||
from django.db.models.functions import Length, Upper
|
|
||||||
|
|
||||||
from students.models import Student
|
|
||||||
|
|
||||||
class Tag(models.Model):
|
|
||||||
name = models.CharField(max_length=50)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('tag-detail', kwargs={'pk': self.pk})
|
|
||||||
|
|
||||||
|
|
||||||
class Subject(models.Model):
|
|
||||||
class Meta:
|
|
||||||
ordering = ['name']
|
|
||||||
|
|
||||||
name = models.CharField(max_length=250)
|
|
||||||
description = models.CharField(max_length=250, blank=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('subject-detail', kwargs={'pk': self.pk})
|
|
||||||
|
|
||||||
|
|
||||||
class Component(models.Model):
|
|
||||||
class Meta:
|
|
||||||
ordering = ['due_date']
|
|
||||||
|
|
||||||
CATEGORY_CHOICES = [
|
|
||||||
('QZ', 'Quiz'),
|
|
||||||
('AS', 'Assignment'),
|
|
||||||
('TS', 'Test'),
|
|
||||||
]
|
|
||||||
|
|
||||||
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
|
|
||||||
name = models.CharField(max_length=50)
|
|
||||||
category = models.CharField(
|
|
||||||
max_length = 2,
|
|
||||||
choices = CATEGORY_CHOICES,
|
|
||||||
default='AS',
|
|
||||||
)
|
|
||||||
due_date = models.DateField()
|
|
||||||
grade_total = models.PositiveIntegerField()
|
|
||||||
tags = models.ManyToManyField(Tag, blank=True)
|
|
||||||
finished_grading = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_due(self):
|
|
||||||
if self.due_date < date.today():
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def grade_avg(self):
|
|
||||||
avg = Score.objects.filter(component=self.pk).aggregate(
|
|
||||||
Avg('value')
|
|
||||||
)
|
|
||||||
if avg['value__avg'] is not None:
|
|
||||||
return round(avg['value__avg'], 2)
|
|
||||||
else:
|
|
||||||
return "No scores yet."
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('component-detail', kwargs={'pk': self.pk})
|
|
||||||
|
|
||||||
|
|
||||||
class Score(models.Model):
|
|
||||||
class Meta:
|
|
||||||
ordering = ('student',)
|
|
||||||
|
|
||||||
component = models.ForeignKey(Component, on_delete=models.CASCADE)
|
|
||||||
student = models.ForeignKey(Student, on_delete=models.CASCADE)
|
|
||||||
value = models.PositiveIntegerField()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def grade(self):
|
|
||||||
return f"{self.value} / {self.component.grade_total}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def grade_as_percentage(self):
|
|
||||||
return round(self.value / self.component.grade_total * 100, 2)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.student} scored: {self.value} / {self.component.grade_total}"
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('score-detail', kwargs={'pk': self.pk})
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Delete {{component}}</h1>
|
|
||||||
<form method="post" action="{% url 'component-delete' component.subject.pk component.pk %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{component}}"> or <a href="{% url 'component-detail' component.subject.pk component.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>{{subject}}</h1>
|
|
||||||
<h2>Create Component</h2>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'component-create' subject.pk %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Create component"> or <a href="{% url 'subject-detail' subject.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<header class="generic__header">
|
|
||||||
<h1>{{component.name}}</h1>
|
|
||||||
<a href="{% url 'component-update' component.subject.pk component.pk %}" class="action-button">Edit</a>
|
|
||||||
</header>
|
|
||||||
{% if component.finished_grading %}
|
|
||||||
<section>
|
|
||||||
<p>
|
|
||||||
<span class="component__grade--graded">✓ Graded</span>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
{% if component.tags.count > 0 %}
|
|
||||||
<section>
|
|
||||||
<span>
|
|
||||||
{% for tag in component.tags.all %}
|
|
||||||
<a class="tag__item" href="{% url 'tag-detail' tag.pk %}">{{tag.name}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</span>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
<section>
|
|
||||||
<dl>
|
|
||||||
<dt>Due Date</dt>
|
|
||||||
<dd>{{component.due_date}}</dd>
|
|
||||||
<dt>Description</dt>
|
|
||||||
<dd>{{component.name}}</dd>
|
|
||||||
<dt>Category</dt>
|
|
||||||
<dd>{{component.get_category_display}}</dd>
|
|
||||||
<dt>Grade Total</dt>
|
|
||||||
<dd>{{component.grade_total}}</dd>
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h3>Scores</h3>
|
|
||||||
<p>
|
|
||||||
<a class="action-button" href="{% url 'component-manager' component.subject.pk component.pk %}">Bulk Edit Scores</a>
|
|
||||||
</p>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td>
|
|
||||||
<td colspan="2">Score</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for score in component.score_set.all %}
|
|
||||||
<tr>
|
|
||||||
<td><a href="{% url 'student-detail' score.student.pk %}">{{score.student.student_id}} — {{score.student}}</a></td>
|
|
||||||
<td>{{score.value}}</td>
|
|
||||||
<td><a href="{% url 'score-update' score.pk %}">Edit score</a></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
<tr>
|
|
||||||
<td><strong>Avg Score</strong></td>
|
|
||||||
<td><strong>{{component.grade_avg_pre|floatformat:2}}</strong></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{% if scoreless %}
|
|
||||||
<section>
|
|
||||||
|
|
||||||
<h3>Scoreless</h3>
|
|
||||||
<ul>
|
|
||||||
{% for student in scoreless %}
|
|
||||||
<li>{{student}}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<div class="generic__header">
|
|
||||||
<h1>Edit Component</h1>
|
|
||||||
<a class="action-button action-delete" href="{% url 'component-delete' component.subject.pk component.pk %}">Delete</a>
|
|
||||||
</div>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'component-update' component.subject.pk component.pk %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'component-detail' component.subject.pk component.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% load gradebook_filters %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>{{component}}, <em>{{component.subject}}</em></h1>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'component-manager' component.subject.pk component.pk %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
<h3>Enter Scores</h3>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Out of:</td>
|
|
||||||
<td>{{component.grade_total}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Student</td>
|
|
||||||
<td>Score</td>
|
|
||||||
{% if formset.errors %}
|
|
||||||
<td>Errors</td>
|
|
||||||
{% endif %}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for student in student_list %}
|
|
||||||
<tr>
|
|
||||||
<td>{{student.full_name}}</td>
|
|
||||||
<td><input type="number" name="student_{{student.pk}}" min="0" max="{{component.grade_total}}" value="{{student.cscore}}"></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'component-detail' component.subject.pk component.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Delete {{score}}</h1>
|
|
||||||
<form method="post" action="{% url 'score-delete' score.pk %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{score}}"> or <a href="{% url 'score-detail' score.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Create Score</h1>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'score-create' %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Create Score"> or <a href="{% url 'subject-list' %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Update Score</h1>
|
|
||||||
<h2>{{score.component.subject}}— <em>{{score.component.get_category_display}}</em>: {{score.component}}</h2>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'score-update' score.pk %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="next" value="{{next}}">
|
|
||||||
<h3>{{score.student}}</h3>
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'student-detail' score.student.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Search Results</h1>
|
|
||||||
{% if student_list.count > 0 %}
|
|
||||||
<section>
|
|
||||||
<h3>Students</h3>
|
|
||||||
<ul>
|
|
||||||
{% for student in student_list %}
|
|
||||||
<li class="student">
|
|
||||||
<a class="student__li" href="{% url 'student-detail' student.pk %}">{{student.student_id}} — {{student.full_name}}</a>
|
|
||||||
{% if student.sit %}
|
|
||||||
<span class="student__attribute">SIT: {{student.get_sit_display}}</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if student.iep_behavioral %}
|
|
||||||
<span class="student__attribute">IEP behavioral</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if student.iep_math %}
|
|
||||||
<span class="student__attribute">IEP math</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if student.iep_ela %}
|
|
||||||
<span class="student__attribute">IEP ELA</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if student.parent__count > 0 %}
|
|
||||||
<br>Parents:
|
|
||||||
{% for parent in student.parent_set.all %}
|
|
||||||
<a href="{% url 'parent-detail' parent.pk %}">{{parent.full_name}}</a>{% if not forloop.last %},{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
{% if component_list.count > 0 %}
|
|
||||||
<section>
|
|
||||||
<h3>Components</h3>
|
|
||||||
<ul class="search__results">
|
|
||||||
<li class="search__result">
|
|
||||||
<strong>Subject</strong>
|
|
||||||
<strong>Component</strong>
|
|
||||||
<strong>Due Date</strong>
|
|
||||||
<strong>Tags</strong>
|
|
||||||
</li>
|
|
||||||
<hr>
|
|
||||||
{% for component in component_list %}
|
|
||||||
<li class="search__result">
|
|
||||||
{{component.subject}}
|
|
||||||
<a href="{% url 'component-detail' component.subject.pk component.pk %}">{{component}}</a>
|
|
||||||
<span>{{component.due_date}}</span>
|
|
||||||
<span>
|
|
||||||
{% for tag in component.tags.all %}
|
|
||||||
<a class="tag__item" href="{% url 'tag-detail' tag.pk %}">{{tag.name}}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
{% empty %}
|
|
||||||
<p>No components by that name were found.</p>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
{% if message_list.count > 0 %}
|
|
||||||
<section>
|
|
||||||
<h3>Messages</h3>
|
|
||||||
<ul class="search__results">
|
|
||||||
{% for message in message_list %}
|
|
||||||
<li class="search__mresult">
|
|
||||||
<p>
|
|
||||||
<a href="{% url 'thread-detail' message.thread.pk %}">{{message.thread.subject}}</a><br>
|
|
||||||
{{message.content|truncatewords:25}}<br>
|
|
||||||
<a class="" href="{% url 'thread-detail' message.thread.pk %}">Read more →</a>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
{% empty %}
|
|
||||||
<p>No components by that name were found.</p>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Delete {{subject}} Subject</h1>
|
|
||||||
<form method="post" action="{% url 'subject-delete' subject.pk %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{subject}}"> or <a href="{% url 'subject-list' %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Create Subject</h1>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'subject-create' %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Create Subject"> or <a href="{% url 'subject-list' %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<div class="generic__header">
|
|
||||||
<h1>{{subject.name}}</h1>
|
|
||||||
<a class="action-button" href="{% url 'subject-update' subject.pk %}">Update subject details</a>
|
|
||||||
</div>
|
|
||||||
{% if subject.description %}
|
|
||||||
<span>{{subject.description}}</span>
|
|
||||||
{% endif %}
|
|
||||||
<section>
|
|
||||||
<h3>Syllabus</h3>
|
|
||||||
<p>
|
|
||||||
<a href="{% url 'component-create' subject.pk %}" class="action-button">+ New component</a>
|
|
||||||
</p>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>Due Date</td>
|
|
||||||
<td>Description</td>
|
|
||||||
<td>Category</td>
|
|
||||||
<td>Grade Total</td>
|
|
||||||
<td>Avg Score</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for component in subject.component_set.all %}
|
|
||||||
<tr>
|
|
||||||
<td>{{component.due_date}}</td>
|
|
||||||
<td><a href="{% url 'component-detail' subject.pk component.pk %}">{{component.name}}</a></td>
|
|
||||||
<td>{{component.get_category_display}}</td>
|
|
||||||
<td>{{component.grade_total}}</td>
|
|
||||||
<td>{{component.grade_avg_pre|floatformat:2}}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<div class="generic__header">
|
|
||||||
<h1>Update Subject</h1>
|
|
||||||
<a class="action-button action-delete" href="{% url 'subject-delete' subject.pk %}">Delete subject</a>
|
|
||||||
</div>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'subject-update' subject.pk %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'subject-detail' subject.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<header>
|
|
||||||
<h1>Curricula</h1>
|
|
||||||
</header>
|
|
||||||
<section>
|
|
||||||
<h3>Subjects</h3>
|
|
||||||
<p>
|
|
||||||
<a href="{% url 'subject-create' %}" class="action-button">+ New subject</a>
|
|
||||||
</p>
|
|
||||||
{% for subject in subject_list %}
|
|
||||||
<div class="subject__item">
|
|
||||||
<h4><a href="{% url 'subject-detail' subject.pk %}">{{subject.name}}</a></h4>
|
|
||||||
{% if subject.description %}
|
|
||||||
<p>{{subject.description}}</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h4>Tags</h4>
|
|
||||||
<p><a href="{% url 'tag-create' %}" class="action-button">+ New tag</a></p>
|
|
||||||
<p>
|
|
||||||
{% for tag in tag_list %}
|
|
||||||
<span class="tag">
|
|
||||||
<a class="tag__item" href="{% url 'tag-detail' tag.pk %}">{{tag.name}}</a>
|
|
||||||
<span class="tag__count">{{tag.component__count}}</span>
|
|
||||||
</span>
|
|
||||||
{% endfor %}
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Delete {{tag}}</h1>
|
|
||||||
<form method="post" action="{% url 'tag-delete' tag.pk %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{tag}}"> or <a href="{% url 'tag-detail' tag.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Create Tag</h1>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'tag-create' %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Create Tag"> or <a href="{% url 'subject-list' %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<div class="generic__header">
|
|
||||||
<h1>{{tag.name}}</h1>
|
|
||||||
<a class="action-button" href="{% url 'tag-update' tag.pk %}">Update tag details</a>
|
|
||||||
</div>
|
|
||||||
<section>
|
|
||||||
<h3>Components with this tag</h3>
|
|
||||||
<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 tag.component_set.all %}
|
|
||||||
<tr>
|
|
||||||
<td>{{component.due_date}}</td>
|
|
||||||
<td><strong>{{component.subject}}</strong>: <a href="{% url 'component-detail' tag.pk component.pk %}">{{component.name}}</a></td>
|
|
||||||
<td>{{component.get_category_display}}</td>
|
|
||||||
<td>{{component.grade_total}}</td>
|
|
||||||
<td>{{component.grade_avg_pre|floatformat:2}}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Update Tag</h1>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'tag-update' tag.pk %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'tag-detail' tag.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Tags</h1>
|
|
||||||
<p>
|
|
||||||
<a href="{% url 'tag-create' %}" class="action-button">+ New tag</a>
|
|
||||||
</p>
|
|
||||||
<section>
|
|
||||||
<ul>
|
|
||||||
{% for tag in tag_list %}
|
|
||||||
<li>
|
|
||||||
<a href="{% url 'tag-detail' tag.pk %}">{{tag.name}}</a>
|
|
||||||
<span class="tag__count">{{tag.component__count}}</span>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
from django import template
|
|
||||||
|
|
||||||
register = template.Library()
|
|
||||||
|
|
||||||
@register.filter(name='grade_as_percentage')
|
|
||||||
def grade_as_percentage(numerator, denominator):
|
|
||||||
return round(numerator / denominator * 100, 2)
|
|
||||||
|
|
||||||
@register.filter(name='keyvalue')
|
|
||||||
def keyvalue(dict, key):
|
|
||||||
try:
|
|
||||||
return dict[key-1]
|
|
||||||
except KeyError:
|
|
||||||
return ''
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
from django.urls import path, include
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('search/', views.SearchResultsView.as_view(), name='search-results'),
|
|
||||||
path('subjects/', views.SubjectListView.as_view(), name='subject-list'),
|
|
||||||
path('subjects/new/', views.SubjectCreateView.as_view(), name='subject-create'),
|
|
||||||
path('subjects/<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'),
|
|
||||||
|
|
||||||
|
|
||||||
path('components/', views.ComponentListView.as_view(), name='component-list'),
|
|
||||||
path('components/new/', views.ComponentCreateView.as_view(), name='component-create'),
|
|
||||||
path('components/<int:comp_pk>/', include([
|
|
||||||
path('', views.ComponentDetailView.as_view(), name='component-detail'),
|
|
||||||
path('update/', views.ComponentUpdateView.as_view(), name='component-update'),
|
|
||||||
path('manager/', views.ComponentManagerView.as_view(), name='component-manager'),
|
|
||||||
path('delete/', views.ComponentDeleteView.as_view(), name='component-delete'),
|
|
||||||
])),
|
|
||||||
])),
|
|
||||||
|
|
||||||
|
|
||||||
path('scores/', views.ScoreListView.as_view(), name='score-list'),
|
|
||||||
path('scores/new/', views.ScoreCreateView.as_view(), name='score-create'),
|
|
||||||
path('scores/<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'),
|
|
||||||
])),
|
|
||||||
|
|
||||||
path('tags/', views.TagListView.as_view(), name='tag-list'),
|
|
||||||
path('tags/new/', views.TagCreateView.as_view(), name='tag-create'),
|
|
||||||
path('tags/<int: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'),
|
|
||||||
])),
|
|
||||||
]
|
|
||||||
@ -1,297 +0,0 @@
|
|||||||
from django.shortcuts import render
|
|
||||||
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.forms.models import inlineformset_factory
|
|
||||||
from django import forms
|
|
||||||
|
|
||||||
from django.db.models import ( Exists, OuterRef,
|
|
||||||
Prefetch, Subquery, Count, Sum, Avg, F, Q, Value)
|
|
||||||
from django.db.models.functions import Length, Upper
|
|
||||||
|
|
||||||
from students.models import Student, Thread, Message
|
|
||||||
|
|
||||||
from .models import (
|
|
||||||
Tag,
|
|
||||||
Subject,
|
|
||||||
Component,
|
|
||||||
Score,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .forms import ComponentForm, ComponentUpdateForm
|
|
||||||
|
|
||||||
|
|
||||||
# UPLOAD CSV
|
|
||||||
# import pandas as pd
|
|
||||||
|
|
||||||
# csv_data = pd.read_csv('file.csv', sep=';')
|
|
||||||
|
|
||||||
# products = [
|
|
||||||
# Product(
|
|
||||||
# name = csv_data.ix[row]['Name'],
|
|
||||||
# description = csv_data.ix[row]['Description'],
|
|
||||||
# price = csv_data.ix[row]['price'],
|
|
||||||
# )
|
|
||||||
# for row in csv_data['ID']
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# Product.objects.bulk_create(products)
|
|
||||||
|
|
||||||
|
|
||||||
# if form.is_valid():
|
|
||||||
# csv_file = form.cleaned_data['csv_file']
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SearchResultsView(ListView):
|
|
||||||
model = Component
|
|
||||||
template_name = 'gradebook/search_results.html'
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
query = self.request.GET.get('q')
|
|
||||||
object_list = Component.objects.filter(
|
|
||||||
Q(name__icontains=query) | Q(tags__name__icontains=query)
|
|
||||||
).prefetch_related(
|
|
||||||
Prefetch(
|
|
||||||
'score_set',
|
|
||||||
queryset=Score.objects.select_related('student')
|
|
||||||
),
|
|
||||||
'tags'
|
|
||||||
).annotate(
|
|
||||||
grade_avg_pre=Avg('score__value')
|
|
||||||
).order_by('subject')
|
|
||||||
return object_list
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
query = self.request.GET.get('q')
|
|
||||||
context['student_list'] = Student.objects.filter(
|
|
||||||
Q(first_name__icontains=query) | Q(last_name__icontains=query) | Q(student_id__icontains=query)
|
|
||||||
)
|
|
||||||
context['message_list'] = Message.objects.filter(
|
|
||||||
Q(content__icontains=query)
|
|
||||||
)
|
|
||||||
context['query'] = query
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
# SUBJECTS
|
|
||||||
class SubjectListView(LoginRequiredMixin, ListView):
|
|
||||||
model = Subject
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['tag_list'] = Tag.objects.annotate(Count('component'))
|
|
||||||
return context
|
|
||||||
|
|
||||||
class SubjectCreateView(LoginRequiredMixin, CreateView):
|
|
||||||
model = Subject
|
|
||||||
template_name_suffix = '_create_form'
|
|
||||||
fields = ('__all__')
|
|
||||||
|
|
||||||
class SubjectDetailView(LoginRequiredMixin, DetailView):
|
|
||||||
model = Subject
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
queryset = Subject.objects.filter(
|
|
||||||
pk=self.kwargs.get(self.pk_url_kwarg)
|
|
||||||
).prefetch_related(
|
|
||||||
Prefetch('component_set', queryset=Component.objects.prefetch_related(
|
|
||||||
'score_set'
|
|
||||||
).annotate(
|
|
||||||
grade_avg_pre=Avg('score__value')
|
|
||||||
))
|
|
||||||
)
|
|
||||||
obj = queryset.get()
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
class SubjectUpdateView(LoginRequiredMixin, UpdateView):
|
|
||||||
model = Subject
|
|
||||||
fields = ('__all__')
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
pk = self.kwargs["pk"]
|
|
||||||
return reverse('subject-detail', kwargs={'pk': pk})
|
|
||||||
|
|
||||||
class SubjectDeleteView(LoginRequiredMixin, DeleteView):
|
|
||||||
model = Subject
|
|
||||||
success_url = reverse_lazy('subject-list')
|
|
||||||
|
|
||||||
|
|
||||||
# COMPONENTS
|
|
||||||
class ComponentListView(LoginRequiredMixin, ListView):
|
|
||||||
model = Component
|
|
||||||
pk_url_kwarg = 'comp_pk'
|
|
||||||
|
|
||||||
class ComponentCreateView(LoginRequiredMixin, CreateView):
|
|
||||||
model = Component
|
|
||||||
form_class = ComponentForm
|
|
||||||
template_name_suffix = '_create_form'
|
|
||||||
pk_url_kwarg = 'comp_pk'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['subject'] = Subject.objects.get(pk=self.kwargs['pk'])
|
|
||||||
return context
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
form.instance.subject = Subject.objects.get(pk=self.kwargs['pk'])
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse('component-detail', kwargs={'pk': self.kwargs['pk'], 'comp_pk': self.object.pk})
|
|
||||||
|
|
||||||
class ComponentDetailView(LoginRequiredMixin, DetailView):
|
|
||||||
model = Component
|
|
||||||
pk_url_kwarg = 'comp_pk'
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
queryset = Component.objects.filter(
|
|
||||||
pk=self.kwargs.get(self.pk_url_kwarg)
|
|
||||||
).prefetch_related(
|
|
||||||
Prefetch(
|
|
||||||
'score_set',
|
|
||||||
queryset=Score.objects.select_related('student')
|
|
||||||
),
|
|
||||||
'tags'
|
|
||||||
).annotate(
|
|
||||||
grade_avg_pre=Avg('score__value')
|
|
||||||
)
|
|
||||||
obj = queryset.get()
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
cscores = Score.objects.filter(
|
|
||||||
component=self.object,
|
|
||||||
student=OuterRef('pk')
|
|
||||||
)
|
|
||||||
context['scoreless'] = Student.objects.exclude(
|
|
||||||
score__in=cscores
|
|
||||||
)
|
|
||||||
return context
|
|
||||||
|
|
||||||
class ComponentUpdateView(LoginRequiredMixin, UpdateView):
|
|
||||||
model = Component
|
|
||||||
form_class = ComponentUpdateForm
|
|
||||||
pk_url_kwarg = 'comp_pk'
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse('subject-detail', kwargs={'pk': self.object.subject.pk})
|
|
||||||
|
|
||||||
class ComponentManagerView(LoginRequiredMixin, UpdateView):
|
|
||||||
model = Component
|
|
||||||
fields = ('finished_grading',)
|
|
||||||
pk_url_kwarg = 'comp_pk'
|
|
||||||
template_name_suffix = '_manager'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
cscores = Score.objects.filter(
|
|
||||||
component=self.object,
|
|
||||||
student=OuterRef('pk')
|
|
||||||
)
|
|
||||||
context['student_list'] = Student.objects.annotate(
|
|
||||||
cscore=Subquery(cscores.values('value')),
|
|
||||||
cscore_pk=Subquery(cscores.values('pk'))
|
|
||||||
)
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse('component-detail', kwargs={'pk': self.object.subject.pk, 'comp_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 ComponentDeleteView(LoginRequiredMixin, DeleteView):
|
|
||||||
model = Component
|
|
||||||
pk_url_kwarg = 'comp_pk'
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse('subject-detail', kwargs={'pk': self.kwargs['pk']})
|
|
||||||
|
|
||||||
|
|
||||||
# SCORES
|
|
||||||
class ScoreListView(LoginRequiredMixin, ListView):
|
|
||||||
model = Score
|
|
||||||
|
|
||||||
class ScoreCreateView(LoginRequiredMixin, CreateView):
|
|
||||||
model = Score
|
|
||||||
template_name_suffix = '_create_form'
|
|
||||||
fields = ('__all__')
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse('component-detail', kwargs={'pk': self.object.component.pk})
|
|
||||||
|
|
||||||
class ScoreDetailView(LoginRequiredMixin, DetailView):
|
|
||||||
model = Score
|
|
||||||
|
|
||||||
class ScoreUpdateView(LoginRequiredMixin, UpdateView):
|
|
||||||
model = Score
|
|
||||||
fields = ['value']
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse('component-detail', kwargs={'pk': self.object.component.subject.pk, 'comp_pk': self.object.component.pk})
|
|
||||||
|
|
||||||
class ScoreDeleteView(LoginRequiredMixin, DeleteView):
|
|
||||||
model = Score
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse('component-detail', kwargs={'pk': self.object.component.subject.pk, 'comp_pk': self.object.component.pk})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# TAGS
|
|
||||||
class TagListView(LoginRequiredMixin, ListView):
|
|
||||||
model = Tag
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
object_list = Tag.objects.annotate(Count('component'))
|
|
||||||
return object_list
|
|
||||||
|
|
||||||
class TagCreateView(LoginRequiredMixin, CreateView):
|
|
||||||
model = Tag
|
|
||||||
template_name_suffix = '_create_form'
|
|
||||||
fields = ('__all__')
|
|
||||||
|
|
||||||
class TagDetailView(LoginRequiredMixin, DetailView):
|
|
||||||
model = Tag
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
queryset = Tag.objects.filter(
|
|
||||||
pk=self.kwargs.get(self.pk_url_kwarg)
|
|
||||||
).prefetch_related(
|
|
||||||
Prefetch('component_set', queryset=Component.objects.prefetch_related(
|
|
||||||
'score_set'
|
|
||||||
).annotate(
|
|
||||||
grade_avg_pre=Avg('score__value')
|
|
||||||
))
|
|
||||||
)
|
|
||||||
obj = queryset.get()
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
class TagUpdateView(LoginRequiredMixin, UpdateView):
|
|
||||||
model = Tag
|
|
||||||
fields = ('__all__')
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
pk = self.kwargs["pk"]
|
|
||||||
return reverse('tag-detail', kwargs={'pk': pk})
|
|
||||||
|
|
||||||
class TagDeleteView(LoginRequiredMixin, DeleteView):
|
|
||||||
model = Tag
|
|
||||||
success_url = reverse_lazy('tag-list')
|
|
||||||
@ -34,9 +34,6 @@ INSTALLED_APPS = [
|
|||||||
# Local
|
# Local
|
||||||
'accounts.apps.AccountsConfig',
|
'accounts.apps.AccountsConfig',
|
||||||
'core.apps.CoreConfig',
|
'core.apps.CoreConfig',
|
||||||
'students.apps.StudentsConfig',
|
|
||||||
'gradebook.apps.GradebookConfig',
|
|
||||||
'attendance.apps.AttendanceConfig',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Middlewares
|
# Middlewares
|
||||||
@ -147,7 +144,7 @@ STATICFILES_FINDERS = (
|
|||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
AUTH_USER_MODEL = 'accounts.User'
|
AUTH_USER_MODEL = 'accounts.User'
|
||||||
LOGIN_REDIRECT_URL = reverse_lazy('core:schoolyear-list')
|
LOGIN_REDIRECT_URL = reverse_lazy('core:today')
|
||||||
|
|
||||||
# Decimal settings
|
# Decimal settings
|
||||||
DEFAULT_DECIMAL_PLACES = 2
|
DEFAULT_DECIMAL_PLACES = 2
|
||||||
|
|||||||
@ -7,9 +7,6 @@ urlpatterns = [
|
|||||||
path('', include(('core.urls', 'core'), namespace='core')),
|
path('', include(('core.urls', 'core'), namespace='core')),
|
||||||
path('accounts/', include('accounts.urls'), name='accounts'),
|
path('accounts/', include('accounts.urls'), name='accounts'),
|
||||||
path('accounts/', include('django.contrib.auth.urls')),
|
path('accounts/', include('django.contrib.auth.urls')),
|
||||||
path('students/', include('students.urls'), name='students'),
|
|
||||||
path('gradebook/', include('gradebook.urls'), name='gradebook'),
|
|
||||||
path('attendance/', include('attendance.urls'), name='attendance'),
|
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('__debug__/', include('debug_toolbar.urls')),
|
path('__debug__/', include('debug_toolbar.urls')),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -354,9 +354,12 @@ form input[type=checkbox] {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.site__logo {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.nav__account {
|
.nav__account {
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
/*align-items: center;*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
from django.contrib import admin
|
|
||||||
from .models import (
|
|
||||||
Student,
|
|
||||||
Parent,
|
|
||||||
)
|
|
||||||
|
|
||||||
admin.site.register(Student)
|
|
||||||
admin.site.register(Parent)
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class StudentsConfig(AppConfig):
|
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
|
||||||
name = 'students'
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
from django import forms
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
from .models import Student, Parent, Thread, Message
|
|
||||||
|
|
||||||
class StudentForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Student
|
|
||||||
fields = (
|
|
||||||
'student_id',
|
|
||||||
'first_name',
|
|
||||||
'last_name',
|
|
||||||
'address',
|
|
||||||
'dob',
|
|
||||||
'glasses',
|
|
||||||
'allergies',
|
|
||||||
'sit',
|
|
||||||
'iep_behavioral',
|
|
||||||
'iep_math',
|
|
||||||
'iep_ela',
|
|
||||||
)
|
|
||||||
labels = {
|
|
||||||
'student_id': 'Student ID',
|
|
||||||
'dob': 'DOB',
|
|
||||||
'sit': 'SIT',
|
|
||||||
'iep_behavioral': 'IEP Behavioral',
|
|
||||||
'iep_math': 'IEP Math',
|
|
||||||
'iep_ela': 'IEP ELA',
|
|
||||||
}
|
|
||||||
|
|
||||||
class ParentForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Parent
|
|
||||||
fields = (
|
|
||||||
'students',
|
|
||||||
'first_name',
|
|
||||||
'last_name',
|
|
||||||
'relation',
|
|
||||||
'phone_number',
|
|
||||||
'email_address',
|
|
||||||
'notes',
|
|
||||||
)
|
|
||||||
|
|
||||||
class ThreadForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Thread
|
|
||||||
fields = (
|
|
||||||
'parent',
|
|
||||||
'student',
|
|
||||||
'subject',
|
|
||||||
)
|
|
||||||
widgets = {
|
|
||||||
'subject': forms.TextInput(attrs = {
|
|
||||||
'placeholder': 'Reason for contact…'
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
class MessageForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Message
|
|
||||||
fields = (
|
|
||||||
'method_of_contact',
|
|
||||||
'date',
|
|
||||||
'time',
|
|
||||||
'content',
|
|
||||||
)
|
|
||||||
widgets = {
|
|
||||||
'content': forms.Textarea(attrs = {
|
|
||||||
'placeholder': 'Enter your message here'
|
|
||||||
}),
|
|
||||||
'date': forms.DateInput(attrs = {
|
|
||||||
'type': 'date',
|
|
||||||
}),
|
|
||||||
'time': forms.DateInput(attrs = {
|
|
||||||
'type': 'time',
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
@ -1,73 +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 = [
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Parent',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('first_name', models.CharField(max_length=50)),
|
|
||||||
('last_name', models.CharField(max_length=50)),
|
|
||||||
('relation', models.CharField(choices=[('MO', 'Mother'), ('FA', 'Father'), ('GM', 'Grandmother'), ('GP', 'Grandfather'), ('SM', 'Stepmother'), ('SF', 'Stepfather'), ('OT', 'Other')], default='MO', max_length=2)),
|
|
||||||
('phone_number', models.IntegerField(blank=True, null=True)),
|
|
||||||
('email_address', models.EmailField(blank=True, max_length=254, null=True)),
|
|
||||||
('notes', models.TextField(blank=True)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Student',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('student_id', models.IntegerField()),
|
|
||||||
('first_name', models.CharField(max_length=50)),
|
|
||||||
('last_name', models.CharField(max_length=50)),
|
|
||||||
('address', models.TextField(blank=True)),
|
|
||||||
('dob', models.DateField()),
|
|
||||||
('sit', models.CharField(blank=True, choices=[('T2', 'Tier 2'), ('T3', 'Tier 3')], max_length=2)),
|
|
||||||
('iep_behavioral', models.BooleanField(default=False)),
|
|
||||||
('iep_math', models.BooleanField(default=False)),
|
|
||||||
('iep_ela', models.BooleanField(default=False)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'ordering': ['first_name'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Thread',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('subject', models.CharField(max_length=250)),
|
|
||||||
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='students.parent')),
|
|
||||||
('student', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='students.student')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='parent',
|
|
||||||
name='students',
|
|
||||||
field=models.ManyToManyField(blank=True, to='students.Student'),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Message',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('date', models.DateField(blank=True, null=True)),
|
|
||||||
('time', models.TimeField(blank=True, null=True)),
|
|
||||||
('content', models.TextField()),
|
|
||||||
('method_of_contact', models.CharField(blank=True, choices=[('PH', 'Phone'), ('EM', 'Email'), ('NO', 'Note'), ('IP', 'In person')], max_length=2)),
|
|
||||||
('thread', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='students.thread')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'ordering': ('-date',),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 3.2.7 on 2021-09-12 00:03
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('students', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='student',
|
|
||||||
name='allergies',
|
|
||||||
field=models.CharField(blank=True, max_length=50),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='student',
|
|
||||||
name='glasses',
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 3.2.7 on 2021-09-12 00:07
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('students', '0002_auto_20210912_0003'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='student',
|
|
||||||
options={'ordering': ['student_id']},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 3.2.7 on 2021-09-12 00:30
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('students', '0003_alter_student_options'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='parent',
|
|
||||||
name='first_name',
|
|
||||||
field=models.CharField(blank=True, max_length=50),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='parent',
|
|
||||||
name='last_name',
|
|
||||||
field=models.CharField(blank=True, max_length=50),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,131 +0,0 @@
|
|||||||
from datetime import datetime, date
|
|
||||||
from django.db import models
|
|
||||||
from django.urls import reverse
|
|
||||||
|
|
||||||
from django.db.models import Count, Sum, F, Value
|
|
||||||
from django.db.models.functions import Length, Upper
|
|
||||||
|
|
||||||
|
|
||||||
class Student(models.Model):
|
|
||||||
class Meta:
|
|
||||||
ordering = ['student_id']
|
|
||||||
|
|
||||||
student_id = models.IntegerField()
|
|
||||||
first_name = models.CharField(max_length=50)
|
|
||||||
last_name = models.CharField(max_length=50)
|
|
||||||
address = models.TextField(blank=True)
|
|
||||||
dob = models.DateField()
|
|
||||||
|
|
||||||
T2 = 'T2'
|
|
||||||
T3 = 'T3'
|
|
||||||
SIT_CHOICES = [
|
|
||||||
(T2, 'Tier 2'),
|
|
||||||
(T3, 'Tier 3'),
|
|
||||||
]
|
|
||||||
|
|
||||||
sit = models.CharField(
|
|
||||||
max_length=2,
|
|
||||||
choices=SIT_CHOICES,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
glasses = models.BooleanField(default=False)
|
|
||||||
allergies = models.CharField(max_length=50, blank=True)
|
|
||||||
|
|
||||||
iep_behavioral = models.BooleanField(default=False)
|
|
||||||
iep_math = models.BooleanField(default=False)
|
|
||||||
iep_ela = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def full_name(self):
|
|
||||||
return f"{self.first_name} {self.last_name}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def age(self):
|
|
||||||
today = date.today()
|
|
||||||
return today.year - self.dob.year - ((today.month, today.day) < (self.dob.month, self.dob.day))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.first_name} {self.last_name}"
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('student-detail', kwargs={'pk': self.pk})
|
|
||||||
|
|
||||||
|
|
||||||
class Parent(models.Model):
|
|
||||||
RELATION_CHOICES = [
|
|
||||||
('MO', 'Mother'),
|
|
||||||
('FA', 'Father'),
|
|
||||||
('GM', 'Grandmother'),
|
|
||||||
('GP', 'Grandfather'),
|
|
||||||
('SM', 'Stepmother'),
|
|
||||||
('SF', 'Stepfather'),
|
|
||||||
('OT', 'Other'),
|
|
||||||
]
|
|
||||||
students = models.ManyToManyField(Student, blank=True)
|
|
||||||
first_name = models.CharField(max_length=50, blank=True)
|
|
||||||
last_name = models.CharField(max_length=50, blank=True)
|
|
||||||
relation = models.CharField(
|
|
||||||
max_length=2,
|
|
||||||
choices=RELATION_CHOICES,
|
|
||||||
default='MO',
|
|
||||||
)
|
|
||||||
phone_number = models.IntegerField(blank=True, null=True)
|
|
||||||
email_address = models.EmailField(blank=True, null=True)
|
|
||||||
notes = models.TextField(blank=True)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def full_name(self):
|
|
||||||
return f"{self.first_name} {self.last_name}"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.first_name} {self.last_name}"
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('parent-detail', kwargs={'pk': self.pk})
|
|
||||||
|
|
||||||
|
|
||||||
class Thread(models.Model):
|
|
||||||
parent = models.ForeignKey(Parent, on_delete=models.CASCADE, blank=True, null=True)
|
|
||||||
student = models.ForeignKey(Student, on_delete=models.CASCADE, blank=True, null=True)
|
|
||||||
subject = models.CharField(max_length=250)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.parent} | {self.subject}"
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('thread-detail', kwargs={'pk': self.pk})
|
|
||||||
|
|
||||||
|
|
||||||
class Message(models.Model):
|
|
||||||
class Meta:
|
|
||||||
ordering = ('-date',)
|
|
||||||
|
|
||||||
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
|
|
||||||
date = models.DateField(blank=True, null=True)
|
|
||||||
time = models.TimeField(blank=True, null=True)
|
|
||||||
content = models.TextField()
|
|
||||||
|
|
||||||
PHONE = 'PH'
|
|
||||||
EMAIL = 'EM'
|
|
||||||
NOTE = 'NO'
|
|
||||||
IN_PERSON = 'IP'
|
|
||||||
|
|
||||||
MOC_CHOICES = [
|
|
||||||
(PHONE, 'Phone'),
|
|
||||||
(EMAIL, 'Email'),
|
|
||||||
(NOTE, 'Note'),
|
|
||||||
(IN_PERSON, 'In person'),
|
|
||||||
]
|
|
||||||
|
|
||||||
method_of_contact = models.CharField(
|
|
||||||
max_length=2,
|
|
||||||
choices=MOC_CHOICES,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.date} {self.thread.subject}"
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('message-detail', kwargs={'pk': self.thread.pk, 'message_pk': self.pk})
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Delete {{message}}</h1>
|
|
||||||
<form method="post" action="{% url 'message-delete' message.thread.pk message.pk %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{message}}"> or <a href="{% url 'thread-detail' message.thread.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>{{thread.subject}}</h1>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'message-create' thread.pk %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Create Message"> or <a href="{% url 'thread-detail' thread.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
<div class="message">
|
|
||||||
<strong class="message__date">
|
|
||||||
{{message.date|date:"D, M j"}}<br>
|
|
||||||
{{message.time|time:"TIME_FORMAT"}} via {{message.get_method_of_contact_display}}
|
|
||||||
</strong>
|
|
||||||
<span><a href="{% url 'message-update' thread.pk message.pk %}">Edit</a></span>
|
|
||||||
<p>{{message.content|linebreaksbr}}</p>
|
|
||||||
</div>
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<div class="generic__header">
|
|
||||||
<h1>Update Message</h1>
|
|
||||||
<a class="action-button action-delete" href="{% url 'message-delete' message.thread.pk message.pk %}">Delete {{message}}</a>
|
|
||||||
</div>
|
|
||||||
<section>
|
|
||||||
<form action="{% url 'message-update' message.thread.pk message.pk %}" method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form.as_p}}
|
|
||||||
<p>
|
|
||||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'thread-detail' message.thread.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Parents</h1>
|
|
||||||
<section>
|
|
||||||
<p>
|
|
||||||
<a class="action-button" href="{% url 'message-create' %}">+ New message</a>
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
{% for message in message_list %}
|
|
||||||
<li class="message">
|
|
||||||
<a class="message__li" href="{% url 'message-detail' message.pk %}">{{message}}</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Delete {{parent}}</h1>
|
|
||||||
<form method="post" action="{% url 'parent-delete' parent.pk %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
<input class="action-button action-delete" type="submit" value="Confirm Delete {{parent}}"> or <a href="{% url 'parent-detail' parent.pk %}">cancel</a>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user