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

Merge branch 'master' into feature/smart-plan-dashboard-widget

# Conflicts:
#	aleksis/apps/chronos/views.py
parents 7e89c1d4 fbc203aa
No related branches found
No related tags found
1 merge request!36Dashboard widget for SMART PLAN
......@@ -9,6 +9,8 @@ from django.db import models
from django.db.models import F, Max, Min, Q
from django.db.models.functions import Coalesce
from django.http.request import QueryDict
from django.urls import reverse
from django.utils import timezone
from django.utils.decorators import classproperty
from django.utils.translation import ugettext_lazy as _
......@@ -205,6 +207,21 @@ class LessonSubstitutionQuerySet(LessonDataQuerySet):
_period_path = "lesson_period__"
_subst_path = ""
def affected_lessons(self):
""" Return all lessons which are affected by selected substitutions """
return Lesson.objects.filter(lesson_periods__substitutions__in=self)
def affected_teachers(self):
""" Return all teachers which are affected by selected substitutions (as substituted or substituting) """
return Person.objects.filter(Q(lessons_as_teacher__in=self.affected_lessons()) | Q(lesson_substitutions__in=self))
def affected_groups(self):
""" Return all groups which are affected by selected substitutions """
return Group.objects.filter(lessons__in=self.affected_lessons())
class TimePeriod(models.Model):
WEEKDAY_CHOICES = list(enumerate(i18n_day_names_lazy()))
......@@ -246,6 +263,46 @@ class TimePeriod(models.Model):
return wanted_week[self.weekday]
@classmethod
def get_next_relevant_day(cls, day: Optional[date] = None, time: Optional[time] = None, prev: bool = False) -> date:
""" Returns next (previous) day with lessons depending on date and time """
if day is None:
day = timezone.now().date()
if time is not None and not prev:
if time > cls.time_max:
day += timedelta(days=1)
cw = CalendarWeek.from_date(day)
if day.weekday() > cls.weekday_max:
if prev:
day = cw[cls.weekday_max]
else:
cw += 1
day = cw[cls.weekday_min]
elif day.weekday() < TimePeriod.weekday_min:
if prev:
cw -= 1
day = cw[cls.weekday_max]
else:
day = cw[cls.weekday_min]
return day
@classmethod
def get_prev_next_by_day(cls, day: date, url: str) -> Tuple[str, str]:
""" Build URLs for previous/next day """
day_prev = cls.get_next_relevant_day(day - timedelta(days=1), prev=True)
day_next = cls.get_next_relevant_day(day + timedelta(days=1))
url_prev = reverse(url, args=[day_prev.year, day_prev.month, day_prev.day])
url_next = reverse(url, args=[day_next.year, day_next.month, day_next.day])
return url_prev, url_next
@classproperty
def period_min(cls) -> int:
return cls.objects.aggregate(period__min=Coalesce(Min("period"), 1)).get("period__min")
......
from django.utils.translation import gettext_lazy as _
CONSTANCE_CONFIG = {
"CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER": (2, _("Number of days shown on substitutions print view")),
"CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER": (
2,
_("Number of days shown on substitutions print view"),
),
"CHRONOS_SUBSTITUTIONS_SHOW_HEADER_BOX": (
True,
_("The header box shows affected teachers/groups."),
),
}
CONSTANCE_CONFIG_FIELDSETS = {
"Chronos settings": ("CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER",),
"Chronos settings": (
"CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER",
"CHRONOS_SUBSTITUTIONS_SHOW_HEADER_BOX",
),
}
......@@ -108,3 +108,7 @@ table.substitutions td, table.substitutions th {
margin: 2px;
letter-spacing: 0.3pt;
}
.black-text-a a {
color: black;
}
{% load i18n %}
{% if affected_teachers and affected_groups %}
<div class="{% if not print %}card{% endif %}">
<div class="{% if not print %}card-content{% endif %}">
{% if affected_teachers %}
<div class="row no-margin">
<div class="col s12 m3">
<strong class="truncate">
{% trans "Affected teachers" %}
</strong>
</div>
<div class="col s12 m9 black-text-a">
{% include "chronos/partials/teachers.html" with teachers=affected_teachers %}
</div>
</div>
{% endif %}
{% if affected_groups %}
<div class="row no-margin">
<div class="col s12 m3">
<strong class="truncate">
{% trans "Affected groups" %}
</strong>
</div>
<div class="col s12 m9 black-text-a">
{% include "chronos/partials/groups.html" with groups=affected_groups %}
</div>
</div>
{% endif %}
</div>
</div>
{% if print %}<br/>{% endif %}
{% endif %}
......@@ -26,22 +26,7 @@
<div class="row no-print">
<div class="col s12 m6 l8">
{% if header_info.is_box_needed %}
<div class="card">
<div class="card-content">
{% for row in header_info.rows %}
<div class="row no-margin">
<div class="col s3">
<strong class="truncate">{{ row.0 }}</strong>
</div>
<div class="col s9">
{{ row.1 }}
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% include "chronos/partials/headerbox.html" %}
{# {% include "chronos/hintsinsub.html" %}#}
</div>
......
......@@ -18,20 +18,7 @@
{# {% include "timetable/hintsinsubprint.html" %}#}
{# <div style="margin-bottom: 20px">#}
{# {% if c.header_info.is_box_needed %}#}
{# {% for row in c.header_info.rows %}#}
{# <div class="row no-margin">#}
{# <div class="col s3 no-padding">#}
{# <strong>{{ row.0 }}</strong>#}
{# </div>#}
{# <div class="col s9 no-padding">#}
{# {{ row.1 }}#}
{# </div>#}
{# </div>#}
{# {% endfor %}#}
{# {% endif %}#}
{# </div>#}
{% include "chronos/partials/headerbox.html" with affected_teachers=c.affected_teachers affected_groups=c.affected_groups print=1 %}
<table class="substitutions">
<thead>
......
from datetime import timedelta, date, time
from typing import Optional, Tuple
from calendarweek import CalendarWeek
from django.urls import reverse
from django.utils import timezone
from ..models import TimePeriod
def get_next_relevant_day(day: Optional[date] = None, time: Optional[time] = None, prev: bool = False) -> date:
""" Returns next (previous) day with lessons depending on date and time """
if day is None:
day = timezone.now().date()
if time is not None and not prev:
if time > TimePeriod.time_max:
day += timedelta(days=1)
cw = CalendarWeek.from_date(day)
if day.weekday() > TimePeriod.weekday_max:
if prev:
day = cw[TimePeriod.weekday_max]
else:
cw += 1
day = cw[TimePeriod.weekday_min]
elif day.weekday() < TimePeriod.weekday_min:
if prev:
cw -= 1
day = cw[TimePeriod.weekday_max]
else:
day = cw[TimePeriod.weekday_min]
return day
def get_prev_next_by_day(day: date, url: str) -> Tuple[str, str]:
""" Build URLs for previous/next day """
day_prev = get_next_relevant_day(day - timedelta(days=1), prev=True)
day_next = get_next_relevant_day(day + timedelta(days=1))
url_prev = reverse(url, args=[day_prev.year, day_prev.month, day_prev.day])
url_next = reverse(url, args=[day_next.year, day_next.month, day_next.day])
return url_prev, url_next
......@@ -19,7 +19,6 @@ from .forms import LessonSubstitutionForm
from .models import LessonPeriod, LessonSubstitution, TimePeriod, Room
from .tables import LessonsTable
from .util.js import date_unix
from .util.prev_next import get_next_relevant_day, get_prev_next_by_day
from .util.date import CalendarWeek, get_weeks_for_year
from aleksis.core.util.core_helpers import has_person
......@@ -56,9 +55,9 @@ def my_timetable(
if day:
wanted_day = timezone.datetime(year=year, month=month, day=day).date()
wanted_day = get_next_relevant_day(wanted_day)
wanted_day = TimePeriod.get_next_relevant_day(wanted_day)
else:
wanted_day = get_next_relevant_day(timezone.now().date(), datetime.now().time())
wanted_day = TimePeriod.get_next_relevant_day(timezone.now().date(), datetime.now().time())
if has_person(request.user):
person = request.user.person
......@@ -98,7 +97,7 @@ def my_timetable(
context["periods"] = TimePeriod.get_times_dict()
context["smart"] = True
context["url_prev"], context["url_next"] = get_prev_next_by_day(
context["url_prev"], context["url_next"] = TimePeriod.get_prev_next_by_day(
wanted_day, "my_timetable_by_date"
)
......@@ -209,9 +208,9 @@ def lessons_day(
if day:
wanted_day = timezone.datetime(year=year, month=month, day=day).date()
wanted_day = get_next_relevant_day(wanted_day)
wanted_day = TimePeriod.get_next_relevant_day(wanted_day)
else:
wanted_day = get_next_relevant_day(timezone.now().date(), datetime.now().time())
wanted_day = TimePeriod.get_next_relevant_day(timezone.now().date(), datetime.now().time())
# Get lessons
lesson_periods = LessonPeriod.objects.on_day(wanted_day)
......@@ -229,7 +228,7 @@ def lessons_day(
"dest": reverse("lessons_day")
}
context["url_prev"], context["url_next"] = get_prev_next_by_day(
context["url_prev"], context["url_next"] = TimePeriod.get_prev_next_by_day(
wanted_day, "lessons_day_by_date"
)
......@@ -305,9 +304,9 @@ def substitutions(
if day:
wanted_day = timezone.datetime(year=year, month=month, day=day).date()
wanted_day = get_next_relevant_day(wanted_day)
wanted_day = TimePeriod.get_next_relevant_day(wanted_day)
else:
wanted_day = get_next_relevant_day(timezone.now().date(), datetime.now().time())
wanted_day = TimePeriod.get_next_relevant_day(timezone.now().date(), datetime.now().time())
day_number = config.CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER
day_contexts = {}
......@@ -316,14 +315,17 @@ def substitutions(
next_day = wanted_day
for i in range(day_number):
day_contexts[next_day] = {"day": next_day}
next_day = get_next_relevant_day(next_day + timedelta(days=1))
next_day = TimePeriod.get_next_relevant_day(next_day + timedelta(days=1))
else:
day_contexts = {wanted_day: {"day": wanted_day}}
for day in day_contexts:
day_contexts[day]["substitutions"] = LessonSubstitution.objects.on_day(
day
).order_by("lesson_period__lesson__groups", "lesson_period__period")
subs = LessonSubstitution.objects.on_day(day).order_by("lesson_period__lesson__groups", "lesson_period__period")
day_contexts[day]["substitutions"] = subs
if config.CHRONOS_SUBSTITUTIONS_SHOW_HEADER_BOX:
day_contexts[day]["affected_teachers"] = subs.affected_teachers()
day_contexts[day]["affected_groups"] = subs.affected_groups()
if not is_print:
context = day_contexts[wanted_day]
......@@ -332,7 +334,7 @@ def substitutions(
"dest": reverse("substitutions"),
}
context["url_prev"], context["url_next"] = get_prev_next_by_day(
context["url_prev"], context["url_next"] = TimePeriod.get_prev_next_by_day(
wanted_day, "substitutions_by_date"
)
......
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