From 19cbe8f57283ec2ad0ae516d17a84eeab510f626 Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Mon, 8 Mar 2021 18:46:17 +0100
Subject: [PATCH] Implement action form with multiple-select in table for
 person overview

Not finished yet
---
 aleksis/apps/alsijil/actions.py               |  25 +++++
 aleksis/apps/alsijil/forms.py                 |  11 ++
 .../alsijil/static/css/alsijil/person.css     |   7 +-
 aleksis/apps/alsijil/tables.py                |  39 +++----
 .../alsijil/class_register/person.html        |   6 +
 .../alsijil/partials/mark_as_buttons.html     |  20 ++--
 aleksis/apps/alsijil/views.py                 | 106 +++---------------
 7 files changed, 86 insertions(+), 128 deletions(-)
 create mode 100644 aleksis/apps/alsijil/actions.py

diff --git a/aleksis/apps/alsijil/actions.py b/aleksis/apps/alsijil/actions.py
new file mode 100644
index 000000000..6025f4a2e
--- /dev/null
+++ b/aleksis/apps/alsijil/actions.py
@@ -0,0 +1,25 @@
+from typing import Callable
+
+from django.utils.translation import gettext_lazy as _
+
+
+def mark_as_excused(modeladmin, request, queryset):
+    queryset.update(excused=True, excuse_type=None)
+
+
+mark_as_excused.short_description = _("Mark as excused")
+
+
+def mark_as_excuse_type_generator(excuse_type) -> Callable:
+    def mark_as_excuse_type(modeladmin, request, queryset):
+        queryset.update(excused=True, excuse_type=excuse_type)
+
+    mark_as_excuse_type.short_description = _(f"Mark as {excuse_type.name}")
+
+    return mark_as_excuse_type
+
+
+def delete_personal_note(modeladmin, request, queryset):
+    queryset.delete()
+
+delete_personal_note.short_description = _("Delete")
\ No newline at end of file
diff --git a/aleksis/apps/alsijil/forms.py b/aleksis/apps/alsijil/forms.py
index 228e825ba..b529f99b2 100644
--- a/aleksis/apps/alsijil/forms.py
+++ b/aleksis/apps/alsijil/forms.py
@@ -11,6 +11,7 @@ from material import Fieldset, Layout, Row
 
 from aleksis.apps.chronos.managers import TimetableType
 from aleksis.apps.chronos.models import TimePeriod
+from aleksis.core.forms import ActionForm
 from aleksis.core.models import Group, Person
 from aleksis.core.util.core_helpers import get_site_preferences
 from aleksis.core.util.predicates import check_global_permission
@@ -23,6 +24,8 @@ from .models import (
     LessonDocumentation,
     PersonalNote,
 )
+from .actions import *
+from .models import ExcuseType, ExtraMark, LessonDocumentation, PersonalNote
 
 
 class LessonDocumentationForm(forms.ModelForm):
@@ -254,3 +257,11 @@ class GroupRoleAssignmentEditForm(forms.ModelForm):
     class Meta:
         model = GroupRoleAssignment
         fields = ["date_start", "date_end"]
+class PersonOverviewForm(ActionForm):
+    def get_actions(self):
+        return [
+            mark_as_excused,
+            delete_personal_note
+        ] + [
+            mark_as_excuse_type_generator(excuse_type) for excuse_type in ExcuseType.models.all()
+        ]
diff --git a/aleksis/apps/alsijil/static/css/alsijil/person.css b/aleksis/apps/alsijil/static/css/alsijil/person.css
index 9c7e44180..ab6d6e799 100644
--- a/aleksis/apps/alsijil/static/css/alsijil/person.css
+++ b/aleksis/apps/alsijil/static/css/alsijil/person.css
@@ -11,7 +11,7 @@ span.input-field.inline > .select-wrapper .caret {
 }
 
 @media screen and (min-width: 1400px) {
-    li.collection-item#title form {
+    li.collection-item form {
         margin: -30px 0 -30px 0;
     }
 
@@ -20,6 +20,11 @@ span.input-field.inline > .select-wrapper .caret {
     }
 }
 
