Changed bus roll to be a pdf view. Small changes needed to be able to email to companies.

Dropdown selection in admin > buses > Email Bus Roll To Company
Will email bus roll only to the company the route is a member of. Will show all to the admin in a pdf file.

Recipients are currently hardcoded for testing purposes.
This commit is contained in:
John Mullins
2023-08-29 12:40:34 +10:00
parent 2f1ae0586b
commit 0c8de0285a
7 changed files with 119 additions and 74 deletions
+6 -4
View File
@@ -9,6 +9,8 @@ from django.utils.http import urlencode
from import_export.admin import ImportExportModelAdmin
from .adminClone import CloneModelAdmin
from .context_helpers import bus_roll_context
from .email_helpers import email_companies_bus_roll, render_to_pdf
from .models import *
from .views import bus_roll
@@ -33,11 +35,11 @@ class ExportCsvMixin:
class BusRollMixin:
def bus_roll(self, request, queryset):
return bus_roll(request, queryset)
def show_bus_roll(self, request, queryset):
return render_to_pdf('reports/bus_roll.html', bus_roll_context(queryset))
def email_company(self, request, queryset):
pass
return email_companies_bus_roll(request, queryset)
email_company.short_description = "Email Bus Roll to Company"
@@ -81,7 +83,7 @@ class BusesAdmin(MyImportExportModelAdmin, admin.ModelAdmin, BusRollMixin):
list_filter = ["company"]
list_display = ["route_name", "company", "contract_number", "route_travellers"]
readonly_fields = ["traveller_count"]
actions = ["email_company", "bus_roll"]
actions = ["email_company", "show_bus_roll"]
inlines = [DriverInline, BusStopInline]
fieldsets = [
(None, {'fields': [
+40
View File
@@ -0,0 +1,40 @@
from coord.models import Bus, BusStop, TravellerRoute
def bus_roll_context(queryset=None):
bus_routes = []
if queryset is None:
buses = Bus.objects.all()
else:
buses = queryset
for bus in buses:
bus_route = []
for bus_stop in BusStop.objects.filter(bus=bus):
traveller_list = []
for trav_route in TravellerRoute.objects.filter(busStop=bus_stop):
traveller = trav_route.traveller
if not traveller.is_active():
continue
is_fared = "---"
if traveller.eligibility_status == "2":
is_fared = "Y"
traveller_list.append({
'display': f"{traveller} ({traveller.get_year_level_display()}, {traveller.school.shortName})",
'isFared': is_fared
})
stop_result = {
'stop_num': bus_stop.get_stop_number(),
'name': bus_stop.address,
'am': bus_stop.am_time,
'pm': bus_stop.pm_time,
'travellers': traveller_list
}
# print(traveller_list)
bus_route.append(stop_result)
# Todo Add shuttles
bus_routes.append({'bus': bus, 'stops': bus_route})
return {'routes': bus_routes}
+47
View File
@@ -0,0 +1,47 @@
from datetime import date
from io import BytesIO
from django.contrib.admin.views.decorators import staff_member_required
from django.core.mail import send_mail, EmailMessage
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa
from coord.context_helpers import bus_roll_context
from coord.models import Company
def render_to_pdf(template, context):
html = get_template(template).render(context)
result = BytesIO()
pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
if pdf.err:
return HttpResponse("Invalid PDF", status_code=400, content_type='text/plan')
return HttpResponse(result.getvalue(), content_type='application/pdf')
@staff_member_required
def email_companies_bus_roll(request, query_set=None):
html_template = 'reports/bus_roll.html'
context = bus_roll_context(query_set)
for company in Company.objects.all():
company_route = []
for route in context.get("routes"):
bus = route.get("bus")
if route.get("bus").company == company:
company_route.append(route)
if not company_route:
continue
company_context = {'routes': company_route}
pdf = render_to_pdf(html_template, company_context)
subject = "Echuca Schools Bus Roll"
message = f"A new bus roll for {company.name} has been generated"
email_from = "bus.manager@education.vic.gov.au"
recipient = ["john.mullins@education.vic.gov.au", "nicole.edwards@education.vic.gov.au"]
email = EmailMessage(subject, message, email_from, recipient)
email.attach(f"school_bus_roll_{date.today()}.pdf", pdf.content)
email.send()
return render_to_pdf(html_template, context)
@@ -6,35 +6,24 @@
width: 100%;
}
table.traveller th {
text-align: left;
border-bottom: 2px solid #000;
}
table.traveller td {
table-layout: fixed;
border-bottom: 2px solid #000;
border-right-style: dashed;
width: 100%;
min-width: 30px;
border: 2px solid #000;
}
hr {
border: 2px solid;
border: 3px solid;
}
</style>
{% for route in routes %}
<h1>{{ route.route_name }}</h1>
<h1 style="font-size: 2.5em">{{ route.bus.route_name }}</h1>
{% for stop in route.stops %}
<hr>
<hr style="border: 5px">
<table class="stopHeader">
<tr>
<th>Stop Number #{{ stop.stop_num }}</th>
<th>Pickup Time</th>
<th>Drop-off Time</th>
<th style="width: 100%">Stop Number #{{ stop.stop_num }}</th>
<th style="width: 20%">Pickup Time</th>
<th style="width: 25%">Drop-off Time</th>
</tr>
<tr>
<td>{{ stop.name }}</td>
@@ -44,23 +33,23 @@
</table>
<table class="traveller">
<tr>
<th>Student</th>
<th>Fare</th>
<th>Mon AM</th>
<th>Mon PM</th>
<th>Tue AM</th>
<th>Tue PM</th>
<th>Wed AM</th>
<th>Wed PM</th>
<th>Thu AM</th>
<th>Thu PM</th>
<th>Fri AM</th>
<th>Fri PM</th>
<th style="width: 100%; text-align: left">Student</th>
<th style="width: 10%">Fare</th>
<th style="width: 8%">Mon AM</th>
<th style="width: 8%">Mon PM</th>
<th style="width: 8%">Tue AM</th>
<th style="width: 8%">Tue PM</th>
<th style="width: 8%">Wed AM</th>
<th style="width: 8%">Wed PM</th>
<th style="width: 8%">Thu AM</th>
<th style="width: 8%">Thu PM</th>
<th style="width: 8%">Fri AM</th>
<th style="width: 8%">Fri PM</th>
</tr>
{% for traveller in stop.travellers %}
<tr>
<td>{{ traveller.display }}</td>
<td><b>{{ traveller.isFared }}</b></td>
<td style="padding-top: 3px; padding-left: 2px; text-align: left">{{ traveller.display }}</td>
<td style="padding-top: 3px; text-align: center"><b>{{ traveller.isFared }}</b></td>
<td> </td>
<td> </td>
<td> </td>
+2 -1
View File
@@ -6,5 +6,6 @@ urlpatterns = [
path("roll", views.bus_roll, name="Student Roll"),
path("contacts", views.emergency_contacts, name="Emergency Contacts"),
path("stops", views.bus_summary, name="Stop Summary"),
path("summary", views.bus_numbers, name="Bus Summary")
path("summary", views.bus_numbers, name="Bus Summary"),
path("testemail", views.TestEmail, name="Test Email")
]
+3 -37
View File
@@ -2,6 +2,8 @@ from django.contrib.admin.views.decorators import staff_member_required
from django.http import HttpResponse
from django.shortcuts import render
from django.views.generic import ListView
from .context_helpers import bus_roll_context
from .models import Company, Bus, Traveller, BusStop, TravellerRoute, Shuttle, Driver
@@ -46,43 +48,7 @@ def bus_numbers(request):
@staff_member_required
def bus_roll(request, queryset=None):
bus_routes = []
if queryset is None:
buses = Bus.objects.all()
else:
buses = queryset
for bus in buses:
bus_route = []
for bus_stop in BusStop.objects.filter(bus=bus):
traveller_list = []
for trav_route in TravellerRoute.objects.filter(busStop=bus_stop):
traveller = trav_route.traveller
if not traveller.is_active():
continue
is_fared = "---"
if traveller.eligibility_status == "2":
is_fared = "Y"
traveller_list.append({
'display': f"{traveller} ({traveller.get_year_level_display()}, {traveller.school.shortName})",
'isFared': is_fared
})
stop_result = {
'stop_num': bus_stop.get_stop_number(),
'name': bus_stop.address,
'am': bus_stop.am_time,
'pm': bus_stop.pm_time,
'travellers': traveller_list
}
# print(traveller_list)
bus_route.append(stop_result)
# Todo Add shuttles
bus_routes.append({'route_name': bus.route_name, 'stops': bus_route})
return render(request, 'reports/bus_roll.html', {'routes': bus_routes})
return render(request, 'reports/bus_roll.html', bus_roll_context(queryset))
@staff_member_required
BIN
View File
Binary file not shown.