From aa15014d0b2cfbdba7f34fb83d4774eca725ae93 Mon Sep 17 00:00:00 2001 From: Julian B Date: Thu, 12 Sep 2024 19:23:41 +0200 Subject: [PATCH] add shift copy --- ephios/core/forms/events.py | 4 ++ .../core/fragments/shift_header.html | 3 + ephios/core/templates/core/shift_copy.html | 24 ++++++++ ephios/core/templates/core/shift_form.html | 3 + ephios/core/urls.py | 8 +-- ephios/core/views/event.py | 61 +++++++++++-------- ephios/core/views/shift.py | 4 ++ .../extra/widgets/recurrence_picker.html | 13 ++-- ephios/static/ephios/js/event_copy.js | 4 +- 9 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 ephios/core/templates/core/shift_copy.html diff --git a/ephios/core/forms/events.py b/ephios/core/forms/events.py index 386b82444..78f8c8964 100644 --- a/ephios/core/forms/events.py +++ b/ephios/core/forms/events.py @@ -241,6 +241,10 @@ class EventCopyForm(forms.Form): recurrence = RecurrenceField() +class ShiftCopyForm(forms.Form): + recurrence = RecurrenceField(pick_hour=True) + + class EventTypeForm(forms.ModelForm): class Meta: model = EventType diff --git a/ephios/core/templates/core/fragments/shift_header.html b/ephios/core/templates/core/fragments/shift_header.html index f6888e2a6..acb43d793 100644 --- a/ephios/core/templates/core/fragments/shift_header.html +++ b/ephios/core/templates/core/fragments/shift_header.html @@ -57,6 +57,9 @@
  • {% translate "Edit" %}
  • +
  • + {% translate "Copy" %}
  • {% if shift.event.shifts.count > 1 %} {% translate "Delete" %} diff --git a/ephios/core/templates/core/shift_copy.html b/ephios/core/templates/core/shift_copy.html new file mode 100644 index 000000000..fc8a5ac8a --- /dev/null +++ b/ephios/core/templates/core/shift_copy.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} +{% load crispy_forms_filters %} +{% load static %} +{% load i18n %} + +{% block title %} + {% translate "Copy shift" %} +{% endblock %} + +{% block css %} +{% endblock %} + +{% block content %} + + {{ form.errors }} +
    + {% csrf_token %} + {{ form.recurrence }} +
    +{% endblock %} diff --git a/ephios/core/templates/core/shift_form.html b/ephios/core/templates/core/shift_form.html index 307e933cc..824b8ed60 100644 --- a/ephios/core/templates/core/shift_form.html +++ b/ephios/core/templates/core/shift_form.html @@ -80,6 +80,9 @@

    + diff --git a/ephios/core/urls.py b/ephios/core/urls.py index e4eb61211..411956db6 100644 --- a/ephios/core/urls.py +++ b/ephios/core/urls.py @@ -41,7 +41,7 @@ EventNotificationView, EventUpdateView, HomeView, - RRuleOccurrenceView, + ShiftCopyView, ) from ephios.core.views.eventtype import ( EventTypeCreateView, @@ -177,12 +177,8 @@ AddPlaceholderParticipantView.as_view(), name="shift_disposition_add_placeholder", ), + path("shifts//copy/", ShiftCopyView.as_view(), name="shift_copy"), path("calendar//", user_event_feed_view, name="user_event_feed"), - path( - "extra/rruleoccurrence/", - RRuleOccurrenceView.as_view(), - name="rrule_occurrences", - ), path("settings/data/", PersonalDataSettingsView.as_view(), name="settings_personal_data"), path("settings/calendar/", CalendarSettingsView.as_view(), name="settings_calendar"), path( diff --git a/ephios/core/views/event.py b/ephios/core/views/event.py index af09e8832..972668463 100644 --- a/ephios/core/views/event.py +++ b/ephios/core/views/event.py @@ -1,22 +1,19 @@ -import json from calendar import _nextmonth, _prevmonth from collections import defaultdict +from copy import copy from datetime import datetime, time, timedelta from csp.decorators import csp_exempt -from dateutil.rrule import rrulestr from django import forms from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import ValidationError from django.db.models import BooleanField, Case, Count, Max, Min, Prefetch, Q, QuerySet, When -from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect from django.urls import reverse, reverse_lazy from django.utils import timezone from django.utils.datastructures import MultiValueDict -from django.utils.formats import date_format from django.utils.functional import cached_property from django.utils.safestring import mark_safe from django.utils.timezone import get_current_timezone, make_aware @@ -36,7 +33,7 @@ from guardian.shortcuts import assign_perm, get_objects_for_user, get_users_with_perms from ephios.core.calendar import ShiftCalendar -from ephios.core.forms.events import EventCopyForm, EventForm, EventNotificationForm +from ephios.core.forms.events import EventCopyForm, EventForm, EventNotificationForm, ShiftCopyForm from ephios.core.models import AbstractParticipation, Event, EventType, Shift from ephios.core.services.notifications.types import ( CustomEventParticipantNotification, @@ -547,16 +544,8 @@ def get_success_url(self): return reverse("core:event_copy", kwargs=dict(pk=self.object.pk)) def form_valid(self, form): - messages.info( - self.request, - [ - date_format(obj, format="SHORT_DATE_FORMAT") - for obj in form.cleaned_data["recurrence"] - ], - ) - return super().form_valid(form) tz = get_current_timezone() - for date in occurrences: + for date in form.cleaned_data["recurrence"].xafter(timezone.now(), 1000, inc=True): event = self.get_object() start_date = event.get_start_time().astimezone(tz).date() shifts = list(event.shifts.all()) @@ -618,20 +607,38 @@ def as_view(cls, **initkwargs): return csp_exempt(super().as_view(**initkwargs)) -class RRuleOccurrenceView(CustomPermissionRequiredMixin, View): - permission_required = "core.add_event" +class ShiftCopyView(CustomPermissionRequiredMixin, SingleObjectMixin, FormView): + permission_required = "core.change_event" + model = Shift + template_name = "core/shift_copy.html" + form_class = ShiftCopyForm - def post(self, *args, **kwargs): - try: - recurrence = rrulestr(json.loads(self.request.body)["recurrence_string"]) - return HttpResponse( - json.dumps( - list(recurrence.xafter(timezone.now(), count=1000)), - default=lambda obj: date_format(obj, format="SHORT_DATE_FORMAT"), - ) - ) - except (TypeError, KeyError, ValueError): - return HttpResponse() + def setup(self, request, *args, **kwargs): + super().setup(request, *args, **kwargs) + self.object = self.get_object() + + def get_success_url(self): + return reverse("core:event_detail", kwargs=dict(pk=self.object.event.pk, slug="event")) + + def form_valid(self, form): + shift = self.object + duration = shift.end_time - shift.start_time + meeting_offset = shift.start_time - shift.meeting_time + shifts_to_create = [] + for dt in form.cleaned_data["recurrence"].xafter(timezone.now(), 1000, inc=True): + shift = copy(shift) + shift.pk = None + shift.meeting_time = dt - meeting_offset + shift.start_time = dt + shift.end_time = dt + duration + shifts_to_create.append(shift) + Shift.objects.bulk_create(shifts_to_create) + messages.success(self.request, _("Shift copied successfully.")) + return super().form_valid(form) + + @classmethod + def as_view(cls, **initkwargs): + return csp_exempt(super().as_view(**initkwargs)) class HomeView(LoginRequiredMixin, TemplateView): diff --git a/ephios/core/views/shift.py b/ephios/core/views/shift.py index 0df102991..d6aa03f94 100644 --- a/ephios/core/views/shift.py +++ b/ephios/core/views/shift.py @@ -108,6 +108,8 @@ def post(self, *args, **kwargs): self.save_plugin_forms() if "addAnother" in self.request.POST: return redirect(reverse("core:event_createshift", kwargs={"pk": self.kwargs.get("pk")})) + elif "copyShift" in self.request.POST: + return redirect(reverse("core:shift_copy", kwargs={"pk": shift.pk})) try: self.event.activate() messages.success( @@ -232,6 +234,8 @@ def post(self, *args, **kwargs): return redirect( reverse("core:event_createshift", kwargs={"pk": shift.event.pk}) ) + elif "copyShift" in self.request.POST: + return redirect(reverse("core:shift_copy", kwargs={"pk": shift.pk})) messages.success( self.request, _("The shift {shift} has been saved.").format(shift=shift) ) diff --git a/ephios/extra/templates/extra/widgets/recurrence_picker.html b/ephios/extra/templates/extra/widgets/recurrence_picker.html index bb736050d..2d0907720 100644 --- a/ephios/extra/templates/extra/widgets/recurrence_picker.html +++ b/ephios/extra/templates/extra/widgets/recurrence_picker.html @@ -2,7 +2,7 @@ {% load static %} -
    +
    {% translate "starting at" %}
    @@ -217,10 +217,13 @@ id="id_recurrence"> {{ csrf_token }}
    -
    -
      -
    • [[ date ]]
    • -
    +
    +

    {% translate "Selected dates:" %}

    +
    +
      +
    • [[ date ]]
    • +
    +
    diff --git a/ephios/static/ephios/js/event_copy.js b/ephios/static/ephios/js/event_copy.js index 6ec828b10..8eb095d0e 100644 --- a/ephios/static/ephios/js/event_copy.js +++ b/ephios/static/ephios/js/event_copy.js @@ -111,7 +111,9 @@ document.addEventListener('DOMContentLoaded', (event) => { }) const computed_dates = computed(() => { - return rrule_set ? rrule_set.value.all(): [] + return rrule_set ? rrule_set.value.all().map(date => { + return formatDate(date) + " " + (date.getUTCHours() < 10 ? "0" : "") + date.getUTCHours() + ":" + (date.getUTCMinutes() < 10 ? "0" : "") + date.getUTCMinutes() + }): [] }) const rrule_string = computed(() => {