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