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

Include support for events and extra lessons in week view

parent 8210047e
No related branches found
No related tags found
1 merge request!120Resolve "Support events and extra lessons in class register"
......@@ -73,17 +73,14 @@
</ul>
</div>
<div class="col s12" id="week-overview">
{% regroup lesson_periods by period.get_weekday_display as periods_by_day %}
{% for weekday, periods in periods_by_day %}
{% for weekday, objects in regrouped_objects.items %}
{% with weekdays|get_dict:forloop.counter0 as advanced_weekday %}
{% weekday_to_date week periods.0.period.weekday as current_date %}
{% if advanced_weekday.holiday and not request.site.preferences.alsijil__allow_entries_in_holidays %}
<div class="card">
<div class="card-content">
{% weekday_to_date week periods.0.period.weekday as current_date %}
<span class="card-title">
{{ weekday }}, {{ current_date }} <span class="badge new blue no-float">{{ advanced_weekday.holiday }}</span>
{{ advanced_weekday.name }}, {{ advanced_weekday.date }} <span
class="badge new blue no-float">{{ advanced_weekday.holiday }}</span>
</span>
</div>
</div>
......@@ -91,7 +88,7 @@
<div class="card show-on-extra-large">
<div class="card-content">
<span class="card-title">
{{ weekday }}, {{ current_date }}
{{ advanced_weekday.name }}, {{ advanced_weekday.date }}
</span>
<table class="striped datatable">
<thead>
......@@ -109,55 +106,76 @@
</tr>
</thead>
<tbody>
{% for period in periods %}
{% has_perm "alsijil.view_lessondocumentation" user period as can_view_lesson_documentation %}
{% for register_object in objects %}
{% has_perm "alsijil.view_lessondocumentation" user register_object as can_view_lesson_documentation %}
{% if can_view_lesson_documentation %}
<tr>
<td class="center-align">
{% include "alsijil/partials/lesson_status_icon.html" with period=period %}
{% include "alsijil/partials/lesson_status_icon.html" with register_object=register_object %}
</td>
<td class="tr-link">
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week period.id %}">
{{ period.period.period }}.
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
{% if register_object.period %}
{{ register_object.period.period }}.
{% else %}
{{ register_object.date_start|date:"SHORT_DATE_FORMAT" }}
{{ register_object.period_from.period }}.–<br/>
{{ register_object.date_end|date:"SHORT_DATE_FORMAT" }}
{{ register_object.period_to.period }}.
{% endif %}
</a>
</td>
{% if not group %}
<td>
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week period.id %}">
{{ period.lesson.group_names }}
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
{% if register_object.lesson %}
{{ register_object.lesson.group_names }}
{% else %}
{{ register_object.group_names }}
{% endif %}
</a>
</td>
{% endif %}
<td>
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week period.id %}">
{{ period.get_subject.name }}
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
{% if register_object.get_subject %}
{{ register_object.get_subject.name }}
{% elif register_object.subject %}
{{ register_object.subject }}
{% else %}
{% trans "Event" %}
{% endif %}
</a>
</td>
<td>
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week period.id %}">
{{ period.get_teacher_names }}
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 %}
{{ register_object.teacher_names }}
{% endif %}
</a>
</td>
<td>
<a class="tr-link"
href="{% url 'lesson_by_week_and_period' week.year week.week period.id %}">
{% firstof period.get_lesson_documentation.topic "–" %}
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
{% 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 period.id %}">
{% firstof period.get_lesson_documentation.homework "–" %}
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
{% 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 period.id %}">
{% firstof period.get_lesson_documentation.group_note "–" %}
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
{% firstof register_object.get_lesson_documentation.group_note "–" %}
</a>
</td>
</tr>
......@@ -169,48 +187,47 @@
</div>
<ul class="collapsible hide-on-extra-large-only">
<li class="">
{% weekday_to_date week periods.0.period.weekday as current_date %}
<div class="collapsible-header flow-text">
{{ weekday }}, {{ current_date }} <i class="material-icons collapsible-icon-right">expand_more</i>
{{ weekday }}, {{ advanced_weekday.date }} <i class="material-icons collapsible-icon-right">expand_more</i>
</div>
<div class="collapsible-body">
<div class="collection">
{% for period in periods %}
{% has_perm "alsijil.view_lessondocumentation" user period as can_view_lesson_documentation %}
{% for register_object in objects %}
{% 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 period.id %}">
{% include "alsijil/partials/lesson_status_icon.html" with period=period css_class="materialize-circle" color_suffix=" " %}
href="{% url 'lesson_by_week_and_period' week.year week.week register_object.id %}">
{% 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>
<th>{% trans "Subject" %}</th>
<td>{{ period.period.period }}. {{ period.get_subject.name }}</td>
<td>{{ register_object.period.period }}. {{ register_object.get_subject.name }}</td>
</tr>
{% if not group %}
<tr>
<th>{% trans "Group" %}</th>
<td>{{ period.lesson.group_names }}</td>
<td>{{ register_object.lesson.group_names }}</td>
</tr>
{% endif %}
<tr>
<th>{% trans "Teachers" %}</th>
<td>{{ period.lesson.teacher_names }}</td>
<td>{{ register_object.lesson.teacher_names }}</td>
</tr>
<tr>
<th>{% trans "Lesson topic" %}</th>
<td>{% firstof period.get_lesson_documentation.topic "–" %}</td>
<td>{% firstof register_object.get_lesson_documentation.topic "–" %}</td>
</tr>
{% with period.get_lesson_documentation as lesson_documentation %}
{% if lesson_documentation.homework %}
<tr>
<th>{% trans "Homework" %}</th>
<td>{% firstof period.get_lesson_documentation.homework "–" %}</td>
<td>{% firstof register_object.get_lesson_documentation.homework "–" %}</td>
</tr>
{% endif %}
{% if lesson_documentation.group_note %}
<tr>
<th>{% trans "Group note" %}</th>
<td>{% firstof period.get_lesson_documentation.group_note "–" %}</td>
<td>{% firstof register_object.get_lesson_documentation.group_note "–" %}</td>
</tr>
{% endif %}
{% endwith %}
......@@ -218,32 +235,32 @@
<div class="hide-on-large-only">
<ul class="collection">
<li class="collection-item">
{{ period.period.period }}. {{ period.get_subject.name }}
{{ register_object.period.period }}. {{ register_object.get_subject.name }}
</li>
{% if not group %}
<li class="collection-item">
{{ period.lesson.group_names }}
{{ register_object.lesson.group_names }}
</li>
{% endif %}
<li class="collection-item">
{{ period.lesson.teacher_names }}
{{ register_object.lesson.teacher_names }}
</li>
<li class="collection-item">
{{ period.get_lesson_documentation.topic }}
{{ register_object.get_lesson_documentation.topic }}
</li>
{% with period.get_lesson_documentation as lesson_documentation %}
{% if lesson_documentation.homework %}
<li class="collection-item">
<strong>{% trans "Homework" %}</strong>
{% firstof period.get_lesson_documentation.homework "–" %}
{% firstof register_object.get_lesson_documentation.homework "–" %}
</li>
{% endif %}
{% if lesson_documentation.group_note %}
<li class="collection-item">
<strong>{% trans "Group note" %}</strong>
{% firstof period.get_lesson_documentation.group_note "–" %}
{% firstof register_object.get_lesson_documentation.group_note "–" %}
</li>
{% endif %}
{% endwith %}
......
from typing import Optional
from typing import List, Optional, Union
from django.db.models.expressions import Exists, OuterRef
from django.db.models.query import Prefetch, QuerySet
from django.db.models.query_utils import Q
from django.http import HttpRequest
from calendarweek import CalendarWeek
from aleksis.apps.chronos.models import LessonPeriod
from aleksis.apps.alsijil.models import LessonDocumentation
from aleksis.apps.chronos.models import Event, ExtraLesson, LessonPeriod
from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk
......@@ -44,3 +48,42 @@ def get_timetable_instance_by_pk(
return get_el_by_pk(request, type_, id_)
elif hasattr(request, "user") and hasattr(request.user, "person"):
return request.user.person
def annotate_documentations(
klass: Union[Event, LessonPeriod, ExtraLesson], wanted_week: CalendarWeek, pks: List[int]
) -> QuerySet:
instances = klass.objects.prefetch_related(
Prefetch(
"documentations",
queryset=LessonDocumentation.objects.filter(
week=wanted_week.week, year=wanted_week.year
),
)
).filter(pk__in=pks)
if klass == LessonPeriod:
instances = instances.annotate_week(wanted_week)
if klass in (LessonPeriod, ExtraLesson):
instances = instances.order_by("period__weekday", "period__period")
else:
instances = instances.order_by("period_from__weekday", "period_from__period")
args = {Event: "event", LessonPeriod: "lesson_period", ExtraLesson: "extra_lesson"}
instances = instances.annotate(
has_documentation=Exists(
LessonDocumentation.objects.filter(
~Q(topic__exact=""), week=wanted_week.week, year=wanted_week.year,
).filter(**{args[klass]: OuterRef("pk")})
)
)
return instances
def register_objects_sorter(register_object: Union[LessonPeriod, Event, ExtraLesson]) -> int:
"""Sort key for sorted/sort for sorting a list of class register objects."""
if hasattr(register_object, "period"):
return register_object.period.period
elif isinstance(register_object, Event):
return register_object.period_from_on_day
else:
return 0
from copy import deepcopy
from datetime import date, datetime, timedelta
from typing import Optional
......@@ -18,7 +19,7 @@ from reversion.views import RevisionMixin
from rules.contrib.views import PermissionRequiredMixin, permission_required
from aleksis.apps.chronos.managers import TimetableType
from aleksis.apps.chronos.models import Holiday, LessonPeriod, TimePeriod
from aleksis.apps.chronos.models import Event, ExtraLesson, Holiday, LessonPeriod, TimePeriod
from aleksis.apps.chronos.util.build import build_weekdays
from aleksis.apps.chronos.util.date import get_weeks_for_year, week_weekday_to_date
from aleksis.core.mixins import AdvancedCreateView, AdvancedDeleteView, AdvancedEditView
......@@ -34,9 +35,14 @@ from .forms import (
RegisterAbsenceForm,
SelectForm,
)
from .models import ExcuseType, ExtraMark, LessonDocumentation, PersonalNote
from .models import ExcuseType, ExtraMark, PersonalNote
from .tables import ExcuseTypeTable, ExtraMarkTable
from .util.alsijil_helpers import get_lesson_period_by_pk, get_timetable_instance_by_pk
from .util.alsijil_helpers import (
annotate_documentations,
get_lesson_period_by_pk,
get_timetable_instance_by_pk,
register_objects_sorter,
)
@permission_required("alsijil.view_lesson", fn=get_lesson_period_by_pk)
......@@ -199,8 +205,10 @@ def week_view(
"lesson__groups__parent_groups",
"lesson__groups__parent_groups__owners",
)
events = Event.objects.in_week(wanted_week)
extra_lessons = ExtraLesson.objects.in_week(wanted_week)
lesson_periods_query_exists = True
query_exists = True
if type_ and id_:
if isinstance(instance, HttpResponseNotFound):
return HttpResponseNotFound()
......@@ -208,15 +216,26 @@ def week_view(
type_ = TimetableType.from_string(type_)
lesson_periods = lesson_periods.filter_from_type(type_, instance)
events = events.filter_from_type(type_, instance)
extra_lessons = extra_lessons.filter_from_Type(type_, instance)
elif hasattr(request, "user") and hasattr(request.user, "person"):
if request.user.person.lessons_as_teacher.exists():
lesson_periods = lesson_periods.filter_teacher(request.user.person)
events = events.filter_teacher(request.user.person)
extra_lessons = extra_lessons.filter_teacher(request.user.person)
type_ = TimetableType.TEACHER
else:
lesson_periods = lesson_periods.filter_participant(request.user.person)
events = events.filter_participant(request.user.person)
extra_lessons = extra_lessons.filter_participant(request.user.person)
else:
lesson_periods_query_exists = False
query_exists = False
lesson_periods = None
events = None
extra_lessons = None
# Add a form to filter the view
if type_:
......@@ -245,35 +264,21 @@ def week_view(
extra_marks = ExtraMark.objects.all()
if lesson_periods_query_exists:
if query_exists:
lesson_periods_pk = list(lesson_periods.values_list("pk", flat=True))
lesson_periods = (
LessonPeriod.objects.prefetch_related(
Prefetch(
"documentations",
queryset=LessonDocumentation.objects.filter(
week=wanted_week.week, year=wanted_week.year
),
)
)
.filter(pk__in=lesson_periods_pk)
.annotate_week(wanted_week)
.annotate(
has_documentation=Exists(
LessonDocumentation.objects.filter(
~Q(topic__exact=""),
lesson_period=OuterRef("pk"),
week=wanted_week.week,
year=wanted_week.year,
)
)
)
.order_by("period__weekday", "period__period")
)
lesson_periods = annotate_documentations(LessonPeriod, wanted_week, lesson_periods_pk)
events_pk = list(events.values_list("pk", flat=True))
events = annotate_documentations(Event, wanted_week, events_pk)
extra_lessons_pk = list(extra_lessons.values_list("pk", flat=True))
extra_lessons = annotate_documentations(ExtraLesson, wanted_week, extra_lessons_pk)
else:
lesson_periods_pk = []
events_pk = []
extra_lessons_pk = []
if lesson_periods_pk:
if lesson_periods_pk or events_pk or extra_lessons_pk:
# Aggregate all personal notes for this group and week
persons_qs = Person.objects.filter(is_active=True)
......@@ -282,7 +287,29 @@ def week_view(
elif group:
persons_qs = persons_qs.filter(member_of=group)
else:
persons_qs = persons_qs.filter(member_of__lessons__lesson_periods__in=lesson_periods_pk)
persons_qs = persons_qs.filter(
Q(member_of__lessons__lesson_periods__in=lesson_periods_pk)
| Q(member_of__events__in=events_pk)
| Q(member_of__extra_lessons__in=extra_lessons_pk)
)
personal_notes_q = (
Q(
personal_notes__week=wanted_week.week,
personal_notes__year=wanted_week.year,
personal_notes__lesson_period__in=lesson_periods_pk,
)
| Q(
personal_notes__event__date_start__lte=wanted_week[6],
personal_notes__event__date_end__gte=wanted_week[0],
personal_notes__event__in=events_pk,
)
| Q(
personal_notes__extra_lesson__week=wanted_week.week,
personal_notes__extra_lesson__year=wanted_week.year,
personal_notes__extra_lesson__in=extra_lessons_pk,
)
)
persons_qs = (
persons_qs.distinct()
......@@ -290,9 +317,21 @@ def week_view(
Prefetch(
"personal_notes",
queryset=PersonalNote.objects.filter(
week=wanted_week.week,
year=wanted_week.year,
lesson_period__in=lesson_periods_pk,
Q(
week=wanted_week.week,
year=wanted_week.year,
lesson_period__in=lesson_periods_pk,
)
| Q(
event__date_start__lte=wanted_week[6],
event__date_end__gte=wanted_week[0],
event__in=events_pk,
)
| Q(
extra_lesson__week=wanted_week.week,
extra_lesson__year=wanted_week.year,
extra_lesson__in=extra_lessons_pk,
)
),
),
"member_of__owners",
......@@ -300,44 +339,25 @@ def week_view(
.annotate(
absences_count=Count(
"personal_notes",
filter=Q(
personal_notes__lesson_period__in=lesson_periods_pk,
personal_notes__week=wanted_week.week,
personal_notes__year=wanted_week.year,
personal_notes__absent=True,
),
filter=personal_notes_q & Q(personal_notes__absent=True,),
distinct=True,
),
unexcused_count=Count(
"personal_notes",
filter=Q(
personal_notes__lesson_period__in=lesson_periods_pk,
personal_notes__week=wanted_week.week,
personal_notes__year=wanted_week.year,
personal_notes__absent=True,
personal_notes__excused=False,
),
filter=personal_notes_q
& Q(personal_notes__absent=True, personal_notes__excused=False,),
distinct=True,
),
tardiness_sum=Subquery(
Person.objects.filter(
pk=OuterRef("pk"),
personal_notes__lesson_period__in=lesson_periods_pk,
personal_notes__week=wanted_week.week,
personal_notes__year=wanted_week.year,
)
Person.objects.filter(personal_notes_q)
.filter(pk=OuterRef("pk"),)
.distinct()
.annotate(tardiness_sum=Sum("personal_notes__late"))
.values("tardiness_sum")
),
tardiness_count=Count(
"personal_notes",
filter=Q(
personal_notes__lesson_period__in=lesson_periods_pk,
personal_notes__week=wanted_week.week,
personal_notes__year=wanted_week.year,
)
& ~Q(personal_notes__late=0),
filter=personal_notes_q & ~Q(personal_notes__late=0),
distinct=True,
),
)
......@@ -348,12 +368,7 @@ def week_view(
**{
extra_mark.count_label: Count(
"personal_notes",
filter=Q(
personal_notes__lesson_period__in=lesson_periods_pk,
personal_notes__week=wanted_week.week,
personal_notes__year=wanted_week.year,
personal_notes__extra_marks=extra_mark,
),
filter=personal_notes_q & Q(personal_notes__extra_marks=extra_mark,),
distinct=True,
)
}
......@@ -368,13 +383,42 @@ def week_view(
context["extra_marks"] = extra_marks
context["week"] = wanted_week
context["weeks"] = get_weeks_for_year(year=wanted_week.year)
context["lesson_periods"] = lesson_periods
context["events"] = events
context["extra_lessons"] = extra_lessons
context["persons"] = persons
context["group"] = group
context["select_form"] = select_form
context["instance"] = instance
context["weekdays"] = build_weekdays(TimePeriod.WEEKDAY_CHOICES, wanted_week)
regrouped_objects = {}
for register_object in list(lesson_periods) + list(extra_lessons):
regrouped_objects.setdefault(register_object.period.weekday, [])
regrouped_objects[register_object.period.weekday].append(register_object)
for event in events:
weekday_from = event.get_start_weekday(wanted_week)
weekday_to = event.get_end_weekday(wanted_week)
print(weekday_from, weekday_to)
for weekday in range(weekday_from, weekday_to + 1):
# Make a copy in order to keep the annotation only on this weekday
event_copy = deepcopy(event)
event.annotate_day(wanted_week[weekday])
regrouped_objects.setdefault(weekday, [])
regrouped_objects[weekday].append(event_copy)
# Sort register objects
for weekday in regrouped_objects.keys():
to_sort = regrouped_objects[weekday]
regrouped_objects[weekday] = sorted(to_sort, key=register_objects_sorter)
context["regrouped_objects"] = regrouped_objects
week_prev = wanted_week - 1
week_next = wanted_week + 1
args_prev = [week_prev.year, week_prev.week]
......
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