Skip to content
Snippets Groups Projects
Verified Commit 7936ea14 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Add support for sending notifications in "All lessons" via actions

parent f122b128
No related branches found
No related tags found
1 merge request!152Overview of all register objects
from typing import Sequence
from django.contrib import messages
from django.contrib.humanize.templatetags.humanize import apnumber
from django.http import HttpRequest
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from aleksis.core.models import Notification
def send_request_to_check_entry(modeladmin, request: HttpRequest, selected_items: Sequence[dict]):
"""Send notifications to the teachers of the selected register objects.
Action for use with ``RegisterObjectTable`` and ``RegisterObjectActionForm``.
"""
# Group class register entries by teachers so each teacher gets just one notification
grouped_by_teachers = {}
for entry in selected_items:
teachers = entry["register_object"].get_teachers().all()
for teacher in teachers:
grouped_by_teachers.setdefault(teacher, [])
grouped_by_teachers[teacher].append(entry)
for teacher, items in grouped_by_teachers.items():
title = _(
f"{request.user.person.addressing_name} wants you to check some class register entries."
)
msg = _("Please check if the following class register entries are complete and correct:\n")
# Add one line for each entry to check
for entry in items:
reg_object = entry["register_object"]
date = entry["date"]
msg += f"- {reg_object} ({date})\n"
Notification.objects.create(
title=title,
description=msg,
sender=request.user.person.addressing_name,
recipient=teacher,
link=request.build_absolute_uri(reverse("overview_me")),
)
count_teachers = len(grouped_by_teachers.keys())
count_items = len(list)
messages.success(
request,
_(
f"We have successfully sent notifications to "
f"{apnumber(count_teachers)} persons for {apnumber(count_items)} lessons."
),
)
send_request_to_check_entry.short_description = _("Notify teacher to check data")
......@@ -14,10 +14,12 @@ from material import Fieldset, Layout, Row
from aleksis.apps.chronos.managers import TimetableType
from aleksis.apps.chronos.models import Subject, TimePeriod
from aleksis.core.forms import ListActionForm
from aleksis.core.models import Group, Person, SchoolTerm
from aleksis.core.util.core_helpers import get_site_preferences
from aleksis.core.util.predicates import check_global_permission
from .actions import send_request_to_check_entry
from .models import (
ExcuseType,
ExtraMark,
......@@ -312,3 +314,7 @@ class FilterRegisterObjectForm(forms.Form):
Q(lessons__groups__in=groups) | Q(extra_lessons__groups__in=groups)
).distinct()
self.fields["subject"].queryset = subject_qs
class RegisterObjectActionForm(ListActionForm):
actions = [send_request_to_check_entry]
......@@ -4,6 +4,8 @@ from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from django_tables2.utils import A
from aleksis.core.tables import SelectColumn
class ExtraMarkTable(tables.Table):
class Meta:
......@@ -81,6 +83,12 @@ class GroupRoleTable(tables.Table):
class RegisterObjectTable(tables.Table):
"""Table to show all register objects in an overview.
.. warning::
Works only with ``generate_list_of_all_register_objects``.
"""
class Meta:
attrs = {"class": "highlight responsive-table"}
......@@ -103,3 +111,15 @@ class RegisterObjectTable(tables.Table):
register_object=value,
),
)
class RegisterObjectSelectTable(RegisterObjectTable):
"""Table to show all register objects with multi-select support.
More information at ``RegisterObjectTable``
"""
selected = SelectColumn()
class Meta(RegisterObjectTable.Meta):
sequence = ("selected", "...")
......@@ -14,17 +14,46 @@
{% endblock %}
{% block content %}
<h5>{% trans "Lesson filter" %}</h5>
<form action="" method="get">
{% form form=filter_form %}{% endform %}
<button type="submit" class="btn waves-effect waves-light">
<i class="material-icons left">refresh</i>
{% trans "Update filters" %}
</button>
</form>
<h5>{% trans "Lesson table" %}</h5>
<div class="card">
<div class="card-content">
<div class="card-title">{% trans "Lesson filter" %}</div>
<form action="" method="get">
{% form form=filter_form %}{% endform %}
<button type="submit" class="btn waves-effect waves-light">
<i class="material-icons left">refresh</i>
{% trans "Update filters" %}
</button>
</form>
</div>
</div>
{% if table %}
{% render_table table %}
<div class="card">
<div class="card-content">
<form action="" method="post">
{% csrf_token %}
<div class="row">
<div class="col s12 m4 l4 xl6">
<div class="card-title">{% trans "Lesson table" %}</div>
</div>
<div class="col s12 m8 l8 xl6">
<div class="col s12 m8">
{% form form=action_form %}{% endform %}</div>
<div class="col s12 m4">
<button type="submit" class="btn waves-effect waves-primary">
{% trans "Execute" %}
<i class="material-icons right">send</i>
</button>
</div>
</div>
</div>
{% render_table table %}
</form>
</div>
</div>
{% endif %}
<script src="{% static "js/multi_select.js" %}"></script>
{% endblock %}
\ No newline at end of file
......@@ -255,6 +255,7 @@ def generate_list_of_all_register_objects(filter_dict: Dict[str, Any]) -> List[d
# Build table entry
entry = {
"pk": f"{lesson_period.pk}_{week.year}_{week.week}",
"week": week,
"has_documentation": has_documentation,
"substitution": sub,
......@@ -306,6 +307,7 @@ def generate_list_of_all_register_objects(filter_dict: Dict[str, Any]) -> List[d
# Build table entry
entry = {
"pk": str(register_object.pk),
"has_documentation": has_documentation,
"register_object": register_object,
"date": day,
......
......@@ -49,6 +49,7 @@ from .forms import (
LessonDocumentationForm,
PersonalNoteFormSet,
RegisterAbsenceForm,
RegisterObjectActionForm,
SelectForm,
)
from .models import (
......@@ -59,7 +60,13 @@ from .models import (
LessonDocumentation,
PersonalNote,
)
from .tables import ExcuseTypeTable, ExtraMarkTable, GroupRoleTable, RegisterObjectTable
from .tables import (
ExcuseTypeTable,
ExtraMarkTable,
GroupRoleTable,
RegisterObjectSelectTable,
RegisterObjectTable,
)
from .util.alsijil_helpers import (
annotate_documentations,
generate_list_of_all_register_objects,
......@@ -1255,7 +1262,7 @@ class GroupRoleAssignmentDeleteView(
class AllRegisterObjectsView(PermissionRequiredMixin, View):
permission_required = "alsijil.view_register_objects_list"
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
def get_context_data(self, request):
context = {}
# Filter selectable groups by permissions
groups = Group.objects.all()
......@@ -1274,8 +1281,22 @@ class AllRegisterObjectsView(PermissionRequiredMixin, View):
context["filter_form"] = filter_form
register_objects = generate_list_of_all_register_objects(filter_dict)
self.action_form = RegisterObjectActionForm(request, register_objects, request.POST or None)
context["action_form"] = self.action_form
if register_objects:
table = RegisterObjectTable(register_objects)
RequestConfig(request,).configure(table) # paginate={"per_page": 100}
context["table"] = table
self.table = RegisterObjectSelectTable(register_objects)
RequestConfig(request, paginate={"per_page": 100}).configure(self.table)
context["table"] = self.table
return context
def get(self, request: HttpRequest) -> HttpResponse:
context = self.get_context_data(request)
return render(request, "alsijil/class_register/all_objects.html", context)
def post(self, request: HttpRequest):
context = self.get_context_data(request)
if self.action_form.is_valid():
self.action_form.execute()
return render(request, "alsijil/class_register/all_objects.html", context)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment