2
0
Fork 0

Compare commits

..

4 Commits

Author SHA1 Message Date
Luca 7242ed2edd feat(fallback): add bucket for last night shifts from 20:00 on
continuous-integration/drone/push Build is passing Details
2024-05-20 00:31:58 +02:00
Luca 23001a3de3 feat(fallback): add command for bulk team member creation
continuous-integration/drone/push Build is passing Details
2024-05-19 23:32:15 +02:00
Luca 2d90662e4c style: remove obsolete comment 2024-05-19 23:32:15 +02:00
Luca 15475e2eec feat(fallback): add comment field to TeamMember 2024-05-19 23:32:15 +02:00
7 changed files with 90 additions and 12 deletions

View File

@ -26,7 +26,6 @@ class RegisterForm(forms.Form):
name = forms.CharField(
max_length=Helper.name.field.max_length, label="Name", widget=text_input()
)
# actually verify phone number, lol
phone = PhoneNumberField(
max_length=Helper.phone.field.max_length,
label="Handynummer für Benachrichtigungen",

View File

@ -29,9 +29,9 @@ class FallbackAssignmentInline(admin.TabularInline):
@admin.register(TeamMember)
class TeamMemberAdmin(admin.ModelAdmin):
fields = ("id", "name", "url")
fields = ("id", "name", "comment", "url")
readonly_fields = ("id", "url")
list_display = ("name", "shift_count")
list_display = ("name", "comment", "shift_count")
ordering = ("name",)
inlines = (FallbackAssignmentInline,)
actions = (assign_random_shifts, clear_shifts) # , reshuffle_shifts)

View File

@ -0,0 +1,42 @@
import sys
from django.core.management.base import BaseCommand, CommandError
from ...models import TeamMember
class Command(BaseCommand):
help = "Import a list of team members, optionally including their affiliations (stored in the comment field)"
def add_arguments(self, parser):
parser.add_argument(
"-d",
"--delimiter",
default=":",
help="character separating name from affiliations",
)
def handle(self, *args, **options):
try:
self._handle(*args, **options)
except KeyboardInterrupt:
self.stderr.write()
except Exception as e:
raise CommandError(e)
def _handle(self, *args, **options):
team_members = []
for line in sys.stdin.readlines():
line = line.strip()
if line == "" or line.startswith("#"):
continue
match line.split(options["delimiter"], maxsplit=1):
case [name, affiliations]:
team_members.append(
TeamMember(name=name, comment=affiliations.strip())
)
case [name]:
team_members.append(TeamMember(name=name))
TeamMember.objects.bulk_create(team_members)

View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.4 on 2024-05-19 21:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("fallback", "0006_fallbackassignment_was_full"),
]
operations = [
migrations.AddField(
model_name="teammember",
name="comment",
field=models.CharField(default="", max_length=100),
),
]

View File

@ -1,15 +1,15 @@
import math
import secrets
from base64 import urlsafe_b64encode
from datetime import datetime, time
from django.db.models import Count, Exists, ExpressionWrapper, Max, OuterRef, Sum
from django.db.models.fields import DateTimeField
from django.db.models.lookups import LessThan
from django.utils import timezone
from shiftregister.importer.models import *
night_shift_query = Q(start_at__hour__gte=21) | Q(start_at__hour__lte=10)
def generate_id():
return int.from_bytes(secrets.token_bytes(3), byteorder="big")
@ -18,6 +18,7 @@ def generate_id():
class TeamMember(models.Model):
id = models.IntegerField(default=generate_id, editable=False, primary_key=True)
name = models.CharField(max_length=100)
comment = models.CharField(max_length=100, default="")
fallback_shifts = models.ManyToManyField(Shift, through="FallbackAssignment")
def url(self):
@ -31,14 +32,32 @@ class TeamMember(models.Model):
)
def assign_random_shifts(self):
needs_fallback = Q(deleted=False, calendar__needs_fallback=True)
current_tz = timezone.get_current_timezone()
# create a datetime combining the last date having fallback shifts
# after 20:00 with the time 20:00 in the current timezone
last_night = datetime.combine(
Event.objects.filter(needs_fallback, start_at__hour__gte=20)
.latest("start_at")
.start_at.astimezone(current_tz),
time(hour=20),
current_tz,
)
is_last_night = Q(start_at__gte=last_night)
is_night_shift = Q(start_at__hour__gte=21) | Q(start_at__hour__lte=10)
if self.fallback_shifts.count() != 0:
return
selector1 = (~night_shift_query) & Q(
deleted=False, calendar__needs_fallback=True
)
selector2 = night_shift_query & Q(deleted=False, calendar__needs_fallback=True)
self._assign_from_bucket(selector1)
self._assign_from_bucket(selector2)
day_shifts = ~is_night_shift & ~is_last_night & needs_fallback
night_shifts = is_night_shift & ~is_last_night & needs_fallback
shit_shifts = is_last_night & needs_fallback
self._assign_from_bucket(day_shifts)
self._assign_from_bucket(night_shifts)
self._assign_from_bucket(shit_shifts)
def _assign_from_bucket(self, bucket_selector):
free_bucket = (
@ -165,7 +184,7 @@ class TeamMember(models.Model):
assignment.delete()
def __str__(self):
return f"{self.name}"
return f"{self.name}{f': {self.comment}' if self.comment else ''}"
class FallbackAssignment(models.Model):