from datetime import datetime import phonenumbers from django.conf import settings from django.db import models from twilio.rest import Client class Setting(models.Model): name = models.CharField(max_length=20, unique=True) value = models.CharField(max_length=20, blank=True) def __str__(self): return self.name class Suburb(models.Model): STATE = [ ("VIC", "Victoria"), ("NSW", "New South Wales"), ("SA", "South Australia"), ("ACT", "Australia Capital Territory"), ("QLD", "Queensland"), ("NT", "Northern Territory"), ("WA", "Western Australia"), ("TAS", "Tasmania"), ] name = models.CharField(max_length=30, unique=True) state = models.CharField(max_length=3, choices=STATE) postcode = models.PositiveSmallIntegerField() distance = models.PositiveSmallIntegerField(blank=True, null=True) class Meta: ordering = ["name"] def __str__(self): return f"{self.name}, {self.state} {self.postcode}" 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 Company(models.Model): name = models.CharField(max_length=50, unique=True) contact_name = models.CharField(max_length=50, blank=True) contact_number = models.CharField(max_length=15, blank=True) contact_mobile = models.CharField(max_length=15, blank=True) contact_email = models.CharField(max_length=50, blank=True) address = models.CharField(max_length=50, blank=True) suburb = models.ForeignKey(Suburb, on_delete=models.CASCADE) notes = models.TextField(blank=True) class Meta: verbose_name_plural = "Companies" def __str__(self): return f"{self.name}" class Bus(models.Model): company = models.ForeignKey(Company, on_delete=models.CASCADE) route_name = models.CharField(max_length=50, unique=True) contract_number = models.CharField(max_length=20, blank=True) registration = models.CharField(max_length=10, blank=True) seating_capacity = models.SmallIntegerField() make = models.CharField(max_length=15, blank=True) model = models.CharField(max_length=15, blank=True) notes = models.TextField(blank=True) class Meta: verbose_name_plural = "Buses" ordering = ["route_name"] def __str__(self): return f"{self.route_name}" def traveller_count(self): count = 0 for traveller in Traveller.objects.filter(bus_stops__bus=self): if traveller.is_active(): count += 1 return count class Shuttle(models.Model): bus = models.ForeignKey(Bus, on_delete=models.CASCADE) school = models.ForeignKey(School, on_delete=models.CASCADE) custom_name = models.CharField(max_length=10, blank=True) class Meta: ordering = ["school__name"] def __str__(self): custom_name = self.custom_name if custom_name: custom_name = f" ({self.custom_name})" else: custom_name = "" return f"{self.school.shortName} <-> {self.bus.route_name}{custom_name}" def traveller_count(self): count = 0 for traveller in Traveller.objects.filter(shuttle=self): if traveller.is_active(): count += 1 return count class Driver(models.Model): bus = models.ForeignKey(Bus, on_delete=models.CASCADE) first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) phone_number = models.CharField(max_length=15, blank=True) class Meta: ordering = ["last_name"] def __str__(self): return f"{self.first_name} {self.last_name}" class BusStop(models.Model): bus = models.ForeignKey(Bus, on_delete=models.CASCADE) am_time = models.TimeField() pm_time = models.TimeField() address = models.CharField(max_length=100) notes = models.TextField(blank=True) class Meta: ordering = ["bus__route_name", "am_time"] def get_stop_number(self): return BusStop.objects.filter(bus=self.bus, am_time__lt=self.am_time).count() + 1 def __str__(self): return f"{self.bus.route_name} #{self.get_stop_number()} - {self.address}" 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", "Fare Payer"), ("3", "<4.8 Exemption"), ("4", "Eligible waitlisted"), ("5", "Ineligible waitlisted"), ("6", "Kinder Exemption"), ("7", "Tafe/Post Secondary Exemption"), ("8", "Other Exemption"), ] 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"), ] 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(BusStop, through='TravellerRoute', blank=True) distance_to_school = models.PositiveSmallIntegerField(blank=True, null=True) residential_address = models.CharField(max_length=50, blank=True) residential_suburb = models.ForeignKey(Suburb, on_delete=models.PROTECT, blank=True, null=True, related_name='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='postal_suburb') 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) 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) is_archived = models.BooleanField(default=False, verbose_name="Archived") notes = models.TextField(blank=True, verbose_name='Admin Notes') shuttle = models.ForeignKey(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): if self.is_archived: return False if not self.travel_start_date: return False if datetime(self.travel_start_date.year, self.travel_start_date.month, self.travel_start_date.day) > datetime.today(): 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 > datetime.today() def fare_paying(self): if self.eligibility_status != "2": return cost_setting = Setting.objects.filter(name="TERM_TRAVEL_COST") 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"${str(cost * stops)}" def send_sms(self, message, parents=False, emergency=False): numbers = [] if parents and self.parent_A_phone: numbers.append(self.parent_A_phone) if parents and self.parent_B_phone: numbers.append(self.parent_B_phone) if emergency and self.emergency_contact_A_phone: numbers.append(self.emergency_contact_A_phone) if emergency and self.emergency_contact_B_phone: numbers.append(self.emergency_contact_B_phone) count = 0 for number in numbers: num = phonenumbers.parse(number, "AU") if phonenumbers.is_valid_number(num): count += 1 # num = f"+{num.country_code}{num.national_number}" # client = Client(settings.TWILIO['ACCOUNT_SID'], settings.TWILIO['AUTH_TOKEN']) # client.messages.create(num, from_=settings.TWILIO['SENDER'], body=message) return count class TravellerRoute(models.Model): traveller = models.ForeignKey(Traveller, on_delete=models.CASCADE) busStop = models.ForeignKey(BusStop, on_delete=models.CASCADE) 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") def __str__(self): return f"{self.busStop}" 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