first shift trade draft. without pin for now
continuous-integration/drone/push Build is passing Details

This commit is contained in:
xAndy 2025-05-14 13:49:07 +02:00
parent fd86c2fcc0
commit f55b653ccd
7 changed files with 125 additions and 20 deletions

View File

@ -25,6 +25,7 @@ class FallbackAssignmentInline(admin.TabularInline):
model = FallbackAssignment model = FallbackAssignment
ordering = ("shift__start_at",) ordering = ("shift__start_at",)
readonly_fields = ("shift",) readonly_fields = ("shift",)
fk_name = "team_member"
@admin.register(TeamMember) @admin.register(TeamMember)

View File

@ -0,0 +1,25 @@
# Generated by Django 5.0.4 on 2025-05-14 11:39
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("fallback", "0008_alter_teammember_comment"),
]
operations = [
migrations.AddField(
model_name="fallbackassignment",
name="traded_to",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="received_trades",
to="fallback.teammember",
),
),
]

View File

@ -21,18 +21,19 @@ class TeamMember(models.Model):
id = models.IntegerField(default=generate_id, editable=False, primary_key=True) id = models.IntegerField(default=generate_id, editable=False, primary_key=True)
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
comment = models.CharField(max_length=100, blank=True, default="") comment = models.CharField(max_length=100, blank=True, default="")
fallback_shifts = models.ManyToManyField(Shift, through="FallbackAssignment") fallback_shifts = models.ManyToManyField(
Shift, through="FallbackAssignment", through_fields=("team_member", "shift")
)
def url(self): def url(self):
return "https://helfen.kntkt.de" + reverse( return "https://helfen.kntkt.de" + reverse(
"my_fallback_shifts", "my_fallback_shifts",
kwargs={ kwargs={"team_member_id": self.url_id()},
"team_member_id": urlsafe_b64encode(
self.id.to_bytes(3, byteorder="big")
).decode("utf-8")
},
) )
def url_id(self):
return urlsafe_b64encode(self.id.to_bytes(3, byteorder="big")).decode("utf-8")
def assign_random_shifts(self): def assign_random_shifts(self):
needs_fallback = Q(deleted=False, calendar__needs_fallback=True) needs_fallback = Q(deleted=False, calendar__needs_fallback=True)
@ -199,6 +200,15 @@ class FallbackAssignment(models.Model):
shift = models.ForeignKey(Shift, on_delete=models.CASCADE) shift = models.ForeignKey(Shift, on_delete=models.CASCADE)
team_member = models.ForeignKey(TeamMember, on_delete=models.CASCADE) team_member = models.ForeignKey(TeamMember, on_delete=models.CASCADE)
was_full = models.BooleanField(default=False) was_full = models.BooleanField(default=False)
traded_to = models.ForeignKey(
TeamMember,
on_delete=models.CASCADE,
null=True,
blank=True,
related_name="received_trades",
)
def __str__(self): def __str__(self):
if self.traded_to:
return f"{self.shift} {self.team_member.name} -> {self.traded_to.name}"
return f"{self.shift} {self.team_member.name}" return f"{self.shift} {self.team_member.name}"

View File

@ -9,6 +9,22 @@
<div class="content"> <div class="content">
<a href="{% url 'pages:view' 'team_faq' %}">Häufig gestellte Fragen zu Teamschichten</a> <a href="{% url 'pages:view' 'team_faq' %}">Häufig gestellte Fragen zu Teamschichten</a>
</div> </div>
<div class="box">
<h4 class="subtitle">Schicht übernehmen</h4>
<form method="POST">
{% csrf_token %}
<div class="field has-addons">
<div class="control">
<input class="input" type="number" name="assignment_id" placeholder="Assignment ID" required>
</div>
<div class="control">
<button type="submit" name="take_shift" class="button is-info">Übernehmen/Entfernen</button>
</div>
</div>
</form>
</div>
{% if assignments %} {% if assignments %}
{% if is_draw %} {% if is_draw %}
<pre class="mb-5 select_all">Hallo {{ team_member.name }}, hier deine Teamschichten für das Festival: <pre class="mb-5 select_all">Hallo {{ team_member.name }}, hier deine Teamschichten für das Festival:
@ -26,6 +42,7 @@ Diese Schichtzuteilung wurde maschinell erstellt und ist auch ohne Unterschrift
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th>Tausch-ID</th>
<th>Wann</th> <th>Wann</th>
<th>Wie lange</th> <th>Wie lange</th>
<th>Wo</th> <th>Wo</th>
@ -38,13 +55,19 @@ Diese Schichtzuteilung wurde maschinell erstellt und ist auch ohne Unterschrift
{% for assignment in assignments %} {% for assignment in assignments %}
{% with assignment.shift as shift %} {% with assignment.shift as shift %}
<tr{% if shift.registration_count == shift.required_helpers|default:shift.room.required_helpers or assignment.was_full %} class="has-text-grey" style="text-decoration: line-through;"{% endif %}> <tr{% if shift.registration_count == shift.required_helpers|default:shift.room.required_helpers or assignment.was_full %} class="has-text-grey" style="text-decoration: line-through;"{% endif %}>
<td>{{ assignment.id }} {% if assignment.traded_to %}*{% endif %}</td>
<td>{{ shift.start_at }}</td> <td>{{ shift.start_at }}</td>
<td>{{ shift.duration|duration }}</td> <td>{{ shift.duration|duration }}</td>
<td>{{ shift.room.name }} </td> <td>{{ shift.room.name }} </td>
<td>{{ shift.registration_count }}/{{ shift.required_helpers|default:shift.room.required_helpers }}</td> <td>{{ shift.registration_count }}/{{ shift.required_helpers|default:shift.room.required_helpers }}</td>
<td> <td>
{% for assignment in shift.fallbackassignment_set.all %} {% for fa in shift.fallbackassignment_set.all %}
{{ assignment.team_member.name }}{% if not forloop.last %}, {% endif %} {% if fa.traded_to %}
{{ fa.traded_to.name }}
{% else %}
{{ fa.team_member.name }}
{% endif %}
{% if not forloop.last %}, {% endif %}
{% endfor %} {% endfor %}
</td> </td>
<td> <td>
@ -61,7 +84,7 @@ Diese Schichtzuteilung wurde maschinell erstellt und ist auch ohne Unterschrift
{% if user.is_authenticated %} {% if user.is_authenticated %}
<form method="POST"> <form method="POST">
{% csrf_token %} {% csrf_token %}
<button class="button is-success" type="submit">Schichten zulosen</button> <button class="button is-success" type="submit" name="draw_shifts">Schichten zulosen</button>
</form> </form>
{% else %} {% else %}
Noch keine Schichten zugewiesen, bitte wende dich an den Infopoint. Noch keine Schichten zugewiesen, bitte wende dich an den Infopoint.

View File

@ -1,11 +1,13 @@
from base64 import urlsafe_b64decode from base64 import urlsafe_b64decode
from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import Count from django.db.models import Count, Q
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from shiftregister.fallback.models import TeamMember from shiftregister.fallback.models import FallbackAssignment, TeamMember
# Create your views here. # Create your views here.
@ -22,15 +24,44 @@ def my_fallback_shifts(request, team_member_id):
is_draw = False is_draw = False
if request.method == "POST": if request.method == "POST":
team_member.assign_random_shifts() if "draw_shifts" in request.POST:
is_draw = True team_member.assign_random_shifts()
is_draw = True
elif "take_shift" in request.POST:
assignment_id = request.POST.get("assignment_id")
try:
assignment = FallbackAssignment.objects.get(pk=assignment_id)
if assignment.team_member == team_member:
assignment.traded_to = None
messages.success(request, f"Schicht erfolgreich zurückgenommen")
elif assignment.traded_to == team_member:
assignment.traded_to = None
messages.success(request, f"Schicht erfolgreich zurückgegeben")
else:
assignment.traded_to = team_member
messages.success(request, f"Schicht erfolgreich übernommen")
assignment.save()
return redirect(
reverse(
"my_fallback_shifts",
kwargs={"team_member_id": team_member.url_id()},
)
)
except FallbackAssignment.DoesNotExist:
messages.error(request, "Ungültige Schicht-ID")
assignments = (
FallbackAssignment.objects.filter(
Q(team_member=team_member, traded_to__isnull=True)
| Q(traded_to=team_member)
)
.prefetch_related("shift", "traded_to", "team_member")
.order_by("shift__start_at")
)
context = { context = {
"team_member": team_member, "team_member": team_member,
"assignments": team_member.fallbackassignment_set.order_by( "assignments": assignments,
"shift__start_at"
).all(),
# "shifts": team_member.fallback_shifts.order_by("start_at").all(),
"is_draw": is_draw, "is_draw": is_draw,
} }
return render(request, "my_fallback_shifts.html", context) return render(request, "my_fallback_shifts.html", context)

View File

@ -18,7 +18,18 @@
<tr> <tr>
<td>{{ shift.room.name }}</td> <td>{{ shift.room.name }}</td>
<td>{{ shift.start_at }}</td> <td>{{ shift.start_at }}</td>
<td>{% for fa in shift.fallbackassignment_set.all %}{% if not fa.was_full %}{{ fa.team_member.name }}{% if not forloop.last %}, {% endif %}{% endif %}{% endfor %}</td> <td>
{% for fa in shift.fallbackassignment_set.all %}
{% if not fa.was_full %}
{% if fa.traded_to %}
{{ fa.traded_to.name }} ({{ fa.id }})
{% else %}
{{ fa.team_member.name }} ({{ fa.id }})
{% endif %}
{% if not forloop.last %}, {% endif %}
{% endif %}
{% endfor %}
</td>
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>

View File

@ -59,7 +59,11 @@
{% for fallback in shift.event.fallbackassignment_set.all %} {% for fallback in shift.event.fallbackassignment_set.all %}
<div class="column is-one-quarter"> <div class="column is-one-quarter">
<div class="box{% if fallback.was_full %} has-text-grey" style="text-decoration: line-through;{% endif %}"> <div class="box{% if fallback.was_full %} has-text-grey" style="text-decoration: line-through;{% endif %}">
{{ fallback.team_member.name }} {% if fallback.traded_to %}
{{ fallback.traded_to.name }}
{% else %}
{{ fallback.team_member.name }}
{% endif %}
</div> </div>
</div> </div>
{% endfor %} {% endfor %}