From 29e6a6ab463dfd09497151817b14bc02b06b516e Mon Sep 17 00:00:00 2001 From: Jonathan Weth <git@jonathanweth.de> Date: Thu, 18 Jun 2020 11:58:29 +0200 Subject: [PATCH] Fix lint issues and reformat all files --- aleksis/apps/alsijil/forms.py | 37 ++- .../apps/alsijil/migrations/0001_initial.py | 224 ++++++++++++++---- aleksis/apps/alsijil/model_extensions.py | 15 +- aleksis/apps/alsijil/models.py | 26 +- aleksis/apps/alsijil/urls.py | 26 +- aleksis/apps/alsijil/views.py | 75 ++++-- 6 files changed, 290 insertions(+), 113 deletions(-) diff --git a/aleksis/apps/alsijil/forms.py b/aleksis/apps/alsijil/forms.py index c09381a0d..92a48aa48 100644 --- a/aleksis/apps/alsijil/forms.py +++ b/aleksis/apps/alsijil/forms.py @@ -1,15 +1,14 @@ 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 diff --git a/aleksis/apps/alsijil/migrations/0001_initial.py b/aleksis/apps/alsijil/migrations/0001_initial.py index 3b396eaaa..ada9a2f3f 100644 --- a/aleksis/apps/alsijil/migrations/0001_initial.py +++ b/aleksis/apps/alsijil/migrations/0001_initial.py @@ -1,10 +1,11 @@ # 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()),], ), ] diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py index b503bbf8f..69dfc7c00 100644 --- a/aleksis/apps/alsijil/model_extensions.py +++ b/aleksis/apps/alsijil/model_extensions.py @@ -1,6 +1,6 @@ from datetime import date -from django.db.models import Exists, F, OuterRef +from django.db.models import Exists, OuterRef from calendarweek import CalendarWeek @@ -19,9 +19,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. @@ -31,7 +30,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 @@ -58,18 +56,17 @@ def mark_absent( @LessonPeriod.method def get_personal_notes(self, 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 = Person.objects.annotate( no_personal_notes=~Exists( diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py index 3867f6c5a..6dc1edee9 100644 --- a/aleksis/apps/alsijil/models.py +++ b/aleksis/apps/alsijil/models.py @@ -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") diff --git a/aleksis/apps/alsijil/urls.py b/aleksis/apps/alsijil/urls.py index fe40f18ee..4406a0685 100644 --- a/aleksis/apps/alsijil/urls.py +++ b/aleksis/apps/alsijil/urls.py @@ -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_>", diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py index 84c12a8e2..8f3218356 100644 --- a/aleksis/apps/alsijil/views.py +++ b/aleksis/apps/alsijil/views.py @@ -1,8 +1,6 @@ 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.http import Http404, HttpRequest, HttpResponse, HttpResponseNotFound @@ -13,7 +11,9 @@ from django.utils.translation import ugettext as _ from calendarweek import CalendarWeek from django_tables2 import RequestConfig +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 @@ -42,29 +42,38 @@ def lesson( wanted_week = CalendarWeek(year=year, week=week) else: # Determine current lesson by current date and time - lesson_period = LessonPeriod.objects.at_time().filter_teacher(request.user.person).first() + lesson_period = ( + LessonPeriod.objects.at_time().filter_teacher(request.user.person).first() + ) wanted_week = CalendarWeek() 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 @@ -76,7 +85,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 @@ -109,7 +120,11 @@ def lesson( 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 = {} @@ -161,8 +176,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 lesson_periods: # Aggregate all personal notes for this group and week @@ -203,7 +223,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 @@ -234,8 +256,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 @@ -244,8 +266,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 = {} @@ -255,10 +276,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) @@ -267,7 +294,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), @@ -283,7 +312,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 + ), ) } ) @@ -343,7 +374,9 @@ def list_personal_note_filters(request: HttpRequest) -> HttpResponse: return render(request, "alsijil/personal_note_filter/list.html", context) -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: -- GitLab