From 86c9b13c9a10a91dad2f94b717af8efd01247b68 Mon Sep 17 00:00:00 2001 From: John Mullins Date: Fri, 1 Sep 2023 11:23:30 +1000 Subject: [PATCH] Added csv export for traveller and email to schools function --- busManager/coord/admin.py | 61 +++++++++++++++-------------- busManager/coord/context_helpers.py | 41 ++++++++++++++++++- busManager/coord/email_helpers.py | 28 ++++++++++--- busManager/coord/models.py | 1 - busManager/coord/views.py | 8 ---- 5 files changed, 93 insertions(+), 46 deletions(-) diff --git a/busManager/coord/admin.py b/busManager/coord/admin.py index 3d69307..d04ca38 100644 --- a/busManager/coord/admin.py +++ b/busManager/coord/admin.py @@ -1,7 +1,7 @@ import csv +from datetime import date from django.contrib.admin import DateFieldListFilter -from django.contrib.admin.views.decorators import staff_member_required from django.forms import widgets from django.contrib import admin from django.http import HttpResponse @@ -11,29 +11,11 @@ from django.utils.http import urlencode from import_export.admin import ImportExportModelAdmin from .adminClone import CloneModelAdmin -from .context_helpers import bus_roll_context, emergency_contacts_context -from .email_helpers import email_companies_bus_roll, render_to_pdf +from .context_helpers import bus_roll_context, emergency_contacts_context, traveller_roll_context +from .email_helpers import email_companies_bus_roll, render_to_pdf, email_school_roll_csv from .models import * -class ExportCsvMixin: - def export_as_csv(self, request, queryset): - meta = self.model._meta - field_names = [field.name for field in meta.fields] - - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta) - writer = csv.writer(response) - - writer.writerow(field_names) - for obj in queryset: - row = writer.writerow([getattr(obj, field) for field in field_names]) - - return response - - export_as_csv.short_description = "Export Selected" - - class BusRollMixin: def show_bus_roll(self, request, queryset): @@ -48,6 +30,30 @@ class BusRollMixin: email_company.short_description = "Email Bus Roll to Company" +class SchoolRollMixin: + + def email_school_roll(self, request, queryset): + for school in queryset: + if not school.email: + return HttpResponse(f"No email is set for {school.name}") + email_school_roll_csv(request, school) + + +class TravellerRollMixin: + + def export_to_csv(self, request, queryset): + traveller_list = traveller_roll_context(queryset) + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = f"attachment; filename=traveller_list_{date.today()}.csv" + + writer = csv.DictWriter(response, fieldnames=traveller_list[0].keys()) + writer.writeheader() + writer.writerows(traveller_list) + + return response + + + class HiddenNowTime(widgets.TimeInput): pass @@ -114,11 +120,6 @@ class BusStopAdmin(MyImportExportModelAdmin, admin.ModelAdmin): list_display = ["__str__", "am_time", "pm_time", "address"] search_fields = ["bus__route_name", "address"] - # change_form_template = 'admin/change_form_busStops.html' - - def export_as_csv(self, request, queryset): - pass - @admin.register(Suburb) class SuburbsAdmin(MyImportExportModelAdmin, admin.ModelAdmin): @@ -131,7 +132,7 @@ class TravellerRouteInline(admin.TabularInline): @admin.register(Traveller) -class TravellerAdmin(MyImportExportModelAdmin, CloneModelAdmin, admin.ModelAdmin): +class TravellerAdmin(MyImportExportModelAdmin, CloneModelAdmin, admin.ModelAdmin, TravellerRollMixin): list_display = ["first_name", "last_name", "school", "year_level", "residential_address", "residential_suburb", "stop_route", "travel_start_date", "travel_end_date"] list_filter = ["school", "eligibility_status", "bus_stops__bus", "residential_suburb", ("travel_end_date", DateFieldListFilter), "is_archived"] search_fields = ["first_name", "last_name", "residential_address"] @@ -144,6 +145,7 @@ class TravellerAdmin(MyImportExportModelAdmin, CloneModelAdmin, admin.ModelAdmin "emergency_contact_B_phone", "emergency_contact_B_relation"] inlines = [TravellerRouteInline] readonly_fields = ["fare_paying", "created_on", "last_edit", "is_archived"] + actions = ["export_to_csv", "yearly_rollover"] fieldsets = [ (None, { 'fields': [ @@ -209,8 +211,6 @@ class TravellerAdmin(MyImportExportModelAdmin, CloneModelAdmin, admin.ModelAdmin ] # list_display_links = None - actions = ["yearly_rollover"] - def yearly_rollover(self, request, queryset): pass @@ -236,8 +236,9 @@ class TravellerRouteAdmin(MyImportExportModelAdmin, admin.ModelAdmin): @admin.register(School) -class SchoolAdmin(MyImportExportModelAdmin, admin.ModelAdmin): +class SchoolAdmin(MyImportExportModelAdmin, admin.ModelAdmin, SchoolRollMixin): list_display = ["__str__", "address", "suburb", "school_email", "phone"] + actions = ["email_school_roll"] def school_email(self, obj): return format_html('{}', obj.email, obj.email) diff --git a/busManager/coord/context_helpers.py b/busManager/coord/context_helpers.py index 2fc368a..8c542de 100644 --- a/busManager/coord/context_helpers.py +++ b/busManager/coord/context_helpers.py @@ -1,4 +1,4 @@ -from coord.models import Bus, BusStop, TravellerRoute, Driver, Traveller, Shuttle +from coord.models import Bus, BusStop, TravellerRoute, Driver, Traveller, Shuttle, School def bus_roll_context(queryset=None): @@ -30,7 +30,6 @@ def bus_roll_context(queryset=None): 'pm': bus_stop.pm_time, 'travellers': traveller_list } - # print(traveller_list) bus_route.append(stop_result) # Todo Add shuttles @@ -39,6 +38,42 @@ def bus_roll_context(queryset=None): return {'routes': bus_routes} +def traveller_roll_context(queryset): + travellers = [] + for traveller in queryset: + for traveller_route in TravellerRoute.objects.filter(traveller=traveller): + travellers.append(traveller_route_context(traveller_route)) + return travellers + + +def school_roll_context(school): + travellers = [] + for travellerRoute in TravellerRoute.objects.filter(traveller__school=school): + traveller = travellerRoute.traveller + if not traveller.is_active(): + continue + travellers.append(traveller_route_context(travellerRoute)) + return travellers + + +def traveller_route_context(traveller_route): + traveller = traveller_route.traveller + return { + 'first_name': traveller.first_name, + 'last_name': traveller.last_name, + 'school': traveller.school, + 'dob': traveller.dob, + 'year_level': traveller.year_level, + 'address': f"{traveller.residential_address} {traveller.residential_suburb}", + 'start_date': traveller.travel_start_date, + 'end_date': traveller.travel_end_date, + 'eligibility': traveller.get_eligibility_status_display(), + 'shuttle': traveller.shuttle, + 'route': traveller_route.busStop.bus, + 'stop': f"#{traveller_route.busStop.get_stop_number()} - {traveller_route.busStop.address}" + } + + def emergency_contacts_context(queryset=None): if queryset is None: buses = Bus.objects.all() @@ -123,3 +158,5 @@ def bus_summary_context(): 'over_capacity': over_capacity, }) return {'routes': bus_routes} + + diff --git a/busManager/coord/email_helpers.py b/busManager/coord/email_helpers.py index b6fe0fc..c5f0e90 100644 --- a/busManager/coord/email_helpers.py +++ b/busManager/coord/email_helpers.py @@ -1,13 +1,13 @@ +import csv from datetime import date -from io import BytesIO +from io import BytesIO, StringIO -from django.contrib.admin.views.decorators import staff_member_required -from django.core.mail import send_mail, EmailMessage +from django.core.mail import 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.context_helpers import bus_roll_context, school_roll_context from coord.models import Company @@ -20,7 +20,6 @@ def render_to_pdf(template, context): 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) @@ -46,3 +45,22 @@ def email_companies_bus_roll(request, query_set=None): email.send(fail_silently=True) return render_to_pdf(html_template, context) + + +def email_school_roll_csv(request, school): + + travellers = school_roll_context(school) + csvFile = StringIO() + fieldnames = list(travellers[0].keys()) + + writer = csv.DictWriter(csvFile, fieldnames=fieldnames) + writer.writeheader(), + writer.writerows(travellers) + + subject = "Bus Roll" + message = f"A new bus roll for {school.name} has been generated" + email_from = "bus.manager@education.vic.gov.au" + recipient = [school.email] + email = EmailMessage(subject, message, email_from, recipient) + email.attach(f"school_bus_roll_{date.today()}.csv", csvFile.getvalue(), 'text/csv') + email.send() diff --git a/busManager/coord/models.py b/busManager/coord/models.py index e3849c7..e0188a3 100644 --- a/busManager/coord/models.py +++ b/busManager/coord/models.py @@ -1,7 +1,6 @@ from datetime import datetime from django.db import models -from django.db.models import Model class Setting(models.Model): diff --git a/busManager/coord/views.py b/busManager/coord/views.py index b879c28..7e66581 100644 --- a/busManager/coord/views.py +++ b/busManager/coord/views.py @@ -1,10 +1,8 @@ from django.contrib.admin.views.decorators import staff_member_required from django.shortcuts import render -from django.views.generic import ListView from .context_helpers import * from .email_helpers import render_to_pdf -from .models import Company, Bus, Traveller, BusStop, TravellerRoute, Shuttle, Driver @staff_member_required @@ -20,9 +18,3 @@ def emergency_contacts(request): @staff_member_required def bus_roll(request): return render_to_pdf('reports/bus_roll.html', bus_roll_context()) - - -@staff_member_required -class CompanyList(ListView): - model = Company - template_name = "companies.html"