Skip to content

Commit

Permalink
Closes #447 Feature to give and receive Solidarity Shifts added (#448)
Browse files Browse the repository at this point in the history
* Initial commit: Using solidarity shifts from the pool and removing used solidarity shifts not yet implemented

* #447 Option to give and receive Solidarity Shifts added

* Solidarity checkbox not required anymore, Behavior of use solidarity button changed slightly

* Solidarity shifts can be given subsequently now

* New template with statistics regarding Solidarity Shifts added

* Bugfix: Changed the starting month for the charts in solidarity_shifts_overview.html to Nov 2023 instead of the current month

* Chart Available Solidarity Shifts removed from Solidarity Shifts Statistics

* Solidarity shift checkbox adjusted + minor code improvements

* 447 Possibility to give and receive Solidarity Shifts added

Solidarity checkbox not required anymore, Behavior of use solidarity button changed slightly

Poetry up

Solidarity shifts can be given subsequently now

Feature Solidarity Shifts added

Bugfix: Changed the starting month for the charts in solidarity_shifts_overview.html to Nov 2023 instead of the current month

Chart Available Solidarity Shifts removed from Solidarity Shifts Statistics

Solidarity shift checkbox adjusted + minor code improvements

* Code improvements #447
  • Loading branch information
alexusfied authored Nov 28, 2023
1 parent e215737 commit dc20a19
Show file tree
Hide file tree
Showing 15 changed files with 443 additions and 9 deletions.
2 changes: 0 additions & 2 deletions members-current.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
AdressID;Nachname;Vorname;RabattN;Strasse;PLZ;Ort;eMail
2200000000016;Takala;Milja;0;Tehtaankatu 7436;49219;Kouvola;[email protected]
2200000000001;Cortes;TEST;18;Calle de La Almudena 7710;65621;Torrejón de Ardoz;[email protected]
7 changes: 7 additions & 0 deletions tapir/shifts/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ def register_sidebar_links(cls):
on_render=cls.get_link_display_name_abcd_calendar,
)

shifts_group.add_link(
display_name="Solidarity Shifts",
material_icon="favorite",
url=reverse_lazy("shifts:solidarity_shifts"),
ordering=4,
)

shifts_group.add_link(
display_name=_("Shift management"),
material_icon="settings",
Expand Down
1 change: 1 addition & 0 deletions tapir/shifts/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ class RegisterUserToShiftSlotForm(MissingCapabilitiesWarningMixin):
user = TapirUserChoiceField()
request_user: TapirUser
slot: ShiftSlot
is_solidarity = BooleanField(required=False, label="Mark as a Solidarity Shift")

def __init__(self, *args, **kwargs):
self.slot = kwargs.pop("slot", None)
Expand Down
18 changes: 18 additions & 0 deletions tapir/shifts/migrations/0049_shiftattendance_is_solidarity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.22 on 2023-11-05 18:01

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("shifts", "0048_auto_20231021_2102"),
]

operations = [
migrations.AddField(
model_name="shiftattendance",
name="is_solidarity",
field=models.BooleanField(default=False),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 3.2.22 on 2023-11-05 18:04

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("shifts", "0049_shiftattendance_is_solidarity"),
]

operations = [
migrations.RemoveField(
model_name="shiftattendance",
name="is_solidarity",
),
]
18 changes: 18 additions & 0 deletions tapir/shifts/migrations/0051_shiftattendance_is_solidarity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.22 on 2023-11-06 09:13

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("shifts", "0050_remove_shiftattendance_is_solidarity"),
]

operations = [
migrations.AddField(
model_name="shiftattendance",
name="is_solidarity",
field=models.BooleanField(default=False),
),
]
29 changes: 29 additions & 0 deletions tapir/shifts/migrations/0052_solidarityshift.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 3.2.22 on 2023-11-07 13:35

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("shifts", "0051_shiftattendance_is_solidarity"),
]

operations = [
migrations.CreateModel(
name="SolidarityShift",
fields=[
(
"shiftAttendance",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
serialize=False,
to="shifts.shiftattendance",
),
),
("is_used_up", models.BooleanField(default=False)),
],
),
]
28 changes: 28 additions & 0 deletions tapir/shifts/migrations/0053_auto_20231116_1130.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.2.23 on 2023-11-16 10:30

import datetime
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("shifts", "0052_solidarityshift"),
]

