cmsmanage/paperwork/models.py

312 lines
10 KiB
Python

import datetime
import re
from semver import VersionInfo
from django.db import models
from django.db.models import OuterRef, Q, ExpressionWrapper, Subquery
from django.conf import settings
from django.core.validators import RegexValidator
from membershipworks.models import Member, Flag as MembershipWorksFlag
class AbstractAudit(models.Model):
date = models.DateField(default=datetime.date.today, unique=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
good = models.BooleanField(default=False)
notes = models.CharField(max_length=255, blank=True)
def __str__(self):
return f"{'Good' if self.good else 'Bad'} audit at {self.date} by {self.user}"
class Meta:
abstract = True
ordering = ["date"]
get_latest_by = ["date"]
class CmsRedRiverVeteransScholarship(models.Model):
serial = models.AutoField(primary_key=True)
program_name = models.CharField(db_column="Program Name", max_length=255)
member_name = models.CharField(
db_column="Member Name", max_length=255, blank=True, null=True
)
discount_percent = models.DecimalField(
db_column="Discount Percent",
max_digits=16,
decimal_places=0,
blank=True,
null=True,
)
discount_code = models.CharField(
db_column="Discount Code", max_length=255, blank=True, null=True
)
membership_code = models.CharField(
db_column="Membership Code", max_length=255, blank=True, null=True
)
start_date = models.DateField(db_column="Start Date", blank=True, null=True)
end_date = models.DateField(db_column="End Date", blank=True, null=True)
program_amount = models.DecimalField(
db_column="Program Amount",
max_digits=16,
decimal_places=0,
blank=True,
null=True,
)
program_status = models.CharField(
db_column="Program Status", max_length=16, blank=True, null=True
)
def __str__(self):
return f"{self.program_name} {self.member_name}"
class Meta:
db_table = "CMS Red River Veterans Scholarship"
class Department(models.Model):
name = models.CharField(
max_length=64,
validators=[RegexValidator("^[-_ A-Za-z0-9]*$")],
help_text="This will also be used to generate the mailing list name",
)
parent = models.ForeignKey(
"self", on_delete=models.PROTECT, related_name="children", blank=True, null=True
)
has_mailing_list = models.BooleanField(default=False)
shop_lead_flag = models.ForeignKey(
MembershipWorksFlag,
on_delete=models.PROTECT,
blank=True,
null=True,
db_constraint=False,
help_text="This will also be used to set the moderators for the mailing list",
)
list_reply_to_address = models.EmailField(max_length=254, blank=True)
def __str__(self):
return self.name
@property
def list_name(self):
if self.has_mailing_list:
return self.name.replace(" ", "_") + "-info"
else:
return None
@property
def list_address(self):
if self.has_mailing_list:
return self.list_name + "@claremontmakerspace.org"
else:
return None
class CertificationDefinition(models.Model):
certification_identifier = models.AutoField(
db_column="Certification Identifier", primary_key=True
)
certification_name = models.CharField(
db_column="Certification Name", max_length=255, blank=True, null=True
)
department = models.ForeignKey(Department, models.PROTECT)
def __str__(self):
return f"{self.certification_name} <{self.department}>"
class Meta:
db_table = "Certification Definitions"
ordering = ("certification_name", "department")
def latest_version(self) -> "CertificationVersion":
return self.certificationversion_set.latest()
class CertificationVersionManager(models.Manager):
def get_queryset(self):
qs = super().get_queryset()
latest = qs.filter(definition__pk=OuterRef("definition__pk")).reverse()
return qs.annotate(
is_latest=ExpressionWrapper(
Q(pk=Subquery(latest.values("pk")[:1])),
output_field=models.BooleanField(),
),
# TODO: should do a more correct comparison than just major version
is_current=ExpressionWrapper(
Q(major=Subquery(latest.values("major")[:1])),
output_field=models.BooleanField(),
),
)
class CertificationVersion(models.Model):
objects = CertificationVersionManager()
definition = models.ForeignKey(
CertificationDefinition, on_delete=models.PROTECT, db_column="Certification"
)
major = models.PositiveSmallIntegerField()
minor = models.PositiveSmallIntegerField()
patch = models.PositiveSmallIntegerField()
prerelease = models.CharField(max_length=255, blank=True)
approval_date = models.DateField(blank=True, null=True)
def __str__(self):
return f"{self.definition} [{self.semantic_version()}]"
class Meta:
constraints = [
models.UniqueConstraint(
fields=["definition", "major", "minor", "patch", "prerelease"],
name="unique_certification_version",
)
]
ordering = (
"definition",
"major",
"minor",
"patch",
"prerelease",
"approval_date",
)
get_latest_by = ("major", "minor", "patch", "prerelease", "approval_date")
base_manager_name = "objects"
def semantic_version(self) -> VersionInfo:
return VersionInfo(
self.major or 0,
self.minor or 0,
self.patch or 0,
re.sub(r"[^.a-zA-Z0-9]", "-", self.prerelease),
self.approval_date.isoformat() if self.approval_date is not None else None,
)
class Certification(models.Model):
number = models.AutoField(db_column="Number", primary_key=True)
certification_version = models.ForeignKey(
CertificationVersion, on_delete=models.PROTECT
)
name = models.CharField(db_column="Name", max_length=255)
member = models.ForeignKey(
Member,
on_delete=models.PROTECT,
to_field="uid",
db_column="uid",
blank=True,
null=True,
db_constraint=False,
)
certified_by = models.CharField(
db_column="Certified_By", max_length=255, blank=True, null=True
)
date = models.DateField(db_column="Date", blank=True, null=True)
shop_lead_notified = models.DateTimeField(
db_column="Shop Lead Notified", blank=True, null=True
)
notes = models.CharField(db_column="Notes", max_length=255, blank=True, null=True)
def __str__(self):
return f"{self.name} - {self.certification_version}"
class Meta:
db_table = "Certifications"
permissions = [
(
"receive_certification_emails",
"Receives notifications of all new certifications",
),
]
class CertificationAudit(AbstractAudit):
certification = models.ForeignKey(
Certification, on_delete=models.CASCADE, related_name="audits"
)
class InstructorOrVendor(models.Model):
serial = models.AutoField(primary_key=True)
name = models.CharField(db_column="Name", max_length=255)
instructor_agreement_date = models.DateField(
db_column="Instructor Agreement Date", blank=True, null=True
)
w9_date = models.DateField(db_column="W9 date", blank=True, null=True)
phone = models.CharField(max_length=255, blank=True, null=True)
email_address = models.CharField(
db_column="email address", max_length=255, blank=True, null=True
)
def __str__(self):
return f"{self.name}"
class Meta:
db_table = "Instructors and Vendors"
class SpecialProgram(models.Model):
program_name = models.CharField(
db_column="Program Name", primary_key=True, max_length=255
)
discount_percent = models.DecimalField(
db_column="Discount Percent",
max_digits=16,
decimal_places=0,
blank=True,
null=True,
)
discount_code = models.CharField(
db_column="Discount Code", max_length=255, blank=True, null=True
)
membership_code = models.CharField(
db_column="Membership Code", max_length=255, blank=True, null=True
)
start_date = models.DateField(db_column="Start Date", blank=True, null=True)
end_date = models.DateField(db_column="End Date", blank=True, null=True)
program_amount = models.DecimalField(
db_column="Program Amount",
max_digits=16,
decimal_places=0,
blank=True,
null=True,
)
program_status = models.CharField(
db_column="Program Status", max_length=16, blank=True, null=True
)
def __str__(self):
return self.program_name
class Meta:
db_table = "Special_Programs"
class Waiver(models.Model):
number = models.AutoField(db_column="Number", primary_key=True)
name = models.CharField(db_column="Name", max_length=255)
date = models.DateField(db_column="Date")
emergency_contact_name = models.CharField(
db_column="Emergency Contact Name", max_length=255, blank=True, null=True
)
emergency_contact_number = models.CharField(
db_column="Emergency Contact Number", max_length=25, blank=True, null=True
)
waiver_version = models.CharField(db_column="Waiver version", max_length=64)
guardian_name = models.CharField(
db_column="Guardian Name", max_length=255, blank=True, null=True
)
guardian_relation = models.CharField(
db_column="Guardian Relation", max_length=255, blank=True, null=True
)
guardian_date = models.DateField(db_column="Guardian Date", blank=True, null=True)
def __str__(self):
return f"{self.name} {self.date}"
class Meta:
db_table = "Waivers"
class WaiverAudit(AbstractAudit):
waiver = models.ForeignKey(Waiver, on_delete=models.CASCADE, related_name="audits")