Compare commits

..

No commits in common. "eec1420ee94e14bc120d1b5ab615b28afa225e67" and "19d8e888f42e2cc88b9a9942c5f20551425fe055" have entirely different histories.

16 changed files with 171 additions and 286 deletions

View File

@ -11,7 +11,6 @@ repos:
rev: v1.34.1 rev: v1.34.1
hooks: hooks:
- id: djlint-django - id: djlint-django
- id: djlint-reformat-django
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.13 rev: v0.1.13

View File

@ -39,7 +39,6 @@ INSTALLED_APPS = [
"django_q", "django_q",
"django_nh3", "django_nh3",
"django_tables2", "django_tables2",
"django_filters",
"tasks.apps.TasksConfig", "tasks.apps.TasksConfig",
"rentals.apps.RentalsConfig", "rentals.apps.RentalsConfig",
"membershipworks.apps.MembershipworksConfig", "membershipworks.apps.MembershipworksConfig",

View File

@ -3,7 +3,6 @@
{% load render_table from django_tables2 %} {% load render_table from django_tables2 %}
{% block title %}{{ selected_report }} | Door Controls | CMS{% endblock %} {% block title %}{{ selected_report }} | Door Controls | CMS{% endblock %}
{% block content %} {% block content %}
<div class="vstack align-items-center"> <div class="vstack align-items-center">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
@ -14,30 +13,28 @@
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
<form method="get" class="container-fluid"> <form method="get" class="container-fluid">
<div class="row g-2 align-items-center justify-content-center"> <div class="row g-2 align-items-center justify-content-center">
<div class="col-auto"> <div class="col-6 col-sm-auto">
<div class="input-group"> <div class="form-floating">
<div class="form-floating"> <input type="date"
<input type="date" class="form-control"
class="form-control" id="startDate"
id="startDate" name="timestamp__gte"
name="timestamp_after" value="{{ timestamp__gte|date:'Y-m-d' }}">
value="{{ filter.form.timestamp.value.0 }}"> <label for="startDate">Start Date</label>
<label for="startDate">Start Date</label> </div>
</div> </div>
<div class="form-floating"> <div class="col-6 col-sm-auto">
<input type="date" <div class="form-floating">
class="form-control" <input type="date"
id="endDate" class="form-control"
name="timestamp_before" id="endDate"
value="{{ filter.form.timestamp.value.1 }}"> name="timestamp__lte"
<label for="endDate">End Date</label> value="{{ timestamp__lte|date:'Y-m-d' }}">
</div> <label for="endDate">End Date</label>
</div> </div>
</div> </div>
<div class="col-12 col-sm-auto"> <div class="col-12 col-sm-auto">
<div class="form-floating"> <div class="form-floating">
<input type="number" <input type="number"
@ -52,16 +49,15 @@
<label for="itemsPerPage">Items Per Page</label> <label for="itemsPerPage">Items Per Page</label>
</div> </div>
</div> </div>
<div class="btn-group col-auto" role="group" aria-label="Form Controls"> <div class="btn-group col-auto" role="group" aria-label="Form Controls">
<button type="submit" class="btn btn-sm btn-primary">Submit</button> <button type="submit" class="btn btn-sm btn-primary">Submit</button>
<a href="?" class="btn btn-sm btn-warning">Reset</a> <a href="?" class="btn btn-sm btn-warning">Reset</a>
</div> </div>
<div class="col-auto">
<div class="col-auto">{% include "cmsmanage/components/download_table.dj.html" %}</div> {% include "cmsmanage/components/download_table.dj.html" %}
</div>
</div> </div>
</form> </form>
{% render_table table %} {% render_table table %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -7,12 +7,12 @@ from django.core.paginator import Page
from django.db.models import Count, F, FloatField, Window from django.db.models import Count, F, FloatField, Window
from django.db.models.functions import Lead, Trunc from django.db.models.functions import Lead, Trunc
from django.urls import path, reverse_lazy from django.urls import path, reverse_lazy
from django.utils import dateparse
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.timezone import localtime
from django.views.generic.list import ListView from django.views.generic.list import ListView
import django_filters
import django_tables2 as tables import django_tables2 as tables
from django_filters.views import BaseFilterView
from django_tables2 import SingleTableMixin from django_tables2 import SingleTableMixin
from django_tables2.export.views import ExportMixin from django_tables2.export.views import ExportMixin
@ -26,12 +26,8 @@ def register_report(cls: "BaseAccessReport"):
return cls return cls
class DateTimeFilters(django_filters.FilterSet):
timestamp = django_filters.DateFromToRangeFilter()
class BaseAccessReport( class BaseAccessReport(
BaseFilterView, ExportMixin, SingleTableMixin, PermissionRequiredMixin, ListView ExportMixin, SingleTableMixin, PermissionRequiredMixin, ListView
): ):
model = HIDEvent model = HIDEvent
permission_required = "doorcontrol.view_hidevent" permission_required = "doorcontrol.view_hidevent"
@ -41,8 +37,6 @@ class BaseAccessReport(
export_formats = ("csv", "xlsx", "ods") export_formats = ("csv", "xlsx", "ods")
filterset_class = DateTimeFilters
_report_name = None _report_name = None
@classmethod @classmethod
@ -64,13 +58,30 @@ class BaseAccessReport(
def _selected_report(self): def _selected_report(self):
return self._report_name return self._report_name
def _get_timestamp_range(self):
timestamp__gte = dateparse.parse_datetime(
self.request.GET.get("timestamp__gte") or "2019-01-01"
)
timestamp__lte = self.request.GET.get("timestamp__lte")
if timestamp__lte:
timestamp__lte = dateparse.parse_datetime(timestamp__lte)
else:
timestamp__lte = localtime()
return timestamp__gte, timestamp__lte
def get_paginate_by(self, queryset) -> int: def get_paginate_by(self, queryset) -> int:
if "items_per_page" in self.request.GET: if "items_per_page" in self.request.GET:
return int(self.request.GET.get("items_per_page")) return int(self.request.GET.get("items_per_page"))
return super().get_paginate_by(queryset) return super().get_paginate_by(queryset)
def get_queryset(self): def get_queryset(self):
return super().get_queryset().select_related("door") return (
super()
.get_queryset()
.filter(timestamp__range=self._get_timestamp_range())
.select_related("door")
)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -83,6 +94,10 @@ class BaseAccessReport(
context["selected_report"] = self._selected_report() context["selected_report"] = self._selected_report()
context["items_per_page"] = self.get_paginate_by(None) context["items_per_page"] = self.get_paginate_by(None)
timestamp__gte, timestamp__lte = self._get_timestamp_range()
context["timestamp__gte"] = timestamp__gte
context["timestamp__lte"] = timestamp__lte
query_params = self.request.GET.copy() query_params = self.request.GET.copy()
if "page" in query_params: if "page" in query_params:
query_params.pop("page") query_params.pop("page")

View File

@ -2,11 +2,7 @@ from django.contrib import admin
from django.contrib.humanize.templatetags.humanize import naturaltime from django.contrib.humanize.templatetags.humanize import naturaltime
from django.utils.html import format_html from django.utils.html import format_html
from django_object_actions import ( from django_object_actions import DjangoObjectActions, action
DjangoObjectActions,
action,
takes_instance_or_queryset,
)
from django_q.models import Task from django_q.models import Task
from django_q.tasks import async_task from django_q.tasks import async_task
@ -19,10 +15,7 @@ from .models import (
Member, Member,
Transaction, Transaction,
) )
from .tasks.scrape import ( from .tasks.scrape import scrape_membershipworks
scrape_event_details,
scrape_membershipworks,
)
class ReadOnlyAdmin(admin.ModelAdmin): class ReadOnlyAdmin(admin.ModelAdmin):
@ -107,11 +100,11 @@ class EventMeetingTimeInline(admin.TabularInline):
@admin.register(EventInstructor) @admin.register(EventInstructor)
class EventInstructorAdmin(admin.ModelAdmin): class EventInstructorAdmin(admin.ModelAdmin):
autocomplete_fields = ["member"] autocomplete_fields = ["member"]
search_fields = ["name", "member__account_name"] search_fields = ["name", "member"]
@admin.register(EventExt) @admin.register(EventExt)
class EventAdmin(DjangoObjectActions, admin.ModelAdmin): class EventAdmin(admin.ModelAdmin):
inlines = [EventMeetingTimeInline] inlines = [EventMeetingTimeInline]
list_display = [ list_display = [
"title", "title",
@ -130,10 +123,8 @@ class EventAdmin(DjangoObjectActions, admin.ModelAdmin):
show_facets = admin.ShowFacets.ALWAYS show_facets = admin.ShowFacets.ALWAYS
search_fields = ["eid", "title", "url"] search_fields = ["eid", "title", "url"]
date_hierarchy = "start" date_hierarchy = "start"
exclude = ["url", "details"] exclude = ["url"]
autocomplete_fields = ["instructor"] autocomplete_fields = ["instructor"]
change_actions = ["fetch_details"]
actions = ["fetch_details"]
@property @property
def readonly_fields(self): def readonly_fields(self):
@ -146,7 +137,6 @@ class EventAdmin(DjangoObjectActions, admin.ModelAdmin):
else: else:
fields.append(field.name) fields.append(field.name)
fields.insert(fields.index("end") + 1, "duration") fields.insert(fields.index("end") + 1, "duration")
fields.append("details_timestamp")
return fields return fields
@admin.display(ordering="duration") @admin.display(ordering="duration")
@ -160,10 +150,6 @@ class EventAdmin(DjangoObjectActions, admin.ModelAdmin):
obj.url, obj.url,
) )
@takes_instance_or_queryset
def fetch_details(self, request, queryset):
scrape_event_details(queryset)
def has_add_permission(self, request, obj=None): def has_add_permission(self, request, obj=None):
return False return False

View File

@ -1,16 +0,0 @@
# Generated by Django 5.0.1 on 2024-01-29 19:17
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("membershipworks", "0009_eventext_materials_fee_included_in_price"),
]
operations = [
migrations.AlterModelOptions(
name="eventext",
options={"ordering": ["-start"], "verbose_name": "event"},
),
]

View File

@ -1,29 +0,0 @@
# Generated by Django 5.0.1 on 2024-01-29 19:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("membershipworks", "0010_alter_eventext_options"),
]
operations = [
migrations.AddField(
model_name="eventext",
name="details",
field=models.JSONField(blank=True, null=True),
),
migrations.AddField(
model_name="eventext",
name="details_timestamp",
field=models.GeneratedField(
db_persist=False,
expression=models.Func(
models.Func(models.F("details___ts"), function="FROM_UNIXTIME"),
template="CONVERT_TZ(%(expressions)s, @@session.time_zone, 'UTC')",
),
output_field=models.DateTimeField(),
),
),
]

View File

@ -9,7 +9,6 @@ from django.db.models import (
Exists, Exists,
ExpressionWrapper, ExpressionWrapper,
F, F,
Func,
OuterRef, OuterRef,
Q, Q,
Subquery, Subquery,
@ -439,7 +438,7 @@ class EventExtQuerySet(models.QuerySet["EventExt"]):
return method( return method(
count__sum=Sum("count", filter=F("occurred")), count__sum=Sum("count", filter=F("occurred")),
instructor__count=Count("instructor", distinct=True, filter=F("occurred")), instructor__count=Count("instructor", distinct=True, filter=F("occurred")),
meetings__sum=Sum("meetings", filter=F("occurred")), meeting_times__count__sum=Sum("meeting_times__count", filter=F("occurred")),
duration__sum=Sum("duration", filter=F("occurred")), duration__sum=Sum("duration", filter=F("occurred")),
person_hours__sum=Sum("person_hours", filter=F("occurred")), person_hours__sum=Sum("person_hours", filter=F("occurred")),
event_count=Count("eid", filter=F("occurred")), event_count=Count("eid", filter=F("occurred")),
@ -449,37 +448,32 @@ class EventExtQuerySet(models.QuerySet["EventExt"]):
class EventExtManager(models.Manager["EventExt"]): class EventExtManager(models.Manager["EventExt"]):
def get_queryset(self) -> models.QuerySet["EventExt"]: def get_queryset(self) -> models.QuerySet["EventExt"]:
return ( return EventExtQuerySet(self.model, using=self._db).annotate(
super() meeting_times__count=Subquery(
.get_queryset() EventMeetingTime.objects.filter(event=OuterRef("pk"))
.annotate( .values("event__pk")
meetings=Subquery( .annotate(d=Count("pk"))
EventMeetingTime.objects.filter(event=OuterRef("pk")) .values("d")[:1],
.values("event__pk") output_field=models.IntegerField(),
.annotate(d=Count("pk")) ),
.values("d")[:1], duration=Subquery(
output_field=models.IntegerField(), EventMeetingTime.objects.filter(event=OuterRef("pk"))
), .values("event__pk")
duration=Subquery( .annotate(d=Sum("duration"))
EventMeetingTime.objects.filter(event=OuterRef("pk")) .values("d")[:1],
.values("event__pk") output_field=models.DurationField(),
.annotate(d=Sum("duration")) ),
.values("d")[:1], person_hours=ExpressionWrapper(
output_field=models.DurationField(), ExpressionWrapper(F("duration"), models.IntegerField()) * F("count"),
), models.DurationField(),
person_hours=ExpressionWrapper( ),
ExpressionWrapper(F("duration"), models.IntegerField())
* F("count"),
models.DurationField(),
),
)
) )
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)() objects = EventExtManager()
instructor = models.ForeignKey( instructor = models.ForeignKey(
EventInstructor, on_delete=models.PROTECT, null=True, blank=True EventInstructor, on_delete=models.PROTECT, null=True, blank=True
@ -494,19 +488,9 @@ class EventExt(Event):
instructor_flat_rate = models.DecimalField( instructor_flat_rate = models.DecimalField(
max_digits=13, decimal_places=4, default=0 max_digits=13, decimal_places=4, default=0
) )
details = models.JSONField(null=True, blank=True)
details_timestamp = models.GeneratedField(
expression=Func(
Func(F("details___ts"), function="FROM_UNIXTIME"),
template="CONVERT_TZ(%(expressions)s, @@session.time_zone, 'UTC')",
),
output_field=models.DateTimeField(),
db_persist=False,
)
class Meta: class Meta:
verbose_name = "event" verbose_name = "event"
ordering = ["-start"]
class EventMeetingTime(models.Model): class EventMeetingTime(models.Model):

View File

@ -3,7 +3,6 @@ from datetime import datetime, timedelta
from django.conf import settings from django.conf import settings
from django.db import transaction from django.db import transaction
from django.db.models import QuerySet
from membershipworks.membershipworks_api import FieldType, MembershipWorks from membershipworks.membershipworks_api import FieldType, MembershipWorks
from membershipworks.models import ( from membershipworks.models import (
@ -100,17 +99,6 @@ def scrape_membershipworks(*args, **options):
scrape_transactions(membershipworks) scrape_transactions(membershipworks)
def scrape_event_details(queryset: QuerySet[EventExt]):
membershipworks = MembershipWorks()
membershipworks.login(
settings.MEMBERSHIPWORKS_USERNAME, settings.MEMBERSHIPWORKS_PASSWORD
)
for event in queryset:
event.details = membershipworks.get_event_by_eid(event.eid)
event.save()
def scrape_events(): def scrape_events():
membershipworks = MembershipWorks() membershipworks = MembershipWorks()
membershipworks.login( membershipworks.login(
@ -162,10 +150,3 @@ def scrape_events():
# if there is exactly one meeting time, it should match the event start/end # if there is exactly one meeting time, it should match the event start/end
elif meeting_times_count == 1: elif meeting_times_count == 1:
event_ext.meeting_times.update(start=event_ext.start, end=event_ext.end) event_ext.meeting_times.update(start=event_ext.start, end=event_ext.end)
# event has no details, or last retrieval was before the event happened
if event_ext.details is None or event_ext.details_timestamp < (
event_ext.end or event_ext.start
):
event_ext.details = membershipworks.get_event_by_eid(event.eid)
event_ext.save()

View File

@ -14,9 +14,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% include "cmsmanage/components/download_table.dj.html" %} {% include "cmsmanage/components/download_table.dj.html" %}
{% render_table table %} {% render_table table %}
<nav aria-label="Page navigation"> <nav aria-label="Page navigation">
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
{% if previous_month %} {% if previous_month %}

View File

@ -11,9 +11,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% include "cmsmanage/components/download_table.dj.html" %} {% include "cmsmanage/components/download_table.dj.html" %}
{% render_table table %} {% render_table table %}
<nav aria-label="Page navigation"> <nav aria-label="Page navigation">
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
{% if previous_year %} {% if previous_year %}

View File

@ -15,7 +15,6 @@
class="wp-image-2319" /> class="wp-image-2319" />
</figure> </figure>
<!-- /wp:image --> <!-- /wp:image -->
<!-- wp:paragraph --> <!-- wp:paragraph -->
<p>Greetings Upper Valley Makers:</p> <p>Greetings Upper Valley Makers:</p>
<!-- /wp:paragraph --> <!-- /wp:paragraph -->
@ -42,7 +41,6 @@
<strong>Tours:</strong> Want to see what the Claremont MakerSpace is all about? Tours are by appointment only. <strong>Tours:</strong> Want to see what the Claremont MakerSpace is all about? Tours are by appointment only.
</p> </p>
<!-- /wp:paragraph --> <!-- /wp:paragraph -->
<!-- wp:paragraph --> <!-- wp:paragraph -->
<p> <p>
<a data-wpel-link="external" <a data-wpel-link="external"
@ -51,7 +49,6 @@
rel="noreferrer noopener external">Contact Us</a> to schedule your tour where you can learn about all the awesome tools that the CMS offers access to, as well as how membership, classes, and studio spaces work. rel="noreferrer noopener external">Contact Us</a> to schedule your tour where you can learn about all the awesome tools that the CMS offers access to, as well as how membership, classes, and studio spaces work.
</p> </p>
<!-- /wp:paragraph --> <!-- /wp:paragraph -->
<!-- wp:separator {"className":"is-style-wide"} --> <!-- wp:separator {"className":"is-style-wide"} -->
<hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" /> <hr class="wp-block-separator has-alpha-channel-opacity is-style-wide" />
<!-- /wp:separator --> <!-- /wp:separator -->
@ -65,7 +62,6 @@
<i>{{ section.blurb }}</i> <i>{{ section.blurb }}</i>
</h4> </h4>
<!-- /wp:heading --> <!-- /wp:heading -->
{% for event in section.events %} {% for event in section.events %}
{% with url="https://claremontmakerspace.org/events/#!event/register/"|add:event.url %} {% with url="https://claremontmakerspace.org/events/#!event/register/"|add:event.url %}
<!-- wp:group {"tagName":"section","layout":{"type":"constrained"}} --> <!-- wp:group {"tagName":"section","layout":{"type":"constrained"}} -->
@ -113,7 +109,6 @@
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
<!-- wp:paragraph --> <!-- wp:paragraph -->
<p style="clear: both;">Happy Makin!</p> <p style="clear: both;">Happy Makin!</p>
<!-- /wp:paragraph --> <!-- /wp:paragraph -->

View File

@ -133,7 +133,7 @@ class EventTable(tables.Table):
start = tables.DateColumn("N d, Y") start = tables.DateColumn("N d, Y")
duration = DurationColumn() duration = DurationColumn()
person_hours = DurationColumn() person_hours = DurationColumn()
meetings = tables.Column() meeting_times__count = tables.Column("Meetings")
class Meta: class Meta:
model = EventExt model = EventExt
@ -145,7 +145,7 @@ class EventTable(tables.Table):
"category", "category",
"count", "count",
"cap", "cap",
"meetings", "meeting_times__count",
"duration", "duration",
"person_hours", "person_hours",
) )
@ -161,7 +161,7 @@ class EventSummaryTable(tables.Table):
canceled_event_count = tables.Column("Canceled Events") canceled_event_count = tables.Column("Canceled Events")
count__sum = tables.Column("Tickets") count__sum = tables.Column("Tickets")
instructor__count = tables.Column("Unique Instructors") instructor__count = tables.Column("Unique Instructors")
meetings__sum = tables.Column("Meetings") meeting_times__count__sum = tables.Column("Meetings")
duration__sum = DurationColumn("Class Hours") duration__sum = DurationColumn("Class Hours")
person_hours__sum = DurationColumn("Person Hours") person_hours__sum = DurationColumn("Person Hours")
@ -178,15 +178,6 @@ class EventIndexReport(
export_formats = ("csv", "xlsx", "ods") export_formats = ("csv", "xlsx", "ods")
export_name = "mw_events_index" export_name = "mw_events_index"
def get_table_data(self):
return (
super()
.get_table_data()
.values(year=TruncYear("start"))
.summarize()
.order_by("year")
)
def get_table_kwargs(self): def get_table_kwargs(self):
year_column = tables.DateColumn( year_column = tables.DateColumn(
"Y", "Y",
@ -200,6 +191,15 @@ class EventIndexReport(
"extra_columns": (("year", year_column),), "extra_columns": (("year", year_column),),
} }
def get_dated_queryset(self, **lookup):
return (
super()
.get_dated_queryset(**lookup)
.values(year=TruncYear("start"))
.summarize()
.order_by("year")
)
class EventYearReport( class EventYearReport(
ExportMixin, SingleTableMixin, PermissionRequiredMixin, YearArchiveView ExportMixin, SingleTableMixin, PermissionRequiredMixin, YearArchiveView
@ -212,15 +212,6 @@ class EventYearReport(
table_class = EventSummaryTable table_class = EventSummaryTable
export_formats = ("csv", "xlsx", "ods") export_formats = ("csv", "xlsx", "ods")
def get_table_data(self):
return (
super()
.get_table_data()
.values(month=TruncMonth("start"))
.summarize()
.order_by("month")
)
def get_export_filename(self, export_format): def get_export_filename(self, export_format):
return f"mw_events_{self.get_year()}.{export_format}" return f"mw_events_{self.get_year()}.{export_format}"
@ -237,19 +228,25 @@ class EventYearReport(
"extra_columns": (("month", month_column),), "extra_columns": (("month", month_column),),
} }
def get_dated_queryset(self, **lookup):
return (
super()
.get_dated_queryset(**lookup)
.values(month=TruncMonth("start"))
.summarize()
.order_by("month")
)
class EventMonthReport( class EventMonthReport(
ExportMixin, SingleTableMixin, PermissionRequiredMixin, MonthArchiveView ExportMixin, SingleTableMixin, PermissionRequiredMixin, MonthArchiveView
): ):
permission_required = "membershipworks.view_eventext" permission_required = "membershipworks.view_eventext"
queryset = EventExt.objects.all() queryset = EventExt.objects.select_related("category", "instructor").all()
date_field = "start" date_field = "start"
template_name = "membershipworks/event_month_report.dj.html" template_name = "membershipworks/event_month_report.dj.html"
table_class = EventTable table_class = EventTable
export_formats = ("csv", "xlsx", "ods") export_formats = ("csv", "xlsx", "ods")
def get_table_data(self):
return super().get_table_data().select_related("category", "instructor")
def get_export_filename(self, export_format): def get_export_filename(self, export_format):
return f"mw_events_{self.get_year()}-{self.get_month():02}.{export_format}" return f"mw_events_{self.get_year()}-{self.get_month():02}.{export_format}"

View File

@ -24,8 +24,7 @@ class PaperworkDashboardFragment(dashboard.DashboardFragment):
) )
if self.request.user.is_superuser or ( if self.request.user.is_superuser or (
member is not None member is not None and Department.filter_by_shop_lead(member).exists()
and Department.objects.filter_by_shop_lead(member).exists()
): ):
links["Department Certifications"] = reverse( links["Department Certifications"] = reverse(
"paperwork:department_certifications" "paperwork:department_certifications"

159
pdm.lock
View File

@ -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:16af371b3e9b9b16889ab6dc4e3bd5d84e0119a6804a6081ad64f86f45a2259f" content_hash = "sha256:c0e5c80b47118152c5b0167a588d3d1d078b0f2a710b8fc97869052ca04e7874"
[[package]] [[package]]
name = "aiohttp" name = "aiohttp"
@ -459,19 +459,6 @@ files = [
{file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"}, {file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"},
] ]
[[package]]
name = "django-filter"
version = "23.5"
requires_python = ">=3.7"
summary = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically."
dependencies = [
"Django>=3.2",
]
files = [
{file = "django-filter-23.5.tar.gz", hash = "sha256:67583aa43b91fe8c49f74a832d95f4d8442be628fd4c6d65e9f811f5153a4e5c"},
{file = "django_filter-23.5-py3-none-any.whl", hash = "sha256:99122a201d83860aef4fe77758b69dda913e874cc5e0eaa50a86b0b18d708400"},
]
[[package]] [[package]]
name = "django-markdownx" name = "django-markdownx"
version = "4.0.7" version = "4.0.7"
@ -894,7 +881,7 @@ files = [
[[package]] [[package]]
name = "ipython" name = "ipython"
version = "8.20.0" version = "8.19.0"
requires_python = ">=3.10" requires_python = ">=3.10"
summary = "IPython: Productive Interactive Computing" summary = "IPython: Productive Interactive Computing"
dependencies = [ dependencies = [
@ -909,8 +896,8 @@ dependencies = [
"traitlets>=5", "traitlets>=5",
] ]
files = [ files = [
{file = "ipython-8.20.0-py3-none-any.whl", hash = "sha256:bc9716aad6f29f36c449e30821c9dd0c1c1a7b59ddcc26931685b87b4c569619"}, {file = "ipython-8.19.0-py3-none-any.whl", hash = "sha256:2f55d59370f59d0d2b2212109fe0e6035cfea436b1c0e6150ad2244746272ec5"},
{file = "ipython-8.20.0.tar.gz", hash = "sha256:2f21bd3fc1d51550c89ee3944ae04bbc7bc79e129ea0937da6e6c68bfdbf117a"}, {file = "ipython-8.19.0.tar.gz", hash = "sha256:ac4da4ecf0042fb4e0ce57c60430c2db3c719fa8bdf92f8631d6bd8a5785d1f0"},
] ]
[[package]] [[package]]
@ -949,52 +936,50 @@ files = [
[[package]] [[package]]
name = "lxml" name = "lxml"
version = "5.1.0" version = "5.0.0"
requires_python = ">=3.6" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*"
summary = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." summary = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
files = [ files = [
{file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.0.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5382612ba2424cea5d2c89e2c29077023d8de88f8d60d5ceff5f76334516df9e"},
{file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.0.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:07a900735bad9af7be3085480bf384f68ed5580ba465b39a098e6a882c060d6b"},
{file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:980ba47c8db4b9d870014c7040edb230825b79017a6a27aa54cdb6fcc02d8cc0"},
{file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, {file = "lxml-5.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6507c58431dbd95b50654b3313c5ad54f90e54e5f2cdacf733de61eae478eec5"},
{file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af8920ce4a55ff41167ddbc20077f5698c2e710ad3353d32a07d3264f3a2021e"}, {file = "lxml-5.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4a45a278518e4308865c1e9dbb2c42ce84fb154efb03adeb16fdae3c1687c7c9"},
{file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cfced4a069003d8913408e10ca8ed092c49a7f6cefee9bb74b6b3e860683b45"}, {file = "lxml-5.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:59cea9ba1c675fbd6867ca1078fc717a113e7f5b7644943b74137b7cc55abebf"},
{file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9e5ac3437746189a9b4121db2a7b86056ac8786b12e88838696899328fc44bb2"}, {file = "lxml-5.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd39ef87fd1f7bb5c4aa53454936e6135cbfe03fe3744e8218be193f9e4fef16"},
{file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e6bb39d91bf932e7520cb5718ae3c2f498052aca53294d5d59fdd9068fe1a7f2"},
{file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.0.0-cp311-cp311-win32.whl", hash = "sha256:21af2c3862db6f4f486cddf73ec1157b40d5828876c47cd880edcbad8240ea1b"},
{file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, {file = "lxml-5.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:c1249aa4eaced30b59ecf8b8cae0b1ccede04583c74ca7d10b6f8bbead908b2c"},
{file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.0.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f30e697b6215e759d0824768b2c5b0618d2dc19abe6c67eeed2b0460f52470d1"},
{file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d1bb64646480c36a4aa1b6a44a5b6e33d0fcbeab9f53f1b39072cd3bb2c6243a"},
{file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4e69c36c8618707a90ed3fb6f48a6cc9254ffcdbf7b259e439a5ae5fbf9c5206"},
{file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, {file = "lxml-5.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9ca498f8554a09fbc3a2f8fc4b23261e07bc27bef99b3df98e2570688033f6fc"},
{file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16dd953fb719f0ffc5bc067428fc9e88f599e15723a85618c45847c96f11f431"}, {file = "lxml-5.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0326e9b8176ea77269fb39e7af4010906e73e9496a9f8eaf06d253b1b1231ceb"},
{file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16018f7099245157564d7148165132c70adb272fb5a17c048ba70d9cc542a1a1"}, {file = "lxml-5.0.0-cp312-cp312-win32.whl", hash = "sha256:5fb988e15378d6e905ca8f60813950a0c56da9469d0e8e5d8fe785b282684ec5"},
{file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82cd34f1081ae4ea2ede3d52f71b7be313756e99b4b5f829f89b12da552d3aa3"}, {file = "lxml-5.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:bb58e8f4b2cfe012cd312239b8d5139995fe8f5945c7c26d5fbbbb1ddb9acd47"},
{file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19a1bc898ae9f06bccb7c3e1dfd73897ecbbd2c96afe9095a6026016e5ca97b8"}, {file = "lxml-5.0.0-pp310-pypy310_pp73-macosx_11_0_x86_64.whl", hash = "sha256:96095bfc0c02072fc89afa67626013a253596ea5118b8a7f4daaae049dafa096"},
{file = "lxml-5.1.0-cp312-cp312-win32.whl", hash = "sha256:13521a321a25c641b9ea127ef478b580b5ec82aa2e9fc076c86169d161798b01"}, {file = "lxml-5.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:992029258ed719f130d5a9c443d142c32843046f1263f2c492862b2a853be570"},
{file = "lxml-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:1ad17c20e3666c035db502c78b86e58ff6b5991906e55bdbef94977700c72623"}, {file = "lxml-5.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:db40e85cffd22f7d65dcce30e85af565a66401a6ed22fc0c56ed342cfa4ffc43"},
{file = "lxml-5.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9bd0ae7cc2b85320abd5e0abad5ccee5564ed5f0cc90245d2f9a8ef330a8deae"}, {file = "lxml-5.0.0-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:cfa8a4cdc3765574b7fd0c7cfa5fbd1e2108014c9dfd299c679e5152bea9a55e"},
{file = "lxml-5.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c1d679df4361408b628f42b26a5d62bd3e9ba7f0c0e7969f925021554755aa"}, {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:049fef98d02513c34f5babd07569fc1cf1ed14c0f2fbff18fe72597f977ef3c2"},
{file = "lxml-5.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ad3a8ce9e8a767131061a22cd28fdffa3cd2dc193f399ff7b81777f3520e372"}, {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a85136d0ee18a41c91cc3e2844c683be0e72e6dda4cb58da9e15fcaef3726af7"},
{file = "lxml-5.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:304128394c9c22b6569eba2a6d98392b56fbdfbad58f83ea702530be80d0f9df"}, {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:766868f729f3ab84125350f1a0ea2594d8b1628a608a574542a5aff7355b9941"},
{file = "lxml-5.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d74fcaf87132ffc0447b3c685a9f862ffb5b43e70ea6beec2fb8057d5d2a1fea"}, {file = "lxml-5.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:99cad5c912f359e59e921689c04e54662cdd80835d80eeaa931e22612f515df7"},
{file = "lxml-5.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8cf5877f7ed384dabfdcc37922c3191bf27e55b498fecece9fd5c2c7aaa34c33"}, {file = "lxml-5.0.0-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:c90c593aa8dd57d5dab0ef6d7d64af894008971d98e6a41b320fdd75258fbc6e"},
{file = "lxml-5.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:877efb968c3d7eb2dad540b6cabf2f1d3c0fbf4b2d309a3c141f79c7e0061324"}, {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8134d5441d1ed6a682e3de3d7a98717a328dce619ee9c4c8b3b91f0cb0eb3e28"},
{file = "lxml-5.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f14a4fb1c1c402a22e6a341a24c1341b4a3def81b41cd354386dcb795f83897"}, {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f298ac9149037d6a3d5c74991bded39ac46292520b9c7c182cb102486cc87677"},
{file = "lxml-5.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:25663d6e99659544ee8fe1b89b1a8c0aaa5e34b103fab124b17fa958c4a324a6"}, {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:894c5f71186b410679aaab5774543fcb9cbabe8893f0b31d11cf28a0740e80be"},
{file = "lxml-5.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8b9f19df998761babaa7f09e6bc169294eefafd6149aaa272081cbddc7ba4ca3"}, {file = "lxml-5.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9cd3d6c2c67d4fdcd795e4945e2ba5434909c96640b4cc09453bd0dc7e8e1bac"},
{file = "lxml-5.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e53d7e6a98b64fe54775d23a7c669763451340c3d44ad5e3a3b48a1efbdc96f"}, {file = "lxml-5.0.0.zip", hash = "sha256:2219cbf790e701acf9a21a31ead75f983e73daf0eceb9da6990212e4d20ebefe"},
{file = "lxml-5.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c3cd1fc1dc7c376c54440aeaaa0dcc803d2126732ff5c6b68ccd619f2e64be4f"},
{file = "lxml-5.1.0.tar.gz", hash = "sha256:3eea6ed6e6c918e468e693c41ef07f3c3acc310b70ddd9cc72d9ef84bc9564ca"},
] ]
[[package]] [[package]]
name = "lxml-stubs" name = "lxml-stubs"
version = "0.5.1" version = "0.4.0"
summary = "Type annotations for the lxml package" summary = "Type annotations for the lxml package"
files = [ files = [
{file = "lxml-stubs-0.5.1.tar.gz", hash = "sha256:e0ec2aa1ce92d91278b719091ce4515c12adc1d564359dfaf81efa7d4feab79d"}, {file = "lxml-stubs-0.4.0.tar.gz", hash = "sha256:184877b42127256abc2b932ba8bd0ab5ea80bd0b0fee618d16daa40e0b71abee"},
{file = "lxml_stubs-0.5.1-py3-none-any.whl", hash = "sha256:1f689e5dbc4b9247cb09ae820c7d34daeb1fdbd1db06123814b856dae7787272"}, {file = "lxml_stubs-0.4.0-py3-none-any.whl", hash = "sha256:3b381e9e82397c64ea3cc4d6f79d1255d015f7b114806d4826218805c10ec003"},
] ]
[[package]] [[package]]
@ -1592,27 +1577,27 @@ files = [
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.1.14" version = "0.1.13"
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.1.14-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:96f76536df9b26622755c12ed8680f159817be2f725c17ed9305b472a757cdbb"}, {file = "ruff-0.1.13-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e3fd36e0d48aeac672aa850045e784673449ce619afc12823ea7868fcc41d8ba"},
{file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab3f71f64498c7241123bb5a768544cf42821d2a537f894b22457a543d3ca7a9"}, {file = "ruff-0.1.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9fb6b3b86450d4ec6a6732f9f60c4406061b6851c4b29f944f8c9d91c3611c7a"},
{file = "ruff-0.1.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7060156ecc572b8f984fd20fd8b0fcb692dd5d837b7606e968334ab7ff0090ab"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b13ba5d7156daaf3fd08b6b993360a96060500aca7e307d95ecbc5bb47a69296"},
{file = "ruff-0.1.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a53d8e35313d7b67eb3db15a66c08434809107659226a90dcd7acb2afa55faea"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ebb40442f7b531e136d334ef0851412410061e65d61ca8ce90d894a094feb22"},
{file = "ruff-0.1.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bea9be712b8f5b4ebed40e1949379cfb2a7d907f42921cf9ab3aae07e6fba9eb"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226b517f42d59a543d6383cfe03cccf0091e3e0ed1b856c6824be03d2a75d3b6"},
{file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2270504d629a0b064247983cbc495bed277f372fb9eaba41e5cf51f7ba705a6a"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f0312ba1061e9b8c724e9a702d3c8621e3c6e6c2c9bd862550ab2951ac75c16"},
{file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80258bb3b8909b1700610dfabef7876423eed1bc930fe177c71c414921898efa"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2f59bcf5217c661254bd6bc42d65a6fd1a8b80c48763cb5c2293295babd945dd"},
{file = "ruff-0.1.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:653230dd00aaf449eb5ff25d10a6e03bc3006813e2cb99799e568f55482e5cae"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6894b00495e00c27b6ba61af1fc666f17de6140345e5ef27dd6e08fb987259d"},
{file = "ruff-0.1.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b3acc6c4e6928459ba9eb7459dd4f0c4bf266a053c863d72a44c33246bfdbf"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1600942485c6e66119da294c6294856b5c86fd6df591ce293e4a4cc8e72989"},
{file = "ruff-0.1.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b3dadc9522d0eccc060699a9816e8127b27addbb4697fc0c08611e4e6aeb8b5"}, {file = "ruff-0.1.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ee3febce7863e231a467f90e681d3d89210b900d49ce88723ce052c8761be8c7"},
{file = "ruff-0.1.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c8eca1a47b4150dc0fbec7fe68fc91c695aed798532a18dbb1424e61e9b721f"}, {file = "ruff-0.1.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dcaab50e278ff497ee4d1fe69b29ca0a9a47cd954bb17963628fa417933c6eb1"},
{file = "ruff-0.1.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:62ce2ae46303ee896fc6811f63d6dabf8d9c389da0f3e3f2bce8bc7f15ef5488"}, {file = "ruff-0.1.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f57de973de4edef3ad3044d6a50c02ad9fc2dff0d88587f25f1a48e3f72edf5e"},
{file = "ruff-0.1.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b2027dde79d217b211d725fc833e8965dc90a16d0d3213f1298f97465956661b"}, {file = "ruff-0.1.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a36fa90eb12208272a858475ec43ac811ac37e91ef868759770b71bdabe27b6"},
{file = "ruff-0.1.14-py3-none-win32.whl", hash = "sha256:722bafc299145575a63bbd6b5069cb643eaa62546a5b6398f82b3e4403329cab"}, {file = "ruff-0.1.13-py3-none-win32.whl", hash = "sha256:a623349a505ff768dad6bd57087e2461be8db58305ebd5577bd0e98631f9ae69"},
{file = "ruff-0.1.14-py3-none-win_amd64.whl", hash = "sha256:e3d241aa61f92b0805a7082bd89a9990826448e4d0398f0e2bc8f05c75c63d99"}, {file = "ruff-0.1.13-py3-none-win_amd64.whl", hash = "sha256:f988746e3c3982bea7f824c8fa318ce7f538c4dfefec99cd09c8770bd33e6539"},
{file = "ruff-0.1.14-py3-none-win_arm64.whl", hash = "sha256:269302b31ade4cde6cf6f9dd58ea593773a37ed3f7b97e793c8594b262466b67"}, {file = "ruff-0.1.13-py3-none-win_arm64.whl", hash = "sha256:6bbbc3042075871ec17f28864808540a26f0f79a4478c357d3e3d2284e832998"},
{file = "ruff-0.1.14.tar.gz", hash = "sha256:ad3f8088b2dfd884820289a06ab718cde7d38b94972212cc4ba90d5fbc9955f3"}, {file = "ruff-0.1.13.tar.gz", hash = "sha256:e261f1baed6291f434ffb1d5c6bd8051d1c2a26958072d38dfbec39b3dda7352"},
] ]
[[package]] [[package]]
@ -1781,15 +1766,15 @@ files = [
[[package]] [[package]]
name = "types-requests" name = "types-requests"
version = "2.31.0.20240125" version = "2.31.0.20231231"
requires_python = ">=3.8" requires_python = ">=3.7"
summary = "Typing stubs for requests" summary = "Typing stubs for requests"
dependencies = [ dependencies = [
"urllib3>=2", "urllib3>=2",
] ]
files = [ files = [
{file = "types-requests-2.31.0.20240125.tar.gz", hash = "sha256:03a28ce1d7cd54199148e043b2079cdded22d6795d19a2c2a6791a4b2b5e2eb5"}, {file = "types-requests-2.31.0.20231231.tar.gz", hash = "sha256:0f8c0c9764773384122813548d9eea92a5c4e1f33ed54556b508968ec5065cee"},
{file = "types_requests-2.31.0.20240125-py3-none-any.whl", hash = "sha256:9592a9a4cb92d6d75d9b491a41477272b710e021011a2a3061157e2fb1f1a5d1"}, {file = "types_requests-2.31.0.20231231-py3-none-any.whl", hash = "sha256:2e2230c7bc8dd63fa3153c1c0ae335f8a368447f0582fc332f17d54f88e69027"},
] ]
[[package]] [[package]]
@ -1823,7 +1808,7 @@ files = [
[[package]] [[package]]
name = "udm-rest-client" name = "udm-rest-client"
version = "1.2.3" version = "1.2.2"
requires_python = ">=3.6" requires_python = ">=3.6"
summary = "Python library to interact with the Univention UDM REST API. Implements the simple Python UDM API." summary = "Python library to interact with the Univention UDM REST API. Implements the simple Python UDM API."
dependencies = [ dependencies = [
@ -1834,8 +1819,8 @@ dependencies = [
"requests<3,>=2.26", "requests<3,>=2.26",
] ]
files = [ files = [
{file = "udm-rest-client-1.2.3.tar.gz", hash = "sha256:80724cafa3498d23849124f27f965bc56f10f1dd09b8c2e61e89670c40931a5e"}, {file = "udm-rest-client-1.2.2.tar.gz", hash = "sha256:219fc5646cd33f158cc595579f58fdcb7de085a8bae44a0f83481a607202d1c0"},
{file = "udm_rest_client-1.2.3-py2.py3-none-any.whl", hash = "sha256:ee29e94e3ba5fba63a694e33d119b1af7450afcdce3a44301d4cd5ddfa1f980b"}, {file = "udm_rest_client-1.2.2-py2.py3-none-any.whl", hash = "sha256:add1d551b89161a8d06663b8fe92385747e1d780f67d2c882b7a023c1bfc7966"},
] ]
[[package]] [[package]]
@ -1850,7 +1835,7 @@ files = [
[[package]] [[package]]
name = "uvicorn" name = "uvicorn"
version = "0.27.0" version = "0.25.0"
requires_python = ">=3.8" requires_python = ">=3.8"
summary = "The lightning-fast ASGI server." summary = "The lightning-fast ASGI server."
dependencies = [ dependencies = [
@ -1858,13 +1843,13 @@ dependencies = [
"h11>=0.8", "h11>=0.8",
] ]
files = [ files = [
{file = "uvicorn-0.27.0-py3-none-any.whl", hash = "sha256:890b00f6c537d58695d3bb1f28e23db9d9e7a17cbcc76d7457c499935f933e24"}, {file = "uvicorn-0.25.0-py3-none-any.whl", hash = "sha256:ce107f5d9bd02b4636001a77a4e74aab5e1e2b146868ebbad565237145af444c"},
{file = "uvicorn-0.27.0.tar.gz", hash = "sha256:c855578045d45625fd027367f7653d249f7c49f9361ba15cf9624186b26b8eb6"}, {file = "uvicorn-0.25.0.tar.gz", hash = "sha256:6dddbad1d7ee0f5140aba5ec138ddc9612c5109399903828b4874c9937f009c2"},
] ]
[[package]] [[package]]
name = "uvicorn" name = "uvicorn"
version = "0.27.0" version = "0.25.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."
@ -1873,14 +1858,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.27.0", "uvicorn==0.25.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.27.0-py3-none-any.whl", hash = "sha256:890b00f6c537d58695d3bb1f28e23db9d9e7a17cbcc76d7457c499935f933e24"}, {file = "uvicorn-0.25.0-py3-none-any.whl", hash = "sha256:ce107f5d9bd02b4636001a77a4e74aab5e1e2b146868ebbad565237145af444c"},
{file = "uvicorn-0.27.0.tar.gz", hash = "sha256:c855578045d45625fd027367f7653d249f7c49f9361ba15cf9624186b26b8eb6"}, {file = "uvicorn-0.25.0.tar.gz", hash = "sha256:6dddbad1d7ee0f5140aba5ec138ddc9612c5109399903828b4874c9937f009c2"},
] ]
[[package]] [[package]]

View File

@ -23,7 +23,7 @@ dependencies = [
"semver~=3.0", "semver~=3.0",
"djangorestframework~=3.14", "djangorestframework~=3.14",
"django-q2~=1.6", "django-q2~=1.6",
"lxml~=5.1", "lxml~=5.0",
"django-object-actions~=4.2", "django-object-actions~=4.2",
"udm-rest-client~=1.2", "udm-rest-client~=1.2",
"openapi-client-udm~=1.0", "openapi-client-udm~=1.0",
@ -31,13 +31,12 @@ dependencies = [
"nh3~=0.2", "nh3~=0.2",
"django-tables2~=2.7", "django-tables2~=2.7",
"tablib[ods,xlsx]~=3.5", "tablib[ods,xlsx]~=3.5",
"django-filter~=23.5",
] ]
requires-python = ">=3.11" requires-python = ">=3.11"
[project.optional-dependencies] [project.optional-dependencies]
server = [ server = [
"uvicorn[standard]~=0.27", "uvicorn[standard]~=0.25",
"setuptools~=69.0", "setuptools~=69.0",
] ]
@ -68,7 +67,6 @@ profile="django"
extension = ".dj.html" extension = ".dj.html"
indent = 2 indent = 2
blank_line_after_tag = "load,extends" blank_line_after_tag = "load,extends"
max_blank_lines = 1
ignore = "T003,H017,H021,H030,H031" ignore = "T003,H017,H021,H030,H031"
format_css = true format_css = true
format_js = true format_js = true
@ -113,14 +111,14 @@ typing = [
"types-requests~=2.31", "types-requests~=2.31",
"types-urllib3~=1.26", "types-urllib3~=1.26",
"djangorestframework-stubs[compatible-mypy]~=3.14", "djangorestframework-stubs[compatible-mypy]~=3.14",
"lxml-stubs~=0.5", "lxml-stubs~=0.4",
] ]
debug = [ debug = [
"django-debug-toolbar~=4.2", "django-debug-toolbar~=4.2",
] ]
dev = [ dev = [
"django-extensions~=3.2", "django-extensions~=3.2",
"ipython~=8.20", "ipython~=8.19",
] ]
[tool.pdm.scripts] [tool.pdm.scripts]