Skip to content
Snippets Groups Projects
Verified Commit 8c7320c1 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Support events and extra lessons in "lesson view"

Needed some extra/rewritten API
parent 9a6a316b
No related branches found
No related tags found
1 merge request!120Resolve "Support events and extra lessons in class register"
Pipeline #5779 passed
......@@ -3,6 +3,7 @@ from typing import Dict, Iterable, Iterator, Optional, Union
from django.db.models import Exists, OuterRef, Q, QuerySet
from django.db.models.aggregates import Count, Sum
from django.urls import reverse
from django.utils.translation import gettext as _
import reversion
......@@ -16,6 +17,18 @@ from aleksis.core.models import Group, Person
from .models import ExcuseType, ExtraMark, LessonDocumentation, PersonalNote
def alsijil_url(self):
if isinstance(self, LessonPeriod):
return reverse("lesson_period", args=[self.week.year, self.week.week, self.pk])
else:
return reverse(self.label_, args=[self.pk])
LessonPeriod.property_(alsijil_url)
Event.property_(alsijil_url)
ExtraLesson.property_(alsijil_url)
@Person.method
def mark_absent(
self,
......@@ -52,17 +65,33 @@ def mark_absent(
.filter(period__period__gte=from_period)
.annotate_week(wanted_week)
)
extra_lessons = (
ExtraLesson.objects.filter(groups__members=self)
.on_day(day)
.filter(period__period__gte=from_period)
)
if to_period:
lesson_periods = lesson_periods.filter(period__period__lte=to_period)
extra_lessons = extra_lessons.filter(period__period__lte=to_period)
# Create and update all personal notes for the discovered lesson periods
if not dry_run:
for lesson_period in lesson_periods:
sub = lesson_period.get_substitution()
for register_object in list(lesson_periods) + list(extra_lessons):
sub = (
register_object.get_substitution()
if isinstance(register_object, LessonPeriod)
else None
)
if sub and sub.cancelled:
continue
q_attrs = (
dict(week=wanted_week.week, year=wanted_week.year, lesson_period=register_object)
if isinstance(register_object, LessonPeriod)
else dict(extra_lesson=register_object)
)
with reversion.create_revision():
set_user(get_request().user)
personal_note, created = (
......@@ -70,14 +99,12 @@ def mark_absent(
.prefetch_related(None)
.update_or_create(
person=self,
lesson_period=lesson_period,
week=wanted_week.week,
year=wanted_week.year,
defaults={
"absent": absent,
"excused": excused,
"excuse_type": excuse_type,
},
**q_attrs,
)
)
personal_note.groups_of_person.set(self.member_of.all())
......@@ -89,11 +116,10 @@ def mark_absent(
personal_note.remarks = remarks
personal_note.save()
return lesson_periods.count()
return lesson_periods.count() + extra_lessons.count()
@LessonPeriod.method
def get_personal_notes(self, persons: QuerySet, wanted_week: CalendarWeek):
def get_personal_notes(self, persons: QuerySet, wanted_week: Optional[CalendarWeek] = None):
"""Get all personal notes for that lesson in a specified week.
Returns all linked `PersonalNote` objects, filtered by the given weeek,
......@@ -106,37 +132,30 @@ def get_personal_notes(self, persons: QuerySet, wanted_week: CalendarWeek):
- Dominik George <dominik.george@teckids.org>
"""
# Find all persons in the associated groups that do not yet have a personal note for this lesson
if isinstance(self, LessonPeriod):
q_attrs = dict(week=wanted_week.week, year=wanted_week.year, lesson_period=self)
elif isinstance(self, Event):
q_attrs = dict(event=self)
else:
q_attrs = dict(extra_lesson=self)
missing_persons = persons.annotate(
no_personal_notes=~Exists(
PersonalNote.objects.filter(
week=wanted_week.week,
year=wanted_week.year,
lesson_period=self,
person__pk=OuterRef("pk"),
)
)
no_personal_notes=~Exists(PersonalNote.objects.filter(person__pk=OuterRef("pk"), **q_attrs))
).filter(
member_of__in=Group.objects.filter(pk__in=self.lesson.groups.all()),
member_of__in=Group.objects.filter(pk__in=self.get_groups().all()),
is_active=True,
no_personal_notes=True,
)
# Create all missing personal notes
new_personal_notes = [
PersonalNote(
person=person, lesson_period=self, week=wanted_week.week, year=wanted_week.year,
)
for person in missing_persons
]
new_personal_notes = [PersonalNote(person=person, **q_attrs,) for person in missing_persons]
PersonalNote.objects.bulk_create(new_personal_notes)
for personal_note in new_personal_notes:
personal_note.groups_of_person.set(personal_note.person.member_of.all())
return (
PersonalNote.objects.filter(
lesson_period=self, week=wanted_week.week, year=wanted_week.year, person__in=persons,
)
PersonalNote.objects.filter(**q_attrs, person__in=persons)
.select_related(None)
.prefetch_related(None)
.select_related("person", "excuse_type")
......@@ -144,6 +163,10 @@ def get_personal_notes(self, persons: QuerySet, wanted_week: CalendarWeek):
)
LessonPeriod.method(get_personal_notes)
Event.method(get_personal_notes)
ExtraLesson.method(get_personal_notes)
# Dynamically add extra permissions to Group and Person models in core
# Note: requires migrate afterwards
Group.add_permission(
......@@ -214,9 +237,7 @@ def get_or_create_lesson_documentation_simple(
self, week: Optional[CalendarWeek] = None
) -> LessonDocumentation:
"""Get or create lesson documentation object for this event/extra lesson."""
lesson_documentation, created = LessonDocumentation.objects.get_or_create(
**{"event" if isinstance(self, Event) else "extra_lesson": self}
)
lesson_documentation, created = LessonDocumentation.objects.get_or_create({self.label_: self})
return lesson_documentation
......
from datetime import date
from typing import Optional, Union
from django.db import models
from django.db.models.constraints import CheckConstraint
from django.db.models.query_utils import Q
from django.urls import reverse
from django.utils.formats import date_format
from django.utils.translation import gettext_lazy as _
......@@ -16,8 +18,7 @@ from aleksis.apps.alsijil.data_checks import (
)
from aleksis.apps.alsijil.managers import PersonalNoteManager
from aleksis.apps.chronos.mixins import WeekRelatedMixin
from aleksis.apps.chronos.models import LessonPeriod
from aleksis.apps.chronos.util.date import get_current_year
from aleksis.apps.chronos.models import Event, ExtraLesson, LessonPeriod
from aleksis.core.mixins import ExtensibleModel
from aleksis.core.util.core_helpers import get_site_preferences
......@@ -73,7 +74,46 @@ lesson_related_constraint_q = (
)
class PersonalNote(ExtensibleModel, WeekRelatedMixin):
class RegisterObjectRelatedMixin(WeekRelatedMixin):
@property
def register_object(self) -> Union[LessonPeriod, Event, ExtraLesson]:
if self.lesson_period:
return self.lesson_period
elif self.event:
return self.event
else:
return self.extra_lesson
@property
def calendar_week(self) -> CalendarWeek:
if self.lesson_period:
return CalendarWeek(week=self.week, year=self.year,)
elif self.extra_lesson:
return self.extra_lesson.calendar_week
else:
return CalendarWeek.from_date(self.register_object.date_start)
@property
def date(self) -> Optional[date]:
if self.lesson_period:
return super().date
elif self.extra_lesson:
return self.extra_lesson.date
return None
@property
def date_formatted(self) -> str:
return (
date_format(self.date)
if self.date
else f"{self.event.date_start}{self.event.date_end}"
)
def get_absolute_url(self) -> str:
return self.register_object.alsijil_url
class PersonalNote(RegisterObjectRelatedMixin, ExtensibleModel):
"""A personal note about a single person.
Used in the class register to note absences, excuses
......@@ -93,9 +133,7 @@ class PersonalNote(ExtensibleModel, WeekRelatedMixin):
groups_of_person = models.ManyToManyField("core.Group", related_name="+")
week = models.IntegerField(blank=True, null=True)
year = models.IntegerField(
verbose_name=_("Year"), default=get_current_year, blank=True, null=True
)
year = models.IntegerField(verbose_name=_("Year"), blank=True, null=True)
lesson_period = models.ForeignKey(
"chronos.LessonPeriod", models.CASCADE, related_name="personal_notes", blank=True, null=True
......@@ -144,16 +182,11 @@ class PersonalNote(ExtensibleModel, WeekRelatedMixin):
self.remarks = defaults.remarks
self.extra_marks.clear()
def __str__(self):
return f"{date_format(self.date)}, {self.lesson_period}, {self.person}"
def __str__(self) -> str:
return f"{self.date_formatted}, {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"
)
def get_absolute_url(self) -> str:
return super().get_absolute_url() + "#personal-notes"
class Meta:
verbose_name = _("Personal note")
......@@ -173,7 +206,7 @@ class PersonalNote(ExtensibleModel, WeekRelatedMixin):
]
class LessonDocumentation(ExtensibleModel, WeekRelatedMixin):
class LessonDocumentation(RegisterObjectRelatedMixin, ExtensibleModel):
"""A documentation on a single lesson period.
Non-personal, includes the topic and homework of the lesson.
......@@ -182,9 +215,7 @@ class LessonDocumentation(ExtensibleModel, WeekRelatedMixin):
data_checks = [LessonDocumentationOnHolidaysDataCheck]
week = models.IntegerField(blank=True, null=True)
year = models.IntegerField(
verbose_name=_("Year"), default=get_current_year, blank=True, null=True
)
year = models.IntegerField(verbose_name=_("Year"), blank=True, null=True)
lesson_period = models.ForeignKey(
"chronos.LessonPeriod", models.CASCADE, related_name="documentations", blank=True, null=True
......@@ -233,16 +264,13 @@ class LessonDocumentation(ExtensibleModel, WeekRelatedMixin):
lesson_documentation.save()
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],
)
return f"{self.lesson_period}, {self.date_formatted}"
def save(self, *args, **kwargs):
if get_site_preferences()["alsijil__carry_over"] and (
self.topic or self.homework or self.group_note
if (
get_site_preferences()["alsijil__carry_over"]
and (self.topic or self.homework or self.group_note)
and self.lesson_period
):
self._carry_over_data()
super().save(*args, **kwargs)
......
......@@ -27,7 +27,7 @@ from .util.predicates import (
)
# View lesson
view_lesson_predicate = has_person & (
view_register_object_predicate = has_person & (
is_none # View is opened as "Current lesson"
| is_lesson_teacher
| is_lesson_participant
......@@ -35,19 +35,19 @@ view_lesson_predicate = has_person & (
| has_global_perm("alsijil.view_lesson")
| has_lesson_group_object_perm("core.view_week_class_register_group")
)
add_perm("alsijil.view_lesson", view_lesson_predicate)
add_perm("alsijil.view_register_object", view_register_object_predicate)
# View lesson in menu
add_perm("alsijil.view_lesson_menu", has_person)
# View lesson personal notes
view_lesson_personal_notes_predicate = view_lesson_predicate & (
view_lesson_personal_notes_predicate = view_register_object_predicate & (
~is_lesson_participant
| is_lesson_teacher
| has_global_perm("alsijil.view_personalnote")
| has_lesson_group_object_perm("core.view_personalnote_group")
)
add_perm("alsijil.view_lesson_personalnote", view_lesson_personal_notes_predicate)
add_perm("alsijil.view_register_object_personalnote", view_lesson_personal_notes_predicate)
# Edit personal note
edit_lesson_personal_note_predicate = view_lesson_personal_notes_predicate & (
......@@ -55,7 +55,7 @@ edit_lesson_personal_note_predicate = view_lesson_personal_notes_predicate & (
| has_global_perm("alsijil.change_personalnote")
| has_lesson_group_object_perm("core.edit_personalnote_group")
)
add_perm("alsijil.edit_lesson_personalnote", edit_lesson_personal_note_predicate)
add_perm("alsijil.edit_register_object_personalnote", edit_lesson_personal_note_predicate)
# View personal note
view_personal_note_predicate = has_person & (
......@@ -76,11 +76,11 @@ edit_personal_note_predicate = view_personal_note_predicate & (
add_perm("alsijil.edit_personalnote", edit_personal_note_predicate)
# View lesson documentation
view_lesson_documentation_predicate = view_lesson_predicate
view_lesson_documentation_predicate = view_register_object_predicate
add_perm("alsijil.view_lessondocumentation", view_lesson_documentation_predicate)
# Edit lesson documentation
edit_lesson_documentation_predicate = view_lesson_predicate & (
edit_lesson_documentation_predicate = view_register_object_predicate & (
is_lesson_teacher
| has_global_perm("alsijil.change_lessondocumentation")
| has_lesson_group_object_perm("core.edit_lessondocumentation_group")
......
......@@ -10,21 +10,21 @@
{% endblock %}
{% block content %}
{% if next_lesson_person or prev_lesson_person %}
{% if next_lesson_person or prev_lesson_person or lesson_documentation %}
<div class="row no-margin">
<div class="col s12 no-padding">
{# Back to week view #}
{% with lesson_period.get_lesson_documentation as lesson_doc %}
<a href="{% url "week_view_by_week" lesson_doc.year lesson_doc.week "group" lesson_period.lesson.groups.all.0.pk %}"
{% if lesson_documentation %}
<a href="{% url "week_view_by_week" lesson_documentation.calendar_week.year lesson_documentation.calendar_week.week "group" register_object.get_groups.all.0.pk %}"
class="btn primary-color waves-light waves-effect alsijil-top-button">
<i class="material-icons left">chevron_left</i> {% trans "Back to week view" %}
</a>
{% endwith %}
{% endif %}
{# Next lesson #}
{% if prev_lesson_person %}
<a class="btn primary waves-effect waves-light alsijil-top-button"
href="{% url "lesson_by_week_and_period" prev_lesson_person.week.year prev_lesson_person.week.week prev_lesson_person.id %}">
href="{% url "lesson_period" prev_lesson_person.week.year prev_lesson_person.week.week prev_lesson_person.id %}">
<i class="material-icons left">arrow_back</i>
{% trans "My previous lesson" %}
</a>
......@@ -33,7 +33,7 @@
{# Previous lesson #}
{% if next_lesson_person %}
<a class="btn primary right waves-effect waves-light alsijil-top-button"
href="{% url "lesson_by_week_and_period" next_lesson_person.week.year next_lesson_person.week.week next_lesson_person.id %}">
href="{% url "lesson_period" next_lesson_person.week.year next_lesson_person.week.week next_lesson_person.id %}">
<i class="material-icons right">arrow_forward</i>
{% trans "My next lesson" %}
</a>
......@@ -43,51 +43,60 @@
{% endif %}
<h4>
{{ day }}, {% blocktrans with period=lesson_period.period.period %}{{ period }}. period{% endblocktrans %} –
{% if register_object.label_ == "event" %}
{{ register_object.date_start }} {{ register_object.period_from.period }}.–{{ register_object.date_end }}
{{ register_object.period_to.period }}.,
{% else %}
{{ day }}, {% blocktrans with period=register_object.period.period %}{{ period }}. period{% endblocktrans %} –
{% endif %}
{% for group in lesson_period.get_groups.all %}
<span>{{ group.name }}</span>,
{% endfor %}
{{ register_object.group_names }},
{{ lesson_period.get_subject.name }},
{% if register_object.label_ == "event" %}
{% trans "Event" %} ({{ register_object.title }})
{% else %}
{{ register_object.get_subject.name }}
{% endif %},
{% for teacher in lesson_period.get_teachers.all %}
{{ teacher.short_name }}
{% endfor %}
{{ register_object.teacher_short_names }}
<span class="right">
{% include "alsijil/partials/lesson_status_icon.html" with period=lesson_period css_class="medium" %}
</span>
{% include "alsijil/partials/lesson_status_icon.html" with register_object=register_object css_class="medium" %}
</span>
</h4>
<br/>
{% has_perm "alsijil.view_lessondocumentation" user lesson_period as can_view_lesson_documentation %}
{% has_perm "alsijil.edit_lessondocumentation" user lesson_period as can_edit_lesson_documentation %}
{% has_perm "alsijil.edit_lesson_personalnote" user lesson_period as can_edit_lesson_personalnote %}
{% has_perm "alsijil.view_lessondocumentation" user register_object as can_view_lesson_documentation %}
{% has_perm "alsijil.edit_lessondocumentation" user register_object as can_edit_lesson_documentation %}
{% has_perm "alsijil.edit_register_object_personalnote" user register_object as can_edit_register_object_personalnote %}
<form method="post" class="row">
<p>
{% if not blocked_because_holidays %}
{% if can_edit_lesson_documentation or can_edit_lesson_personalnote %}
{% if can_edit_lesson_documentation or can_edit_register_object_personalnote %}
{% include "core/partials/save_button.html" %}
{% endif %}
{% endif %}
<a class="btn waves-effect waves-light primary"
href="{% url "lesson_by_week_and_period" prev_lesson.week.year prev_lesson.week.week prev_lesson.id %}">
<i class="material-icons left">arrow_back</i>
{% blocktrans with subject=lesson_period.get_subject.name %}
Previous {{ subject }} lesson
{% endblocktrans %}
</a>
<a class="btn right waves-effect waves-light primary"
href="{% url "lesson_by_week_and_period" next_lesson.week.year next_lesson.week.week next_lesson.id %}">
<i class="material-icons right">arrow_forward</i>
{% blocktrans with subject=lesson_period.get_subject.name %}
Next {{ subject }} lesson
{% endblocktrans %}
</a>
{% if prev_lesson %}
<a class="btn waves-effect waves-light primary"
href="{% url "lesson_period" prev_lesson.week.year prev_lesson.week.week prev_lesson.id %}">
<i class="material-icons left">arrow_back</i>
{% blocktrans with subject=register_object.get_subject.name %}
Previous {{ subject }} lesson
{% endblocktrans %}
</a>
{% endif %}
{% if next_lesson %}
<a class="btn right waves-effect waves-light primary"
href="{% url "lesson_period" next_lesson.week.year next_lesson.week.week next_lesson.id %}">
<i class="material-icons right">arrow_forward</i>
{% blocktrans with subject=register_object.get_subject.name %}
Next {{ subject }} lesson
{% endblocktrans %}
</a>
{% endif %}
</p>
{% csrf_token %}
......@@ -100,13 +109,13 @@
<li class="tab">
<a href="#lesson-documentation">{% trans "Lesson documentation" %}</a>
</li>
{% if not lesson_period.get_substitution.cancelled or not request.site.preferences.alsijil__block_personal_notes_for_cancelled %}
{% if register_object.label_ != "lesson_period" or not register_object.get_substitution.cancelled or not request.site.preferences.alsijil__block_personal_notes_for_cancelled %}
<li class="tab">
<a href="#personal-notes">{% trans "Personal notes" %}</a>
</li>
{% endif %}
{% has_perm "alsijil.view_lessondocumentation" user lesson_period.prev as can_view_prev_lesson_documentation %}
{% if lesson_period.prev.get_lesson_documentation and can_view_prev_lesson_documentation %}
{% has_perm "alsijil.view_lessondocumentation" user register_object.prev as can_view_prev_lesson_documentation %}
{% if register_object.prev.get_lesson_documentation and can_view_prev_lesson_documentation %}
<li class="tab">
<a href="#previous-lesson">{% trans "Previous lesson" %}</a>
</li>
......@@ -158,7 +167,7 @@
</div>
</div>
{% with prev_lesson=lesson_period.prev prev_doc=prev_lesson.get_lesson_documentation %}
{% with prev_lesson=register_object.prev prev_doc=prev_lesson.get_lesson_documentation %}
{% with absences=prev_lesson.get_absences tardinesses=prev_lesson.get_tardinesses extra_marks=prev_lesson.get_extra_marks %}
{% has_perm "alsijil.view_lessondocumentation" user prev_lesson as can_view_prev_lesson_documentation %}
{% if prev_doc and can_view_prev_lesson_documentation %}
......@@ -229,14 +238,14 @@
{% endwith %}
{% endwith %}
{% if not lesson_period.get_substitution.cancelled or not request.site.preferences.alsijil__block_personal_notes_for_cancelled %}
{% if register_object.label_ != "lesson_period" or not register_object.get_substitution.cancelled or not request.site.preferences.alsijil__block_personal_notes_for_cancelled %}
<div class="col s12" id="personal-notes">
<div class="card">
<div class="card-content">
<span class="card-title">
{% blocktrans %}Personal notes{% endblocktrans %}
</span>
{% if can_edit_lesson_personalnote %}
{% if can_edit_register_object_personalnote %}
{% form form=personal_note_formset.management_form %}{% endform %}
{% endif %}
......@@ -254,7 +263,7 @@
</thead>
<tbody>
{% for form in personal_note_formset %}
{% if can_edit_lesson_personalnote %}
{% if can_edit_register_object_personalnote %}
<tr>
{{ form.id }}
<td>{{ form.person_name }}{{ form.person_name.value }}</td>
......@@ -354,25 +363,29 @@
<p>
{% if can_edit_lesson_documentation or can_edit_lesson_personalnote %}
{% if can_edit_lesson_documentation or can_edit_register_object_personalnote %}
{% include "core/partials/save_button.html" %}
{% endif %}
<a class="btn primary waves-effect waves-light"
href="{% url "lesson_by_week_and_period" prev_lesson.week.year prev_lesson.week.week prev_lesson.id %}">
<i class="material-icons left">arrow_back</i>
{% blocktrans with subject=lesson_period.get_subject.name %}
Previous {{ subject }} lesson
{% endblocktrans %}
</a>
{% if prev_lesson %}
<a class="btn primary waves-effect waves-light"
href="{% url "lesson_period" prev_lesson.week.year prev_lesson.week.week prev_lesson.id %}">
<i class="material-icons left">arrow_back</i>
{% blocktrans with subject=register_object.get_subject.name %}
Previous {{ subject }} lesson
{% endblocktrans %}
</a>
{% endif %}
<a class="btn primary right waves-effect waves-light"
href="{% url "lesson_by_week_and_period" next_lesson.week.year next_lesson.week.week next_lesson.id %}">
<i class="material-icons right">arrow_forward</i>
{% blocktrans with subject=lesson_period.get_subject.name %}
Next {{ subject }} lesson
{% endblocktrans %}
</a>
{% if next_lesson %}
<a class="btn primary right waves-effect waves-light"
href="{% url "lesson_period" next_lesson.week.year next_lesson.week.week next_lesson.id %}">
<i class="material-icons right">arrow_forward</i>
{% blocktrans with subject=register_object.get_subject.name %}
Next {{ subject }} lesson
{% endblocktrans %}
</a>
{% endif %}
</p>
{% else %}
......
......@@ -115,7 +115,7 @@
</td>
<td class="tr-link">
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
href="{{ register_object.alsijil_url }}">
{% if register_object.period %}
{{ register_object.period.period }}.
{% else %}
......@@ -126,7 +126,7 @@
{% if not group %}
<td>
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
href="{{ register_object.alsijil_url }}">
{% if register_object.lesson %}
{{ register_object.lesson.group_names }}
{% else %}
......@@ -137,41 +137,37 @@
{% endif %}
<td>
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
href="{{ register_object.alsijil_url }}">
{% if register_object.get_subject %}
{{ register_object.get_subject.name }}
{% elif register_object.subject %}
{{ register_object.subject }}
{% else %}
{% trans "Event" %}
{% trans "Event" %} ({{ register_object.title }})
{% endif %}
</a>
</td>
<td>
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
{% if register_object.get_teacher_names %}
{{ register_object.get_teacher_names }}
{% else %}
href="{{ register_object.alsijil_url }}">
{{ register_object.teacher_names }}
{% endif %}
</a>
</td>
<td>
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
href="{{ register_object.alsijil_url }}">
{% firstof register_object.get_lesson_documentation.topic "–" %}
</a>
</td>
<td>
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
href="{{ register_object.alsijil_url }}">
{% firstof register_object.get_lesson_documentation.homework "–" %}
</a>
</td>
<td>
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
href="{{ register_object.alsijil_url }}">
{% firstof register_object.get_lesson_documentation.group_note "–" %}
</a>
</td>
......@@ -194,7 +190,7 @@
{% has_perm "alsijil.view_lessondocumentation" user register_object as can_view_lesson_documentation %}
{% if can_view_lesson_documentation %}
<a class="collection-item avatar"
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
href="{{ register_object.alsijil_url }}">
{% include "alsijil/partials/lesson_status_icon.html" with register_object=register_object css_class="materialize-circle" color_suffix=" " %}
<table class="hide-on-med-and-down">
<tr>
......@@ -229,11 +225,7 @@
<tr>
<th>{% trans "Teachers" %}</th>
<td>
{% if register_object.get_teacher_names %}
{{ register_object.get_teacher_names }}
{% else %}
{{ register_object.teacher_names }}
{% endif %}
</td>
</tr>
<tr>
......@@ -268,7 +260,7 @@
{% elif register_object.subject %}
{{ register_object.subject }}
{% else %}
{% trans "Event" %}
{% trans "Event" %} ({{ register_object.title }})
{% endif %}
</li>
{% if not group %}
......@@ -281,11 +273,7 @@
</li>
{% endif %}
<li class="collection-item">
{% if register_object.get_teacher_names %}
{{ register_object.get_teacher_names }}
{% else %}
{{ register_object.teacher_names }}
{% endif %}
</li>
<li class="collection-item">
{{ register_object.get_lesson_documentation.topic }}
......@@ -359,10 +347,10 @@
{% if note.remarks %}
<blockquote>
{{ note.remarks }}
{% weekday_to_date week note.lesson_period.period.weekday as note_date %}
{% weekday_to_date week note.register_object.period.weekday as note_date %}
<em class="right">
<a href="{% url 'lesson_by_week_and_period' week.year week.week note.lesson_period.id %}">
{{ note_date }}, {{ note.lesson_period.get_subject.name }}
<a href="{{ note.register_object.alsijil_url }}">
{{ note.date }}, {{ note.register_object.get_subject.name }}
</a>
</em>
</blockquote>
......
......@@ -3,12 +3,20 @@ from django.urls import path
from . import views
urlpatterns = [
path("lesson", views.lesson, name="lesson"),
path("lesson", views.register_object, {"model": "lesson"}, name="lesson_period"),
path(
"lesson/<int:year>/<int:week>/<int:period_id>",
views.lesson,
name="lesson_by_week_and_period",
"lesson/<int:year>/<int:week>/<int:id_>",
views.register_object,
{"model": "lesson"},
name="lesson_period",
),
path(
"extra_lesson/<int:id_>/",
views.register_object,
{"model": "extra_lesson"},
name="extra_lesson",
),
path("event/<int:id_>/", views.register_object, {"model": "event"}, name="event",),
path("week/", views.week_view, name="week_view"),
path("week/<int:year>/<int:week>/", views.week_view, name="week_view_by_week"),
path("week/year/cw/", views.week_view, name="week_view_placeholders"),
......
......@@ -12,28 +12,33 @@ from aleksis.apps.chronos.models import Event, ExtraLesson, LessonPeriod
from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk
def get_lesson_period_by_pk(
def get_register_object_by_pk(
request: HttpRequest,
model: Optional[str] = None,
year: Optional[int] = None,
week: Optional[int] = None,
period_id: Optional[int] = None,
):
"""Get LessonPeriod object either by given object_id or by time and current person."""
id_: Optional[int] = None,
) -> Optional[Union[LessonPeriod, Event, ExtraLesson]]:
"""Get register object either by given object_id or by time and current person."""
wanted_week = CalendarWeek(year=year, week=week)
if period_id:
lesson_period = LessonPeriod.objects.annotate_week(wanted_week).get(pk=period_id)
if id_ and model == "lesson":
register_object = LessonPeriod.objects.annotate_week(wanted_week).get(pk=id_)
elif id_ and model == "event":
register_object = Event.objects.get(pk=id_)
elif id_ and model == "extra_lesson":
register_object = ExtraLesson.objects.get(pk=id_)
elif hasattr(request, "user") and hasattr(request.user, "person"):
if request.user.person.lessons_as_teacher.exists():
lesson_period = (
register_object = (
LessonPeriod.objects.at_time().filter_teacher(request.user.person).first()
)
else:
lesson_period = (
register_object = (
LessonPeriod.objects.at_time().filter_participant(request.user.person).first()
)
else:
lesson_period = None
return lesson_period
register_object = None
return register_object
def get_timetable_instance_by_pk(
......@@ -53,14 +58,18 @@ def get_timetable_instance_by_pk(
def annotate_documentations(
klass: Union[Event, LessonPeriod, ExtraLesson], wanted_week: CalendarWeek, pks: List[int]
) -> QuerySet:
instances = klass.objects.prefetch_related(
Prefetch(
if isinstance(klass, LessonPeriod):
prefetch = Prefetch(
"documentations",
queryset=LessonDocumentation.objects.filter(
week=wanted_week.week, year=wanted_week.year
),
)
).filter(pk__in=pks)
else:
prefetch = Prefetch("documentations")
instances = klass.objects.prefetch_related(prefetch).filter(pk__in=pks)
if klass == LessonPeriod:
instances = instances.annotate_week(wanted_week)
if klass in (LessonPeriod, ExtraLesson):
......
......@@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission, User
from guardian.models import UserObjectPermission
from rules import predicate
from aleksis.apps.chronos.models import LessonPeriod
from aleksis.apps.chronos.models import Event, ExtraLesson, LessonPeriod
from aleksis.core.models import Group, Person
from aleksis.core.util.core_helpers import get_content_type_by_perm
......@@ -19,17 +19,17 @@ def is_none(user: User, obj: Any) -> bool:
@predicate
def is_lesson_teacher(user: User, obj: LessonPeriod) -> bool:
def is_lesson_teacher(user: User, obj: Union[LessonPeriod, Event, ExtraLesson]) -> bool:
"""Predicate for teachers of a lesson.
Checks whether the person linked to the user is a teacher
in the lesson or the substitution linked to the given LessonPeriod.
"""
if obj:
sub = obj.get_substitution()
sub = obj.get_substitution() if isinstance(obj, LessonPeriod) else None
if sub and sub in user.person.lesson_substitutions.all():
return True
return user.person in obj.lesson.teachers.all()
return user.person in obj.get_teachers().all()
return False
......@@ -40,8 +40,8 @@ def is_lesson_participant(user: User, obj: LessonPeriod) -> bool:
Checks whether the person linked to the user is a member in
the groups linked to the given LessonPeriod.
"""
if hasattr(obj, "lesson"):
for group in obj.lesson.groups.all():
if hasattr(obj, "lesson") or hasattr(obj, "groups"):
for group in obj.get_groups().all():
if user.person in list(group.members.all()):
return True
return False
......@@ -55,8 +55,8 @@ def is_lesson_parent_group_owner(user: User, obj: LessonPeriod) -> bool:
Checks whether the person linked to the user is the owner of
any parent groups of any groups of the given LessonPeriods lesson.
"""
if hasattr(obj, "lesson"):
for group in obj.lesson.groups.all():
if hasattr(obj, "lesson") or hasattr(obj, "groups"):
for group in obj.get_groups().all():
for parent_group in group.parent_groups.all():
if user.person in list(parent_group.owners.all()):
return True
......
......@@ -39,36 +39,37 @@ from .models import ExcuseType, ExtraMark, PersonalNote
from .tables import ExcuseTypeTable, ExtraMarkTable
from .util.alsijil_helpers import (
annotate_documentations,
get_lesson_period_by_pk,
get_register_object_by_pk,
get_timetable_instance_by_pk,
register_objects_sorter,
)
@permission_required("alsijil.view_lesson", fn=get_lesson_period_by_pk)
def lesson(
@permission_required("alsijil.view_register_object", fn=get_register_object_by_pk) # FIXME
def register_object(
request: HttpRequest,
model: Optional[str] = None,
year: Optional[int] = None,
week: Optional[int] = None,
period_id: Optional[int] = None,
id_: Optional[int] = None,
) -> HttpResponse:
context = {}
lesson_period = get_lesson_period_by_pk(request, year, week, period_id)
register_object = get_register_object_by_pk(request, model, year, week, id_)
if period_id:
if id_ and model == "lesson":
wanted_week = CalendarWeek(year=year, week=week)
elif id_ and model == "extra_lesson":
wanted_week = register_object.calendar_week
elif hasattr(request, "user") and hasattr(request.user, "person"):
wanted_week = CalendarWeek()
else:
wanted_week = None
if not all((year, week, period_id)):
if lesson_period:
return redirect(
"lesson_by_week_and_period", wanted_week.year, wanted_week.week, lesson_period.pk,
)
else:
if not all((year, week, id_)):
if register_object and model == "lesson":
return redirect("lesson", wanted_week.year, wanted_week.week, register_object.pk,)
elif not register_object:
raise Http404(
_(
"You either selected an invalid lesson or "
......@@ -76,22 +77,30 @@ def lesson(
)
)
date_of_lesson = week_weekday_to_date(wanted_week, lesson_period.period.weekday)
date_of_lesson = (
week_weekday_to_date(wanted_week, register_object.period.weekday)
if not isinstance(register_object, Event)
else register_object.date_start
)
start_time = (
register_object.period.time_start
if not isinstance(register_object, Event)
else register_object.period_from.time_start
)
if (
date_of_lesson < lesson_period.lesson.validity.date_start
or date_of_lesson > lesson_period.lesson.validity.date_end
if isinstance(register_object, Event):
register_object.annotate_day(date_of_lesson)
if isinstance(register_object, LessonPeriod) and (
date_of_lesson < register_object.lesson.validity.date_start
or date_of_lesson > register_object.lesson.validity.date_end
):
return HttpResponseNotFound()
if (
datetime.combine(
wanted_week[lesson_period.period.weekday], lesson_period.period.time_start,
)
> datetime.now()
datetime.combine(date_of_lesson, start_time) > datetime.now()
and not (
get_site_preferences()["alsijil__open_periods_same_day"]
and wanted_week[lesson_period.period.weekday] <= datetime.now().date()
and date_of_lesson <= datetime.now().date()
)
and not request.user.is_superuser
):
......@@ -106,39 +115,51 @@ def lesson(
context["blocked_because_holidays"] = blocked_because_holidays
context["holiday"] = holiday
next_lesson = request.user.person.next_lesson(lesson_period, date_of_lesson)
prev_lesson = request.user.person.previous_lesson(lesson_period, date_of_lesson)
next_lesson = (
request.user.person.next_lesson(register_object, date_of_lesson)
if isinstance(register_object, LessonPeriod)
else None
)
prev_lesson = (
request.user.person.previous_lesson(register_object, date_of_lesson)
if isinstance(register_object, LessonPeriod)
else None
)
context["lesson_period"] = lesson_period
context["register_object"] = register_object
context["week"] = wanted_week
context["day"] = wanted_week[lesson_period.period.weekday]
context["day"] = date_of_lesson
context["next_lesson_person"] = next_lesson
context["prev_lesson_person"] = prev_lesson
context["prev_lesson"] = lesson_period.prev
context["next_lesson"] = lesson_period.next
context["prev_lesson"] = (
register_object.prev if isinstance(register_object, LessonPeriod) else None
)
context["next_lesson"] = (
register_object.next if isinstance(register_object, LessonPeriod) else None
)
if not blocked_because_holidays:
# Create or get lesson documentation object; can be empty when first opening lesson
lesson_documentation = lesson_period.get_or_create_lesson_documentation(wanted_week)
lesson_documentation = register_object.get_or_create_lesson_documentation(wanted_week)
lesson_documentation_form = LessonDocumentationForm(
request.POST or None, instance=lesson_documentation, prefix="lesson_documentation",
)
# Create a formset that holds all personal notes for all persons in this lesson
if not request.user.has_perm("alsijil.view_lesson_personalnote", lesson_period):
if not request.user.has_perm("alsijil.view_register_object_personalnote", register_object):
persons = Person.objects.filter(pk=request.user.person.pk)
else:
persons = Person.objects.all()
persons_qs = lesson_period.get_personal_notes(persons, wanted_week)
persons_qs = register_object.get_personal_notes(persons, wanted_week)
personal_note_formset = PersonalNoteFormSet(
request.POST or None, queryset=persons_qs, prefix="personal_notes"
)
if request.method == "POST":
if lesson_documentation_form.is_valid() and request.user.has_perm(
"alsijil.edit_lessondocumentation", lesson_period
"alsijil.edit_lessondocumentation", register_object
):
with reversion.create_revision():
reversion.set_user(request.user)
......@@ -146,27 +167,33 @@ def lesson(
messages.success(request, _("The lesson documentation has been saved."))
substitution = lesson_period.get_substitution()
substitution = (
register_object.get_substitution()
if isinstance(register_object, LessonPeriod)
else None
)
if (
not getattr(substitution, "cancelled", False)
or not get_site_preferences()["alsijil__block_personal_notes_for_cancelled"]
):
if personal_note_formset.is_valid() and request.user.has_perm(
"alsijil.edit_lesson_personalnote", lesson_period
"alsijil.edit_register_object_personalnote", register_object
):
with reversion.create_revision():
reversion.set_user(request.user)
instances = personal_note_formset.save()
# Iterate over personal notes and carry changed absences to following lessons
for instance in instances:
instance.person.mark_absent(
wanted_week[lesson_period.period.weekday],
lesson_period.period.period + 1,
instance.absent,
instance.excused,
instance.excuse_type,
)
if not isinstance(register_object, Event):
# Iterate over personal notes
# and carry changed absences to following lessons
for instance in instances:
instance.person.mark_absent(
wanted_week[register_object.period.weekday],
register_object.period.period + 1,
instance.absent,
instance.excused,
instance.excuse_type,
)
messages.success(request, _("The personal notes have been saved."))
......@@ -376,7 +403,12 @@ def week_view(
persons = []
for person in persons_qs:
persons.append({"person": person, "personal_notes": list(person.personal_notes.all())})
personal_notes = []
for note in person.personal_notes.all():
if note.lesson_period:
note.lesson_period.annotate_week(wanted_week)
personal_notes.append(note)
persons.append({"person": person, "personal_notes": personal_notes})
else:
persons = None
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment