Moved active status to a database entry apposed to a function

Added settings page
Cleaned up rollover page
This commit is contained in:
John Mullins
2023-12-21 16:14:56 +11:00
parent 1240ab2538
commit 25ddf1edfa
12 changed files with 128 additions and 58 deletions
@@ -14,6 +14,7 @@
<a href={% url 'report:bus_roll' %}>Bus Roll</a> /
<a href={% url 'report:bus_summary' %}>Bus Routes</a> /
<a href="https://www2.education.vic.gov.au/pal/school-bus-program/resources">Resources</a> /
<a href={% url 'settings:index' %}>Settings</a> /
<form id="logout-form" method="post" action="{% url 'admin:logout' %}">
{% csrf_token %}
<button type="submit">{% translate 'Log out' %}</button>
+1 -3
View File
@@ -18,14 +18,12 @@ from azure_auth.views import azure_auth_logout
from django.contrib import admin
from django.urls import path, include
from coord.utils.rollover import rollover
admin.site.site_header = "Bus Portal Admin"
urlpatterns = [
path('report/', include("coord.urls")),
path('logout/', azure_auth_logout),
path('auth/', include("azure_auth.urls"),),
path('rollover', rollover, name="rollover"),
path('settings/', include('coord.urls_settings')),
path('', admin.site.urls),
]
+4 -23
View File
@@ -58,7 +58,7 @@ class SchoolRollMixin:
traveller_list = []
for school in queryset:
for travellerRoute in TravellerRoute.objects.filter(traveller__school=school):
if not travellerRoute.traveller.is_active():
if not travellerRoute.traveller._is_active():
continue
traveller_list.append(traveller_route_context(travellerRoute))
@@ -92,25 +92,6 @@ class TravellerRollMixin:
return response
class ArchiveFilter(BooleanFieldListFilter):
parameter_name = 'is_archived'
def choices(self, changelist):
field_choices = dict(self.field.flatchoices)
for lookup, title in (
(None, gettext_lazy("All")),
("0", field_choices.get(True, gettext_lazy("Current Year"))),
("1", field_choices.get(False, gettext_lazy("Archived"))),
):
yield {
"selected": self.lookup_val == lookup and not self.lookup_val2,
"query_string": changelist.get_query_string(
{self.lookup_kwarg: lookup}, [self.lookup_kwarg2]
),
"display": title,
}
class HiddenNowTime(widgets.TimeInput):
pass
@@ -192,7 +173,7 @@ class TravellerRouteInline(admin.TabularInline):
class TravellerAdmin(MyImportExportModelAdmin, CloneModelAdmin, admin.ModelAdmin, TravellerRollMixin):
list_display = ["first_name", "last_name", "school", "year_level", "residential_address", "residential_suburb", "stop_route", "shuttle", "travel_start_date", "travel_end_date"]
list_filter = [
("is_archived", ArchiveFilter), "school", "year_level", "eligibility_status", "bus_stops__bus", "shuttle", "residential_suburb",
"is_active", "school", "year_level", "eligibility_status", "bus_stops__bus", "shuttle", "residential_suburb",
("travel_start_date", DateRangeFilterBuilder(
title="Start date"
)),
@@ -209,7 +190,7 @@ class TravellerAdmin(MyImportExportModelAdmin, CloneModelAdmin, admin.ModelAdmin
"emergency_contact_A_relation", "emergency_contact_B_firstname", "emergency_contact_B_lastname",
"emergency_contact_B_phone", "emergency_contact_B_relation"]
inlines = [TravellerRouteInline]
readonly_fields = ["fare_paying", "created_on", "last_edit", "is_archived"]
readonly_fields = ["travel_start_date", "travel_end_date", "fare_paying", "created_on", "last_edit", "is_active"]
actions = ["export_to_csv", "send_sms", "confirmation_letter", "letter_creator"]
fieldsets = [
(None, {
@@ -248,7 +229,7 @@ class TravellerAdmin(MyImportExportModelAdmin, CloneModelAdmin, admin.ModelAdmin
"seat_number",
"created_on",
"last_edit",
"is_archived",
"is_active",
]
}),
('Adult Contacts', {
+8 -7
View File
@@ -33,7 +33,7 @@ def school_roll_context(queryset):
travellers = []
for trav_route in TravellerRoute.objects.filter(query).filter(busStop__bus=bus).order_by('busStop__am_time'):
traveller = trav_route.traveller
if not traveller.is_active():
if not traveller._is_active():
continue
bus_stop = trav_route.busStop
@@ -82,7 +82,7 @@ def route_paged_context(bus):
traveller_list = []
for trav_route in traveller_routes:
traveller = trav_route.traveller
if not traveller.is_active():
if not traveller._is_active():
continue
is_fared = "---"
if traveller.eligibility_status == "2":
@@ -114,17 +114,18 @@ def route_paged_context(bus):
def shuttle_route_context(shuttle):
shuttle_travellers = []
for traveller in Traveller.objects.filter(shuttle=shuttle):
if traveller.is_active():
if traveller._is_active():
shuttle_travellers.append({
'display': f"{traveller} ({traveller.get_year_level_display()}, {traveller.school})",
})
return {'shuttle': shuttle, 'shuttle_travellers': shuttle_travellers}
def school_travellerRoute_context(school):
travellers = []
for travellerRoute in TravellerRoute.objects.filter(traveller__school=school):
traveller = travellerRoute.traveller
if not traveller.is_active():
if not traveller._is_active():
continue
travellers.append(traveller_route_context(travellerRoute))
return travellers
@@ -165,7 +166,7 @@ def emergency_contacts_context(queryset=None):
traveller_list = []
for travellerRoute in TravellerRoute.objects.filter(busStop__bus=bus):
traveller = travellerRoute.traveller
if not traveller.is_active():
if not traveller._is_active():
continue
parent_a = ""
if travellerRoute.traveller.parent_A_firstname:
@@ -206,7 +207,7 @@ def bus_summary_context():
traveller_count = 0
for travellerRoute in TravellerRoute.objects.filter(busStop__bus=bus):
if travellerRoute.traveller.is_active():
if travellerRoute.traveller._is_active():
traveller_count += 1
shuttle_name = ""
@@ -218,7 +219,7 @@ def bus_summary_context():
shuttle_name += f", {shuttle.school.shortName}"
for traveller in Traveller.objects.filter(shuttle=shuttle):
if traveller.is_active():
if traveller._is_active():
shuttle_count += 1
over_capacity = traveller_count > bus.seating_capacity or shuttle_count > bus.seating_capacity
+34 -9
View File
@@ -87,7 +87,7 @@ class Bus(models.Model):
def traveller_count(self):
count = 0
for traveller in Traveller.objects.filter(bus_stops__bus=self):
if traveller.is_active():
if traveller._is_active():
count += 1
return count
@@ -96,6 +96,9 @@ class Shuttle(models.Model):
bus = models.ForeignKey(Bus, on_delete=models.CASCADE)
school = models.ForeignKey(School, on_delete=models.CASCADE)
custom_name = models.CharField(max_length=10, blank=True)
transfer_school = models.ForeignKey(School, related_name='transfer_school', on_delete=models.CASCADE)
am_service = models.BooleanField(default=True)
pm_service = models.BooleanField(default=True)
class Meta:
ordering = ["school__name"]
@@ -111,7 +114,7 @@ class Shuttle(models.Model):
def traveller_count(self):
count = 0
for traveller in Traveller.objects.filter(shuttle=self, is_archived=False):
if traveller.is_active():
if traveller._is_active():
count += 1
return count
@@ -235,6 +238,7 @@ class Traveller(models.Model):
created_on = models.DateTimeField(auto_now_add=True, blank=True, null=True)
last_edit = models.DateTimeField(auto_now=True, blank=True, null=True)
is_archived = models.BooleanField(default=False, verbose_name="Archived")
is_active = models.BooleanField(default=True, verbose_name='Active')
notes = models.TextField(blank=True, verbose_name='Admin Notes')
shuttle = models.ForeignKey(Shuttle, on_delete=models.SET_NULL, blank=True, null=True)
@@ -244,18 +248,33 @@ class Traveller(models.Model):
def __str__(self):
return f"{self.first_name} {self.last_name}"
def is_active(self):
if self.is_archived:
return False
if not self.travel_start_date:
return False
if datetime(self.travel_start_date.year, self.travel_start_date.month, self.travel_start_date.day) > datetime.today():
def _is_active(self):
today = datetime.today()
today = datetime(today.year, today.month, today.day)
if not self.travel_start_date or datetime(self.travel_start_date.year, self.travel_start_date.month, self.travel_start_date.day) > today:
return False
if not self.travel_end_date:
return True
end_date = datetime(self.travel_end_date.year, self.travel_end_date.month, self.travel_end_date.day)
return end_date > datetime.today()
return end_date >= today
def update_active_status(self):
self.travel_start_date = None
self.travel_end_date = None
for travellerRoute in TravellerRoute.objects.filter(traveller=self):
route_start = travellerRoute.travel_start_date
print(route_start)
route_end = travellerRoute.travel_end_date
if route_start is not None:
if self.travel_start_date is None or self.travel_start_date > route_start:
self.travel_start_date = route_start
if route_end is not None:
if self.travel_end_date is None or self.travel_end_date < route_end:
self.travel_end_date = route_end
self.is_active = self._is_active()
self.save()
def fare_paying(self):
if self.eligibility_status != "2":
@@ -299,6 +318,8 @@ class Traveller(models.Model):
class TravellerRoute(models.Model):
traveller = models.ForeignKey(Traveller, on_delete=models.CASCADE)
busStop = models.ForeignKey(BusStop, on_delete=models.CASCADE)
travel_start_date = models.DateField(blank=True, null=True)
travel_end_date = models.DateField(blank=True, null=True)
mon_am = models.BooleanField(default=True)
mon_pm = models.BooleanField(default=True)
tue_am = models.BooleanField(default=True)
@@ -314,6 +335,10 @@ class TravellerRoute(models.Model):
def __str__(self):
return f"{self.busStop}"
def save(self, *args, **kwargs):
super(TravellerRoute, self).save(*args, **kwargs)
# self.traveller.update_active_status()
def active_stops(self):
stops = 0
if self.mon_am:
+8
View File
@@ -0,0 +1,8 @@
from coord.models import TravellerRoute
def copy_travel_dates():
for Route in TravellerRoute.objects.all():
Route.travel_start_date = Route.traveller.travel_start_date
Route.travel_end_date = Route.traveller.travel_end_date
Route.save()
@@ -0,0 +1,16 @@
{% extends "admin/base_site.html" %}
{% block content %}
<h1>Yearly rollover</h1>
<p>Initiating a rollover will perform the following actions.</p>
<ul>
<li>Set any active Year 12 traveller without an exit date to the selected exit date.</li>
<li>Set any active traveller with an exit date before today to inactive</li>
<li>Increase the year level of everyone except for <b>Adult Learners</b> and <b>Year 12</b></li>
</ul>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
</form>
<input type="submit" style="padding: 15px 30px; font-size: 18px; background: var(--delete-button-bg);" value="Initiate!">
{% endblock %}
@@ -0,0 +1,16 @@
{% extends "admin/base_site.html" %}
{% block content %}
<div><a href={% url 'settings:index' %}>Run nightly task</a></div>
<div>
<div class="flex-container">
<label for="id_traveller_term_cost">
Per term cost
</label>
<input type="number" name="traveller_term_cost" class="vIntegerField" min="0" id="id_traveller_term_cost">
</div>
</div>
<a class="button" style="position: absolute; left: 50%; transform: translateX(-50%); padding: 15px 50px; background: var(--delete-button-bg);" href={% url 'settings:rollover' %}>Rollover</a>
</div>
{% endblock %}
+10
View File
@@ -0,0 +1,10 @@
from django.urls import path
from . import views_settings
app_name = "settings"
urlpatterns = [
path('rollover', views_settings.rollover, name='rollover'),
path('nightly_task', views_settings.nightly_task, name='nightly-task'),
path('', views_settings.settings, name='index'),
]
+1 -14
View File
@@ -23,7 +23,7 @@ class RolloverForm(forms.Form):
agreement = forms.BooleanField(label='I have run an export/backup for travellers')
def _rollover(exit_date):
def execute_rollover(exit_date):
print(f"Setting exit date {exit_date}")
result = Traveller.objects.filter(year_level='12', travel_end_date=None).update(travel_end_date=exit_date)
print(f"{result} Year 12s exited")
@@ -45,16 +45,3 @@ def _rollover(exit_date):
result += Traveller.objects.filter(is_archived=False, year_level='PS').update(year_level='00')
print(f"{result} travellers modified")
@staff_member_required
def rollover(request):
# if 'confirm' in request.POST:
if request.method == 'POST':
form = RolloverForm(request.POST)
if form.is_valid():
_rollover(form.cleaned_data['exit_date'])
# rollover_mixin.message_user(request, "Users have been rolled over")
return HttpResponseRedirect('/')
form = RolloverForm()
return render(request, 'admin/rollover_form.html', context={'form': form})
+2 -2
View File
@@ -12,7 +12,7 @@ class SMSForm(forms.Form):
def send_sms(send_sms_mixin, request, queryset):
if 'send' in request.POST:
if 'confirm' in request.POST:
message = request.POST["message"]
send_to_parents = False
if request.POST.get("send_to_parents"):
@@ -26,7 +26,7 @@ def send_sms(send_sms_mixin, request, queryset):
total = 0
numbers = []
for traveller in queryset:
if only_include_active_travellers and not traveller.is_active():
if only_include_active_travellers and not traveller._is_active():
continue
numbers.append(traveller.get_parsed_numbers(parents=send_to_parents, emergency=send_to_emergency_contacts))
+27
View File
@@ -0,0 +1,27 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import render, redirect
from coord.scheduled_tasks import copy_travel_dates
from coord.utils.rollover import RolloverForm, execute_rollover
@staff_member_required
def settings(request):
return render(request, 'admin/settings_index.html')
@staff_member_required
def rollover(request):
if request.method == 'POST':
form = RolloverForm(request.POST)
if form.is_valid():
execute_rollover(form.cleaned_data['exit_date'])
return redirect('settings:index')
form = RolloverForm()
return render(request, 'admin/rollover_form.html', context={'form': form})
@staff_member_required
def nightly_task(request):
copy_travel_dates()
return redirect('settings:index')