Newer
Older
from datetime import date, datetime
from typing import TYPE_CHECKING, Optional, Sequence, Union
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

Jonathan Weth
committed
from django.db.models.query_utils import Q
from django.utils.translation import gettext as _

Jonathan Weth
committed
from calendarweek import CalendarWeek
from aleksis.apps.chronos.managers import DateRangeQuerySetMixin
from aleksis.core.managers import AlekSISBaseManagerWithoutMigrations, RecurrencePolymorphicManager
if TYPE_CHECKING:
from aleksis.core.models import Group
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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"),
),
)
def annotate_subject(self) -> QuerySet:
"""Annotate lesson documentations with the subjects."""
return self.annotate(
subject=Case(
When(
lesson_period__isnull=False,
then="lesson_period__lesson__subject__name",
),
When(
extra_lesson__isnull=False,
then="extra_lesson__subject__name",
),
class PersonalNoteManager(AlekSISBaseManagerWithoutMigrations):
"""Manager adding specific methods to personal notes."""
def get_queryset(self):
"""Ensure all related lesson and person data are loaded as well."""
return (
super()
.get_queryset()
.select_related(
"person",
"excuse_type",
"lesson_period",
"lesson_period__lesson",
"lesson_period__lesson__subject",
"lesson_period__period",
"lesson_period__lesson__validity",
"lesson_period__lesson__validity__school_term",

Jonathan Weth
committed
"event",
"extra_lesson",
"extra_lesson__subject",
)
.prefetch_related("extra_marks")
)

Jonathan Weth
committed
class PersonalNoteQuerySet(RegisterObjectRelatedQuerySet, QuerySet):

Jonathan Weth
committed
def not_empty(self):
"""Get all not empty personal notes."""
return self.filter(
~Q(remarks="") | Q(absent=True) | ~Q(tardiness=0) | Q(extra_marks__isnull=False)

Jonathan Weth
committed
)
class LessonDocumentationManager(AlekSISBaseManagerWithoutMigrations):

Jonathan Weth
committed
pass
class LessonDocumentationQuerySet(RegisterObjectRelatedQuerySet, QuerySet):

Jonathan Weth
committed
def not_empty(self):
"""Get all not empty lesson documentations."""
return self.filter(~Q(topic="") | ~Q(group_note="") | ~Q(homework=""))
class GroupRoleManager(AlekSISBaseManagerWithoutMigrations):
pass
class GroupRoleQuerySet(QuerySet):
def with_assignments(
self, time_ref: Union[date, CalendarWeek], groups: Sequence["Group"]
) -> QuerySet:
from aleksis.apps.alsijil.models import GroupRoleAssignment
if isinstance(time_ref, CalendarWeek):
qs = GroupRoleAssignment.objects.in_week(time_ref)
else:
qs = GroupRoleAssignment.objects.on_day(time_ref)
qs = qs.for_groups(groups).distinct()
return self.prefetch_related(
Prefetch(
"assignments",
queryset=qs,
)
)
class GroupRoleAssignmentManager(AlekSISBaseManagerWithoutMigrations):
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
pass
class GroupRoleAssignmentQuerySet(DateRangeQuerySetMixin, QuerySet):
def within_dates(self, start: date, end: date):
"""Filter for all role assignments within a date range."""
return self.filter(
Q(date_start__lte=end) & (Q(date_end__gte=start) | Q(date_end__isnull=True))
)
def at_time(self, when: Optional[datetime] = None):
"""Filter for role assignments assigned at a certain point in time."""
now = when or datetime.now()
return self.on_day(now.date())
def for_groups(self, groups: Sequence["Group"]):
"""Filter all role assignments for a sequence of groups."""
qs = self
for group in groups:
qs = qs.for_group(group)
return qs
def for_group(self, group: "Group"):
"""Filter all role assignments for a group."""
return self.filter(Q(groups=group) | Q(groups__child_groups=group))
class DocumentationManager(RecurrencePolymorphicManager):
"""Manager adding specific methods to documentations."""
def get_queryset(self):
"""Ensure often used related data are loaded as well."""
return (
super()
.get_queryset()
.select_related(
"course",
"subject",
)
.prefetch_related("teachers")
)
class ParticipationStatusManager(RecurrencePolymorphicManager):
"""Manager adding specific methods to participation statuses."""