Compare commits
No commits in common. "40eca22eb8332e4cc8b54ee5e8ea634464465126" and "b1d3f164591d25e1d19ecaa78020560bf79fa0b9" have entirely different histories.
40eca22eb8
...
b1d3f16459
@ -1,5 +1,4 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
@ -304,15 +303,8 @@ class Dev(NonCIBase):
|
|||||||
DEBUG = values.BooleanValue(True)
|
DEBUG = values.BooleanValue(True)
|
||||||
INTERNAL_IPS = ["127.0.0.1"]
|
INTERNAL_IPS = ["127.0.0.1"]
|
||||||
|
|
||||||
INSTALLED_APPS = NonCIBase.INSTALLED_APPS + ["django_extensions"]
|
INSTALLED_APPS = NonCIBase.INSTALLED_APPS + ["debug_toolbar", "django_extensions"]
|
||||||
|
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + Base.MIDDLEWARE
|
||||||
# bit of a hack to disable debug toolbar when running tests
|
|
||||||
if DEBUG and "test" not in sys.argv:
|
|
||||||
INSTALLED_APPS += ["debug_toolbar"]
|
|
||||||
MIDDLEWARE = NonCIBase.MIDDLEWARE + [
|
|
||||||
"debug_toolbar.middleware.DebugToolbarMiddleware",
|
|
||||||
]
|
|
||||||
|
|
||||||
EMAIL = values.EmailURLValue("smtp://localhost:1025") # for local `mailpit`
|
EMAIL = values.EmailURLValue("smtp://localhost:1025") # for local `mailpit`
|
||||||
|
|
||||||
SENDFILE_BACKEND = "django_sendfile.backends.development"
|
SENDFILE_BACKEND = "django_sendfile.backends.development"
|
||||||
|
@ -22,9 +22,6 @@ class Door(models.Model):
|
|||||||
help_text="Membershipworks field that grants members access to this door",
|
help_text="Membershipworks field that grants members access to this door",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def controller(self) -> DoorController:
|
def controller(self) -> DoorController:
|
||||||
return DoorController(
|
return DoorController(
|
||||||
@ -33,6 +30,9 @@ class Door(models.Model):
|
|||||||
settings.HID_DOOR_PASSWORD,
|
settings.HID_DOOR_PASSWORD,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def card_formats(self):
|
def card_formats(self):
|
||||||
return self.controller.get_cardFormats()
|
return self.controller.get_cardFormats()
|
||||||
@ -149,6 +149,8 @@ class HIDEventQuerySet(models.QuerySet):
|
|||||||
|
|
||||||
|
|
||||||
class HIDEvent(models.Model):
|
class HIDEvent(models.Model):
|
||||||
|
objects = HIDEventQuerySet.as_manager()
|
||||||
|
|
||||||
class EventType(models.IntegerChoices):
|
class EventType(models.IntegerChoices):
|
||||||
DENIED_ACCESS_CARD_NOT_FOUND = 1022, "Denied Access: Card Not Found"
|
DENIED_ACCESS_CARD_NOT_FOUND = 1022, "Denied Access: Card Not Found"
|
||||||
DENIED_ACCESS_ACCESS_PIN_NOT_FOUND = 1023, "Denied Access Access: PIN Not Found"
|
DENIED_ACCESS_ACCESS_PIN_NOT_FOUND = 1023, "Denied Access Access: PIN Not Found"
|
||||||
@ -222,20 +224,6 @@ class HIDEvent(models.Model):
|
|||||||
db_persist=False,
|
db_persist=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = HIDEventQuerySet.as_manager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
constraints = [
|
|
||||||
models.UniqueConstraint(
|
|
||||||
fields=["door", "timestamp", "event_type"], name="unique_hidevent"
|
|
||||||
)
|
|
||||||
]
|
|
||||||
db_table = "hidevent"
|
|
||||||
ordering = ("-timestamp",)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.door.name} {self.timestamp} - {self.description}"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_xml_attributes(cls, door: Door, attrib: dict[str, str]):
|
def from_xml_attributes(cls, door: Door, attrib: dict[str, str]):
|
||||||
field_lookup = {
|
field_lookup = {
|
||||||
@ -301,6 +289,9 @@ class HIDEvent(models.Model):
|
|||||||
|
|
||||||
return event_types.get(self.event_type, f"Unknown Event Type {self.event_type}")
|
return event_types.get(self.event_type, f"Unknown Event Type {self.event_type}")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.door.name} {self.timestamp} - {self.description}"
|
||||||
|
|
||||||
def decoded_card_number(self) -> str | None:
|
def decoded_card_number(self) -> str | None:
|
||||||
"""Requires annotations from `with_decoded_card_number`"""
|
"""Requires annotations from `with_decoded_card_number`"""
|
||||||
if self.raw_card_number is None:
|
if self.raw_card_number is None:
|
||||||
@ -312,3 +303,12 @@ class HIDEvent(models.Model):
|
|||||||
return "Invalid"
|
return "Invalid"
|
||||||
else:
|
else:
|
||||||
return "Not 26 bit card"
|
return "Not 26 bit card"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
constraints = [
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=["door", "timestamp", "event_type"], name="unique_hidevent"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
db_table = "hidevent"
|
||||||
|
ordering = ("-timestamp",)
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import dataclasses
|
import dataclasses
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
from django_q.tasks import async_task
|
from django_q.tasks import async_task
|
||||||
|
|
||||||
from cmsmanage.django_q2_helper import q_task_group
|
from cmsmanage.django_q2_helper import q_task_group
|
||||||
@ -65,17 +62,6 @@ class DoorMember:
|
|||||||
if getattr(member, attribute_rule.access_field)
|
if getattr(member, attribute_rule.access_field)
|
||||||
}
|
}
|
||||||
|
|
||||||
# grant instructors access for ~1 hour around their class times
|
|
||||||
if hasattr(member, "eventinstructor") and getattr(member, door.access_field):
|
|
||||||
now = timezone.now()
|
|
||||||
margin = timedelta(hours=1)
|
|
||||||
if member.eventinstructor.eventext_set.filter(
|
|
||||||
occurred=True,
|
|
||||||
meeting_times__start__lt=now + margin,
|
|
||||||
meeting_times__end__gt=now - margin,
|
|
||||||
).exists():
|
|
||||||
reasons_and_schedules["Instructor for Active Class"] = "Active Class"
|
|
||||||
|
|
||||||
reasons = sorted(reasons_and_schedules.keys())
|
reasons = sorted(reasons_and_schedules.keys())
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
|
@ -1,18 +1,8 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from membershipworks.tasks.scrape import logger, scrape_events
|
from membershipworks.tasks.scrape import scrape_events
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
def handle(self, *args, verbosity: int, **options):
|
def handle(self, *args, **options):
|
||||||
verbosity_levels = {
|
|
||||||
0: logging.ERROR,
|
|
||||||
1: logging.WARNING,
|
|
||||||
2: logging.INFO,
|
|
||||||
3: logging.DEBUG,
|
|
||||||
}
|
|
||||||
logger.setLevel(verbosity_levels.get(verbosity, logging.WARNING))
|
|
||||||
|
|
||||||
scrape_events()
|
scrape_events()
|
||||||
|
@ -88,13 +88,13 @@ class Flag(BaseModel):
|
|||||||
name = models.TextField(null=True, blank=True)
|
name = models.TextField(null=True, blank=True)
|
||||||
type = models.CharField(max_length=6)
|
type = models.CharField(max_length=6)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name} ({self.type})"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "flag"
|
db_table = "flag"
|
||||||
ordering = ("name",)
|
ordering = ("name",)
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.name} ({self.type})"
|
|
||||||
|
|
||||||
|
|
||||||
class MemberQuerySet(models.QuerySet):
|
class MemberQuerySet(models.QuerySet):
|
||||||
# TODO: maybe rename to reflect EXISTS?
|
# TODO: maybe rename to reflect EXISTS?
|
||||||
@ -122,6 +122,8 @@ class MemberQuerySet(models.QuerySet):
|
|||||||
|
|
||||||
# TODO: is this still a temporal table?
|
# TODO: is this still a temporal table?
|
||||||
class Member(BaseModel):
|
class Member(BaseModel):
|
||||||
|
objects = MemberQuerySet.as_manager()
|
||||||
|
|
||||||
uid = models.CharField(max_length=24, primary_key=True)
|
uid = models.CharField(max_length=24, primary_key=True)
|
||||||
year_of_birth = models.TextField(db_column="Year of Birth", null=True, blank=True)
|
year_of_birth = models.TextField(db_column="Year of Birth", null=True, blank=True)
|
||||||
account_name = models.TextField(db_column="Account Name", null=True, blank=True)
|
account_name = models.TextField(db_column="Account Name", null=True, blank=True)
|
||||||
@ -262,7 +264,8 @@ class Member(BaseModel):
|
|||||||
"Waiver form signed and on file date.": "%m/%d/%Y",
|
"Waiver form signed and on file date.": "%m/%d/%Y",
|
||||||
}
|
}
|
||||||
|
|
||||||
objects = MemberQuerySet.as_manager()
|
def __str__(self):
|
||||||
|
return f"{self.account_name}"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "members"
|
db_table = "members"
|
||||||
@ -273,9 +276,6 @@ class Member(BaseModel):
|
|||||||
models.Index(fields=["last_name"], name="last_name_idx"),
|
models.Index(fields=["last_name"], name="last_name_idx"),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.account_name}"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_user(cls, user) -> "Member | None":
|
def from_user(cls, user) -> "Member | None":
|
||||||
if hasattr(user, "ldap_user"):
|
if hasattr(user, "ldap_user"):
|
||||||
@ -304,6 +304,9 @@ class MemberFlag(BaseModel):
|
|||||||
)
|
)
|
||||||
flag = models.ForeignKey(Flag, on_delete=models.PROTECT)
|
flag = models.ForeignKey(Flag, on_delete=models.PROTECT)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.member} - {self.flag}"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "memberflag"
|
db_table = "memberflag"
|
||||||
constraints = [
|
constraints = [
|
||||||
@ -312,9 +315,6 @@ class MemberFlag(BaseModel):
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.member} - {self.flag}"
|
|
||||||
|
|
||||||
|
|
||||||
class Transaction(BaseModel):
|
class Transaction(BaseModel):
|
||||||
sid = models.CharField(max_length=256, null=True, blank=True)
|
sid = models.CharField(max_length=256, null=True, blank=True)
|
||||||
@ -364,24 +364,24 @@ class Transaction(BaseModel):
|
|||||||
"_dp": None,
|
"_dp": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "transactions"
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.type} [{self.member if self.member else self.name}] {self.timestamp}"
|
return f"{self.type} [{self.member if self.member else self.name}] {self.timestamp}"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "transactions"
|
||||||
|
|
||||||
|
|
||||||
class EventCategory(models.Model):
|
class EventCategory(models.Model):
|
||||||
id = models.IntegerField(primary_key=True)
|
id = models.IntegerField(primary_key=True)
|
||||||
title = models.TextField()
|
title = models.TextField()
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_api_dict(cls, id: int, data):
|
def from_api_dict(cls, id: int, data):
|
||||||
return cls(id=id, title=data["ttl"])
|
return cls(id=id, title=data["ttl"])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
class Event(BaseModel):
|
class Event(BaseModel):
|
||||||
class EventCalendar(models.IntegerChoices):
|
class EventCalendar(models.IntegerChoices):
|
||||||
@ -433,13 +433,13 @@ class Event(BaseModel):
|
|||||||
|
|
||||||
_allowed_missing_fields = ["cap", "edp", "adn"]
|
_allowed_missing_fields = ["cap", "edp", "adn"]
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.unescaped_title
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unescaped_title(self):
|
def unescaped_title(self):
|
||||||
return nh3.clean(self.title, tags=set())
|
return nh3.clean(self.title, tags=set())
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.unescaped_title
|
||||||
|
|
||||||
|
|
||||||
class EventInstructor(models.Model):
|
class EventInstructor(models.Model):
|
||||||
name = models.TextField(blank=True)
|
name = models.TextField(blank=True)
|
||||||
@ -526,6 +526,8 @@ class EventExtManager(models.Manager):
|
|||||||
class EventExt(Event):
|
class EventExt(Event):
|
||||||
"""Extension of `Event` to capture some fields not supported in MembershipWorks"""
|
"""Extension of `Event` to capture some fields not supported in MembershipWorks"""
|
||||||
|
|
||||||
|
objects = EventExtManager.from_queryset(EventExtQuerySet)()
|
||||||
|
|
||||||
instructor = models.ForeignKey(
|
instructor = models.ForeignKey(
|
||||||
EventInstructor, on_delete=models.PROTECT, null=True, blank=True
|
EventInstructor, on_delete=models.PROTECT, null=True, blank=True
|
||||||
)
|
)
|
||||||
@ -555,12 +557,6 @@ class EventExt(Event):
|
|||||||
should_survey = models.BooleanField(default=False)
|
should_survey = models.BooleanField(default=False)
|
||||||
survey_email_sent = models.BooleanField(default=False)
|
survey_email_sent = models.BooleanField(default=False)
|
||||||
|
|
||||||
objects = EventExtManager.from_queryset(EventExtQuerySet)()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "event"
|
|
||||||
ordering = ["-start"]
|
|
||||||
|
|
||||||
def get_absolute_url(self) -> str:
|
def get_absolute_url(self) -> str:
|
||||||
return reverse("membershipworks:event-detail", kwargs={"eid": self.eid})
|
return reverse("membershipworks:event-detail", kwargs={"eid": self.eid})
|
||||||
|
|
||||||
@ -585,6 +581,10 @@ class EventExt(Event):
|
|||||||
and getattr(self, "total_due_to_instructor") is not None
|
and getattr(self, "total_due_to_instructor") is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "event"
|
||||||
|
ordering = ["-start"]
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
@ -633,9 +633,6 @@ class EventMeetingTime(models.Model):
|
|||||||
models.CheckConstraint(check=Q(end__gt=F("start")), name="end_after_start"),
|
models.CheckConstraint(check=Q(end__gt=F("start")), name="end_after_start"),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return f"{self.start} - {self.end}"
|
|
||||||
|
|
||||||
|
|
||||||
class EventInvoice(models.Model):
|
class EventInvoice(models.Model):
|
||||||
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
|
@ -20,14 +20,14 @@ class AbstractAudit(models.Model):
|
|||||||
good = models.BooleanField(default=False)
|
good = models.BooleanField(default=False)
|
||||||
notes = models.CharField(max_length=255, blank=True)
|
notes = models.CharField(max_length=255, blank=True)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{'Good' if self.good else 'Bad'} audit at {self.date} by {self.user}"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
ordering = ["date"]
|
ordering = ["date"]
|
||||||
get_latest_by = ["date"]
|
get_latest_by = ["date"]
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return f"{'Good' if self.good else 'Bad'} audit at {self.date} by {self.user}"
|
|
||||||
|
|
||||||
|
|
||||||
class CmsRedRiverVeteransScholarship(models.Model):
|
class CmsRedRiverVeteransScholarship(models.Model):
|
||||||
serial = models.AutoField(primary_key=True)
|
serial = models.AutoField(primary_key=True)
|
||||||
@ -61,12 +61,12 @@ class CmsRedRiverVeteransScholarship(models.Model):
|
|||||||
db_column="Program Status", max_length=16, blank=True, null=True
|
db_column="Program Status", max_length=16, blank=True, null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "CMS Red River Veterans Scholarship"
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.program_name} {self.member_name}"
|
return f"{self.program_name} {self.member_name}"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "CMS Red River Veterans Scholarship"
|
||||||
|
|
||||||
|
|
||||||
class DepartmentQuerySet(models.QuerySet):
|
class DepartmentQuerySet(models.QuerySet):
|
||||||
def filter_by_shop_lead(self, member: Member) -> models.QuerySet["Department"]:
|
def filter_by_shop_lead(self, member: Member) -> models.QuerySet["Department"]:
|
||||||
@ -80,6 +80,8 @@ class DepartmentQuerySet(models.QuerySet):
|
|||||||
|
|
||||||
|
|
||||||
class Department(models.Model):
|
class Department(models.Model):
|
||||||
|
objects = DepartmentQuerySet.as_manager()
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=64,
|
max_length=64,
|
||||||
validators=[RegexValidator("^[-_ A-Za-z0-9]*$")],
|
validators=[RegexValidator("^[-_ A-Za-z0-9]*$")],
|
||||||
@ -99,8 +101,6 @@ class Department(models.Model):
|
|||||||
)
|
)
|
||||||
list_reply_to_address = models.EmailField(max_length=254, blank=True)
|
list_reply_to_address = models.EmailField(max_length=254, blank=True)
|
||||||
|
|
||||||
objects = DepartmentQuerySet.as_manager()
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@ -123,13 +123,13 @@ class CertificationDefinition(models.Model):
|
|||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
department = models.ForeignKey(Department, models.PROTECT)
|
department = models.ForeignKey(Department, models.PROTECT)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{self.name} <{self.department}>"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "Certification Definitions"
|
db_table = "Certification Definitions"
|
||||||
ordering = ("name", "department")
|
ordering = ("name", "department")
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return f"{self.name} <{self.department}>"
|
|
||||||
|
|
||||||
def latest_version(self) -> "CertificationVersion":
|
def latest_version(self) -> "CertificationVersion":
|
||||||
return self.certificationversion_set.latest()
|
return self.certificationversion_set.latest()
|
||||||
|
|
||||||
@ -157,6 +157,8 @@ class CertificationVersionManager(models.Manager["CertificationVersion"]):
|
|||||||
|
|
||||||
|
|
||||||
class CertificationVersion(models.Model):
|
class CertificationVersion(models.Model):
|
||||||
|
objects = CertificationVersionManager()
|
||||||
|
|
||||||
definition = models.ForeignKey(CertificationDefinition, on_delete=models.PROTECT)
|
definition = models.ForeignKey(CertificationDefinition, on_delete=models.PROTECT)
|
||||||
major = models.PositiveSmallIntegerField()
|
major = models.PositiveSmallIntegerField()
|
||||||
minor = models.PositiveSmallIntegerField()
|
minor = models.PositiveSmallIntegerField()
|
||||||
@ -164,7 +166,8 @@ class CertificationVersion(models.Model):
|
|||||||
prerelease = models.CharField(max_length=255, blank=True)
|
prerelease = models.CharField(max_length=255, blank=True)
|
||||||
approval_date = models.DateField(blank=True, null=True)
|
approval_date = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
objects = CertificationVersionManager()
|
def __str__(self) -> str:
|
||||||
|
return f"{self.definition} [{self.semantic_version()}]"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
@ -184,9 +187,6 @@ class CertificationVersion(models.Model):
|
|||||||
get_latest_by = ("major", "minor", "patch", "prerelease", "approval_date")
|
get_latest_by = ("major", "minor", "patch", "prerelease", "approval_date")
|
||||||
base_manager_name = "objects"
|
base_manager_name = "objects"
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return f"{self.definition} [{self.semantic_version()}]"
|
|
||||||
|
|
||||||
def semantic_version(self) -> VersionInfo:
|
def semantic_version(self) -> VersionInfo:
|
||||||
return VersionInfo(
|
return VersionInfo(
|
||||||
self.major or 0,
|
self.major or 0,
|
||||||
@ -224,6 +224,9 @@ class Certification(models.Model):
|
|||||||
shop_lead_notified = models.DateTimeField(blank=True, null=True)
|
shop_lead_notified = models.DateTimeField(blank=True, null=True)
|
||||||
notes = models.CharField(max_length=255, blank=True, null=True)
|
notes = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{self.name} - {self.certification_version}"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "Certifications"
|
db_table = "Certifications"
|
||||||
permissions = [
|
permissions = [
|
||||||
@ -233,9 +236,6 @@ class Certification(models.Model):
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return f"{self.name} - {self.certification_version}"
|
|
||||||
|
|
||||||
|
|
||||||
class CertificationAudit(AbstractAudit):
|
class CertificationAudit(AbstractAudit):
|
||||||
certification = models.ForeignKey(
|
certification = models.ForeignKey(
|
||||||
@ -255,12 +255,12 @@ class InstructorOrVendor(models.Model):
|
|||||||
db_column="email address", max_length=255, blank=True, null=True
|
db_column="email address", max_length=255, blank=True, null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "Instructors and Vendors"
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.name}"
|
return f"{self.name}"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "Instructors and Vendors"
|
||||||
|
|
||||||
|
|
||||||
class SpecialProgram(models.Model):
|
class SpecialProgram(models.Model):
|
||||||
program_name = models.CharField(
|
program_name = models.CharField(
|
||||||
@ -292,12 +292,12 @@ class SpecialProgram(models.Model):
|
|||||||
db_column="Program Status", max_length=16, blank=True, null=True
|
db_column="Program Status", max_length=16, blank=True, null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "Special_Programs"
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.program_name
|
return self.program_name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "Special_Programs"
|
||||||
|
|
||||||
|
|
||||||
class Waiver(models.Model):
|
class Waiver(models.Model):
|
||||||
number = models.AutoField(db_column="Number", primary_key=True)
|
number = models.AutoField(db_column="Number", primary_key=True)
|
||||||
@ -319,12 +319,12 @@ class Waiver(models.Model):
|
|||||||
guardian_date = models.DateField(db_column="Guardian Date", blank=True, null=True)
|
guardian_date = models.DateField(db_column="Guardian Date", blank=True, null=True)
|
||||||
notes = models.CharField(max_length=255, blank=True, null=True)
|
notes = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = "Waivers"
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.name} {self.date}"
|
return f"{self.name} {self.date}"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "Waivers"
|
||||||
|
|
||||||
|
|
||||||
class WaiverAudit(AbstractAudit):
|
class WaiverAudit(AbstractAudit):
|
||||||
waiver = models.ForeignKey(Waiver, on_delete=models.CASCADE, related_name="audits")
|
waiver = models.ForeignKey(Waiver, on_delete=models.CASCADE, related_name="audits")
|
||||||
|
144
pdm.lock
144
pdm.lock
@ -5,7 +5,7 @@
|
|||||||
groups = ["default", "debug", "lint", "server", "typing", "dev"]
|
groups = ["default", "debug", "lint", "server", "typing", "dev"]
|
||||||
strategy = ["cross_platform"]
|
strategy = ["cross_platform"]
|
||||||
lock_version = "4.4.1"
|
lock_version = "4.4.1"
|
||||||
content_hash = "sha256:b32d3518b2ef3d797f260f44adff417959def44b75fb60134fcdd93351c92581"
|
content_hash = "sha256:8b3bb37d6fbada119262035c0e94ada756c2f43ca8863b50b51f47a232ee84b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohttp"
|
name = "aiohttp"
|
||||||
@ -224,15 +224,15 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitstring"
|
name = "bitstring"
|
||||||
version = "4.2.3"
|
version = "4.2.2"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "Simple construction, analysis and modification of binary data."
|
summary = "Simple construction, analysis and modification of binary data."
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitarray<3.0.0,>=2.9.0",
|
"bitarray<3.0.0,>=2.9.0",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "bitstring-4.2.3-py3-none-any.whl", hash = "sha256:20ed0036e2fcf0323acb0f92f0b7b178516a080f3e91061470aa019ac4ede404"},
|
{file = "bitstring-4.2.2-py3-none-any.whl", hash = "sha256:8b784373e78e953879c8192589e1ecbcda8f111841633a2aaf88d2b073fd6c35"},
|
||||||
{file = "bitstring-4.2.3.tar.gz", hash = "sha256:e0c447af3fda0d114f77b88c2d199f02f97ee7e957e6d719f40f41cf15fbb897"},
|
{file = "bitstring-4.2.2.tar.gz", hash = "sha256:b40b01d911eebaea6efff40d826580806dced5e04b9d3cbad6aebf9422f4b643"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -578,29 +578,29 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-db-views"
|
name = "django-db-views"
|
||||||
version = "0.1.8"
|
version = "0.1.7"
|
||||||
summary = "Handle database views. Allow to create migrations for database views. View migrations using django code. They can be reversed. Changes in model view definition are detected automatically. Support almost all options as regular makemigrations command"
|
summary = "Handle database views. Allow to create migrations for database views. View migrations using django code. They can be reversed. Changes in model view definition are detected automatically. Support almost all options as regular makemigrations command"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"django>=2.2",
|
"Django",
|
||||||
"six",
|
"six",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "django_db_views-0.1.8-py2.py3-none-any.whl", hash = "sha256:089c9f193c265f956c0f06d0695b54ff57343bbb25f6c5d546f816e7cb9b456c"},
|
{file = "django-db-views-0.1.7.tar.gz", hash = "sha256:7c0dc78aa5f53cc4eefc4d880450a8cb61bb8376b5494356e20383f71a3e1657"},
|
||||||
{file = "django_db_views-0.1.8.tar.gz", hash = "sha256:b737b01b782d859a90f4d579147a5fa34f4b5376606443abe35c5fdc00313bcc"},
|
{file = "django_db_views-0.1.7-py3-none-any.whl", hash = "sha256:ffa399af1678e60f532f8c2b531927e94e8249e1012a83fc865234384419bff8"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-debug-toolbar"
|
name = "django-debug-toolbar"
|
||||||
version = "4.4.2"
|
version = "4.3.0"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "A configurable set of panels that display various debug information about the current request/response."
|
summary = "A configurable set of panels that display various debug information about the current request/response."
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"django>=4.2.9",
|
"django>=3.2.4",
|
||||||
"sqlparse>=0.2",
|
"sqlparse>=0.2",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "django_debug_toolbar-4.4.2-py3-none-any.whl", hash = "sha256:5d7afb2ea5f8730241e5b0735396e16cd1fd8c6b53a2f3e1e30bbab9abb23728"},
|
{file = "django_debug_toolbar-4.3.0-py3-none-any.whl", hash = "sha256:e09b7dcb8417b743234dfc57c95a7c1d1d87a88844abd13b4c5387f807b31bf6"},
|
||||||
{file = "django_debug_toolbar-4.4.2.tar.gz", hash = "sha256:9204050fcb1e4f74216c5b024bc76081451926a6303993d6c513f5e142675927"},
|
{file = "django_debug_toolbar-4.3.0.tar.gz", hash = "sha256:0b0dddee5ea29b9cb678593bc0d7a6d76b21d7799cb68e091a2148341a80f3c4"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -734,24 +734,24 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-stubs"
|
name = "django-stubs"
|
||||||
version = "5.0.2"
|
version = "5.0.0"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "Mypy stubs for Django"
|
summary = "Mypy stubs for Django"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"asgiref",
|
"asgiref",
|
||||||
"django",
|
"django",
|
||||||
"django-stubs-ext>=5.0.2",
|
"django-stubs-ext>=5.0.0",
|
||||||
"types-PyYAML",
|
"types-PyYAML",
|
||||||
"typing-extensions>=4.11.0",
|
"typing-extensions",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "django_stubs-5.0.2-py3-none-any.whl", hash = "sha256:cb0c506cb5c54c64612e4a2ee8d6b913c6178560ec168009fe847c09747c304b"},
|
{file = "django_stubs-5.0.0-py3-none-any.whl", hash = "sha256:084484cbe16a6d388e80ec687e46f529d67a232f3befaf55c936b3b476be289d"},
|
||||||
{file = "django_stubs-5.0.2.tar.gz", hash = "sha256:236bc5606e5607cb968f92b648471f9edaa461a774bc013bf9e6bff8730f6bdf"},
|
{file = "django_stubs-5.0.0.tar.gz", hash = "sha256:b8a792bee526d6cab31e197cb414ee7fa218abd931a50948c66a80b3a2548621"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-stubs-ext"
|
name = "django-stubs-ext"
|
||||||
version = "5.0.2"
|
version = "5.0.0"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "Monkey-patching and extensions for django-stubs"
|
summary = "Monkey-patching and extensions for django-stubs"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@ -759,23 +759,23 @@ dependencies = [
|
|||||||
"typing-extensions",
|
"typing-extensions",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "django_stubs_ext-5.0.2-py3-none-any.whl", hash = "sha256:8d8efec5a86241266bec94a528fe21258ad90d78c67307f3ae5f36e81de97f12"},
|
{file = "django_stubs_ext-5.0.0-py3-none-any.whl", hash = "sha256:8e1334fdf0c8bff87e25d593b33d4247487338aaed943037826244ff788b56a8"},
|
||||||
{file = "django_stubs_ext-5.0.2.tar.gz", hash = "sha256:409c62585d7f996cef5c760e6e27ea3ff29f961c943747e67519c837422cad32"},
|
{file = "django_stubs_ext-5.0.0.tar.gz", hash = "sha256:5bacfbb498a206d5938454222b843d81da79ea8b6fcd1a59003f529e775bc115"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-stubs"
|
name = "django-stubs"
|
||||||
version = "5.0.2"
|
version = "5.0.0"
|
||||||
extras = ["compatible-mypy"]
|
extras = ["compatible-mypy"]
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "Mypy stubs for Django"
|
summary = "Mypy stubs for Django"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"django-stubs==5.0.2",
|
"django-stubs==5.0.0",
|
||||||
"mypy~=1.10.0",
|
"mypy~=1.10.0",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "django_stubs-5.0.2-py3-none-any.whl", hash = "sha256:cb0c506cb5c54c64612e4a2ee8d6b913c6178560ec168009fe847c09747c304b"},
|
{file = "django_stubs-5.0.0-py3-none-any.whl", hash = "sha256:084484cbe16a6d388e80ec687e46f529d67a232f3befaf55c936b3b476be289d"},
|
||||||
{file = "django_stubs-5.0.2.tar.gz", hash = "sha256:236bc5606e5607cb968f92b648471f9edaa461a774bc013bf9e6bff8730f6bdf"},
|
{file = "django_stubs-5.0.0.tar.gz", hash = "sha256:b8a792bee526d6cab31e197cb414ee7fa218abd931a50948c66a80b3a2548621"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1079,7 +1079,7 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hypothesis"
|
name = "hypothesis"
|
||||||
version = "6.103.1"
|
version = "6.102.4"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "A library for property-based testing"
|
summary = "A library for property-based testing"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@ -1087,23 +1087,23 @@ dependencies = [
|
|||||||
"sortedcontainers<3.0.0,>=2.1.0",
|
"sortedcontainers<3.0.0,>=2.1.0",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "hypothesis-6.103.1-py3-none-any.whl", hash = "sha256:d3c959fab6233e78867499e2117ae9db8dc40eeed936d71a2cfc7b6094972e74"},
|
{file = "hypothesis-6.102.4-py3-none-any.whl", hash = "sha256:013df31b04a4daede13756f497e60e451963d86f426395a79f99c5d692919bbd"},
|
||||||
{file = "hypothesis-6.103.1.tar.gz", hash = "sha256:d299d5c21d6408eab3be670c94c974f3acf0b511c61fe81804b09091e393ee1f"},
|
{file = "hypothesis-6.102.4.tar.gz", hash = "sha256:59b4d144346d5cffb482cc1bafbd21b13ff31608e8c4b3e4630339aee3e87763"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hypothesis"
|
name = "hypothesis"
|
||||||
version = "6.103.1"
|
version = "6.102.4"
|
||||||
extras = ["django"]
|
extras = ["django"]
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "A library for property-based testing"
|
summary = "A library for property-based testing"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"django>=3.2",
|
"django>=3.2",
|
||||||
"hypothesis==6.103.1",
|
"hypothesis==6.102.4",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "hypothesis-6.103.1-py3-none-any.whl", hash = "sha256:d3c959fab6233e78867499e2117ae9db8dc40eeed936d71a2cfc7b6094972e74"},
|
{file = "hypothesis-6.102.4-py3-none-any.whl", hash = "sha256:013df31b04a4daede13756f497e60e451963d86f426395a79f99c5d692919bbd"},
|
||||||
{file = "hypothesis-6.103.1.tar.gz", hash = "sha256:d299d5c21d6408eab3be670c94c974f3acf0b511c61fe81804b09091e393ee1f"},
|
{file = "hypothesis-6.102.4.tar.gz", hash = "sha256:59b4d144346d5cffb482cc1bafbd21b13ff31608e8c4b3e4630339aee3e87763"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1118,7 +1118,7 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipython"
|
name = "ipython"
|
||||||
version = "8.25.0"
|
version = "8.24.0"
|
||||||
requires_python = ">=3.10"
|
requires_python = ">=3.10"
|
||||||
summary = "IPython: Productive Interactive Computing"
|
summary = "IPython: Productive Interactive Computing"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@ -1134,8 +1134,8 @@ dependencies = [
|
|||||||
"typing-extensions>=4.6; python_version < \"3.12\"",
|
"typing-extensions>=4.6; python_version < \"3.12\"",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "ipython-8.25.0-py3-none-any.whl", hash = "sha256:53eee7ad44df903a06655871cbab66d156a051fd86f3ec6750470ac9604ac1ab"},
|
{file = "ipython-8.24.0-py3-none-any.whl", hash = "sha256:d7bf2f6c4314984e3e02393213bab8703cf163ede39672ce5918c51fe253a2a3"},
|
||||||
{file = "ipython-8.25.0.tar.gz", hash = "sha256:c6ed726a140b6e725b911528f80439c534fac915246af3efc39440a6b0f9d716"},
|
{file = "ipython-8.24.0.tar.gz", hash = "sha256:010db3f8a728a578bb641fdd06c063b9fb8e96a9464c63aec6310fbcb5e80501"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1817,8 +1817,8 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
version = "2.32.3"
|
version = "2.31.0"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.7"
|
||||||
summary = "Python HTTP for Humans."
|
summary = "Python HTTP for Humans."
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"certifi>=2017.4.17",
|
"certifi>=2017.4.17",
|
||||||
@ -1827,33 +1827,33 @@ dependencies = [
|
|||||||
"urllib3<3,>=1.21.1",
|
"urllib3<3,>=1.21.1",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
|
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
|
||||||
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
|
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.4.8"
|
version = "0.4.4"
|
||||||
requires_python = ">=3.7"
|
requires_python = ">=3.7"
|
||||||
summary = "An extremely fast Python linter and code formatter, written in Rust."
|
summary = "An extremely fast Python linter and code formatter, written in Rust."
|
||||||
files = [
|
files = [
|
||||||
{file = "ruff-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066"},
|
{file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"},
|
||||||
{file = "ruff-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913"},
|
{file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"},
|
||||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3"},
|
{file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"},
|
||||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb"},
|
{file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"},
|
||||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764"},
|
{file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"},
|
||||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883"},
|
{file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"},
|
||||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199"},
|
{file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"},
|
||||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b"},
|
{file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"},
|
||||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45"},
|
{file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"},
|
||||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a"},
|
{file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"},
|
||||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc"},
|
{file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"},
|
||||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed"},
|
{file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"},
|
||||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa"},
|
{file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"},
|
||||||
{file = "ruff-0.4.8-py3-none-win32.whl", hash = "sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9"},
|
{file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"},
|
||||||
{file = "ruff-0.4.8-py3-none-win_amd64.whl", hash = "sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d"},
|
{file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"},
|
||||||
{file = "ruff-0.4.8-py3-none-win_arm64.whl", hash = "sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780"},
|
{file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"},
|
||||||
{file = "ruff-0.4.8.tar.gz", hash = "sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268"},
|
{file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1868,12 +1868,12 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "setuptools"
|
name = "setuptools"
|
||||||
version = "70.0.0"
|
version = "69.5.1"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "Easily download, build, install, upgrade, and uninstall Python packages"
|
summary = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||||
files = [
|
files = [
|
||||||
{file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"},
|
{file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"},
|
||||||
{file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"},
|
{file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2117,15 +2117,15 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-requests"
|
name = "types-requests"
|
||||||
version = "2.32.0.20240602"
|
version = "2.31.0.20240406"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "Typing stubs for requests"
|
summary = "Typing stubs for requests"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"urllib3>=2",
|
"urllib3>=2",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "types-requests-2.32.0.20240602.tar.gz", hash = "sha256:3f98d7bbd0dd94ebd10ff43a7fbe20c3b8528acace6d8efafef0b6a184793f06"},
|
{file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"},
|
||||||
{file = "types_requests-2.32.0.20240602-py3-none-any.whl", hash = "sha256:ed3946063ea9fbc6b5fc0c44fa279188bae42d582cb63760be6cb4b9d06c3de8"},
|
{file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2196,7 +2196,7 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uvicorn"
|
name = "uvicorn"
|
||||||
version = "0.30.1"
|
version = "0.29.0"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "The lightning-fast ASGI server."
|
summary = "The lightning-fast ASGI server."
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@ -2204,13 +2204,13 @@ dependencies = [
|
|||||||
"h11>=0.8",
|
"h11>=0.8",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"},
|
{file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"},
|
||||||
{file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"},
|
{file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uvicorn"
|
name = "uvicorn"
|
||||||
version = "0.30.1"
|
version = "0.29.0"
|
||||||
extras = ["standard"]
|
extras = ["standard"]
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "The lightning-fast ASGI server."
|
summary = "The lightning-fast ASGI server."
|
||||||
@ -2219,14 +2219,14 @@ dependencies = [
|
|||||||
"httptools>=0.5.0",
|
"httptools>=0.5.0",
|
||||||
"python-dotenv>=0.13",
|
"python-dotenv>=0.13",
|
||||||
"pyyaml>=5.1",
|
"pyyaml>=5.1",
|
||||||
"uvicorn==0.30.1",
|
"uvicorn==0.29.0",
|
||||||
"uvloop!=0.15.0,!=0.15.1,>=0.14.0; (sys_platform != \"cygwin\" and sys_platform != \"win32\") and platform_python_implementation != \"PyPy\"",
|
"uvloop!=0.15.0,!=0.15.1,>=0.14.0; (sys_platform != \"cygwin\" and sys_platform != \"win32\") and platform_python_implementation != \"PyPy\"",
|
||||||
"watchfiles>=0.13",
|
"watchfiles>=0.13",
|
||||||
"websockets>=10.4",
|
"websockets>=10.4",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"},
|
{file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"},
|
||||||
{file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"},
|
{file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2311,7 +2311,7 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "weasyprint"
|
name = "weasyprint"
|
||||||
version = "62.2"
|
version = "62.1"
|
||||||
requires_python = ">=3.9"
|
requires_python = ">=3.9"
|
||||||
summary = "The Awesome Document Factory"
|
summary = "The Awesome Document Factory"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@ -2325,8 +2325,8 @@ dependencies = [
|
|||||||
"tinycss2>=1.3.0",
|
"tinycss2>=1.3.0",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "weasyprint-62.2-py3-none-any.whl", hash = "sha256:6fd84e9f55ac239c5657845eae117fd43916c3c5986fe98f69ea13fdab8ec9ad"},
|
{file = "weasyprint-62.1-py3-none-any.whl", hash = "sha256:654d4c266336cbf9acc4da118c7778ef5839717e6055d5b8f995cf50be200c46"},
|
||||||
{file = "weasyprint-62.2.tar.gz", hash = "sha256:a08ac400e11919d996d76becaa33160d7c1ac55ba160628c42ce7586574c1a51"},
|
{file = "weasyprint-62.1.tar.gz", hash = "sha256:bf3c1a9ac4194271a7cf117229c093744105b50ac2fa64c0a6e44e68ae742992"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -18,8 +18,8 @@ dependencies = [
|
|||||||
"mdformat-tables~=0.4",
|
"mdformat-tables~=0.4",
|
||||||
"mysqlclient~=2.2",
|
"mysqlclient~=2.2",
|
||||||
"django-autocomplete-light~=3.11",
|
"django-autocomplete-light~=3.11",
|
||||||
"weasyprint~=62.2",
|
"weasyprint~=62.1",
|
||||||
"requests~=2.32",
|
"requests~=2.31",
|
||||||
"semver~=3.0",
|
"semver~=3.0",
|
||||||
"djangorestframework~=3.15",
|
"djangorestframework~=3.15",
|
||||||
"django-q2~=1.6",
|
"django-q2~=1.6",
|
||||||
@ -44,8 +44,8 @@ requires-python = ">=3.11"
|
|||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
server = [
|
server = [
|
||||||
"uvicorn[standard]~=0.30",
|
"uvicorn[standard]~=0.29",
|
||||||
"setuptools~=70.0",
|
"setuptools~=69.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.entry-points."djangoq.errorreporters"]
|
[project.entry-points."djangoq.errorreporters"]
|
||||||
@ -55,7 +55,7 @@ admin_email = "cmsmanage.django_q2_admin_email_reporter:AdminEmailReporter"
|
|||||||
line-length = 88
|
line-length = 88
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
select = ["E4", "E7", "E9", "F", "I", "C4", "UP", "PERF", "PL", "SIM", "FIX003", "DJ012"]
|
select = ["E4", "E7", "E9", "F", "I", "C4", "UP", "PERF", "PL", "SIM", "FIX003"]
|
||||||
|
|
||||||
[tool.ruff.lint.isort]
|
[tool.ruff.lint.isort]
|
||||||
known-first-party = [
|
known-first-party = [
|
||||||
@ -118,9 +118,9 @@ lint = [
|
|||||||
typing = [
|
typing = [
|
||||||
"mypy~=1.10",
|
"mypy~=1.10",
|
||||||
"django-stubs~=5.0",
|
"django-stubs~=5.0",
|
||||||
"setuptools~=70.0",
|
"setuptools~=69.5",
|
||||||
"types-bleach~=6.1",
|
"types-bleach~=6.1",
|
||||||
"types-requests~=2.32",
|
"types-requests~=2.31",
|
||||||
"types-urllib3~=1.26",
|
"types-urllib3~=1.26",
|
||||||
"djangorestframework-stubs[compatible-mypy]~=3.15",
|
"djangorestframework-stubs[compatible-mypy]~=3.15",
|
||||||
"types-Markdown~=3.6",
|
"types-Markdown~=3.6",
|
||||||
@ -129,12 +129,12 @@ typing = [
|
|||||||
"types-lxml~=2024.4",
|
"types-lxml~=2024.4",
|
||||||
]
|
]
|
||||||
debug = [
|
debug = [
|
||||||
"django-debug-toolbar~=4.4",
|
"django-debug-toolbar~=4.3",
|
||||||
]
|
]
|
||||||
dev = [
|
dev = [
|
||||||
"django-extensions~=3.2",
|
"django-extensions~=3.2",
|
||||||
"ipython~=8.25",
|
"ipython~=8.24",
|
||||||
"hypothesis[django]~=6.103",
|
"hypothesis[django]~=6.102",
|
||||||
"tblib~=3.0",
|
"tblib~=3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -33,6 +33,17 @@ class LockerUnit(models.Model):
|
|||||||
rows = models.PositiveIntegerField(default=5)
|
rows = models.PositiveIntegerField(default=5)
|
||||||
columns = models.PositiveIntegerField(default=2)
|
columns = models.PositiveIntegerField(default=2)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self._state.adding:
|
||||||
|
# Create LockerInfo for each locker
|
||||||
|
with transaction.atomic():
|
||||||
|
super().save(self, *args, **kwargs)
|
||||||
|
for column in range(self.columns):
|
||||||
|
for row in range(self.rows):
|
||||||
|
self.lockers.create(column=column + 1, row=row + 1)
|
||||||
|
else:
|
||||||
|
super().save(self, *args, **kwargs)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
# TODO: add constraint to check for letter overlaps
|
# TODO: add constraint to check for letter overlaps
|
||||||
constraints = [
|
constraints = [
|
||||||
@ -45,17 +56,6 @@ class LockerUnit(models.Model):
|
|||||||
last_number = self.first_number + self.columns * self.rows
|
last_number = self.first_number + self.columns * self.rows
|
||||||
return f"{self.bank.name} (Unit {last_letter}{self.first_number}-{self.first_letter}{last_number})"
|
return f"{self.bank.name} (Unit {last_letter}{self.first_number}-{self.first_letter}{last_number})"
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
if self._state.adding:
|
|
||||||
# Create LockerInfo for each locker
|
|
||||||
with transaction.atomic():
|
|
||||||
super().save(self, *args, **kwargs)
|
|
||||||
for column in range(self.columns):
|
|
||||||
for row in range(self.rows):
|
|
||||||
self.lockers.create(column=column + 1, row=row + 1)
|
|
||||||
else:
|
|
||||||
super().save(self, *args, **kwargs)
|
|
||||||
|
|
||||||
def letter_for_column(self, column: int) -> str:
|
def letter_for_column(self, column: int) -> str:
|
||||||
return chr(column + ord(self.first_letter))
|
return chr(column + ord(self.first_letter))
|
||||||
|
|
||||||
@ -91,6 +91,10 @@ class LockerInfo(models.Model):
|
|||||||
)
|
)
|
||||||
notes = models.TextField(blank=True)
|
notes = models.TextField(blank=True)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if self.reserved and self.renter is not None:
|
||||||
|
raise ValidationError("Locker cannot both be reserved and rented!")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
models.UniqueConstraint(
|
models.UniqueConstraint(
|
||||||
@ -102,13 +106,6 @@ class LockerInfo(models.Model):
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.locker_unit}-{self.address} [{self.renter}]"
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
if self.reserved and self.renter is not None:
|
|
||||||
raise ValidationError("Locker cannot both be reserved and rented!")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
return self.renter is None and not self.reserved
|
return self.renter is None and not self.reserved
|
||||||
@ -135,3 +132,6 @@ class LockerInfo(models.Model):
|
|||||||
)
|
)
|
||||||
def address(self) -> str:
|
def address(self) -> str:
|
||||||
return f"{self.letter}{self.number}"
|
return f"{self.letter}{self.number}"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.locker_unit}-{self.address} [{self.renter}]"
|
||||||
|
@ -90,18 +90,18 @@ class GroupToolSubscription(SubscriptionSettings):
|
|||||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||||
tool = models.ForeignKey(Tool, on_delete=models.CASCADE)
|
tool = models.ForeignKey(Tool, on_delete=models.CASCADE)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = (("group", "tool"),)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.group}-{self.tool}, {super().__str__()}"
|
|
||||||
|
|
||||||
def get_task_subscriptions(self):
|
def get_task_subscriptions(self):
|
||||||
for task in self.tool.task_set.all():
|
for task in self.tool.task_set.all():
|
||||||
yield GroupTaskSubscription(
|
yield GroupTaskSubscription(
|
||||||
days_before=self.days_before, group=self.group, task=task
|
days_before=self.days_before, group=self.group, task=task
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (("group", "tool"),)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.group}-{self.tool}, {super().__str__()}"
|
||||||
|
|
||||||
|
|
||||||
class GroupTaskSubscription(SubscriptionSettings):
|
class GroupTaskSubscription(SubscriptionSettings):
|
||||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||||
@ -110,9 +110,6 @@ class GroupTaskSubscription(SubscriptionSettings):
|
|||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (("group", "task"),)
|
unique_together = (("group", "task"),)
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.group}-{self.task}, {super().__str__()}"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_remind(self):
|
def should_remind(self):
|
||||||
next_recurrence = self.task.next_recurrence
|
next_recurrence = self.task.next_recurrence
|
||||||
@ -121,6 +118,9 @@ class GroupTaskSubscription(SubscriptionSettings):
|
|||||||
time_until_overdue = next_recurrence - datetime.datetime.now()
|
time_until_overdue = next_recurrence - datetime.datetime.now()
|
||||||
return self.task.is_overdue or (time_until_overdue.days <= self.days_before)
|
return self.task.is_overdue or (time_until_overdue.days <= self.days_before)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.group}-{self.task}, {super().__str__()}"
|
||||||
|
|
||||||
|
|
||||||
class Event(models.Model):
|
class Event(models.Model):
|
||||||
task = models.ForeignKey(Task, on_delete=models.CASCADE)
|
task = models.ForeignKey(Task, on_delete=models.CASCADE)
|
||||||
@ -128,9 +128,9 @@ class Event(models.Model):
|
|||||||
date = models.DateField()
|
date = models.DateField()
|
||||||
notes = MarkdownxField(blank=True)
|
notes = MarkdownxField(blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.task}: {self.user} {self.date}"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def notes_html(self):
|
def notes_html(self):
|
||||||
return markdown_to_clean_html(self.notes)
|
return markdown_to_clean_html(self.notes)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.task}: {self.user} {self.date}"
|
||||||
|
Loading…
Reference in New Issue
Block a user