From 0d5c031f32e39c50cc7ee744155041fd8f5c185b Mon Sep 17 00:00:00 2001 From: FA Date: Mon, 18 May 2026 12:31:31 -0400 Subject: [PATCH] feat(enrichment): implement making multiple enrichments --- intranet/apps/enrichment/forms.py | 56 ++++++++++++++ intranet/apps/enrichment/urls.py | 1 + intranet/apps/enrichment/views.py | 49 +++++++++++- .../templates/enrichment/bulk_create.html | 74 +++++++++++++++++++ intranet/templates/enrichment/home.html | 1 + 5 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 intranet/templates/enrichment/bulk_create.html diff --git a/intranet/apps/enrichment/forms.py b/intranet/apps/enrichment/forms.py index 9b5dfdf35a5..cea776bfeb8 100644 --- a/intranet/apps/enrichment/forms.py +++ b/intranet/apps/enrichment/forms.py @@ -1,3 +1,5 @@ +import datetime # for mass form + from django import forms from .models import EnrichmentActivity @@ -13,3 +15,57 @@ class Meta: model = EnrichmentActivity fields = ["title", "description", "time", "location", "capacity", "presign", "groups_allowed", "groups_blacklisted"] + + +WEEKDAY_FIELDS = [ + ("monday", "Monday"), + ("tuesday", "Tuesday"), + ("wednesday", "Wednesday"), + ("thursday", "Thursday"), + ("friday", "Friday"), +] + + +class EnrichmentActivityBulkForm(EnrichmentActivityForm): + # A way to create multiple enrichment activities at once. + + week_of = forms.DateField( + label="Target Week", + widget=forms.DateInput(attrs={"type": "date"}), + ) + monday = forms.BooleanField(label="Monday", required=False) + tuesday = forms.BooleanField(label="Tuesday", required=False) + wednesday = forms.BooleanField(label="Wednesday", required=False) + thursday = forms.BooleanField(label="Thursday", required=False) + friday = forms.BooleanField(label="Friday", required=False) + activity_time = forms.TimeField(label="Time of day", widget=forms.TimeInput(attrs={"type": "time"}), initial=datetime.time(12, 0)) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # remove time so it doesn't appear in the form + self.fields.pop("time") + + def clean(self): + cleaned = super().clean() + # Require at least one day to be checked + days_checked = any(cleaned.get(day) for day, _ in WEEKDAY_FIELDS) + if not days_checked: + raise forms.ValidationError("Please select at least one day of the week.") + return cleaned + + def get_selected_dates(self): # returns in the form of a list. + week_of = self.cleaned_data["week_of"] + activity_time = self.cleaned_data["activity_time"] + monday = week_of - datetime.timedelta(days=week_of.weekday()) + offsets = { + "monday": 0, + "tuesday": 1, + "wednesday": 2, + "thursday": 3, + "friday": 4, + } + return [ + datetime.datetime.combine(monday + datetime.timedelta(days=offset), activity_time) + for day_name, offset in offsets.items() + if self.cleaned_data.get(day_name) + ] diff --git a/intranet/apps/enrichment/urls.py b/intranet/apps/enrichment/urls.py index f166cd14ace..2990df78e23 100644 --- a/intranet/apps/enrichment/urls.py +++ b/intranet/apps/enrichment/urls.py @@ -9,6 +9,7 @@ urlpatterns = [ path("", views.enrichment_view, name="enrichment"), path("/add", views.add_enrichment_view, name="add_enrichment"), + path("/bulk_create", views.enrichment_bulk_create_view, name="enrichment_bulk_create"), path("/modify/", views.modify_enrichment_view, name="modify_enrichment"), path("/delete/", views.delete_enrichment_view, name="delete_enrichment"), path("/join/", views.enrichment_signup_view, name="enrichment_signup"), diff --git a/intranet/apps/enrichment/views.py b/intranet/apps/enrichment/views.py index 99473cc60ba..99958458e35 100644 --- a/intranet/apps/enrichment/views.py +++ b/intranet/apps/enrichment/views.py @@ -10,7 +10,7 @@ from ...utils.html import safe_html from ..auth.decorators import deny_restricted -from .forms import EnrichmentActivityForm +from .forms import EnrichmentActivityBulkForm, EnrichmentActivityForm from .models import EnrichmentActivity logger = logging.getLogger(__name__) @@ -309,13 +309,56 @@ def add_enrichment_view(request): return render(request, "enrichment/add_modify.html", context) +@login_required +@deny_restricted +def enrichment_bulk_create_view(request): + """View for creating multiple enrichments. + + Get requests will render bulk-create form with Mon-Fri checkboxes. + Post requests create one EnrichmentActivity for each checked day and then redirect. + """ + is_enrichment_admin = request.user.has_admin_permission("enrichment") + if not is_enrichment_admin: + raise http.Http404 + + if request.method == "POST": + form = EnrichmentActivityBulkForm(data=request.POST) + if form.is_valid(): + created = [] + for dt in form.get_selected_dates(): + activity = form.save(commit=False) + activity.pk = None + activity.user = request.user + activity.time = dt + activity.description = safe_html(activity.description) # sanitize the html to prevent injection + activity.save() + form.save_m2m() + created.append(dt.strftime("%A, %b %-d")) + + messages.success( + request, + f"Created {len(created)} enrichment activities: " + ", ".join(created), + ) + return redirect("enrichment") + else: + form = EnrichmentActivityBulkForm() + + return render( + request, + "enrichment/bulk_create.html", + {"form": form}, + ) + + @login_required @deny_restricted def modify_enrichment_view(request, enrichment_id): - """Modify enrichment activity page. + """ + Modify enrichment activity page. Args: - enrichment_id (int): enrichment activity id + request (HttpRequest): The HTTP request object. + enrichment_id (int): The unique identifier of the enrichment activity. """ enrichment = get_object_or_404(EnrichmentActivity, id=enrichment_id) diff --git a/intranet/templates/enrichment/bulk_create.html b/intranet/templates/enrichment/bulk_create.html new file mode 100644 index 00000000000..d36939ff9d1 --- /dev/null +++ b/intranet/templates/enrichment/bulk_create.html @@ -0,0 +1,74 @@ +{% extends "page_with_nav.html" %} +{% load static %} +{% load dates %} +{% load pipeline %} +{% block title %} + {{ block.super }} - Bulk Create Enrichment Activities +{% endblock %} +{% block js %} + {{ block.super }} + + +{% endblock %} +{% block css %} + {{ block.super }} + + {% stylesheet 'announcements.form' %} + {% stylesheet 'enrichment' %} +{% endblock %} +{% block head %} + {% if dark_mode_enabled %} + {% stylesheet 'dark/base' %} + {% stylesheet 'dark/nav' %} + {% stylesheet 'dark/events' %} + {% endif %} +{% endblock %} +{% block main %} +
+

