331 lines
12 KiB
Python
331 lines
12 KiB
Python
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)
|
|
|
|
class Meta:
|
|
ordering = ["school__name"]
|
|
|
|
def __str__(self):
|
|
return f"{self.school.shortName} <-> {self.bus.route_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"]
|
|
|
|
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_end_date:
|
|
return True
|
|
return datetime(self.travel_end_date.year, self.travel_end_date.month,
|
|
self.travel_end_date.day) < 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
|