Files
bus-manager/busManager/traveller/models.py
T
st01765 b695dd8054 Created new models to replicate out of and split coord app
Moved helpers and views to their respective new apps
2026-02-05 11:18:08 +11:00

295 lines
11 KiB
Python

from datetime import datetime
import phonenumbers
from django.core.exceptions import ValidationError
from django.db import models
from common.models import Suburb
class School(models.Model):
name = models.CharField(max_length=30, unique=True)
shortName = models.CharField(max_length=10, unique=True)
address = models.CharField(max_length=50)
suburb = models.ForeignKey(Suburb, on_delete=models.CASCADE)
email = models.CharField(max_length=50, blank=True)
phone = models.CharField(max_length=15, blank=True)
principal_name = models.CharField(max_length=50, blank=True)
principal_phone = models.CharField(max_length=15, blank=True)
notes = models.TextField(blank=True)
def __str__(self):
return self.name
class Traveller(models.Model):
YEAR = [
("PS", "PreSchool"),
("00", "Year 00"),
("01", "Year 01"),
("02", "Year 02"),
("03", "Year 03"),
("04", "Year 04"),
("05", "Year 05"),
("06", "Year 06"),
("07", "Year 07"),
("08", "Year 08"),
("09", "Year 09"),
("10", "Year 10"),
("11", "Year 11"),
("12", "Year 12"),
("AL", "Adult Learner"),
]
ELIGIBILITY_STATUS = [
("1", "Eligible"),
("2", "Ineligible"),
("3", "<4.8 Exemption"),
("4", "Eligible waitlisted"),
("5", "Ineligible waitlisted"),
("6", "Kinder Exemption"),
("7", "Tafe/Post Secondary Exemption"),
("8", "Other Exemption"),
]
school = models.ForeignKey(School, on_delete=models.PROTECT)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
dob = models.DateField(blank=True, null=True)
year_level = models.CharField(max_length=2, choices=YEAR)
bus_stops = models.ManyToManyField("transport.BusStop", through='TravellerRoute', blank=True)
distance_to_school = models.PositiveSmallIntegerField(blank=True, null=True)
address = models.CharField(max_length=100, default="")
travel_start_date = models.DateField(blank=True, null=True)
travel_end_date = models.DateField(blank=True, null=True)
eligibility_status = models.CharField(max_length=1, choices=ELIGIBILITY_STATUS)
assessment_date = models.DateField(blank=True, null=True)
fee_per_term = models.DecimalField(decimal_places=2, max_digits=5, blank=True, null=True)
term_1_paid = models.BooleanField(default=False)
term_2_paid = models.BooleanField(default=False)
term_3_paid = models.BooleanField(default=False)
term_4_paid = models.BooleanField(default=False)
application_form_completed = models.BooleanField()
parent_notified = models.BooleanField()
seat_number = models.CharField(max_length=5, blank=True)
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=False, verbose_name='Active')
notes = models.TextField(blank=True, verbose_name='Admin Notes')
shuttle = models.ForeignKey("transport.Shuttle", on_delete=models.SET_NULL, blank=True, null=True)
class Meta:
ordering = ["last_name", "first_name"]
def __str__(self):
return f"{self.first_name} {self.last_name}"
def save(self, *args, **kwargs):
self._update_active_status()
self._repopulate_address()
super(Traveller, self).save(*args, **kwargs)
def _is_active(self, date=None):
if date is None:
today = datetime.today()
date = 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) > date:
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 >= date
def _update_active_status(self):
new_start_date = None
new_end_date = None
for travellerRoute in TravellerRoute.objects.filter(traveller=self.id):
route_start = travellerRoute.travel_start_date
route_end = travellerRoute.travel_end_date
if route_start is not None:
if new_start_date is None or new_start_date > route_start:
new_start_date = route_start
if route_end is not None:
if new_end_date is None or new_end_date < route_end:
new_end_date = route_end
self.travel_start_date = new_start_date
self.travel_end_date = new_end_date
self.is_active = self._is_active()
def fare_paying(self):
if self.eligibility_status != "2":
return
cost_setting = 0
if not cost_setting.exists():
return "TERM_TRAVEL_COST not configured"
cost = int(cost_setting.get().value)
stops = 0
for stop in TravellerRoute.objects.filter(traveller=self.id):
stops += stop.active_stops()
if stops > 1:
stops = 1
return f"${round(cost*stops)}"
def _repopulate_address(self):
families = self.get_families()
if families.count() == 0:
self.address = ""
elif families.count() == 1:
family = families.first()
self.address = f"{family.residential_address} {family.residential_suburb}"
else:
self.address = "Multiple"
def get_families(self):
return Family.objects.filter(traveller__id__exact=self.id)
class Family(models.Model):
RELATIONS = [
("1", "Parent"),
("2", "Step-Parent"),
("3", "Foster Parent"),
("4", "Host Family"),
("5", "Sibling"),
("6", "Grandparent"),
("7", "Aunt/Uncle"),
("8", "Cousin"),
("9", "Carer"),
("10", "Case Worker"),
("11", "Friend/Other"),
]
traveller = models.ForeignKey(Traveller, on_delete=models.CASCADE)
residential_address = models.CharField(max_length=50, blank=True)
residential_suburb = models.ForeignKey(Suburb, on_delete=models.PROTECT, blank=True, null=True,
related_name='family_residential_suburb')
postal_address = models.CharField(max_length=50, blank=True)
postal_suburb = models.ForeignKey(Suburb, on_delete=models.PROTECT, blank=True, null=True,
related_name='family_postal_suburb')
parent_A_firstname = models.CharField(max_length=50, blank=True)
parent_A_lastname = models.CharField(max_length=50, blank=True)
parent_A_phone = models.CharField(max_length=15, blank=True)
parent_A_email = models.CharField(max_length=50, blank=True)
parent_B_firstname = models.CharField(max_length=50, blank=True)
parent_B_lastname = models.CharField(max_length=50, blank=True)
parent_B_phone = models.CharField(max_length=15, blank=True)
parent_B_email = models.CharField(max_length=50, blank=True)
emergency_contact_A_firstname = models.CharField(max_length=50, blank=True)
emergency_contact_A_lastname = models.CharField(max_length=50, blank=True)
emergency_contact_A_phone = models.CharField(max_length=15, blank=True)
emergency_contact_A_relation = models.CharField(max_length=50, choices=RELATIONS, blank=True)
emergency_contact_B_firstname = models.CharField(max_length=50, blank=True)
emergency_contact_B_lastname = models.CharField(max_length=50, blank=True)
emergency_contact_B_phone = models.CharField(max_length=15, blank=True)
emergency_contact_B_relation = models.CharField(max_length=50, choices=RELATIONS, blank=True)
created_on = models.DateTimeField(auto_now_add=True, blank=True, null=True)
last_edit = models.DateTimeField(auto_now=True, blank=True, null=True)
class Meta:
verbose_name_plural = "Families"
def __str__(self):
return self.parent_names()
def clean(self):
valid_numbers, failed_numbers = self.get_parsed_numbers(True, True)
if len(failed_numbers) > 0:
raise ValidationError(f"Phone number {failed_numbers[0]} not valid")
def parent_names(self):
a_name = self.parent_A_firstname
b_name = self.parent_B_firstname
if a_name:
if b_name:
return f"{a_name} and {b_name}"
return a_name
elif b_name:
return b_name
return ""
def get_parsed_numbers(self, parents=False, emergency=False):
numbers = []
if parents:
if self.parent_A_phone:
numbers.append(self.parent_A_phone)
if self.parent_B_phone:
numbers.append(self.parent_B_phone)
if emergency:
if self.emergency_contact_A_phone:
numbers.append(self.emergency_contact_A_phone)
if self.emergency_contact_B_phone:
numbers.append(self.emergency_contact_B_phone)
valid_numbers = []
failed_numbers = []
for number in numbers:
try:
num = phonenumbers.parse(number, "AU")
if phonenumbers.is_valid_number(num):
valid_numbers.append(f"+{num.country_code}{num.national_number}")
else:
failed_numbers.append(number)
except:
failed_numbers.append(number)
return valid_numbers, failed_numbers
class TravellerRoute(models.Model):
traveller = models.ForeignKey(Traveller, on_delete=models.CASCADE)
busStop = models.ForeignKey("transport.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)
tue_pm = models.BooleanField(default=True)
wen_am = models.BooleanField(default=True)
wen_pm = models.BooleanField(default=True)
thu_am = models.BooleanField(default=True)
thu_pm = models.BooleanField(default=True)
fri_am = models.BooleanField(default=True)
fri_pm = models.BooleanField(default=True)
notes = models.TextField(blank=True, verbose_name="Driver Notes")
created_on = models.DateTimeField(auto_now_add=True, blank=True, null=True)
last_edit = models.DateTimeField(auto_now=True, blank=True, null=True)
def __str__(self):
return f"{self.busStop}"
def save(self, *args, **kwargs):
super(TravellerRoute, self).save(*args, **kwargs)
self.traveller.save()
def active_stops(self):
stops = 0
if self.mon_am:
stops += 1
if self.mon_pm:
stops += 1
if self.tue_am:
stops += 1
if self.tue_pm:
stops += 1
if self.wen_am:
stops += 1
if self.wen_pm:
stops += 1
if self.thu_am:
stops += 1
if self.thu_pm:
stops += 1
if self.fri_am:
stops += 1
if self.fri_pm:
stops += 1
return stops * 0.1