+.collection {
+    overflow: visible;
+    overflow-x: hidden;
+}
+
 #select_all_container {
     display: none;
 }
diff --git a/aleksis/apps/alsijil/tables.py b/aleksis/apps/alsijil/tables.py
index 1ea4f23f4..afa50cb15 100644
--- a/aleksis/apps/alsijil/tables.py
+++ b/aleksis/apps/alsijil/tables.py
@@ -1,13 +1,13 @@
 from django.template.loader import render_to_string
-<<<<<<< HEAD
-=======
 from django.urls import reverse
->>>>>>> 48d6587... Create a basic table for all PersonalNotes of an user
+from django.utils.safestring import mark_safe
 from django.utils.translation import gettext_lazy as _
 
 import django_tables2 as tables
 from django_tables2.utils import A
 
+from aleksis.core.tables import MaterializeCheckboxColumn
+
 from .models import PersonalNote
 
 
@@ -85,12 +85,13 @@ class GroupRoleTable(tables.Table):
         if not request.user.has_perm("alsijil.delete_grouprole"):
             self.columns.hide("delete")
 class PersonalNoteTable(tables.Table):
+    selected = MaterializeCheckboxColumn(verbose_name=_("Gay"), attrs={"name": "selected_objects"})
     lesson_period_lesson = tables.Column(verbose_name=_("Lesson"), accessor=A("lesson_period"))
     # lesson_period_teacher = tables.Column(verbose_name=_("Teacher"), accessor=A("lesson_period__get_teacher_names"))
     personal_note_date = tables.Column(verbose_name=_("Date"), accessor=A("date"))
     lesson_period_period = tables.Column(verbose_name=_("Period"), accessor=A("lesson_period__period__period"))
     # lesson_period_subject = tables.Column(verbose_name=_("Subject"), accessor=A("lesson_period__get_subject__name"))
-    absent = tables.Column(attrs={"td": {"class": "material-icons"}})
+    # absent = tables.Column(attrs={"td": {"class": "material-icons"}})
     excused = tables.Column(verbose_name=_("Excuse"))
     extra_marks = tables.Column(verbose_name="Extra marks", accessor=A("extra_marks__all"))
 
@@ -111,31 +112,18 @@ class PersonalNoteTable(tables.Table):
         return render_to_string("alsijil/partials/personal_note_link.html", context)
 
     def render_absent(self, value):
-        return "check" if value else "clear"
+        return render_to_string("components/materialize-chips.html", dict(content="Absent", classes="red white-text"))
 
     def render_excused(self, value, record):
         if record.absent:
-            absent_badge = render_to_string("components/materialize-chips.html",
-                                            dict(content="Absent", classes="red white-text"))
             if value:
                 context = dict(content=_("Excused"), classes="green white-text")
                 badge = render_to_string("components/materialize-chips.html", context)
                 if record.excuse_type:
-                    context = dict(content=record.excuse_type.short_name, classes="green white-text")
+                    context = dict(content=record.excuse_type.name, classes="green white-text")
                     badge = render_to_string("components/materialize-chips.html", context)
-            else:
-                badge = ""
-            return absent_badge + badge
-        else:
-            return ""
-
-    def render_excuse_type(self, value):
-        if value:
-            content = value.short_name
-            context = dict(content=content, classes="green white-text")
-            return render_to_string("components/materialize-chips.html", context)
-        else:
-            return "–"
+                return badge
+        return ""
 
     def render_late(self, value):
         if value:
@@ -149,16 +137,17 @@ class PersonalNoteTable(tables.Table):
         if value:
             badges = ""
             for extra_mark in value:
-                content = extra_mark.short_name
+                content = extra_mark.name
                 badges += render_to_string("components/materialize-chips.html", context=dict(content=content))
-            return badges
+            return mark_safe(badges)
         else:
             return "–"
 
     class Meta:
         model = PersonalNote
         sequence = (
-            "year", "week", "personal_note_date", "lesson_period_period", "lesson_period_lesson",
-            "absent", "excused", "late", "extra_marks", "remarks")
+            "selected", "year", "week", "personal_note_date", "lesson_period_period",
+            "lesson_period_lesson", "absent", "excused", "late", "extra_marks", "remarks"
+        )
         exclude = ("site", "id", "extended_data", "person", "lesson_period", "excuse_type")
         template_name = "django_tables2/materialize.html"
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/person.html b/aleksis/apps/alsijil/templates/alsijil/class_register/person.html
index efe9e9b2a..be91e703b 100644
--- a/aleksis/apps/alsijil/templates/alsijil/class_register/person.html
+++ b/aleksis/apps/alsijil/templates/alsijil/class_register/person.html
@@ -4,6 +4,7 @@
 {% load data_helpers %}
 {% load week_helpers %}
 {% load i18n %}
+{% load material_form %}
 {% load static %}
 {% load render_table from django_tables2 %}
 
@@ -174,6 +175,11 @@
   </div>
   <div class="col s12" id="overview">
     <h5>{% trans "Relevant personal notes" %}</h5>
+  <form action="" method="post">
+      {% csrf_token %}
+  {%  form form=action_form %}{% endform %}
+  <button type="submit">Enter</button>
+  </form>
     {% render_table personal_notes_table %}
     <ul class="collapsible">
       <li>
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/mark_as_buttons.html b/aleksis/apps/alsijil/templates/alsijil/partials/mark_as_buttons.html
index 5b198afa4..bce78468d 100644
--- a/aleksis/apps/alsijil/templates/alsijil/partials/mark_as_buttons.html
+++ b/aleksis/apps/alsijil/templates/alsijil/partials/mark_as_buttons.html
@@ -1,12 +1,12 @@
 {% load i18n %}
-<button type="submit" class="btn-flat tooltipped" name="excuse_type" value="e" title="{% trans "Excused" %}"
-        data-position="bottom" data-tooltip="{% trans "Excused" %}" style="width: 50px;">
-  {% trans "e" %}
+<span class="input-field inline">
+    <select class="inline" name="excuse_type">
+        <option value="e" selected>{% trans "Excused" %}</option>
+        {% for excuse_type in excuse_types %}
+            <option value="{{ excuse_type.pk }}">{{ excuse_type.name }}</option>
+        {% endfor %}
+    </select>
+</span>
+<button type="submit" class="btn secondary-color">
+    {% trans "Submit" %}<i class="material-icons right">send</i>
 </button>
-{% for excuse_type in excuse_types %}
-  <button type="submit" class="btn-flat tooltipped" value="{{ excuse_type.pk }}" name="excuse_type"
-          title="{{ excuse_type.name }}" data-position="bottom" data-tooltip="{{ excuse_type.name }}"
-          style="width: 50px;">
-    {{ excuse_type.short_name }}
-  </button>
-{% endfor %}
diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py
index 5d3344802..5479745a4 100644
--- a/aleksis/apps/alsijil/views.py
+++ b/aleksis/apps/alsijil/views.py
@@ -19,7 +19,7 @@ from django.views.generic import DetailView
 
 import reversion
 from calendarweek import CalendarWeek
-from django_tables2 import SingleTableView
+from django_tables2 import SingleTableView, RequestConfig
 from reversion.views import RevisionMixin
 from rules.contrib.views import PermissionRequiredMixin, permission_required
 
@@ -47,6 +47,7 @@ from .forms import (
     PersonalNoteFormSet,
     RegisterAbsenceForm,
     SelectForm,
+    PersonOverviewForm,
 )
 from .models import (
     ExcuseType,
@@ -724,96 +725,6 @@ def overview_person(request: HttpRequest, id_: Optional[int] = None) -> HttpResp
     )
     context["person"] = person
 
