4025c28eae
Moved contacts to new model
327 lines
13 KiB
Python
327 lines
13 KiB
Python
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
|