Newer
Older
from django.urls import reverse
from django.utils.formats import date_format
from django.utils.translation import gettext_lazy as _

Jonathan Weth
committed
from calendarweek import CalendarWeek
from aleksis.apps.alsijil.data_checks import (
ExcusesWithoutAbsences,
LessonDocumentationOnHolidaysDataCheck,
NoGroupsOfPersonsSetInPersonalNotesDataCheck,
NoPersonalNotesInCancelledLessonsDataCheck,
PersonalNoteOnHolidaysDataCheck,
)

Jonathan Weth
committed
from aleksis.apps.alsijil.managers import (
GroupRoleAssignmentManager,
GroupRoleAssignmentQuerySet,
GroupRoleManager,
GroupRoleQuerySet,

Jonathan Weth
committed
LessonDocumentationManager,
LessonDocumentationQuerySet,
PersonalNoteManager,
PersonalNoteQuerySet,
)
from aleksis.apps.chronos.managers import GroupPropertiesMixin
from aleksis.apps.chronos.mixins import WeekRelatedMixin

Jonathan Weth
committed
from aleksis.apps.chronos.models import LessonPeriod
from aleksis.apps.chronos.util.date import get_current_year

Jonathan Weth
committed
from aleksis.core.util.core_helpers import get_site_preferences
from aleksis.core.util.model_helpers import COLOURS, ICONS
def isidentifier(value: str) -> bool:
return value.isidentifier()
class ExcuseType(ExtensibleModel):
"""An type of excuse.
Can be used to count different types of absences separately.
"""
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} ({self.short_name})"
@property
def count_label(self):
return f"{self.short_name}_count"
class Meta:
ordering = ["name"]
verbose_name = _("Excuse type")
verbose_name_plural = _("Excuse types")
class PersonalNote(ExtensibleModel, WeekRelatedMixin):
"""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.
data_checks = [
NoPersonalNotesInCancelledLessonsDataCheck,
NoGroupsOfPersonsSetInPersonalNotesDataCheck,
PersonalNoteOnHolidaysDataCheck,
ExcusesWithoutAbsences,
]

Jonathan Weth
committed
objects = PersonalNoteManager.from_queryset(PersonalNoteQuerySet)()
person = models.ForeignKey("core.Person", models.CASCADE, related_name="personal_notes")
groups_of_person = models.ManyToManyField("core.Group", related_name="+")
week = models.IntegerField()
year = models.IntegerField(verbose_name=_("Year"), default=get_current_year)
lesson_period = models.ForeignKey(
"chronos.LessonPeriod", models.CASCADE, related_name="personal_notes"
)
absent = models.BooleanField(default=False)
late = models.IntegerField(default=0)
excused = models.BooleanField(default=False)
ExcuseType, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("Excuse type"),
remarks = models.CharField(max_length=200, blank=True)
extra_marks = models.ManyToManyField("ExtraMark", blank=True, verbose_name=_("Extra marks"))
def save(self, *args, **kwargs):
if self.excuse_type:
self.excused = True
if not self.absent:
self.excused = False
self.excuse_type = None
def reset_values(self):
"""Reset all saved data to default values.
.. warning ::
This won't save the data, please execute ``save`` extra.
defaults = PersonalNote()
self.absent = defaults.absent
self.late = defaults.late
self.excused = defaults.excused
self.excuse_type = defaults.excuse_type
self.remarks = defaults.remarks
self.extra_marks.clear()
def __str__(self):
return f"{date_format(self.date)}, {self.lesson_period}, {self.person}"
def get_absolute_url(self):
return (
reverse(
"lesson_by_week_and_period", args=[self.year, self.week, self.lesson_period.pk],
)
+ "#personal-notes"
)
verbose_name = _("Personal note")
verbose_name_plural = _("Personal notes")
unique_together = [["lesson_period", "week", "person"]]
ordering = [
"week",
"lesson_period__period__weekday",
"lesson_period__period__period",
"person__last_name",
"person__first_name",
]
class LessonDocumentation(ExtensibleModel, WeekRelatedMixin):
"""A documentation on a single lesson period.
Non-personal, includes the topic and homework of the lesson.

Jonathan Weth
committed
objects = LessonDocumentationManager.from_queryset(LessonDocumentationQuerySet)()
data_checks = [LessonDocumentationOnHolidaysDataCheck]
week = models.IntegerField()
year = models.IntegerField(verbose_name=_("Year"), default=get_current_year)
"chronos.LessonPeriod", models.CASCADE, related_name="documentations"
)
topic = models.CharField(verbose_name=_("Lesson topic"), max_length=200, blank=True)
homework = models.CharField(verbose_name=_("Homework"), max_length=200, blank=True)
group_note = models.CharField(verbose_name=_("Group note"), max_length=200, blank=True)
"""Carry over data to directly adjacent periods in this lesson if data is not already set.

