diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 86e88a880f855e8a9a0f7557085dcf42e20124a4..dcdec3c5956617ba23b75766c5d49aea6e9aed7a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,4 +10,4 @@ include: - project: "AlekSIS/official/AlekSIS" file: /ci/build/dist.yml - project: "AlekSIS/official/AlekSIS" - file: /ci/deploy/pypi.yml + file: /ci/publish/pypi.yml diff --git a/aleksis/apps/alsijil/data_checks.py b/aleksis/apps/alsijil/data_checks.py index be36446beaf9d1bfc2474df29242f47a23d9a1c8..864f019646411443605b0495075ba6acfb47219b 100644 --- a/aleksis/apps/alsijil/data_checks.py +++ b/aleksis/apps/alsijil/data_checks.py @@ -1,9 +1,6 @@ import logging from django.db.models import F -from django.db.models.expressions import ExpressionWrapper, Func, Value -from django.db.models.fields import DateField -from django.db.models.functions import Concat from django.db.models.query_utils import Q from django.utils.translation import gettext as _ @@ -92,15 +89,6 @@ class NoGroupsOfPersonsSetInPersonalNotesDataCheck(DataCheck): cls.register_result(note) -weekday_to_date = ExpressionWrapper( - Func( - Concat(F("year"), F("week")), Value("IYYYIW"), output_field=DateField(), function="TO_DATE" - ) - + F("lesson_period__period__weekday"), - output_field=DateField(), -) - - class LessonDocumentationOnHolidaysDataCheck(DataCheck): """Checks for lesson documentation objects on holidays. @@ -123,13 +111,11 @@ class LessonDocumentationOnHolidaysDataCheck(DataCheck): holidays = Holiday.objects.all() - documentations = LessonDocumentation.objects.not_empty().annotate( - actual_date=weekday_to_date - ) + documentations = LessonDocumentation.objects.not_empty().annotate_date_range() q = Q() for holiday in holidays: - q = q | Q(actual_date__gte=holiday.date_start, actual_date__lte=holiday.date_end) + q = q | Q(day_end__gte=holiday.date_start, day_start__lte=holiday.date_end) documentations = documentations.filter(q) for doc in documentations: @@ -159,11 +145,11 @@ class PersonalNoteOnHolidaysDataCheck(DataCheck): holidays = Holiday.objects.all() - personal_notes = PersonalNote.objects.not_empty().annotate(actual_date=weekday_to_date) + personal_notes = PersonalNote.objects.not_empty().annotate_date_range() q = Q() for holiday in holidays: - q = q | Q(actual_date__gte=holiday.date_start, actual_date__lte=holiday.date_end) + q = q | Q(day_end__gte=holiday.date_start, day_start__lte=holiday.date_end) personal_notes = personal_notes.filter(q) for note in personal_notes: diff --git a/aleksis/apps/alsijil/managers.py b/aleksis/apps/alsijil/managers.py index 862e33a6d6a1cf0c8893359a65cb9a8ea6440da5..4c7741d3b89620b1682532192acf7fb9ad1c0196 100644 --- a/aleksis/apps/alsijil/managers.py +++ b/aleksis/apps/alsijil/managers.py @@ -1,7 +1,9 @@ from datetime import date, datetime from typing import Optional, Sequence, Union -from django.db.models import QuerySet +from django.db.models import Case, ExpressionWrapper, F, Func, QuerySet, Value, When +from django.db.models.fields import DateField +from django.db.models.functions import Concat from django.db.models.query import Prefetch from django.db.models.query_utils import Q @@ -11,6 +13,65 @@ from aleksis.apps.chronos.managers import DateRangeQuerySetMixin from aleksis.core.managers import CurrentSiteManagerWithoutMigrations +class RegisterObjectRelatedQuerySet(QuerySet): + """Common queryset for personal notes and lesson documentations with shared API.""" + + def _get_weekday_to_date(self, weekday_name, year_name="year", week_name="week"): + """Get a ORM function which converts a weekday, a week and a year to a date.""" + return ExpressionWrapper( + Func( + Concat(F(year_name), F(week_name)), + Value("IYYYIW"), + output_field=DateField(), + function="TO_DATE", + ) + + F(weekday_name), + output_field=DateField(), + ) + + def annotate_day(self) -> QuerySet: + """Annotate every personal note/lesson documentation with the real date. + + Attribute name: ``day`` + + .. note:: + For events, this will annotate ``None``. + """ + return self.annotate( + day=Case( + When( + lesson_period__isnull=False, + then=self._get_weekday_to_date("lesson_period__period__weekday"), + ), + When( + extra_lesson__isnull=False, + then=self._get_weekday_to_date( + "extra_lesson__period__weekday", "extra_lesson__year", "extra_lesson__week" + ), + ), + ) + ) + + def annotate_date_range(self) -> QuerySet: + """Annotate every personal note/lesson documentation with the real date. + + Attribute names: ``day_start``, ``day_end`` + + .. note:: + For lesson periods and extra lessons, + this will annotate the same date for start and end day. + """ + return self.annotate_day().annotate( + day_start=Case( + When(day__isnull=False, then="day"), + When(day__isnull=True, then="event__date_start"), + ), + day_end=Case( + When(day__isnull=False, then="day"), When(day__isnull=True, then="event__date_end"), + ), + ) + + class PersonalNoteManager(CurrentSiteManagerWithoutMigrations): """Manager adding specific methods to personal notes.""" @@ -33,7 +94,7 @@ class PersonalNoteManager(CurrentSiteManagerWithoutMigrations): ) -class PersonalNoteQuerySet(QuerySet): +class PersonalNoteQuerySet(RegisterObjectRelatedQuerySet, QuerySet): def not_empty(self): """Get all not empty personal notes.""" return self.filter( @@ -45,7 +106,7 @@ class LessonDocumentationManager(CurrentSiteManagerWithoutMigrations): pass -class LessonDocumentationQuerySet(QuerySet): +class LessonDocumentationQuerySet(RegisterObjectRelatedQuerySet, QuerySet): def not_empty(self): """Get all not empty lesson documentations.""" return self.filter(~Q(topic="") | ~Q(group_note="") | ~Q(homework="")) diff --git a/aleksis/apps/alsijil/static/css/alsijil/full_register.css b/aleksis/apps/alsijil/static/css/alsijil/full_register.css index 9a3dc493aa468c989ed766817436d20b40cd0bff..0584c12ee4e266f43fe8d34c8003d37d4a09815c 100644 --- a/aleksis/apps/alsijil/static/css/alsijil/full_register.css +++ b/aleksis/apps/alsijil/static/css/alsijil/full_register.css @@ -25,7 +25,7 @@ tr.lessons-day-first { border-top: 3px solid rgba(0, 0, 0, 0.3); } -th.lessons-day-head, td.rotate, th.rotate { +td.rotate, th.rotate { text-align: center; transform: rotate(-90deg); } diff --git a/aleksis/apps/alsijil/templates/alsijil/print/full_register.html b/aleksis/apps/alsijil/templates/alsijil/print/full_register.html index ca2d801d60cf9657be5378c38abf5903304a2ed7..3434853ae9af44ca86c8972c4f24a7b388d27acc 100644 --- a/aleksis/apps/alsijil/templates/alsijil/print/full_register.html +++ b/aleksis/apps/alsijil/templates/alsijil/print/full_register.html @@ -370,7 +370,7 @@ {% endfor %} {% for week in weeks %} - <h4>{% trans 'Lesson documentation for week' %} {{ week.week }}</h4> + <h4>{% trans 'Week' %} {{ week.week }}: {{ week.0 }}–{{ week.6 }}</h4> <table class="small-print"> <thead> @@ -401,7 +401,7 @@ {% endif %} "> {% if forloop.first %} - <th rowspan="{{ register_objects|length }}" class="lessons-day-head">{{ day }}</th> + <th rowspan="{{ register_objects|length }}" class="lessons-day-head">{{ day|date:"D" }}</th> {% endif %} <td class="lesson-pe"> {% if register_object.label_ != "event" %} diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py index 99910ee7897b73d2f5bb1970e1fcbbeb937cbf2c..1f400054e242fb21197bd454f01d71bc358fbfb1 100644 --- a/aleksis/apps/alsijil/views.py +++ b/aleksis/apps/alsijil/views.py @@ -247,8 +247,8 @@ def register_object( reversion.set_user(request.user) for instance in instances: instance.person.mark_absent( - wanted_week[lesson_period.period.weekday], - lesson_period.period.period + 1, + wanted_week[register_object.period.weekday], + register_object.period.period + 1, instance.absent, instance.excused, instance.excuse_type,