Bulk Create Enrichment Activities

+

Choose a week for your activity, and check off the days you want. One activity will be created for each checked day.

+
+ + {% csrf_token %} + + + + + + + + + + + + + + +
{{ form.title }}{{ form.title.errors }}
{{ form.description }}{{ form.description.errors }}
{{ form.capacity }}{{ form.capacity.errors }}
{{ form.location }}{{ form.location.errors }}
{{ form.groups_allowed }}{{ form.groups_allowed.errors }}
{{ form.groups_blacklisted }}{{ form.groups_blacklisted.errors }}
{{ form.presign }}{{ form.presign.errors }}
{{ form.week_of }}{{ form.week_of.errors }}
{{ form.activity_time }}{{ form.activity_time.errors }}
Days of the week: +
+ {# creates a day checkbox within the weekday checkboxes for each day #} + + + + + +
+ {% if form.non_field_errors %} +
    {{ form.non_field_errors }}
+ {% endif %} +
  + + Cancel +
+
+
+{% endblock %} diff --git a/intranet/templates/enrichment/home.html b/intranet/templates/enrichment/home.html index dac9db69876..58eeca2199d 100644 --- a/intranet/templates/enrichment/home.html +++ b/intranet/templates/enrichment/home.html @@ -101,6 +101,7 @@

Enrichment

Add + Bulk Create (by week) {% endif %}