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

Merge branch 'master' into 73-add-rules-and-permissions

parents c8791096 43e6be1f
No related branches found
No related tags found
1 merge request!49Resolve "Add rules and permissions"
from datetime import datetime
from aleksis.apps.chronos.managers import TimetableType
from django import forms
from django.core.exceptions import ValidationError
from django.db.models import Count
from django.utils.translation import gettext_lazy as _
from django_select2.forms import Select2Widget
from material import Layout, Row
from material import Row, Layout
from aleksis.apps.chronos.managers import TimetableType
from aleksis.core.models import Group, Person
from .models import LessonDocumentation, PersonalNote, PersonalNoteFilter
......@@ -43,15 +42,17 @@ class SelectForm(forms.Form):
layout = Layout(Row("group", "teacher"))
group = forms.ModelChoiceField(
queryset=Group.objects.annotate(lessons_count=Count("lessons")).filter(lessons_count__gt=0),
queryset=Group.objects.annotate(lessons_count=Count("lessons")).filter(
lessons_count__gt=0
),
label=_("Group"),
required=False,
widget=Select2Widget,
)
teacher = forms.ModelChoiceField(
queryset=Person.objects.annotate(lessons_count=Count("lessons_as_teacher")).filter(
lessons_count__gt=0
),
queryset=Person.objects.annotate(
lessons_count=Count("lessons_as_teacher")
).filter(lessons_count__gt=0),
label=_("Teacher"),
required=False,
widget=Select2Widget,
......@@ -60,7 +61,7 @@ class SelectForm(forms.Form):
def clean(self) -> dict:
data = super().clean()
if data.get("group") and not data.get("teacher") :
if data.get("group") and not data.get("teacher"):
type_ = TimetableType.GROUP
instance = data["group"]
elif data.get("teacher") and not data.get("group"):
......@@ -75,24 +76,22 @@ class SelectForm(forms.Form):
data["instance"] = instance
return data
PersonalNoteFormSet = forms.modelformset_factory(
PersonalNote, form=PersonalNoteForm, max_num=0, extra=0
)
class RegisterAbsenceForm(forms.Form):
layout = Layout(Row("date_start", "date_end"),
Row("from_period"),
Row("absent", "excused"),
Row("person"),
Row("remarks")
)
date_start = forms.DateField(
label=_("Start date"), initial=datetime.today
)
date_end = forms.DateField(
label=_("End date"), initial=datetime.today
layout = Layout(
Row("date_start", "date_end"),
Row("from_period"),
Row("absent", "excused"),
Row("person"),
Row("remarks"),
)
date_start = forms.DateField(label=_("Start date"), initial=datetime.today)
date_end = forms.DateField(label=_("End date"), initial=datetime.today)
from_period = forms.IntegerField(label=_("From period"), initial=0, min_value=0)
person = forms.ModelChoiceField(
label=_("Person"), queryset=Person.objects.all(), widget=Select2Widget
......
# Generated by Django 3.0.6 on 2020-05-29 10:29
import aleksis.apps.alsijil.models
import django.contrib.postgres.fields.jsonb
import django.contrib.sites.managers
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
import aleksis.apps.alsijil.models
class Migration(migrations.Migration):
......@@ -12,74 +13,195 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('core', '0001_initial'),
('chronos', '0001_initial'),
('sites', '0002_alter_domain_unique'),
("core", "0001_initial"),
("chronos", "0001_initial"),
("sites", "0002_alter_domain_unique"),
]
operations = [
migrations.CreateModel(
name='PersonalNoteFilter',
name="PersonalNoteFilter",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
('identifier', models.CharField(max_length=30, unique=True, validators=[aleksis.apps.alsijil.models.isidentifier], verbose_name='Identifier')),
('description', models.CharField(blank=True, max_length=60, unique=True, verbose_name='Description')),
('regex', models.CharField(max_length=100, unique=True, verbose_name='Match expression')),
('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"extended_data",
django.contrib.postgres.fields.jsonb.JSONField(
default=dict, editable=False
),
),
(
"identifier",
models.CharField(
max_length=30,
unique=True,
validators=[aleksis.apps.alsijil.models.isidentifier],
verbose_name="Identifier",
),
),
(
"description",
models.CharField(
blank=True,
max_length=60,
unique=True,
verbose_name="Description",
),
),
(
"regex",
models.CharField(
max_length=100, unique=True, verbose_name="Match expression"
),
),
(
"site",
models.ForeignKey(
default=1,
editable=False,
on_delete=django.db.models.deletion.CASCADE,
to="sites.Site",
),
),
],
options={
'verbose_name': 'Personal note filter',
'verbose_name_plural': 'Personal note filters',
'ordering': ['identifier'],
"verbose_name": "Personal note filter",
"verbose_name_plural": "Personal note filters",
"ordering": ["identifier"],
},
managers=[
('objects', django.contrib.sites.managers.CurrentSiteManager()),
],
managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
),
migrations.CreateModel(
name='PersonalNote',
name="PersonalNote",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
('week', models.IntegerField()),
('absent', models.BooleanField(default=False)),
('late', models.IntegerField(default=0)),
('excused', models.BooleanField(default=False)),
('remarks', models.CharField(blank=True, max_length=200)),
('lesson_period', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='personal_notes', to='chronos.LessonPeriod')),
('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='personal_notes', to='core.Person')),
('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"extended_data",
django.contrib.postgres.fields.jsonb.JSONField(
default=dict, editable=False
),
),
("week", models.IntegerField()),
("absent", models.BooleanField(default=False)),
("late", models.IntegerField(default=0)),
("excused", models.BooleanField(default=False)),
("remarks", models.CharField(blank=True, max_length=200)),
(
"lesson_period",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="personal_notes",
to="chronos.LessonPeriod",
),
),
(
"person",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="personal_notes",
to="core.Person",
),
),
(
"site",
models.ForeignKey(
default=1,
editable=False,
on_delete=django.db.models.deletion.CASCADE,
to="sites.Site",
),
),
],
options={
'verbose_name': 'Personal note',
'verbose_name_plural': 'Personal notes',
'ordering': ['lesson_period__lesson__date_start', 'week', 'lesson_period__period__weekday', 'lesson_period__period__period', 'person__last_name', 'person__first_name'],
'unique_together': {('lesson_period', 'week', 'person')},
"verbose_name": "Personal note",
"verbose_name_plural": "Personal notes",
"ordering": [
"lesson_period__lesson__date_start",
"week",
"lesson_period__period__weekday",
"lesson_period__period__period",
"person__last_name",
"person__first_name",
],
"unique_together": {("lesson_period", "week", "person")},
},
managers=[
('objects', django.contrib.sites.managers.CurrentSiteManager()),
],
managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
),
migrations.CreateModel(
name='LessonDocumentation',
name="LessonDocumentation",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
('week', models.IntegerField()),
('topic', models.CharField(blank=True, max_length=200, verbose_name='Lesson topic')),
('homework', models.CharField(blank=True, max_length=200, verbose_name='Homework')),
('lesson_period', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documentations', to='chronos.LessonPeriod')),
('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"extended_data",
django.contrib.postgres.fields.jsonb.JSONField(
default=dict, editable=False
),
),
("week", models.IntegerField()),
(
"topic",
models.CharField(
blank=True, max_length=200, verbose_name="Lesson topic"
),
),
(
"homework",
models.CharField(
blank=True, max_length=200, verbose_name="Homework"
),
),
(
"lesson_period",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="documentations",
to="chronos.LessonPeriod",
),
),
(
"site",
models.ForeignKey(
default=1,
editable=False,
on_delete=django.db.models.deletion.CASCADE,
to="sites.Site",
),
),
],
options={
'verbose_name': 'Lesson documentation',
'verbose_name_plural': 'Lesson documentations',
'ordering': ['lesson_period__lesson__date_start', 'week', 'lesson_period__period__weekday', 'lesson_period__period__period'],
'unique_together': {('lesson_period', 'week')},
"verbose_name": "Lesson documentation",
"verbose_name_plural": "Lesson documentations",
"ordering": [
"lesson_period__lesson__date_start",
"week",
"lesson_period__period__weekday",
"lesson_period__period__period",
],
"unique_together": {("lesson_period", "week")},
},
managers=[
('objects', django.contrib.sites.managers.CurrentSiteManager()),
],
managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),],
),
]
from datetime import date
from django.db.models import Exists, F, OuterRef, QuerySet
from django.db.models import Exists, OuterRef, QuerySet
from django.utils.translation import gettext as _
from calendarweek import CalendarWeek
......@@ -20,9 +20,8 @@ def mark_absent(
excused: bool = False,
remarks: str = "",
):
""" Mark a person absent for all lessons in a day, optionally starting with
a selected period number.
"""Mark a person absent for all lessons in a day, optionally starting with a selected period number.
This function creates `PersonalNote` objects for every `LessonPeriod` the person
participates in on the selected day and marks them as absent/excused.
......@@ -32,7 +31,6 @@ def mark_absent(
:Authors:
- Dominik George <dominik.george@teckids.org>
"""
wanted_week = CalendarWeek.from_date(day)
# Get all lessons of this person on the specified day
......@@ -59,18 +57,17 @@ def mark_absent(
@LessonPeriod.method
def get_personal_notes(self, persons: QuerySet, wanted_week: CalendarWeek):
""" Get all personal notes for that lesson in a specified week.
"""Get all personal notes for that lesson in a specified week.
Returns all linked `PersonalNote` objects, filtered by the given weeek,
creating those objects that haven't been created yet.
..note:: Only available when AlekSIS-App-Alsijil is installed.
:Date: 2019-11-09
:Authors:
- Dominik George <dominik.george@teckids.org>
"""
# Find all persons in the associated groups that do not yet have a personal note for this lesson
missing_persons = persons.annotate(
no_personal_notes=~Exists(
......
......@@ -9,11 +9,15 @@ def isidentifier(value: str) -> bool:
class PersonalNote(ExtensibleModel):
""" A personal note about a single person. Used in the class register to note
absences, excuses and remarks about a student in a single lesson period.
"""A personal note about a single person.
Used in the class register to note absences, excuses
and remarks about a student in a single lesson period.
"""
person = models.ForeignKey("core.Person", models.CASCADE, related_name="personal_notes")
person = models.ForeignKey(
"core.Person", models.CASCADE, related_name="personal_notes"
)
week = models.IntegerField()
lesson_period = models.ForeignKey(
......@@ -41,8 +45,9 @@ class PersonalNote(ExtensibleModel):
class LessonDocumentation(ExtensibleModel):
""" A documentation on a single lesson period. Non-personal, includes
the topic and homework of the lesson.
"""A documentation on a single lesson period.
Non-personal, includes the topic and homework of the lesson.
"""
week = models.IntegerField()
......@@ -66,16 +71,21 @@ class LessonDocumentation(ExtensibleModel):
class PersonalNoteFilter(ExtensibleModel):
""" A filter definition that can generate statistics on personal note texts. """
"""A filter definition that can generate statistics on personal note texts."""
identifier = models.CharField(
verbose_name=_("Identifier"), max_length=30, validators=[isidentifier], unique=True,
verbose_name=_("Identifier"),
max_length=30,
validators=[isidentifier],
unique=True,
)
description = models.CharField(
verbose_name=_("Description"), max_length=60, blank=True, unique=True
)
regex = models.CharField(verbose_name=_("Match expression"), max_length=100, unique=True)
regex = models.CharField(
verbose_name=_("Match expression"), max_length=100, unique=True
)
class Meta:
verbose_name = _("Personal note filter")
......
......@@ -21,6 +21,10 @@
{% for teacher in lesson_period.get_teachers.all %}
{{ teacher.short_name }}
{% endfor %}
<span class="right">
{% include "alsijil/partials/lesson_status_icon.html" with period=lesson_period css_class="medium" %}
</span>
{% endblock %}
{% block content %}
......
......@@ -46,6 +46,7 @@
<table class="striped datatable">
<thead>
<tr>
<th></th>
<th>{% blocktrans %}Period{% endblocktrans %}</th>
<th>{% blocktrans %}Subject{% endblocktrans %}</th>
<th>{% blocktrans %}Teachers{% endblocktrans %}</th>
......@@ -53,24 +54,10 @@
</thead>
<tbody>
{% for period in periods %}
<tr class="
{% if period.has_documentation %}
success
{% else %}
{% weekday_to_date week period.period.weekday as current_date %}
{% today as today %}
{% if current_date < today %}
error
{% else %}
{% if period.get_substitution %}
warning
{% if period.get_substitution.cancelled %}
alsijil-lesson-cancelled
{% endif %}
{% endif %}
{% endif %}
{% endif %}
">
<tr>
<td class="center-align">
{% include "alsijil/partials/lesson_status_icon.html" with period=period %}
</td>
<td class="tr-link">
<a class="tr-link" href="{% url 'lesson_by_week_and_period' week.year week.week period.id %}">
{{ period.period.period }}.
......
{% load i18n week_helpers %}
{% now_datetime as now_dt %}
{% if period.has_documentation %}
<i class="material-icons green-text tooltipped {{ css_class }}" data-position="bottom" data-tooltip="{% trans "Data complete" %}" title="{% trans "Data complete" %}">check_circle</i>
{% else %}
{% period_to_time_start week period.period as time_start %}
{% period_to_time_end week period.period as time_end %}
{% if now_dt > time_end %}
<i class="material-icons red-text tooltipped {{ css_class }}" data-position="bottom" data-tooltip="{% trans "Missing data" %}" title="{% trans "Missing data" %}">history</i>
{% elif now_dt > time_start and now_dt < time_end %}
<i class="material-icons orange-text tooltipped {{ css_class }}" data-position="bottom" data-tooltip="{% trans "Pending" %}" title="{% trans "Pending" %}">more_horiz</i>
{% else %}
{% if period.get_substitution %}
{% if period.get_substitution.cancelled %}
<i class="material-icons red-text tooltipped {{ css_class }}" data-position="bottom" data-tooltip="{% trans "Lesson cancelled" %}" title="{% trans "Lesson cancelled" %}">cancel</i>
{% else %}
<i class="material-icons orange-text tooltipped {{ css_class }}" data-position="bottom" data-tooltip="{% trans "Substitution" %}" title="{% trans "Substitution" %}">update</i>
{% endif %}
{% endif %}
{% endif %}
{% endif %}
......@@ -12,13 +12,29 @@ urlpatterns = [
path("week", views.week_view, name="week_view"),
path("week/<int:year>/<int:week>", views.week_view, name="week_view_by_week"),
path("week/<str:type_>/<int:id_>/", views.week_view, name="week_view"),
path("week/<int:year>/<int:week>/<str:type_>/<int:id_>/", views.week_view, name="week_view_by_week"),
path("print/group/<int:id_>", views.full_register_group, name="full_register_group"),
path(
"week/<int:year>/<int:week>/<str:type_>/<int:id_>/",
views.week_view,
name="week_view_by_week",
),
path(
"print/group/<int:id_>", views.full_register_group, name="full_register_group"
),
path("absence/new", views.register_absence, name="register_absence"),
path("filters/list", views.list_personal_note_filters, name="list_personal_note_filters",),
path("filters/create", views.edit_personal_note_filter, name="create_personal_note_filter",),
path(
"filters/edit/<int:id>", views.edit_personal_note_filter, name="edit_personal_note_filter",
"filters/list",
views.list_personal_note_filters,
name="list_personal_note_filters",
),
path(
"filters/create",
views.edit_personal_note_filter,
name="create_personal_note_filter",
),
path(
"filters/edit/<int:id_>",
views.edit_personal_note_filter,
name="edit_personal_note_filter",
),
path(
"filters/delete/<int:id_>",
......
from datetime import date, datetime, timedelta
from typing import Optional
from aleksis.apps.chronos.managers import TimetableType
from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk
from django.core.exceptions import PermissionDenied
from django.db.models import Count, Exists, OuterRef, Q, Sum
from django.db.models import Count, Exists, F, OuterRef, Q, Subquery, Sum
from django.http import Http404, HttpRequest, HttpResponse, HttpResponseNotFound
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
......@@ -14,7 +12,9 @@ from calendarweek import CalendarWeek
from django_tables2 import RequestConfig
from rules.contrib.views import permission_required
from aleksis.apps.chronos.managers import TimetableType
from aleksis.apps.chronos.models import LessonPeriod
from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk
from aleksis.core.models import Group, Person, SchoolTerm
from aleksis.core.util import messages
from aleksis.core.util.core_helpers import objectgetter_optional
......@@ -51,24 +51,31 @@ def lesson(
if not (year and week and period_id):
if lesson_period:
return redirect(
"lesson_by_week_and_period", wanted_week.year, wanted_week.week, lesson_period.pk,
"lesson_by_week_and_period",
wanted_week.year,
wanted_week.week,
lesson_period.pk,
)
else:
raise Http404(
_(
"You either selected an invalid lesson or there is currently no lesson in progress."
"You either selected an invalid lesson or "
"there is currently no lesson in progress."
)
)
if (
datetime.combine(
wanted_week[lesson_period.period.weekday - 1], lesson_period.period.time_start,
wanted_week[lesson_period.period.weekday - 1],
lesson_period.period.time_start,
)
> datetime.now()
and not request.user.is_superuser
):
raise PermissionDenied(
_("You are not allowed to create a lesson documentation for a lesson in the future.")
_(
"You are not allowed to create a lesson documentation for a lesson in the future."
)
)
context["lesson_period"] = lesson_period
......@@ -80,7 +87,9 @@ def lesson(
lesson_period=lesson_period, week=wanted_week.week
)
lesson_documentation_form = LessonDocumentationForm(
request.POST or None, instance=lesson_documentation, prefix="lesson_documentation",
request.POST or None,
instance=lesson_documentation,
prefix="lesson_documentation",
)
# Create a formset that holds all personal notes for all persons in this lesson
......@@ -117,7 +126,11 @@ def lesson(
@permission_required("alsijil.view_week", fn=get_instance_by_pk)
def week_view(
request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None, type_: Optional[str] = None, id_: Optional[int] = None
request: HttpRequest,
year: Optional[int] = None,
week: Optional[int] = None,
type_: Optional[str] = None,
id_: Optional[int] = None,
) -> HttpResponse:
context = {}
......@@ -166,8 +179,13 @@ def week_view(
if "type_" not in select_form.cleaned_data:
return redirect("week_view_by_week", wanted_week.year, wanted_week.week)
else:
return redirect("week_view_by_week", wanted_week.year, wanted_week.week,
select_form.cleaned_data["type_"].value, select_form.cleaned_data["instance"].pk)
return redirect(
"week_view_by_week",
wanted_week.year,
wanted_week.week,
select_form.cleaned_data["type_"].value,
select_form.cleaned_data["instance"].pk,
)
if type_ == TimetableType.GROUP:
group = instance
......@@ -189,28 +207,33 @@ def week_view(
.prefetch_related("personal_notes")
.annotate(
absences_count=Count(
"personal_notes__absent",
"personal_notes",
filter=Q(
personal_notes__lesson_period__in=lesson_periods_pk,
personal_notes__lesson_period__in=lesson_periods,
personal_notes__week=wanted_week.week,
personal_notes__absent=True,
),
distinct=True,
),
unexcused_count=Count(
"personal_notes__absent",
"personal_notes",
filter=Q(
personal_notes__lesson_period__in=lesson_periods_pk,
personal_notes__lesson_period__in=lesson_periods,
personal_notes__week=wanted_week.week,
personal_notes__absent=True,
personal_notes__excused=False,
),
distinct=True,
),
tardiness_sum=Sum(
"personal_notes__late",
filter=Q(
personal_notes__lesson_period__in=lesson_periods_pk,
tardiness_sum=Subquery(
Person.objects.filter(
pk=OuterRef("pk"),
personal_notes__lesson_period__in=lesson_periods,
personal_notes__week=wanted_week.week,
),
)
.distinct()
.annotate(tardiness_sum=Sum("personal_notes__late"))
.values("tardiness_sum")
),
)
)
......@@ -218,7 +241,9 @@ def week_view(
persons = None
# Resort lesson periods manually because an union queryset doesn't support order_by
lesson_periods = sorted(lesson_periods, key=lambda x: (x.period.weekday, x.period.period))
lesson_periods = sorted(
lesson_periods, key=lambda x: (x.period.weekday, x.period.period)
)
context["week"] = wanted_week
context["lesson_periods"] = lesson_periods
......@@ -250,8 +275,8 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
# Get all lesson periods for the selected group
lesson_periods = (
LessonPeriod.objects.filter_group(group)
.distinct()
.prefetch_related("documentations", "personal_notes")
.distinct()
.prefetch_related("documentations", "personal_notes")
)
current_school_term = SchoolTerm.current
......@@ -260,8 +285,7 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
return HttpResponseNotFound(_("There is no current school term."))
weeks = CalendarWeek.weeks_within(
current_school_term.date_start,
current_school_term.date_end,
current_school_term.date_start, current_school_term.date_end,
)
periods_by_day = {}
......@@ -271,10 +295,16 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
if lesson_period.lesson.date_start <= day <= lesson_period.lesson.date_end:
documentations = list(
filter(lambda d: d.week == week.week, lesson_period.documentations.all(),)
filter(
lambda d: d.week == week.week,
lesson_period.documentations.all(),
)
)
notes = list(
filter(lambda d: d.week == week.week, lesson_period.personal_notes.all(),)
filter(
lambda d: d.week == week.week,
lesson_period.personal_notes.all(),
)
)
substitution = lesson_period.get_substitution(week.week)
......@@ -283,7 +313,9 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
)
persons = group.members.annotate(
absences_count=Count("personal_notes__absent", filter=Q(personal_notes__absent=True)),
absences_count=Count(
"personal_notes__absent", filter=Q(personal_notes__absent=True)
),
unexcused=Count(
"personal_notes__absent",
filter=Q(personal_notes__absent=True, personal_notes__excused=False),
......@@ -299,7 +331,9 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
"_personal_notes_with_%s"
% personal_note_filter.identifier: Count(
"personal_notes__remarks",
filter=Q(personal_notes__remarks__iregex=personal_note_filter.regex),
filter=Q(
personal_notes__remarks__iregex=personal_note_filter.regex
),
)
}
)
......@@ -362,7 +396,9 @@ def list_personal_note_filters(request: HttpRequest) -> HttpResponse:
@permission_required("alsijil.edit_personal_note_filter", fn=objectgetter_optional(PersonalNoteFilter, None, False))
def edit_personal_note_filter(request: HttpRequest, id: Optional["int"] = None) -> HttpResponse:
def edit_personal_note_filter(
request: HttpRequest, id_: Optional["int"] = None
) -> HttpResponse:
context = {}
if id:
......
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