Files
bus-manager/busManager/traveller/models.py
T
st01765 4025c28eae Added locations app
Moved contacts to new model
2026-02-26 13:34:32 +11:00

327 lines
13 KiB
Python

from datetime import datetime
from django.core.exceptions import ValidationError
from django.db import models
from common.models import Suburb
from locations.models import Location
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 ContactPerson(models.Model):
first_name = models.CharField(max_length=100, blank=True)
last_name = models.CharField(max_length=100, blank=True)
phone = models.CharField(max_length=15, blank=True)
email = models.EmailField(blank=True)
class Meta:
indexes = [
models.Index(fields=["phone"]),
models.Index(fields=["last_name"]),
]
def __str__(self):
return f"{self.first_name} {self.last_name}"
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)
location = models.ForeignKey(Location, on_delete=models.PROTECT, blank=True, null=True)
postal_location = models.ForeignKey(Location, on_delete=models.PROTECT, null=True, blank=True,
related_name="family_postal_locations")
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)
contact_A_relation = models.CharField(max_length=50, choices=RELATIONS, blank=True)
contact_A = models.ForeignKey(ContactPerson, on_delete=models.SET_NULL, blank=True, null=True, related_name='family_contact_A')
contact_B_relation = models.CharField(max_length=50, choices=RELATIONS, blank=True)
contact_B = models.ForeignKey(ContactPerson, on_delete=models.SET_NULL, blank=True, null=True, related_name='family_contact_B')
emergency_contact_A = models.ForeignKey(ContactPerson, on_delete=models.SET_NULL, blank=True, null=True, related_name='family_emergency_contact_A')
emergency_contact_B = models.ForeignKey(ContactPerson, on_delete=models.SET_NULL, blank=True, null=True, related_name='family_emergency_contact_B')
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"
indexes = [
models.Index(fields=["contact_A"]),
models.Index(fields=["contact_B"]),
models.Index(fields=["emergency_contact_A"]),
models.Index(fields=["emergency_contact_B"]),
]
def __str__(self):
name = self.parent_names()
if name:
return f"{self.traveller}: {name}"
return str(self.traveller)
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.contact_A.first_name
b_name = self.contact_B.first_name
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):
import phonenumbers
numbers = []
if parents:
for contact in [self.contact_A, self.contact_B]:
if contact and contact.phone:
numbers.append(contact.phone)
if emergency:
for contact in [self.emergency_contact_A, self.emergency_contact_B]:
if contact and contact.phone:
numbers.append(contact.phone)
valid_numbers = []
failed_numbers = []
for number in numbers:
try:
parsed = phonenumbers.parse(number, "AU")
if phonenumbers.is_valid_number(parsed):
valid_numbers.append(f"+{parsed.country_code}{parsed.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