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