Compare commits
No commits in common. "40eca22eb8332e4cc8b54ee5e8ea634464465126" and "b1d3f164591d25e1d19ecaa78020560bf79fa0b9" have entirely different histories.
40eca22eb8
...
b1d3f16459
@ -1,5 +1,4 @@
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from django.core import validators
|
||||
@ -304,15 +303,8 @@ class Dev(NonCIBase):
|
||||
DEBUG = values.BooleanValue(True)
|
||||
INTERNAL_IPS = ["127.0.0.1"]
|
||||
|
||||
INSTALLED_APPS = NonCIBase.INSTALLED_APPS + ["django_extensions"]
|
||||
|
||||
# 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",
|
||||
]
|
||||
|
||||
INSTALLED_APPS = NonCIBase.INSTALLED_APPS + ["debug_toolbar", "django_extensions"]
|
||||
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + Base.MIDDLEWARE
|
||||
EMAIL = values.EmailURLValue("smtp://localhost:1025") # for local `mailpit`
|
||||
|
||||
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",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def controller(self) -> DoorController:
|
||||
return DoorController(
|
||||
@ -33,6 +30,9 @@ class Door(models.Model):
|
||||
settings.HID_DOOR_PASSWORD,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@cached_property
|
||||
def card_formats(self):
|
||||
return self.controller.get_cardFormats()
|
||||
@ -149,6 +149,8 @@ class HIDEventQuerySet(models.QuerySet):
|
||||
|
||||
|
||||
class HIDEvent(models.Model):
|
||||
objects = HIDEventQuerySet.as_manager()
|
||||
|
||||
class EventType(models.IntegerChoices):
|
||||
DENIED_ACCESS_CARD_NOT_FOUND = 1022, "Denied Access: Card 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,
|
||||
)
|
||||
|
||||
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
|
||||
def from_xml_attributes(cls, door: Door, attrib: dict[str, str]):
|
||||
field_lookup = {
|
||||
@ -301,6 +289,9 @@ class HIDEvent(models.Model):
|
||||
|
||||
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:
|
||||
"""Requires annotations from `with_decoded_card_number`"""
|
||||
if self.raw_card_number is None:
|
||||
@ -312,3 +303,12 @@ class HIDEvent(models.Model):
|
||||
return "Invalid"
|
||||
else:
|
||||
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 logging
|
||||
from datetime import timedelta
|
||||
from typing import TypedDict
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from django_q.tasks import async_task
|
||||
|
||||
from cmsmanage.django_q2_helper import q_task_group
|
||||
@ -65,17 +62,6 @@ class DoorMember:
|
||||
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())
|
||||
|
||||
return cls(
|
||||
|
@ -1,18 +1,8 @@
|
||||
import logging
|
||||
|
||||
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):
|
||||
def handle(self, *args, verbosity: int, **options):
|
||||
verbosity_levels = {
|
||||
0: logging.ERROR,
|
||||
1: logging.WARNING,
|
||||
2: logging.INFO,
|
||||
3: logging.DEBUG,
|
||||
}
|
||||
logger.setLevel(verbosity_levels.get(verbosity, logging.WARNING))
|
||||
|
||||
def handle(self, *args, **options):
|
||||
scrape_events()
|
||||
|
@ -88,13 +88,13 @@ class Flag(BaseModel):
|
||||
name = models.TextField(null=True, blank=True)
|
||||
type = models.CharField(max_length=6)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.type})"
|
||||
|
||||
class Meta:
|
||||
db_table = "flag"
|
||||
ordering = ("name",)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.type})"
|
||||
|
||||
|
||||
class MemberQuerySet(models.QuerySet):
|
||||
# TODO: maybe rename to reflect EXISTS?
|
||||
@ -122,6 +122,8 @@ class MemberQuerySet(models.QuerySet):
|
||||
|
||||
# TODO: is this still a temporal table?
|
||||
class Member(BaseModel):
|
||||
objects = MemberQuerySet.as_manager()
|
||||
|
||||
uid = models.CharField(max_length=24, primary_key=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)
|
||||
@ -262,7 +264,8 @@ class Member(BaseModel):
|
||||
"Waiver form signed and on file date.": "%m/%d/%Y",
|
||||
}
|
||||
|
||||
objects = MemberQuerySet.as_manager()
|
||||
def __str__(self):
|
||||
return f"{self.account_name}"
|
||||
|
||||
class Meta:
|
||||
db_table = "members"
|
||||
@ -273,9 +276,6 @@ class Member(BaseModel):
|
||||
models.Index(fields=["last_name"], name="last_name_idx"),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.account_name}"
|
||||
|
||||
@classmethod
|
||||
def from_user(cls, user) -> "Member | None":
|
||||
if hasattr(user, "ldap_user"):
|
||||
@ -304,6 +304,9 @@ class MemberFlag(BaseModel):
|
||||
)
|
||||
flag = models.ForeignKey(Flag, on_delete=models.PROTECT)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.member} - {self.flag}"
|
||||
|
||||
class Meta:
|
||||
db_table = "memberflag"
|
||||
constraints = [
|
||||
@ -312,9 +315,6 @@ class MemberFlag(BaseModel):
|
||||
)
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.member} - {self.flag}"
|
||||
|
||||
|
||||
class Transaction(BaseModel):
|
||||
sid = models.CharField(max_length=256, null=True, blank=True)
|
||||
@ -364,24 +364,24 @@ class Transaction(BaseModel):
|
||||
"_dp": None,
|
||||
}
|
||||
|
||||
class Meta:
|
||||
db_table = "transactions"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.type} [{self.member if self.member else self.name}] {self.timestamp}"
|
||||
|
||||
class Meta:
|
||||
db_table = "transactions"
|
||||
|
||||
|
||||
class EventCategory(models.Model):
|
||||
id = models.IntegerField(primary_key=True)
|
||||
title = models.TextField()
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
@classmethod
|
||||
def from_api_dict(cls, id: int, data):
|
||||
return cls(id=id, title=data["ttl"])
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Event(BaseModel):
|
||||
class EventCalendar(models.IntegerChoices):
|
||||
@ -433,13 +433,13 @@ class Event(BaseModel):
|
||||
|
||||
_allowed_missing_fields = ["cap", "edp", "adn"]
|
||||
|
||||
def __str__(self):
|
||||
return self.unescaped_title
|
||||
|
||||
@property
|
||||
def unescaped_title(self):
|
||||
return nh3.clean(self.title, tags=set())
|
||||
|
||||
def __str__(self):
|
||||
return self.unescaped_title
|
||||
|
||||
|
||||
class EventInstructor(models.Model):
|
||||
name = models.TextField(blank=True)
|
||||
@ -526,6 +526,8 @@ class EventExtManager(models.Manager):
|
||||
class EventExt(Event):
|
||||
"""Extension of `Event` to capture some fields not supported in MembershipWorks"""
|
||||
|
||||
objects = EventExtManager.from_queryset(EventExtQuerySet)()
|
||||
|
||||
instructor = models.ForeignKey(
|
||||
EventInstructor, on_delete=models.PROTECT, null=True, blank=True
|
||||
)
|
||||
@ -555,12 +557,6 @@ class EventExt(Event):
|
||||
should_survey = 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:
|
||||
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
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "event"
|
||||
ordering = ["-start"]
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@ -633,9 +633,6 @@ class EventMeetingTime(models.Model):
|
||||
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):
|
||||
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
|
@ -20,14 +20,14 @@ class AbstractAudit(models.Model):
|
||||
good = models.BooleanField(default=False)
|
||||
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:
|
||||
abstract = True
|
||||
ordering = ["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):
|
||||
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
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = "CMS Red River Veterans Scholarship"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.program_name} {self.member_name}"
|
||||
|
||||
class Meta:
|
||||
db_table = "CMS Red River Veterans Scholarship"
|
||||
|
||||
|
||||
class DepartmentQuerySet(models.QuerySet):
|
||||
def filter_by_shop_lead(self, member: Member) -> models.QuerySet["Department"]:
|
||||
@ -80,6 +80,8 @@ class DepartmentQuerySet(models.QuerySet):
|
||||
|
||||
|
||||
class Department(models.Model):
|
||||
objects = DepartmentQuerySet.as_manager()
|
||||
|
||||
name = models.CharField(
|
||||
max_length=64,
|
||||
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)
|
||||
|
||||
objects = DepartmentQuerySet.as_manager()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
@ -123,13 +123,13 @@ class CertificationDefinition(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
department = models.ForeignKey(Department, models.PROTECT)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name} <{self.department}>"
|
||||
|
||||
class Meta:
|
||||
db_table = "Certification Definitions"
|
||||
ordering = ("name", "department")
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name} <{self.department}>"
|
||||
|
||||
def latest_version(self) -> "CertificationVersion":
|
||||
return self.certificationversion_set.latest()
|
||||
|
||||
@ -157,6 +157,8 @@ class CertificationVersionManager(models.Manager["CertificationVersion"]):
|
||||
|
||||
|
||||
class CertificationVersion(models.Model):
|
||||
objects = CertificationVersionManager()
|
||||
|
||||
definition = models.ForeignKey(CertificationDefinition, on_delete=models.PROTECT)
|
||||
major = models.PositiveSmallIntegerField()
|
||||
minor = models.PositiveSmallIntegerField()
|
||||
@ -164,7 +166,8 @@ class CertificationVersion(models.Model):
|
||||
prerelease = models.CharField(max_length=255, blank=True)
|
||||
approval_date = models.DateField(blank=True, null=True)
|
||||
|
||||
objects = CertificationVersionManager()
|
||||
def __str__(self) -> str:
|
||||
return f"{self.definition} [{self.semantic_version()}]"
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
@ -184,9 +187,6 @@ class CertificationVersion(models.Model):
|
||||
get_latest_by = ("major", "minor", "patch", "prerelease", "approval_date")
|
||||
base_manager_name = "objects"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.definition} [{self.semantic_version()}]"
|
||||
|
||||
def semantic_version(self) -> VersionInfo:
|
||||
return VersionInfo(
|
||||
self.major or 0,
|
||||
@ -224,6 +224,9 @@ class Certification(models.Model):
|
||||
shop_lead_notified = models.DateTimeField(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:
|
||||
db_table = "Certifications"
|
||||
permissions = [
|
||||
@ -233,9 +236,6 @@ class Certification(models.Model):
|
||||
),
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name} - {self.certification_version}"
|
||||
|
||||
|
||||
class CertificationAudit(AbstractAudit):
|
||||
certification = models.ForeignKey(
|
||||
@ -255,12 +255,12 @@ class InstructorOrVendor(models.Model):
|
||||
db_column="email address", max_length=255, blank=True, null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = "Instructors and Vendors"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name}"
|
||||
|
||||
class Meta:
|
||||
db_table = "Instructors and Vendors"
|
||||
|
||||
|
||||
class SpecialProgram(models.Model):
|
||||
program_name = models.CharField(
|
||||
@ -292,12 +292,12 @@ class SpecialProgram(models.Model):
|
||||
db_column="Program Status", max_length=16, blank=True, null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = "Special_Programs"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.program_name
|
||||
|
||||
class Meta:
|
||||
db_table = "Special_Programs"
|
||||
|
||||
|
||||
class Waiver(models.Model):
|
||||
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)
|
||||
notes = models.CharField(max_length=255, blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "Waivers"
|
||||
|
||||
def __str__(self) -> str:
|
||||
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")
|
||||
|
146
pdm.lock
146
pdm.lock
@ -5,7 +5,7 @@
|
||||
groups = ["default", "debug", "lint", "server", "typing", "dev"]
|
||||
strategy = ["cross_platform"]
|
||||
lock_version = "4.4.1"
|
||||
content_hash = "sha256:b32d3518b2ef3d797f260f44adff417959def44b75fb60134fcdd93351c92581"
|
||||
content_hash = "sha256:8b3bb37d6fbada119262035c0e94ada756c2f43ca8863b50b51f47a232ee84b9"
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
@ -224,15 +224,15 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "bitstring"
|
||||
version = "4.2.3"
|
||||
version = "4.2.2"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Simple construction, analysis and modification of binary data."
|
||||
dependencies = [
|
||||
"bitarray<3.0.0,>=2.9.0",
|
||||
]
|
||||
files = [
|
||||
{file = "bitstring-4.2.3-py3-none-any.whl", hash = "sha256:20ed0036e2fcf0323acb0f92f0b7b178516a080f3e91061470aa019ac4ede404"},
|
||||
{file = "bitstring-4.2.3.tar.gz", hash = "sha256:e0c447af3fda0d114f77b88c2d199f02f97ee7e957e6d719f40f41cf15fbb897"},
|
||||
{file = "bitstring-4.2.2-py3-none-any.whl", hash = "sha256:8b784373e78e953879c8192589e1ecbcda8f111841633a2aaf88d2b073fd6c35"},
|
||||
{file = "bitstring-4.2.2.tar.gz", hash = "sha256:b40b01d911eebaea6efff40d826580806dced5e04b9d3cbad6aebf9422f4b643"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -578,29 +578,29 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "django-db-views"
|
||||
version = "0.1.8"
|
||||
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"
|
||||
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"
|
||||
dependencies = [
|
||||
"django>=2.2",
|
||||
"Django",
|
||||
"six",
|
||||
]
|
||||
files = [
|
||||
{file = "django_db_views-0.1.8-py2.py3-none-any.whl", hash = "sha256:089c9f193c265f956c0f06d0695b54ff57343bbb25f6c5d546f816e7cb9b456c"},
|
||||
{file = "django_db_views-0.1.8.tar.gz", hash = "sha256:b737b01b782d859a90f4d579147a5fa34f4b5376606443abe35c5fdc00313bcc"},
|
||||
{file = "django-db-views-0.1.7.tar.gz", hash = "sha256:7c0dc78aa5f53cc4eefc4d880450a8cb61bb8376b5494356e20383f71a3e1657"},
|
||||
{file = "django_db_views-0.1.7-py3-none-any.whl", hash = "sha256:ffa399af1678e60f532f8c2b531927e94e8249e1012a83fc865234384419bff8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-debug-toolbar"
|
||||
version = "4.4.2"
|
||||
version = "4.3.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "A configurable set of panels that display various debug information about the current request/response."
|
||||
dependencies = [
|
||||
"django>=4.2.9",
|
||||
"django>=3.2.4",
|
||||
"sqlparse>=0.2",
|
||||
]
|
||||
files = [
|
||||
{file = "django_debug_toolbar-4.4.2-py3-none-any.whl", hash = "sha256:5d7afb2ea5f8730241e5b0735396e16cd1fd8c6b53a2f3e1e30bbab9abb23728"},
|
||||
{file = "django_debug_toolbar-4.4.2.tar.gz", hash = "sha256:9204050fcb1e4f74216c5b024bc76081451926a6303993d6c513f5e142675927"},
|
||||
{file = "django_debug_toolbar-4.3.0-py3-none-any.whl", hash = "sha256:e09b7dcb8417b743234dfc57c95a7c1d1d87a88844abd13b4c5387f807b31bf6"},
|
||||
{file = "django_debug_toolbar-4.3.0.tar.gz", hash = "sha256:0b0dddee5ea29b9cb678593bc0d7a6d76b21d7799cb68e091a2148341a80f3c4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -734,24 +734,24 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "django-stubs"
|
||||
version = "5.0.2"
|
||||
version = "5.0.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Mypy stubs for Django"
|
||||
dependencies = [
|
||||
"asgiref",
|
||||
"django",
|
||||
"django-stubs-ext>=5.0.2",
|
||||
"django-stubs-ext>=5.0.0",
|
||||
"types-PyYAML",
|
||||
"typing-extensions>=4.11.0",
|
||||
"typing-extensions",
|
||||
]
|
||||
files = [
|
||||
{file = "django_stubs-5.0.2-py3-none-any.whl", hash = "sha256:cb0c506cb5c54c64612e4a2ee8d6b913c6178560ec168009fe847c09747c304b"},
|
||||
{file = "django_stubs-5.0.2.tar.gz", hash = "sha256:236bc5606e5607cb968f92b648471f9edaa461a774bc013bf9e6bff8730f6bdf"},
|
||||
{file = "django_stubs-5.0.0-py3-none-any.whl", hash = "sha256:084484cbe16a6d388e80ec687e46f529d67a232f3befaf55c936b3b476be289d"},
|
||||
{file = "django_stubs-5.0.0.tar.gz", hash = "sha256:b8a792bee526d6cab31e197cb414ee7fa218abd931a50948c66a80b3a2548621"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-stubs-ext"
|
||||
version = "5.0.2"
|
||||
version = "5.0.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Monkey-patching and extensions for django-stubs"
|
||||
dependencies = [
|
||||
@ -759,23 +759,23 @@ dependencies = [
|
||||
"typing-extensions",
|
||||
]
|
||||
files = [
|
||||
{file = "django_stubs_ext-5.0.2-py3-none-any.whl", hash = "sha256:8d8efec5a86241266bec94a528fe21258ad90d78c67307f3ae5f36e81de97f12"},
|
||||
{file = "django_stubs_ext-5.0.2.tar.gz", hash = "sha256:409c62585d7f996cef5c760e6e27ea3ff29f961c943747e67519c837422cad32"},
|
||||
{file = "django_stubs_ext-5.0.0-py3-none-any.whl", hash = "sha256:8e1334fdf0c8bff87e25d593b33d4247487338aaed943037826244ff788b56a8"},
|
||||
{file = "django_stubs_ext-5.0.0.tar.gz", hash = "sha256:5bacfbb498a206d5938454222b843d81da79ea8b6fcd1a59003f529e775bc115"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-stubs"
|
||||
version = "5.0.2"
|
||||
version = "5.0.0"
|
||||
extras = ["compatible-mypy"]
|
||||
requires_python = ">=3.8"
|
||||
summary = "Mypy stubs for Django"
|
||||
dependencies = [
|
||||
"django-stubs==5.0.2",
|
||||
"django-stubs==5.0.0",
|
||||
"mypy~=1.10.0",
|
||||
]
|
||||
files = [
|
||||
{file = "django_stubs-5.0.2-py3-none-any.whl", hash = "sha256:cb0c506cb5c54c64612e4a2ee8d6b913c6178560ec168009fe847c09747c304b"},
|
||||
{file = "django_stubs-5.0.2.tar.gz", hash = "sha256:236bc5606e5607cb968f92b648471f9edaa461a774bc013bf9e6bff8730f6bdf"},
|
||||
{file = "django_stubs-5.0.0-py3-none-any.whl", hash = "sha256:084484cbe16a6d388e80ec687e46f529d67a232f3befaf55c936b3b476be289d"},
|
||||
{file = "django_stubs-5.0.0.tar.gz", hash = "sha256:b8a792bee526d6cab31e197cb414ee7fa218abd931a50948c66a80b3a2548621"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1079,7 +1079,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "hypothesis"
|
||||
version = "6.103.1"
|
||||
version = "6.102.4"
|
||||
requires_python = ">=3.8"
|
||||
summary = "A library for property-based testing"
|
||||
dependencies = [
|
||||
@ -1087,23 +1087,23 @@ dependencies = [
|
||||
"sortedcontainers<3.0.0,>=2.1.0",
|
||||
]
|
||||
files = [
|
||||
{file = "hypothesis-6.103.1-py3-none-any.whl", hash = "sha256:d3c959fab6233e78867499e2117ae9db8dc40eeed936d71a2cfc7b6094972e74"},
|
||||
{file = "hypothesis-6.103.1.tar.gz", hash = "sha256:d299d5c21d6408eab3be670c94c974f3acf0b511c61fe81804b09091e393ee1f"},
|
||||
{file = "hypothesis-6.102.4-py3-none-any.whl", hash = "sha256:013df31b04a4daede13756f497e60e451963d86f426395a79f99c5d692919bbd"},
|
||||
{file = "hypothesis-6.102.4.tar.gz", hash = "sha256:59b4d144346d5cffb482cc1bafbd21b13ff31608e8c4b3e4630339aee3e87763"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hypothesis"
|
||||
version = "6.103.1"
|
||||
version = "6.102.4"
|
||||
extras = ["django"]
|
||||
requires_python = ">=3.8"
|
||||
summary = "A library for property-based testing"
|
||||
dependencies = [
|
||||
"django>=3.2",
|
||||
"hypothesis==6.103.1",
|
||||
"hypothesis==6.102.4",
|
||||
]
|
||||
files = [
|
||||
{file = "hypothesis-6.103.1-py3-none-any.whl", hash = "sha256:d3c959fab6233e78867499e2117ae9db8dc40eeed936d71a2cfc7b6094972e74"},
|
||||
{file = "hypothesis-6.103.1.tar.gz", hash = "sha256:d299d5c21d6408eab3be670c94c974f3acf0b511c61fe81804b09091e393ee1f"},
|
||||
{file = "hypothesis-6.102.4-py3-none-any.whl", hash = "sha256:013df31b04a4daede13756f497e60e451963d86f426395a79f99c5d692919bbd"},
|
||||
{file = "hypothesis-6.102.4.tar.gz", hash = "sha256:59b4d144346d5cffb482cc1bafbd21b13ff31608e8c4b3e4630339aee3e87763"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1118,7 +1118,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "ipython"
|
||||
version = "8.25.0"
|
||||
version = "8.24.0"
|
||||
requires_python = ">=3.10"
|
||||
summary = "IPython: Productive Interactive Computing"
|
||||
dependencies = [
|
||||
@ -1134,8 +1134,8 @@ dependencies = [
|
||||
"typing-extensions>=4.6; python_version < \"3.12\"",
|
||||
]
|
||||
files = [
|
||||
{file = "ipython-8.25.0-py3-none-any.whl", hash = "sha256:53eee7ad44df903a06655871cbab66d156a051fd86f3ec6750470ac9604ac1ab"},
|
||||
{file = "ipython-8.25.0.tar.gz", hash = "sha256:c6ed726a140b6e725b911528f80439c534fac915246af3efc39440a6b0f9d716"},
|
||||
{file = "ipython-8.24.0-py3-none-any.whl", hash = "sha256:d7bf2f6c4314984e3e02393213bab8703cf163ede39672ce5918c51fe253a2a3"},
|
||||
{file = "ipython-8.24.0.tar.gz", hash = "sha256:010db3f8a728a578bb641fdd06c063b9fb8e96a9464c63aec6310fbcb5e80501"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1817,8 +1817,8 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.3"
|
||||
requires_python = ">=3.8"
|
||||
version = "2.31.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Python HTTP for Humans."
|
||||
dependencies = [
|
||||
"certifi>=2017.4.17",
|
||||
@ -1827,33 +1827,33 @@ dependencies = [
|
||||
"urllib3<3,>=1.21.1",
|
||||
]
|
||||
files = [
|
||||
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
|
||||
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
|
||||
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
|
||||
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.4.8"
|
||||
version = "0.4.4"
|
||||
requires_python = ">=3.7"
|
||||
summary = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
files = [
|
||||
{file = "ruff-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066"},
|
||||
{file = "ruff-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b"},
|
||||
{file = "ruff-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45"},
|
||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a"},
|
||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc"},
|
||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed"},
|
||||
{file = "ruff-0.4.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa"},
|
||||
{file = "ruff-0.4.8-py3-none-win32.whl", hash = "sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9"},
|
||||
{file = "ruff-0.4.8-py3-none-win_amd64.whl", hash = "sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d"},
|
||||
{file = "ruff-0.4.8-py3-none-win_arm64.whl", hash = "sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780"},
|
||||
{file = "ruff-0.4.8.tar.gz", hash = "sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268"},
|
||||
{file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"},
|
||||
{file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"},
|
||||
{file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"},
|
||||
{file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"},
|
||||
{file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"},
|
||||
{file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"},
|
||||
{file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"},
|
||||
{file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"},
|
||||
{file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"},
|
||||
{file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1868,12 +1868,12 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "70.0.0"
|
||||
version = "69.5.1"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
files = [
|
||||
{file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"},
|
||||
{file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"},
|
||||
{file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"},
|
||||
{file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2117,15 +2117,15 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "types-requests"
|
||||
version = "2.32.0.20240602"
|
||||
version = "2.31.0.20240406"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Typing stubs for requests"
|
||||
dependencies = [
|
||||
"urllib3>=2",
|
||||
]
|
||||
files = [
|
||||
{file = "types-requests-2.32.0.20240602.tar.gz", hash = "sha256:3f98d7bbd0dd94ebd10ff43a7fbe20c3b8528acace6d8efafef0b6a184793f06"},
|
||||
{file = "types_requests-2.32.0.20240602-py3-none-any.whl", hash = "sha256:ed3946063ea9fbc6b5fc0c44fa279188bae42d582cb63760be6cb4b9d06c3de8"},
|
||||
{file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"},
|
||||
{file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2196,7 +2196,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.30.1"
|
||||
version = "0.29.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "The lightning-fast ASGI server."
|
||||
dependencies = [
|
||||
@ -2204,13 +2204,13 @@ dependencies = [
|
||||
"h11>=0.8",
|
||||
]
|
||||
files = [
|
||||
{file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"},
|
||||
{file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"},
|
||||
{file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"},
|
||||
{file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.30.1"
|
||||
version = "0.29.0"
|
||||
extras = ["standard"]
|
||||
requires_python = ">=3.8"
|
||||
summary = "The lightning-fast ASGI server."
|
||||
@ -2219,14 +2219,14 @@ dependencies = [
|
||||
"httptools>=0.5.0",
|
||||
"python-dotenv>=0.13",
|
||||
"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\"",
|
||||
"watchfiles>=0.13",
|
||||
"websockets>=10.4",
|
||||
]
|
||||
files = [
|
||||
{file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"},
|
||||
{file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"},
|
||||
{file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"},
|
||||
{file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2311,7 +2311,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "weasyprint"
|
||||
version = "62.2"
|
||||
version = "62.1"
|
||||
requires_python = ">=3.9"
|
||||
summary = "The Awesome Document Factory"
|
||||
dependencies = [
|
||||
@ -2325,8 +2325,8 @@ dependencies = [
|
||||
"tinycss2>=1.3.0",
|
||||
]
|
||||
files = [
|
||||
{file = "weasyprint-62.2-py3-none-any.whl", hash = "sha256:6fd84e9f55ac239c5657845eae117fd43916c3c5986fe98f69ea13fdab8ec9ad"},
|
||||
{file = "weasyprint-62.2.tar.gz", hash = "sha256:a08ac400e11919d996d76becaa33160d7c1ac55ba160628c42ce7586574c1a51"},
|
||||
{file = "weasyprint-62.1-py3-none-any.whl", hash = "sha256:654d4c266336cbf9acc4da118c7778ef5839717e6055d5b8f995cf50be200c46"},
|
||||
{file = "weasyprint-62.1.tar.gz", hash = "sha256:bf3c1a9ac4194271a7cf117229c093744105b50ac2fa64c0a6e44e68ae742992"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -18,8 +18,8 @@ dependencies = [
|
||||
"mdformat-tables~=0.4",
|
||||
"mysqlclient~=2.2",
|
||||
"django-autocomplete-light~=3.11",
|
||||
"weasyprint~=62.2",
|
||||
"requests~=2.32",
|
||||
"weasyprint~=62.1",
|
||||
"requests~=2.31",
|
||||
"semver~=3.0",
|
||||
"djangorestframework~=3.15",
|
||||
"django-q2~=1.6",
|
||||
@ -44,8 +44,8 @@ requires-python = ">=3.11"
|
||||
|
||||
[project.optional-dependencies]
|
||||
server = [
|
||||
"uvicorn[standard]~=0.30",
|
||||
"setuptools~=70.0",
|
||||
"uvicorn[standard]~=0.29",
|
||||
"setuptools~=69.5",
|
||||
]
|
||||
|
||||
[project.entry-points."djangoq.errorreporters"]
|
||||
@ -55,7 +55,7 @@ admin_email = "cmsmanage.django_q2_admin_email_reporter:AdminEmailReporter"
|
||||
line-length = 88
|
||||
|
||||
[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]
|
||||
known-first-party = [
|
||||
@ -118,9 +118,9 @@ lint = [
|
||||
typing = [
|
||||
"mypy~=1.10",
|
||||
"django-stubs~=5.0",
|
||||
"setuptools~=70.0",
|
||||
"setuptools~=69.5",
|
||||
"types-bleach~=6.1",
|
||||
"types-requests~=2.32",
|
||||
"types-requests~=2.31",
|
||||
"types-urllib3~=1.26",
|
||||
"djangorestframework-stubs[compatible-mypy]~=3.15",
|
||||
"types-Markdown~=3.6",
|
||||
@ -129,12 +129,12 @@ typing = [
|
||||
"types-lxml~=2024.4",
|
||||
]
|
||||
debug = [
|
||||
"django-debug-toolbar~=4.4",
|
||||
"django-debug-toolbar~=4.3",
|
||||
]
|
||||
dev = [
|
||||
"django-extensions~=3.2",
|
||||
"ipython~=8.25",
|
||||
"hypothesis[django]~=6.103",
|
||||
"ipython~=8.24",
|
||||
"hypothesis[django]~=6.102",
|
||||
"tblib~=3.0",
|
||||
]
|
||||
|
||||
|
@ -33,6 +33,17 @@ class LockerUnit(models.Model):
|
||||
rows = models.PositiveIntegerField(default=5)
|
||||
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:
|
||||
# TODO: add constraint to check for letter overlaps
|
||||
constraints = [
|
||||
@ -45,17 +56,6 @@ class LockerUnit(models.Model):
|
||||
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})"
|
||||
|
||||
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:
|
||||
return chr(column + ord(self.first_letter))
|
||||
|
||||
@ -91,6 +91,10 @@ class LockerInfo(models.Model):
|
||||
)
|
||||
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:
|
||||
constraints = [
|
||||
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
|
||||
def available(self) -> bool:
|
||||
return self.renter is None and not self.reserved
|
||||
@ -135,3 +132,6 @@ class LockerInfo(models.Model):
|
||||
)
|
||||
def address(self) -> str:
|
||||
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)
|
||||
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):
|
||||
for task in self.tool.task_set.all():
|
||||
yield GroupTaskSubscription(
|
||||
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):
|
||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
@ -110,9 +110,6 @@ class GroupTaskSubscription(SubscriptionSettings):
|
||||
class Meta:
|
||||
unique_together = (("group", "task"),)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.group}-{self.task}, {super().__str__()}"
|
||||
|
||||
@property
|
||||
def should_remind(self):
|
||||
next_recurrence = self.task.next_recurrence
|
||||
@ -121,6 +118,9 @@ class GroupTaskSubscription(SubscriptionSettings):
|
||||
time_until_overdue = next_recurrence - datetime.datetime.now()
|
||||
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):
|
||||
task = models.ForeignKey(Task, on_delete=models.CASCADE)
|
||||
@ -128,9 +128,9 @@ class Event(models.Model):
|
||||
date = models.DateField()
|
||||
notes = MarkdownxField(blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.task}: {self.user} {self.date}"
|
||||
|
||||
@property
|
||||
def notes_html(self):
|
||||
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