class Component(models.Model): name = models.CharField(max_length=64) category = models.ForeignKey(ComponentCategory, db_constraint=False, on_delete=models.DO_NOTHING) uid = models.CharField(max_length=64, null=False, default="") #uuid.uuid4().hex) unit = mysql_models.EnumField( choices=['in', 'mm', 'oz', 'ml', 'hours', 'each'], default='each') thumbnail = models.ImageField(blank=True, null=True) unit_price = models.DecimalField(decimal_places=4, max_digits=10) in_stock = models.IntegerField(default=0) unit_threshold = models.IntegerField(default=0) vendor_url = models.URLField(blank=True, null=True) active = models.BooleanField(default=True) def __str__(self): return self.name def inactive(self, page=0): return Paginator.page(Component.objects.filter(active=False), page) def low(self, page=0): return Paginator.page( Component.objects.filter(active=True, in_stock__lte='unit_threshold')) def shortfall(self): if (self.unit_threshold > 0): return abs(self.in_stock - self.unit_threshold) else: return 0 shortfall.admin_order_field = "shortfall"
class ProductionQueue(models.Model): product_option = models.ForeignKey(ProductOption, on_delete=models.CASCADE) units = models.IntegerField(default=0) status = mysql_models.EnumField( choices=['Pending', 'In Progress', 'On Hold'], default="Pending") def __str__(self): return self.product_option.__str__()
class Greencheck(mysql_models.Model): # NOTE: ideally we would have these two as Foreign keys, as the greencheck # table links back to where the recorded ip ranges we checked against are. # However, some `GreencheckIP` ip range objects have been deleted over # the years. # Also, # We might be better off with a special 'DELETED' Greencheck IP # to at least track this properly. hostingprovider = models.IntegerField(db_column="id_hp", default=0) # hostingprovider = models.ForeignKey( # ac_models.Hostingprovider, # db_column="id_hp", # on_delete=models.CASCADE, # blank=True, # null=True, # ) greencheck_ip = models.IntegerField(db_column="id_greencheck", default=0) # greencheck_ip = models.ForeignKey( # GreencheckIp, # on_delete=models.CASCADE, # db_column="id_greencheck", # blank=True, # null=True, # ) date = models.DateTimeField(db_column="datum") green = dj_mysql_models.EnumField(choices=gc_choices.BoolChoice.choices) ip = IpAddressField() tld = models.CharField(max_length=64) type = dj_mysql_models.EnumField( choices=gc_choices.GreenlistChoice.choices, default=gc_choices.GreenlistChoice.NONE, ) url = models.CharField(max_length=255) class Meta: db_table = "greencheck_2021" def __str__(self): return f"{self.url} - {self.ip}"
class GreenList(models.Model): greencheck = models.ForeignKey(Greencheck, on_delete=models.CASCADE, db_column="id_greencheck") hostingprovider = models.ForeignKey(ac_models.Hostingprovider, on_delete=models.CASCADE, db_column="id_hp") last_checked = models.DateTimeField() name = models.CharField(max_length=255, db_column="naam") type = dj_mysql_models.EnumField(choices=gc_choices.ActionChoice.choices) url = models.CharField(max_length=255) website = models.CharField(max_length=255) class Meta: # managed = False db_table = "greenlist" indexes = [ models.Index(fields=["url"], name="url"), ]
class Parent(models.Model): hid = models.CharField(max_length=7, null=True) first = models.CharField(max_length=20) family_id = models.PositiveIntegerField() alt_last = models.CharField(default='', max_length=30) sex_choices = sex_choices sex = models.CharField(max_length=1, choices=sex_choices) alt_phone = custom.PhoneNumberField(null=True) phone_type = sqlmod.EnumField(choices=['', 'Home', 'Cell', 'Work'], default='') alt_email = models.EmailField(default='') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) rest_model = "parent" objects = Parents def last(self): return self.alt_last if self.alt_last else self.family.last if self.family else "_____" def phone(self): return self.alt_phone if self.alt_phone > 0 else self.family.phone if self.family else 0 def unique_phone(self): if self.alt_phone.value != self.family.phone.value: return self.alt_phone def email(self): return self.alt_email if self.alt_email else self.family.email if self.family else "_____" def unique_email(self): if self.alt_email != self.family.email: if self.sex == 'M' or (self.family.father and self.alt_email != self.family.father.alt_email): return self.alt_email def family(self): return Families.fetch(id=self.family_id) def emoji(self): return "👨" if self.sex == 'M' else "👩" def stand(self, me): if me.owner_type == 'Family': family = me.owner elif me.owner_type == 'Student': family = me.owner.family else: family = None return self.family.id == family.id def __getattribute__(self, field): if field in ['last', 'phone', 'email', 'family', 'emoji']: call = super(Parent, self).__getattribute__(field) return call() else: return super(Parent, self).__getattribute__(field) def __str__(self): if self.sex == 'M': prefix = 'Mr. ' elif self.sex == 'F': prefix = 'Mrs. ' return prefix + self.first + ' ' + self.last
class Family(models.Model): hid = models.CharField(max_length=10, null=True) oid = models.PositiveIntegerField(default=0) last = models.CharField(max_length=30) name_num = models.PositiveIntegerField(default=0) phone = custom.PhoneNumberField() phone_type = sqlmod.EnumField(choices=['', 'Home', 'Cell', 'Work'], default='') email = models.EmailField() mother = models.ForeignKey(Parent, null=True, related_name='mother') father = models.ForeignKey(Parent, null=True, related_name='father') address = models.OneToOneField(Address, null=True, primary_key=False, rel=True) policyYear = models.DecimalField(max_digits=4, decimal_places=0, null=True) policyPage = models.PositiveIntegerField(default=0) policyDate = models.DateTimeField(null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) rest_model = "family" objects = Families def stand(self, me): if me.owner_type == 'Family': family = me.owner elif me.owner_type == 'Student': family = me.owner.family else: family = None return self.id == family.id def has_accepted_policy(self, year=getyear()): policy = Policies.fetch(year=year) return not policy or self.policyYear == year and self.policyPage == policy.nPages def unique_last(self): return '{} #{}'.format(self.last, self.name_num) if self.name_num else self.last def unique_last_in(self, year): clashes = Families.filter( last=self.last, children__enrollment__course__year=year).exclude(id=self.id) if clashes: return self.unique_last else: return self.last def emoji(self): people = [] if self.father: people.append("👨") if self.mother: people.append("👩") if not people: people = ["👨", "👩"] nGirls = len(self.children.filter(sex='F')) nBoys = len(self.children.filter(sex='M')) if nGirls: people.append("👧") if nBoys: people.append("👦") if nGirls > 1 and nBoys == 0: people.append("👧") if nBoys > 1 and nGirls == 0: people.append("👦") return "‍".join(people) def accounts(self): return Users.filter(owner_type='Family', owner_id=self.id) def invoices(self): return Invoices.filter(family=self) # def children(self): # return Students.filter(family_id=self.id).order_by('birthday') def children_eligible_in(self, year): ids = [] for child in self.children.all(): if set(Each(list(child.course_menu())).status) - set(['not_elig']): ids.append(child.id) return Students.filter(id__in=ids) def children_enrolled_in(self, year): return self.children.filter(enrollment__course__year=year).distinct() def all_enrollments_in(self, year): return Enrollments.filter(student__family=self, course__year=year) def all_enrollments(self): return self.all_enrollments_in(getyear()) def enrollments_in(self, year): qset = self.all_enrollments_in(year) qset = qset.exclude(status__in=['nonexist', 'aud_fail', 'aud_drop']) qset = qset.order_by('created_at') return qset def enrollments(self): return self.enrollments_in(getyear()) def live_enrollments_in(self, year): qset = self.all_enrollments_in(year) qset = qset.filter(status__in=[ 'enrolled', 'invoiced', 'need_pay', 'aud_pend', 'pendpass', 'pendfail', 'pend_pub', 'fail_pub', 'aud_pass', 'aud_drop', 'aud_lock', 'maydefer', 'deferred' ]) qset = qset.order_by('created_at') return qset def live_enrollments(self): return self.live_enrollments_in(getyear()) def total_tuition_in(self, year): return sum(Each(self.enrollments_in(year)).course.tuition) def paid_tuition_in(self, year): return sum(Each(Invoices.filter(family=self, status='P')).amount) # return sum(collect(self.enrollments_in(year), lambda enr: 0 if enr.isAudition else enr.course.tuition())) def pend_tuition_in(self, year): return sum(Each(self.pend_enrollments_in(year)).course.tuition) # return sum(Each(Invoices.filter(family=self,status='N')).amount) + sum(Each(Enrollments.filter(student__family=self, course__year=year, isAudition=True, happened=False)).course).tuition) def unpaid_tuition_in(self, year): return self.total_tuition_in(year) - self.pend_tuition_in( year) - self.paid_tuition_in(year) def volunteer_total_in(self, year): return max([0.0] + list( collect(self.enrollments_in(year), lambda enr: enr.course.vol_hours))) def hours_worked_in(self, year): return 0.0 def hours_signed_in(self, year): return 0.0 def fate(self, year=None): if not year: year = getyear() Each(self.children.all()).fate(year) def delete(self): # Manual cascading for Parents and Users safe_delete(self.mother) safe_delete(self.father) safe_delete(self.address) Users.filter(owner=self).delete() return super(Family, self).delete() def clear_name_num(self): self.name_num = 0 self.save() def update_name_num(self, fix_all=False): clashes = Families.filter(last=self.last).exclude(id=self.id) older = clashes.filter(created_at__lt=self.created_at) if fix_all: Each(older).update_name_num() if older: self.name_num = max(Each(older).name_num) + 1 else: self.name_num = int(bool(clashes)) self.save() if fix_all: newer = clashes.filter(created_at__lt=self.created_at) Each(newer).update_name_num() return self.name_num def __str__(self): # return ('{} Family #{}' if self.name_num else '{} Family').format(self.last,self.name_num) return '{} Family'.format(self.last) def __getattribute__(self, field): if field in [ 'unique_last', 'enrollments', 'hours_worked', 'accounts', 'invoices', 'emoji' ]: call = super(Family, self).__getattribute__(field) return call() else: return super(Family, self).__getattribute__(field)
class CourseTrad(models.Model): # General: id = models.CharField(max_length=2, primary_key=True) oid = models.CharField(max_length=10,default='') title = models.CharField(max_length=50) abbr = models.CharField(max_length=13) alias = models.ForeignKey('self', null=True) order = models.FloatField(null=True) e = models.BooleanField(default=True) # Whether course appears in Shopping Cart m = models.BooleanField(default=True) # Whether course appears in Course Menu r = models.BooleanField(default=True) # Whether Courses can inherit from this CourseTrad # Commitment: day = custom.DayOfWeekField(default='') start = models.TimeField(default="00:00:00") end = models.TimeField(default="00:00:00") nMeets = models.PositiveIntegerField(default=0) place = models.ForeignKey(Venue, null=True) show = models.CharField(max_length=2, default="") sa = models.BooleanField(default=False) semester_choices = [ ('N','Neither'), ('F','Fall'), ('S','Spring'), ('B','Both'), ] semester = models.CharField(max_length=1,default='N') # Prerequisites nSlots = models.PositiveIntegerField(default=0) min_age = models.PositiveIntegerField(default=9) max_age = models.PositiveIntegerField(default=18) min_grd = models.PositiveIntegerField(default=1) max_grd = models.PositiveIntegerField(default=12) eligex = models.TextField(default="#") default = models.CharField(max_length=8,choices=status_choices) # Cost early_tuit = models.DecimalField(max_digits=6, decimal_places=2, default=0) after_tuit = models.DecimalField(max_digits=6, decimal_places=2, default=0) vol_hours = models.FloatField(default=0) the_hours = models.FloatField(default=0) # Behavior action_choices = ['none','trig','casc','stat'] action = sqlmod.EnumField(choices=action_choices, default='none') deferrable = models.BooleanField(default=False) # Whether course may be paid for in October droppable = models.BooleanField(default=True) # Whether course may be dropped AFTER a successful audition # Meta created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) rest_model = "coursetrad" emoji = "🎩" objects = CourseTrads def stand(self, me): if me.owner_type == 'Family': return bool(self.courses.filter(enrollment__student__family=me.owner)) elif me.owner_type == 'Student': return bool(self.courses.filter(enrollment__student=me.owner)) genre_codes = { 'A':'Acting', 'B':'Ballet', # Historical 'C':'Choir', 'D':'DanceIntensive', # Historical # 'E':'', 'F':'Finale', 'G':'GeneralAudition', 'H':'HipHop', 'I':'Irish', 'J':'Jazz', 'K':'PrepaidTickets', # Admin 'L':'SignLanguage', # Historical 'M':'Makeup', # 'N':'', 'O':'Overture', # Historical 'P':'Tap', # Broadway (Older Beginners) # 'Q':'', 'R':'Statistical', 'S':'Troupe', 'T':'Tap', # 'U':'', # 'V':'', 'W':'Workshop', 'X':'Tech', # 'Y':'', 'Z':'Jazz', # Broadway (Older Beginners) } def genre(self): code = self.id[0] if code in self.genre_codes: return self.genre_codes[code] def subid(self): return sub(self.id, { 'SB':'TT', 'SG':'GB', 'SJ':'JR', 'P1':'BT1', 'P2':'BT2', 'Z1':'BJ1', 'Z2':'BJ2', }) def display_semester(self): sem = self.semester return ' ({})'.format(sem) if sem in 'FS' else '' def byFamily(self): return '+' in self.eligex def courses(self): return Courses.filter(tradition_id=self.id) def make(self, year): course = Courses.fetch(tradition=self, year=year) if course: return course else: return Courses.create(tradition=self, year=year) def find(self, year, all_students=None): course = Courses.fetch(tradition=self, year=year) def __str__(self): return self.title.upper() def __getattribute__(self, field): if field in ['courses','genre','subid','display_semester']: function = super(CourseTrad, self).__getattribute__(field) return function() else: return super(CourseTrad, self).__getattribute__(field) def eligible(self, student, year): if not student.family.has_accepted_policy(year): return False course = Courses.fetch(tradition=self, year=year) if course: return course.eligible(student) else: return self.check_eligex(student, year)
class Enrollment(models.Model): student = models.ForeignKey('people.Student', related_name='enrollment') course = models.ForeignKey(Course, related_name='enrollment') invoice = models.ForeignKey('payment.Invoice', null=True) tuition = models.DecimalField(max_digits=6, decimal_places=2, default=0) role = models.TextField(null=True) role_type = sqlmod.EnumField(choices=['','Chorus','Support','Lead']) status_choices = status_choices status = models.CharField(max_length=8,choices=status_choices) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) rest_model = "enrollment" objects = Enrollments def title(self): display = self.get_status_display() kwargs = self.title_kwargs() # if self.course.tradition.byFamily(): # display = '{family} {proverb} recieving {course} {year}' return display.format(**kwargs) def title_kwargs(self): return { 'student' : self.student.family if self.course.tradition.byFamily() else self.student, 'family' : self.student.family, 'course' : self.course.title, 'year' : self.course.year, 'invoice' : self.invoice.id if self.invoice else 0, 'pronoun' : 'he' if self.student.sex == 'M' else 'she', 'proverb' : 'was' if self.course.year < getyear() else 'is', 'audskil' : 'audition' if self.course.id[2] in 'SC' else 'skill assessment', 'article' : 'an' if self.course.id[2] in 'SC' else 'a', } def public_status(self): return sub(self.status, {"pendpass":"******","pendfail":"aud_pend","aud_fail":"fail_pub"}) def public_title(self): real_status = self.status self.status = self.public_status title = self.title self.status = real_status return title def emoji(self): red = "❤️" orange = "🧡" yellow = "💛" green = "💚" blue = "💙" purple = "💜" black = "🖤" decor = "💟" ribbon = "💝" status_emoji = { "enrolled":green, "eligible":black, "invoiced":orange, "need_pay":yellow, "not_elig":black, "aud_need":black, "aud_pend":blue, "pendpass":blue, "pendfail":blue, "pend_pub":blue, "fail_pub":black, "aud_pass":yellow, "aud_fail":black, "aud_drop":red, "aud_lock":ribbon, "conflict":black, "need_cur":black, "needboth":black, "nonexist":black, "nopolicy":black, "clasfull":black, "maydefer":yellow, "deferred":purple, } if self.status in status_emoji: return status_emoji[self.status] else: return "" def stand(self, me): if me.owner_type == 'Family': return self.student.family.id == me.owner.id elif me.owner_type == 'Student': return self.student.id == me.owner.id def price(self): if self.tuition: return self.tuition elif self.status == "enrolled" and self.invoice: return self.course.tuition(self.invoice.updated_at) else: return self.course.tuition() def pay(self): if self.status == "invoiced": self.tuition = self.course.tuition() self.status = "enrolled" self.save() return self.tuition def accept(self, user): if self.status in ["aud_pend","pendpass","pendfail"]: if user.permission >= 5: # self.course.accept(self.student) self.status = "aud_pass" if self.course.tradition.droppable else "aud_lock" elif user.permission >= 4: self.status = "pendpass" self.save() self.student.trigger(self.course.year) def reject(self, user): if self.status in ["aud_pend","pendpass","pendfail"]: if user.permission >= 5: self.status = "aud_fail" elif user.permission >= 4: self.status = "pendfail" self.save() self.student.trigger(self.course.year) def fate(self): self.status = calc_status(self) self.save() if self.status in ["not_elig","aud_need","conflict","need_cur","needboth"]: self.delete() def drop(self): if self.status == "aud_pass" and self.course.tradition.droppable: self.status = "aud_drop" self.save() # self.student.family.fate(self.course.year) elif self.status == "need_pay": self.delete() elif self.status == "aud_pend": self.status = "aud_need" self.save() elif self.status == "invoiced": invoice = self.invoice self.delete() invoice.update_amount() # if self.course.tradition.action == 'trig': self.student.family.fate(self.course.year) def defer(self): if self.status == "maydefer": self.status = "deferred" self.save() # def cancel(self): # if self.status == "invoiced": # self.status = "nonexist" # self.save() def cancel(self): if self.status == "invoiced" and self.invoice: self.invoice.cancel() def __str__(self): return '{} in {}'.format(self.student, self.course) def __getattribute__(self, field): if field in ['paid','eligible','display_student','title','public_title','public_status','emoji']: call = super(Enrollment, self).__getattribute__(field) return call() elif field in Students.fields: return self.student.__getattribute__(field) elif field in Courses.fields: return self.course.__getattribute__(field) else: return super(Enrollment, self).__getattribute__(field)