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 ...@@ -9,6 +9,8 @@ from django.db import models
from django.db.models import F, Max, Min, Q from django.db.models import F, Max, Min, Q
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.http.request import QueryDict 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.decorators import classproperty
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
...@@ -205,6 +207,21 @@ class LessonSubstitutionQuerySet(LessonDataQuerySet): ...@@ -205,6 +207,21 @@ class LessonSubstitutionQuerySet(LessonDataQuerySet):
_period_path = "lesson_period__" _period_path = "lesson_period__"
_subst_path = "" _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): class TimePeriod(models.Model):
WEEKDAY_CHOICES = list(enumerate(i18n_day_names_lazy())) WEEKDAY_CHOICES = list(enumerate(i18n_day_names_lazy()))
...@@ -246,6 +263,46 @@ class TimePeriod(models.Model): ...@@ -246,6 +263,46 @@ class TimePeriod(models.Model):
return wanted_week[self.weekday] 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 @classproperty
def period_min(cls) -> int: def period_min(cls) -> int:
return cls.objects.aggregate(period__min=Coalesce(Min("period"), 1)).get("period__min") return cls.objects.aggregate(period__min=Coalesce(Min("period"), 1)).get("period__min")
......
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
CONSTANCE_CONFIG = { 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 = { 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 { ...@@ -108,3 +108,7 @@ table.substitutions td, table.substitutions th {
margin: 2px; margin: 2px;
letter-spacing: 0.3pt; 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 @@ ...@@ -26,22 +26,7 @@
<div class="row no-print"> <div class="row no-print">
<div class="col s12 m6 l8"> <div class="col s12 m6 l8">
{% if header_info.is_box_needed %} {% include "chronos/partials/headerbox.html" %}
<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/hintsinsub.html" %}#} {# {% include "chronos/hintsinsub.html" %}#}
</div> </div>
......
...@@ -18,20 +18,7 @@ ...@@ -18,20 +18,7 @@
{# {% include "timetable/hintsinsubprint.html" %}#} {# {% include "timetable/hintsinsubprint.html" %}#}
{# <div style="margin-bottom: 20px">#} {% include "chronos/partials/headerbox.html" with affected_teachers=c.affected_teachers affected_groups=c.affected_groups print=1 %}
{# {% 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>#}
<table class="substitutions"> <table class="substitutions">
<thead> <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 ...@@ -19,7 +19,6 @@ from .forms import LessonSubstitutionForm
from .models import LessonPeriod, LessonSubstitution, TimePeriod, Room from .models import LessonPeriod, LessonSubstitution, TimePeriod, Room
from .tables import LessonsTable from .tables import LessonsTable
from .util.js import date_unix 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 .util.date import CalendarWeek, get_weeks_for_year
from aleksis.core.util.core_helpers import has_person from aleksis.core.util.core_helpers import has_person
...@@ -56,9 +55,9 @@ def my_timetable( ...@@ -56,9 +55,9 @@ def my_timetable(
if day: if day:
wanted_day = timezone.datetime(year=year, month=month, day=day).date() 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: 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): if has_person(request.user):
person = request.user.person person = request.user.person
...@@ -98,7 +97,7 @@ def my_timetable( ...@@ -98,7 +97,7 @@ def my_timetable(
context["periods"] = TimePeriod.get_times_dict() context["periods"] = TimePeriod.get_times_dict()
context["smart"] = True 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" wanted_day, "my_timetable_by_date"
) )
...@@ -209,9 +208,9 @@ def lessons_day( ...@@ -209,9 +208,9 @@ def lessons_day(
if day: if day:
wanted_day = timezone.datetime(year=year, month=month, day=day).date() 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: 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 # Get lessons
lesson_periods = LessonPeriod.objects.on_day(wanted_day) lesson_periods = LessonPeriod.objects.on_day(wanted_day)
...@@ -229,7 +228,7 @@ def lessons_day( ...@@ -229,7 +228,7 @@ def lessons_day(
"dest": reverse("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" wanted_day, "lessons_day_by_date"
) )
...@@ -305,9 +304,9 @@ def substitutions( ...@@ -305,9 +304,9 @@ def substitutions(
if day: if day:
wanted_day = timezone.datetime(year=year, month=month, day=day).date() 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: 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_number = config.CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER
day_contexts = {} day_contexts = {}
...@@ -316,14 +315,17 @@ def substitutions( ...@@ -316,14 +315,17 @@ def substitutions(
next_day = wanted_day next_day = wanted_day
for i in range(day_number): for i in range(day_number):
day_contexts[next_day] = {"day": next_day} 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: else:
day_contexts = {wanted_day: {"day": wanted_day}} day_contexts = {wanted_day: {"day": wanted_day}}
for day in day_contexts: for day in day_contexts:
day_contexts[day]["substitutions"] = LessonSubstitution.objects.on_day( subs = LessonSubstitution.objects.on_day(day).order_by("lesson_period__lesson__groups", "lesson_period__period")
day day_contexts[day]["substitutions"] = subs
).order_by("lesson_period__lesson__groups", "lesson_period__period")
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: if not is_print:
context = day_contexts[wanted_day] context = day_contexts[wanted_day]
...@@ -332,7 +334,7 @@ def substitutions( ...@@ -332,7 +334,7 @@ def substitutions(
"dest": reverse("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" 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