d74290036e
Cleaned up logout redirects Added suburbs admin page back. Wasn't able to create new suburbs Made active status check to any traveller save function
367 lines
13 KiB
Python
367 lines
13 KiB
Python
from datetime import datetime
|
|
|
|
import phonenumbers
|
|
from django.db import models
|
|
|
|
|
|
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)
|
|
# transfer_school = models.ForeignKey(School, related_name='transfer_school', on_delete=models.CASCADE)
|
|
am_service = models.BooleanField(default=True)
|
|
pm_service = models.BooleanField(default=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):
|
|
return Traveller.objects.filter(shuttle=self, is_active=True).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")
|
|
is_active = models.BooleanField(default=False, verbose_name='Active')
|
|
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 save(self, *args, **kwargs):
|
|
self._update_active_status()
|
|
super(Traveller, self).save(*args, **kwargs)
|
|
|
|
def _is_active(self):
|
|
today = datetime.today()
|
|
today = 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) > 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 >= today
|
|
|
|
def _update_active_status(self):
|
|
new_start_date = None
|
|
new_end_date = None
|
|
|
|
for travellerRoute in TravellerRoute.objects.filter(traveller=self):
|
|
route_start = travellerRoute.travel_start_date
|
|
print(route_start)
|
|
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 = 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)
|
|
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")
|
|
|
|
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
|