Moved active status to a database entry apposed to a function
Added settings page Cleaned up rollover page
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
|
||||
@@ -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', {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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'),
|
||||
]
|
||||
@@ -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})
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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')
|
||||
Reference in New Issue
Block a user