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 _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 recalculate_travel_dates(self): routes = TravellerRoute.objects.filter(traveller=self) new_start = None new_end = None for route in routes: if route.travel_start_date: if new_start is None or route.travel_start_date < new_start: new_start = route.travel_start_date if route.travel_end_date: if new_end is None or route.travel_end_date > new_end: new_end = route.travel_end_date self.travel_start_date = new_start self.travel_end_date = new_end 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 update_address_from_families(self): addresses = { str(f.location) for f in self.get_families() if f.location } self.address = " | ".join(sorted(addresses)) if addresses else "" 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 = "" b_name = "" if self.contact_A: a_name = self.contact_A.first_name if self.contact_B: 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