diff --git a/aleksis/apps/chronos/fallback_view.py b/aleksis/apps/chronos/fallback_view.py deleted file mode 100644 index 50103287eeeaca9b276334e10da09da74f31b2bf..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/fallback_view.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.shortcuts import render - - -@login_required -def fallback(request, *args, **kwargs): - return render(request, "timetable/fallback.html") diff --git a/aleksis/apps/chronos/forms.py b/aleksis/apps/chronos/forms.py index 871ec7dc0d9fbaf37adcfd48d488b3a5c93f7fd1..d519ba6c303717976801a3326ffca55a9ca670fe 100644 --- a/aleksis/apps/chronos/forms.py +++ b/aleksis/apps/chronos/forms.py @@ -1,37 +1,7 @@ from django import forms -from django.db.models import Count -from django.utils.translation import ugettext_lazy as _ +from django_select2.forms import ModelSelect2MultipleWidget -from django_select2.forms import ModelSelect2MultipleWidget, Select2Widget - -from aleksis.core.models import Group, Person - -from .models import LessonPeriod, LessonSubstitution, Room, Subject - - -class SelectForm(forms.Form): - group = forms.ModelChoiceField( - queryset=Group.objects.annotate(lessons_count=Count("lessons")).filter(lessons_count__gt=0), - label=_("Group"), - required=False, - widget=Select2Widget, - ) - teacher = forms.ModelChoiceField( - queryset=Person.objects.annotate(lessons_count=Count("lessons_as_teacher")).filter( - lessons_count__gt=0 - ), - label=_("Teacher"), - required=False, - widget=Select2Widget, - ) - room = forms.ModelChoiceField( - queryset=Room.objects.annotate(lessons_count=Count("lesson_periods")).filter( - lessons_count__gt=0 - ), - label=_("Room"), - required=False, - widget=Select2Widget, - ) +from .models import LessonSubstitution class LessonSubstitutionForm(forms.ModelForm): diff --git a/aleksis/apps/chronos/m2l.py b/aleksis/apps/chronos/m2l.py deleted file mode 100644 index 754578068a463b132873a9e11ea4bf3ab2963602..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/m2l.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import subprocess - -from debug.models import register_log_with_filename -from aleksis.settings import BASE_DIR - - -# from .models import register_log_with_filename - - -def convert_markdown_2_latex(s): - try: - # Write markdown file - md_file = open(os.path.join(BASE_DIR, "latex", "m2l.md"), "w", encoding="utf8") - md_file.write(s) - md_file.close() - - # Execute pandoc to convert markdown to latex - bash_command = "pandoc --log={} --from markdown --to latex --output {} {}".format( - os.path.join(BASE_DIR, "latex", "m2l.log"), - os.path.join(BASE_DIR, "latex", "m2l.tex"), - os.path.join(BASE_DIR, "latex", "m2l.md")) - process = subprocess.Popen(bash_command.split(), stdout=subprocess.PIPE) - output = process.communicate()[0] - del output - - # Register log file in debugging tool - register_log_with_filename("m2l", "m2l", "m2l.log", process.returncode) - - # Read converted latex from file - tex_file = open(os.path.join(BASE_DIR, "latex", "m2l.tex"), "r", encoding="utf8") - r = tex_file.read() - tex_file.close() - - # Replace some things - r = r.replace("\subparagraph", "\subsubsection") - r = r.replace("\paragraph", "\subsubsection") - r = r.replace("section", "section*") - - # Return latex - return r - except Exception as e: - # Print error - print("[MD TO LATEX]", e) - return "" diff --git a/aleksis/apps/chronos/menus.py b/aleksis/apps/chronos/menus.py index 0b92d8fbb8c620d21b9e95a5c23e9b3ffcd25f66..b82f3556d8f0279b214a0384889bc95a6220007b 100644 --- a/aleksis/apps/chronos/menus.py +++ b/aleksis/apps/chronos/menus.py @@ -5,6 +5,7 @@ MENUS = { { "name": _("Timetables"), "url": "#", + "icon": "school", "root": True, "validators": [ "menu_generator.validators.is_authenticated", @@ -12,18 +13,27 @@ MENUS = { ], "submenu": [ { - "name": _("Timetable"), - "url": "timetable", + "name": _("My timetable"), + "url": "my_timetable", + "icon": "person", + "validators": ["menu_generator.validators.is_authenticated"], + }, + { + "name": _("All timetables"), + "url": "all_timetables", + "icon": "grid_on", "validators": ["menu_generator.validators.is_authenticated"], }, { "name": _("Daily lessons"), "url": "lessons_day", + "icon": "calendar_today", "validators": ["menu_generator.validators.is_authenticated"], }, { "name": _("Substitutions"), "url": "substitutions", + "icon": "update", "validators": ["menu_generator.validators.is_authenticated"], }, ], diff --git a/aleksis/apps/chronos/migrations/0001_initial.py b/aleksis/apps/chronos/migrations/0001_initial.py index 1ef08a22733fa8e67c4ea449e3c3bf32d502a9f6..28eab760977e1f3290581cf1930a09a1c2ad7fc5 100644 --- a/aleksis/apps/chronos/migrations/0001_initial.py +++ b/aleksis/apps/chronos/migrations/0001_initial.py @@ -4,7 +4,7 @@ import django.core.validators import django.db.models.deletion from django.db import migrations, models -import aleksis.apps.chronos.util +import aleksis.apps.chronos.util.weeks import aleksis.core.util.core_helpers @@ -229,7 +229,7 @@ class Migration(migrations.Migration): ( "week", models.IntegerField( - default=aleksis.apps.chronos.util.CalendarWeek.current_week, + default=aleksis.apps.chronos.util.weeks.CalendarWeek.current_week, verbose_name="Week", ), ), diff --git a/aleksis/apps/chronos/migrations/0005_remove_school_related.py b/aleksis/apps/chronos/migrations/0005_remove_school_related.py new file mode 100644 index 0000000000000000000000000000000000000000..ff4239caccbce749d651069a70f3fa7c3281e78a --- /dev/null +++ b/aleksis/apps/chronos/migrations/0005_remove_school_related.py @@ -0,0 +1,74 @@ +# Generated by Django 3.0.2 on 2020-01-10 16:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0007_create_admin_user'), + ('chronos', '0004_room_name_not_unique'), + ] + + operations = [ + migrations.RemoveField( + model_name='lesson', + name='school', + ), + migrations.RemoveField( + model_name='lessonperiod', + name='school', + ), + migrations.AlterField( + model_name='lesson', + name='teachers', + field=models.ManyToManyField(related_name='lessons_as_teacher', to='core.Person'), + ), + migrations.AlterField( + model_name='room', + name='short_name', + field=models.CharField(max_length=10, unique=True, verbose_name='Short name, e.g. room number'), + ), + migrations.AlterField( + model_name='subject', + name='abbrev', + field=models.CharField(max_length=10, unique=True, verbose_name='Abbreviation of subject in timetable'), + ), + migrations.AlterField( + model_name='subject', + name='name', + field=models.CharField(max_length=30, unique=True, verbose_name='Long name of subject'), + ), + migrations.AlterUniqueTogether( + name='lessonsubstitution', + unique_together={('lesson_period', 'week')}, + ), + migrations.AlterUniqueTogether( + name='room', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='subject', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='timeperiod', + unique_together={('weekday', 'period')}, + ), + migrations.RemoveField( + model_name='lessonsubstitution', + name='school', + ), + migrations.RemoveField( + model_name='room', + name='school', + ), + migrations.RemoveField( + model_name='subject', + name='school', + ), + migrations.RemoveField( + model_name='timeperiod', + name='school', + ), + ] diff --git a/aleksis/apps/chronos/migrations/0006_extended_data.py b/aleksis/apps/chronos/migrations/0006_extended_data.py new file mode 100644 index 0000000000000000000000000000000000000000..71785e3f7b61e8445ce2a5425952a13be3227552 --- /dev/null +++ b/aleksis/apps/chronos/migrations/0006_extended_data.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.2 on 2020-01-19 13:15 + +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('chronos', '0005_remove_school_related'), + ] + + operations = [ + migrations.AddField( + model_name='lessonperiod', + name='extended_data', + field=django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False), + ), + ] diff --git a/aleksis/apps/chronos/model_extensions.py b/aleksis/apps/chronos/model_extensions.py index 1d93db9aa30cb4436ede00bc67eadc962bdab59f..8f16b828ae522f64daa3df7371871c8afcc4a0c3 100644 --- a/aleksis/apps/chronos/model_extensions.py +++ b/aleksis/apps/chronos/model_extensions.py @@ -3,6 +3,13 @@ from aleksis.core.models import Person from .models import Lesson, LessonPeriod +@Person.property +def is_teacher(self): + """ Check if the user has lessons as a teacher """ + + return self.lesson_periods_as_teacher.exists() + + @Person.property def lessons_as_participant(self): """ Return a `QuerySet` containing all `Lesson`s this person diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py index c80de907ef94c21876fadef948001be368f273b0..293b62ba8f886a0689d79121421de7e346bb9473 100644 --- a/aleksis/apps/chronos/models.py +++ b/aleksis/apps/chronos/models.py @@ -6,14 +6,16 @@ from typing import Dict, Optional, Tuple, Union from django.core import validators from django.core.exceptions import ValidationError from django.db import models -from django.db.models import Q +from django.db.models import F, Q from django.http.request import QueryDict from django.utils.translation import ugettext_lazy as _ +from calendarweek.django import CalendarWeek, i18n_day_names_lazy, i18n_day_abbrs_lazy + from aleksis.core.mixins import ExtensibleModel from aleksis.core.models import Group, Person -from .util import CalendarWeek, week_weekday_from_date +from aleksis.apps.chronos.util.weeks import week_weekday_from_date class LessonPeriodManager(models.Manager): @@ -30,20 +32,51 @@ class LessonPeriodManager(models.Manager): ) -class LessonPeriodQuerySet(models.QuerySet): +class LessonSubstitutionManager(models.Manager): + """ Manager adding specific methods to lesson substitutions. """ + + def get_queryset(self): + """ Ensures all related lesson data is loaded as well. """ + + return ( + super() + .get_queryset() + .select_related( + "lesson_period", + "lesson_period__lesson", + "subject", + "lesson_period__period", + "room", + ) + .prefetch_related("lesson_period__lesson__groups", "teachers") + ) + + +class LessonDataQuerySet(models.QuerySet): """ Overrides default QuerySet to add specific methods for lesson data. """ + # Overridden in the subclasses. Swaps the paths to the base lesson period + # and to any substitutions depending on whether the query is run on a + # lesson period or a substitution + _period_path = None + _subst_path = None + def within_dates(self, start: date, end: date): """ Filter for all lessons within a date range. """ - return self.filter(lesson__date_start__lte=start, lesson__date_end__gte=end) + return self.filter( + **{ + self._period_path + "lesson__date_start__lte": start, + self._period_path + "lesson__date_end__gte": end, + } + ) def in_week(self, wanted_week: CalendarWeek): """ Filter for all lessons within a calendar week. """ return self.within_dates( - wanted_week[0] + timedelta(days=1) * (models.F("period__weekday") - 1), - wanted_week[0] + timedelta(days=1) * (models.F("period__weekday") - 1), + wanted_week[0] + timedelta(days=1) * (F(self._period_path + "period__weekday") - 1), + wanted_week[0] + timedelta(days=1) * (F(self._period_path + "period__weekday") - 1), ).annotate_week(wanted_week) def on_day(self, day: date): @@ -51,7 +84,11 @@ class LessonPeriodQuerySet(models.QuerySet): week, weekday = week_weekday_from_date(day) - return self.within_dates(day, day).filter(period__weekday=weekday).annotate_week(week) + return ( + self.within_dates(day, day) + .filter(**{self._period_path + "period__weekday": weekday}) + .annotate_week(week) + ) def at_time(self, when: Optional[datetime] = None): """ Filter for the lessons taking place at a certain point in time. """ @@ -60,38 +97,45 @@ class LessonPeriodQuerySet(models.QuerySet): week, weekday = week_weekday_from_date(now.date()) return self.filter( - lesson__date_start__lte=now.date(), - lesson__date_end__gte=now.date(), - period__weekday=now.isoweekday(), - period__time_start__lte=now.time(), - period__time_end__gte=now.time(), + **{ + self._period_path + "lesson__date_start__lte": now.date(), + self._period_path + "lesson__date_end__gte": now.date(), + self._period_path + "period__weekday": now.weekday(), + self._period_path + "period__time_start__lte": now.time(), + self._period_path + "period__time_end__gte": now.time(), + } ).annotate_week(week) def filter_participant(self, person: Union[Person, int]): """ Filter for all lessons a participant (student) attends. """ return self.filter( - Q(lesson__groups__members=person) | Q(lesson__groups__parent_groups__members=person) + Q(**{self._period_path + "lesson__groups__members": person}) + | Q(**{self._period_path + "lesson__groups__parent_groups__members": person}) ) def filter_group(self, group: Union[Group, int]): """ Filter for all lessons a group (class) regularly attends. """ - return self.filter(Q(lesson__groups=group) | Q(lesson__groups__parent_groups=group)) + return self.filter( + Q(**{self._period_path + "lesson__groups": group}) + | Q(**{self._period_path + "lesson__groups__parent_groups": group}) + ) def filter_teacher(self, teacher: Union[Person, int]): """ Filter for all lessons given by a certain teacher. """ return self.filter( - Q(substitutions__teachers=teacher, substitutions__week=models.F("_week")) - | Q(lesson__teachers=teacher) + Q(**{self._subst_path + "teachers": teacher, self._subst_path + "week": F("_week"),}) + | Q(**{self._period_path + "lesson__teachers": teacher}) ) def filter_room(self, room: Union[Room, int]): """ Filter for all lessons taking part in a certain room. """ return self.filter( - Q(substitutions__room=room, substitutions__week=models.F("_week")) | Q(room=room) + Q(**{self._subst_path + "room": room, self._subst_path + "week": F("_week"),}) + | Q(**{self._period_path + "room": room}) ) def annotate_week(self, week: Union[CalendarWeek, int]): @@ -104,6 +148,11 @@ class LessonPeriodQuerySet(models.QuerySet): return self.annotate(_week=models.Value(week_num, models.IntegerField())) + +class LessonPeriodQuerySet(LessonDataQuerySet): + _period_path = "" + _subst_path = "substitutions__" + def next(self, reference: LessonPeriod, offset: Optional[int] = 1) -> LessonPeriod: """ Get another lesson in an ordered set of lessons. @@ -123,7 +172,7 @@ class LessonPeriodQuerySet(models.QuerySet): return self.annotate_week(week).all()[next_index] - def filter_from_query(self, query_data: QueryDict): + def filter_from_query(self, query_data: QueryDict) -> models.QuerySet: """ Apply all filters from a GET or POST query. This method expects a QueryDict, like the GET or POST attribute of a Request @@ -139,17 +188,25 @@ class LessonPeriodQuerySet(models.QuerySet): if query_data.get("room", None): return self.filter_room(int(query_data["room"])) + def filter_from_type(self, type_: str, pk: int) -> Optional[models.QuerySet]: + if type_ == "group": + return self.filter_group(pk) + elif type_ == "teacher": + return self.filter_teacher(pk) + elif type_ == "room": + return self.filter_room(pk) + else: + return None + + +class LessonSubstitutionQuerySet(LessonDataQuerySet): + _period_path = "lesson_period__" + _subst_path = "" + class TimePeriod(models.Model): - WEEKDAY_CHOICES = [ - (0, _("Sunday")), - (1, _("Monday")), - (2, _("Tuesday")), - (3, _("Wednesday")), - (4, _("Thursday")), - (5, _("Friday")), - (6, _("Saturday")), - ] + WEEKDAY_CHOICES = list(enumerate(i18n_day_names_lazy())) + WEEKDAY_CHOICES_SHORT = list(enumerate(i18n_day_abbrs_lazy())) weekday = models.PositiveSmallIntegerField(verbose_name=_("Week day"), choices=WEEKDAY_CHOICES) period = models.PositiveSmallIntegerField(verbose_name=_("Number of period")) @@ -185,7 +242,7 @@ class TimePeriod(models.Model): wanted_week = CalendarWeek(year=year, week=week_number) - return wanted_week[self.weekday - 1] + return wanted_week[self.weekday] class Meta: unique_together = [["weekday", "period"]] @@ -262,6 +319,8 @@ class Lesson(models.Model): class LessonSubstitution(models.Model): + objects = LessonSubstitutionManager.from_queryset(LessonSubstitutionQuerySet)() + week = models.IntegerField(verbose_name=_("Week"), default=CalendarWeek.current_week) lesson_period = models.ForeignKey("LessonPeriod", models.CASCADE, "substitutions") @@ -285,6 +344,14 @@ class LessonSubstitution(models.Model): if self.subject and self.cancelled: raise ValidationError(_("Lessons can only be either substituted or cancelled.")) + @property + def type_(self): + # TODO: Add cases events and supervisions + if self.cancelled: + return "cancellation" + else: + return "substitution" + class Meta: unique_together = [["lesson_period", "week"]] ordering = [ @@ -301,7 +368,7 @@ class LessonSubstitution(models.Model): ] -class LessonPeriod(models.Model, ExtensibleModel): +class LessonPeriod(ExtensibleModel): objects = LessonPeriodManager.from_queryset(LessonPeriodQuerySet)() lesson = models.ForeignKey("Lesson", models.CASCADE, related_name="lesson_periods") diff --git a/aleksis/apps/chronos/pdf.py b/aleksis/apps/chronos/pdf.py deleted file mode 100644 index 16814fcf4adf09edfde5358a46e5968195f5def3..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/pdf.py +++ /dev/null @@ -1,52 +0,0 @@ -import os -import subprocess - -from django.template.loader import render_to_string - -from aleksis.settings import BASE_DIR -from debug.models import register_log_with_filename - -LOGO_FILENAME = os.path.join(BASE_DIR, "static", "common", "logo.png") - - -def generate_pdf(tex, filename): - """Generate a PDF by LaTeX code""" - - # Write LaTeX file - tex_file = open(os.path.join(BASE_DIR, "latex", filename + ".tex"), "w", encoding="utf8") - tex_file.write(tex) - tex_file.close() - - # Execute pdflatex to generate the PDF - bash_command = "pdflatex -halt-on-error -output-directory {} {}.tex".format(os.path.join(BASE_DIR, "latex"), - os.path.join(BASE_DIR, "latex", - filename)) - run_args = list(bash_command.split()) - - # Execute two times to get number of last page - subprocess.run(run_args, stdout=None) - process = subprocess.run(run_args, stdout=None) - - # Register log file in debugging tool - register_log_with_filename("latex_{}".format(filename), "latex", "{}.log".format(filename), process.returncode) - -def generate_class_tex_header(): - """Generate LaTeX for a PDF by a substitution table""" - - context = { - "LOGO_FILENAME": LOGO_FILENAME, - } - return render_to_string("timetable/latex/header.tex", context) - - -def generate_class_tex_body(subs, date, header_info, hints=None): - """Generate LaTeX for a PDF by a substitution table""" - - context = { - "subs": subs, - "date": date, - "header_info": header_info, - "LOGO_FILENAME": LOGO_FILENAME, - "hints": hints - } - return render_to_string("timetable/latex/substitutions.tex", context) diff --git a/aleksis/apps/chronos/settings.py b/aleksis/apps/chronos/settings.py deleted file mode 100644 index df9248e2487d7a618c0b16c7d24cdfe30477f0b9..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/settings.py +++ /dev/null @@ -1,23 +0,0 @@ -import dbsettings -from django import forms - -from untisconnect.api_helper import get_terms, get_school_years - -choices_school_years = [] -school_years = get_school_years() -for year in school_years: - choices_school_years.append((year.id, year.name)) - -choices_terms = [] -terms = get_terms() -for term in terms: - choices_terms.append((term.id, "{}, #{}: {}".format(term.school_year_id, term.id, term.name))) - - -class UNTISSettings(dbsettings.Group): - school_year = dbsettings.PositiveIntegerValue("Schuljahr", widget=forms.Select, choices=choices_school_years) - term = dbsettings.IntegerValue("Periode", widget=forms.Select, choices=choices_terms, - help_text="Bitte wähle oberhalb auch das zur Periode passende Schuljahr aus.") - - -untis_settings = UNTISSettings("UNTIS") diff --git a/aleksis/apps/chronos/static/css/chronos/timetable.css b/aleksis/apps/chronos/static/css/chronos/timetable.css index eb0ed9a984d776e326a9bbe56ae09d6b279ee248..c10a7c1942a4e7585d3700f97bc73046ca1b9df1 100644 --- a/aleksis/apps/chronos/static/css/chronos/timetable.css +++ b/aleksis/apps/chronos/static/css/chronos/timetable.css @@ -1,13 +1,98 @@ -.chronos-lesson { - height: 6em; +/*+++++++++++*/ +/* Timetable */ +/*+++++++++++*/ +.smart-plan-badge { + margin: 5px 20px 5px 0; } -ul#timetable_select_form li { - dispaly: inline; +li.active > a > .sidenav-badge { + background-color: whitesmoke !important; + color: #DA3D56 !important; +} + +.timetable-plan .row, .timetable-plan .col { + display: flex; + padding: 0 .25rem; +} + +.timetable-plan .row { + margin-bottom: .25rem; +} + +.lesson-card, .timetable-title-card { + margin: 0; + display: flex; + flex-grow: 1; + min-height: 65px; +} + +.lesson-card .card-content { + padding: 0; + text-align: center; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; +} + +.lesson-card .card-content div { + padding: 3px; + flex: auto; + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.timetable-title-card .card-content { + padding: 10px; + text-align: center; + width: 100%; +} + +.timetable-mobile-title-card { + margin-top: 50px; + margin-bottom: .60rem; +} + +.timetable-mobile-title-card:first-child { + margin-top: -10px; + margin-bottom: .60rem; +} + +.timetable-mobile-title-card .card-content { + padding: 10px; + text-align: center; + width: 100%; +} + +.timetable-mobile-title-card .card-content .card-title { + font-weight: bold; +} + +table.substitutions td, table.substitutions th { + padding: 10px 5px; +} + +.lesson-with-sub { + border: 3px solid red; + border-radius: 3px; +} + +.lesson-with-sub .badge { + margin: 0; +} + +.lesson-with-event { + border: 3px solid #9c27b0; + border-radius: 3px; +} +.lesson-card a, .substitutions a { + color: inherit; } -.chronos-lesson-cancelled { - background-color: inherit !important; - text-decoration: line-through; +.btn-timetable-quicklaunch { + margin: 1%; + width: 30%; } diff --git a/aleksis/apps/chronos/static/js/chronos/date_select.js b/aleksis/apps/chronos/static/js/chronos/date_select.js new file mode 100644 index 0000000000000000000000000000000000000000..b32309b3e3f4307463dd0b947f94af6cde0356a1 --- /dev/null +++ b/aleksis/apps/chronos/static/js/chronos/date_select.js @@ -0,0 +1,25 @@ +var data = getJSONScript("datepicker_data"); +var activeDate = new Date(data.date); + +function updateDatepicker() { + $("#date").val(formatDate(activeDate)); +} + +function loadNew() { + window.location.href = data.dest + formatDateForDjango(activeDate); +} + +function onDateChanged() { + var str = $("#date").val(); + var split = str.split("."); + activeDate = new Date(split[2], split[1] - 1, split[0]); + updateDatepicker(); + loadNew(); +} + + +$(document).ready(function () { + $("#date").change(onDateChanged); + + updateDatepicker(); +}); diff --git a/aleksis/apps/chronos/static/js/chronos/week_select.js b/aleksis/apps/chronos/static/js/chronos/week_select.js new file mode 100644 index 0000000000000000000000000000000000000000..3d683a886a909d5996c8038267c177e81c545797 --- /dev/null +++ b/aleksis/apps/chronos/static/js/chronos/week_select.js @@ -0,0 +1,21 @@ +var data = getJSONScript("week_select"); + +function goToCalendarWeek(cw, year) { + window.location.href = data.dest + year + "/" + cw; +} + +function onCalendarWeekChanged(where) { + goToCalendarWeek($(where).val(), data.year); +} + +$(document).ready(function () { + $("#calendar-week-1").change(function () { + onCalendarWeekChanged("#calendar-week-1"); + }); + $("#calendar-week-2").change(function () { + onCalendarWeekChanged("#calendar-week-2"); + }); + $("#calendar-week-3").change(function () { + onCalendarWeekChanged("#calendar-week-3"); + }); +}); diff --git a/aleksis/apps/chronos/tables.py b/aleksis/apps/chronos/tables.py index 688db9ccb0c0a8ba3ef296e3ef9580438a248b27..edc64154a65aa01b15b002c8a3f1ce07d9c154e3 100644 --- a/aleksis/apps/chronos/tables.py +++ b/aleksis/apps/chronos/tables.py @@ -15,16 +15,16 @@ def _css_class_from_lesson_state( ) -> str: if record.get_substitution(record._week): if record.get_substitution(record._week).cancelled: - return "table-danger" + return "success" else: - return "table-warning" + return "warning" else: return "" class LessonsTable(tables.Table): class Meta: - attrs = {"class": "table table-striped table-bordered table-hover table-responsive-xl"} + attrs = {"class": "highlight"} row_attrs = {"class": _css_class_from_lesson_state} period__period = tables.Column(accessor="period__period") @@ -33,21 +33,6 @@ class LessonsTable(tables.Table): lesson__subject = tables.Column(accessor="lesson__subject") room = tables.Column(accessor="room") edit_substitution = tables.LinkColumn( - "edit_substitution", args=[A("id"), A("_week")], text=_("Substitution") + "edit_substitution", args=[A("id"), A("_week")], text=_("Substitution"), + attrs={"a": {"class": "btn-flat waves-effect waves-orange"}}, verbose_name=_("Manage substitution") ) - - -class SubstitutionsTable(tables.Table): - class Meta: - attrs = {"class": "table table-striped table-bordered table-hover table-responsive-xl"} - - lesson_period = tables.Column(verbose_name=_("Lesson")) - lesson__groups = tables.Column( - accessor="lesson_period__lesson__group_names", verbose_name=_("Groups") - ) - lesson__teachers = tables.Column( - accessor="lesson_period__get_teacher_names", verbose_name=_("Teachers") - ) - lesson__subject = tables.Column(accessor="subject") - room = tables.Column(accessor="room") - cancelled = tables.BooleanColumn(accessor="cancelled", verbose_name=_("Cancelled")) diff --git a/aleksis/apps/chronos/templates/chronos/all.html b/aleksis/apps/chronos/templates/chronos/all.html new file mode 100644 index 0000000000000000000000000000000000000000..bedf5ca129d781ff2f13e198714871bf2c4b7b1c --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/all.html @@ -0,0 +1,50 @@ +{# -*- engine:django -*- #} + +{% extends 'core/base.html' %} + +{% load i18n static %} + +{% block extra_head %} + <link rel="stylesheet" href="{% static 'css/chronos/timetable.css' %}"> +{% endblock %} + +{% block browser_title %}{% blocktrans %}All timetables{% endblocktrans %}{% endblock %} +{% block page_title %}{% trans "All timetables" %}{% endblock %} + +{% block content %} + <div class="row"> + <div class="col s12 m4"> + <h5>{% trans "Teachers" %}</h5> + + {% for teacher in teachers %} + <a class="waves-effect waves-light btn btn-timetable-quicklaunch primary" + href="{% url 'timetable' 'teacher' teacher.pk %}"> + {{ teacher.short_name }} + </a> + {% endfor %} + </div> + + <div class="col s12 m4"> + <h5>{% trans "Groups" %}</h5> + + {% for class in classes %} + <a class="waves-effect waves-light btn btn-timetable-quicklaunch primary" + href="{% url 'timetable' 'group' class.pk %}"> + {{ class.short_name }} + </a> + {% endfor %} + </div> + + <div class="col s12 m4"> + <h5>{% trans "Rooms" %}</h5> + + {% for room in rooms %} + <a class="waves-effect waves-light btn btn-timetable-quicklaunch primary" + href="{% url 'timetable' 'room' room.pk %}"> + {{ room.short_name }} + </a> + {% endfor %} + </div> + </div> + +{% endblock %} diff --git a/aleksis/apps/chronos/templates/chronos/edit_substitution.html b/aleksis/apps/chronos/templates/chronos/edit_substitution.html index 213ad7a83f2725d6452a63875add14724c7fc1b1..b9164bfb081ab5abae5ee86d6ca50ead558cf01d 100644 --- a/aleksis/apps/chronos/templates/chronos/edit_substitution.html +++ b/aleksis/apps/chronos/templates/chronos/edit_substitution.html @@ -1,40 +1,24 @@ {# -*- engine:django -*- #} {% extends "core/base.html" %} -{% load bootstrap4 i18n %} - -{% block bootstrap4_extra_head %} - {{ block.super }} - {{ edit_substitution_form.media.css }} -{% endblock %} - -{% block bootstrap4_extra_script %} - {{ block.super }} - {{ edit_substitution_form.media.js }} -{% endblock %} - - -{% block bootstrap4_title %}{% blocktrans %}Edit substitution{% endblocktrans %} - {{ block.super }}{% endblock %} +{% load material_form i18n %} +{% block browser_title %}{% blocktrans %}Edit substitution.{% endblocktrans %}{% endblock %} {% block page_title %}{% blocktrans %}Edit substitution{% endblocktrans %}{% endblock %} {% block content %} - <div class="d-flex justify-content-between"> - <div class="btn-group" role="group" aria-label="Day actions"> - {% if substitution %} - {# FIXME Respect year as well #} - <a href="{% url 'delete_substitution' substitution.lesson_period.id substitution.week %}" class="btn btn-danger"> - <span class="mdi mdi-delete"></span> - </a> - {% endif %} - </div> - </div> - <form method="post"> {% csrf_token %} - {% bootstrap_form edit_substitution_form %} - <button type="submit" class="btn btn-dark"> - <span class="mdi mdi-content-save"></span> - </button> + + {% form form=edit_substitution_form %}{% endform %} + + {% include "core/save_button.html" %} + {% if substitution %} + {# FIXME Respect year as well #} + <a href="{% url 'delete_substitution' substitution.lesson_period.id substitution.week %}" + class="btn red waves-effect waves-light"> + <i class="material-icons left">delete</i> {% trans "Delete" %} + </a> + {% endif %} </form> {% endblock %} diff --git a/aleksis/apps/chronos/templates/timetable/hintform.html b/aleksis/apps/chronos/templates/chronos/hint_form.html similarity index 76% rename from aleksis/apps/chronos/templates/timetable/hintform.html rename to aleksis/apps/chronos/templates/chronos/hint_form.html index 60593e9ae08ea34294e78fc502e8d97fbc663aa7..b0e621311881fd384f02e98211859987223c273a 100644 --- a/aleksis/apps/chronos/templates/timetable/hintform.html +++ b/aleksis/apps/chronos/templates/chronos/hint_form.html @@ -1,15 +1,19 @@ -{% include 'partials/header.html' %} -{% load material_form %} -{% load martortags %} -{% load static %} -{% load widget_tweaks %} +{# -*- engine:django -*- #} -<main> +{% extends 'core/base.html' %} +{% load material_form martortags static widget_tweaks %} + + +{% block content %} <h4> {% if mode == "new" %} - Neuen Hinweis erstellen + {% blocktrans %} + Create hint + {% endblocktrans %} {% else %} - Hinweis bearbeiten + {% blocktrans %} + Edit hint + {% endblocktrans %} {% endif %} </h4> @@ -17,13 +21,12 @@ <div class="alert success"> <p> <i class="material-icons left">check_circle</i> - Der Hinweis wurde erfolgreich erstellt und veröffentlicht. Weiteren Hinweis anlegen oder zur Ãœbersicht - zurück?<br> + {% blocktrans %}The hint was successfully created and published. Do you want to create another hint or go back to overview?{% endblocktrans %}<br> <a class="btn waves-effect waves-light" href="{% url "timetable_hints" %}"><i - class="material-icons left">arrow_back</i> Zurück zur Ãœbersicht + class="material-icons left">arrow_back</i>{% blocktrans %}Back to overview{% endblocktrans %} </a> <a class="btn waves-effect waves-light green" href="#add-hint"><i class="material-icons left">add</i> - Weiteren Hinweis hinzufügen + {% blocktrans %}Create antoher hint{% endblocktrans %} </a> </p> </div> @@ -39,7 +42,7 @@ </div> </div> {% endif %} - <h5>Ãœber welchen Zeitraum soll der Hinweis angezeigt werden? + <h5>{% blocktrans %}When should the hint be displayed?{% blocktrans %} <span class="red-text">*</span> </h5> @@ -69,7 +72,7 @@ {{ form.to_date|add_class:"datepicker required" }} </div> </div> - <h5>Für wen soll der Hinweis angezeigt werden? + <h5>{% blocktrans %}For whom should the hint be displayed?{% endblocktrans %} <span class="red-text">*</span> </h5> @@ -83,9 +86,9 @@ {% endif %} {{ form.classes.label_tag }} {{ form.classes }} - <a href="#" id="select-all-classes">Alle auswählen</a> + <a href="#" id="select-all-classes">{% blocktrans %}Select all{% endblocktrans %}</a> · - <a href="#" id="deselect-all-classes">Alle abwählen</a> + <a href="#" id="deselect-all-classes">{% blocktrans %}Unselect all{% endblocktrans %}</a> <script type="text/javascript"> $("#select-all-classes").click(function () { $(".select-wrapper ul li:not(.selected)").click(); @@ -106,11 +109,11 @@ <p> <label> {{ form.teachers }} - <span>Für Lehrer anzeigen?</span> + <span>{% blocktrans %}Show for teachers?{% endblocktrans %}</span> </label> </p> - <h5>Hinweistext + <h5>{% blocktrans %}Hint text{% endblocktrans %} <span class="red-text">*</span> </h5> {% if form.text.errors %} @@ -125,8 +128,7 @@ <button type="submit" class="waves-effect waves-light btn green"> - <i class="material-icons left">save</i> Hinweis {% if mode == "new" %} erstellen und - veröffentlichen {% else %} aktualisieren {% endif %} + <i class="material-icons left">save</i>{% blocktrans %} {% if mode == "new" %} Create and publish {% else %} Update {% endif %}hint{% endblocktrans %} </button> </form> @@ -137,6 +139,4 @@ <script type="text/javascript" src="{% static 'plugins/js/highlight.min.js' %}"></script> <script type="text/javascript" src="{% static 'plugins/js/resizable.min.js' %}"></script> <script type="text/javascript" src="{% static 'martor/js/martor.min.js' %}"></script> -</main> - -{% include 'partials/footer.html' %} +{% endblock %} diff --git a/aleksis/apps/chronos/templates/timetable/hints.html b/aleksis/apps/chronos/templates/chronos/hints.html similarity index 77% rename from aleksis/apps/chronos/templates/timetable/hints.html rename to aleksis/apps/chronos/templates/chronos/hints.html index bd289497abc631395f82ef67daf6b34937794c23..3c26f0651276472ffc2af4b755e2fc49c657ed08 100644 --- a/aleksis/apps/chronos/templates/timetable/hints.html +++ b/aleksis/apps/chronos/templates/chronos/hints.html @@ -1,16 +1,18 @@ -{% include 'partials/header.html' %} +{# -*- engine:django -*- #} + +{% extends 'core/base.html' %} {% load material_form %} {% load martortags %} -<main> - <h4>Hinweismanagement</h4> +{% block content %} + <h4>{% blocktrans %}Hint management{% endblocktrans %}</h4> {% if msg %} <div class="alert success"> <p> <i class="material-icons left">check_circle</i> - Der Hinweis wurde erfolgreich {% if msg == "success_edit" %}gespeichert. {% else %} - gelöscht. {% endif %} + The hint was successfully {% if msg == "success_edit" %}saved. {% else %} + deleted. {% endif %} </p> </div> {% endif %} @@ -21,15 +23,15 @@ <div class="col s12 m4"> <a href="{% url 'timetable_add_hint' %}" class="waves-effect waves-light btn green"> <i class="material-icons left">add</i> - Neuen Hinweis erstellen + {% blocktrans %}Create hint{% endblocktrans %} </a> </div> <div class="col s12 m8 right-align"> <button type="submit" class="waves-effect waves-green btn-flat"> - <i class="material-icons left">refresh</i> Filter aktualisieren + <i class="material-icons left">refresh</i>{% blocktrans %}Refresh filter{% endblocktrans %} </button> <a class="waves-effect waves-red btn-flat " href="{% url "timetable_hints" %}"> - <i class="material-icons left">clear</i> Filter entfernen + <i class="material-icons left">clear</i>{% blocktrans }Clear filter{% endblocktrans %} </a> </div> </div> @@ -44,12 +46,12 @@ <div class="collapsible-header row no-margin"> <div class="col s10"> - <strong>{{ hint.from_date }} — {{ hint.to_date }}</strong> für + <strong>{{ hint.from_date }} — {{ hint.to_date }}</strong>{% blocktrans %}Hint for{% endblocktrans %} <strong> {{ hint.classes_formatted }} </strong> {% if hint.teachers %} - <span class="badge new green no-float no-margin">Lehrkräfte</span> + <span class="badge new green no-float no-margin">{% blocktrans %}Teachers{% endblocktrans %}</span> {% endif %} </div> <div class="col s2"> @@ -63,12 +65,12 @@ <a class="btn-flat waves-effect waves-green green-text" href="{% url "timetable_edit_hint" hint.id %}"> <i class="material-icons left">edit</i> - <span class="hide-on-small-only">Bearbeiten</span> + <span class="hide-on-small-only">{% blocktrans %}Edit{% endblocktrans %}</span> </a> <a class="btn-flat waves-effect waves-red red-text delete-button" href="{% url "timetable_delete_hint" hint.id %}"> <i class="material-icons left">delete</i> - <span class="hide-on-small-only">Löschen</span> + <span class="hide-on-small-only">{% blocktrans %}Delete{% endblocktrans %}</span> </a> </div> @@ -80,6 +82,4 @@ </li> {% endfor %} </ul> -</main> - -{% include 'partials/footer.html' %} +{% endblock %} diff --git a/aleksis/apps/chronos/templates/chronos/lessons_day.html b/aleksis/apps/chronos/templates/chronos/lessons_day.html index 1f025c3b39ace7cd2ca3f6bdecac611e33d37a84..46feace7d131e3cee51163e43bdbd548c40db120 100644 --- a/aleksis/apps/chronos/templates/chronos/lessons_day.html +++ b/aleksis/apps/chronos/templates/chronos/lessons_day.html @@ -1,11 +1,27 @@ {# -*- engine:django -*- #} -{% extends "core/turnable.html" %} -{% load bootstrap4 i18n %} +{% extends "core/base.html" %} +{% load i18n %} + + {% load render_table from django_tables2 %} -{% block bootstrap4_title %}{% blocktrans %}Lessons{% endblocktrans %} - {{ block.super }}{% endblock %} +{% block browser_title %}{% blocktrans %}Lessons{% endblocktrans %}{% endblock %} +{% block no_page_title %}{% endblock %} + +{% block content %} + <script type="text/javascript"> + var dest = Urls.lessonsDay(); + </script> + + <div class="row no-margin"> + <div class="col s12 m6 l8 no-padding"> + <h4>{% blocktrans %}Lessons{% endblocktrans %} {{ day|date:"l" }}, {{ day }}</h4> + </div> + <div class="col s12 m6 l4 no-padding"> + {% include "chronos/partials/datepicker.html" %} + </div> + </div> -{% block current_content %} {% render_table lessons_table %} {% endblock %} diff --git a/aleksis/apps/chronos/templates/chronos/my_timetable.html b/aleksis/apps/chronos/templates/chronos/my_timetable.html new file mode 100644 index 0000000000000000000000000000000000000000..90adf8fb4a2f0c04eeb605ae7ff6b271a6ab6141 --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/my_timetable.html @@ -0,0 +1,68 @@ +{# -*- engine:django -*- #} + +{% extends 'core/base.html' %} + +{% load i18n static %} + +{% block extra_head %} + <link rel="stylesheet" href="{% static 'css/chronos/timetable.css' %}"> +{% endblock %} + +{% block browser_title %}{% blocktrans %}My timetable{% endblocktrans %}{% endblock %} +{% block no_page_title %}{% endblock %} + +{% block content %} + <div class="row no-margin"> + <div class="col m12 s12 l6 xl4"> + <h4> + {% trans "My timetable" %} <i>{{ el }}</i> + <span class="badge new primary-color ">{% trans "SMART PLAN" %}</span> + </h4> + <a class="btn-flat waves-effect waves-light" href="{% url "timetable" super.type super.el.pk %}"> + {% trans "Show week timetable for" %} {{ super.el.short_name }} + </a> + </div> + </div> + + <div class="row nomargin"> + <div class="col m12 s12 l6 xl4"> + {# {% include "timetable/hintsinplan.html" %}#} + </div> + </div> + + <div class="row"> + <div class="timetable-plan col s12 m12 xl4"> + + {# Date #} + + <div class="row"> + <div class="col s12"> + <div class="card timetable-title-card"> + <div class="card-content"> + <span class="card-title"> + {% include "chronos/partials/datepicker.html" with display_date_only=1 %} +{# {% if holiday %}#} +{# <span class="badge new blue center-align holiday-badge">{{ holiday.0 }}</span>#} +{# {% endif %}#} + </span> + </div> + </div> + + </div> + </div> + {# Lessons #} + {% for period, lessons in lesson_periods.items %} + <div class="row"> + <div class="col s4"> + {% include "chronos/partials/period_time.html" with period=period periods=periods %} + </div> + <div class="col s8"> + {# A lesson #} + {% include "chronos/partials/lesson.html" with lessons=lessons %} + </div> + </div> + {% endfor %} + + </div> + </div> +{% endblock %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/datepicker.html b/aleksis/apps/chronos/templates/chronos/partials/datepicker.html new file mode 100644 index 0000000000000000000000000000000000000000..ede1fb06e37da69590468018bec6ca5384bd459f --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/datepicker.html @@ -0,0 +1,30 @@ +{% load static %} + +{% if not display_date_only %} + <script type="text/javascript" src="{% static "js/helper.js" %}"></script> + {{ datepicker|json_script:"datepicker_data" }} + <script type="text/javascript" src="{% static "js/chronos/date_select.js" %}"></script> +{% endif %} + +<div class="col s2 no-padding"> + <a class="waves-effect waves-teal btn-flat btn-flat-medium left" href="{{ url_prev }}"> + <i class="material-icons center">navigate_before</i> + </a> +</div> + +{% if display_date_only %} + <div class="col s8"> + <span class="card-title center-block" id="date"> + {{ day|date:"l" }}, {{ day }} + </span> + </div> +{% else %} + <div class="col s8 no-padding"> + <input type="text" class="datepicker center-align" id="date"> + </div> +{% endif %} +<div class="col s2 no-padding"> + <a class="waves-effect waves-teal btn-flat btn-flat-medium right" href="{{ url_next }}"> + <i class="material-icons center">navigate_next</i> + </a> +</div> diff --git a/aleksis/apps/chronos/templates/chronos/partials/groups.html b/aleksis/apps/chronos/templates/chronos/partials/groups.html new file mode 100644 index 0000000000000000000000000000000000000000..3ccfc00c4f6789fe4878880179e837408601c6ba --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/groups.html @@ -0,0 +1,5 @@ +{% for group in groups %} + <a href="{% url "timetable" "group" group.pk %}"> + {{ group.short_name }}{% if not forloop.last %},{% endif %} + </a> +{% endfor %} diff --git a/aleksis/apps/chronos/templates/timetable/hintsinsub.html b/aleksis/apps/chronos/templates/chronos/partials/hints/substitutions.html similarity index 100% rename from aleksis/apps/chronos/templates/timetable/hintsinsub.html rename to aleksis/apps/chronos/templates/chronos/partials/hints/substitutions.html diff --git a/aleksis/apps/chronos/templates/timetable/hintsinsubprint.html b/aleksis/apps/chronos/templates/chronos/partials/hints/substitutions_print.html similarity index 100% rename from aleksis/apps/chronos/templates/timetable/hintsinsubprint.html rename to aleksis/apps/chronos/templates/chronos/partials/hints/substitutions_print.html diff --git a/aleksis/apps/chronos/templates/timetable/hintsinplan.html b/aleksis/apps/chronos/templates/chronos/partials/hints/timetable.html similarity index 100% rename from aleksis/apps/chronos/templates/timetable/hintsinplan.html rename to aleksis/apps/chronos/templates/chronos/partials/hints/timetable.html diff --git a/aleksis/apps/chronos/templates/chronos/partials/lesson.html b/aleksis/apps/chronos/templates/chronos/partials/lesson.html new file mode 100644 index 0000000000000000000000000000000000000000..8dc4f9fb2f264cfe188241502d298cf3e095ccf3 --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/lesson.html @@ -0,0 +1,131 @@ +{% load i18n %} + +<div class="card lesson-card"> + <div class="card-content"> + + {# Every element of the lesson #} + {% for lesson_period in lessons %} + <div style=" + {# Display background color only if no badge exists and it is not the old room and there are no holidays #} + {% if not lesson_period.get_substitution.cancelled and not lesson_period.is_hol %} + {% if not lesson_period.room != lesson_period.get_room or type != 1 %} + {% if lesson_period.lesson.subject.colour_fg %} + color: {{ lesson_period.lesson.subject.colour_fg }}; + {% endif %} + {% if lesson_period.lesson.subject.colour_bg %} + background-color: {{ lesson_period.lesson.subject.colour_bg }}; + {% endif %} + {% endif %} + {% endif %} + " + {# Add CSS class for sub when it's a sub #} + class="{% if lesson_period.get_substitution and smart %}{% if lesson_period.substitution.table.is_event %}lesson-with-event{% else %}lesson-with-sub{% endif %}{% endif %}" + > + <p> + {% if lesson_period.is_hol and smart %} + {# Do nothing #} + {% elif lesson_period.get_substitution and smart %} + {% with sub=lesson_period.get_substitution %} + {# SUBSTITUTION #} + {% if type == "room" and lesson_period.room != lesson_period.get_room %} + {# When it's the old room, let it empty #} + + {% elif lesson_period.get_substitution.cancelled %} + {# When a badge (cancellation, etc.) exists, then display it with the teacher#} + + {# Class or room > Display teacher #} + {% if type == "group" or type == "room" and lesson_period.lesson.teachers.all %} + {% include "chronos/partials/teachers.html" with teachers=lesson_period.lesson.teachers.all %}<br/> + {% endif %} + + {# Badge #} + <span class="badge new green darken-2">{% trans "Cancelled" %}</span> + + {% else %} + {# Display sub #} + + {# Teacher or room > display classes #} + {% if type == "teacher" or type == "room" %} + {% include "chronos/partials/groups.html" with groups=lesson_period.lesson.groups.all %} + {% endif %} + + {# Display teacher with tooltip #} + {% include "chronos/partials/subs/teachers.html" %} + + {# Display subject #} + {% include "chronos/partials/subs/subject.html" %} + + {# Teacher or class > display room #} + {% if type == "teacher" or type == "group" %} + {% include "chronos/partials/subs/room.html" %} + {% endif %} + {% endif %} + + + {# When it isn't a room or the old plan, then display the extra text (e. g. work orders) AND NOT A EVENT#} + {% if not lesson_period.substitution.table.is_event %} + {% if not type == "room" or not lesson_period.is_old %} + <br> + <small> + <em>{{ lesson_period.substitution.table.text|default:"" }}</em> + </small> + {% endif %} + {% endif %} + {# Display the extra text for events #} + {% if lesson_period.substitution.table.is_event %} + {% if type == 0 and lesson_period.substitution.table.classes == "" and lesson_period.substitution.table.rooms|length == 0 and lesson_period.substitutions.table.teachers|length == 0 %} + <em>{{ lesson_period.substitution.table.text|default:"" }}</em> + {% elif type == 2 and lesson_period.substitution.table.teachers|length == 0 and lesson_period.substitution.table.rooms|length == 0 %} + <em>{{ lesson_period.substitution.table.text|default:"" }}</em> + {% elif type == 1 and lesson_period.substitution.table.teachers|length == 0 and lesson_period.substitution.table.classes == "" %} + <em>{{ lesson_period.substitution.table.text|default:"" }}</em> + {% else %} + <br> + <small> + <em>{{ lesson_period.substitution.table.text|default:"" }}</em> + </small> + {% endif %} + {% endif %} + {% endwith %} + + + {% else %} + {# Normal plan #} + + {# Teacher or room > Display classes #} + {% if type == "teacher" or type == "room" %} + {# {{ element_container.element.classes }}#} + {% if lesson_period.lesson.groups %} + {% include "chronos/partials/groups.html" with groups=lesson_period.lesson.groups.all %} + {% endif %} + {% endif %} + + {# Class or room > Display teacher #} + {% if type == "room" or type == "group" %} + {% include "chronos/partials/teachers.html" with teachers=lesson_period.lesson.teachers.all %} + {% endif %} + + {# Display subject #} + <strong> + <span data-position="bottom" class="tooltipped" + data-tooltip="{{ lesson_period.lesson.subject.name }}">{{ lesson_period.lesson.subject.abbrev }}</span> + </strong> + + {# Teacher or class > Display room #} + {% if type == "teacher" or type == "group" %} + {% if lesson_period.room %} + <span class="tooltipped" data-position="bottom" + data-tooltip="{{ lesson_period.room.name }}"> + <a href="{% url "timetable" "room" lesson_period.room.pk %}"> + {{ lesson_period.room.short_name }} + </a> + </span> + {% endif %} + {% endif %} + {% endif %} + </p> + </div> + + {% endfor %} + </div> +</div> diff --git a/aleksis/apps/chronos/templates/chronos/partials/period_time.html b/aleksis/apps/chronos/templates/chronos/partials/period_time.html new file mode 100644 index 0000000000000000000000000000000000000000..ebd76dab6393c83332e07c1b194248480846323f --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/period_time.html @@ -0,0 +1,20 @@ +{% load data_helpers %} + +<div class="card timetable-title-card"> + <div class="card-content"> + + {# Lesson number #} + <span class="card-title left"> + {{ period }}. + </span> + + {# Time dimension of lesson #} + <div class="right timetable-time grey-text text-darken-2"> + {% with period_obj=periods|get_dict:period %} + <span>{{ period_obj.0|time }}</span> + <br/> + <span>{{ period_obj.1|time }}</span> + {% endwith %} + </div> + </div> +</div> diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html new file mode 100644 index 0000000000000000000000000000000000000000..cf637b92303dfe31d1c19dc781809f1c3c10dac3 --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html @@ -0,0 +1,47 @@ +{% if not sub.is_event %} + {% if sub.sub.type == 3 %} + {# Supervisement #} + {{ sub.sub.corridor.name }} + {% elif sub.sub.type == 1 or sub.sub.type == 2 %} + {# Canceled lesson: no room #} + {% elif sub.room and sub.lesson_period.room %} + {# New and old room available #} + <span class="tooltipped" data-position="bottom" + data-tooltip="{{ sub.lesson_period.room.name }} → {{ sub.lesson_period.room.name }}"> + <a href="{% url "timetable" "room" sub.lesson_period.room.pk %}"> + <s>{{ sub.lesson_period.room.short_name }}</s> + </a> + → + <a href="{% url "timetable" "room" sub.room.pk %}"> + <strong>{{ sub.room.short_name }}</strong> + </a> + </span> + {% elif sub.room and not sub.lesson_period.room %} + {# Only new room available #} + <span class="tooltipped" data-position="bottom" + data-tooltip="{{ sub.room.name }}"> + <a href="{% url "timetable" "room" sub.room.pk %}"> + {{ sub.room.short_name }} + </a> + </span> + {% elif not sub.room and not sub.lesson_period.room %} + {# Nothing to view #} + {% else %} + {# Only old room available #} + <span class="tooltipped" data-position="bottom" + data-tooltip="{{ sub.lesson_period.room.name }}"> + <a href="{% url "timetable" "room" sub.lesson_period.room.pk %}"> + {{ sub.lesson_period.room.short_name }} + </a> + </span> + {% endif %} +{% else %} + {% for room in sub.rooms %} + <span class="tooltipped" data-position="bottom" + data-tooltip="{{ room.name }}"> + <a href="{% url "timetable_smart_plan" "room" room.id %}"> + <strong>{{ room.short_name }}{% if not forloop.last %},{% endif %}</strong> + </a> + </span> + {% endfor %} +{% endif %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html b/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html new file mode 100644 index 0000000000000000000000000000000000000000..6fe8eb5a02bb7a6afb75adf50012efa9d2712239 --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html @@ -0,0 +1,28 @@ +{% load i18n %} + +{% if not sub.sub.is_event %} + {% if sub.sub.type == 3 %} + <strong>{% trans "Supervision" %}</strong> + {% elif not sub.lesson_period.lesson.subject and not sub.subject %} + {% elif sub.sub.type == 1 or sub.sub.type == 2 %} + <span data-position="bottom" class="tooltipped" data-tooltip="{{ sub.lesson_period.lesson.subject.name }}"> + <s>{{ sub.lesson_period.lesson.subject.abbrev }}</s> + </span> + {% elif sub.subject and sub.lesson_period.lesson.subject %} + <span data-position="bottom" class="tooltipped" data-tooltip="{{ sub.lesson_period.lesson.subject.name }}"> + <s>{{ sub.lesson_period.lesson.subject.abbrev }}</s> + </span> + → + <span data-position="bottom" class="tooltipped" data-tooltip="{{ sub.subject.name }}"> + <strong>{{ sub.subject.abbrev }}</strong> + </span> + {% elif sub.subject and not sub.lesson_period.lesson.subject %} + <span data-position="bottom" class="tooltipped" data-tooltip="{{ sub.subject.name }}"> + <strong>{{ sub.subject.abbrev }}</strong> + </span> + {% else %} + <span data-position="bottom" class="tooltipped" data-tooltip="{{ sub.lesson_period.lesson.subject.name }}"> + <strong>{{ sub.lesson_period.lesson.subject.abbrev }}</strong> + </span> + {% endif %} +{% endif %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html b/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html new file mode 100644 index 0000000000000000000000000000000000000000..ec17d8b811cfad34c1cad90d2a3302aa5752ba53 --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html @@ -0,0 +1,19 @@ +{% if not sub.is_event %} + {% if sub.sub.type == 1 and sub.lesson_period.lesson.teachers.all %} + {% include "chronos/partials/teachers.html" with teachers=sub.lesson_period.lesson.teachers.all %} + {% elif sub.teachers.all and sub.lesson_period.lesson.teachers.all %} + <s> + {% include "chronos/partials/teachers.html" with teachers=sub.lesson_period.lesson.teachers.all %} + </s> + → + <strong> + {% include "chronos/partials/teachers.html" with teachers=sub.teachers.all %} + </strong> + {% elif sub.teachers.all and not sub.lesson_period.lesson.teachers.all %} + {% include "chronos/partials/teachers.html" with teachers=sub.teachers.all %} + {% elif sub.lesson_period.lesson.teachers.all %} + {% include "chronos/partials/teachers.html" with teachers=sub.lesson_period.lesson.teachers.all %} + {% endif %} +{% else %} + {% include "chronos/partials/teachers.html" with teachers=sub.teachers.all %} +{% endif %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/teachers.html b/aleksis/apps/chronos/templates/chronos/partials/teachers.html new file mode 100644 index 0000000000000000000000000000000000000000..d7ec97197f1309c651d0a8c07b0d1e387ecaade8 --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/teachers.html @@ -0,0 +1,8 @@ +{% for teacher in teachers %} + <span data-position="bottom" class="tooltipped" + data-tooltip="{{ teacher }}"> + <a href="{% url "timetable" "teacher" teacher.pk %}"> + {{ teacher.short_name }}{% if not forloop.last %},{% endif %} + </a> + </span> +{% endfor %} diff --git a/aleksis/apps/chronos/templates/chronos/substitutions.html b/aleksis/apps/chronos/templates/chronos/substitutions.html index cd18828b5366a38f5ae2da521824229f691af6a9..2d0c99044c421e22d9bea5a3ac66733ee471c7fa 100644 --- a/aleksis/apps/chronos/templates/chronos/substitutions.html +++ b/aleksis/apps/chronos/templates/chronos/substitutions.html @@ -1,11 +1,117 @@ {# -*- engine:django -*- #} -{% extends "core/turnable.html" %} -{% load bootstrap4 i18n %} -{% load render_table from django_tables2 %} +{% extends 'core/base.html' %} -{% block bootstrap4_title %}{% blocktrans %}Substitutions{% endblocktrans %} - {{ block.super }}{% endblock %} +{% load i18n static %} + +{% block extra_head %} + <link rel="stylesheet" href="{% static 'css/chronos/timetable.css' %}"> +{% endblock %} + +{% block browser_title %}{% blocktrans %}Substitutions{% endblocktrans %}{% endblock %} +{% block no_page_title %}{% endblock %} + +{% block content %} + <div class="row no-margin"> + <div class="col s10 m6 no-padding"> + <h4>{% trans "Substitutions" %}</h4> + </div> + <div class="col s2 m6 right align-right print-icon"> + {# <a class="waves-effect waves-teal btn-flat btn-flat-medium right"#} + {# href="#} + {# {% if debug %}#} + {# {% url "timetable_substitutions_pdf_date" date|date:"Y-m-d" %}#} + {# {% else %}#} + {# {% url "timetable_substitutions_pdf" %}#} + {# {% endif %}#} + {# ">#} + {# <i class="material-icons center">print</i>#} + {# </a>#} + </div> + </div> + + <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/hintsinsub.html" %}#} + </div> + <div class="col s12 m6 l4 no-padding"> + {% include "chronos/partials/datepicker.html" %} + </div> + </div> + + <h5 class="hide-on-small-and-down">{{ day|date:"l" }}, {{ day }}</h5> + + <table class="substitutions striped responsive-table"> + <thead> + <tr> + <th><i class="material-icons">people</i></th> + <th><i class="material-icons">access_time</i></th> + <th>{% trans "Teacher" %}</th> + <th>{% trans "Subject" %}</th> + <th>{% trans "Room" %}</th> + <th>{% trans "Notes" %}</th> + <th></th> + </tr> + </thead> + <tbody> + {% if not substitutions %} + <td colspan="7"> + <p class="flow-text center"> + {% blocktrans %}No substitutions available.{% endblocktrans %} + </p> + </td> + {% endif %} + {% for sub in substitutions %} + <tr class="{% if sub.type_ == "cancellation" %}green-text{% else %}black-text{% endif %}"> {# TODO: Extend support for blue and purple (supervisions and events) #} + <td> + {% include "chronos/partials/groups.html" with groups=sub.lesson_period.lesson.groups.all %} + </td> + <td> + <strong> + {{ sub.lesson_period.period.period }}. + </strong> + </td> + <td> + {% include "chronos/partials/subs/teachers.html" %} + </td> + <td> + {% include "chronos/partials/subs/subject.html" %} + </td> + <td> + {% include "chronos/partials/subs/room.html" %} + </td> + <td> + {% if sub.cancelled %} + {# TODO: Support other cases#} + <span class="badge new green hide-on-med-and-up">{% trans "Cancelled" %}</span> + {% endif %} +{# <em>{{ sub.text|default:"" }}</em>#} + </td> + <td class="hide-on-small-and-down"> + {% if sub.cancelled %} + <span class="badge new green darken-2">{% trans "Cancelled" %}</span> + {% endif %} + </td> + </tr> + {% endfor %} + </tbody> + </table> -{% block current_content %} - {% render_table substitutions_table %} {% endblock %} diff --git a/aleksis/apps/chronos/templates/timetable/substitutionprint.html b/aleksis/apps/chronos/templates/chronos/substitutions_print.html similarity index 84% rename from aleksis/apps/chronos/templates/timetable/substitutionprint.html rename to aleksis/apps/chronos/templates/chronos/substitutions_print.html index 39363d68a5b46e52b00d3099716362fb8aa9784e..9f30bd141cb846f71b88e5fb3cb6a9e766ecf271 100644 --- a/aleksis/apps/chronos/templates/timetable/substitutionprint.html +++ b/aleksis/apps/chronos/templates/chronos/substitutions_print.html @@ -1,8 +1,11 @@ +{# -*- engine:django -*- #} + +{% extends 'core/base.html' %} {% load common %} -{% include 'partials/paper/header.html' %} +{% block content %} <script type="text/javascript"> - var dest = "/timetable/substitutions/"; + var dest = Urls.substitutions(); </script> <style> @@ -20,7 +23,7 @@ </style> {% for c in days %} - <h4>Vertretungen {{ c.date|date:"l, j. F Y" }}</h4> + <h4>Substitutions {{ c.date|date" }}</h4> {% include "timetable/hintsinsubprint.html" %} @@ -45,10 +48,10 @@ <tr> <th><i class="material-icons">people</i></th> <th><i class="material-icons">access_time</i></th> - <th>Lehrer</th> - <th>Fach</th> - <th>Raum</th> - <th>Hinweis</th> + <th>{% blocktrans %}Teachers{% endblocktrans %}</th> + <th>{% blocktrans %}Subject{% endblocktrans %}</th> + <th>{% blocktrans %}Room{% endblocktrans %}</th> + <th>{% blocktrans %}Hint{% endblocktrans %}</th> <th></th> </tr> </thead> @@ -56,7 +59,7 @@ {% if not c.sub_table %} <td colspan="7"> <p class="flow-text center"> - Keine Vertretungen vorhanden + {% blocktrans %}No existing substitutions{% endblocktrans %} </p> </td> {% endif %} @@ -105,6 +108,4 @@ </table> {% endfor %} - - -{% include 'partials/paper/footer.html' %} +{% endblock %} diff --git a/aleksis/apps/chronos/templates/chronos/timetable.html b/aleksis/apps/chronos/templates/chronos/timetable.html new file mode 100644 index 0000000000000000000000000000000000000000..4fa91ac68f3ef7c34678ae9c94958f324ca43380 --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/timetable.html @@ -0,0 +1,209 @@ +{# -*- engine:django -*- #} + +{% extends 'core/base.html' %} + +{% load data_helpers static i18n %} + +{% block extra_head %} + <link rel="stylesheet" href="{% static 'css/chronos/timetable.css' %}"> +{% endblock %} + +{% block browser_title %}{% blocktrans %}Timetable{% endblocktrans %}{% endblock %} +{% block content %} + + {% if smart %} + <script type="text/javascript" src="{% static "js/helper.js" %}"></script> + {{ week_select|json_script:"week_select" }} + <script type="text/javascript" src="{% static "js/chronos/week_select.js" %}"></script> + {% endif %} + + <div class="row no-margin"> + <div class="col s8 m6 l8 xl9"> + <h4> + {% trans "Timetable" %} <i>{{ el }}</i> + </h4> + + {# Show class teacher and deputy class teacher #} + {% if type == "group" and el.owners.all %} + <h5>{% trans "Group teachers:" %} + {% for teacher in el.owners.all %} + <span data-position="bottom" class="tooltipped" + data-tooltip="{{ teacher }}"> + <a href="{% url "timetable" "teacher" teacher.pk %}"> + {{ teacher.short_name }}</a></span>{% if not forloop.last %},{% endif %} + {% endfor %} + </h5> + {% endif %} + </div> + {# Show print button only if not on mobile #} + <div class="col s4 m6 l4 xl3 right align-right no-print"> + <a class="waves-effect waves-teal btn-flat btn-flat-medium right hide-on-small-and-down" id="print"> + <i class="material-icons center">print</i> + </a> + </div> + </div> + <div class="row"> + {% if smart %} + {# Show if smart #} + {# Toggle button to regular and smart plan badge #} + <div class="row s12 m6 left"> + <span class="badge new primary-color left smart-plan-badge">{% trans "SMART PLAN" %}</span> + + <a class="waves-effect waves-light btn-flat no-print" + href="{% url "timetable_regular" type pk "regular" %}"> + <i class="material-icons left">slideshow</i> + {% trans "Show regular timetable" %} + </a> + </div> + + {# Week select #} + <div class="col s12 m6 right"> + <div class="col s2 no-print"> + <a class="waves-effect waves-teal btn-flat btn-flat-medium right" href="{{ url_prev }}"> + <i class="material-icons center">navigate_before</i> + </a> + </div> + + {% with wanted_week=week %} + <div class="input-field col s8 no-margin hide-on-med-and-up"> + <select id="calendar-week-1"> + {% for week in weeks %} + <option value="{{ week.week }}" {% if week == wanted_week %} + selected {% endif %}>{% trans "CW" %} {{ week.week }} ({{ week.0|date:"SHORT_DATE_FORMAT" }}–{{ week.6|date:"SHORT_DATE_FORMAT" }}) + </option> + {% endfor %} + </select> + </div> + + <div class="input-field col s8 no-margin hide-on-med-and-down"> + <select id="calendar-week-2"> + {% for week in weeks %} + <option value="{{ week.week }}" {% if week == wanted_week %} + selected {% endif %}>{% trans "CW" %} {{ week.week }} ({{ week.0|date:"j.n" }}–{{ week.6|date:"SHORT_DATE_FORMAT" }}) + </option> + {% endfor %} + </select> + </div> + + <div class="input-field col s8 no-margin hide-on-small-and-down hide-on-large-only"> + <select id="calendar-week-3"> + {% for week in weeks %} + <option value="{{ week.week }}" {% if week == wanted_week %} + selected {% endif %}>{% trans "CW" %} {{ week.week }} ({{ week.0|date:"j.n" }}–{{ week.6|date:"j.n" }}) + </option> + {% endfor %} + </select> + </div> + {% endwith %} + + <div class="col s2 no-print"> + <a class="waves-effect waves-teal btn-flat btn-flat-medium left" href="{{ url_next }}"> + <i class="material-icons center">navigate_next</i> + </a> + </div> + </div> + + {% else %} + {# Show if regular #} + <a class="waves-effect waves-light btn-flat no-print" + href="{% url "timetable" type pk %}"> + <i class="material-icons left">slideshow</i> + {% trans "Show SMART PLAN" %} + </a> + {% endif %} + </div> + + {# {% include "chronos/hintsinplan.html" %}#} + + {# show full timetable on tablets, laptops and pcs #} + <div class="timetable-plan hide-on-small-and-down"> + + {# Week days #} + <div class="row"> + <div class="col s2"> + + </div> + {# Show short weekdays on tablets #} + {% for day in weekdays_short.items %} + <div class="col s2 hide-on-large-only"> + <div class="card timetable-title-card"> + <div class="card-content"> + <span class="card-title"> + {{ day.1 }} + </span> + {# {% if day.1 %}#} + {# <span class="badge new blue center-align holiday-badge">{{ day.1.0 }}</span>#} + {# {% endif %}#} + </div> + </div> + </div> + {% endfor %} + + {# Show long weekdays elsewere #} + {% for day in weekdays.items %} + <div class="col s2 hide-on-med-only"> + <div class="card timetable-title-card"> + <div class="card-content"> + <span class="card-title"> + {{ day.1 }} + </span> + {# {% if day.1 %}#} + {# <span class="badge new blue center-align holiday-badge">{{ day.1.0 }}</span>#} + {# {% endif %}#} + </div> + </div> + </div> + {% endfor %} + </div> + + {# Lessons #} + {% for period, lesson_periods_period in lesson_periods.items %} + + <div class="row"> + <div class="col s2"> + {% include "chronos/partials/period_time.html" with period=period periods=periods %} + </div> + + {% for weekday, lessons in lesson_periods_period.items %} + {# A lesson #} + <div class="col s2"> + {% include "chronos/partials/lesson.html" with lessons=lessons %} + </div> + {% endfor %} + </div> + {% endfor %} + </div> + + {# show 5 seperate ones on mobiles #} + <div class="timetable-plan hide-on-med-and-up"> + {% for day in weekdays.items %} + <div class="card timetable-mobile-title-card"> + <div class="card-content"> + <span class="card-title"> + {{ day.1 }} + </span> + {# {% if day.1 %}#} + {# <span class="badge new blue center-align holiday-badge">{{ day.1.0 }}</span>#} + {# {% endif %}#} + {# #} + </div> + </div> + {% for period, lesson_periods_period in lesson_periods.items %} + <div class="row"> + <div class="col s4"> + {% include "chronos/partials/period_time.html" with period=period periods=periods %} + </div> + + {% for weekday, lessons in lesson_periods_period.items %} + {% if forloop.counter0|add:"1" == day.0 %} + <div class="col s8"> + {# A lesson #} + {% include "chronos/partials/lesson.html" with lessons=lessons %} + </div> + {% endif %} + {% endfor %} + </div> + {% endfor %} + {% endfor %} + </div> +{% endblock %} diff --git a/aleksis/apps/chronos/templates/chronos/tt_day.html b/aleksis/apps/chronos/templates/chronos/tt_day.html deleted file mode 100644 index 203d8091f4cf65c3b3c65c4c3ec99b33951bb125..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/tt_day.html +++ /dev/null @@ -1,22 +0,0 @@ -{# -*- engine:django -*- #} - -<div class="col-sm px-0"> - <div class="container"> - <div class="row"> - <div class="col-sm px-0"> - <div class="card chronos-head"> - <div class="card-body"> - <span class="card-title">{{ weekday_name }}</span> - </div> - </div> - </div> - </div> - {% for period_num, lesson_period in lesson_periods.items %} - <div class="row"> - <div class="col-sm px-0"> - {% include "chronos/tt_lesson.html" with lesson_period=lesson_period %} - </div> - </div> - {% endfor %} - </div> -</div> diff --git a/aleksis/apps/chronos/templates/chronos/tt_lesson.html b/aleksis/apps/chronos/templates/chronos/tt_lesson.html deleted file mode 100644 index 6627fb51963735d926f188c92933d32eab303715..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/tt_lesson.html +++ /dev/null @@ -1,46 +0,0 @@ -{# -*- engine:django -*- #} - -<div class="card chronos-lesson - {% if lesson_period.get_substitution.cancelled %} - border border-danger chronos-lesson-cancelled - {% elif lesson_period.get_substitution %} - border border-warning - {% endif %} - " - style=" - {% if lesson_period.lesson.subject.colour_fg %} - color: {{ lesson_period.lesson.subject.colour_fg }}; - {% endif %} - {% if lesson_period.lesson.subject.colour_bg %} - background-color: {{ lesson_period.lesson.subject.colour_bg }}; - {% endif %} - "> - <div class="card-body"> - <div class="row"> - <div class="col-sm"> - <span class="card-title"> - {{ lesson_period.get_subject.abbrev }} - </span> - </div> - </div> - <div class="row"> - <div class="col-sm"> - {% for group in lesson_period.get_groups.all %} - <span class="card-text">{{ group.short_name }}</span> - {% endfor %} - </div> - <div class="col-sm"> - {% for teacher in lesson_period.get_teachers.all %} - <a href="{% url 'person_by_id' teacher.id %}" - title="{{ teacher.first_name }} {{ teacher.last_name }}" - class="card-text"> - {{ teacher.short_name }} - </a> - {% endfor %} - </div> - <div class="col-sm"> - <span class="card-text">{{ lesson_period.get_room.short_name }}</span> - </div> - </div> - </div> -</div> diff --git a/aleksis/apps/chronos/templates/chronos/tt_week.html b/aleksis/apps/chronos/templates/chronos/tt_week.html deleted file mode 100644 index d85c4bea6030f2a6cb8f72b23f0675d7581bce51..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/tt_week.html +++ /dev/null @@ -1,74 +0,0 @@ -{# -*- engine:django -*- #} - -{% extends "core/turnable.html" %} -{% load bootstrap4 data_helpers i18n staticfiles %} - -{% block bootstrap4_extra_head %} - {{ block.super }} - <link rel="stylesheet" href="{% static 'css/chronos/timetable.css' %}" /> - {{ select_form.media.css }} -{% endblock %} - -{% block bootstrap4_extra_script %} - {{ block.super }} - {{ select_form.media.js }} -{% endblock %} - -{% block bootstrap4_title %}{% blocktrans %}Timetable{% endblocktrans %} - {{ block.super }}{% endblock %} - -{% block current_content %} - <form method="get"> - <ul id="timetable_select_form"> - {{ select_form.as_ul }} - </ul> - <button type="submit" class="btn btn-dark"> - {% blocktrans %}Select{% endblocktrans %} - </button> - </form> - - <div class="row"> - <div class="col-sm px-0"> - <div class="container"> - <div class="row"> - <div class="col-sm px-0"> - <div class="card chronos-head"> - <div class="card-body"> - <span class="card-title">{% blocktrans %}Times{% endblocktrans %}</span> - </div> - </div> - </div> - </div> - {% for num, times in periods.items %} - <div class="row"> - <div class="col-sm px-0"> - <div class="card chronos-lesson"> - <div class="card-body"> - <div class="row"> - <div class="col-sm px-0"> - <span class="card-title">{{ num }}.</span> - </div> - </div> - <div class="row"> - <div class="col-sm px-0"> - <span class="card-text">{{ times.0}}</span> - </div> - <div class="col-sm px-0"> - <span class="card-text">-</span> - </div> - <div class="col-sm px-0"> - <span class="card-text">{{ times.1 }}</span> - </div> - </div> - </div> - </div> - </div> - </div> - {% endfor %} - </div> - </div> - - {% for weekday, lesson_periods_day in lesson_periods.items %} - {% include "chronos/tt_day.html" with weekday_name=weekdays|get_dict:weekday lesson_periods=lesson_periods_day %} - {% endfor %} - </div> -{% endblock %} diff --git a/aleksis/apps/chronos/templates/timetable/all.html b/aleksis/apps/chronos/templates/timetable/all.html deleted file mode 100644 index ce5cc999c6fa461bbb9456c90a97b5f9d7294423..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/all.html +++ /dev/null @@ -1,52 +0,0 @@ -{% include 'partials/header.html' %} - -<main> - <h3>Alle Pläne</h3> - <div class="row"> - <div class="col s12 m4"> - <h4>Lehrkräfte</h4> - <ul class="collection"> - {% for teacher in teachers %} - <li class="collection-item avatar"> - <i class="circle">{{ teacher.shortcode }}</i><!-- Shortcode --> - <a href="{% url 'timetable_smart_plan' 'teacher' teacher.id %}">{{ teacher.first_name }} - <strong>{{ teacher.name }}</strong></a> - </li> - {% endfor %} - </ul> - </div> - - <div class="col s12 m4"> - <h4>Klassen</h4> - <ul class="collection"> - {% for class in classes %} - <li class="collection-item avatar"> - <i class="circle">{{ class.name }}</i> - <a href="{% url 'timetable_smart_plan' 'class' class.id %}"><strong>{{ class.name }}</strong></a> - <p> - {{ class.text1|default:"" }} – {{ class.text2|default:"" }} <br> - Raum: {{ class.room.name|default:"---" }} - </p> - </li> - {% endfor %} - </ul> - </div> - - <div class="col s12 m4"> - <h4>Räume</h4> - <ul class="collection"> - {% for room in rooms %} - <li class="collection-item avatar"> - <i class="circle">{{ room.shortcode }}</i> - <a href="{% url 'timetable_smart_plan' 'room' room.id %}"><strong>{{ room.name }}</strong></a> - </li> - {% endfor %} - </ul> - </div> - - - </div> - -</main> - -{% include 'partials/footer.html' %} diff --git a/aleksis/apps/chronos/templates/timetable/datepicker.html b/aleksis/apps/chronos/templates/timetable/datepicker.html deleted file mode 100644 index 56fd84d04da8dcecd13cae3cc10342682f2582ed..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/datepicker.html +++ /dev/null @@ -1,81 +0,0 @@ -<script type="text/javascript"> - function updateDatepicker() { - {% if not display_date_only %} - $("#date").val(formatDate(activeDate)); - {% endif %} - } - - function update() { - console.log("Render new."); - - updateDatepicker(); - } - - function loadNew() { - window.location.href = dest + formatDateForDjango(activeDate); - } - - function onDateBeforeClick() { - if (activeDate.getDay() === 1) { - var minus = 3; - } else { - var minus = 1; - } - activeDate.setDate(activeDate.getDate() - minus); - update(); - loadNew(); - } - - function onDateNextClick() { - if (activeDate.getDay() === 5) { - var plus = 3; - } else { - var plus = 1; - } - activeDate.setDate(activeDate.getDate() + plus); - update(); - loadNew(); - } - - function onDateChanged() { - var str = $("#date").val(); - var split = str.split(".") - activeDate = new Date(split[2], split[1] - 1, split[0]); - update(); - loadNew(); - } - - var activeDate = new Date({{ date_js }}); - - $(document).ready(function () { - $("#date-before").click(onDateBeforeClick); - $("#date-next").click(onDateNextClick); - $("#date").change(onDateChanged); - - update(); - }) -</script> - - -<div class="col s2" style="display: initial;"> - <a class="waves-effect waves-teal btn-flat btn-flat-medium left" id="date-before"> - <i class="material-icons center">navigate_before</i> - </a> - -</div> -{% if display_date_only %} - <div class="col s8"> - <span class="card-title center-block" id="date"> - {{ date|date:"l, j.n.Y" }} - </span> - </div> -{% else %} - <div class="col s8"> - <input type="text" class="datepicker center-align" id="date"> - </div> -{% endif %} -<div class="col s2" style="display: initial;"> - <a class="waves-effect waves-teal btn-flat btn-flat-medium right" id="date-next"> - <i class="material-icons center">navigate_next</i> - </a> -</div> diff --git a/aleksis/apps/chronos/templates/timetable/fallback.html b/aleksis/apps/chronos/templates/timetable/fallback.html deleted file mode 100644 index b68dbeed2525673e4f40f2daa369c08280054ef2..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/fallback.html +++ /dev/null @@ -1,8 +0,0 @@ -{% include 'partials/header.html' %} - -<main> - <h3>Die Stundenpläne sind aktuell noch nicht einsehbar.</h3> - -</main> - -{% include 'partials/footer.html' %} diff --git a/aleksis/apps/chronos/templates/timetable/latex/header.tex b/aleksis/apps/chronos/templates/timetable/latex/header.tex deleted file mode 100644 index 1f80f9705ebe19e66fa906ece98adeafd577b666..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/latex/header.tex +++ /dev/null @@ -1,63 +0,0 @@ -\documentclass[11pt]{article} -\usepackage[ngerman]{babel} -\usepackage[sfdefault]{cabin} -\usepackage[utf8]{inputenc} -\usepackage[a4paper,left=1cm,right=1cm,top=2cm,bottom=2.8cm,bindingoffset=0mm]{geometry} - -% Packages -\usepackage{fancyhdr} -\usepackage{lastpage} -\usepackage{graphicx} -\usepackage{longtable} -\usepackage{booktabs} -\usepackage{multirow} -\usepackage{color, colortbl} -\usepackage[colorlinks, linkcolor = black, citecolor = black, filecolor = black, urlcolor = black]{hyperref} - -\usepackage{ulem, xpatch} -\xpatchcmd{\sout} -{\bgroup} -{\bgroup\def\ULthickness{1.5pt}} -{}{} - -% Badge box -\usepackage{tcolorbox} -\newtcbox{\badge}{nobeforeafter,colframe=green,colback=green,boxrule=0.5pt,arc=4pt, -boxsep=0pt,left=4pt,right=4pt,top=4pt,bottom=4pt,tcbox raise base, -grow to left by=0pt, -grow to right by=-3pt, -enlarge top by=3pt, -enlarge bottom by=3pt,coltext=white} - -% Define colors -\definecolor{ grey }{RGB}{208, 208, 208} -\definecolor{ darkgrey }{rgb}{0.6,0.6,0.6} -\definecolor{ white }{rgb}{1,1,1} -\definecolor{ green }{RGB}{76,175,80} -\definecolor{green}{RGB}{76,175,80} -\definecolor{ blue }{RGB}{41,7,249} -\definecolor{ black }{RGB}{0,0,0} -\definecolor{ purple }{RGB}{156, 39, 176} - -% Define header -\pagestyle{fancy} - -% Left header: logo -\lhead{\includegraphics[width=5cm]{% templatetag openbrace %}{{LOGO_FILENAME}}{% templatetag closebrace %}} - -% Define footer -\lfoot{Katharineum zu Lübeck} -\cfoot{\thepage}%\ von \pageref{LastPage}} -\rfoot{\small Umsetzung: © 2018--2019 by Computer-AG} -\rhead{\textbf{ -Vertretungen }\\ -Stand: {% now "j. F Y H:i" %}\\ -} - -% Define own commands -%% tightlist from pandoc is missing in default.latex -%% see https://tex.stackexchange.com/questions/257418/error-tightlist-converting-md-file-into-pdf-using-pandoc -\providecommand{\tightlist}{% - \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} - -\begin{document} diff --git a/aleksis/apps/chronos/templates/timetable/latex/hints.tex b/aleksis/apps/chronos/templates/timetable/latex/hints.tex deleted file mode 100644 index 6077fd4adf1648c135c01181d3f09628be414793..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/latex/hints.tex +++ /dev/null @@ -1,15 +0,0 @@ -{% load martortags %} -{% if hints %} -\subsection*{\hspace{0.7em}Hinweise} -\vspace{-0.7em} -\begin{itemize} - \setlength\itemsep{0.1em} - {% for hint in hints %} - \normalsize - \item \small - {{ hint.classes_formatted }}{% if hint.teachers and hint.classes.all %}, {% endif %}{% if hint.teachers %}Lehrkräfte{% endif %}: - \normalsize -{{ hint.text_as_latex|safe }} -{% endfor %} -\end{itemize} -{% endif %} diff --git a/aleksis/apps/chronos/templates/timetable/latex/room.tex b/aleksis/apps/chronos/templates/timetable/latex/room.tex deleted file mode 100644 index 7b9b5678487b54271fce13bf31195b24e36f8709..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/latex/room.tex +++ /dev/null @@ -1,15 +0,0 @@ -{% if not sub.is_event %}{% if sub.sub.type == 3 %} -{{ sub.sub.corridor.name }} -{% elif sub.sub.type == 1 or sub.sub.type == 2 %} -{% elif sub.sub.room_new and sub.sub.room_old %} -\sout{% templatetag openbrace %}{{ sub.sub.room_old.shortcode }}} -$\rightarrow$ -{{ sub.sub.room_new.shortcode }} -{% elif sub.sub.room_new and not sub.sub.room_old %} -{{ sub.sub.room_new.shortcode }} -{% elif not sub.sub.room_new and not sub.sub.room_old %} -{% else %} -{{ sub.sub.room_old.shortcode }} -{% endif %}{% else %} -{% for room in sub.rooms %}{{ room.shortcode }}{% if not forloop.last %}, {% endif %}{% endfor %} -{% endif %} diff --git a/aleksis/apps/chronos/templates/timetable/latex/subject.tex b/aleksis/apps/chronos/templates/timetable/latex/subject.tex deleted file mode 100644 index 96b9d061a792a018cfd86ae5d8f9c78045399aa4..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/latex/subject.tex +++ /dev/null @@ -1,14 +0,0 @@ -{% if sub.is_event %} -Veranstaltung -{% elif sub.sub.type == 3 %} -Aufsicht -{% elif not sub.sub.subject_new and not sub.sub.subject_old %} -{% elif sub.sub.type == 1 or sub.sub.type == 2 %} -\sout{% templatetag openbrace %}{{ sub.sub.subject_old.shortcode }}} -{% elif sub.sub.subject_new and sub.sub.subject_old %} -\sout{% templatetag openbrace %}{{ sub.sub.subject_old.shortcode }}} $\rightarrow$ \textbf { {{ sub.sub.subject_new.shortcode }} } -{% elif sub.sub.subject_new and not sub.sub.subject_old %} -{{ sub.sub.subject_new.shortcode }} -{% else %} -{{ sub.sub.subject_old.shortcode }} -{% endif %} diff --git a/aleksis/apps/chronos/templates/timetable/latex/substitutions.tex b/aleksis/apps/chronos/templates/timetable/latex/substitutions.tex deleted file mode 100644 index 2b3d21e35170597ca04506b5e6483604764b737d..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/latex/substitutions.tex +++ /dev/null @@ -1,50 +0,0 @@ -{% load common %} -{% load tex %} - -\large -\subsubsection*{} - -\section*{\Huge Vertretungen {{ date|date:"l, j. F Y"}}} - -{% include "timetable/latex/hints.tex" %} - -{% if header_info.is_box_needed %} -\begin{tabular}{@{\hspace{-1em}}p{0.22\linewidth}p{0.73\linewidth}} -{% for row in header_info.rows %} - \textbf{ {{ row.0 }} } & {{ row.1 }} \\ -{% endfor %} -\end{tabular} -{% endif %} - -% Init table -{% set color_background = 1 %} -{% set last_classes = "" %} - -\def\arraystretch{1.1} -\begin{longtable}{p{20mm}p{10mm}p{30mm}p{25mm}p{30mm}p{45mm}} - \textbf{Klassen} & \textbf{Std.} & \textbf{Lehrer} & \textbf{Fach} & \textbf{Raum} & \textbf{Hinweis}\\\hline - \endhead - {% for sub in subs %} - {# Color groups of classes in grey/white #} - {% if last_classes != sub.classes %} - {% if color_background %}{% set color_background = 0 %} - {% else %}{% set color_background = 1 %} - {% endif %} - {% endif %} - {% set last_classes = sub.classes %} - {# Background color #} - {% if color_background %}\rowcolor{ grey }{% endif %} - {% with c=sub.color %} - {# Display classes #} - \color{ {{c}} }\textbf{ {{ sub.classes }} } & - {# Display lesson number #} - \color{ {{c}} } {{ sub.lesson }} & - \color{ {{c}} } {% include "timetable/latex/teacher.tex" %} & - \color{ {{c}} } {% include "timetable/latex/subject.tex" %} & - \color{ {{c}} } {% include "timetable/latex/room.tex" %} & - {# Display badge (for cancellations) #} {# Display notice and new line #} - {% if sub.badge %} \footnotesize\badge{ {{ sub.badge }} } {% endif %} - \color{ {{c}} } \large\textit{ {{sub.text|default:""|safe|texify|safe}} } \\ - {% endwith %} - {% endfor %} -\end{longtable} diff --git a/aleksis/apps/chronos/templates/timetable/latex/teacher.tex b/aleksis/apps/chronos/templates/timetable/latex/teacher.tex deleted file mode 100644 index 5320e06994ce99db3671e74c078a2fe6cb0e768f..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/latex/teacher.tex +++ /dev/null @@ -1,13 +0,0 @@ -{% if not sub.is_event %}{% if sub.sub.type == 1 %} -\sout{% templatetag openbrace %}{{ sub.sub.teacher_old.shortcode }}} -{% elif sub.sub.teacher_new and sub.sub.teacher_old %} -\sout{% templatetag openbrace %}{{ sub.sub.teacher_old.shortcode }}} -$\rightarrow$ -{{ sub.sub.teacher_new.shortcode }} -{% elif sub.sub.teacher_new and not sub.sub.teacher_old %} -{{ sub.sub.teacher_new.shortcode }} -{% elif sub.sub.teacher_old %} -{{ sub.sub.teacher_old.shortcode }} -{% endif %}{% else %} -{% for teacher in sub.teachers %}{{ teacher.shortcode }}{% if not forloop.last %}, {% endif %}{% endfor %} -{% endif %} diff --git a/aleksis/apps/chronos/templates/timetable/lesson.html b/aleksis/apps/chronos/templates/timetable/lesson.html deleted file mode 100644 index 5b2cca1c6a196d4e90e9e784c39d9569d30d78b3..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/lesson.html +++ /dev/null @@ -1,141 +0,0 @@ -<div class="card lesson-card"> - <div class="card-content"> - - {# Every element of the lesson #} - {% for element_container in col.elements %} - <div style=" - - {# Display background color only if no badge exists and it is not the old room and there are no holidays #} - {% if not element_container.substitution.table.badge %} - {% if not element_container.is_old or type != 1 %} - {% if not element_container.is_hol %} - background-color: {{ element_container.element.subject.hex_color }}; - {% endif %} - {% endif %} - {% endif %}" - - {# Add CSS class for sub when it's a sub #} - class="{% if element_container.substitution %}{% if element_container.substitution.table.is_event %}lesson-with-event{% else %}lesson-with-sub{% endif %}{% endif %}"> - <p> - {% if element_container.is_hol %} - - {# <p><strong>{ element_container.element.holiday_reason }}</strong></p>span class="badge new blue darken-2">Schulfrei</span>#} - - {% elif element_container.substitution %} - {# SUBSTITUTION #} - {% if type == 1 and element_container.is_old %} - {# When it's the old room, let it empty #} - - {% elif element_container.substitution.table.badge %} - {# When a badge (cancellation, etc.) exists, then display it with the teacher#} - - {# Class or room > Display teacher #} - {% if type == 2 or type == 1 %} - {% if element_container.element.teacher %} - <span data-position="bottom" class="tooltipped" - data-tooltip="{{ element_container.element.teacher }}"> - <a - href="{% url "timetable_smart_plan" "teacher" element_container.element.teacher.id %}">{{ element_container.element.teacher.shortcode }}</a> - </span> - <br> - {% endif %} - {% endif %} - - {# Badge #} - <span class="badge new green darken-2">{{ element_container.substitution.table.badge }}</span> - - - - {% else %} - {# Display sub #} - - {% with sub=element_container.substitution.table %} - {# Teacher or room > display classes #} - {% if type == 0 or type == 1 %} - {{ element_container.substitution.table.classes }} - {% endif %} - - {# Display teacher with tooltip #} - {% include "timetable/subs/teacher.html" %} - - {# Display subject #} - {% include "timetable/subs/subject.html" %} - - {# Teacher or class > display room #} - {% if type == 0 or type == 2 %} - {% include "timetable/subs/room.html" %} - {% endif %} - {% endwith %} - {% endif %} - - {# When it isn't a room or the old plan, then display the extra text (e. g. work orders) AND NOT A EVENT#} - {% if not element_container.substitution.table.is_event %} - {% if not type == 1 or not element_container.is_old %} - <br> - <small> - <em>{{ element_container.substitution.table.text|default:"" }}</em> - </small> - {% endif %} - {% endif %} - {# Display the extra text for events #} - {% if element_container.substitution.table.is_event %} - {% if type == 0 and element_container.substitution.table.classes == "" and element_container.substitution.table.rooms|length == 0 and element_container.substitutions.table.teachers|length == 0 %} - <em>{{ element_container.substitution.table.text|default:"" }}</em> - {% elif type == 2 and element_container.substitution.table.teachers|length == 0 and element_container.substitution.table.rooms|length == 0 %} - <em>{{ element_container.substitution.table.text|default:"" }}</em> - {% elif type == 1 and element_container.substitution.table.teachers|length == 0 and element_container.substitution.table.classes == "" %} - <em>{{ element_container.substitution.table.text|default:"" }}</em> - {% else %} - <br> - <small> - <em>{{ element_container.substitution.table.text|default:"" }}</em> - </small> - {% endif %} - {% endif %} - - {% else %} - {# Normal plan #} - - {# Teacher or room > Display classes #} - {% if type == 0 or type == 1 %} - {# {{ element_container.element.classes }}#} - {% if element_container.element.classes %} - <a href="{% url "timetable_smart_plan" "class" element_container.element.classes.0.id %}"> - {{ element_container.classes_formatted }} - </a> - {% endif %} - {% endif %} - - {# Class or room > Display teacher #} - {% if type == 2 or type == 1 %} - {% if element_container.element.teacher %} - <span data-position="bottom" class="tooltipped" - data-tooltip="{{ element_container.element.teacher }}"> - <a href="{% url "timetable_smart_plan" "teacher" element_container.element.teacher.id %}"> - {{ element_container.element.teacher.shortcode }} - </a> - </span> - {% endif %} - {% endif %} - - {# Display subject #} - <strong>{{ element_container.element.subject.shortcode }}</strong> - - {# Teacher or class > Display room #} - {% if type == 0 or type == 2 %} - <span class="tooltipped" data-position="bottom" - data-tooltip="{{ element_container.room.name }}"> - {% if element_container.room %} - <a href="{% url "timetable_smart_plan" "room" element_container.room.id %}"> - {{ element_container.room.shortcode }} - </a> - {% endif %} - </span> - {% endif %} - {% endif %} - </p> - </div> - - {% endfor %} - </div> -</div> diff --git a/aleksis/apps/chronos/templates/timetable/myplan.html b/aleksis/apps/chronos/templates/timetable/myplan.html deleted file mode 100644 index 49385ff63350268bb81d24b31f0671580747e803..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/myplan.html +++ /dev/null @@ -1,82 +0,0 @@ -{% include 'partials/header.html' %} - - -<main> - <div class="row nomargin"> - <div class="col m12 s12 l6"> - <h4> - Mein Plan <i>{{ el }}</i> - <span class="badge new primary-color ">SMART PLAN</span> - </h4> - <a class="btn-flat waves-effect waves-light" href="{% url "timetable_smart_plan" raw_type id %}">Wochenplan - anzeigen</a> - </div> - </div> - - <div class="row nomargin"> - <div class="col m12 s12 l6"> - {% include "timetable/hintsinplan.html" %} - </div> - </div> - - <script type="text/javascript"> - var dest = "/timetable/my/"; - </script> - - <div class="row"> - <div class="timetable-plan col s12 m12 l6"> - - {# Date #} - - <div class="row"> - <div class="col s12"> - <div class="card timetable-title-card"> - <div class="card-content"> - <span class="card-title"> - {% include "timetable/datepicker.html" %} - {% if holiday %} - <span class="badge new blue center-align holiday-badge">{{ holiday.0 }}</span> - {% endif %} - </span> - </div> - </div> - - </div> - </div> - {# Lessons #} - {% for row, time in plan %} - <div class="row"> - <div class="col s4"> - <div class="card timetable-title-card"> - <div class="card-content"> - - {# Lesson number #} - <span class="card-title left"> - {{ time.number_format }} - </span> - - {# Time dimension of lesson #} - <div class="right timetable-time grey-text text-darken-2"> - <span>{{ time.start|date:"H:i" }}</span><br> - <span>{{ time.end|date:"H:i" }}</span> - </div> - </div> - </div> - - </div> - {% for col in row %} - {% if forloop.counter0 == week_day %} - <div class="col s8"> - {# A lesson #} - {% include "timetable/lesson.html" %} - </div> - {% endif %} - {% endfor %} - </div> - {% endfor %} - - </div> - </div> -</main> - -{% include 'partials/footer.html' %} diff --git a/aleksis/apps/chronos/templates/timetable/plan.html b/aleksis/apps/chronos/templates/timetable/plan.html deleted file mode 100644 index 88e6e30a42cc90d3546bbe256f115a9e3d6f43f5..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/plan.html +++ /dev/null @@ -1,262 +0,0 @@ -{% include 'partials/header.html' %} -{% load copy_filter %} -<script type="text/javascript"> - {% if smart %} - var week = {{ selected_week }}; - var year = {{ selected_year }}; - function goToCalendarWeek(cw, year) { - window.location.href = "{% url "timetable_smart_plan" raw_type id %}/" + year + "/" + cw; - } - function onCalendarWeekChanged(where) { - goToCalendarWeek($(where).val(), year); - } - - function weekBefore() { - if (week > 1) { - goToCalendarWeek(week - 1, year) - } else { - goToCalendarWeek(52, year - 1) - } - } - - function weekNext() { - if (week < 52) { - goToCalendarWeek(week + 1, year); - } else { - goToCalendarWeek(1, year + 1); - } - } - - $(document).ready(function () { - $("#calendar-week-1").change(function () { - onCalendarWeekChanged("#calendar-week-1"); - }); - $("#calendar-week-2").change(function () { - onCalendarWeekChanged("#calendar-week-2"); - }); - $("#calendar-week-3").change(function () { - onCalendarWeekChanged("#calendar-week-3"); - }); - $("#week-before").click(weekBefore); - $("#week-next").click(weekNext); - }); - {% endif %} -</script> -<main> - <div class="row no-margin"> - <div class="col s8 m6 l8 xl9"> - <h3> - Stundenplan <i>{{ el }}</i> - </h3> - - {# Show class teacher and deputy class teacher #} - {% if type == 2 and el.teachers %} - <h5>Klassenlehrkräfte: - {% for teacher in el.teachers %} - - <span data-position="bottom" class="tooltipped" - data-tooltip="{{ teacher }}"> - <a href="{% url "timetable_smart_plan" "teacher" teacher.id %}"> - {{ teacher.shortcode }}</a></span>{% if not forloop.last %},{% endif %} - {% endfor %} - </h5> - {% endif %} - </div> - {# Show print button only if not on mobile #} - <div class="col s4 m6 l4 xl3 right align-right no-print"> - <a class="waves-effect waves-teal btn-flat btn-flat-medium right hide-on-small-and-down" id="print"> - <i class="material-icons center">print</i> - </a> - </div> - </div> - <div class="row"> - {% if smart %} - {# Show if smart #} - {# Toggle button to regular and smart plan badge #} - <div class="row s12 m6 left"> - <span class="badge new primary-color left smart-plan-badge">SMART PLAN</span> - - <a class="waves-effect waves-light btn-flat no-print" style="padding-left: 3px; padding-right: 3px;" - href="{% url "timetable_regular_plan" raw_type id "regular" %}"> - <i class="material-icons left">slideshow</i> - REGELPLAN ANZEIGEN - </a> - </div> - - {# Week select #} - <div class="col s12 m6 right"> - <div class="col s2 no-print"> - <a class="waves-effect waves-teal btn-flat btn-flat-medium right" id="week-before"> - <i class="material-icons center">navigate_before</i> - </a> - </div> - <div class="input-field col s8 no-margin hide-on-med-and-up"> - <select id="calendar-week-1"> - {% for week in weeks %} - <option value="{{ week.calendar_week }}" {% if week.calendar_week == selected_week %} - selected {% endif %}> KW {{ week.calendar_week }} - ({{ week.first_day|date:"j.n" }}–{{ week.last_day|date:"j.n" }}) - </option> - {% endfor %} - </select> - </div> - <div class="input-field col s8 no-margin hide-on-med-and-down"> - <select id="calendar-week-2"> - {% for week in weeks %} - <option value="{{ week.calendar_week }}" {% if week.calendar_week == selected_week %} - selected {% endif %}> KW {{ week.calendar_week }} ({{ week.first_day|date:"j.n.Y" }}–{{ week.last_day|date:"j.n.Y" }}) - </option> - {% endfor %} - </select> - </div> - <div class="input-field col s8 no-margin hide-on-small-and-down hide-on-large-only"> - <select id="calendar-week-3"> - {% for week in weeks %} - <option value="{{ week.calendar_week }}" {% if week.calendar_week == selected_week %} - selected {% endif %}> KW {{ week.calendar_week }} - ({{ week.first_day|date:"j.n" }}–{{ week.last_day|date:"j.n.Y" }}) - </option> - {% endfor %} - </select> - </div> - <div class="col s2 no-print"> - <a class="waves-effect waves-teal btn-flat btn-flat-medium left" id="week-next"> - <i class="material-icons center">navigate_next</i> - </a> - </div> - </div> - - {% else %} - {# Show if regular #} - <a class="waves-effect waves-light btn-flat no-print" - href="{% url "timetable_smart_plan" raw_type id %}"> - <i class="material-icons left">slideshow</i> - SMART PLAN ANZEIGEN - </a> - {% endif %} - </div> - - {% include "timetable/hintsinplan.html" %} - - {# show full timetable on tablets, laptops and pcs #} - <div class="timetable-plan hide-on-small-and-down"> - - {# Week days #} - <div class="row"> - <div class="col s2"> - - </div> - {# Show short weekdays on tablets #} - {% for day in short_week_days|deepcopy %} - <div class="col s2 hide-on-large-only"> - <div class="card timetable-title-card"> - <div class="card-content"> - <span class="card-title"> - {{ day.0 }} - </span> - {% if day.1 %} - <span class="badge new blue center-align holiday-badge">{{ day.1.0 }}</span> - {% endif %} - </div> - </div> - </div> - {% endfor %} - {# Show long weekdays elsewere #} - {% for day in long_week_days|deepcopy %} - <div class="col s2 hide-on-med-only"> - <div class="card timetable-title-card"> - <div class="card-content"> - <span class="card-title"> - {{ day.0.0 }} - </span> - {% if day.1 %} - <span class="badge new blue center-align holiday-badge">{{ day.1.0 }}</span> - {% endif %} - </div> - </div> - </div> - {% endfor %} - </div> - - {# Lessons #} - {% for row, time in plan|deepcopy %} - <div class="row"> - <div class="col s2"> - <div class="card timetable-title-card"> - <div class="card-content"> - - {# Lesson number #} - <span class="card-title left"> - {{ time.number_format }} - </span> - - {# Time dimension of lesson #} - <div class="right timetable-time grey-text text-darken-2"> - <span>{{ time.start|date:"H:i" }}</span> - <br> - <span>{{ time.end|date:"H:i" }}</span> - </div> - </div> - </div> - - </div> - {% for col in row %} - {# A lesson #} - <div class="col s2"> - {% include "timetable/lesson.html" %} - </div> - {% endfor %} - </div> - {% endfor %} - </div> - - {# show 5 seperate ones on mobiles #} - <div class="timetable-plan hide-on-med-and-up"> - {% for day in long_week_days|deepcopy %} - <div class="card timetable-mobile-title-card"> - <div class="card-content"> - <span class="card-title"> - {{ day.0.0 }} - </span> - {% if day.1 %} - <span class="badge new blue center-align holiday-badge">{{ day.1.0 }}</span> - {% endif %} - - </div> - </div> - {% for row, time in plan|deepcopy %} - <div class="row"> - <div class="col s4"> - <div class="card timetable-title-card"> - <div class="card-content"> - - {# Lesson number #} - <span class="card-title left"> - {{ time.number_format }} - </span> - - {# Time dimension of lesson #} - <div class="right timetable-time grey-text text-darken-2"> - <span>{{ time.start|date:"H:i" }}</span> - <br> - <span>{{ time.end|date:"H:i" }}</span> - </div> - </div> - </div> - - </div> - {% for col in row|deepcopy %} - {% if forloop.counter0 == day.0.1 %} - <div class="col s8"> - {# A lesson #} - {% include "timetable/lesson.html" %} - </div> - {% endif %} - {% endfor %} - </div> - {% endfor %} - {% endfor %} - </div> -</main> - -{% include 'partials/footer.html' %} diff --git a/aleksis/apps/chronos/templates/timetable/quicklaunch.html b/aleksis/apps/chronos/templates/timetable/quicklaunch.html deleted file mode 100644 index 2647f44c697ec19ce5c05bd8d08ebeda22b14446..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/quicklaunch.html +++ /dev/null @@ -1,43 +0,0 @@ -{% include 'partials/header.html' %} - -<main> - <h4>Alle Pläne</h4> - <div class="row"> - <div class="col s12 m4"> - <h5>Lehrkräfte</h5> - {% for teacher in teachers %} - <a class="waves-effect waves-light btn btn-timetable-quicklaunch" - href="{% url 'timetable_smart_plan' 'teacher' teacher.id %}"> - {{ teacher.shortcode }} - </a><!-- Shortcode --> - {% endfor %} - </div> - - <div class="col s12 m4"> - <h5>Klassen</h5> - - {% for class in classes %} - <a class="waves-effect waves-light btn btn-timetable-quicklaunch" - href="{% url 'timetable_smart_plan' 'class' class.id %}"> - {{ class.name }} - </a> - {% endfor %} - - </div> - - <div class="col s12 m4"> - <h5>Räume</h5> - {% for room in rooms %} - <a class="waves-effect waves-light btn btn-timetable-quicklaunch" - href="{% url 'timetable_smart_plan' 'room' room.id %}"> - {{ room.shortcode }} - </a> - {% endfor %} - </div> - - - </div> - -</main> - -{% include 'partials/footer.html' %} diff --git a/aleksis/apps/chronos/templates/timetable/subs/room.html b/aleksis/apps/chronos/templates/timetable/subs/room.html deleted file mode 100644 index 0b9aa41a5f875ea222bbed39fd875e7ba34c88ed..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/subs/room.html +++ /dev/null @@ -1,47 +0,0 @@ -{% if not sub.is_event %} - {% if sub.sub.type == 3 %} - {# Supervisement #} - {{ sub.sub.corridor.name }} - {% elif sub.sub.type == 1 or sub.sub.type == 2 %} - {# Canceled lesson: no room #} - {% elif sub.sub.room_new and sub.sub.room_old %} - {# New and old room available #} - <span class="tooltipped" data-position="bottom" - data-tooltip="{{ sub.sub.room_old.name }} → {{ sub.sub.room_new.name }}"> - <a href="{% url "timetable_smart_plan" "room" sub.sub.room_old.id %}"> - <s>{{ sub.sub.room_old.shortcode }}</s> - </a> - → - <a href="{% url "timetable_smart_plan" "room" sub.sub.room_new.id %}"> - <strong>{{ sub.sub.room_new.shortcode }}</strong> - </a> - </span> - {% elif sub.sub.room_new and not sub.sub.room_old %} - {# Only new room available #} - <span class="tooltipped" data-position="bottom" - data-tooltip="{{ sub.sub.room_new.name }}"> - <a href="{% url "timetable_smart_plan" "room" sub.sub.room_new.id %}"> - {{ sub.sub.room_new.shortcode }} - </a> - </span> - {% elif not sub.sub.room_new and not sub.sub.room_old %} - {# Nothing to view #} - {% else %} - {# Only old room available #} - <span class="tooltipped" data-position="bottom" - data-tooltip="{{ sub.sub.room_old.name }}"> - <a href="{% url "timetable_smart_plan" "room" sub.sub.room_old.id %}"> - {{ sub.sub.room_old.shortcode }} - </a> - </span> - {% endif %} -{% else %} - {% for room in sub.rooms %} - <span class="tooltipped" data-position="bottom" - data-tooltip="{{ room.name }}"> - <a href="{% url "timetable_smart_plan" "room" room.id %}"> - <strong>{{ room.shortcode }}{% if not forloop.last %},{% endif %}</strong> - </a> - </span> - {% endfor %} -{% endif %} diff --git a/aleksis/apps/chronos/templates/timetable/subs/subject.html b/aleksis/apps/chronos/templates/timetable/subs/subject.html deleted file mode 100644 index f8232e9cf1972413bad5339ab8c08d84dad424ca..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/subs/subject.html +++ /dev/null @@ -1,15 +0,0 @@ -{% if not sub.sub.is_event %} - {% if sub.sub.type == 3 %} - <strong>Aufsicht</strong> - {% elif not sub.sub.subject_new and not sub.sub.subject_old %} - {% elif sub.sub.type == 1 or sub.sub.type == 2 %} - <s>{{ sub.sub.subject_old.shortcode }}</s> - {% elif sub.sub.subject_new and sub.sub.subject_old %} - <s>{{ sub.sub.subject_old.shortcode }}</s> → - <strong>{{ sub.sub.subject_new.shortcode }}</strong> - {% elif sub.sub.subject_new and not sub.sub.subject_old %} - <strong>{{ sub.sub.subject_new.shortcode }}</strong> - {% else %} - <strong>{{ sub.sub.subject_old.shortcode }}</strong> - {% endif %} -{% endif %} diff --git a/aleksis/apps/chronos/templates/timetable/subs/teacher.html b/aleksis/apps/chronos/templates/timetable/subs/teacher.html deleted file mode 100644 index 4cf5862c798da9072a9fd6d31a882e982b426cdc..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/subs/teacher.html +++ /dev/null @@ -1,44 +0,0 @@ -{% if not sub.is_event %} - {% if sub.sub.type == 1 and sub.sub.teacher_old %} - <span class="tooltipped" data-position="bottom" - data-tooltip="{{ sub.sub.teacher_old.name }}"> - <a href="{% url "timetable_smart_plan" "teacher" sub.sub.teacher_old.id %}"> - <s>{{ sub.sub.teacher_old.shortcode }}</s> - </a> - </span> - {% elif sub.sub.teacher_new and sub.sub.teacher_old %} - <span class="tooltipped" data-position="bottom" - data-tooltip="{{ sub.sub.teacher_old.name }} → {{ sub.sub.teacher_new.name }}"> - <a href="{% url "timetable_smart_plan" "teacher" sub.sub.teacher_old.id %}"> - <s>{{ sub.sub.teacher_old.shortcode }}</s> - </a> - → - <a href="{% url "timetable_smart_plan" "teacher" sub.sub.teacher_new.id %}"> - <strong>{{ sub.sub.teacher_new.shortcode }}</strong> - </a> - </span> - {% elif sub.sub.teacher_new and not sub.sub.teacher_old %} - <span class="tooltipped" data-position="bottom" - data-tooltip="{{ sub.sub.teacher_new.name }}"> - <a href="{% url "timetable_smart_plan" "teacher" sub.sub.teacher_new.id %}"> - <strong>{{ sub.sub.teacher_new.shortcode }}</strong> - </a> - </span> - {% elif sub.sub.teacher_old %} - <span class="tooltipped" data-position="bottom" - data-tooltip="{{ sub.sub.teacher_old.name }}"> - <a href="{% url "timetable_smart_plan" "teacher" sub.sub.teacher_old.id %}"> - <strong>{{ sub.sub.teacher_old.shortcode }}</strong> - </a> - </span> - {% endif %} -{% else %} - {% for teacher in sub.teachers %} - <span class="tooltipped" data-position="bottom" - data-tooltip="{{ teacher.name }}"> - <a href="{% url "timetable_smart_plan" "teacher" teacher.id %}"> - <strong>{{ teacher.shortcode }}{% if not forloop.last %},{% endif %}</strong> - </a> - </span> - {% endfor %} -{% endif %} diff --git a/aleksis/apps/chronos/templates/timetable/substitution.html b/aleksis/apps/chronos/templates/timetable/substitution.html deleted file mode 100644 index ca5983743f6bd98297899fa2bba601e1b14e0a6f..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/timetable/substitution.html +++ /dev/null @@ -1,112 +0,0 @@ -{% include 'partials/header.html' %} - - -<script type="text/javascript"> - var dest = "/timetable/substitutions/"; -</script> - -<main> - <div class="row no-margin"> - <div class="col s10 m6"> - <h4>Vertretungen</h4> - </div> - <div class="col s2 m6 right align-right print-icon"> - <a class="waves-effect waves-teal btn-flat btn-flat-medium right" - href=" - {% if debug %} - {% url "timetable_substitutions_pdf_date" date|date:"Y-m-d" %} - {% else %} - {% url "timetable_substitutions_pdf" %} - {% endif %} - "> - <i class="material-icons center">print</i> - </a> - </div> - </div> - - <div class="row no-print"> - <div class="col s12 m6 l8 xl9"> - {% 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 "timetable/hintsinsub.html" %} - </div> - <div class="col s12 m6 l4 xl3"> - {% include "timetable/datepicker.html" %} - </div> - </div> - - <h5 class="hide-on-small-and-down">{{ date|date:"l, j. F Y" }}</h5> - - <table class="substitutions striped responsive-table"> - <thead> - <tr> - <th><i class="material-icons">people</i></th> - <th><i class="material-icons">access_time</i></th> - <th>Lehrer</th> - <th>Fach</th> - <th>Raum</th> - <th>Hinweis</th> - <th></th> - </tr> - </thead> - <tbody> - {% if not sub_table %} - <td colspan="7"> - <p class="flow-text center"> - Keine Vertretungen vorhanden - </p> - </td> - {% endif %} - {% for sub in sub_table %} - <tr class="{{ sub.color }}-text"> - <td> - {{ sub.classes }} - </td> - <td> - <strong> - {{ sub.lesson }} - </strong> - </td> - <td> - {% include "timetable/subs/teacher.html" %} - </td> - <td> - {% include "timetable/subs/subject.html" %} - </td> - <td> - {% include "timetable/subs/room.html" %} - </td> - <td> - {% if sub.badge %} - <span class="badge new green hide-on-med-and-up">{{ sub.badge }}</span> - {% endif %} - <em>{{ sub.text|default:"" }}</em> - </td> - <td class="hide-on-small-and-down"> - {% if sub.badge %} - <span class="badge new green darken-2">{{ sub.badge }}</span> - {% endif %} - </td> - </tr> - {% endfor %} - </tbody> - </table> - -</main> - -{% include 'partials/footer.html' %} diff --git a/aleksis/apps/chronos/templatetags/week_helpers.py b/aleksis/apps/chronos/templatetags/week_helpers.py index 529d253ae7b9a66879598c794650820358aac29c..26dee1e900e84367e8ec4b1efbcdd8c6d298499e 100644 --- a/aleksis/apps/chronos/templatetags/week_helpers.py +++ b/aleksis/apps/chronos/templatetags/week_helpers.py @@ -4,7 +4,7 @@ from typing import Optional, Union from django import template from django.db.models.query import QuerySet -from ..util import CalendarWeek, week_period_to_date, week_weekday_to_date +from aleksis.apps.chronos.util.weeks import CalendarWeek, week_period_to_date, week_weekday_to_date register = template.Library() diff --git a/aleksis/apps/chronos/urls.py b/aleksis/apps/chronos/urls.py index cf94f08a3115eae77e447046aa21332add8566d3..fb3e7c2e0fe25027493b3d5fcf6c2816846c68a3 100644 --- a/aleksis/apps/chronos/urls.py +++ b/aleksis/apps/chronos/urls.py @@ -3,20 +3,24 @@ from django.urls import path from . import views urlpatterns = [ - path("timetable", views.timetable, name="timetable"), - path("timetable/<int:year>/<int:week>", views.timetable, name="timetable_by_week"), - path("lessons", views.lessons_day, name="lessons_day"), - path("lessons/<when>", views.lessons_day, name="lessons_day_by_date"), + path("", views.all_timetables, name="all_timetables"), + path("timetable/my/", views.my_timetable, name="my_timetable"), + path("timetable/my/<int:year>/<int:month>/<int:day>/", views.my_timetable, name="my_timetable_by_date"), + path("timetable/<str:type_>/<int:pk>/", views.timetable, name="timetable"), + path("timetable/<str:type_>/<int:pk>/<int:year>/<int:week>/", views.timetable, name="timetable_by_week"), + path("timetable/<str:type_>/<int:pk>/<str:regular>/", views.timetable, name="timetable_regular"), + path("lessons/", views.lessons_day, name="lessons_day"), + path("lessons/<int:year>/<int:month>/<int:day>/", views.lessons_day, name="lessons_day_by_date"), path( - "lessons/<int:id_>/<int:week>/substition", + "lessons/<int:id_>/<int:week>/substition/", views.edit_substitution, name="edit_substitution", ), path( - "lessons/<int:id_>/<int:week>/substition/delete", + "lessons/<int:id_>/<int:week>/substition/delete/", views.delete_substitution, name="delete_substitution", ), - path("substitutions", views.substitutions, name="substitutions"), - path("substitutions/<int:year>/<int:week>", views.substitutions, name="substitutions_by_week",), + path("substitutions/", views.substitutions, name="substitutions"), + path("substitutions/<int:year>/<int:month>/<int:day>/", views.substitutions, name="substitutions_by_date"), ] diff --git a/aleksis/apps/chronos/util.py b/aleksis/apps/chronos/util.py deleted file mode 100644 index c9501c0385d9eba9f3ca3bca611a105c4a53a5cf..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/util.py +++ /dev/null @@ -1,106 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from datetime import date, datetime, timedelta -from typing import Optional, Sequence, Tuple, Union - -from django.apps import apps -from django.db import models -from django.utils.translation import ugettext as _ - - -@dataclass -class CalendarWeek: - """ A calendar week defined by year and ISO week number. """ - - year: Optional[int] = None - week: Optional[int] = None - - @classmethod - def from_date(cls, when: date): - """ Get the calendar week by a date object (the week this date is in). """ - - week = int(when.strftime("%V")) - year = when.year + 1 if when.month == 12 and week == 1 else when.year - - return cls(year=year, week=week) - - @classmethod - def current_week(cls) -> int: - """ Get the current week number. """ - - return cls().week - - @classmethod - def weeks_within(cls, start: date, end: date) -> Sequence[CalendarWeek]: - """ Get all calendar weeks within a date range. """ - - if start > end: - raise ValueError("End date must be after start date.") - - current = start - weeks = [] - while current < end: - weeks.append(cls.from_date(current)) - current += timedelta(days=7) - - return weeks - - def __post_init__(self) -> None: - today = date.today() - - if not self.year: - self.year = today.year - if not self.week: - self.week = int(today.strftime("%V")) - - def __str__(self) -> str: - return "%s %d (%s %s %s)" % (_("Calendar Week"), self.week, self[0], _("to"), self[-1],) - - def __len__(self) -> int: - return 7 - - def __getitem__(self, n: int) -> date: - if n < -7 or n > 6: - raise IndexError("Week day %d is out of range." % n) - - if n < 0: - n += 7 - - return datetime.strptime("%d-%d-%d" % (self.year, self.week, n + 1), "%G-%V-%u").date() - - def __contains__(self, day: date) -> bool: - return self.__class__.form_date(day) == self - - def __eq__(self, other: CalendarWeek) -> bool: - return self.year == other.year and self.week == other.week - - def __lt__(self, other: CalendarWeek) -> bool: - return self[0] < other[0] - - def __gt__(self, other: CalendarWeek) -> bool: - return self[0] > other[0] - - def __le__(self, other: CalendarWeek) -> bool: - return self[0] <= other[0] - - def __gr__(self, other: CalendarWeek) -> bool: - return self[0] >= other[0] - - def __add__(self, weeks: int) -> CalendarWeek: - return self.__class__.from_date(self[0] + timedelta(days=weeks * 7)) - - def __sub__(self, weeks: int) -> CalendarWeek: - return self.__class__.from_date(self[0] - timedelta(days=weeks * 7)) - - -def week_weekday_from_date(when: date) -> Tuple[CalendarWeek, int]: - return (CalendarWeek.from_date(when), when.isoweekday()) - - -def week_weekday_to_date(week: CalendarWeek, weekday: int) -> date: - return week[weekday - 1] - - -def week_period_to_date(week: Union[CalendarWeek, int], period) -> date: - return period.get_date(week) diff --git a/aleksis/apps/chronos/util/js.py b/aleksis/apps/chronos/util/js.py new file mode 100644 index 0000000000000000000000000000000000000000..eb9082b0e46bcb06090ae853561d5c86c159ce56 --- /dev/null +++ b/aleksis/apps/chronos/util/js.py @@ -0,0 +1,8 @@ +from datetime import datetime, time, date + + +def date_unix(value: date) -> int: + """ Converts a date object to an UNIX timestamp """ + + value = datetime.combine(value, time(hour=0, minute=0)) + return int(value.timestamp()) * 1000 diff --git a/aleksis/apps/chronos/util/min_max.py b/aleksis/apps/chronos/util/min_max.py new file mode 100644 index 0000000000000000000000000000000000000000..4d5dd421443f36b3b2f01ce0a2ef74b6504a0b5e --- /dev/null +++ b/aleksis/apps/chronos/util/min_max.py @@ -0,0 +1,19 @@ +from django.db.models import Min, Max + +from aleksis.apps.chronos.models import TimePeriod + +# Determine overall first and last day and period +min_max = TimePeriod.objects.aggregate( + Min("period"), Max("period"), Min("weekday"), Max("weekday"), Min("time_start"), Max("time_end") +) + +period_min = min_max.get("period__min", 1) +period_max = min_max.get("period__max", 7) + +time_min = min_max.get("time_start__min", None) +time_max = min_max.get("time_end__max", None) + +weekday_min_ = min_max.get("weekday__min", 0) +weekday_max = min_max.get("weekday__max", 6) + + diff --git a/aleksis/apps/chronos/util/prev_next.py b/aleksis/apps/chronos/util/prev_next.py new file mode 100644 index 0000000000000000000000000000000000000000..c0a844dc40e728a17c98e6289def5bd69c1dd9b9 --- /dev/null +++ b/aleksis/apps/chronos/util/prev_next.py @@ -0,0 +1,48 @@ +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 aleksis.apps.chronos.util.min_max import weekday_min_, weekday_max, time_max + + +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 > time_max: + day += timedelta(days=1) + + cw = CalendarWeek.from_date(day) + + if day.weekday() > weekday_max: + if prev: + day = cw[weekday_max] + else: + cw += 1 + day = cw[weekday_min_] + elif day.weekday() < weekday_min_: + if prev: + cw -= 1 + day = cw[weekday_max] + else: + day = cw[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 diff --git a/aleksis/apps/chronos/util/weeks.py b/aleksis/apps/chronos/util/weeks.py new file mode 100644 index 0000000000000000000000000000000000000000..32ec8b81f006d0332b5be8b1502043a1c1806766 --- /dev/null +++ b/aleksis/apps/chronos/util/weeks.py @@ -0,0 +1,30 @@ +from datetime import date +from typing import Tuple, List, Union + +from calendarweek import CalendarWeek + + +def week_weekday_from_date(when: date) -> Tuple[CalendarWeek, int]: + return (CalendarWeek.from_date(when), when.weekday()) + + +def week_weekday_to_date(week: CalendarWeek, weekday: int) -> date: + return week[weekday - 1] + + +def week_period_to_date(week: Union[CalendarWeek, int], period) -> date: + return period.get_date(week) + + +def get_weeks_for_year(year: int) -> List[CalendarWeek]: + """ Generates all weeks for one year """ + weeks = [] + + # Go for all weeks in year and create week list + current_week = CalendarWeek(year=year, week=1) + + while current_week.year == year: + weeks.append(current_week) + current_week += 1 + + return weeks diff --git a/aleksis/apps/chronos/views.py b/aleksis/apps/chronos/views.py index 966dc0b831ffe457c661c09228d2f9d1bb7ed1cb..90cac9127e56297b181583de6d8b0572024b59b6 100644 --- a/aleksis/apps/chronos/views.py +++ b/aleksis/apps/chronos/views.py @@ -1,130 +1,242 @@ from collections import OrderedDict from datetime import date, datetime, timedelta -from typing import Optional +from typing import Optional, Tuple from django.contrib.auth.decorators import login_required -from django.db.models import Max, Min -from django.http import HttpRequest, HttpResponse +from django.db.models import Count +from django.http import HttpRequest, HttpResponse, HttpResponseNotFound from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse +from django.utils import timezone from django.utils.translation import ugettext as _ - from django_tables2 import RequestConfig from aleksis.core.decorators import admin_required +from aleksis.core.models import Person, Group from aleksis.core.util import messages +from .forms import LessonSubstitutionForm +from .models import LessonPeriod, LessonSubstitution, TimePeriod, Room +from .tables import LessonsTable +from .util.js import date_unix +from .util.min_max import ( + period_min, + period_max, + weekday_min_, + weekday_max +) +from .util.prev_next import get_next_relevant_day, get_prev_next_by_day +from .util.weeks import CalendarWeek, get_weeks_for_year +from aleksis.core.util.core_helpers import has_person + + +@login_required +def all_timetables(request: HttpRequest) -> HttpResponse: + context = {} + + teachers = Person.objects.annotate( + lessons_count=Count("lessons_as_teacher") + ).filter(lessons_count__gt=0) + classes = Group.objects.annotate(lessons_count=Count("lessons")).filter( + lessons_count__gt=0, parent_groups=None + ) + rooms = Room.objects.annotate(lessons_count=Count("lesson_periods")).filter( + lessons_count__gt=0 + ) + + context["teachers"] = teachers + context["classes"] = classes + context["rooms"] = rooms + + return render(request, "chronos/all.html", context) + + +@login_required +def my_timetable( + request: HttpRequest, + year: Optional[int] = None, + month: Optional[int] = None, + day: Optional[int] = None, +) -> HttpResponse: + context = {} + + if day: + wanted_day = timezone.datetime(year=year, month=month, day=day).date() + wanted_day = get_next_relevant_day(wanted_day) + else: + wanted_day = get_next_relevant_day(timezone.now().date(), datetime.now().time()) + + if has_person(request.user): + person = request.user.person + + if person.is_teacher: + # Teacher + + type_ = "teacher" + super_el = person + lesson_periods_person = person.lesson_periods_as_teacher + + elif person.primary_group: + # Student + + type_ = "group" + super_el = person.primary_group + lesson_periods_person = person.lesson_periods_as_participant + + else: + # If no student or teacher, redirect to all timetables + return redirect("all_timetables") + + lesson_periods = lesson_periods_person.on_day(wanted_day) + + # Build dictionary with lessons + per_period = {} + for lesson_period in lesson_periods: + if lesson_period.period.period in per_period: + per_period[lesson_period.period.period].append(lesson_period) + else: + per_period[lesson_period.period.period] = [lesson_period] + + context["lesson_periods"] = OrderedDict(sorted(per_period.items())) + context["super"] = {"type": type_, "el": super_el} + context["type"] = type_ + context["day"] = wanted_day + context["periods"] = TimePeriod.get_times_dict() + context["smart"] = True -from .forms import LessonSubstitutionForm, SelectForm -from .models import LessonPeriod, LessonSubstitution, TimePeriod -from .tables import LessonsTable, SubstitutionsTable -from .util import CalendarWeek + context["url_prev"], context["url_next"] = get_prev_next_by_day( + wanted_day, "my_timetable_by_date" + ) + + return render(request, "chronos/my_timetable.html", context) @login_required def timetable( - request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None + request: HttpRequest, + type_: str, + pk: int, + year: Optional[int] = None, + week: Optional[int] = None, + regular: Optional[str] = None, ) -> HttpResponse: context = {} + is_smart = regular != "regular" + + if type_ == "group": + el = get_object_or_404(Group, pk=pk) + elif type_ == "teacher": + el = get_object_or_404(Person, pk=pk) + elif type_ == "room": + el = get_object_or_404(Room, pk=pk) + else: + return HttpResponseNotFound() + if year and week: wanted_week = CalendarWeek(year=year, week=week) else: + # TODO: On not used days show next week wanted_week = CalendarWeek() lesson_periods = LessonPeriod.objects.in_week(wanted_week) - - # Incrementally filter lesson periods by GET parameters - if ( - request.GET.get("group", None) - or request.GET.get("teacher", None) - or request.GET.get("room", None) - ): - lesson_periods = lesson_periods.filter_from_query(request.GET) - else: - # Redirect to a selected view if no filter provided - if request.user.person: - if request.user.person.primary_group: - return redirect( - reverse("timetable") + "?group=%d" % request.user.person.primary_group.pk - ) - elif lesson_periods.filter(lesson__teachers=request.user.person).exists(): - return redirect(reverse("timetable") + "?teacher=%d" % request.user.person.pk) + lesson_periods = lesson_periods.filter_from_type(type_, pk) # Regroup lesson periods per weekday - per_day = {} + per_period = {} for lesson_period in lesson_periods: - per_day.setdefault(lesson_period.period.weekday, {})[ - lesson_period.period.period - ] = lesson_period - - # Determine overall first and last day and period - min_max = TimePeriod.objects.aggregate( - Min("period"), Max("period"), Min("weekday"), Max("weekday") - ) + added = False + if lesson_period.period.period in per_period: + if lesson_period.period.weekday in per_period[lesson_period.period.period]: + per_period[lesson_period.period.period][ + lesson_period.period.weekday + ].append(lesson_period) + added = True + + if not added: + per_period.setdefault(lesson_period.period.period, {})[ + lesson_period.period.weekday + ] = [lesson_period] # Fill in empty lessons - for weekday_num in range(min_max.get("weekday__min", 0), min_max.get("weekday__max", 6) + 1): + for period_num in range(period_min, period_max + 1): # Fill in empty weekdays - if weekday_num not in per_day.keys(): - per_day[weekday_num] = {} + if period_num not in per_period.keys(): + per_period[period_num] = {} # Fill in empty lessons on this workday - for period_num in range(min_max.get("period__min", 1), min_max.get("period__max", 7) + 1): - if period_num not in per_day[weekday_num].keys(): - per_day[weekday_num][period_num] = None + for weekday_num in range(weekday_min_, weekday_max + 1): + if weekday_num not in per_period[period_num].keys(): + per_period[period_num][weekday_num] = [] # Order this weekday by periods - per_day[weekday_num] = OrderedDict(sorted(per_day[weekday_num].items())) + per_period[period_num] = OrderedDict(sorted(per_period[period_num].items())) - # Add a form to filter the view - select_form = SelectForm(request.GET or None) - - context["current_head"] = _("Timetable") - context["lesson_periods"] = OrderedDict(sorted(per_day.items())) + context["lesson_periods"] = OrderedDict(sorted(per_period.items())) context["periods"] = TimePeriod.get_times_dict() - context["weekdays"] = dict(TimePeriod.WEEKDAY_CHOICES) + context["weekdays"] = dict( + TimePeriod.WEEKDAY_CHOICES[weekday_min_ : weekday_max + 1] + ) + context["weekdays_short"] = dict( + TimePeriod.WEEKDAY_CHOICES_SHORT[weekday_min_ : weekday_max + 1] + ) + context["weeks"] = get_weeks_for_year(year=wanted_week.year) context["week"] = wanted_week - context["select_form"] = select_form + context["type"] = type_ + context["pk"] = pk + context["el"] = el + context["smart"] = is_smart + context["week_select"] = { + "year": wanted_week.year, + "dest": reverse("timetable", args=[type_, pk]) + } week_prev = wanted_week - 1 week_next = wanted_week + 1 - context["url_prev"] = "%s?%s" % ( - reverse("timetable_by_week", args=[week_prev.year, week_prev.week]), - request.GET.urlencode(), + + context["url_prev"] = reverse( + "timetable_by_week", args=[type_, pk, week_prev.year, week_prev.week] ) - context["url_next"] = "%s?%s" % ( - reverse("timetable_by_week", args=[week_next.year, week_next.week]), - request.GET.urlencode(), + context["url_next"] = reverse( + "timetable_by_week", args=[type_, pk, week_next.year, week_next.week] ) - return render(request, "chronos/tt_week.html", context) + return render(request, "chronos/timetable.html", context) @login_required -def lessons_day(request: HttpRequest, when: Optional[str] = None) -> HttpResponse: +def lessons_day( + request: HttpRequest, + year: Optional[int] = None, + month: Optional[int] = None, + day: Optional[int] = None, +) -> HttpResponse: context = {} - if when: - day = datetime.strptime(when, "%Y-%m-%d").date() + if day: + wanted_day = timezone.datetime(year=year, month=month, day=day).date() + wanted_day = get_next_relevant_day(wanted_day) else: - day = date.today() + wanted_day = get_next_relevant_day(timezone.now().date(), datetime.now().time()) # Get lessons - lesson_periods = LessonPeriod.objects.on_day(day) + lesson_periods = LessonPeriod.objects.on_day(wanted_day) # Build table lessons_table = LessonsTable(lesson_periods.all()) RequestConfig(request).configure(lessons_table) - context["current_head"] = _("Lessons %s") % (day) context["lessons_table"] = lessons_table - context["day"] = day + context["day"] = wanted_day context["lesson_periods"] = lesson_periods - day_prev = day - timedelta(days=1) - day_next = day + timedelta(days=1) - context["url_prev"] = reverse("lessons_day_by_date", args=[day_prev.strftime("%Y-%m-%d")]) - context["url_next"] = reverse("lessons_day_by_date", args=[day_next.strftime("%Y-%m-%d")]) + context["datepicker"] = { + "date": date_unix(wanted_day), + "dest": reverse("lessons_day") + } + + context["url_prev"], context["url_next"] = get_prev_next_by_day( + wanted_day, "lessons_day_by_date" + ) return render(request, "chronos/lessons_day.html", context) @@ -156,9 +268,11 @@ def edit_substitution(request: HttpRequest, id_: int, week: int) -> HttpResponse edit_substitution_form.save(commit=True) messages.success(request, _("The substitution has been saved.")) + + date = wanted_week[lesson_period.period.weekday] return redirect( "lessons_day_by_date", - when=wanted_week[lesson_period.period.weekday - 1].strftime("%Y-%m-%d"), + year=date.year, month=date.month, day=date.day ) context["edit_substitution_form"] = edit_substitution_form @@ -168,45 +282,48 @@ def edit_substitution(request: HttpRequest, id_: int, week: int) -> HttpResponse @admin_required def delete_substitution(request: HttpRequest, id_: int, week: int) -> HttpResponse: - context = {} - lesson_period = get_object_or_404(LessonPeriod, pk=id_) wanted_week = lesson_period.lesson.get_calendar_week(week) - LessonSubstitution.objects.filter(week=wanted_week.week, lesson_period=lesson_period).delete() + LessonSubstitution.objects.filter( + week=wanted_week.week, lesson_period=lesson_period + ).delete() messages.success(request, _("The substitution has been deleted.")) + + date = wanted_week[lesson_period.period.weekday] return redirect( "lessons_day_by_date", - when=wanted_week[lesson_period.period.weekday - 1].strftime("%Y-%m-%d"), + year=date.year, month=date.month, day=date.day ) +@login_required def substitutions( - request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None + request: HttpRequest, + year: Optional[int] = None, + month: Optional[int] = None, + day: Optional[int] = None, ) -> HttpResponse: context = {} - if week: - wanted_week = CalendarWeek(year=year, week=week) + if day: + wanted_day = timezone.datetime(year=year, month=month, day=day).date() + wanted_day = get_next_relevant_day(wanted_day) else: - wanted_week = CalendarWeek() + wanted_day = get_next_relevant_day(timezone.now().date(), datetime.now().time()) - substitutions = LessonSubstitution.objects.filter(week=wanted_week.week) + substitutions = LessonSubstitution.objects.on_day(wanted_day) - # Prepare table - substitutions_table = SubstitutionsTable(substitutions) - RequestConfig(request).configure(substitutions_table) + context["substitutions"] = substitutions + context["day"] = wanted_day + context["datepicker"] = { + "date": date_unix(wanted_day), + "dest": reverse("substitutions") + } - context["current_head"] = str(wanted_week) - context["substitutions_table"] = substitutions_table - week_prev = wanted_week - 1 - week_next = wanted_week + 1 - context["url_prev"] = "%s" % ( - reverse("substitutions_by_week", args=[week_prev.year, week_prev.week]) - ) - context["url_next"] = "%s" % ( - reverse("substitutions_by_week", args=[week_next.year, week_next.week]) + context["url_prev"], context["url_next"] = get_prev_next_by_day( + wanted_day, "substitutions_by_date" ) return render(request, "chronos/substitutions.html", context) diff --git a/poetry.lock b/poetry.lock index 1be77e4f6088b9a87a981292867ac28a64928828..28e6962c2c88ad1487dfccbf777f814cff0d214a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,6 +1,7 @@ [[package]] category = "main" description = "" +develop = true name = "aleksis" optional = false python-versions = "^3.7" @@ -9,9 +10,11 @@ version = "1.0a4.dev0" [package.dependencies] Django = "^3.0" Pillow = "^7.0" +calendarweek = "^0.4.3" colour = "^0.1.5" django-any-js = "^1.0" django-bootstrap4 = "^1.0" +django-ckeditor = "^5.8.0" django-debug-toolbar = "^2.0" django-easy-audit = "^1.2rc1" django-filter = "^2.2.0" @@ -19,6 +22,7 @@ django-hattori = "^0.2" django-image-cropping = "^1.2" django-impersonate = "^1.4" django-ipware = "^2.1" +django-js-reverse = "^0.9.1" django-maintenance-mode = "^0.14.0" django-material = "^1.6.0" django-menu-generator = "^1.0.4" @@ -27,10 +31,12 @@ django-pwa = "^1.0.6" django-sass-processor = "^0.8" django-settings-context-processor = "^0.2" django-tables2 = "^2.1" +django-templated-email = "^2.3.0" django-yarnpkg = "^6.0" django_select2 = "^7.1" django_widget_tweaks = "^1.4.5" easy-thumbnails = "^2.6" +html2text = "^2020.0.0" libsass = "^0.19.2" psycopg2 = "^2.8" python-memcached = "^1.59" @@ -53,6 +59,7 @@ extras = ["yaml", "toml", "ini"] version = "^2.0" [package.extras] +celery = ["Celery (^4.4.0)", "django-celery-results (^1.1.2)", "django-celery-beat (^1.5.0)", "django-celery-email (^3.0.0)"] ldap = ["django-auth-ldap (^2.0)"] [package.source] @@ -97,6 +104,17 @@ soupsieve = ">=1.2" html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +category = "main" +description = "Utilities for working with calendar weeks in Python and Django" +name = "calendarweek" +optional = false +python-versions = ">=3.7,<4.0" +version = "0.4.4" + +[package.extras] +django = ["Django (>=2.2,<4.0)"] + [[package]] category = "main" description = "Python package for providing Mozilla's CA Bundle." @@ -149,9 +167,6 @@ optional = false python-versions = "*" version = "5.0.6" -[package.dependencies] -six = "*" - [[package]] category = "main" description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." @@ -214,6 +229,17 @@ version = "2.2.0" [package.dependencies] Django = ">=1.8" +[[package]] +category = "main" +description = "Django admin CKEditor integration." +name = "django-ckeditor" +optional = false +python-versions = "*" +version = "5.8.0" + +[package.dependencies] +django-js-asset = ">=1.2.2" + [[package]] category = "main" description = "Django live settings with pluggable backends, including Redis." @@ -322,6 +348,25 @@ optional = false python-versions = "*" version = "2.1.0" +[[package]] +category = "main" +description = "script tag with additional attributes for django.forms.Media" +name = "django-js-asset" +optional = false +python-versions = "*" +version = "1.2.2" + +[[package]] +category = "main" +description = "Javascript url handling for Django that doesn't hurt." +name = "django-js-reverse" +optional = false +python-versions = "*" +version = "0.9.1" + +[package.dependencies] +Django = ">=1.5" + [[package]] category = "main" description = "django-maintenance-mode shows a 503 error page when maintenance-mode is on." @@ -433,6 +478,17 @@ version = "1.0.6" [package.dependencies] django = ">=1.8" +[[package]] +category = "main" +description = "Render a particular block from a template to a string." +name = "django-render-block" +optional = false +python-versions = "*" +version = "0.6" + +[package.dependencies] +django = ">=1.11" + [[package]] category = "main" description = "SASS processor to compile SCSS files into *.css, while rendering, or offline." @@ -479,6 +535,18 @@ Django = ">=1.11" [package.extras] tablib = ["tablib"] +[[package]] +category = "main" +description = "A Django oriented templated / transaction email abstraction" +name = "django-templated-email" +optional = false +python-versions = "*" +version = "2.3.0" + +[package.dependencies] +django-render-block = ">=0.5" +six = ">=1" + [[package]] category = "main" description = "Complete Two-Factor Authentication for Django" @@ -587,14 +655,21 @@ category = "main" description = "Faker is a Python package that generates fake data for you." name = "faker" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "3.0.0" +python-versions = ">=3.4" +version = "4.0.0" [package.dependencies] python-dateutil = ">=2.4" -six = ">=1.10" text-unidecode = "1.3" +[[package]] +category = "main" +description = "Turn HTML into equivalent Markdown-structured text." +name = "html2text" +optional = false +python-versions = ">=3.5" +version = "2020.1.16" + [[package]] category = "main" description = "Internationalized Domain Names in Applications (IDNA)" @@ -620,7 +695,7 @@ description = "Python version of Google's common library for parsing, formatting name = "phonenumbers" optional = false python-versions = "*" -version = "8.11.1" +version = "8.11.2" [[package]] category = "main" @@ -716,7 +791,7 @@ category = "main" description = "YAML parser and emitter for Python" name = "pyyaml" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "*" version = "5.3" [[package]] @@ -760,8 +835,8 @@ category = "main" description = "Python 2 and 3 compatibility utilities" name = "six" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "1.13.0" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.14.0" [[package]] category = "main" @@ -812,7 +887,7 @@ description = "Twilio API client and TwiML generator" name = "twilio" optional = false python-versions = "*" -version = "6.35.1" +version = "6.35.2" [package.dependencies] PyJWT = ">=1.4.2" @@ -849,7 +924,7 @@ pycryptodome = "*" six = "*" [metadata] -content-hash = "61923d9b1cbc5b40a1fc873e212a307b49d4ec4a47d37c9d56d9e76017afe3ce" +content-hash = "f2db0ef57bb256e1e7f9ae769792ce2b3288a37dccbae621f568844e2a656d0c" python-versions = "^3.7" [metadata.files] @@ -867,6 +942,10 @@ beautifulsoup4 = [ {file = "beautifulsoup4-4.8.2-py3-none-any.whl", hash = "sha256:9fbb4d6e48ecd30bcacc5b63b94088192dcda178513b2ae3c394229f8911b887"}, {file = "beautifulsoup4-4.8.2.tar.gz", hash = "sha256:05fd825eb01c290877657a56df4c6e4c311b3965bda790c613a3d6fb01a5462a"}, ] +calendarweek = [ + {file = "calendarweek-0.4.4-py3-none-any.whl", hash = "sha256:6510a42015558f140ed6677e79efbb45d8bf87ccded069db4026283eb639a256"}, + {file = "calendarweek-0.4.4.tar.gz", hash = "sha256:02f092ec54ebe162dc9f3614de6efbf3d7fb35115e8ca5d62e99d65c342f5732"}, +] certifi = [ {file = "certifi-2019.11.28-py2.py3-none-any.whl", hash = "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3"}, {file = "certifi-2019.11.28.tar.gz", hash = "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"}, @@ -909,6 +988,10 @@ django-bulk-update = [ {file = "django-bulk-update-2.2.0.tar.gz", hash = "sha256:5ab7ce8a65eac26d19143cc189c0f041d5c03b9d1b290ca240dc4f3d6aaeb337"}, {file = "django_bulk_update-2.2.0-py2.py3-none-any.whl", hash = "sha256:49a403392ae05ea872494d74fb3dfa3515f8df5c07cc277c3dc94724c0ee6985"}, ] +django-ckeditor = [ + {file = "django-ckeditor-5.8.0.tar.gz", hash = "sha256:46fc9c7346ea36183dc0cea350f98704f8b04c4722b7fe4fb18baf8ae20423fb"}, + {file = "django_ckeditor-5.8.0-py2.py3-none-any.whl", hash = "sha256:a59bab13f4481318f8a048b1b0aef5c7da768a6352dcfb9ba0e77d91fbb9462a"}, +] django-constance = [] django-debug-toolbar = [ {file = "django-debug-toolbar-2.1.tar.gz", hash = "sha256:24c157bc6c0e1648e0a6587511ecb1b007a00a354ce716950bff2de12693e7a8"}, @@ -939,6 +1022,14 @@ django-impersonate = [ django-ipware = [ {file = "django-ipware-2.1.0.tar.gz", hash = "sha256:a7c7a8fd019dbdc9c357e6e582f65034e897572fc79a7e467674efa8aef9d00b"}, ] +django-js-asset = [ + {file = "django-js-asset-1.2.2.tar.gz", hash = "sha256:c163ae80d2e0b22d8fb598047cd0dcef31f81830e127cfecae278ad574167260"}, + {file = "django_js_asset-1.2.2-py2.py3-none-any.whl", hash = "sha256:8ec12017f26eec524cab436c64ae73033368a372970af4cf42d9354fcb166bdd"}, +] +django-js-reverse = [ + {file = "django-js-reverse-0.9.1.tar.gz", hash = "sha256:2a392d169f44e30b883c30dfcfd917a14167ce8fe196c99d2385b31c90d77aa0"}, + {file = "django_js_reverse-0.9.1-py2.py3-none-any.whl", hash = "sha256:8134c2ab6307c945edfa90671ca65e85d6c1754d48566bdd6464be259cc80c30"}, +] django-maintenance-mode = [ {file = "django-maintenance-mode-0.14.0.tar.gz", hash = "sha256:f3fef1760fdcda5e0bf6c2966aadc77eea6f328580a9c751920daba927281a68"}, {file = "django_maintenance_mode-0.14.0-py2-none-any.whl", hash = "sha256:b4cc24a469ed10897826a28f05d64e6166a58d130e4940ac124ce198cd4cc778"}, @@ -973,6 +1064,9 @@ django-pwa = [ {file = "django-pwa-1.0.6.tar.gz", hash = "sha256:b3f1ad0c5241fae4c7505423540de4db93077d7c88416ff6d2af545ffe209f34"}, {file = "django_pwa-1.0.6-py3-none-any.whl", hash = "sha256:9306105fcb637ae16fea6527be4b147d45fd53db85efb1d4f61dfea6bf793e56"}, ] +django-render-block = [ + {file = "django_render_block-0.6-py2.py3-none-any.whl", hash = "sha256:95c7dc9610378a10e0c4a10d8364ec7307210889afccd6a67a6aaa0fd599bd4d"}, +] django-sass-processor = [ {file = "django-sass-processor-0.8.tar.gz", hash = "sha256:e039551994feaaba6fcf880412b25a772dd313162a34cbb4289814988cfae340"}, ] @@ -987,6 +1081,9 @@ django-tables2 = [ {file = "django-tables2-2.2.1.tar.gz", hash = "sha256:0d9b17f5c030ba1b5fcaeb206d8397bf58f1fdfc6beaf56e7874841b8647aa94"}, {file = "django_tables2-2.2.1-py2.py3-none-any.whl", hash = "sha256:6afa0496695e15b332e98537265d09fe01a55b28c75a85323d8e6b0dc2350280"}, ] +django-templated-email = [ + {file = "django-templated-email-2.3.0.tar.gz", hash = "sha256:536c4e5ae099eabfb9aab36087d4d7799948c654e73da55a744213d086d5bb33"}, +] django-two-factor-auth = [ {file = "django-two-factor-auth-1.10.0.tar.gz", hash = "sha256:3c3af3cd747462be18e7494c4068a2bdc606d7a2d2b2914f8d4590fc80995a71"}, {file = "django_two_factor_auth-1.10.0-py2.py3-none-any.whl", hash = "sha256:0945260fa84e4522d8fa951c35e401616579fd8564938441614399dc588a1c1f"}, @@ -1006,8 +1103,12 @@ easy-thumbnails = [ {file = "easy-thumbnails-2.7.tar.gz", hash = "sha256:e4e7a0dd4001f56bfd4058428f2c91eafe27d33ef3b8b33ac4e013b159b9ff91"}, ] faker = [ - {file = "Faker-3.0.0-py2.py3-none-any.whl", hash = "sha256:202ad3b2ec16ae7c51c02904fb838831f8d2899e61bf18db1e91a5a582feab11"}, - {file = "Faker-3.0.0.tar.gz", hash = "sha256:92c84a10bec81217d9cb554ee12b3838c8986ce0b5d45f72f769da22e4bb5432"}, + {file = "Faker-4.0.0-py3-none-any.whl", hash = "sha256:047d4d1791bfb3756264da670d99df13d799bb36e7d88774b1585a82d05dbaec"}, + {file = "Faker-4.0.0.tar.gz", hash = "sha256:1b1a58961683b30c574520d0c739c4443e0ef6a185c04382e8cc888273dbebed"}, +] +html2text = [ + {file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"}, + {file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"}, ] idna = [ {file = "idna-2.8-py2.py3-none-any.whl", hash = "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"}, @@ -1032,8 +1133,8 @@ libsass = [ {file = "libsass-0.19.4.tar.gz", hash = "sha256:8b5b6d1a7c4ea1d954e0982b04474cc076286493f6af2d0a13c2e950fbe0be95"}, ] phonenumbers = [ - {file = "phonenumbers-8.11.1-py2.py3-none-any.whl", hash = "sha256:aaa19bc1f2c7efbf7a94be33558e0c5b71620377c9271692d3e314c558962460"}, - {file = "phonenumbers-8.11.1.tar.gz", hash = "sha256:239507184ee5b1b83557005af1d5fcce70f83ae18f5dff45b94a67226db10d63"}, + {file = "phonenumbers-8.11.2-py2.py3-none-any.whl", hash = "sha256:796ba25c3064727ca0b8edf7a8ef5ef247c6da37aee498562e6e0ed46970a57f"}, + {file = "phonenumbers-8.11.2.tar.gz", hash = "sha256:a22d3b14c7f18af9be7c4ade92285035f621c6a17b75352dc9c2e5d146aee348"}, ] pillow = [ {file = "Pillow-7.0.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00"}, @@ -1154,8 +1255,8 @@ requests = [ {file = "requests-2.22.0.tar.gz", hash = "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4"}, ] six = [ - {file = "six-1.13.0-py2.py3-none-any.whl", hash = "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd"}, - {file = "six-1.13.0.tar.gz", hash = "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"}, + {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, + {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, ] soupsieve = [ {file = "soupsieve-1.9.5-py2.py3-none-any.whl", hash = "sha256:bdb0d917b03a1369ce964056fc195cfdff8819c40de04695a80bc813c3cfa1f5"}, @@ -1179,7 +1280,7 @@ tqdm = [ {file = "tqdm-4.41.1.tar.gz", hash = "sha256:4789ccbb6fc122b5a6a85d512e4e41fc5acad77216533a6f2b8ce51e0f265c23"}, ] twilio = [ - {file = "twilio-6.35.1.tar.gz", hash = "sha256:c784e55d150ebeb2ba837afbab7168edfb91db57e77a9da49f2a1892688a1930"}, + {file = "twilio-6.35.2.tar.gz", hash = "sha256:a086443642c0e1f13c8f8f087b426ca81ec883efbe496d8279180a49bb9287bc"}, ] urllib3 = [ {file = "urllib3-1.25.7-py2.py3-none-any.whl", hash = "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293"}, diff --git a/pyproject.toml b/pyproject.toml index 9065066e24063dde8042828c88bc814afc7fa639..45e06f206c94d9da5035321b0dd4f5e4cc02044e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.7" +calendarweek = "^0.4.4" AlekSIS = { path = "../../.." } [build-system]