Jonathan Weth
committed
Can be deactivated using site preference ``alsijil__carry_over``.

Jonathan Weth
committed
"""
following_periods = LessonPeriod.objects.filter(
lesson=self.lesson_period.lesson,
period__weekday=self.lesson_period.period.weekday,
period__period__gt=self.lesson_period.period.period,
)
for period in following_periods:
lesson_documentation = period.get_or_create_lesson_documentation(
CalendarWeek(week=self.week, year=self.year)

Jonathan Weth
committed

Jonathan Weth
committed
if not lesson_documentation.topic:
lesson_documentation.topic = self.topic
changed = True

Jonathan Weth
committed
if not lesson_documentation.homework:
lesson_documentation.homework = self.homework
changed = True

Jonathan Weth
committed
if not lesson_documentation.group_note:
lesson_documentation.group_note = self.group_note
changed = True

Jonathan Weth
committed
if changed:
lesson_documentation.save()

Jonathan Weth
committed
def __str__(self):
return f"{self.lesson_period}, {date_format(self.date)}"
def get_absolute_url(self):
return reverse(
"lesson_by_week_and_period", args=[self.year, self.week, self.lesson_period.pk],
)

Jonathan Weth
committed
def save(self, *args, **kwargs):
if get_site_preferences()["alsijil__carry_over"] and (
self.topic or self.homework or self.group_note
):
self._carry_over_data()

Jonathan Weth
committed
super().save(*args, **kwargs)
verbose_name = _("Lesson documentation")
verbose_name_plural = _("Lesson documentations")
unique_together = [["lesson_period", "week"]]
ordering = [
"week",
"lesson_period__period__weekday",
"lesson_period__period__period",
]
class ExtraMark(ExtensibleModel):
"""A model for extra marks.
Can be used for lesson-based counting of things (like forgotten homework).
"""
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:
ordering = ["short_name"]
verbose_name = _("Extra mark")
verbose_name_plural = _("Extra marks")
objects = GroupRoleManager.from_queryset(GroupRoleQuerySet)()
name = models.CharField(max_length=255, verbose_name=_("Name"))
icon = models.CharField(max_length=50, blank=True, choices=ICONS, verbose_name=_("Icon"))
colour = models.CharField(max_length=50, blank=True, choices=COLOURS, verbose_name=_("Colour"))
def __str__(self):
return self.name
class Meta:
verbose_name = _("Group role")
verbose_name_plural = _("Group roles")
class GroupRoleAssignment(GroupPropertiesMixin, ExtensibleModel):
objects = GroupRoleAssignmentManager.from_queryset(GroupRoleAssignmentQuerySet)()
on_delete=models.CASCADE,
related_name="assignments",
)
person = models.ForeignKey(
"core.Person",
on_delete=models.CASCADE,
"core.Group", related_name="group_roles", verbose_name=_("Groups"),
)
date_start = models.DateField(verbose_name=_("Start date"))
date_end = models.DateField(
blank=True,
null=True,
verbose_name=_("End date"),
help_text=_("Can be left empty if end date is not clear yet"),
)
def __str__(self):
date_end = date_format(self.date_end) if self.date_end else "?"
return f"{self.role}: {self.person}, {date_format(self.date_start)}–{date_end}"
@property
def date_range(self) -> str:
if not self.date_end:
return f"{date_format(self.date_start)}–?"
else:
return f"{date_format(self.date_start)}–{date_format(self.date_end)}"
verbose_name = _("Group role assignment")
verbose_name_plural = _("Group role assignments")
class AlsijilGlobalPermissions(ExtensibleModel):
class Meta:
managed = False
permissions = (
("view_week", _("Can view week overview")),
("register_absence", _("Can register absence")),
("list_personal_note_filters", _("Can list all personal note filters")),
)