diff --git a/aleksis/apps/alsijil/forms.py b/aleksis/apps/alsijil/forms.py index 1ed20e8ef9295c73da7bce6aef38be6583d976f5..167a73dbc378e01b1189db08508a4d1828fc6ca2 100644 --- a/aleksis/apps/alsijil/forms.py +++ b/aleksis/apps/alsijil/forms.py @@ -11,7 +11,7 @@ from material import Layout, Row from aleksis.apps.chronos.managers import TimetableType from aleksis.core.models import Group, Person -from .models import ExcuseType, LessonDocumentation, PersonalNote, PersonalNoteFilter +from .models import ExcuseType, ExtraMark, LessonDocumentation, PersonalNote class LessonDocumentationForm(forms.ModelForm): @@ -28,7 +28,7 @@ class LessonDocumentationForm(forms.ModelForm): class PersonalNoteForm(forms.ModelForm): class Meta: model = PersonalNote - fields = ["absent", "late", "excused", "excuse_type", "remarks"] + fields = ["absent", "late", "excused", "excuse_type", "extra_marks", "remarks"] person_name = forms.CharField(disabled=True) @@ -109,12 +109,12 @@ class RegisterAbsenceForm(forms.Form): remarks = forms.CharField(label=_("Remarks"), max_length=30, required=False) -class PersonalNoteFilterForm(forms.ModelForm): - layout = Layout(Row("identifier", "description"), Row("regex")) +class ExtraMarkForm(forms.ModelForm): + layout = Layout("short_name", "name") class Meta: - model = PersonalNoteFilter - fields = ["identifier", "description", "regex"] + model = ExtraMark + fields = ["short_name", "name"] class ExcuseTypeForm(forms.ModelForm): diff --git a/aleksis/apps/alsijil/menus.py b/aleksis/apps/alsijil/menus.py index c5fea59e3790fba0407d55d760434ac2e3dd0d3d..a3ec5e9cc443a951ca5ddf4803361bd29e0637f9 100644 --- a/aleksis/apps/alsijil/menus.py +++ b/aleksis/apps/alsijil/menus.py @@ -31,14 +31,14 @@ MENUS = { "validators": ["menu_generator.validators.is_superuser"], }, { - "name": _("Personal note filters"), - "url": "list_personal_note_filters", - "icon": "filter_list", + "name": _("Excuse types"), + "url": "excuse_types", + "icon": "label", "validators": ["menu_generator.validators.is_superuser"], }, { - "name": _("Excuse types"), - "url": "excuse_types", + "name": _("Extra marks"), + "url": "extra_marks", "icon": "label", "validators": ["menu_generator.validators.is_superuser"], }, diff --git a/aleksis/apps/alsijil/migrations/0003_extra_mark.py b/aleksis/apps/alsijil/migrations/0003_extra_mark.py new file mode 100644 index 0000000000000000000000000000000000000000..307d9168a296661c8b520b55a1b5cd400b5e6df3 --- /dev/null +++ b/aleksis/apps/alsijil/migrations/0003_extra_mark.py @@ -0,0 +1,72 @@ +# Generated by Django 3.0.8 on 2020-07-12 12:43 + +import django.contrib.postgres.fields.jsonb +import django.contrib.sites.managers +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("sites", "0002_alter_domain_unique"), + ("alsijil", "0002_excuse_type"), + ] + + operations = [ + migrations.CreateModel( + name="ExtraMark", + 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 + ), + ), + ( + "short_name", + models.CharField( + max_length=255, unique=True, verbose_name="Short name" + ), + ), + ( + "name", + models.CharField(max_length=255, unique=True, verbose_name="Name"), + ), + ( + "site", + models.ForeignKey( + default=1, + editable=False, + on_delete=django.db.models.deletion.CASCADE, + to="sites.Site", + ), + ), + ], + options={ + "verbose_name": "Extra mark", + "verbose_name_plural": "Extra marks", + "ordering": ["short_name"], + }, + managers=[("objects", django.contrib.sites.managers.CurrentSiteManager()),], + ), + migrations.AddField( + model_name="personalnote", + name="extra_marks", + field=models.ManyToManyField( + blank=True, + null=True, + to="alsijil.ExtraMark", + verbose_name="Extra marks", + ), + ), + ] diff --git a/aleksis/apps/alsijil/migrations/0004_delete_personal_notes_filter.py b/aleksis/apps/alsijil/migrations/0004_delete_personal_notes_filter.py new file mode 100644 index 0000000000000000000000000000000000000000..d04764b80ebf1318630b71830ea8f3bb030c0468 --- /dev/null +++ b/aleksis/apps/alsijil/migrations/0004_delete_personal_notes_filter.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.8 on 2020-07-18 15:23 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("alsijil", "0003_extra_mark"), + ] + + operations = [ + migrations.DeleteModel(name="PersonalNoteFilter",), + ] diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py index a09aa38c9be47c6a73e5d1e86caf6f375c9045e1..ecba9f773f54ba1343fbf86ab2cd2fc7003a760b 100644 --- a/aleksis/apps/alsijil/model_extensions.py +++ b/aleksis/apps/alsijil/model_extensions.py @@ -1,5 +1,5 @@ from datetime import date -from typing import Optional, Union +from typing import Dict, Optional, Union from django.db.models import Exists, OuterRef, QuerySet @@ -8,7 +8,7 @@ from calendarweek import CalendarWeek from aleksis.apps.chronos.models import LessonPeriod from aleksis.core.models import Group, Person -from .models import ExcuseType, LessonDocumentation, PersonalNote +from .models import ExcuseType, ExtraMark, LessonDocumentation, PersonalNote @Person.method @@ -151,3 +151,20 @@ def get_tardinesses(self, week: Optional[CalendarWeek] = None) -> QuerySet: if not week: week = self.week return self.personal_notes.filter(week=week.week, late__gt=0) + + +@LessonPeriod.method +def get_extra_marks( + self, week: Optional[CalendarWeek] = None +) -> Dict[ExtraMark, QuerySet]: + """Get all statistics on extra marks for this lesson.""" + if not week: + week = self.week + + stats = {} + for extra_mark in ExtraMark.objects.all(): + qs = self.personal_notes.filter(week=week.week, extra_marks=extra_mark) + if qs: + stats[extra_mark] = qs + + return stats diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py index b8491bf6dcd3def3c8bb399fbc4e9721171db274..dee6542fccb73fb0e179e355487d3752a23f2736 100644 --- a/aleksis/apps/alsijil/models.py +++ b/aleksis/apps/alsijil/models.py @@ -61,6 +61,10 @@ class PersonalNote(ExtensibleModel): remarks = models.CharField(max_length=200, blank=True) + extra_marks = models.ManyToManyField( + "ExtraMark", null=True, blank=True, verbose_name=_("Extra marks") + ) + def save(self, *args, **kwargs): if self.excuse_type: self.excused = True @@ -107,24 +111,25 @@ class LessonDocumentation(ExtensibleModel): ] -class PersonalNoteFilter(ExtensibleModel): - """A filter definition that can generate statistics on personal note texts.""" +class ExtraMark(ExtensibleModel): + """A model for extra marks. - identifier = models.CharField( - verbose_name=_("Identifier"), - max_length=30, - validators=[isidentifier], - unique=True, - ) - description = models.CharField( - verbose_name=_("Description"), max_length=60, blank=True, unique=True - ) + Can be used for lesson-based counting of things (like forgotten homework). + """ - regex = models.CharField( - verbose_name=_("Match expression"), max_length=100, unique=True + short_name = models.CharField( + max_length=255, unique=True, verbose_name=_("Short name") ) + name = models.CharField(max_length=255, unique=True, verbose_name=_("Name")) + + def __str__(self): + return f"{self.name}" + + @property + def count_label(self): + return f"{self.short_name}_count" class Meta: - verbose_name = _("Personal note filter") - verbose_name_plural = _("Personal note filters") - ordering = ["identifier"] + ordering = ["short_name"] + verbose_name = _("Extra mark") + verbose_name_plural = _("Extra marks") diff --git a/aleksis/apps/alsijil/static/css/alsijil/lesson.css b/aleksis/apps/alsijil/static/css/alsijil/lesson.css index 83b7db8e90d35891d0106f73ebf6176b36d32262..6d57c7e9910063dc8dfe359c643e65765cb6ad9c 100644 --- a/aleksis/apps/alsijil/static/css/alsijil/lesson.css +++ b/aleksis/apps/alsijil/static/css/alsijil/lesson.css @@ -1,3 +1,11 @@ +.alsijil-check-box { + margin-right: 10px; +} + +.alsijil-check-box [type="checkbox"] { + padding-left: 30px; +} + .alsijil-lesson-cancelled { text-decoration: line-through; } diff --git a/aleksis/apps/alsijil/tables.py b/aleksis/apps/alsijil/tables.py index 3a6762c3744ead9d02e9917aa975b530b5e448f0..bd6b47a73f34c5e4343138993ee70162cd19b847 100644 --- a/aleksis/apps/alsijil/tables.py +++ b/aleksis/apps/alsijil/tables.py @@ -4,18 +4,23 @@ import django_tables2 as tables from django_tables2.utils import A -class PersonalNoteFilterTable(tables.Table): +class ExtraMarkTable(tables.Table): class Meta: attrs = {"class": "highlight"} - identifier = tables.Column() - description = tables.Column() - regex = tables.Column() - edit_filter = tables.LinkColumn( - "edit_personal_note_filter", + name = tables.LinkColumn("edit_extra_mark", args=[A("id")]) + short_name = tables.Column() + edit = tables.LinkColumn( + "edit_extra_mark", args=[A("id")], text=_("Edit"), - attrs={"a": {"class": "btn-flat waves-effect waves-orange"}}, + attrs={"a": {"class": "btn-flat waves-effect waves-orange orange-text"}}, + ) + delete = tables.LinkColumn( + "delete_extra_mark", + args=[A("id")], + text=_("Delete"), + attrs={"a": {"class": "btn-flat waves-effect waves-red red-text"}}, ) diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html b/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html index 71277476a293ec3b798c19866291e3326177b9d2..3f2c5be29af5e4dcc48df7678d4019665774a7c1 100644 --- a/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html +++ b/aleksis/apps/alsijil/templates/alsijil/class_register/lesson.html @@ -1,6 +1,6 @@ {# -*- engine:django -*- #} {% extends "core/base.html" %} -{% load week_helpers %} +{% load week_helpers material_form_internal %} {% load material_form i18n static %} {% block browser_title %}{% blocktrans %}Lesson{% endblocktrans %}{% endblock %} @@ -70,7 +70,7 @@ <div class="col s12" id="lesson-documentation"> {% with prev_lesson=lesson_period.prev prev_doc=prev_lesson.get_lesson_documentation %} - {% with prev_doc=prev_lesson.get_lesson_documentation absences=prev_lesson.get_absences tardinesses=prev_lesson.get_tardinesses %} + {% with prev_doc=prev_lesson.get_lesson_documentation absences=prev_lesson.get_absences tardinesses=prev_lesson.get_tardinesses extra_marks=prev_lesson.get_extra_marks %} {% if prev_doc %} {% weekday_to_date prev_lesson.week prev_lesson.period.weekday as prev_date %} @@ -116,6 +116,18 @@ <td>{% include "alsijil/partials/tardinesses.html" with notes=tardinesses %}</td> </tr> {% endif %} + + {% for extra_mark, notes in extra_marks.items %} + <tr> + <th>{{ extra_mark.name }}</th> + <td> + {% for note in notes %} + <span>{{ note.person }}{% if not forloop.last %},{% endif %}</span> + {% endfor %} + </td> + </tr> + {% endfor %} + </table> </div> </div> @@ -150,6 +162,7 @@ <th>{% blocktrans %}Tardiness{% endblocktrans %}</th> <th>{% blocktrans %}Excused{% endblocktrans %}</th> <th>{% blocktrans %}Excuse type{% endblocktrans %}</th> + <th>{% blocktrans %}Extra marks{% endblocktrans %}</th> <th>{% blocktrans %}Remarks{% endblocktrans %}</th> </tr> </thead> @@ -186,6 +199,19 @@ </label> </div> </td> + <td> + {% for group, items in form.extra_marks|select_options %} + {% for choice, value, selected in items %} + <label class="{% if selected %} active{% endif %} alsijil-check-box"> + <input type="checkbox" + {% if value == None or value == '' %}disabled{% else %}value="{{ value }}"{% endif %} + {% if selected %} checked="checked"{% endif %} + name="{{ form.extra_marks.html_name }}"> + <span>{{ choice }}</span> + </label> + {% endfor %} + {% endfor %} + </td> <td> <div class="input-field"> {{ form.remarks }} diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html b/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html index f1b94d845db471586524254cc582df7d3cebd2f0..594e876b65f561ff8489fae3226bbeacc24bbd98 100644 --- a/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html +++ b/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html @@ -1,7 +1,7 @@ {# -*- engine:django -*- #} {% extends "core/base.html" %} -{% load material_form i18n week_helpers static %} +{% load material_form i18n week_helpers static data_helpers %} {% block browser_title %}{% blocktrans %}Week view{% endblocktrans %}{% endblock %} @@ -96,6 +96,11 @@ <p class="card-text"> {% trans "Summed up tardiness" %}: {{ person.person.tardiness_sum }}' </p> + {% for extra_mark in extra_marks %} + <p class="card-text"> + {{ extra_mark.name }}: {{ person.person|get_dict:extra_mark.count_label }} + </p> + {% endfor %} {% for note in person.personal_notes %} {% if note.remarks %} <blockquote> diff --git a/aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html b/aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html new file mode 100644 index 0000000000000000000000000000000000000000..d0ee3a9055561df1f468692f79d794404a60c1d1 --- /dev/null +++ b/aleksis/apps/alsijil/templates/alsijil/extra_mark/create.html @@ -0,0 +1,17 @@ + {# -*- engine:django -*- #} + + {% extends "core/base.html" %} + {% load material_form i18n %} + + {% block browser_title %}{% blocktrans %}Create extra mark{% endblocktrans %}{% endblock %} + {% block page_title %}{% blocktrans %}Create extra mark{% endblocktrans %}{% endblock %} + + {% block content %} + + <form method="post"> + {% csrf_token %} + {% form form=form %}{% endform %} + {% include "core/partials/save_button.html" %} + </form> + + {% endblock %} diff --git a/aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html b/aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html new file mode 100644 index 0000000000000000000000000000000000000000..7adee30a1cfd30256d70b2a823827384de331e05 --- /dev/null +++ b/aleksis/apps/alsijil/templates/alsijil/extra_mark/edit.html @@ -0,0 +1,17 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} +{% load material_form i18n %} + +{% block browser_title %}{% blocktrans %}Edit extra mark{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Edit extra mark{% endblocktrans %}{% endblock %} + +{% block content %} + + <form method="post"> + {% csrf_token %} + {% form form=form %}{% endform %} + {% include "core/partials/save_button.html" %} + </form> + +{% endblock %} diff --git a/aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html b/aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html new file mode 100644 index 0000000000000000000000000000000000000000..a1a12b38096de60bae34d61425a1da9c688ab9d6 --- /dev/null +++ b/aleksis/apps/alsijil/templates/alsijil/extra_mark/list.html @@ -0,0 +1,18 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} + +{% load i18n %} +{% load render_table from django_tables2 %} + +{% block browser_title %}{% blocktrans %}Extra marks{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Extra marks{% endblocktrans %}{% endblock %} + +{% block content %} + <a class="btn green waves-effect waves-light" href="{% url 'create_extra_mark' %}"> + <i class="material-icons left">add</i> + {% trans "Create extra mark" %} + </a> + + {% render_table table %} +{% endblock %} diff --git a/aleksis/apps/alsijil/templates/alsijil/personal_note_filter/list.html b/aleksis/apps/alsijil/templates/alsijil/personal_note_filter/list.html deleted file mode 100644 index 3673e60abb2395a368d6721320dbf654d139d953..0000000000000000000000000000000000000000 --- a/aleksis/apps/alsijil/templates/alsijil/personal_note_filter/list.html +++ /dev/null @@ -1,16 +0,0 @@ -{# -*- engine:django -*- #} - -{% extends "core/base.html" %} -{% load i18n %} -{% load render_table from django_tables2 %} - -{% block browser_title %}{% blocktrans %}All personal note filters{% endblocktrans %}{% endblock %} -{% block page_title %}{% blocktrans %}Personal note filters{% endblocktrans %}{% endblock %} - -{% block content %} - <a href="{% url 'create_personal_note_filter' %}" class="waves-effect waves-light green btn"> - <i class="material-icons left">add</i>{% blocktrans %}Add filter{% endblocktrans %} - </a> - - {% render_table personal_note_filters_table %} -{% endblock %} diff --git a/aleksis/apps/alsijil/templates/alsijil/personal_note_filter/manage.html b/aleksis/apps/alsijil/templates/alsijil/personal_note_filter/manage.html deleted file mode 100644 index 427777439d73975a0cf3434bd507d27077cb9a2a..0000000000000000000000000000000000000000 --- a/aleksis/apps/alsijil/templates/alsijil/personal_note_filter/manage.html +++ /dev/null @@ -1,34 +0,0 @@ -{# -*- engine:django -*- #} -{% extends "core/base.html" %} -{% load material_form i18n static %} - -{% block browser_title %} - {% if personal_note_filter %} - {% trans "Update personal note filter" %} - {% else %} - {% trans "Create personal note filter" %} - {% endif %} -{% endblock %} -{% block page_title %} - {% if personal_note_filter %} - {% trans "Update personal note filter" %} - {% else %} - {% trans "Create personal note filter" %} - {% endif %} -{% endblock %} - - -{% block content %} - <form method="post"> - {% csrf_token %} - {% form form=personal_note_filter_form %}{% endform %} - {% include "core/partials/save_button.html" %} - {% if personal_note_filter %} - <a href="{% url 'delete_personal_note_filter' personal_note_filter.id %}" - class="waves-effect waves-light btn red"> - <i class="material-icons left">delete</i>{% blocktrans %}Delete filter{% endblocktrans %} - </a> - {% endif %} - </form> - -{% endblock %} diff --git a/aleksis/apps/alsijil/templates/alsijil/print/full_register.html b/aleksis/apps/alsijil/templates/alsijil/print/full_register.html index bb866c6d2beab7d8960678f42c324a4363692c60..2090a42682a5fb8a7144f12606363da0b62680a9 100644 --- a/aleksis/apps/alsijil/templates/alsijil/print/full_register.html +++ b/aleksis/apps/alsijil/templates/alsijil/print/full_register.html @@ -97,6 +97,18 @@ </ul> {% endif %} + {% if extra_marks %} + <h5>{% trans "Available extra marks" %}</h5> + + <ul class="collection"> + {% for extra_mark in extra_marks %} + <li class="collection-item"> + <strong>{{ extra_mark.short_name }}</strong> {{ extra_mark.name }} + </li> + {% endfor %} + </ul> + {% endif %} + <div class="page-break"> </div> @@ -117,6 +129,9 @@ {% endfor %} <th>{% trans '(u)' %}</th> <th>{% trans '(b)' %}</th> + {% for extra_mark in extra_marks %} + <th>{{ extra_mark.short_name }}</th> + {% endfor %} </tr> </thead> @@ -135,6 +150,9 @@ {% endfor %} <td>{{ person.unexcused }}</td> <td>{{ person.tardiness }}'</td> + {% for extra_mark in extra_marks %} + <td>{{ person|get_dict:extra_mark.count_label }}</td> + {% endfor %} </tr> {% endfor %} </tbody> @@ -245,29 +263,6 @@ </tr> </table> - {% if personal_note_filters %} - <h5>{% trans 'Statistics on remarks' %}</h5> - <table> - <thead> - <tr> - <th>{% trans 'Description' %}</th> - <th>{% trans 'Count' %}</th> - </tr> - </thead> - - <tbody> - {% for note_filter in personal_note_filters %} - <tr> - <td>{{ note_filter.description }}</td> - {% with "_personal_notes_with_"|add:note_filter.identifier as identifier %} - <td>{{ person|get_dict:identifier }}</td> - {% endwith %} - </tr> - {% endfor %} - </tbody> - </table> - {% endif %} - <h5>{% trans 'Absences and tardiness' %}</h5> <table> <tr> @@ -294,6 +289,18 @@ </tr> </table> + {% if extra_marks %} + <h5>{% trans 'Extra marks' %}</h5> + <table> + {% for extra_mark in extra_marks %} + <tr> + <th>{{ extra_mark.name }}</th> + <td>{{ person|get_dict:extra_mark.count_label }}</td> + </tr> + {% endfor %} + </table> + {% endif %} + <h5>{% trans 'Relevant personal notes' %}</h5> <table class="small-print"> <thead> @@ -304,14 +311,14 @@ <th>{% trans 'Te.' %}</th> <th>{% trans 'Absent' %}</th> <th>{% trans 'Tard.' %}</th> - <th>{% trans 'Remarks' %}</th> + <th colspan="2">{% trans 'Remarks' %}</th> </tr> </thead> <tbody> {% for note in person.personal_notes.all %} - {% if note.lesson_period in lesson_periods or note.lesson_period in lesson_periods %} - {% if note.absent or note.late or note.remarks %} + {% if note.lesson_period in lesson_periods %} + {% if note.absent or note.late or note.remarks or note.extra_marks.all %} {% period_to_date note.week note.lesson_period.period as note_date %} <tr> <td>{{ note_date }}</td> @@ -335,6 +342,11 @@ {{ note.late }}' {% endif %} </td> + <td> + {% for extra_mark in note.extra_marks.all %} + {{ extra_mark.short_name }}{% if not forloop.last %},{% endif %} + {% endfor %} + </td> <td>{{ note.remarks }}</td> </tr> {% endif %} @@ -429,6 +441,12 @@ {% endif %} </span> {% endif %} + {% for extra_mark in note.extra_marks.all %} + <span> + {{ note.person.last_name }}, {{ note.person.first_name|slice:"0:1" }}. + ({{ extra_mark.short_name }}) + </span> + {% endfor %} {% endfor %} </td> <td class="lesson-te"> diff --git a/aleksis/apps/alsijil/urls.py b/aleksis/apps/alsijil/urls.py index 89ab0ffe5f47c8cfebb015a6f76e597af4dd11dd..3f68362598ec4c8e624f0d5df42e3d028de7ceb9 100644 --- a/aleksis/apps/alsijil/urls.py +++ b/aleksis/apps/alsijil/urls.py @@ -21,25 +21,21 @@ urlpatterns = [ "print/group/<int:id_>", views.full_register_group, name="full_register_group" ), path("absence/new", views.register_absence, name="register_absence"), + path("extra_marks/", views.ExtraMarkListView.as_view(), name="extra_marks"), path( - "filters/list", - views.list_personal_note_filters, - name="list_personal_note_filters", + "extra_marks/create/", + views.ExtraMarkCreateView.as_view(), + name="create_extra_mark", ), path( - "filters/create", - views.edit_personal_note_filter, - name="create_personal_note_filter", + "extra_marks/<int:pk>/edit/", + views.ExtraMarkEditView.as_view(), + name="edit_extra_mark", ), path( - "filters/edit/<int:id_>", - views.edit_personal_note_filter, - name="edit_personal_note_filter", - ), - path( - "filters/delete/<int:id_>", - views.delete_personal_note_filter, - name="delete_personal_note_filter", + "extra_marks/<int:pk>/delete/", + views.ExtraMarkDeleteView.as_view(), + name="delete_extra_mark", ), path("excuse_types/", views.ExcuseTypeListView.as_view(), name="excuse_types"), path( diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py index 82973f4b220a30eac5eb197293fa214ff7457dc0..7a041822212b4fca3f8b6a03c7f92f859a19bb33 100644 --- a/aleksis/apps/alsijil/views.py +++ b/aleksis/apps/alsijil/views.py @@ -23,14 +23,14 @@ from aleksis.core.util import messages from .forms import ( ExcuseTypeForm, + ExtraMarkForm, LessonDocumentationForm, - PersonalNoteFilterForm, PersonalNoteFormSet, RegisterAbsenceForm, SelectForm, ) -from .models import ExcuseType, LessonDocumentation, PersonalNoteFilter -from .tables import ExcuseTypeTable, PersonalNoteFilterTable +from .models import ExcuseType, ExtraMark, LessonDocumentation +from .tables import ExcuseTypeTable, ExtraMarkTable def lesson( @@ -255,6 +255,21 @@ def week_view( ) ) + for extra_mark in ExtraMark.objects.all(): + persons_qs = persons_qs.annotate( + **{ + extra_mark.count_label: Count( + "personal_notes", + filter=Q( + personal_notes__lesson_period__in=lesson_periods_pk, + personal_notes__week=wanted_week.week, + personal_notes__extra_marks=extra_mark, + ), + distinct=True, + ) + } + ) + persons = [] for person in persons_qs: persons.append( @@ -273,6 +288,7 @@ def week_view( lesson_periods, key=lambda x: (x.period.weekday, x.period.period) ) + context["extra_marks"] = ExtraMark.objects.all() context["week"] = wanted_week context["lesson_periods"] = lesson_periods context["persons"] = persons @@ -372,30 +388,23 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse: tardiness=Sum("personal_notes__late"), ) - for excuse_type in ExcuseType.objects.all(): + for extra_mark in ExtraMark.objects.all(): persons = persons.annotate( **{ - excuse_type.count_label: Count( - "personal_notes__absent", - filter=Q( - personal_notes__absent=True, - personal_notes__excuse_type=excuse_type, - personal_notes__lesson_period__lesson__validity__school_term=current_school_term, - ), + extra_mark.count_label: Count( + "personal_notes", filter=Q(personal_notes__extra_marks=extra_mark,personal_notes__lesson_period__lesson__validity__school_term=current_school_term), ) } ) - # FIXME Move to manager - personal_note_filters = PersonalNoteFilter.objects.all() - for personal_note_filter in personal_note_filters: + for excuse_type in ExcuseType.objects.all(): persons = persons.annotate( **{ - "_personal_notes_with_%s" - % personal_note_filter.identifier: Count( - "personal_notes__remarks", + excuse_type.count_label: Count( + "personal_notes__absent", filter=Q( - personal_notes__remarks__iregex=personal_note_filter.regex, + personal_notes__absent=True, + personal_notes__excuse_type=excuse_type, personal_notes__lesson_period__lesson__validity__school_term=current_school_term, ), ) @@ -404,8 +413,8 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse: context["school_term"] = current_school_term context["persons"] = persons - context["personal_note_filters"] = personal_note_filters context["excuse_types"] = ExcuseType.objects.all() + context["extra_marks"] = ExtraMark.objects.all() context["group"] = group context["weeks"] = weeks context["periods_by_day"] = periods_by_day @@ -446,57 +455,45 @@ def register_absence(request: HttpRequest) -> HttpResponse: return render(request, "alsijil/absences/register.html", context) -def list_personal_note_filters(request: HttpRequest) -> HttpResponse: - context = {} - - personal_note_filters = PersonalNoteFilter.objects.all() - - # Prepare table - personal_note_filters_table = PersonalNoteFilterTable(personal_note_filters) - RequestConfig(request).configure(personal_note_filters_table) - - context["personal_note_filters_table"] = personal_note_filters_table - - return render(request, "alsijil/personal_note_filter/list.html", context) - - -def edit_personal_note_filter( - request: HttpRequest, id_: Optional["int"] = None -) -> HttpResponse: - context = {} - - if id_: - personal_note_filter = PersonalNoteFilter.objects.get(id=id_) - context["personal_note_filter"] = personal_note_filter - personal_note_filter_form = PersonalNoteFilterForm( - request.POST or None, instance=personal_note_filter - ) - else: - personal_note_filter_form = PersonalNoteFilterForm(request.POST or None) +class ExtraMarkListView(SingleTableView, PermissionRequiredMixin): + """Table of all extra marks.""" - if request.method == "POST": - if personal_note_filter_form.is_valid(): - personal_note_filter_form.save(commit=True) + model = ExtraMark + table_class = ExtraMarkTable + permission_required = "core.view_extramark" + template_name = "alsijil/extra_mark/list.html" - messages.success(request, _("The filter has been saved")) - return redirect("list_personal_note_filters") - context["personal_note_filter_form"] = personal_note_filter_form +class ExtraMarkCreateView(AdvancedCreateView, PermissionRequiredMixin): + """Create view for extra marks.""" - return render(request, "alsijil/personal_note_filter/manage.html", context) + model = ExtraMark + form_class = ExtraMarkForm + permission_required = "core.create_extramark" + template_name = "alsijil/extra_mark/create.html" + success_url = reverse_lazy("extra_marks") + success_message = _("The extra mark has been created.") -def delete_personal_note_filter(request: HttpRequest, id_: int) -> HttpResponse: - context = {} +class ExtraMarkEditView(AdvancedEditView, PermissionRequiredMixin): + """Edit view for extra marks.""" - personal_note_filter = get_object_or_404(PersonalNoteFilter, pk=id_) + model = ExtraMark + form_class = ExtraMarkForm + permission_required = "core.edit_extramark" + template_name = "alsijil/extra_mark/edit.html" + success_url = reverse_lazy("extra_marks") + success_message = _("The extra mark has been saved.") - PersonalNoteFilter.objects.filter(pk=id_).delete() - messages.success(request, _("The filter has been deleted.")) +class ExtraMarkDeleteView(AdvancedDeleteView, PermissionRequiredMixin, RevisionMixin): + """Delete view for extra marks""" - context["personal_note_filter"] = personal_note_filter - return redirect("list_personal_note_filters") + model = ExtraMark + permission_required = "core.delete_extramark" + template_name = "core/pages/delete.html" + success_url = reverse_lazy("extra_marks") + success_message = _("The extra mark has been deleted.") class ExcuseTypeListView(SingleTableView, PermissionRequiredMixin):