operations = [
migrations.AddField(
model_name="solidarityshift",
name="date_gifted",
field=models.DateField(
auto_now_add=True,
default=datetime.datetime(2023, 11, 16, 11, 30, 14, 216680),
),
preserve_default=False,
),
migrations.AddField(
model_name="solidarityshift",
name="date_used",
field=models.DateField(null=True),
),
]
26 changes: 23 additions & 3 deletions tapir/shifts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,7 @@ def with_valid_state(self):
ShiftSlot, related_name="attendances", on_delete=models.CASCADE
)
reminder_email_sent = models.BooleanField(default=False)
is_solidarity = models.BooleanField(default=False)

class State(models.IntegerChoices):
PENDING = 1
Expand Down Expand Up @@ -806,12 +807,17 @@ def update_shift_account_entry(self, entry_description=""):
ShiftAttendance.State.DONE,
ShiftAttendance.State.MISSED_EXCUSED,
]:
entry_value = 1
if self.is_solidarity:
entry_value = 0
else:
entry_value = 1

if entry_value is None:
return

description = f"Shift {SHIFT_ATTENDANCE_STATES[self.state]} {self.slot.get_display_name()} {entry_description}"
is_solidarity_str = "Solidarity" if self.is_solidarity else ""

description = f"{is_solidarity_str} Shift {SHIFT_ATTENDANCE_STATES[self.state]} {self.slot.get_display_name()} {entry_description}"

entry = ShiftAccountEntry.objects.create(
user=self.user,
Expand Down Expand Up @@ -937,7 +943,10 @@ def is_balance_negative(self):
return self.get_account_balance() < -1

def is_balance_positive(self):
return self.get_account_balance() > 1
return self.get_account_balance() > 0

def get_available_solidarity_shifts(self):
return SolidarityShift.objects.filter(is_used_up=False).count()

def get_current_shift_exemption(self, date=None):
if not hasattr(self, "shift_exemptions") or self.shift_exemptions is None:
Expand Down Expand Up @@ -1080,3 +1089,14 @@ class Meta:
on_delete=models.SET_NULL,
null=True,
)


class SolidarityShift(models.Model):
shiftAttendance = models.OneToOneField(
ShiftAttendance,
on_delete=models.CASCADE,
primary_key=True,
)
is_used_up = models.BooleanField(default=False)
date_gifted = models.DateField(auto_now_add=True)
date_used = models.DateField(null=True)
79 changes: 79 additions & 0 deletions tapir/shifts/templates/shifts/solidarity_shifts_overview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{% extends "shifts/base.html" %}

{% load i18n %}
{% load core %}
{% load static %}

{% block head %}
<script src="{% static 'statistics/chart_4.4.0.js' %}"></script>
<script src="{% static 'statistics/tapir_charts.js' %}" defer></script>
{% endblock head %}

{% block content %}
<div class="row">
<div class="col-xl-6">
<div class="card mb-2">
<h5 class="card-header d-flex justify-content-between align-items-center">
{% translate "Available" %}
</h5>
<div class="card-body">
{% if available_solidarity_shifts == 1 %}
<p>There is {{ available_solidarity_shifts }} solidarity shift available at the moment</p>
{% else %}
<p>There are {{ available_solidarity_shifts }} solidarity shifts available at the moment</p>
{% endif %}
</div>
</div>
</div>
<div class="col-xl-6">
<div class="card mb-2">
<h5 class="card-header d-flex justify-content-between align-items-center">
{% translate "Used" %}
</h5>
<div class="card-body">
{% if used_solidarity_shifts_total == 1 %}
<p>{{ used_solidarity_shifts_total }} solidarity shift has been used in total</p>
{% else %}
<p>{{ used_solidarity_shifts_total }} solidarity shifts have been used in total</p>
{% endif %}
<span class="{% tapir_button_link %}"
onclick="chartManager.show_stats_chart(
this,
'{% url "shifts:used_solidarity_shifts_json" %}',
'used_solidarity_shifts_canvas',
)">
<span class="material-icons">leaderboard</span>
<span class="button-text">{% translate "Show graph: " %}{% translate "Solidarity shifts used" %}</span>
</span>
<canvas id="used_solidarity_shifts_canvas" style="display: none;"></canvas>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xl-6">
<div class="card mb-2">
<h5 class="card-header d-flex justify-content-between align-items-center">
{% translate "Gifted" %}
</h5>
<div class="card-body">
{% if gifted_solidarity_shifts_total == 1 %}
<p>{{ gifted_solidarity_shifts_total }} solidarity shift has been gifted in total</p>
{% else %}
<p>{{ gifted_solidarity_shifts_total }} solidarity shifts have been gifted in total</p>
{% endif %}
<span class="{% tapir_button_link %}"
onclick="chartManager.show_stats_chart(
this,
'{% url "shifts:gifted_solidarity_shifts_json" %}',
'gifted_solidarity_shifts_canvas',
)">
<span class="material-icons">leaderboard</span>
<span class="button-text">{% translate "Show graph: " %}{% translate "Solidarity shifts gifted" %}</span>
</span>
<canvas id="gifted_solidarity_shifts_canvas" style="display: none;"></canvas>
</div>
</div>
</div>
</div>
{% endblock %}
16 changes: 15 additions & 1 deletion tapir/shifts/templates/shifts/user_shifts_overview_tag.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{% load utils %}
{% load i18n %}
{% load shifts %}
{% load core %}
Expand Down Expand Up @@ -74,7 +75,7 @@ <h5 class="card-header d-flex justify-content-between align-items-center" id="us