-    if request.method == "POST":
-        if request.POST.get("excuse_type"):
-            # Get excuse type
-            excuse_type = request.POST["excuse_type"]
-            found = False
-            if excuse_type == "e":
-                excuse_type = None
-                found = True
-            else:
-                try:
-                    excuse_type = ExcuseType.objects.get(pk=int(excuse_type))
-                    found = True
-                except (ExcuseType.DoesNotExist, ValueError):
-                    pass
-
-            if found:
-                if request.POST.get("excuse_multiple") and request.POST.get("selected_notes"):
-                    if not request.user.has_perm(
-                            "alsijil.edit_person_overview_personalnote", person
-                    ):
-                        raise PermissionDenied()
-
-                    lesson_pks = request.POST.getlist("selected_notes")
-
-                    lesson_pks = filter(str.isnumeric, lesson_pks)
-
-                    notes = person.personal_notes.filter(
-                        pk__in=lesson_pks,
-                        absent=True,
-                        excused=False,
-                    )
-                    for note in notes:
-                        note.excused = True
-                        note.excuse_type = excuse_type
-                        with reversion.create_revision():
-                            reversion.set_user(request.user)
-                            note.save()
-
-                    messages.success(request, _("The absences have been marked as excused."))
-
-                elif request.POST.get("date"):
-                    # Mark absences on date as excused
-                    try:
-                        date = datetime.strptime(request.POST["date"], "%Y-%m-%d").date()
-
-                        if not request.user.has_perm(
-                            "alsijil.edit_person_overview_personalnote", person
-                        ):
-                            raise PermissionDenied()
-
-                        notes = person.personal_notes.filter(absent=True, excused=False,).filter(
-                            Q(
-                                week=date.isocalendar()[1],
-                                lesson_period__period__weekday=date.weekday(),
-                                lesson_period__lesson__validity__date_start__lte=date,
-                                lesson_period__lesson__validity__date_end__gte=date,
-                            )
-                            | Q(
-                                extra_lesson__week=date.isocalendar()[1],
-                                extra_lesson__period__weekday=date.weekday(),
-                            )
-                        )
-                        for note in notes:
-                            note.excused = True
-                            note.excuse_type = excuse_type
-                            with reversion.create_revision():
-                                reversion.set_user(request.user)
-                                note.save()
-
-                        messages.success(request, _("The absences have been marked as excused."))
-                    except ValueError:
-                        pass
-                elif request.POST.get("personal_note"):
-                    # Mark specific absence as excused
-                    try:
-                        note = PersonalNote.objects.get(pk=int(request.POST["personal_note"]))
-                        if not request.user.has_perm("alsijil.edit_personalnote", note):
-                            raise PermissionDenied()
-                        if note.absent:
-                            note.excused = True
-                            note.excuse_type = excuse_type
-                            with reversion.create_revision():
-                                reversion.set_user(request.user)
-                                note.save()
-                            messages.success(request, _("The absence has been marked as excused."))
-                    except (PersonalNote.DoesNotExist, ValueError):
-                        pass
-
-                person.refresh_from_db()
-
     person_personal_notes = person.personal_notes.all().prefetch_related(
         "lesson_period__lesson__groups",
         "lesson_period__lesson__teachers",
@@ -872,7 +783,18 @@ def overview_person(request: HttpRequest, id_: Optional[int] = None) -> HttpResp
     context["personal_notes"] = personal_notes
     context["excuse_types"] = ExcuseType.objects.all()
 
-    context["personal_notes_table"] = PersonalNoteTable(personal_notes)
+    form = PersonOverviewForm(request, request.POST or None, queryset=personal_notes)
+    if request.method == "POST":
+        if form.is_valid():
+            with reversion.create_revision():
+                reversion.set_user(request.user)
+                form.execute()
+        person.refresh_from_db()
+    context["action_form"] = form
+    table = PersonalNoteTable(personal_notes)
+    RequestConfig(request, paginate={"per_page": 20}).configure(table)
+    context["personal_notes_table"] = table
+    print(table.columns, table.rows, sep="\n"*3)
 
     extra_marks = ExtraMark.objects.all()
     excuse_types = ExcuseType.objects.all()
-- 
GitLab