<div class="row m-1">
<div class="col-4 fw-bold text-end">{% translate "Shift Status" %}:</div>
<div class="col-8" id="user-shift-status">
<div class="col-8 d-flex gap-2" id="user-shift-status">
{% if user.shift_user_data.is_balance_ok %}
<span class="text-success">{% translate "OK" %}</span>

Expand Down Expand Up @@ -128,5 +129,18 @@ <h5 class="card-header d-flex justify-content-between align-items-center" id="us
</a>
</div>
</div>
<div class="row m-1">
<div class="col-4 fw-bold text-end">{% translate "Solidarity" %}:</div>
<div class="col-8 d-flex gap-1">
<form method="post" action="{% url 'shifts:solidarity_shift_used' object.pk%}">
{% csrf_token %}
<button type="submit" class="{% tapir_button_action %} btn-sm {% disabled_if_user_cant_receive_solidarity user %}"><span class="material-icons">favorite</span>{% translate 'Receive Solidarity' %}</button>
</form>
<form method="post" action="{% url 'shifts:solidarity_shift_given' object.pk%}">
{% csrf_token %}
<button type="submit" class="{% tapir_button_action %} btn-sm {% disabled_if_user_cant_give_solidarity user %}"><span class="material-icons">favorite</span>{% translate 'Give Solidarity' %}</button>
</form>
</div>
</div>
</div>
</div>
25 changes: 25 additions & 0 deletions tapir/shifts/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,29 @@
views.ConvertShiftExemptionToMembershipPauseView.as_view(),
name="convert_shift_exemption_to_membership_pause",
),
path(
"solidarity_shift_used/<int:pk>",
views.SolidarityShiftUsed.as_view(),
name="solidarity_shift_used",
),
path(
"solidarity_shift_given/<int:pk>",
views.SolidarityShiftGiven.as_view(),
name="solidarity_shift_given",
),
path(
"solidarity_shifts",
views.SolidarityShiftsView.as_view(),
name="solidarity_shifts",
),
path(
"solidarity_shifts/gifted_solidarity_shifts_json",
views.GiftedSolidarityShiftsJsonView.as_view(),
name="gifted_solidarity_shifts_json",
),
path(
"solidarity_shifts/used_solidarity_shifts_json",
views.UsedSolidarityShiftsJsonView.as_view(),
name="used_solidarity_shifts_json",
),
]
6 changes: 5 additions & 1 deletion tapir/shifts/views/attendance.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
CreateShiftAttendanceLogEntry,
UpdateShiftAttendanceStateLogEntry,
ShiftAttendanceTakenOverLogEntry,
SolidarityShift,
)
from tapir.shifts.views.views import SelectedUserViewMixin
from tapir.utils.shortcuts import safe_redirect
Expand Down Expand Up @@ -285,11 +286,14 @@ def form_valid(self, form):
response = super().form_valid(form)
slot = self.get_slot()
user_to_register = form.cleaned_data["user"]
is_solidarity = form.cleaned_data["is_solidarity"]

with transaction.atomic():
attendance = ShiftAttendance.objects.create(
user=user_to_register, slot=slot
user=user_to_register, slot=slot, is_solidarity=is_solidarity
)
if attendance.is_solidarity:
SolidarityShift.objects.create(shiftAttendance=attendance)
self.mark_stand_in_found_if_relevant(slot, self.request.user)
CreateShiftAttendanceLogEntry().populate(
actor=self.request.user,
Expand Down
Loading

0 comments on commit dc20a19

Please sign in to comment.