diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5389b8747a7f76495756ad47fda11d9e410319cf..902a10933a40914e7119fec6733cbf139cd7f1d2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,15 +6,35 @@ All notable changes to this project will be documented in this file. The format is based on `Keep a Changelog`_, and this project adheres to `Semantic Versioning`_. -Unreleased ----------- +`4.0.0.dev8`_ - 2024-10-17 +-------------------------- + +Upgrade notice +~~~~~~~~~~~~~~ + +If you're updating from 3.x, there is a migration path to use. +Therefore, please install ``AlekSIS-App-Lesrooster`` which now +includes parts of the legacy Chronos and the migration path. Added ~~~~~ * New timetable interface based on calendar system. +* Dialog for fast changing lessons and creating substitutions in the calendar. * [Dev] LessonEvent and SupervisionEvent basing on calendar system. +Changed +~~~~~~~ + +* Substitution table was updated to new frontend. +* Substitution PDF was updated to new timetable interface and + therefore has a slightly different look. + +Removed +~~~~~~~ + +* Legacy timetable frontend. + `3.0.2`_ - 2023-09-10 --------------------- @@ -411,3 +431,4 @@ Fixed .. _3.0: https://edugit.org/AlekSIS/Official/AlekSIS-App-Chronos/-/tags/3.0 .. _3.0.1: https://edugit.org/AlekSIS/Official/AlekSIS-App-Chronos/-/tags/3.0.1 .. _3.0.2: https://edugit.org/AlekSIS/Official/AlekSIS-App-Chronos/-/tags/3.0.2 +.. _4.0.0.dev8: https://edugit.org/AlekSIS/Official/AlekSIS-App-Chronos/-/tags/4.0.0.dev8 diff --git a/aleksis/apps/chronos/admin.py b/aleksis/apps/chronos/admin.py index 9da4770d092663c4baa2c5a9e770677305ea0121..2ed048d96d6cdb40a440e22a4a3fcb421b330013 100644 --- a/aleksis/apps/chronos/admin.py +++ b/aleksis/apps/chronos/admin.py @@ -1,218 +1,9 @@ # noqa from django.contrib import admin -from django.utils.html import format_html - -from guardian.admin import GuardedModelAdmin - -from aleksis.core.models import Room from .models import ( - Absence, - AbsenceReason, AutomaticPlan, - Break, - Event, - ExtraLesson, - Holiday, - Lesson, - LessonPeriod, - LessonSubstitution, - Subject, - Supervision, - SupervisionArea, - SupervisionSubstitution, - TimePeriod, - ValidityRange, ) -from .util.format import format_date_period, format_m2m - - -def colour_badge(fg: str, bg: str, val: str): - html = """ - <div style=" - color: {}; - background-color: {}; - padding-top: 3px; - padding-bottom: 4px; - text-align: center; - border-radius: 3px; - ">{}</span> - """ - return format_html(html, fg, bg, val) - - -class AbsenceReasonAdmin(admin.ModelAdmin): - list_display = ("short_name", "name") - list_display_links = ("short_name", "name") - - -admin.site.register(AbsenceReason, AbsenceReasonAdmin) - - -class AbsenceAdmin(admin.ModelAdmin): - def start(self, obj): - return format_date_period(obj.date_start, obj.period_from) - - def end(self, obj): - return format_date_period(obj.date_end, obj.period_to) - - list_display = ("__str__", "reason", "start", "end") - - -admin.site.register(Absence, AbsenceAdmin) - - -class SupervisionInline(admin.TabularInline): - model = Supervision - - -class BreakAdmin(admin.ModelAdmin): - list_display = ("__str__", "after_period", "before_period") - inlines = [SupervisionInline] - - -admin.site.register(Break, BreakAdmin) - - -class EventAdmin(admin.ModelAdmin): - def start(self, obj): - return format_date_period(obj.date_start, obj.period_from) - - def end(self, obj): - return format_date_period(obj.date_end, obj.period_to) - - def _groups(self, obj): - return format_m2m(obj.groups) - - def _teachers(self, obj): - return format_m2m(obj.teachers) - - def _rooms(self, obj): - return format_m2m(obj.rooms) - - filter_horizontal = ("groups", "teachers", "rooms") - list_display = ("__str__", "_groups", "_teachers", "_rooms", "start", "end") - - -admin.site.register(Event, EventAdmin) - - -class ExtraLessonAdmin(admin.ModelAdmin): - def _groups(self, obj): - return format_m2m(obj.groups) - - def _teachers(self, obj): - return format_m2m(obj.teachers) - - list_display = ("week", "period", "subject", "_groups", "_teachers", "room") - - -admin.site.register(ExtraLesson, ExtraLessonAdmin) - - -class HolidayAdmin(admin.ModelAdmin): - list_display = ("title", "date_start", "date_end") - - -admin.site.register(Holiday, HolidayAdmin) - - -class LessonPeriodInline(admin.TabularInline): - model = LessonPeriod - - -class LessonSubstitutionAdmin(admin.ModelAdmin): - list_display = ("lesson_period", "week", "date") - list_display_links = ("lesson_period", "week", "date") - filter_horizontal = ("teachers",) - - -admin.site.register(LessonSubstitution, LessonSubstitutionAdmin) - - -class LessonAdmin(admin.ModelAdmin): - def _groups(self, obj): - return format_m2m(obj.groups) - - def _teachers(self, obj): - return format_m2m(obj.teachers) - - filter_horizontal = ["teachers", "groups"] - inlines = [LessonPeriodInline] - list_filter = ("subject", "groups", "groups__parent_groups", "teachers") - list_display = ("_groups", "subject", "_teachers") - - -admin.site.register(Lesson, LessonAdmin) - - -class RoomAdmin(GuardedModelAdmin): - list_display = ("short_name", "name") - list_display_links = ("short_name", "name") - - -admin.site.register(Room, RoomAdmin) - - -class SubjectAdmin(admin.ModelAdmin): - def _colour(self, obj): - return colour_badge( - obj.colour_fg, - obj.colour_bg, - obj.short_name, - ) - - list_display = ("short_name", "name", "_colour") - list_display_links = ("short_name", "name") - - -admin.site.register(Subject, SubjectAdmin) - - -class SupervisionAreaAdmin(admin.ModelAdmin): - def _colour(self, obj): - return colour_badge( - obj.colour_fg, - obj.colour_bg, - obj.short_name, - ) - - list_display = ("short_name", "name", "_colour") - list_display_links = ("short_name", "name") - inlines = [SupervisionInline] - - -admin.site.register(SupervisionArea, SupervisionAreaAdmin) - - -class SupervisionSubstitutionAdmin(admin.ModelAdmin): - list_display = ("supervision", "date") - - -admin.site.register(SupervisionSubstitution, SupervisionSubstitutionAdmin) - - -class SupervisionAdmin(admin.ModelAdmin): - list_display = ("break_item", "area", "teacher") - - -admin.site.register(Supervision, SupervisionAdmin) - - -class TimePeriodAdmin(admin.ModelAdmin): - list_display = ("weekday", "period", "time_start", "time_end") - list_display_links = ("weekday", "period") - - -admin.site.register(TimePeriod, TimePeriodAdmin) - - -class ValidityRangeAdmin(admin.ModelAdmin): - list_display = ("__str__", "date_start", "date_end") - list_display_links = ("__str__", "date_start", "date_end") - - -admin.site.register(ValidityRange, ValidityRangeAdmin) admin.site.register(AutomaticPlan) diff --git a/aleksis/apps/chronos/apps.py b/aleksis/apps/chronos/apps.py index 377cefd6c1a90969c1c6c650da75fad34d3f2ae5..8eeee15eb1aebf7170021a338a22ca7c0cc5db68 100644 --- a/aleksis/apps/chronos/apps.py +++ b/aleksis/apps/chronos/apps.py @@ -1,6 +1,3 @@ -from typing import Any, Optional - -import django.apps from django.db import transaction from reversion.signals import post_revision_commit @@ -37,75 +34,3 @@ class ChronosConfig(AppConfig): transaction.on_commit(lambda: handle_new_revision.delay(revision.pk)) post_revision_commit.connect(_handle_post_revision_commit, weak=False) - - def _ensure_notification_task(self): - """Update or create the task for sending notifications.""" - from django.conf import settings # noqa - - from celery import schedules - from django_celery_beat.models import CrontabSchedule, PeriodicTask - - from aleksis.core.util.core_helpers import get_site_preferences - - time_for_sending = get_site_preferences()["chronos__time_for_sending_notifications"] - active = get_site_preferences()["chronos__send_notifications_site"] - - if active: - schedule = schedules.crontab( - minute=str(time_for_sending.minute), hour=str(time_for_sending.hour) - ) - schedule = CrontabSchedule.from_schedule(schedule) - schedule.timezone = settings.TIME_ZONE - schedule.save() - - possible_periodic_tasks = PeriodicTask.objects.filter( - task="chronos_send_notifications_for_next_day" - ) - - if not active: - possible_periodic_tasks.delete() - - elif possible_periodic_tasks.exists(): - task = possible_periodic_tasks[0] - for d_task in possible_periodic_tasks: - if d_task != task: - d_task.delete() - - if task.crontab != schedule: - task.interval, task.solar, task.clocked = None, None, None - task.crontab = schedule - task.save() - - elif active: - PeriodicTask.objects.get_or_create( - task="chronos_send_notifications_for_next_day", - crontab=schedule, - defaults=dict(name="Send notifications for next day (automatic schedule)"), - ) - - def preference_updated( - self, - sender: Any, - section: Optional[str] = None, - name: Optional[str] = None, - old_value: Optional[Any] = None, - new_value: Optional[Any] = None, - **kwargs, - ) -> None: - if section == "chronos" and name in ( - "send_notifications_site", - "time_for_sending_notifications", - ): - self._ensure_notification_task() - - def post_migrate( - self, - app_config: django.apps.AppConfig, - verbosity: int, - interactive: bool, - using: str, - **kwargs, - ) -> None: - super().post_migrate(app_config, verbosity, interactive, using, **kwargs) - # Ensure that the notification task is created after setting up AlekSIS - self._ensure_notification_task() diff --git a/aleksis/apps/chronos/frontend/components/NoTimetableCard.vue b/aleksis/apps/chronos/frontend/components/NoTimetableCard.vue index 84cf21c8498e54de13efd2056117fe478212aae1..f11b2ed2ebc60b72895f3e682c4b66cc024fddad 100644 --- a/aleksis/apps/chronos/frontend/components/NoTimetableCard.vue +++ b/aleksis/apps/chronos/frontend/components/NoTimetableCard.vue @@ -1,6 +1,9 @@ <script> +import Mascot from "aleksis.core/components/generic/mascot/Mascot.vue"; + export default { name: "NoTimetableCard", + components: { Mascot }, props: { titleKey: { type: String, @@ -22,7 +25,7 @@ export default { v-bind="$attrs" > <div class="text-center"> - <v-icon color="secondary" size="60" class="mb-4"> mdi-grid-off </v-icon> + <mascot type="timetable" size="60" class="mb-4" /> <div class="text-h5 grey--text text--darken-2 mb-2"> {{ $t(titleKey) }} </div> diff --git a/aleksis/apps/chronos/frontend/components/Substitutions.vue b/aleksis/apps/chronos/frontend/components/Substitutions.vue new file mode 100644 index 0000000000000000000000000000000000000000..93c6f6dcab78d79b8b24f0cfd39744bb1ee26502 --- /dev/null +++ b/aleksis/apps/chronos/frontend/components/Substitutions.vue @@ -0,0 +1,236 @@ +<script setup> +import CRUDList from "aleksis.core/components/generic/CRUDList.vue"; +import PrimaryActionButton from "aleksis.core/components/generic/buttons/PrimaryActionButton.vue"; +import PersonChip from "aleksis.core/components/person/PersonChip.vue"; +import GroupChip from "aleksis.core/components/group/GroupChip.vue"; +import DateSelectFooter from "aleksis.core/components/generic/DateSelectFooter.vue"; +</script> + +<template> + <c-r-u-d-list + :gql-query="query" + :gql-additional-query-args="{ date: date }" + :get-gql-data="prepareList" + :headers="headers" + :disable-sort="true" + :item-class="itemColor" + :show-select="false" + :enable-create="false" + :enable-edit="false" + > + <template #title> + <v-row class="d-flex align-center pt-2 pa-2"> + <v-card-title class="text-h4"> + {{ $d(new Date(date), "dateWithWeekday") }} + </v-card-title> + <v-spacer /> + <primary-action-button + class="mr-4" + i18n-key="chronos.substitutions.print" + icon-text="$print" + :to="{ + name: 'chronos.printSubstitutionsForDate', + params: { + date: date, + }, + }" + /> + </v-row> + <v-card-text> + <div v-if="affectedTeachers.length > 0"> + <strong> + {{ $t("chronos.substitutions.affected_teachers") }}: + </strong> + <person-chip + v-for="teacher in affectedTeachers" + :key="teacher.id" + class="ma-1" + :person="teacher" + :to="{ + name: 'chronos.timetableWithId', + params: { + type: 'person', + id: teacher.id, + }, + }" + /> + </div> + <div v-if="affectedGroups.length > 0"> + <strong> {{ $t("chronos.substitutions.affected_groups") }}: </strong> + <!-- TODO: Link to group-timetable as well --> + <!-- as soon as it becomes possible to resolve a --> + <!-- group-timetable from the lesson-event group too. --> + <group-chip + v-for="group in affectedGroups" + class="ma-1" + :key="group.id" + :group="group" + format="short" + /> + </div> + </v-card-text> + </template> + <!-- TODO: Extract strike -> bold || normal pattern into own --> + <!-- component and reuse? --> + <template #groups="{ item: { oldGroups, newGroups } }"> + <span v-if="newGroups.length > 0"> + <span class="strike-through" v-for="g in oldGroups" :key="g.id">{{ + g.shortName + }}</span> + <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> + <span> → </span> + <strong v-for="g in newGroups" :key="g.id">{{ g.shortName }}</strong> + </span> + <span v-else v-for="g in oldGroups" :key="g.id">{{ g.shortName }}</span> + </template> + <template #time="{ item: { startSlot, endSlot, startTime, endTime } }"> + <span v-if="startSlot && endSlot && startSlot === endSlot"> + {{ startSlot }}. + </span> + <span v-else-if="startSlot && endSlot"> + {{ startSlot }}.–{{ endSlot }}. + </span> + <span v-else-if="startTime && endTime"> + {{ $d(new Date(startTime), "shortTime") }} + – + {{ $d(new Date(endTime), "shortTime") }} + </span> + <span v-else>{{ $t("chronos.substitutions.all_day") }}</span> + </template> + <template #teachers="{ item: { oldTeachers, newTeachers } }"> + <span v-if="newTeachers.length > 0"> + <span class="strike-through" v-for="t in oldTeachers" :key="t.id"> + {{ t.shortName || t.fullName }} + </span> + <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> + <span> → </span> + <strong v-for="t in newTeachers" :key="t.id"> + {{ t.shortName || t.fullName }} + </strong> + </span> + <span v-else v-for="t in oldTeachers" :key="t.id"> + {{ t.shortName || t.fullName }} + </span> + </template> + <template #subject="{ item: { oldSubject, newSubject } }"> + <span v-if="oldSubject === 'SUPERVISION'"> + {{ $t("chronos.substitutions.supervision") }} + </span> + <span v-else-if="newSubject"> + <span class="strike-through">{{ oldSubject }}</span> + <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> + <span> → </span> + <strong>{{ newSubject }}</strong> + </span> + <span v-else>{{ oldSubject }}</span> + </template> + <template #rooms="{ item: { oldRooms, newRooms } }"> + <span v-if="newRooms.length > 0"> + <span class="strike-through" v-for="r in oldRooms" :key="r.id">{{ + r.shortName || r.name + }}</span> + <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> + <span> → </span> + <strong v-for="r in newRooms" :key="r.id">{{ + r.shortName || r.name + }}</strong> + </span> + <span v-else v-for="r in oldRooms" :key="r.id">{{ + r.shortName || r.name + }}</span> + </template> + <template #notes="{ item: { cancelled, notes } }"> + <v-chip v-if="cancelled" color="green" text-color="white"> + {{ $t("chronos.substitutions.cancelled") }} + </v-chip> + {{ notes }} + </template> + <template #no-data> + {{ $t("chronos.substitutions.no_substitutions") }} + </template> + <template #footer> + <!-- TODO: Skip over unneeded days; eg. weekends. --> + <date-select-footer + :value="date" + @input="gotoDate" + @prev="gotoDate(DateTime.fromISO(date).minus({ days: 1 }).toISODate())" + @next="gotoDate(DateTime.fromISO(date).plus({ days: 1 }).toISODate())" + /> + </template> + </c-r-u-d-list> +</template> + +<script> +import { substitutionsForDate } from "./substitutions.graphql"; +import { DateTime } from "luxon"; + +export default { + name: "Substitutions", + props: { + date: { + type: String, + required: true, + }, + }, + data() { + return { + query: substitutionsForDate, + affectedTeachers: [], + affectedGroups: [], + headers: [ + { + text: this.$t("chronos.substitutions.groups"), + value: "groups", + }, + { + text: this.$t("chronos.substitutions.time"), + value: "time", + }, + { + text: this.$t("chronos.substitutions.teachers"), + value: "teachers", + }, + { + text: this.$t("chronos.substitutions.subject"), + value: "subject", + }, + { + text: this.$t("chronos.substitutions.rooms"), + value: "rooms", + }, + { + text: this.$t("chronos.substitutions.notes"), + value: "notes", + }, + ], + }; + }, + methods: { + prepareList(data) { + this.affectedTeachers = data.affectedTeachers; + this.affectedGroups = data.affectedGroups; + return data.substitutions; + }, + itemColor(item) { + return item.cancelled ? "green-text" : ""; + }, + gotoDate(date) { + this.$router.push({ + name: "chronos.listSubstitutionsForDate", + params: { + date: date, + }, + }); + }, + }, +}; +</script> + +<style> +.green-text { + color: green; +} +.strike-through { + text-decoration: line-through; +} +</style> diff --git a/aleksis/apps/chronos/frontend/components/Timetable.vue b/aleksis/apps/chronos/frontend/components/Timetable.vue index 5c0f66811aa7fc88098863b7fb71f53975e272b0..78947059bd524bcd829077d790dec3581bfe42a2 100644 --- a/aleksis/apps/chronos/frontend/components/Timetable.vue +++ b/aleksis/apps/chronos/frontend/components/Timetable.vue @@ -1,9 +1,84 @@ <script setup> import TimetableWrapper from "./TimetableWrapper.vue"; </script> + <script> +import { DateTime } from "luxon"; + export default { name: "Timetable", + data() { + return { + calendarFocus: "", + calendarType: "week", + initialRouteFocusSet: false, + }; + }, + methods: { + setCalendarFocus(val) { + this.calendarFocus = val; + }, + setCalendarType(val) { + this.calendarType = val; + }, + setInnerFocusAndType() { + if (this.$route.name === "chronos.timetableWithId") { + this.$refs.calendarWithControls.setCalendarFocus( + DateTime.now().toISODate(), + ); + this.$refs.calendarWithControls.setCalendarType( + this.$vuetify.breakpoint.mdAndDown ? "day" : "week", + ); + } else { + this.initialRouteFocusSet = true; + this.$refs.calendarWithControls.setCalendarFocus( + [ + this.$route.params.year, + this.$route.params.month, + this.$route.params.day, + ].join("-"), + ); + this.$refs.calendarWithControls.setCalendarType( + this.$route.params.view, + ); + } + }, + }, + watch: { + calendarFocus(newValue, oldValue) { + // Do not redirect on first page load + if (oldValue === "") return; + + // Do not redirect when calendar focus was just set with route param values + if (this.initialRouteFocusSet) { + this.initialRouteFocusSet = false; + return; + } + + const [year, month, day] = newValue.split("-"); + this.$router.push({ + name: "chronos.timetableWithIdAndParams", + params: { + view: this.calendarType, + year, + month, + day, + }, + }); + }, + calendarType(newValue) { + const [year, month, day] = this.calendarFocus.split("-"); + this.$router.push({ + name: "chronos.timetableWithIdAndParams", + params: { + view: newValue, + year, + month, + day, + }, + }); + }, + }, }; </script> @@ -17,6 +92,10 @@ export default { { name: 'holidays' }, ]" :params="{ type: selected.type, id: selected.objId }" + ref="calendarWithControls" + @changeCalendarFocus="setCalendarFocus" + @changeCalendarType="setCalendarType" + @calendarReady="setInnerFocusAndType" /> </template> </timetable-wrapper> diff --git a/aleksis/apps/chronos/frontend/components/substitutions.graphql b/aleksis/apps/chronos/frontend/components/substitutions.graphql new file mode 100644 index 0000000000000000000000000000000000000000..f747c6b56552193374115245f552b14bd6584df8 --- /dev/null +++ b/aleksis/apps/chronos/frontend/components/substitutions.graphql @@ -0,0 +1,45 @@ +query substitutionsForDate($date: Date!) { + items: substitutionsForDate(date: $date) { + affectedTeachers { + id + shortName + fullName + } + affectedGroups { + id + shortName + } + substitutions { + oldGroups { + shortName + } + newGroups { + shortName + } + startSlot + endSlot + startTime + endTime + oldTeachers { + shortName + fullName + } + newTeachers { + shortName + fullName + } + oldSubject + newSubject + oldRooms { + shortName + name + } + newRooms { + shortName + name + } + cancelled + notes + } + } +} diff --git a/aleksis/apps/chronos/frontend/index.js b/aleksis/apps/chronos/frontend/index.js index 4749ac593afcd244d9d4f06b992ef6dea99c0225..1893e4f184e67177fb6f3d0e9d2f8d661c643d55 100644 --- a/aleksis/apps/chronos/frontend/index.js +++ b/aleksis/apps/chronos/frontend/index.js @@ -1,5 +1,7 @@ import { hasPersonValidator } from "aleksis.core/routeValidators"; import Timetable from "./components/Timetable.vue"; +import Substitutions from "./components/Substitutions.vue"; +import { DateTime } from "luxon"; export default { meta: { @@ -7,7 +9,7 @@ export default { titleKey: "chronos.menu_title", icon: "mdi-school-outline", iconActive: "mdi-school", - validators: [hasPersonValidator], + permission: "chronos.view_menu_rule", }, children: [ { @@ -31,11 +33,22 @@ export default { permission: "chronos.view_timetable_overview_rule", fullWidth: true, }, + children: [ + { + path: ":view(month|week|day)/:year(\\d\\d\\d\\d)/:month(\\d\\d)/:day(\\d\\d)/", + component: Timetable, + name: "chronos.timetableWithIdAndParams", + meta: { + permission: "chronos.view_timetable_overview_rule", + fullWidth: true, + }, + }, + ], }, { path: "substitutions/print/", component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), - name: "chronos.substitutions", + name: "chronos.printSubstitutions", props: { byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, }, @@ -43,10 +56,42 @@ export default { { path: "substitutions/print/:date/", component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"), - name: "chronos.substitutionsByDate", + name: "chronos.printSubstitutionsForDate", props: { byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true, }, }, + { + path: "substitutions/", + props: true, + component: Substitutions, + name: "chronos.listSubstitutionsForDateLanding", + redirect: () => { + return { + name: "chronos.listSubstitutionsForDate", + params: { + date: DateTime.now().toISODate(), + }, + }; + }, + meta: { + inMenu: true, + titleKey: "chronos.substitutions.menu_title", + toolbarTitle: "chronos.substitutions.menu_title", + icon: "mdi-list-status", + permission: "chronos.view_substitutions_rule", + }, + children: [ + { + path: ":date/", + component: Substitutions, + name: "chronos.listSubstitutionsForDate", + meta: { + toolbarTitle: "chronos.substitutions.menu_title", + permission: "chronos.view_substitutions_rule", + }, + }, + ], + }, ], }; diff --git a/aleksis/apps/chronos/frontend/messages/de.json b/aleksis/apps/chronos/frontend/messages/de.json index f729d415edb8336cdddb7b5abe853bebc3cad5a4..a7c50a848e25f14b0cc24a36f0cf80df2e0efd94 100644 --- a/aleksis/apps/chronos/frontend/messages/de.json +++ b/aleksis/apps/chronos/frontend/messages/de.json @@ -22,7 +22,20 @@ }, "menu_title": "Stundenpläne", "substitutions": { - "menu_title": "Vertretungen" + "menu_title": "Vertretungen", + "print": "Drucken", + "groups": "Gruppen", + "time": "Zeit", + "teachers": "Lehrer", + "subject": "Fach", + "rooms": "Räume", + "notes": "Notizen", + "supervision": "Aufsicht", + "cancelled": "Entfällt", + "affected_teachers": "Betroffene Lehrer", + "affected_groups": "Betroffene Gruppen", + "all_day": "Ganztägig", + "no_substitutions": "Keine Vertretungen" }, "supervisions": { "menu_title_daily": "Aufsichten", diff --git a/aleksis/apps/chronos/frontend/messages/en.json b/aleksis/apps/chronos/frontend/messages/en.json index e38a4e03df2f494032be278d85d1c4fcec880984..7ca0d3ee9fcc7774032eb2c2fa13950c361d898f 100644 --- a/aleksis/apps/chronos/frontend/messages/en.json +++ b/aleksis/apps/chronos/frontend/messages/en.json @@ -23,7 +23,20 @@ "menu_title_daily": "Daily lessons" }, "substitutions": { - "menu_title": "Substitutions" + "menu_title": "Substitutions", + "print": "Print", + "groups": "Groups", + "time": "Time", + "teachers": "Teachers", + "subject": "Subject", + "rooms": "Rooms", + "notes": "Notes", + "supervision": "Supervision", + "cancelled": "Cancelled", + "affected_teachers": "Affected teachers", + "affected_groups": "Affected groups", + "all_day": "All day", + "no_substitutions": "No substitutions" }, "supervisions": { "title": "Supervision", diff --git a/aleksis/apps/chronos/locale/ar/LC_MESSAGES/django.po b/aleksis/apps/chronos/locale/ar/LC_MESSAGES/django.po index 7bfa932f4f871a7787fc4f238c190419a4f07021..67e0bba4ca0e30d6ab451ba309c91fd6048dada5 100644 --- a/aleksis/apps/chronos/locale/ar/LC_MESSAGES/django.po +++ b/aleksis/apps/chronos/locale/ar/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-18 14:23+0200\n" +"POT-Creation-Date: 2024-10-17 10:48+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -18,418 +18,133 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" -#: aleksis/apps/chronos/mixins.py:25 -msgid "Linked validity range" -msgstr "" - -#: aleksis/apps/chronos/model_extensions.py:145 +#: aleksis/apps/chronos/model_extensions.py:9 msgid "Can view group timetable" msgstr "" -#: aleksis/apps/chronos/model_extensions.py:149 +#: aleksis/apps/chronos/model_extensions.py:13 msgid "Can view person timetable" msgstr "" -#: aleksis/apps/chronos/models.py:87 -msgid "School term" -msgstr "" - -#: aleksis/apps/chronos/models.py:90 aleksis/apps/chronos/models.py:626 -#: aleksis/apps/chronos/models.py:1310 -msgid "Name" -msgstr "" - -#: aleksis/apps/chronos/models.py:92 aleksis/apps/chronos/models.py:676 -#: aleksis/apps/chronos/models.py:747 aleksis/apps/chronos/models.py:979 -msgid "Start date" -msgstr "" - -#: aleksis/apps/chronos/models.py:93 aleksis/apps/chronos/models.py:677 -#: aleksis/apps/chronos/models.py:748 aleksis/apps/chronos/models.py:980 -msgid "End date" +#: aleksis/apps/chronos/models.py:48 +msgid "Number of days shown in the plan" msgstr "" -#: aleksis/apps/chronos/models.py:112 -msgid "The start date must be earlier than the end date." +#: aleksis/apps/chronos/models.py:52 +msgid "Show header box" msgstr "" -#: aleksis/apps/chronos/models.py:118 -msgid "The validity range must be within the school term." +#: aleksis/apps/chronos/models.py:53 aleksis/apps/chronos/preferences.py:79 +msgid "The header box shows affected teachers/groups." msgstr "" -#: aleksis/apps/chronos/models.py:125 -msgid "There is already a validity range for this time or a part of this time." +#: aleksis/apps/chronos/models.py:60 +msgid "Revision which triggered the last update" msgstr "" #: aleksis/apps/chronos/models.py:132 -msgid "Validity range" +msgid "Automatic plan" msgstr "" #: aleksis/apps/chronos/models.py:133 -msgid "Validity ranges" +msgid "Automatic plans" msgstr "" -#: aleksis/apps/chronos/models.py:149 -msgid "Week day" +#: aleksis/apps/chronos/models.py:147 +msgid "Can view all room timetables" msgstr "" -#: aleksis/apps/chronos/models.py:151 -msgid "Number of period" +#: aleksis/apps/chronos/models.py:148 +msgid "Can view all group timetables" msgstr "" -#: aleksis/apps/chronos/models.py:153 -msgid "Start time" +#: aleksis/apps/chronos/models.py:149 +msgid "Can view all person timetables" msgstr "" -#: aleksis/apps/chronos/models.py:154 -msgid "End time" +#: aleksis/apps/chronos/models.py:150 +msgid "Can view timetable overview" msgstr "" -#: aleksis/apps/chronos/models.py:324 aleksis/apps/chronos/models.py:496 -#: aleksis/apps/chronos/models.py:1122 -msgid "Time period" +#: aleksis/apps/chronos/models.py:151 +msgid "Can view substitutions table" msgstr "" -#: aleksis/apps/chronos/models.py:325 -msgid "Time periods" +#: aleksis/apps/chronos/models.py:159 +msgid "Lessons" msgstr "" -#: aleksis/apps/chronos/models.py:329 aleksis/apps/chronos/models.py:625 -#: aleksis/apps/chronos/models.py:793 aleksis/apps/chronos/models.py:810 -msgid "Short name" +#: aleksis/apps/chronos/models.py:163 +msgid "Name" msgstr "" -#: aleksis/apps/chronos/models.py:330 aleksis/apps/chronos/models.py:794 -#: aleksis/apps/chronos/models.py:811 -msgid "Long name" +#: aleksis/apps/chronos/models.py:166 +msgid "Start slot number" msgstr "" -#: aleksis/apps/chronos/models.py:332 -msgid "Foreground colour" +#: aleksis/apps/chronos/models.py:169 +msgid "End slot number" msgstr "" -#: aleksis/apps/chronos/models.py:333 -msgid "Background colour" +#: aleksis/apps/chronos/models.py:173 +msgid "Course" msgstr "" -#: aleksis/apps/chronos/models.py:340 aleksis/apps/chronos/models.py:349 -#: aleksis/apps/chronos/models.py:416 aleksis/apps/chronos/models.py:1129 -#: aleksis/apps/chronos/models.py:1345 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 -msgid "Subject" +#: aleksis/apps/chronos/models.py:179 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 +msgid "Groups" msgstr "" -#: aleksis/apps/chronos/models.py:341 -msgid "Subjects" +#: aleksis/apps/chronos/models.py:185 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 +msgid "Rooms" msgstr "" -#: aleksis/apps/chronos/models.py:352 aleksis/apps/chronos/models.py:422 -#: aleksis/apps/chronos/models.py:998 aleksis/apps/chronos/models.py:1137 -#: aleksis/apps/chronos/models.py:1338 +#: aleksis/apps/chronos/models.py:191 #: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 #: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 msgid "Teachers" msgstr "" -#: aleksis/apps/chronos/models.py:358 -msgid "Periods" -msgstr "" - -#: aleksis/apps/chronos/models.py:360 aleksis/apps/chronos/models.py:995 -#: aleksis/apps/chronos/models.py:1132 aleksis/apps/chronos/models.py:1326 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 -msgid "Groups" -msgstr "" - -#: aleksis/apps/chronos/models.py:394 aleksis/apps/chronos/models.py:490 -#: aleksis/apps/chronos/models.py:716 aleksis/apps/chronos/models.py:1430 -#: aleksis/apps/chronos/models.py:1454 -msgid "Lesson" -msgstr "" - -#: aleksis/apps/chronos/models.py:395 aleksis/apps/chronos/models.py:1306 -msgid "Lessons" -msgstr "" - -#: aleksis/apps/chronos/models.py:403 aleksis/apps/chronos/models.py:1116 -msgid "Week" -msgstr "" - -#: aleksis/apps/chronos/models.py:404 aleksis/apps/chronos/models.py:1117 -msgid "Year" -msgstr "" - -#: aleksis/apps/chronos/models.py:407 aleksis/apps/chronos/models.py:620 -msgid "Lesson period" -msgstr "" - -#: aleksis/apps/chronos/models.py:425 aleksis/apps/chronos/models.py:504 -#: aleksis/apps/chronos/models.py:673 aleksis/apps/chronos/models.py:1144 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 -msgid "Room" -msgstr "" - -#: aleksis/apps/chronos/models.py:428 -msgid "Cancelled?" +#: aleksis/apps/chronos/models.py:198 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 +msgid "Subject" msgstr "" -#: aleksis/apps/chronos/models.py:430 -msgid "Cancelled for teachers?" +#: aleksis/apps/chronos/models.py:206 +#: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 +msgid "Cancelled" msgstr "" -#: aleksis/apps/chronos/models.py:433 aleksis/apps/chronos/models.py:692 -#: aleksis/apps/chronos/models.py:734 aleksis/apps/chronos/models.py:1147 -#: aleksis/apps/chronos/models.py:1357 +#: aleksis/apps/chronos/models.py:210 msgid "Comment" msgstr "" -#: aleksis/apps/chronos/models.py:437 -msgid "Lessons can only be either substituted or cancelled." -msgstr "" - -#: aleksis/apps/chronos/models.py:477 -msgid "Lesson substitution" -msgstr "" - -#: aleksis/apps/chronos/models.py:478 -msgid "Lesson substitutions" -msgstr "" - -#: aleksis/apps/chronos/models.py:621 -msgid "Lesson periods" -msgstr "" - -#: aleksis/apps/chronos/models.py:635 aleksis/apps/chronos/models.py:648 -msgid "Absence reason" -msgstr "" - -#: aleksis/apps/chronos/models.py:636 -msgid "Absence reasons" -msgstr "" - -#: aleksis/apps/chronos/models.py:657 aleksis/apps/chronos/models.py:889 -#: aleksis/apps/chronos/models.py:943 -msgid "Teacher" -msgstr "" - -#: aleksis/apps/chronos/models.py:665 -msgid "Group" -msgstr "" - -#: aleksis/apps/chronos/models.py:681 aleksis/apps/chronos/models.py:723 -msgid "Start period" -msgstr "" - -#: aleksis/apps/chronos/models.py:688 aleksis/apps/chronos/models.py:729 -msgid "End period" -msgstr "" - -#: aleksis/apps/chronos/models.py:702 -msgid "Unknown absence" -msgstr "" - -#: aleksis/apps/chronos/models.py:707 -msgid "Absence" -msgstr "" - -#: aleksis/apps/chronos/models.py:708 -msgid "Absences" -msgstr "" - -#: aleksis/apps/chronos/models.py:719 -msgid "Date of exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:733 aleksis/apps/chronos/models.py:746 -#: aleksis/apps/chronos/models.py:977 -msgid "Title" -msgstr "" - -#: aleksis/apps/chronos/models.py:739 -msgid "Exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:740 -msgid "Exams" -msgstr "" - -#: aleksis/apps/chronos/models.py:749 -msgid "Comments" -msgstr "" - -#: aleksis/apps/chronos/models.py:788 -msgid "Holiday" -msgstr "" - -#: aleksis/apps/chronos/models.py:789 -msgid "Holidays" -msgstr "" - -#: aleksis/apps/chronos/models.py:803 aleksis/apps/chronos/models.py:879 -msgid "Supervision area" -msgstr "" - -#: aleksis/apps/chronos/models.py:804 -msgid "Supervision areas" -msgstr "" - -#: aleksis/apps/chronos/models.py:816 -msgid "Time period after break starts" -msgstr "" - -#: aleksis/apps/chronos/models.py:824 -msgid "Time period before break ends" +#: aleksis/apps/chronos/models.py:254 aleksis/apps/chronos/models.py:266 +#: aleksis/apps/chronos/models.py:278 +msgid "{} (instead of {})" msgstr "" -#: aleksis/apps/chronos/models.py:864 aleksis/apps/chronos/models.py:883 -msgid "Break" +#: aleksis/apps/chronos/models.py:283 aleksis/apps/chronos/models.py:307 +msgid "Lesson" msgstr "" -#: aleksis/apps/chronos/models.py:865 -msgid "Breaks" +#: aleksis/apps/chronos/models.py:467 +msgid "Lesson Event" msgstr "" -#: aleksis/apps/chronos/models.py:923 aleksis/apps/chronos/models.py:936 -#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 -msgid "Supervision" +#: aleksis/apps/chronos/models.py:468 +msgid "Lesson Events" msgstr "" -#: aleksis/apps/chronos/models.py:924 aleksis/apps/chronos/models.py:1622 +#: aleksis/apps/chronos/models.py:475 msgid "Supervisions" msgstr "" -#: aleksis/apps/chronos/models.py:932 -msgid "Date" -msgstr "" - -#: aleksis/apps/chronos/models.py:966 -msgid "Supervision substitution" -msgstr "" - -#: aleksis/apps/chronos/models.py:967 -msgid "Supervision substitutions" -msgstr "" - -#: aleksis/apps/chronos/models.py:985 -msgid "Start time period" -msgstr "" - -#: aleksis/apps/chronos/models.py:991 -msgid "End time period" -msgstr "" - -#: aleksis/apps/chronos/models.py:996 aleksis/apps/chronos/models.py:1332 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 -msgid "Rooms" -msgstr "" - -#: aleksis/apps/chronos/models.py:1005 -#, python-brace-format -msgid "Event {pk}" -msgstr "" - -#: aleksis/apps/chronos/models.py:1103 -msgid "Event" -msgstr "" - -#: aleksis/apps/chronos/models.py:1104 -msgid "Events" -msgstr "" - -#: aleksis/apps/chronos/models.py:1152 -msgid "Related exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:1181 -msgid "Extra lesson" -msgstr "" - -#: aleksis/apps/chronos/models.py:1182 -msgid "Extra lessons" -msgstr "" - -#: aleksis/apps/chronos/models.py:1194 -msgid "Number of days shown in the plan" -msgstr "" - -#: aleksis/apps/chronos/models.py:1198 -msgid "Show header box" -msgstr "" - -#: aleksis/apps/chronos/models.py:1199 aleksis/apps/chronos/preferences.py:100 -msgid "The header box shows affected teachers/groups." -msgstr "" - -#: aleksis/apps/chronos/models.py:1206 -msgid "Revision which triggered the last update" -msgstr "" - -#: aleksis/apps/chronos/models.py:1278 -msgid "Automatic plan" -msgstr "" - -#: aleksis/apps/chronos/models.py:1279 -msgid "Automatic plans" -msgstr "" - -#: aleksis/apps/chronos/models.py:1293 -msgid "Can view all room timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1294 -msgid "Can view all group timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1295 -msgid "Can view all person timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1296 -msgid "Can view timetable overview" -msgstr "" - -#: aleksis/apps/chronos/models.py:1297 -msgid "Can view all lessons per day" -msgstr "" - -#: aleksis/apps/chronos/models.py:1298 -msgid "Can view all supervisions per day" -msgstr "" - -#: aleksis/apps/chronos/models.py:1313 -msgid "Start slot number" -msgstr "" - -#: aleksis/apps/chronos/models.py:1316 -msgid "End slot number" -msgstr "" - -#: aleksis/apps/chronos/models.py:1320 -msgid "Course" -msgstr "" - -#: aleksis/apps/chronos/models.py:1353 -#: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 -msgid "Cancelled" -msgstr "" - -#: aleksis/apps/chronos/models.py:1401 aleksis/apps/chronos/models.py:1413 -#: aleksis/apps/chronos/models.py:1425 -msgid "{} (instead of {})" -msgstr "" - -#: aleksis/apps/chronos/models.py:1614 -msgid "Lesson Event" -msgstr "" - -#: aleksis/apps/chronos/models.py:1615 -msgid "Lesson Events" -msgstr "" - -#: aleksis/apps/chronos/models.py:1630 +#: aleksis/apps/chronos/models.py:483 msgid "Supervision: {}" msgstr "" @@ -445,72 +160,39 @@ msgstr "" msgid "If a lesson or substitution has only one group and this group has parent groups, show the parent groups instead of the original group." msgstr "" -#: aleksis/apps/chronos/preferences.py:42 -msgid "Shorten groups in timetable views" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:43 -msgid "If there are more groups than the set limit, they will be collapsed." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:51 -msgid "Limit of groups for shortening of groups" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:53 -msgid "If a user activates shortening of groups,they will be collapsed if there are more groups than this limit." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:65 +#: aleksis/apps/chronos/preferences.py:44 msgid "Relevant days for substitution plans" msgstr "" -#: aleksis/apps/chronos/preferences.py:82 +#: aleksis/apps/chronos/preferences.py:61 msgid "Time when substitution plans switch to the next day" msgstr "" -#: aleksis/apps/chronos/preferences.py:91 +#: aleksis/apps/chronos/preferences.py:70 msgid "Number of days shown on substitutions print view" msgstr "" -#: aleksis/apps/chronos/preferences.py:99 +#: aleksis/apps/chronos/preferences.py:78 msgid "Show header box in substitution views" msgstr "" -#: aleksis/apps/chronos/preferences.py:109 +#: aleksis/apps/chronos/preferences.py:88 msgid "Show parent groups in header box in substitution views instead of original groups" msgstr "" -#: aleksis/apps/chronos/preferences.py:118 -msgid "How many days in advance users should be notified about timetable changes?" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:126 -msgid "Time for sending notifications about timetable changes" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:129 -msgid "This is only used for scheduling notifications which doesn't affect the time period configured above. All other notifications affecting the next days are sent immediately." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:140 -#: aleksis/apps/chronos/preferences.py:148 -msgid "Send notifications for current timetable changes" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:158 +#: aleksis/apps/chronos/preferences.py:99 msgid "Group types to show in timetables" msgstr "" -#: aleksis/apps/chronos/preferences.py:159 +#: aleksis/apps/chronos/preferences.py:100 msgid "If you leave it empty, all groups will be shown." msgstr "" -#: aleksis/apps/chronos/preferences.py:169 +#: aleksis/apps/chronos/preferences.py:110 msgid "Lesson calendar feed color" msgstr "" -#: aleksis/apps/chronos/preferences.py:181 +#: aleksis/apps/chronos/preferences.py:122 msgid "Supervision calendar feed color" msgstr "" @@ -534,6 +216,10 @@ msgstr "" msgid "all day" msgstr "" +#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 +msgid "Supervision" +msgstr "" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:11 msgid "Print: Substitutions" msgstr "" @@ -546,6 +232,10 @@ msgstr "" msgid "Time" msgstr "" +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 +msgid "Room" +msgstr "" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:31 msgid "Notes" msgstr "" @@ -557,99 +247,3 @@ msgstr "" #: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:2 msgid "Areas" msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:48 -#, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has been cancelled." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:55 -#, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has some current changes." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:64 -#, python-brace-format -msgid "The teacher {old} is substituted by {new}." -msgid_plural "The teachers {old} are substituted by {new}." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" -msgstr[3] "" -msgstr[4] "" -msgstr[5] "" - -#: aleksis/apps/chronos/util/notifications.py:76 -#, python-brace-format -msgid "The subject is changed to {subject}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:82 -#, python-brace-format -msgid "The lesson is moved from {old} to {new}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:91 -#, python-brace-format -msgid "There is an additional comment: {comment}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:99 -#, python-brace-format -msgid "There is an event that starts on {date_start}, {period_from}. period and ends on {date_end}, {period_to}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:112 -#, python-brace-format -msgid "There is an event on {date} from the {period_from}. period to the {period_to}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:123 -#: aleksis/apps/chronos/util/notifications.py:143 -#, python-brace-format -msgid "Groups: {groups}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:125 -#: aleksis/apps/chronos/util/notifications.py:147 -#, python-brace-format -msgid "Teachers: {teachers}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:128 -#, python-brace-format -msgid "Rooms: {rooms}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:135 -#, python-brace-format -msgid "There is an extra lesson on {date} in the {period}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:145 -#, python-brace-format -msgid "Subject: {subject}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:149 -#, python-brace-format -msgid "Room: {room}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:151 -#, python-brace-format -msgid "Comment: {comment}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:154 -#, python-brace-format -msgid "The supervision of {old} on {date} between the {period_from}. period and the {period_to}. period in the area {area} is substituted by {new}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:204 -msgid "Timetable" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:205 -msgid "There are current changes to your timetable." -msgstr "" diff --git a/aleksis/apps/chronos/locale/de_DE/LC_MESSAGES/django.po b/aleksis/apps/chronos/locale/de_DE/LC_MESSAGES/django.po index 6cc74cf61d93a9433e0182e2955f670f4c7bbec7..d53ce3b87c37282efc92294219a0fa18d0cdc4d2 100644 --- a/aleksis/apps/chronos/locale/de_DE/LC_MESSAGES/django.po +++ b/aleksis/apps/chronos/locale/de_DE/LC_MESSAGES/django.po @@ -2,499 +2,158 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# Jonathan Weth <git@jonathanewth.de>, 2024. # msgid "" msgstr "" -"Project-Id-Version: \n" +"Project-Id-Version: unnamed project\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-18 14:23+0200\n" -"PO-Revision-Date: 2024-08-18 13:31+0000\n" -"Last-Translator: magicfelix <felix@felix-zauberer.de>\n" -"Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis-app-chronos/de/>\n" -"Language: de_DE\n" +"POT-Creation-Date: 2024-10-17 10:48+0200\n" +"PO-Revision-Date: 2024-10-17 11:54+0200\n" +"Last-Translator: Jonathan Weth <git@jonathanewth.de>\n" +"Language-Team: English\n" +"Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.0.2\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Gtranslator 47.0\n" -#: aleksis/apps/chronos/mixins.py:25 -msgid "Linked validity range" -msgstr "Zugeordneter Gültigkeitsbereich" - -#: aleksis/apps/chronos/model_extensions.py:145 -#: aleksis/apps/chronos/model_extensions.py:151 +#: aleksis/apps/chronos/model_extensions.py:9 msgid "Can view group timetable" msgstr "Kann Gruppenstundenpläne sehen" -#: aleksis/apps/chronos/model_extensions.py:149 -#: aleksis/apps/chronos/model_extensions.py:155 +#: aleksis/apps/chronos/model_extensions.py:13 msgid "Can view person timetable" msgstr "Kann Personenstundenpläne sehen" -#: aleksis/apps/chronos/models.py:87 aleksis/apps/chronos/models.py:86 -msgid "School term" -msgstr "Schuljahr" - -#: aleksis/apps/chronos/models.py:90 aleksis/apps/chronos/models.py:626 -#: aleksis/apps/chronos/models.py:1310 aleksis/apps/chronos/models.py:89 -#: aleksis/apps/chronos/models.py:699 -msgid "Name" -msgstr "Name" - -#: aleksis/apps/chronos/models.py:92 aleksis/apps/chronos/models.py:676 -#: aleksis/apps/chronos/models.py:747 aleksis/apps/chronos/models.py:979 -#: aleksis/apps/chronos/models.py:91 aleksis/apps/chronos/models.py:754 -#: aleksis/apps/chronos/models.py:827 aleksis/apps/chronos/models.py:1061 -msgid "Start date" -msgstr "Startdatum" - -#: aleksis/apps/chronos/models.py:93 aleksis/apps/chronos/models.py:677 -#: aleksis/apps/chronos/models.py:748 aleksis/apps/chronos/models.py:980 -#: aleksis/apps/chronos/models.py:92 aleksis/apps/chronos/models.py:755 -#: aleksis/apps/chronos/models.py:828 aleksis/apps/chronos/models.py:1062 -msgid "End date" -msgstr "Enddatum" - -#: aleksis/apps/chronos/models.py:112 aleksis/apps/chronos/models.py:111 -msgid "The start date must be earlier than the end date." -msgstr "Das Startdatum muss vor dem Enddatum liegen." - -#: aleksis/apps/chronos/models.py:118 -msgid "The validity range must be within the school term." -msgstr "Der Gültigkeitsbereich muss innerhalb des Schuljahres liegen." - -#: aleksis/apps/chronos/models.py:125 -msgid "There is already a validity range for this time or a part of this time." -msgstr "" -"Es gibt bereits einen Gültigkeitsbereich für diesen Zeitraum oder einen Teil " -"diesen Zeitraumes." - -#: aleksis/apps/chronos/models.py:132 -msgid "Validity range" -msgstr "Gültigkeitsbereich" - -#: aleksis/apps/chronos/models.py:133 -msgid "Validity ranges" -msgstr "Gültigkeitsbereiche" - -#: aleksis/apps/chronos/models.py:149 aleksis/apps/chronos/models.py:150 -msgid "Week day" -msgstr "Wochentag" - -#: aleksis/apps/chronos/models.py:151 aleksis/apps/chronos/models.py:152 -msgid "Number of period" -msgstr "Nummer der Stunde" - -#: aleksis/apps/chronos/models.py:153 aleksis/apps/chronos/models.py:154 -msgid "Start time" -msgstr "Startzeit" - -#: aleksis/apps/chronos/models.py:154 aleksis/apps/chronos/models.py:155 -msgid "End time" -msgstr "Endzeit" - -#: aleksis/apps/chronos/models.py:324 aleksis/apps/chronos/models.py:496 -#: aleksis/apps/chronos/models.py:1122 aleksis/apps/chronos/models.py:338 -#: aleksis/apps/chronos/models.py:525 aleksis/apps/chronos/models.py:1191 -msgid "Time period" -msgstr "Stunde" - -#: aleksis/apps/chronos/models.py:325 aleksis/apps/chronos/models.py:339 -msgid "Time periods" -msgstr "Stunden" - -#: aleksis/apps/chronos/models.py:329 aleksis/apps/chronos/models.py:625 -#: aleksis/apps/chronos/models.py:793 aleksis/apps/chronos/models.py:810 -#: aleksis/apps/chronos/models.py:343 aleksis/apps/chronos/models.py:364 -#: aleksis/apps/chronos/models.py:698 aleksis/apps/chronos/models.py:873 -#: aleksis/apps/chronos/models.py:895 -msgid "Short name" -msgstr "Kurzname" - -#: aleksis/apps/chronos/models.py:330 aleksis/apps/chronos/models.py:794 -#: aleksis/apps/chronos/models.py:811 aleksis/apps/chronos/models.py:344 -#: aleksis/apps/chronos/models.py:365 aleksis/apps/chronos/models.py:874 -#: aleksis/apps/chronos/models.py:896 -msgid "Long name" -msgstr "Langname" - -#: aleksis/apps/chronos/models.py:332 aleksis/apps/chronos/models.py:346 -msgid "Foreground colour" -msgstr "Vordergrundfarbe" - -#: aleksis/apps/chronos/models.py:333 aleksis/apps/chronos/models.py:347 -msgid "Background colour" -msgstr "Hintergrundfarbe" - -#: aleksis/apps/chronos/models.py:340 aleksis/apps/chronos/models.py:349 -#: aleksis/apps/chronos/models.py:416 aleksis/apps/chronos/models.py:1129 -#: aleksis/apps/chronos/models.py:1345 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 -#: aleksis/apps/chronos/models.py:354 aleksis/apps/chronos/models.py:390 -#: aleksis/apps/chronos/models.py:446 aleksis/apps/chronos/models.py:1198 -#: aleksis/apps/chronos/templates/chronos/substitutions.html:47 -msgid "Subject" -msgstr "Fach" - -#: aleksis/apps/chronos/models.py:341 aleksis/apps/chronos/models.py:355 -msgid "Subjects" -msgstr "Fächer" - -#: aleksis/apps/chronos/models.py:352 aleksis/apps/chronos/models.py:422 -#: aleksis/apps/chronos/models.py:998 aleksis/apps/chronos/models.py:1137 -#: aleksis/apps/chronos/models.py:1338 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 -#: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 -#: aleksis/apps/chronos/models.py:393 aleksis/apps/chronos/models.py:452 -#: aleksis/apps/chronos/models.py:1080 aleksis/apps/chronos/models.py:1206 -#: aleksis/apps/chronos/tables.py:35 -#: aleksis/apps/chronos/templates/chronos/all.html:17 -msgid "Teachers" -msgstr "Lehrkräfte" - -#: aleksis/apps/chronos/models.py:358 aleksis/apps/chronos/models.py:399 -msgid "Periods" -msgstr "Stunden" - -#: aleksis/apps/chronos/models.py:360 aleksis/apps/chronos/models.py:995 -#: aleksis/apps/chronos/models.py:1132 aleksis/apps/chronos/models.py:1326 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 -#: aleksis/apps/chronos/models.py:401 aleksis/apps/chronos/models.py:1077 -#: aleksis/apps/chronos/models.py:1201 aleksis/apps/chronos/tables.py:34 -#: aleksis/apps/chronos/templates/chronos/all.html:31 -msgid "Groups" -msgstr "Gruppen" - -#: aleksis/apps/chronos/models.py:394 aleksis/apps/chronos/models.py:490 -#: aleksis/apps/chronos/models.py:716 aleksis/apps/chronos/models.py:1430 -#: aleksis/apps/chronos/models.py:1454 aleksis/apps/chronos/models.py:424 -#: aleksis/apps/chronos/models.py:519 aleksis/apps/chronos/models.py:795 -msgid "Lesson" -msgstr "Unterrichtsstunde" - -#: aleksis/apps/chronos/models.py:395 aleksis/apps/chronos/models.py:1306 -#: aleksis/apps/chronos/models.py:425 -#: aleksis/apps/chronos/templates/chronos/lessons_day.html:9 -#: aleksis/apps/chronos/templates/chronos/lessons_day.html:19 -msgid "Lessons" -msgstr "Unterrichtsstunden" - -#: aleksis/apps/chronos/models.py:403 aleksis/apps/chronos/models.py:1116 -#: aleksis/apps/chronos/models.py:433 aleksis/apps/chronos/models.py:1185 -msgid "Week" -msgstr "KW" - -#: aleksis/apps/chronos/models.py:404 aleksis/apps/chronos/models.py:1117 -#: aleksis/apps/chronos/models.py:434 aleksis/apps/chronos/models.py:1186 -msgid "Year" -msgstr "Jahr" - -#: aleksis/apps/chronos/models.py:407 aleksis/apps/chronos/models.py:620 -#: aleksis/apps/chronos/models.py:437 aleksis/apps/chronos/models.py:654 -msgid "Lesson period" -msgstr "Unterrichtsstunde" - -#: aleksis/apps/chronos/models.py:425 aleksis/apps/chronos/models.py:504 -#: aleksis/apps/chronos/models.py:673 aleksis/apps/chronos/models.py:1144 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 -#: aleksis/apps/chronos/models.py:376 aleksis/apps/chronos/models.py:454 -#: aleksis/apps/chronos/models.py:533 aleksis/apps/chronos/models.py:751 -#: aleksis/apps/chronos/models.py:1213 -#: aleksis/apps/chronos/templates/chronos/substitutions.html:48 -msgid "Room" -msgstr "Raum" - -#: aleksis/apps/chronos/models.py:428 aleksis/apps/chronos/models.py:456 -msgid "Cancelled?" -msgstr "Entfällt?" - -#: aleksis/apps/chronos/models.py:430 aleksis/apps/chronos/models.py:458 -msgid "Cancelled for teachers?" -msgstr "Entfällt für Lehrkräfte?" - -#: aleksis/apps/chronos/models.py:433 aleksis/apps/chronos/models.py:692 -#: aleksis/apps/chronos/models.py:734 aleksis/apps/chronos/models.py:1147 -#: aleksis/apps/chronos/models.py:1357 aleksis/apps/chronos/models.py:461 -#: aleksis/apps/chronos/models.py:770 aleksis/apps/chronos/models.py:813 -#: aleksis/apps/chronos/models.py:1216 -msgid "Comment" -msgstr "Kommentar" - -#: aleksis/apps/chronos/models.py:437 aleksis/apps/chronos/models.py:465 -msgid "Lessons can only be either substituted or cancelled." -msgstr "" -"Unterrichtsstunden können nur entweder vertreten werden oder ausfallen." - -#: aleksis/apps/chronos/models.py:477 aleksis/apps/chronos/models.py:506 -msgid "Lesson substitution" -msgstr "Vertretung" - -#: aleksis/apps/chronos/models.py:478 aleksis/apps/chronos/models.py:507 -msgid "Lesson substitutions" -msgstr "Vertretungen" - -#: aleksis/apps/chronos/models.py:621 aleksis/apps/chronos/models.py:655 -msgid "Lesson periods" -msgstr "Unterrichtsstunden" - -#: aleksis/apps/chronos/models.py:635 aleksis/apps/chronos/models.py:648 -#: aleksis/apps/chronos/models.py:708 aleksis/apps/chronos/models.py:726 -msgid "Absence reason" -msgstr "Abwesenheitsgrund" - -#: aleksis/apps/chronos/models.py:636 aleksis/apps/chronos/models.py:709 -msgid "Absence reasons" -msgstr "Abwesenheitsgründe" - -#: aleksis/apps/chronos/models.py:657 aleksis/apps/chronos/models.py:889 -#: aleksis/apps/chronos/models.py:943 aleksis/apps/chronos/models.py:735 -#: aleksis/apps/chronos/models.py:974 aleksis/apps/chronos/models.py:1025 -#: aleksis/apps/chronos/templates/chronos/substitutions.html:46 -msgid "Teacher" -msgstr "Lehrkraft" - -#: aleksis/apps/chronos/models.py:665 aleksis/apps/chronos/models.py:743 -msgid "Group" -msgstr "Gruppe" - -#: aleksis/apps/chronos/models.py:681 aleksis/apps/chronos/models.py:723 -#: aleksis/apps/chronos/models.py:759 aleksis/apps/chronos/models.py:802 -msgid "Start period" -msgstr "Startstunde" - -#: aleksis/apps/chronos/models.py:688 aleksis/apps/chronos/models.py:729 -#: aleksis/apps/chronos/models.py:766 aleksis/apps/chronos/models.py:808 -msgid "End period" -msgstr "Endstunde" - -#: aleksis/apps/chronos/models.py:702 aleksis/apps/chronos/models.py:780 -msgid "Unknown absence" -msgstr "Unbekannte Absenz" - -#: aleksis/apps/chronos/models.py:707 aleksis/apps/chronos/models.py:786 -msgid "Absence" -msgstr "Abwesenheit" - -#: aleksis/apps/chronos/models.py:708 aleksis/apps/chronos/models.py:787 -msgid "Absences" -msgstr "Abwesenheiten" - -#: aleksis/apps/chronos/models.py:719 aleksis/apps/chronos/models.py:798 -msgid "Date of exam" -msgstr "Datum der Klausur" - -#: aleksis/apps/chronos/models.py:733 aleksis/apps/chronos/models.py:746 -#: aleksis/apps/chronos/models.py:977 aleksis/apps/chronos/models.py:812 -#: aleksis/apps/chronos/models.py:826 aleksis/apps/chronos/models.py:1059 -msgid "Title" -msgstr "Titel" - -#: aleksis/apps/chronos/models.py:739 aleksis/apps/chronos/models.py:819 -msgid "Exam" -msgstr "Klausur" - -#: aleksis/apps/chronos/models.py:740 aleksis/apps/chronos/models.py:820 -msgid "Exams" -msgstr "Klausuren" - -#: aleksis/apps/chronos/models.py:749 aleksis/apps/chronos/models.py:829 -msgid "Comments" -msgstr "Kommentare" - -#: aleksis/apps/chronos/models.py:788 aleksis/apps/chronos/models.py:868 -msgid "Holiday" -msgstr "Ferien" - -#: aleksis/apps/chronos/models.py:789 aleksis/apps/chronos/models.py:869 -msgid "Holidays" -msgstr "Ferien" - -#: aleksis/apps/chronos/models.py:803 aleksis/apps/chronos/models.py:879 -#: aleksis/apps/chronos/models.py:883 aleksis/apps/chronos/models.py:964 -msgid "Supervision area" -msgstr "Aufsichtsgebiet" - -#: aleksis/apps/chronos/models.py:804 aleksis/apps/chronos/models.py:884 -msgid "Supervision areas" -msgstr "Aufsichtsgebiete" - -#: aleksis/apps/chronos/models.py:816 aleksis/apps/chronos/models.py:901 -msgid "Time period after break starts" -msgstr "Stunde, nach der die Pause startet" - -#: aleksis/apps/chronos/models.py:824 aleksis/apps/chronos/models.py:909 -msgid "Time period before break ends" -msgstr "Stunde nach der Pause" - -#: aleksis/apps/chronos/models.py:864 aleksis/apps/chronos/models.py:883 -#: aleksis/apps/chronos/models.py:949 aleksis/apps/chronos/models.py:968 -msgid "Break" -msgstr "Pause" - -#: aleksis/apps/chronos/models.py:865 aleksis/apps/chronos/models.py:950 -msgid "Breaks" -msgstr "Pausen" - -#: aleksis/apps/chronos/models.py:923 aleksis/apps/chronos/models.py:936 -#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 -#: aleksis/apps/chronos/models.py:1005 aleksis/apps/chronos/models.py:1018 -#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:23 -#: aleksis/apps/chronos/templates/chronos/partials/supervision.html:15 -msgid "Supervision" -msgstr "Aufsicht" - -#: aleksis/apps/chronos/models.py:924 aleksis/apps/chronos/models.py:1622 -#: aleksis/apps/chronos/models.py:1006 -msgid "Supervisions" -msgstr "Aufsichten" - -#: aleksis/apps/chronos/models.py:932 aleksis/apps/chronos/models.py:1014 -msgid "Date" -msgstr "Datum" - -#: aleksis/apps/chronos/models.py:966 aleksis/apps/chronos/models.py:1048 -msgid "Supervision substitution" -msgstr "Aufsichtsvertretung" - -#: aleksis/apps/chronos/models.py:967 aleksis/apps/chronos/models.py:1049 -msgid "Supervision substitutions" -msgstr "Aufsichtsvertretungen" - -#: aleksis/apps/chronos/models.py:985 aleksis/apps/chronos/models.py:1067 -msgid "Start time period" -msgstr "Startstunde" - -#: aleksis/apps/chronos/models.py:991 aleksis/apps/chronos/models.py:1073 -msgid "End time period" -msgstr "Endstunde" - -#: aleksis/apps/chronos/models.py:996 aleksis/apps/chronos/models.py:1332 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 -#: aleksis/apps/chronos/models.py:377 aleksis/apps/chronos/models.py:1078 -#: aleksis/apps/chronos/templates/chronos/all.html:45 -msgid "Rooms" -msgstr "Räume" - -#: aleksis/apps/chronos/models.py:1005 aleksis/apps/chronos/models.py:1087 -#, python-brace-format -msgid "Event {pk}" -msgstr "Veranstaltung {pk}" - -#: aleksis/apps/chronos/models.py:1103 aleksis/apps/chronos/models.py:1172 -#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:27 -msgid "Event" -msgstr "Veranstaltung" - -#: aleksis/apps/chronos/models.py:1104 aleksis/apps/chronos/models.py:1173 -msgid "Events" -msgstr "Veranstaltungen" - -#: aleksis/apps/chronos/models.py:1152 aleksis/apps/chronos/models.py:1221 -msgid "Related exam" -msgstr "Zugehörige Klausur" - -#: aleksis/apps/chronos/models.py:1181 aleksis/apps/chronos/models.py:1251 -msgid "Extra lesson" -msgstr "Sonderstunde" - -#: aleksis/apps/chronos/models.py:1182 aleksis/apps/chronos/models.py:1252 -msgid "Extra lessons" -msgstr "Sonderstunden" - -#: aleksis/apps/chronos/models.py:1194 aleksis/apps/chronos/models.py:1264 +#: aleksis/apps/chronos/models.py:48 msgid "Number of days shown in the plan" msgstr "Anzahl der Tage, die im Plan angezeigt werden" -#: aleksis/apps/chronos/models.py:1198 aleksis/apps/chronos/models.py:1268 +#: aleksis/apps/chronos/models.py:52 msgid "Show header box" msgstr "Kopfbox anzeigen" -#: aleksis/apps/chronos/models.py:1199 aleksis/apps/chronos/preferences.py:100 -#: aleksis/apps/chronos/models.py:1269 aleksis/apps/chronos/preferences.py:61 +#: aleksis/apps/chronos/models.py:53 aleksis/apps/chronos/preferences.py:79 msgid "The header box shows affected teachers/groups." msgstr "Die Kopfbox zeigt betroffene Lehrkräfte/Gruppen." -#: aleksis/apps/chronos/models.py:1206 aleksis/apps/chronos/models.py:1276 +#: aleksis/apps/chronos/models.py:60 msgid "Revision which triggered the last update" msgstr "Revision, die die letzte Aktualisierung ausgelöst hat" -#: aleksis/apps/chronos/models.py:1278 aleksis/apps/chronos/models.py:1337 +#: aleksis/apps/chronos/models.py:132 msgid "Automatic plan" msgstr "Automatischer Plan" -#: aleksis/apps/chronos/models.py:1279 aleksis/apps/chronos/models.py:1338 +#: aleksis/apps/chronos/models.py:133 msgid "Automatic plans" msgstr "Automatische Pläne" -#: aleksis/apps/chronos/models.py:1293 aleksis/apps/chronos/models.py:1352 +#: aleksis/apps/chronos/models.py:147 msgid "Can view all room timetables" msgstr "Kann alle Raumstundenpläne sehen" -#: aleksis/apps/chronos/models.py:1294 aleksis/apps/chronos/models.py:1353 +#: aleksis/apps/chronos/models.py:148 msgid "Can view all group timetables" msgstr "Kann alle Gruppenstundenpläne sehen" -#: aleksis/apps/chronos/models.py:1295 aleksis/apps/chronos/models.py:1354 +#: aleksis/apps/chronos/models.py:149 msgid "Can view all person timetables" msgstr "Kann alle Personenstundenpläne sehen" -#: aleksis/apps/chronos/models.py:1296 aleksis/apps/chronos/models.py:1355 +#: aleksis/apps/chronos/models.py:150 msgid "Can view timetable overview" msgstr "Kann Stundenplanübersicht sehen" -#: aleksis/apps/chronos/models.py:1297 aleksis/apps/chronos/models.py:1356 -msgid "Can view all lessons per day" -msgstr "Kann alle Tagesstunden sehen" +#: aleksis/apps/chronos/models.py:151 +msgid "Can view substitutions table" +msgstr "Kann Vertretungstabelle sehen" -#: aleksis/apps/chronos/models.py:1298 -msgid "Can view all supervisions per day" -msgstr "Kann alle Tagesaufsichten sehen" +#: aleksis/apps/chronos/models.py:159 +msgid "Lessons" +msgstr "Unterrichtsstunden" -#: aleksis/apps/chronos/models.py:1313 +#: aleksis/apps/chronos/models.py:163 +msgid "Name" +msgstr "Name" + +#: aleksis/apps/chronos/models.py:166 msgid "Start slot number" msgstr "Startzeitfenster-Nummer" -#: aleksis/apps/chronos/models.py:1316 +#: aleksis/apps/chronos/models.py:169 msgid "End slot number" msgstr "Endzeitfenster-Nummer" -#: aleksis/apps/chronos/models.py:1320 +#: aleksis/apps/chronos/models.py:173 msgid "Course" msgstr "Kurs" -#: aleksis/apps/chronos/models.py:1353 +#: aleksis/apps/chronos/models.py:179 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 +msgid "Groups" +msgstr "Gruppen" + +#: aleksis/apps/chronos/models.py:185 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 +msgid "Rooms" +msgstr "Räume" + +#: aleksis/apps/chronos/models.py:191 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 +#: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 +msgid "Teachers" +msgstr "Lehrkräfte" + +#: aleksis/apps/chronos/models.py:198 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 +msgid "Subject" +msgstr "Fach" + +#: aleksis/apps/chronos/models.py:206 #: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 msgid "Cancelled" msgstr "Entfall" -#: aleksis/apps/chronos/models.py:1401 aleksis/apps/chronos/models.py:1413 -#: aleksis/apps/chronos/models.py:1425 +#: aleksis/apps/chronos/models.py:210 +msgid "Comment" +msgstr "Kommentar" + +#: aleksis/apps/chronos/models.py:254 aleksis/apps/chronos/models.py:266 +#: aleksis/apps/chronos/models.py:278 msgid "{} (instead of {})" msgstr "{} (statt {})" -#: aleksis/apps/chronos/models.py:1614 +#: aleksis/apps/chronos/models.py:283 aleksis/apps/chronos/models.py:307 +msgid "Lesson" +msgstr "Unterrichtsstunde" + +#: aleksis/apps/chronos/models.py:467 msgid "Lesson Event" msgstr "Unterrichtsstunde" -#: aleksis/apps/chronos/models.py:1615 +#: aleksis/apps/chronos/models.py:468 msgid "Lesson Events" msgstr "Unterrichtsstunden" -#: aleksis/apps/chronos/models.py:1630 +#: aleksis/apps/chronos/models.py:475 +msgid "Supervisions" +msgstr "Aufsichten" + +#: aleksis/apps/chronos/models.py:483 msgid "Supervision: {}" msgstr "Aufsicht: {}" -#: aleksis/apps/chronos/preferences.py:21 aleksis/apps/chronos/menus.py:6 -#: aleksis/apps/chronos/preferences.py:10 +#: aleksis/apps/chronos/preferences.py:21 msgid "Timetables" msgstr "Stundenpläne" #: aleksis/apps/chronos/preferences.py:29 -#: aleksis/apps/chronos/preferences.py:18 msgid "Use parent groups in timetable views" msgstr "Elterngruppen in Stundenplanansichten benutzen" @@ -502,53 +161,29 @@ msgstr "Elterngruppen in Stundenplanansichten benutzen" msgid "" "If a lesson or substitution has only one group and this group has parent " "groups, show the parent groups instead of the original group." -msgstr "Wenn eine Stunde oder Vertretung nur eine Gruppe hat und diese Gruppe Elterngruppen hat, zeige die Elterngruppen anstelle der eigentlichen Gruppe." - -#: aleksis/apps/chronos/preferences.py:42 -#: aleksis/apps/chronos/preferences.py:31 -msgid "Shorten groups in timetable views" -msgstr "Gruppen in Stundenplanansichten kürzen" - -#: aleksis/apps/chronos/preferences.py:43 -#: aleksis/apps/chronos/preferences.py:32 -msgid "If there are more groups than the set limit, they will be collapsed." msgstr "" -"Wenn es mehr Gruppen als das gesetzte Limit gibt, werden die Gruppenangaben " -"gekürzt." - -#: aleksis/apps/chronos/preferences.py:51 -#: aleksis/apps/chronos/preferences.py:40 -msgid "Limit of groups for shortening of groups" -msgstr "Anzahl der Gruppen, ab der gekürzt wird" - -#: aleksis/apps/chronos/preferences.py:53 -msgid "" -"If a user activates shortening of groups,they will be collapsed if there are " -"more groups than this limit." -msgstr "Wenn Benutzer*innen die Kürzung von Gruppen aktiviert hat, werden sie ab diesem Limit gekürzt." +"Wenn eine Stunde oder Vertretung nur eine Gruppe hat und diese Gruppe " +"Elterngruppen hat, zeige die Elterngruppen anstelle der eigentlichen Gruppe." -#: aleksis/apps/chronos/preferences.py:65 +#: aleksis/apps/chronos/preferences.py:44 msgid "Relevant days for substitution plans" msgstr "Relevante Tage für Vertretungspläne" -#: aleksis/apps/chronos/preferences.py:82 +#: aleksis/apps/chronos/preferences.py:61 msgid "Time when substitution plans switch to the next day" msgstr "Zeit zu der Vertretungspläne zum nächsten Tag wechseln" -#: aleksis/apps/chronos/preferences.py:91 -#: aleksis/apps/chronos/preferences.py:52 +#: aleksis/apps/chronos/preferences.py:70 msgid "Number of days shown on substitutions print view" msgstr "" "Anzahl der Tage, die in der Druckansicht des Vertretungsplanes angezeigt " "werden soll" -#: aleksis/apps/chronos/preferences.py:99 -#: aleksis/apps/chronos/preferences.py:60 +#: aleksis/apps/chronos/preferences.py:78 msgid "Show header box in substitution views" msgstr "Kopfbox in Vertretungsplänen anzeigen" -#: aleksis/apps/chronos/preferences.py:109 -#: aleksis/apps/chronos/preferences.py:70 +#: aleksis/apps/chronos/preferences.py:88 msgid "" "Show parent groups in header box in substitution views instead of original " "groups" @@ -556,50 +191,19 @@ msgstr "" "Elterngruppen in der Kopfbox in Vertretungsplänen an Stelle der eigentlichen " "Gruppen anzeigen" -#: aleksis/apps/chronos/preferences.py:118 -#: aleksis/apps/chronos/preferences.py:79 -msgid "" -"How many days in advance users should be notified about timetable changes?" -msgstr "" -"Wie viele Tage im Voraus sollen Benutzer über Stundenplanänderungen " -"benachrichtigt werden?" - -#: aleksis/apps/chronos/preferences.py:126 -#: aleksis/apps/chronos/preferences.py:87 -msgid "Time for sending notifications about timetable changes" -msgstr "Zeitpunkt zum Senden von Benachrichtigungen über Stundenplanänderungen" - -#: aleksis/apps/chronos/preferences.py:129 -#: aleksis/apps/chronos/preferences.py:90 -msgid "" -"This is only used for scheduling notifications which doesn't affect the time " -"period configured above. All other notifications affecting the next days are " -"sent immediately." -msgstr "" -"Dies wird nur benutzt, um Benachrichtigungen zu planen, welche nicht den " -"oben konfigurierten Zeitraum betreffen. Alle weiteren Benachrichtigungen, " -"die die nächsten Tage betreffen, werden sofort gesendet." - -#: aleksis/apps/chronos/preferences.py:140 -#: aleksis/apps/chronos/preferences.py:148 -#: aleksis/apps/chronos/preferences.py:101 -#: aleksis/apps/chronos/preferences.py:109 -msgid "Send notifications for current timetable changes" -msgstr "Benachrichtigungen für aktuelle Stundenplanänderungen verschicken" - -#: aleksis/apps/chronos/preferences.py:158 +#: aleksis/apps/chronos/preferences.py:99 msgid "Group types to show in timetables" msgstr "In Stundenplänen anzuzeigende Gruppentypen" -#: aleksis/apps/chronos/preferences.py:159 +#: aleksis/apps/chronos/preferences.py:100 msgid "If you leave it empty, all groups will be shown." msgstr "Wenn Sie das Feld frei lassen, werden alle Gruppen angezeigt." -#: aleksis/apps/chronos/preferences.py:169 +#: aleksis/apps/chronos/preferences.py:110 msgid "Lesson calendar feed color" msgstr "Unterrichtsstunden-Kalenderfarbe" -#: aleksis/apps/chronos/preferences.py:181 +#: aleksis/apps/chronos/preferences.py:122 msgid "Supervision calendar feed color" msgstr "Aufsichts-Kalenderfarbe" @@ -623,14 +227,15 @@ msgstr "Betroffene Gruppen" msgid "all day" msgstr "ganzer Tag" +#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 +msgid "Supervision" +msgstr "Aufsicht" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:11 msgid "Print: Substitutions" msgstr "Druckansicht: Vertretungen" #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:12 -#: aleksis/apps/chronos/menus.py:49 -#: aleksis/apps/chronos/templates/chronos/substitutions.html:11 -#: aleksis/apps/chronos/templates/chronos/substitutions.html:17 msgid "Substitutions" msgstr "Vertretungen" @@ -638,13 +243,15 @@ msgstr "Vertretungen" msgid "Time" msgstr "Zeit" +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 +msgid "Room" +msgstr "Raum" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:31 -#: aleksis/apps/chronos/templates/chronos/substitutions.html:49 msgid "Notes" msgstr "Notizen" #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:41 -#: aleksis/apps/chronos/templates/chronos/substitutions.html:57 msgid "No substitutions available." msgstr "Keine Vertretungen vorhanden." @@ -652,302 +259,458 @@ msgstr "Keine Vertretungen vorhanden." msgid "Areas" msgstr "Bereiche" -#: aleksis/apps/chronos/util/notifications.py:48 -#: aleksis/apps/chronos/util/notifications.py:50 +#~ msgid "Linked validity range" +#~ msgstr "Zugeordneter Gültigkeitsbereich" + +#~ msgid "School term" +#~ msgstr "Schuljahr" + +#~ msgid "Start date" +#~ msgstr "Startdatum" + +#~ msgid "End date" +#~ msgstr "Enddatum" + +#~ msgid "The start date must be earlier than the end date." +#~ msgstr "Das Startdatum muss vor dem Enddatum liegen." + +#~ msgid "The validity range must be within the school term." +#~ msgstr "Der Gültigkeitsbereich muss innerhalb des Schuljahres liegen." + +#~ msgid "" +#~ "There is already a validity range for this time or a part of this time." +#~ msgstr "" +#~ "Es gibt bereits einen Gültigkeitsbereich für diesen Zeitraum oder einen " +#~ "Teil diesen Zeitraumes." + +#~ msgid "Validity range" +#~ msgstr "Gültigkeitsbereich" + +#~ msgid "Validity ranges" +#~ msgstr "Gültigkeitsbereiche" + +#~ msgid "Week day" +#~ msgstr "Wochentag" + +#~ msgid "Number of period" +#~ msgstr "Nummer der Stunde" + +#~ msgid "Start time" +#~ msgstr "Startzeit" + +#~ msgid "End time" +#~ msgstr "Endzeit" + +#~ msgid "Time period" +#~ msgstr "Stunde" + +#~ msgid "Time periods" +#~ msgstr "Stunden" + +#~ msgid "Short name" +#~ msgstr "Kurzname" + +#~ msgid "Long name" +#~ msgstr "Langname" + +#~ msgid "Foreground colour" +#~ msgstr "Vordergrundfarbe" + +#~ msgid "Background colour" +#~ msgstr "Hintergrundfarbe" + +#~ msgid "Subjects" +#~ msgstr "Fächer" + +#~ msgid "Periods" +#~ msgstr "Stunden" + +#~ msgid "Week" +#~ msgstr "KW" + +#~ msgid "Year" +#~ msgstr "Jahr" + +#~ msgid "Lesson period" +#~ msgstr "Unterrichtsstunde" + +#~ msgid "Cancelled?" +#~ msgstr "Entfällt?" + +#~ msgid "Cancelled for teachers?" +#~ msgstr "Entfällt für Lehrkräfte?" + +#~ msgid "Lessons can only be either substituted or cancelled." +#~ msgstr "" +#~ "Unterrichtsstunden können nur entweder vertreten werden oder ausfallen." + +#~ msgid "Lesson substitution" +#~ msgstr "Vertretung" + +#~ msgid "Lesson substitutions" +#~ msgstr "Vertretungen" + +#~ msgid "Lesson periods" +#~ msgstr "Unterrichtsstunden" + +#~ msgid "Absence reason" +#~ msgstr "Abwesenheitsgrund" + +#~ msgid "Absence reasons" +#~ msgstr "Abwesenheitsgründe" + +#~ msgid "Teacher" +#~ msgstr "Lehrkraft" + +#~ msgid "Group" +#~ msgstr "Gruppe" + +#~ msgid "Start period" +#~ msgstr "Startstunde" + +#~ msgid "End period" +#~ msgstr "Endstunde" + +#~ msgid "Unknown absence" +#~ msgstr "Unbekannte Absenz" + +#~ msgid "Absence" +#~ msgstr "Abwesenheit" + +#~ msgid "Absences" +#~ msgstr "Abwesenheiten" + +#~ msgid "Date of exam" +#~ msgstr "Datum der Klausur" + +#~ msgid "Title" +#~ msgstr "Titel" + +#~ msgid "Exam" +#~ msgstr "Klausur" + +#~ msgid "Exams" +#~ msgstr "Klausuren" + +#~ msgid "Comments" +#~ msgstr "Kommentare" + +#~ msgid "Holiday" +#~ msgstr "Ferien" + +#~ msgid "Holidays" +#~ msgstr "Ferien" + +#~ msgid "Supervision area" +#~ msgstr "Aufsichtsgebiet" + +#~ msgid "Supervision areas" +#~ msgstr "Aufsichtsgebiete" + +#~ msgid "Time period after break starts" +#~ msgstr "Stunde, nach der die Pause startet" + +#~ msgid "Time period before break ends" +#~ msgstr "Stunde nach der Pause" + +#~ msgid "Break" +#~ msgstr "Pause" + +#~ msgid "Breaks" +#~ msgstr "Pausen" + +#~ msgid "Date" +#~ msgstr "Datum" + +#~ msgid "Supervision substitution" +#~ msgstr "Aufsichtsvertretung" + +#~ msgid "Supervision substitutions" +#~ msgstr "Aufsichtsvertretungen" + +#~ msgid "Start time period" +#~ msgstr "Startstunde" + +#~ msgid "End time period" +#~ msgstr "Endstunde" + #, python-brace-format -msgid "" -"The {subject} lesson in the {period}. period on {day} has been cancelled." -msgstr "Die {subject}-Stunde in der {period}. Stunde am {day} fällt aus." +#~ msgid "Event {pk}" +#~ msgstr "Veranstaltung {pk}" + +#~ msgid "Event" +#~ msgstr "Veranstaltung" + +#~ msgid "Events" +#~ msgstr "Veranstaltungen" + +#~ msgid "Related exam" +#~ msgstr "Zugehörige Klausur" + +#~ msgid "Extra lesson" +#~ msgstr "Sonderstunde" + +#~ msgid "Extra lessons" +#~ msgstr "Sonderstunden" + +#~ msgid "Can view all lessons per day" +#~ msgstr "Kann alle Tagesstunden sehen" + +#~ msgid "Can view all supervisions per day" +#~ msgstr "Kann alle Tagesaufsichten sehen" + +#~ msgid "Shorten groups in timetable views" +#~ msgstr "Gruppen in Stundenplanansichten kürzen" + +#~ msgid "If there are more groups than the set limit, they will be collapsed." +#~ msgstr "" +#~ "Wenn es mehr Gruppen als das gesetzte Limit gibt, werden die " +#~ "Gruppenangaben gekürzt." + +#~ msgid "Limit of groups for shortening of groups" +#~ msgstr "Anzahl der Gruppen, ab der gekürzt wird" + +#~ msgid "" +#~ "If a user activates shortening of groups,they will be collapsed if there " +#~ "are more groups than this limit." +#~ msgstr "" +#~ "Wenn Benutzer*innen die Kürzung von Gruppen aktiviert hat, werden sie ab " +#~ "diesem Limit gekürzt." + +#~ msgid "" +#~ "How many days in advance users should be notified about timetable changes?" +#~ msgstr "" +#~ "Wie viele Tage im Voraus sollen Benutzer über Stundenplanänderungen " +#~ "benachrichtigt werden?" + +#~ msgid "Time for sending notifications about timetable changes" +#~ msgstr "" +#~ "Zeitpunkt zum Senden von Benachrichtigungen über Stundenplanänderungen" + +#~ msgid "" +#~ "This is only used for scheduling notifications which doesn't affect the " +#~ "time period configured above. All other notifications affecting the next " +#~ "days are sent immediately." +#~ msgstr "" +#~ "Dies wird nur benutzt, um Benachrichtigungen zu planen, welche nicht den " +#~ "oben konfigurierten Zeitraum betreffen. Alle weiteren Benachrichtigungen, " +#~ "die die nächsten Tage betreffen, werden sofort gesendet." + +#~ msgid "Send notifications for current timetable changes" +#~ msgstr "Benachrichtigungen für aktuelle Stundenplanänderungen verschicken" -#: aleksis/apps/chronos/util/notifications.py:55 -#: aleksis/apps/chronos/util/notifications.py:57 #, python-brace-format -msgid "" -"The {subject} lesson in the {period}. period on {day} has some current " -"changes." -msgstr "" -"Die {subject}-Stunde in der {period}. Stunde am {day} hat aktuelle " -"Änderungen." +#~ msgid "" +#~ "The {subject} lesson in the {period}. period on {day} has been cancelled." +#~ msgstr "Die {subject}-Stunde in der {period}. Stunde am {day} fällt aus." -#: aleksis/apps/chronos/util/notifications.py:64 -#: aleksis/apps/chronos/util/notifications.py:66 #, python-brace-format -msgid "The teacher {old} is substituted by {new}." -msgid_plural "The teachers {old} are substituted by {new}." -msgstr[0] "Die Lehrkraft {old} wird von {new} vertreten." -msgstr[1] "Die Lehrkräfte {old} werden von {new} vertreten." +#~ msgid "" +#~ "The {subject} lesson in the {period}. period on {day} has some current " +#~ "changes." +#~ msgstr "" +#~ "Die {subject}-Stunde in der {period}. Stunde am {day} hat aktuelle " +#~ "Änderungen." -#: aleksis/apps/chronos/util/notifications.py:76 -#: aleksis/apps/chronos/util/notifications.py:78 #, python-brace-format -msgid "The subject is changed to {subject}." -msgstr "Das Fach ist zu {subject} geändert." +#~ msgid "The teacher {old} is substituted by {new}." +#~ msgid_plural "The teachers {old} are substituted by {new}." +#~ msgstr[0] "Die Lehrkraft {old} wird von {new} vertreten." +#~ msgstr[1] "Die Lehrkräfte {old} werden von {new} vertreten." -#: aleksis/apps/chronos/util/notifications.py:82 -#: aleksis/apps/chronos/util/notifications.py:84 #, python-brace-format -msgid "The lesson is moved from {old} to {new}." -msgstr "Die Stunde wurde von {old} nach {new} verschoben." +#~ msgid "The subject is changed to {subject}." +#~ msgstr "Das Fach ist zu {subject} geändert." -#: aleksis/apps/chronos/util/notifications.py:91 -#: aleksis/apps/chronos/util/notifications.py:93 #, python-brace-format -msgid "There is an additional comment: {comment}." -msgstr "Es gibt einen zusätzlichen Hinweis: {comment}." +#~ msgid "The lesson is moved from {old} to {new}." +#~ msgstr "Die Stunde wurde von {old} nach {new} verschoben." -#: aleksis/apps/chronos/util/notifications.py:99 -#: aleksis/apps/chronos/util/notifications.py:101 #, python-brace-format -msgid "" -"There is an event that starts on {date_start}, {period_from}. period and " -"ends on {date_end}, {period_to}. period:" -msgstr "" -"Es gibt eine Veranstaltung, die am {date_start} in der {period_from}. Stunde " -"startet und am {date_end} in der {period_to}. Stunde endet:" +#~ msgid "There is an additional comment: {comment}." +#~ msgstr "Es gibt einen zusätzlichen Hinweis: {comment}." -#: aleksis/apps/chronos/util/notifications.py:112 -#: aleksis/apps/chronos/util/notifications.py:114 #, python-brace-format -msgid "" -"There is an event on {date} from the {period_from}. period to the " -"{period_to}. period:" -msgstr "" -"Es gibt eine Veranstaltung am {date} von der {period_from}. Stunde bis zur " -"{period_to}. Stunde:" +#~ msgid "" +#~ "There is an event that starts on {date_start}, {period_from}. period and " +#~ "ends on {date_end}, {period_to}. period:" +#~ msgstr "" +#~ "Es gibt eine Veranstaltung, die am {date_start} in der {period_from}. " +#~ "Stunde startet und am {date_end} in der {period_to}. Stunde endet:" -#: aleksis/apps/chronos/util/notifications.py:123 -#: aleksis/apps/chronos/util/notifications.py:143 -#: aleksis/apps/chronos/util/notifications.py:125 -#: aleksis/apps/chronos/util/notifications.py:145 #, python-brace-format -msgid "Groups: {groups}" -msgstr "Gruppen: {groups}" +#~ msgid "" +#~ "There is an event on {date} from the {period_from}. period to the " +#~ "{period_to}. period:" +#~ msgstr "" +#~ "Es gibt eine Veranstaltung am {date} von der {period_from}. Stunde bis " +#~ "zur {period_to}. Stunde:" -#: aleksis/apps/chronos/util/notifications.py:125 -#: aleksis/apps/chronos/util/notifications.py:147 -#: aleksis/apps/chronos/util/notifications.py:127 -#: aleksis/apps/chronos/util/notifications.py:149 #, python-brace-format -msgid "Teachers: {teachers}" -msgstr "Lehrkräfte: {teachers}" +#~ msgid "Groups: {groups}" +#~ msgstr "Gruppen: {groups}" -#: aleksis/apps/chronos/util/notifications.py:128 -#: aleksis/apps/chronos/util/notifications.py:130 #, python-brace-format -msgid "Rooms: {rooms}" -msgstr "Räume: {rooms}" +#~ msgid "Teachers: {teachers}" +#~ msgstr "Lehrkräfte: {teachers}" -#: aleksis/apps/chronos/util/notifications.py:135 -#: aleksis/apps/chronos/util/notifications.py:137 #, python-brace-format -msgid "There is an extra lesson on {date} in the {period}. period:" -msgstr "Es gibt eine Zusatzstunde am {date} in der {period}. Stunde:" +#~ msgid "Rooms: {rooms}" +#~ msgstr "Räume: {rooms}" -#: aleksis/apps/chronos/util/notifications.py:145 -#: aleksis/apps/chronos/util/notifications.py:147 #, python-brace-format -msgid "Subject: {subject}" -msgstr "Fach: {subject}" +#~ msgid "There is an extra lesson on {date} in the {period}. period:" +#~ msgstr "Es gibt eine Zusatzstunde am {date} in der {period}. Stunde:" -#: aleksis/apps/chronos/util/notifications.py:149 -#: aleksis/apps/chronos/util/notifications.py:151 #, python-brace-format -msgid "Room: {room}" -msgstr "Raum: {room}" +#~ msgid "Subject: {subject}" +#~ msgstr "Fach: {subject}" -#: aleksis/apps/chronos/util/notifications.py:151 -#: aleksis/apps/chronos/util/notifications.py:153 #, python-brace-format -msgid "Comment: {comment}." -msgstr "Kommentar: {comment}." +#~ msgid "Room: {room}" +#~ msgstr "Raum: {room}" -#: aleksis/apps/chronos/util/notifications.py:154 -#: aleksis/apps/chronos/util/notifications.py:156 #, python-brace-format -msgid "" -"The supervision of {old} on {date} between the {period_from}. period and the " -"{period_to}. period in the area {area} is substituted by {new}." -msgstr "" -"Die Aufsicht von {old} am {date} zwischen der {period_from}. Stunde un der " -"{period_to}. Stunde im Gebiet {area} wird von {new} vertreten." - -#: aleksis/apps/chronos/util/notifications.py:204 -#: aleksis/apps/chronos/templates/chronos/timetable.html:11 -#: aleksis/apps/chronos/templates/chronos/timetable.html:24 -#: aleksis/apps/chronos/templates/chronos/timetable_print.html:11 -#: aleksis/apps/chronos/util/notifications.py:206 -msgid "Timetable" -msgstr "Stundenplan" - -#: aleksis/apps/chronos/util/notifications.py:205 -#: aleksis/apps/chronos/util/notifications.py:207 -msgid "There are current changes to your timetable." -msgstr "Es gibt aktuelle Änderungen in Ihrem Stundenplan." - -#: aleksis/apps/chronos/form_extensions.py:7 -msgid "Options for timetables" -msgstr "Einstellungen für Stundenpläne" - -#: aleksis/apps/chronos/form_extensions.py:8 -msgid "Optional data for timetables" -msgstr "Optionale Daten für Stundenpläne" - -#: aleksis/apps/chronos/menus.py:16 -#: aleksis/apps/chronos/templates/chronos/my_timetable.html:11 -#: aleksis/apps/chronos/templates/chronos/my_timetable.html:18 -msgid "My timetable" -msgstr "Mein Plan" - -#: aleksis/apps/chronos/menus.py:27 -#: aleksis/apps/chronos/templates/chronos/all.html:11 -#: aleksis/apps/chronos/templates/chronos/all.html:12 -msgid "All timetables" -msgstr "Alle Stundenpläne" - -#: aleksis/apps/chronos/menus.py:38 -msgid "Daily lessons" -msgstr "Tagesstunden" - -#: aleksis/apps/chronos/model_extensions.py:142 -msgid "Show announcement in timetable views?" -msgstr "Ankündigungen in Stundenplanansichten anzeigen?" - -#: aleksis/apps/chronos/models.py:374 -msgid "Can view room timetable" -msgstr "Kann Raum-Stundenplan sehen" - -#: aleksis/apps/chronos/models.py:693 -msgid "Timetable widget" -msgstr "Stundenplanwidget" - -#: aleksis/apps/chronos/models.py:694 -msgid "Timetable widgets" -msgstr "Stundenplanwidgets" - -#: aleksis/apps/chronos/tables.py:41 -msgid "Substitution" -msgstr "Vertretung" - -#: aleksis/apps/chronos/tables.py:43 -msgid "Manage substitution" -msgstr "Vertretung verwalten" - -#: aleksis/apps/chronos/templates/chronos/all.html:25 -msgid "No teachers timetables available." -msgstr "Keine Stundenpläne von Lehrkräften vorhanden." - -#: aleksis/apps/chronos/templates/chronos/all.html:39 -msgid "No group timetables available." -msgstr "Keine Gruppen-Stundenpläne verfügbar." - -#: aleksis/apps/chronos/templates/chronos/all.html:53 -msgid "No room timetables available." -msgstr "Keine Raum-Stundenpläne verfügbar." - -#: aleksis/apps/chronos/templates/chronos/edit_substitution.html:11 -msgid "Edit substitution." -msgstr "Vertretung bearbeiten." - -#: aleksis/apps/chronos/templates/chronos/edit_substitution.html:12 -msgid "Edit substitution" -msgstr "Vertretung bearbeiten" - -#: aleksis/apps/chronos/templates/chronos/edit_substitution.html:25 -msgid "Delete" -msgstr "Löschen" - -#: aleksis/apps/chronos/templates/chronos/my_timetable.html:19 -#: aleksis/apps/chronos/templates/chronos/timetable.html:50 -msgid "SMART PLAN" -msgstr "SMART PLAN" - -#: aleksis/apps/chronos/templates/chronos/my_timetable.html:22 -msgid "Show week timetable for" -msgstr "Wochenplan anzeigen für" - -#: aleksis/apps/chronos/templates/chronos/partials/lesson.html:27 -msgid "Cancelled due to an event" -msgstr "Fällt wg. einer Veranstaltung aus" - -#: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:6 -msgid "Cancelled for teachers" -msgstr "Entfall für Lehrkräfte" - -#: aleksis/apps/chronos/templates/chronos/partials/today.html:2 -msgid "Today" -msgstr "Heute" - -#: aleksis/apps/chronos/templates/chronos/partials/week_select.html:14 -#: aleksis/apps/chronos/templates/chronos/partials/week_select.html:25 -#: aleksis/apps/chronos/templates/chronos/partials/week_select.html:36 -msgid "CW" -msgstr "KW" - -#: aleksis/apps/chronos/templates/chronos/timetable.html:29 -msgid "Group teachers:" -msgstr "Gruppenlehrkräfte:" - -#: aleksis/apps/chronos/templates/chronos/timetable.html:55 -msgid "Show regular timetable" -msgstr "Regulären Plan anzeigen" - -#: aleksis/apps/chronos/templates/chronos/timetable.html:63 -#: aleksis/apps/chronos/templates/chronos/timetable.html:84 -msgid "View class register of this week" -msgstr "Klassenbuch dieser Woche anzeigen" - -#: aleksis/apps/chronos/templates/chronos/timetable.html:76 -msgid "Show SMART PLAN" -msgstr "SMART PLAN anzeigen" - -#: aleksis/apps/chronos/templates/chronos/widget.html:8 +#~ msgid "Comment: {comment}." +#~ msgstr "Kommentar: {comment}." + +#, python-brace-format +#~ msgid "" +#~ "The supervision of {old} on {date} between the {period_from}. period and " +#~ "the {period_to}. period in the area {area} is substituted by {new}." +#~ msgstr "" +#~ "Die Aufsicht von {old} am {date} zwischen der {period_from}. Stunde un " +#~ "der {period_to}. Stunde im Gebiet {area} wird von {new} vertreten." + +#~ msgid "Timetable" +#~ msgstr "Stundenplan" + +#~ msgid "There are current changes to your timetable." +#~ msgstr "Es gibt aktuelle Änderungen in Ihrem Stundenplan." + +#~ msgid "Options for timetables" +#~ msgstr "Einstellungen für Stundenpläne" + +#~ msgid "Optional data for timetables" +#~ msgstr "Optionale Daten für Stundenpläne" + +#~ msgid "My timetable" +#~ msgstr "Mein Plan" + +#~ msgid "All timetables" +#~ msgstr "Alle Stundenpläne" + +#~ msgid "Daily lessons" +#~ msgstr "Tagesstunden" + +#~ msgid "Show announcement in timetable views?" +#~ msgstr "Ankündigungen in Stundenplanansichten anzeigen?" + +#~ msgid "Can view room timetable" +#~ msgstr "Kann Raum-Stundenplan sehen" + +#~ msgid "Timetable widget" +#~ msgstr "Stundenplanwidget" + +#~ msgid "Timetable widgets" +#~ msgstr "Stundenplanwidgets" + +#~ msgid "Substitution" +#~ msgstr "Vertretung" + +#~ msgid "Manage substitution" +#~ msgstr "Vertretung verwalten" + +#~ msgid "No teachers timetables available." +#~ msgstr "Keine Stundenpläne von Lehrkräften vorhanden." + +#~ msgid "No group timetables available." +#~ msgstr "Keine Gruppen-Stundenpläne verfügbar." + +#~ msgid "No room timetables available." +#~ msgstr "Keine Raum-Stundenpläne verfügbar." + +#~ msgid "Edit substitution." +#~ msgstr "Vertretung bearbeiten." + +#~ msgid "Edit substitution" +#~ msgstr "Vertretung bearbeiten" + +#~ msgid "Delete" +#~ msgstr "Löschen" + +#~ msgid "SMART PLAN" +#~ msgstr "SMART PLAN" + +#~ msgid "Show week timetable for" +#~ msgstr "Wochenplan anzeigen für" + +#~ msgid "Cancelled due to an event" +#~ msgstr "Fällt wg. einer Veranstaltung aus" + +#~ msgid "Cancelled for teachers" +#~ msgstr "Entfall für Lehrkräfte" + +#~ msgid "Today" +#~ msgstr "Heute" + +#~ msgid "CW" +#~ msgstr "KW" + +#~ msgid "Group teachers:" +#~ msgstr "Gruppenlehrkräfte:" + +#~ msgid "Show regular timetable" +#~ msgstr "Regulären Plan anzeigen" + +#~ msgid "View class register of this week" +#~ msgstr "Klassenbuch dieser Woche anzeigen" + +#~ msgid "Show SMART PLAN" +#~ msgstr "SMART PLAN anzeigen" + #, python-format -msgid "" -"\n" -" My timetable for %(day)s\n" -" " -msgstr "" -"\n" -" Mein Stundenplan für %(day)s\n" -" " +#~ msgid "" +#~ "\n" +#~ " My timetable for %(day)s\n" +#~ " " +#~ msgstr "" +#~ "\n" +#~ " Mein Stundenplan für %(day)s\n" +#~ " " -#: aleksis/apps/chronos/templates/chronos/widget.html:18 -msgid "" -"\n" -" There is no timetable linked to your person.\n" -" " -msgstr "" -"\n" -" Es gibt keine Stundenplan, der Ihrer Person zugeordnet ist.\n" -" " +#~ msgid "" +#~ "\n" +#~ " There is no timetable linked to your person.\n" +#~ " " +#~ msgstr "" +#~ "\n" +#~ " Es gibt keine Stundenplan, der Ihrer Person zugeordnet ist.\n" +#~ " " -#: aleksis/apps/chronos/templates/chronos/widget.html:27 -msgid "Go to smart plan" -msgstr "SMART PLAN anzeigen" +#~ msgid "Go to smart plan" +#~ msgstr "SMART PLAN anzeigen" -#: aleksis/apps/chronos/views.py:270 -msgid "The substitution has been saved." -msgstr "Die Vertretung wurde gespeichert." +#~ msgid "The substitution has been saved." +#~ msgstr "Die Vertretung wurde gespeichert." -#: aleksis/apps/chronos/views.py:291 -msgid "The substitution has been deleted." -msgstr "Die Vertretung wurde gelöscht." +#~ msgid "The substitution has been deleted." +#~ msgstr "Die Vertretung wurde gelöscht." -#: aleksis/apps/chronos/preferences.py:20 -msgid "" -"If an lesson or substitution has only one group and this group has parent " -"groups, show the parent groups instead of the original group." -msgstr "" -"Wenn eine Stunde oder Vertretung nur eine Gruppe hat und diese Gruppe " -"Elterngruppen hat, zeige die Elterngruppen anstelle der eigentlichen Gruppe." +#~ msgid "" +#~ "If an lesson or substitution has only one group and this group has parent " +#~ "groups, show the parent groups instead of the original group." +#~ msgstr "" +#~ "Wenn eine Stunde oder Vertretung nur eine Gruppe hat und diese Gruppe " +#~ "Elterngruppen hat, zeige die Elterngruppen anstelle der eigentlichen " +#~ "Gruppe." -#: aleksis/apps/chronos/preferences.py:42 -msgid "" -"If an user activates shortening of groups,they will be collapsed if there " -"are more groups than this limit." -msgstr "" -"Wenn ein Benutzer die Kürzung von Gruppen aktiviert hat, werden sie ab " -"diesem Limit gekürzt." +#~ msgid "" +#~ "If an user activates shortening of groups,they will be collapsed if there " +#~ "are more groups than this limit." +#~ msgstr "" +#~ "Wenn ein Benutzer die Kürzung von Gruppen aktiviert hat, werden sie ab " +#~ "diesem Limit gekürzt." #~ msgid "View class register of the current week" #~ msgstr "Klassenbuch der aktuellen Woche anzeigen" diff --git a/aleksis/apps/chronos/locale/fr/LC_MESSAGES/django.po b/aleksis/apps/chronos/locale/fr/LC_MESSAGES/django.po index 0efbbcbc0bddc2a0dfd0fc65f2f487e69d5c6532..cea5c852f6887401bcd06dc8ae9fb07cdb4b222d 100644 --- a/aleksis/apps/chronos/locale/fr/LC_MESSAGES/django.po +++ b/aleksis/apps/chronos/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-18 14:23+0200\n" +"POT-Creation-Date: 2024-10-17 10:48+0200\n" "PO-Revision-Date: 2021-06-16 11:59+0000\n" "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n" "Language-Team: French <https://translate.edugit.org/projects/aleksis/aleksis-app-chronos/fr/>\n" @@ -18,424 +18,137 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.4\n" -#: aleksis/apps/chronos/mixins.py:25 -msgid "Linked validity range" -msgstr "" - -#: aleksis/apps/chronos/model_extensions.py:145 +#: aleksis/apps/chronos/model_extensions.py:9 msgid "Can view group timetable" msgstr "" -#: aleksis/apps/chronos/model_extensions.py:149 +#: aleksis/apps/chronos/model_extensions.py:13 msgid "Can view person timetable" msgstr "" -#: aleksis/apps/chronos/models.py:87 -msgid "School term" -msgstr "" - -#: aleksis/apps/chronos/models.py:90 aleksis/apps/chronos/models.py:626 -#: aleksis/apps/chronos/models.py:1310 -msgid "Name" -msgstr "" - -#: aleksis/apps/chronos/models.py:92 aleksis/apps/chronos/models.py:676 -#: aleksis/apps/chronos/models.py:747 aleksis/apps/chronos/models.py:979 -msgid "Start date" -msgstr "" - -#: aleksis/apps/chronos/models.py:93 aleksis/apps/chronos/models.py:677 -#: aleksis/apps/chronos/models.py:748 aleksis/apps/chronos/models.py:980 -msgid "End date" +#: aleksis/apps/chronos/models.py:48 +msgid "Number of days shown in the plan" msgstr "" -#: aleksis/apps/chronos/models.py:112 -msgid "The start date must be earlier than the end date." +#: aleksis/apps/chronos/models.py:52 +msgid "Show header box" msgstr "" -#: aleksis/apps/chronos/models.py:118 -msgid "The validity range must be within the school term." +#: aleksis/apps/chronos/models.py:53 aleksis/apps/chronos/preferences.py:79 +msgid "The header box shows affected teachers/groups." msgstr "" -#: aleksis/apps/chronos/models.py:125 -msgid "There is already a validity range for this time or a part of this time." +#: aleksis/apps/chronos/models.py:60 +msgid "Revision which triggered the last update" msgstr "" #: aleksis/apps/chronos/models.py:132 -msgid "Validity range" +msgid "Automatic plan" msgstr "" #: aleksis/apps/chronos/models.py:133 -msgid "Validity ranges" -msgstr "" - -#: aleksis/apps/chronos/models.py:149 -msgid "Week day" +msgid "Automatic plans" msgstr "" -#: aleksis/apps/chronos/models.py:151 -msgid "Number of period" +#: aleksis/apps/chronos/models.py:147 +msgid "Can view all room timetables" msgstr "" -#: aleksis/apps/chronos/models.py:153 -msgid "Start time" +#: aleksis/apps/chronos/models.py:148 +msgid "Can view all group timetables" msgstr "" -#: aleksis/apps/chronos/models.py:154 -msgid "End time" +#: aleksis/apps/chronos/models.py:149 +msgid "Can view all person timetables" msgstr "" -#: aleksis/apps/chronos/models.py:324 aleksis/apps/chronos/models.py:496 -#: aleksis/apps/chronos/models.py:1122 -msgid "Time period" +#: aleksis/apps/chronos/models.py:150 +msgid "Can view timetable overview" msgstr "" -#: aleksis/apps/chronos/models.py:325 -msgid "Time periods" +#: aleksis/apps/chronos/models.py:151 +msgid "Can view substitutions table" msgstr "" -#: aleksis/apps/chronos/models.py:329 aleksis/apps/chronos/models.py:625 -#: aleksis/apps/chronos/models.py:793 aleksis/apps/chronos/models.py:810 -msgid "Short name" +#: aleksis/apps/chronos/models.py:159 +msgid "Lessons" msgstr "" -#: aleksis/apps/chronos/models.py:330 aleksis/apps/chronos/models.py:794 -#: aleksis/apps/chronos/models.py:811 -msgid "Long name" +#: aleksis/apps/chronos/models.py:163 +msgid "Name" msgstr "" -#: aleksis/apps/chronos/models.py:332 -msgid "Foreground colour" +#: aleksis/apps/chronos/models.py:166 +msgid "Start slot number" msgstr "" -#: aleksis/apps/chronos/models.py:333 -msgid "Background colour" +#: aleksis/apps/chronos/models.py:169 +msgid "End slot number" msgstr "" -#: aleksis/apps/chronos/models.py:340 aleksis/apps/chronos/models.py:349 -#: aleksis/apps/chronos/models.py:416 aleksis/apps/chronos/models.py:1129 -#: aleksis/apps/chronos/models.py:1345 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 -msgid "Subject" -msgstr "Sujet" - -#: aleksis/apps/chronos/models.py:341 -#, fuzzy -#| msgid "Subject" -msgid "Subjects" -msgstr "Sujet" - -#: aleksis/apps/chronos/models.py:352 aleksis/apps/chronos/models.py:422 -#: aleksis/apps/chronos/models.py:998 aleksis/apps/chronos/models.py:1137 -#: aleksis/apps/chronos/models.py:1338 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 -#: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 -msgid "Teachers" -msgstr "Profs" - -#: aleksis/apps/chronos/models.py:358 -msgid "Periods" +#: aleksis/apps/chronos/models.py:173 +msgid "Course" msgstr "" -#: aleksis/apps/chronos/models.py:360 aleksis/apps/chronos/models.py:995 -#: aleksis/apps/chronos/models.py:1132 aleksis/apps/chronos/models.py:1326 +#: aleksis/apps/chronos/models.py:179 #: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 msgid "Groups" msgstr "" -#: aleksis/apps/chronos/models.py:394 aleksis/apps/chronos/models.py:490 -#: aleksis/apps/chronos/models.py:716 aleksis/apps/chronos/models.py:1430 -#: aleksis/apps/chronos/models.py:1454 -msgid "Lesson" -msgstr "Cours" - -#: aleksis/apps/chronos/models.py:395 aleksis/apps/chronos/models.py:1306 -msgid "Lessons" -msgstr "" - -#: aleksis/apps/chronos/models.py:403 aleksis/apps/chronos/models.py:1116 -msgid "Week" -msgstr "" - -#: aleksis/apps/chronos/models.py:404 aleksis/apps/chronos/models.py:1117 -msgid "Year" -msgstr "" - -#: aleksis/apps/chronos/models.py:407 aleksis/apps/chronos/models.py:620 -msgid "Lesson period" -msgstr "" - -#: aleksis/apps/chronos/models.py:425 aleksis/apps/chronos/models.py:504 -#: aleksis/apps/chronos/models.py:673 aleksis/apps/chronos/models.py:1144 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 -msgid "Room" -msgstr "Salle" - -#: aleksis/apps/chronos/models.py:428 -msgid "Cancelled?" -msgstr "" - -#: aleksis/apps/chronos/models.py:430 -msgid "Cancelled for teachers?" -msgstr "" - -#: aleksis/apps/chronos/models.py:433 aleksis/apps/chronos/models.py:692 -#: aleksis/apps/chronos/models.py:734 aleksis/apps/chronos/models.py:1147 -#: aleksis/apps/chronos/models.py:1357 -msgid "Comment" -msgstr "" - -#: aleksis/apps/chronos/models.py:437 -msgid "Lessons can only be either substituted or cancelled." -msgstr "" - -#: aleksis/apps/chronos/models.py:477 -msgid "Lesson substitution" -msgstr "" - -#: aleksis/apps/chronos/models.py:478 -msgid "Lesson substitutions" -msgstr "" - -#: aleksis/apps/chronos/models.py:621 -msgid "Lesson periods" -msgstr "" - -#: aleksis/apps/chronos/models.py:635 aleksis/apps/chronos/models.py:648 -msgid "Absence reason" -msgstr "" - -#: aleksis/apps/chronos/models.py:636 -msgid "Absence reasons" -msgstr "" - -#: aleksis/apps/chronos/models.py:657 aleksis/apps/chronos/models.py:889 -#: aleksis/apps/chronos/models.py:943 -msgid "Teacher" -msgstr "prof" - -#: aleksis/apps/chronos/models.py:665 -msgid "Group" -msgstr "groupe" - -#: aleksis/apps/chronos/models.py:681 aleksis/apps/chronos/models.py:723 -msgid "Start period" -msgstr "" - -#: aleksis/apps/chronos/models.py:688 aleksis/apps/chronos/models.py:729 -msgid "End period" -msgstr "" - -#: aleksis/apps/chronos/models.py:702 -msgid "Unknown absence" -msgstr "" - -#: aleksis/apps/chronos/models.py:707 -msgid "Absence" -msgstr "" - -#: aleksis/apps/chronos/models.py:708 -msgid "Absences" -msgstr "Absences" - -#: aleksis/apps/chronos/models.py:719 -msgid "Date of exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:733 aleksis/apps/chronos/models.py:746 -#: aleksis/apps/chronos/models.py:977 -msgid "Title" -msgstr "" - -#: aleksis/apps/chronos/models.py:739 -msgid "Exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:740 -msgid "Exams" -msgstr "" - -#: aleksis/apps/chronos/models.py:749 -msgid "Comments" -msgstr "" - -#: aleksis/apps/chronos/models.py:788 -msgid "Holiday" -msgstr "" - -#: aleksis/apps/chronos/models.py:789 -msgid "Holidays" -msgstr "" - -#: aleksis/apps/chronos/models.py:803 aleksis/apps/chronos/models.py:879 -msgid "Supervision area" -msgstr "" - -#: aleksis/apps/chronos/models.py:804 -msgid "Supervision areas" -msgstr "" - -#: aleksis/apps/chronos/models.py:816 -msgid "Time period after break starts" -msgstr "" - -#: aleksis/apps/chronos/models.py:824 -msgid "Time period before break ends" -msgstr "" - -#: aleksis/apps/chronos/models.py:864 aleksis/apps/chronos/models.py:883 -msgid "Break" -msgstr "" - -#: aleksis/apps/chronos/models.py:865 -msgid "Breaks" -msgstr "" - -#: aleksis/apps/chronos/models.py:923 aleksis/apps/chronos/models.py:936 -#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 -msgid "Supervision" -msgstr "" - -#: aleksis/apps/chronos/models.py:924 aleksis/apps/chronos/models.py:1622 -msgid "Supervisions" -msgstr "" - -#: aleksis/apps/chronos/models.py:932 -msgid "Date" -msgstr "Date" - -#: aleksis/apps/chronos/models.py:966 -msgid "Supervision substitution" -msgstr "" - -#: aleksis/apps/chronos/models.py:967 -msgid "Supervision substitutions" -msgstr "" - -#: aleksis/apps/chronos/models.py:985 -msgid "Start time period" -msgstr "" - -#: aleksis/apps/chronos/models.py:991 -msgid "End time period" -msgstr "" - -#: aleksis/apps/chronos/models.py:996 aleksis/apps/chronos/models.py:1332 +#: aleksis/apps/chronos/models.py:185 #: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 msgid "Rooms" msgstr "" -#: aleksis/apps/chronos/models.py:1005 -#, python-brace-format -msgid "Event {pk}" -msgstr "" - -#: aleksis/apps/chronos/models.py:1103 -msgid "Event" -msgstr "" - -#: aleksis/apps/chronos/models.py:1104 -msgid "Events" -msgstr "" - -#: aleksis/apps/chronos/models.py:1152 -msgid "Related exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:1181 -msgid "Extra lesson" -msgstr "" - -#: aleksis/apps/chronos/models.py:1182 -msgid "Extra lessons" -msgstr "" - -#: aleksis/apps/chronos/models.py:1194 -msgid "Number of days shown in the plan" -msgstr "" - -#: aleksis/apps/chronos/models.py:1198 -msgid "Show header box" -msgstr "" - -#: aleksis/apps/chronos/models.py:1199 aleksis/apps/chronos/preferences.py:100 -msgid "The header box shows affected teachers/groups." -msgstr "" - -#: aleksis/apps/chronos/models.py:1206 -msgid "Revision which triggered the last update" -msgstr "" - -#: aleksis/apps/chronos/models.py:1278 -msgid "Automatic plan" -msgstr "" - -#: aleksis/apps/chronos/models.py:1279 -msgid "Automatic plans" -msgstr "" - -#: aleksis/apps/chronos/models.py:1293 -msgid "Can view all room timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1294 -msgid "Can view all group timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1295 -msgid "Can view all person timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1296 -msgid "Can view timetable overview" -msgstr "" - -#: aleksis/apps/chronos/models.py:1297 -msgid "Can view all lessons per day" -msgstr "" - -#: aleksis/apps/chronos/models.py:1298 -msgid "Can view all supervisions per day" -msgstr "" - -#: aleksis/apps/chronos/models.py:1313 -msgid "Start slot number" -msgstr "" - -#: aleksis/apps/chronos/models.py:1316 -msgid "End slot number" -msgstr "" +#: aleksis/apps/chronos/models.py:191 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 +#: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 +msgid "Teachers" +msgstr "Profs" -#: aleksis/apps/chronos/models.py:1320 -msgid "Course" -msgstr "" +#: aleksis/apps/chronos/models.py:198 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 +msgid "Subject" +msgstr "Sujet" -#: aleksis/apps/chronos/models.py:1353 +#: aleksis/apps/chronos/models.py:206 #: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 msgid "Cancelled" msgstr "" -#: aleksis/apps/chronos/models.py:1401 aleksis/apps/chronos/models.py:1413 -#: aleksis/apps/chronos/models.py:1425 +#: aleksis/apps/chronos/models.py:210 +msgid "Comment" +msgstr "" + +#: aleksis/apps/chronos/models.py:254 aleksis/apps/chronos/models.py:266 +#: aleksis/apps/chronos/models.py:278 msgid "{} (instead of {})" msgstr "" -#: aleksis/apps/chronos/models.py:1614 +#: aleksis/apps/chronos/models.py:283 aleksis/apps/chronos/models.py:307 +msgid "Lesson" +msgstr "Cours" + +#: aleksis/apps/chronos/models.py:467 #, fuzzy #| msgid "Lesson" msgid "Lesson Event" msgstr "Cours" -#: aleksis/apps/chronos/models.py:1615 +#: aleksis/apps/chronos/models.py:468 #, fuzzy #| msgid "Lesson" msgid "Lesson Events" msgstr "Cours" -#: aleksis/apps/chronos/models.py:1630 +#: aleksis/apps/chronos/models.py:475 +msgid "Supervisions" +msgstr "" + +#: aleksis/apps/chronos/models.py:483 msgid "Supervision: {}" msgstr "" @@ -451,72 +164,39 @@ msgstr "" msgid "If a lesson or substitution has only one group and this group has parent groups, show the parent groups instead of the original group." msgstr "" -#: aleksis/apps/chronos/preferences.py:42 -msgid "Shorten groups in timetable views" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:43 -msgid "If there are more groups than the set limit, they will be collapsed." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:51 -msgid "Limit of groups for shortening of groups" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:53 -msgid "If a user activates shortening of groups,they will be collapsed if there are more groups than this limit." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:65 +#: aleksis/apps/chronos/preferences.py:44 msgid "Relevant days for substitution plans" msgstr "" -#: aleksis/apps/chronos/preferences.py:82 +#: aleksis/apps/chronos/preferences.py:61 msgid "Time when substitution plans switch to the next day" msgstr "" -#: aleksis/apps/chronos/preferences.py:91 +#: aleksis/apps/chronos/preferences.py:70 msgid "Number of days shown on substitutions print view" msgstr "" -#: aleksis/apps/chronos/preferences.py:99 +#: aleksis/apps/chronos/preferences.py:78 msgid "Show header box in substitution views" msgstr "" -#: aleksis/apps/chronos/preferences.py:109 +#: aleksis/apps/chronos/preferences.py:88 msgid "Show parent groups in header box in substitution views instead of original groups" msgstr "" -#: aleksis/apps/chronos/preferences.py:118 -msgid "How many days in advance users should be notified about timetable changes?" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:126 -msgid "Time for sending notifications about timetable changes" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:129 -msgid "This is only used for scheduling notifications which doesn't affect the time period configured above. All other notifications affecting the next days are sent immediately." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:140 -#: aleksis/apps/chronos/preferences.py:148 -msgid "Send notifications for current timetable changes" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:158 +#: aleksis/apps/chronos/preferences.py:99 msgid "Group types to show in timetables" msgstr "" -#: aleksis/apps/chronos/preferences.py:159 +#: aleksis/apps/chronos/preferences.py:100 msgid "If you leave it empty, all groups will be shown." msgstr "" -#: aleksis/apps/chronos/preferences.py:169 +#: aleksis/apps/chronos/preferences.py:110 msgid "Lesson calendar feed color" msgstr "" -#: aleksis/apps/chronos/preferences.py:181 +#: aleksis/apps/chronos/preferences.py:122 msgid "Supervision calendar feed color" msgstr "" @@ -540,6 +220,10 @@ msgstr "" msgid "all day" msgstr "" +#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 +msgid "Supervision" +msgstr "" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:11 msgid "Print: Substitutions" msgstr "" @@ -552,6 +236,10 @@ msgstr "" msgid "Time" msgstr "" +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 +msgid "Room" +msgstr "Salle" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:31 msgid "Notes" msgstr "Notes" @@ -564,95 +252,24 @@ msgstr "" msgid "Areas" msgstr "" -#: aleksis/apps/chronos/util/notifications.py:48 -#, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has been cancelled." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:55 -#, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has some current changes." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:64 -#, python-brace-format -msgid "The teacher {old} is substituted by {new}." -msgid_plural "The teachers {old} are substituted by {new}." -msgstr[0] "" -msgstr[1] "" - -#: aleksis/apps/chronos/util/notifications.py:76 -#, python-brace-format -msgid "The subject is changed to {subject}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:82 -#, python-brace-format -msgid "The lesson is moved from {old} to {new}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:91 -#, python-brace-format -msgid "There is an additional comment: {comment}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:99 -#, python-brace-format -msgid "There is an event that starts on {date_start}, {period_from}. period and ends on {date_end}, {period_to}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:112 -#, python-brace-format -msgid "There is an event on {date} from the {period_from}. period to the {period_to}. period:" -msgstr "" +#, fuzzy +#~| msgid "Subject" +#~ msgid "Subjects" +#~ msgstr "Sujet" -#: aleksis/apps/chronos/util/notifications.py:123 -#: aleksis/apps/chronos/util/notifications.py:143 -#, python-brace-format -msgid "Groups: {groups}" -msgstr "" +#~ msgid "Teacher" +#~ msgstr "prof" -#: aleksis/apps/chronos/util/notifications.py:125 -#: aleksis/apps/chronos/util/notifications.py:147 -#, python-brace-format -msgid "Teachers: {teachers}" -msgstr "" +#~ msgid "Group" +#~ msgstr "groupe" -#: aleksis/apps/chronos/util/notifications.py:128 -#, python-brace-format -msgid "Rooms: {rooms}" -msgstr "" +#~ msgid "Absences" +#~ msgstr "Absences" -#: aleksis/apps/chronos/util/notifications.py:135 -#, python-brace-format -msgid "There is an extra lesson on {date} in the {period}. period:" -msgstr "" +#~ msgid "Date" +#~ msgstr "Date" -#: aleksis/apps/chronos/util/notifications.py:145 #, fuzzy, python-brace-format -#| msgid "Subject" -msgid "Subject: {subject}" -msgstr "Sujet" - -#: aleksis/apps/chronos/util/notifications.py:149 -#, python-brace-format -msgid "Room: {room}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:151 -#, python-brace-format -msgid "Comment: {comment}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:154 -#, python-brace-format -msgid "The supervision of {old} on {date} between the {period_from}. period and the {period_to}. period in the area {area} is substituted by {new}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:204 -msgid "Timetable" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:205 -msgid "There are current changes to your timetable." -msgstr "" +#~| msgid "Subject" +#~ msgid "Subject: {subject}" +#~ msgstr "Sujet" diff --git a/aleksis/apps/chronos/locale/la/LC_MESSAGES/django.po b/aleksis/apps/chronos/locale/la/LC_MESSAGES/django.po index 279ead538768dffda25f3cdb31fb9af609117621..8d0616a26f634a0470c7980ef1529cd4a1642a10 100644 --- a/aleksis/apps/chronos/locale/la/LC_MESSAGES/django.po +++ b/aleksis/apps/chronos/locale/la/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-18 14:23+0200\n" +"POT-Creation-Date: 2024-10-17 10:48+0200\n" "PO-Revision-Date: 2020-08-23 13:49+0000\n" "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n" "Language-Team: Latin <https://translate.edugit.org/projects/aleksis/aleksis-app-chronos/la/>\n" @@ -18,418 +18,133 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.1.1\n" -#: aleksis/apps/chronos/mixins.py:25 -msgid "Linked validity range" -msgstr "" - -#: aleksis/apps/chronos/model_extensions.py:145 +#: aleksis/apps/chronos/model_extensions.py:9 msgid "Can view group timetable" msgstr "" -#: aleksis/apps/chronos/model_extensions.py:149 +#: aleksis/apps/chronos/model_extensions.py:13 msgid "Can view person timetable" msgstr "" -#: aleksis/apps/chronos/models.py:87 -msgid "School term" -msgstr "" - -#: aleksis/apps/chronos/models.py:90 aleksis/apps/chronos/models.py:626 -#: aleksis/apps/chronos/models.py:1310 -msgid "Name" -msgstr "" - -#: aleksis/apps/chronos/models.py:92 aleksis/apps/chronos/models.py:676 -#: aleksis/apps/chronos/models.py:747 aleksis/apps/chronos/models.py:979 -msgid "Start date" -msgstr "" - -#: aleksis/apps/chronos/models.py:93 aleksis/apps/chronos/models.py:677 -#: aleksis/apps/chronos/models.py:748 aleksis/apps/chronos/models.py:980 -msgid "End date" +#: aleksis/apps/chronos/models.py:48 +msgid "Number of days shown in the plan" msgstr "" -#: aleksis/apps/chronos/models.py:112 -msgid "The start date must be earlier than the end date." +#: aleksis/apps/chronos/models.py:52 +msgid "Show header box" msgstr "" -#: aleksis/apps/chronos/models.py:118 -msgid "The validity range must be within the school term." +#: aleksis/apps/chronos/models.py:53 aleksis/apps/chronos/preferences.py:79 +msgid "The header box shows affected teachers/groups." msgstr "" -#: aleksis/apps/chronos/models.py:125 -msgid "There is already a validity range for this time or a part of this time." +#: aleksis/apps/chronos/models.py:60 +msgid "Revision which triggered the last update" msgstr "" #: aleksis/apps/chronos/models.py:132 -msgid "Validity range" +msgid "Automatic plan" msgstr "" #: aleksis/apps/chronos/models.py:133 -msgid "Validity ranges" +msgid "Automatic plans" msgstr "" -#: aleksis/apps/chronos/models.py:149 -msgid "Week day" +#: aleksis/apps/chronos/models.py:147 +msgid "Can view all room timetables" msgstr "" -#: aleksis/apps/chronos/models.py:151 -msgid "Number of period" +#: aleksis/apps/chronos/models.py:148 +msgid "Can view all group timetables" msgstr "" -#: aleksis/apps/chronos/models.py:153 -msgid "Start time" +#: aleksis/apps/chronos/models.py:149 +msgid "Can view all person timetables" msgstr "" -#: aleksis/apps/chronos/models.py:154 -msgid "End time" +#: aleksis/apps/chronos/models.py:150 +msgid "Can view timetable overview" msgstr "" -#: aleksis/apps/chronos/models.py:324 aleksis/apps/chronos/models.py:496 -#: aleksis/apps/chronos/models.py:1122 -msgid "Time period" +#: aleksis/apps/chronos/models.py:151 +msgid "Can view substitutions table" msgstr "" -#: aleksis/apps/chronos/models.py:325 -msgid "Time periods" +#: aleksis/apps/chronos/models.py:159 +msgid "Lessons" msgstr "" -#: aleksis/apps/chronos/models.py:329 aleksis/apps/chronos/models.py:625 -#: aleksis/apps/chronos/models.py:793 aleksis/apps/chronos/models.py:810 -msgid "Short name" +#: aleksis/apps/chronos/models.py:163 +msgid "Name" msgstr "" -#: aleksis/apps/chronos/models.py:330 aleksis/apps/chronos/models.py:794 -#: aleksis/apps/chronos/models.py:811 -msgid "Long name" +#: aleksis/apps/chronos/models.py:166 +msgid "Start slot number" msgstr "" -#: aleksis/apps/chronos/models.py:332 -msgid "Foreground colour" +#: aleksis/apps/chronos/models.py:169 +msgid "End slot number" msgstr "" -#: aleksis/apps/chronos/models.py:333 -msgid "Background colour" +#: aleksis/apps/chronos/models.py:173 +msgid "Course" msgstr "" -#: aleksis/apps/chronos/models.py:340 aleksis/apps/chronos/models.py:349 -#: aleksis/apps/chronos/models.py:416 aleksis/apps/chronos/models.py:1129 -#: aleksis/apps/chronos/models.py:1345 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 -msgid "Subject" +#: aleksis/apps/chronos/models.py:179 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 +msgid "Groups" msgstr "" -#: aleksis/apps/chronos/models.py:341 -msgid "Subjects" +#: aleksis/apps/chronos/models.py:185 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 +msgid "Rooms" msgstr "" -#: aleksis/apps/chronos/models.py:352 aleksis/apps/chronos/models.py:422 -#: aleksis/apps/chronos/models.py:998 aleksis/apps/chronos/models.py:1137 -#: aleksis/apps/chronos/models.py:1338 +#: aleksis/apps/chronos/models.py:191 #: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 #: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 msgid "Teachers" msgstr "" -#: aleksis/apps/chronos/models.py:358 -msgid "Periods" -msgstr "" - -#: aleksis/apps/chronos/models.py:360 aleksis/apps/chronos/models.py:995 -#: aleksis/apps/chronos/models.py:1132 aleksis/apps/chronos/models.py:1326 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 -msgid "Groups" -msgstr "" - -#: aleksis/apps/chronos/models.py:394 aleksis/apps/chronos/models.py:490 -#: aleksis/apps/chronos/models.py:716 aleksis/apps/chronos/models.py:1430 -#: aleksis/apps/chronos/models.py:1454 -msgid "Lesson" -msgstr "" - -#: aleksis/apps/chronos/models.py:395 aleksis/apps/chronos/models.py:1306 -msgid "Lessons" -msgstr "" - -#: aleksis/apps/chronos/models.py:403 aleksis/apps/chronos/models.py:1116 -msgid "Week" -msgstr "" - -#: aleksis/apps/chronos/models.py:404 aleksis/apps/chronos/models.py:1117 -msgid "Year" -msgstr "" - -#: aleksis/apps/chronos/models.py:407 aleksis/apps/chronos/models.py:620 -msgid "Lesson period" -msgstr "" - -#: aleksis/apps/chronos/models.py:425 aleksis/apps/chronos/models.py:504 -#: aleksis/apps/chronos/models.py:673 aleksis/apps/chronos/models.py:1144 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 -msgid "Room" -msgstr "" - -#: aleksis/apps/chronos/models.py:428 -msgid "Cancelled?" +#: aleksis/apps/chronos/models.py:198 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 +msgid "Subject" msgstr "" -#: aleksis/apps/chronos/models.py:430 -msgid "Cancelled for teachers?" +#: aleksis/apps/chronos/models.py:206 +#: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 +msgid "Cancelled" msgstr "" -#: aleksis/apps/chronos/models.py:433 aleksis/apps/chronos/models.py:692 -#: aleksis/apps/chronos/models.py:734 aleksis/apps/chronos/models.py:1147 -#: aleksis/apps/chronos/models.py:1357 +#: aleksis/apps/chronos/models.py:210 msgid "Comment" msgstr "" -#: aleksis/apps/chronos/models.py:437 -msgid "Lessons can only be either substituted or cancelled." -msgstr "" - -#: aleksis/apps/chronos/models.py:477 -msgid "Lesson substitution" -msgstr "" - -#: aleksis/apps/chronos/models.py:478 -msgid "Lesson substitutions" -msgstr "" - -#: aleksis/apps/chronos/models.py:621 -msgid "Lesson periods" -msgstr "" - -#: aleksis/apps/chronos/models.py:635 aleksis/apps/chronos/models.py:648 -msgid "Absence reason" -msgstr "" - -#: aleksis/apps/chronos/models.py:636 -msgid "Absence reasons" -msgstr "" - -#: aleksis/apps/chronos/models.py:657 aleksis/apps/chronos/models.py:889 -#: aleksis/apps/chronos/models.py:943 -msgid "Teacher" -msgstr "" - -#: aleksis/apps/chronos/models.py:665 -msgid "Group" -msgstr "Grex" - -#: aleksis/apps/chronos/models.py:681 aleksis/apps/chronos/models.py:723 -msgid "Start period" -msgstr "" - -#: aleksis/apps/chronos/models.py:688 aleksis/apps/chronos/models.py:729 -msgid "End period" -msgstr "" - -#: aleksis/apps/chronos/models.py:702 -msgid "Unknown absence" -msgstr "" - -#: aleksis/apps/chronos/models.py:707 -msgid "Absence" -msgstr "" - -#: aleksis/apps/chronos/models.py:708 -msgid "Absences" -msgstr "" - -#: aleksis/apps/chronos/models.py:719 -msgid "Date of exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:733 aleksis/apps/chronos/models.py:746 -#: aleksis/apps/chronos/models.py:977 -msgid "Title" -msgstr "" - -#: aleksis/apps/chronos/models.py:739 -msgid "Exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:740 -msgid "Exams" -msgstr "" - -#: aleksis/apps/chronos/models.py:749 -msgid "Comments" -msgstr "" - -#: aleksis/apps/chronos/models.py:788 -msgid "Holiday" -msgstr "" - -#: aleksis/apps/chronos/models.py:789 -msgid "Holidays" -msgstr "" - -#: aleksis/apps/chronos/models.py:803 aleksis/apps/chronos/models.py:879 -msgid "Supervision area" -msgstr "" - -#: aleksis/apps/chronos/models.py:804 -msgid "Supervision areas" -msgstr "" - -#: aleksis/apps/chronos/models.py:816 -msgid "Time period after break starts" -msgstr "" - -#: aleksis/apps/chronos/models.py:824 -msgid "Time period before break ends" +#: aleksis/apps/chronos/models.py:254 aleksis/apps/chronos/models.py:266 +#: aleksis/apps/chronos/models.py:278 +msgid "{} (instead of {})" msgstr "" -#: aleksis/apps/chronos/models.py:864 aleksis/apps/chronos/models.py:883 -msgid "Break" +#: aleksis/apps/chronos/models.py:283 aleksis/apps/chronos/models.py:307 +msgid "Lesson" msgstr "" -#: aleksis/apps/chronos/models.py:865 -msgid "Breaks" +#: aleksis/apps/chronos/models.py:467 +msgid "Lesson Event" msgstr "" -#: aleksis/apps/chronos/models.py:923 aleksis/apps/chronos/models.py:936 -#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 -msgid "Supervision" +#: aleksis/apps/chronos/models.py:468 +msgid "Lesson Events" msgstr "" -#: aleksis/apps/chronos/models.py:924 aleksis/apps/chronos/models.py:1622 +#: aleksis/apps/chronos/models.py:475 msgid "Supervisions" msgstr "" -#: aleksis/apps/chronos/models.py:932 -msgid "Date" -msgstr "" - -#: aleksis/apps/chronos/models.py:966 -msgid "Supervision substitution" -msgstr "" - -#: aleksis/apps/chronos/models.py:967 -msgid "Supervision substitutions" -msgstr "" - -#: aleksis/apps/chronos/models.py:985 -msgid "Start time period" -msgstr "" - -#: aleksis/apps/chronos/models.py:991 -msgid "End time period" -msgstr "" - -#: aleksis/apps/chronos/models.py:996 aleksis/apps/chronos/models.py:1332 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 -msgid "Rooms" -msgstr "" - -#: aleksis/apps/chronos/models.py:1005 -#, python-brace-format -msgid "Event {pk}" -msgstr "" - -#: aleksis/apps/chronos/models.py:1103 -msgid "Event" -msgstr "" - -#: aleksis/apps/chronos/models.py:1104 -msgid "Events" -msgstr "" - -#: aleksis/apps/chronos/models.py:1152 -msgid "Related exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:1181 -msgid "Extra lesson" -msgstr "" - -#: aleksis/apps/chronos/models.py:1182 -msgid "Extra lessons" -msgstr "" - -#: aleksis/apps/chronos/models.py:1194 -msgid "Number of days shown in the plan" -msgstr "" - -#: aleksis/apps/chronos/models.py:1198 -msgid "Show header box" -msgstr "" - -#: aleksis/apps/chronos/models.py:1199 aleksis/apps/chronos/preferences.py:100 -msgid "The header box shows affected teachers/groups." -msgstr "" - -#: aleksis/apps/chronos/models.py:1206 -msgid "Revision which triggered the last update" -msgstr "" - -#: aleksis/apps/chronos/models.py:1278 -msgid "Automatic plan" -msgstr "" - -#: aleksis/apps/chronos/models.py:1279 -msgid "Automatic plans" -msgstr "" - -#: aleksis/apps/chronos/models.py:1293 -msgid "Can view all room timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1294 -msgid "Can view all group timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1295 -msgid "Can view all person timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1296 -msgid "Can view timetable overview" -msgstr "" - -#: aleksis/apps/chronos/models.py:1297 -msgid "Can view all lessons per day" -msgstr "" - -#: aleksis/apps/chronos/models.py:1298 -msgid "Can view all supervisions per day" -msgstr "" - -#: aleksis/apps/chronos/models.py:1313 -msgid "Start slot number" -msgstr "" - -#: aleksis/apps/chronos/models.py:1316 -msgid "End slot number" -msgstr "" - -#: aleksis/apps/chronos/models.py:1320 -msgid "Course" -msgstr "" - -#: aleksis/apps/chronos/models.py:1353 -#: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 -msgid "Cancelled" -msgstr "" - -#: aleksis/apps/chronos/models.py:1401 aleksis/apps/chronos/models.py:1413 -#: aleksis/apps/chronos/models.py:1425 -msgid "{} (instead of {})" -msgstr "" - -#: aleksis/apps/chronos/models.py:1614 -msgid "Lesson Event" -msgstr "" - -#: aleksis/apps/chronos/models.py:1615 -msgid "Lesson Events" -msgstr "" - -#: aleksis/apps/chronos/models.py:1630 +#: aleksis/apps/chronos/models.py:483 msgid "Supervision: {}" msgstr "" @@ -445,72 +160,39 @@ msgstr "" msgid "If a lesson or substitution has only one group and this group has parent groups, show the parent groups instead of the original group." msgstr "" -#: aleksis/apps/chronos/preferences.py:42 -msgid "Shorten groups in timetable views" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:43 -msgid "If there are more groups than the set limit, they will be collapsed." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:51 -msgid "Limit of groups for shortening of groups" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:53 -msgid "If a user activates shortening of groups,they will be collapsed if there are more groups than this limit." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:65 +#: aleksis/apps/chronos/preferences.py:44 msgid "Relevant days for substitution plans" msgstr "" -#: aleksis/apps/chronos/preferences.py:82 +#: aleksis/apps/chronos/preferences.py:61 msgid "Time when substitution plans switch to the next day" msgstr "" -#: aleksis/apps/chronos/preferences.py:91 +#: aleksis/apps/chronos/preferences.py:70 msgid "Number of days shown on substitutions print view" msgstr "" -#: aleksis/apps/chronos/preferences.py:99 +#: aleksis/apps/chronos/preferences.py:78 msgid "Show header box in substitution views" msgstr "" -#: aleksis/apps/chronos/preferences.py:109 +#: aleksis/apps/chronos/preferences.py:88 msgid "Show parent groups in header box in substitution views instead of original groups" msgstr "" -#: aleksis/apps/chronos/preferences.py:118 -msgid "How many days in advance users should be notified about timetable changes?" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:126 -msgid "Time for sending notifications about timetable changes" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:129 -msgid "This is only used for scheduling notifications which doesn't affect the time period configured above. All other notifications affecting the next days are sent immediately." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:140 -#: aleksis/apps/chronos/preferences.py:148 -msgid "Send notifications for current timetable changes" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:158 +#: aleksis/apps/chronos/preferences.py:99 msgid "Group types to show in timetables" msgstr "" -#: aleksis/apps/chronos/preferences.py:159 +#: aleksis/apps/chronos/preferences.py:100 msgid "If you leave it empty, all groups will be shown." msgstr "" -#: aleksis/apps/chronos/preferences.py:169 +#: aleksis/apps/chronos/preferences.py:110 msgid "Lesson calendar feed color" msgstr "" -#: aleksis/apps/chronos/preferences.py:181 +#: aleksis/apps/chronos/preferences.py:122 msgid "Supervision calendar feed color" msgstr "" @@ -534,6 +216,10 @@ msgstr "" msgid "all day" msgstr "" +#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 +msgid "Supervision" +msgstr "" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:11 msgid "Print: Substitutions" msgstr "" @@ -546,6 +232,10 @@ msgstr "" msgid "Time" msgstr "" +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 +msgid "Room" +msgstr "" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:31 msgid "Notes" msgstr "" @@ -558,94 +248,5 @@ msgstr "" msgid "Areas" msgstr "" -#: aleksis/apps/chronos/util/notifications.py:48 -#, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has been cancelled." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:55 -#, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has some current changes." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:64 -#, python-brace-format -msgid "The teacher {old} is substituted by {new}." -msgid_plural "The teachers {old} are substituted by {new}." -msgstr[0] "" -msgstr[1] "" - -#: aleksis/apps/chronos/util/notifications.py:76 -#, python-brace-format -msgid "The subject is changed to {subject}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:82 -#, python-brace-format -msgid "The lesson is moved from {old} to {new}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:91 -#, python-brace-format -msgid "There is an additional comment: {comment}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:99 -#, python-brace-format -msgid "There is an event that starts on {date_start}, {period_from}. period and ends on {date_end}, {period_to}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:112 -#, python-brace-format -msgid "There is an event on {date} from the {period_from}. period to the {period_to}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:123 -#: aleksis/apps/chronos/util/notifications.py:143 -#, python-brace-format -msgid "Groups: {groups}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:125 -#: aleksis/apps/chronos/util/notifications.py:147 -#, python-brace-format -msgid "Teachers: {teachers}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:128 -#, python-brace-format -msgid "Rooms: {rooms}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:135 -#, python-brace-format -msgid "There is an extra lesson on {date} in the {period}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:145 -#, python-brace-format -msgid "Subject: {subject}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:149 -#, python-brace-format -msgid "Room: {room}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:151 -#, python-brace-format -msgid "Comment: {comment}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:154 -#, python-brace-format -msgid "The supervision of {old} on {date} between the {period_from}. period and the {period_to}. period in the area {area} is substituted by {new}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:204 -msgid "Timetable" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:205 -msgid "There are current changes to your timetable." -msgstr "" +#~ msgid "Group" +#~ msgstr "Grex" diff --git a/aleksis/apps/chronos/locale/nb_NO/LC_MESSAGES/django.po b/aleksis/apps/chronos/locale/nb_NO/LC_MESSAGES/django.po index cfc760f01b3206aaf7123340eccf529e9e4db354..e7015c68a7c47522f5d628d897256ea935a950ed 100644 --- a/aleksis/apps/chronos/locale/nb_NO/LC_MESSAGES/django.po +++ b/aleksis/apps/chronos/locale/nb_NO/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-18 14:23+0200\n" +"POT-Creation-Date: 2024-10-17 10:48+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,418 +17,133 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: aleksis/apps/chronos/mixins.py:25 -msgid "Linked validity range" -msgstr "" - -#: aleksis/apps/chronos/model_extensions.py:145 +#: aleksis/apps/chronos/model_extensions.py:9 msgid "Can view group timetable" msgstr "" -#: aleksis/apps/chronos/model_extensions.py:149 +#: aleksis/apps/chronos/model_extensions.py:13 msgid "Can view person timetable" msgstr "" -#: aleksis/apps/chronos/models.py:87 -msgid "School term" -msgstr "" - -#: aleksis/apps/chronos/models.py:90 aleksis/apps/chronos/models.py:626 -#: aleksis/apps/chronos/models.py:1310 -msgid "Name" -msgstr "" - -#: aleksis/apps/chronos/models.py:92 aleksis/apps/chronos/models.py:676 -#: aleksis/apps/chronos/models.py:747 aleksis/apps/chronos/models.py:979 -msgid "Start date" -msgstr "" - -#: aleksis/apps/chronos/models.py:93 aleksis/apps/chronos/models.py:677 -#: aleksis/apps/chronos/models.py:748 aleksis/apps/chronos/models.py:980 -msgid "End date" +#: aleksis/apps/chronos/models.py:48 +msgid "Number of days shown in the plan" msgstr "" -#: aleksis/apps/chronos/models.py:112 -msgid "The start date must be earlier than the end date." +#: aleksis/apps/chronos/models.py:52 +msgid "Show header box" msgstr "" -#: aleksis/apps/chronos/models.py:118 -msgid "The validity range must be within the school term." +#: aleksis/apps/chronos/models.py:53 aleksis/apps/chronos/preferences.py:79 +msgid "The header box shows affected teachers/groups." msgstr "" -#: aleksis/apps/chronos/models.py:125 -msgid "There is already a validity range for this time or a part of this time." +#: aleksis/apps/chronos/models.py:60 +msgid "Revision which triggered the last update" msgstr "" #: aleksis/apps/chronos/models.py:132 -msgid "Validity range" +msgid "Automatic plan" msgstr "" #: aleksis/apps/chronos/models.py:133 -msgid "Validity ranges" +msgid "Automatic plans" msgstr "" -#: aleksis/apps/chronos/models.py:149 -msgid "Week day" +#: aleksis/apps/chronos/models.py:147 +msgid "Can view all room timetables" msgstr "" -#: aleksis/apps/chronos/models.py:151 -msgid "Number of period" +#: aleksis/apps/chronos/models.py:148 +msgid "Can view all group timetables" msgstr "" -#: aleksis/apps/chronos/models.py:153 -msgid "Start time" +#: aleksis/apps/chronos/models.py:149 +msgid "Can view all person timetables" msgstr "" -#: aleksis/apps/chronos/models.py:154 -msgid "End time" +#: aleksis/apps/chronos/models.py:150 +msgid "Can view timetable overview" msgstr "" -#: aleksis/apps/chronos/models.py:324 aleksis/apps/chronos/models.py:496 -#: aleksis/apps/chronos/models.py:1122 -msgid "Time period" +#: aleksis/apps/chronos/models.py:151 +msgid "Can view substitutions table" msgstr "" -#: aleksis/apps/chronos/models.py:325 -msgid "Time periods" +#: aleksis/apps/chronos/models.py:159 +msgid "Lessons" msgstr "" -#: aleksis/apps/chronos/models.py:329 aleksis/apps/chronos/models.py:625 -#: aleksis/apps/chronos/models.py:793 aleksis/apps/chronos/models.py:810 -msgid "Short name" +#: aleksis/apps/chronos/models.py:163 +msgid "Name" msgstr "" -#: aleksis/apps/chronos/models.py:330 aleksis/apps/chronos/models.py:794 -#: aleksis/apps/chronos/models.py:811 -msgid "Long name" +#: aleksis/apps/chronos/models.py:166 +msgid "Start slot number" msgstr "" -#: aleksis/apps/chronos/models.py:332 -msgid "Foreground colour" +#: aleksis/apps/chronos/models.py:169 +msgid "End slot number" msgstr "" -#: aleksis/apps/chronos/models.py:333 -msgid "Background colour" +#: aleksis/apps/chronos/models.py:173 +msgid "Course" msgstr "" -#: aleksis/apps/chronos/models.py:340 aleksis/apps/chronos/models.py:349 -#: aleksis/apps/chronos/models.py:416 aleksis/apps/chronos/models.py:1129 -#: aleksis/apps/chronos/models.py:1345 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 -msgid "Subject" +#: aleksis/apps/chronos/models.py:179 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 +msgid "Groups" msgstr "" -#: aleksis/apps/chronos/models.py:341 -msgid "Subjects" +#: aleksis/apps/chronos/models.py:185 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 +msgid "Rooms" msgstr "" -#: aleksis/apps/chronos/models.py:352 aleksis/apps/chronos/models.py:422 -#: aleksis/apps/chronos/models.py:998 aleksis/apps/chronos/models.py:1137 -#: aleksis/apps/chronos/models.py:1338 +#: aleksis/apps/chronos/models.py:191 #: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 #: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 msgid "Teachers" msgstr "" -#: aleksis/apps/chronos/models.py:358 -msgid "Periods" -msgstr "" - -#: aleksis/apps/chronos/models.py:360 aleksis/apps/chronos/models.py:995 -#: aleksis/apps/chronos/models.py:1132 aleksis/apps/chronos/models.py:1326 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 -msgid "Groups" -msgstr "" - -#: aleksis/apps/chronos/models.py:394 aleksis/apps/chronos/models.py:490 -#: aleksis/apps/chronos/models.py:716 aleksis/apps/chronos/models.py:1430 -#: aleksis/apps/chronos/models.py:1454 -msgid "Lesson" -msgstr "" - -#: aleksis/apps/chronos/models.py:395 aleksis/apps/chronos/models.py:1306 -msgid "Lessons" -msgstr "" - -#: aleksis/apps/chronos/models.py:403 aleksis/apps/chronos/models.py:1116 -msgid "Week" -msgstr "" - -#: aleksis/apps/chronos/models.py:404 aleksis/apps/chronos/models.py:1117 -msgid "Year" -msgstr "" - -#: aleksis/apps/chronos/models.py:407 aleksis/apps/chronos/models.py:620 -msgid "Lesson period" -msgstr "" - -#: aleksis/apps/chronos/models.py:425 aleksis/apps/chronos/models.py:504 -#: aleksis/apps/chronos/models.py:673 aleksis/apps/chronos/models.py:1144 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 -msgid "Room" -msgstr "" - -#: aleksis/apps/chronos/models.py:428 -msgid "Cancelled?" +#: aleksis/apps/chronos/models.py:198 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 +msgid "Subject" msgstr "" -#: aleksis/apps/chronos/models.py:430 -msgid "Cancelled for teachers?" +#: aleksis/apps/chronos/models.py:206 +#: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 +msgid "Cancelled" msgstr "" -#: aleksis/apps/chronos/models.py:433 aleksis/apps/chronos/models.py:692 -#: aleksis/apps/chronos/models.py:734 aleksis/apps/chronos/models.py:1147 -#: aleksis/apps/chronos/models.py:1357 +#: aleksis/apps/chronos/models.py:210 msgid "Comment" msgstr "" -#: aleksis/apps/chronos/models.py:437 -msgid "Lessons can only be either substituted or cancelled." -msgstr "" - -#: aleksis/apps/chronos/models.py:477 -msgid "Lesson substitution" -msgstr "" - -#: aleksis/apps/chronos/models.py:478 -msgid "Lesson substitutions" -msgstr "" - -#: aleksis/apps/chronos/models.py:621 -msgid "Lesson periods" -msgstr "" - -#: aleksis/apps/chronos/models.py:635 aleksis/apps/chronos/models.py:648 -msgid "Absence reason" -msgstr "" - -#: aleksis/apps/chronos/models.py:636 -msgid "Absence reasons" -msgstr "" - -#: aleksis/apps/chronos/models.py:657 aleksis/apps/chronos/models.py:889 -#: aleksis/apps/chronos/models.py:943 -msgid "Teacher" -msgstr "" - -#: aleksis/apps/chronos/models.py:665 -msgid "Group" -msgstr "" - -#: aleksis/apps/chronos/models.py:681 aleksis/apps/chronos/models.py:723 -msgid "Start period" -msgstr "" - -#: aleksis/apps/chronos/models.py:688 aleksis/apps/chronos/models.py:729 -msgid "End period" -msgstr "" - -#: aleksis/apps/chronos/models.py:702 -msgid "Unknown absence" -msgstr "" - -#: aleksis/apps/chronos/models.py:707 -msgid "Absence" -msgstr "" - -#: aleksis/apps/chronos/models.py:708 -msgid "Absences" -msgstr "" - -#: aleksis/apps/chronos/models.py:719 -msgid "Date of exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:733 aleksis/apps/chronos/models.py:746 -#: aleksis/apps/chronos/models.py:977 -msgid "Title" -msgstr "" - -#: aleksis/apps/chronos/models.py:739 -msgid "Exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:740 -msgid "Exams" -msgstr "" - -#: aleksis/apps/chronos/models.py:749 -msgid "Comments" -msgstr "" - -#: aleksis/apps/chronos/models.py:788 -msgid "Holiday" -msgstr "" - -#: aleksis/apps/chronos/models.py:789 -msgid "Holidays" -msgstr "" - -#: aleksis/apps/chronos/models.py:803 aleksis/apps/chronos/models.py:879 -msgid "Supervision area" -msgstr "" - -#: aleksis/apps/chronos/models.py:804 -msgid "Supervision areas" -msgstr "" - -#: aleksis/apps/chronos/models.py:816 -msgid "Time period after break starts" -msgstr "" - -#: aleksis/apps/chronos/models.py:824 -msgid "Time period before break ends" +#: aleksis/apps/chronos/models.py:254 aleksis/apps/chronos/models.py:266 +#: aleksis/apps/chronos/models.py:278 +msgid "{} (instead of {})" msgstr "" -#: aleksis/apps/chronos/models.py:864 aleksis/apps/chronos/models.py:883 -msgid "Break" +#: aleksis/apps/chronos/models.py:283 aleksis/apps/chronos/models.py:307 +msgid "Lesson" msgstr "" -#: aleksis/apps/chronos/models.py:865 -msgid "Breaks" +#: aleksis/apps/chronos/models.py:467 +msgid "Lesson Event" msgstr "" -#: aleksis/apps/chronos/models.py:923 aleksis/apps/chronos/models.py:936 -#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 -msgid "Supervision" +#: aleksis/apps/chronos/models.py:468 +msgid "Lesson Events" msgstr "" -#: aleksis/apps/chronos/models.py:924 aleksis/apps/chronos/models.py:1622 +#: aleksis/apps/chronos/models.py:475 msgid "Supervisions" msgstr "" -#: aleksis/apps/chronos/models.py:932 -msgid "Date" -msgstr "" - -#: aleksis/apps/chronos/models.py:966 -msgid "Supervision substitution" -msgstr "" - -#: aleksis/apps/chronos/models.py:967 -msgid "Supervision substitutions" -msgstr "" - -#: aleksis/apps/chronos/models.py:985 -msgid "Start time period" -msgstr "" - -#: aleksis/apps/chronos/models.py:991 -msgid "End time period" -msgstr "" - -#: aleksis/apps/chronos/models.py:996 aleksis/apps/chronos/models.py:1332 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 -msgid "Rooms" -msgstr "" - -#: aleksis/apps/chronos/models.py:1005 -#, python-brace-format -msgid "Event {pk}" -msgstr "" - -#: aleksis/apps/chronos/models.py:1103 -msgid "Event" -msgstr "" - -#: aleksis/apps/chronos/models.py:1104 -msgid "Events" -msgstr "" - -#: aleksis/apps/chronos/models.py:1152 -msgid "Related exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:1181 -msgid "Extra lesson" -msgstr "" - -#: aleksis/apps/chronos/models.py:1182 -msgid "Extra lessons" -msgstr "" - -#: aleksis/apps/chronos/models.py:1194 -msgid "Number of days shown in the plan" -msgstr "" - -#: aleksis/apps/chronos/models.py:1198 -msgid "Show header box" -msgstr "" - -#: aleksis/apps/chronos/models.py:1199 aleksis/apps/chronos/preferences.py:100 -msgid "The header box shows affected teachers/groups." -msgstr "" - -#: aleksis/apps/chronos/models.py:1206 -msgid "Revision which triggered the last update" -msgstr "" - -#: aleksis/apps/chronos/models.py:1278 -msgid "Automatic plan" -msgstr "" - -#: aleksis/apps/chronos/models.py:1279 -msgid "Automatic plans" -msgstr "" - -#: aleksis/apps/chronos/models.py:1293 -msgid "Can view all room timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1294 -msgid "Can view all group timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1295 -msgid "Can view all person timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1296 -msgid "Can view timetable overview" -msgstr "" - -#: aleksis/apps/chronos/models.py:1297 -msgid "Can view all lessons per day" -msgstr "" - -#: aleksis/apps/chronos/models.py:1298 -msgid "Can view all supervisions per day" -msgstr "" - -#: aleksis/apps/chronos/models.py:1313 -msgid "Start slot number" -msgstr "" - -#: aleksis/apps/chronos/models.py:1316 -msgid "End slot number" -msgstr "" - -#: aleksis/apps/chronos/models.py:1320 -msgid "Course" -msgstr "" - -#: aleksis/apps/chronos/models.py:1353 -#: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 -msgid "Cancelled" -msgstr "" - -#: aleksis/apps/chronos/models.py:1401 aleksis/apps/chronos/models.py:1413 -#: aleksis/apps/chronos/models.py:1425 -msgid "{} (instead of {})" -msgstr "" - -#: aleksis/apps/chronos/models.py:1614 -msgid "Lesson Event" -msgstr "" - -#: aleksis/apps/chronos/models.py:1615 -msgid "Lesson Events" -msgstr "" - -#: aleksis/apps/chronos/models.py:1630 +#: aleksis/apps/chronos/models.py:483 msgid "Supervision: {}" msgstr "" @@ -444,72 +159,39 @@ msgstr "" msgid "If a lesson or substitution has only one group and this group has parent groups, show the parent groups instead of the original group." msgstr "" -#: aleksis/apps/chronos/preferences.py:42 -msgid "Shorten groups in timetable views" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:43 -msgid "If there are more groups than the set limit, they will be collapsed." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:51 -msgid "Limit of groups for shortening of groups" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:53 -msgid "If a user activates shortening of groups,they will be collapsed if there are more groups than this limit." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:65 +#: aleksis/apps/chronos/preferences.py:44 msgid "Relevant days for substitution plans" msgstr "" -#: aleksis/apps/chronos/preferences.py:82 +#: aleksis/apps/chronos/preferences.py:61 msgid "Time when substitution plans switch to the next day" msgstr "" -#: aleksis/apps/chronos/preferences.py:91 +#: aleksis/apps/chronos/preferences.py:70 msgid "Number of days shown on substitutions print view" msgstr "" -#: aleksis/apps/chronos/preferences.py:99 +#: aleksis/apps/chronos/preferences.py:78 msgid "Show header box in substitution views" msgstr "" -#: aleksis/apps/chronos/preferences.py:109 +#: aleksis/apps/chronos/preferences.py:88 msgid "Show parent groups in header box in substitution views instead of original groups" msgstr "" -#: aleksis/apps/chronos/preferences.py:118 -msgid "How many days in advance users should be notified about timetable changes?" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:126 -msgid "Time for sending notifications about timetable changes" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:129 -msgid "This is only used for scheduling notifications which doesn't affect the time period configured above. All other notifications affecting the next days are sent immediately." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:140 -#: aleksis/apps/chronos/preferences.py:148 -msgid "Send notifications for current timetable changes" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:158 +#: aleksis/apps/chronos/preferences.py:99 msgid "Group types to show in timetables" msgstr "" -#: aleksis/apps/chronos/preferences.py:159 +#: aleksis/apps/chronos/preferences.py:100 msgid "If you leave it empty, all groups will be shown." msgstr "" -#: aleksis/apps/chronos/preferences.py:169 +#: aleksis/apps/chronos/preferences.py:110 msgid "Lesson calendar feed color" msgstr "" -#: aleksis/apps/chronos/preferences.py:181 +#: aleksis/apps/chronos/preferences.py:122 msgid "Supervision calendar feed color" msgstr "" @@ -533,6 +215,10 @@ msgstr "" msgid "all day" msgstr "" +#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 +msgid "Supervision" +msgstr "" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:11 msgid "Print: Substitutions" msgstr "" @@ -545,6 +231,10 @@ msgstr "" msgid "Time" msgstr "" +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 +msgid "Room" +msgstr "" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:31 msgid "Notes" msgstr "" @@ -556,95 +246,3 @@ msgstr "" #: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:2 msgid "Areas" msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:48 -#, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has been cancelled." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:55 -#, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has some current changes." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:64 -#, python-brace-format -msgid "The teacher {old} is substituted by {new}." -msgid_plural "The teachers {old} are substituted by {new}." -msgstr[0] "" -msgstr[1] "" - -#: aleksis/apps/chronos/util/notifications.py:76 -#, python-brace-format -msgid "The subject is changed to {subject}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:82 -#, python-brace-format -msgid "The lesson is moved from {old} to {new}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:91 -#, python-brace-format -msgid "There is an additional comment: {comment}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:99 -#, python-brace-format -msgid "There is an event that starts on {date_start}, {period_from}. period and ends on {date_end}, {period_to}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:112 -#, python-brace-format -msgid "There is an event on {date} from the {period_from}. period to the {period_to}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:123 -#: aleksis/apps/chronos/util/notifications.py:143 -#, python-brace-format -msgid "Groups: {groups}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:125 -#: aleksis/apps/chronos/util/notifications.py:147 -#, python-brace-format -msgid "Teachers: {teachers}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:128 -#, python-brace-format -msgid "Rooms: {rooms}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:135 -#, python-brace-format -msgid "There is an extra lesson on {date} in the {period}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:145 -#, python-brace-format -msgid "Subject: {subject}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:149 -#, python-brace-format -msgid "Room: {room}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:151 -#, python-brace-format -msgid "Comment: {comment}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:154 -#, python-brace-format -msgid "The supervision of {old} on {date} between the {period_from}. period and the {period_to}. period in the area {area} is substituted by {new}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:204 -msgid "Timetable" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:205 -msgid "There are current changes to your timetable." -msgstr "" diff --git a/aleksis/apps/chronos/locale/ru/LC_MESSAGES/django.po b/aleksis/apps/chronos/locale/ru/LC_MESSAGES/django.po index 1b0a45605bebc00ee478a4bec86722a8d2d2e4d2..05b241d475490fd91d69b5e92ee3d1cf8a598a12 100644 --- a/aleksis/apps/chronos/locale/ru/LC_MESSAGES/django.po +++ b/aleksis/apps/chronos/locale/ru/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-18 14:23+0200\n" +"POT-Creation-Date: 2024-10-17 10:48+0200\n" "PO-Revision-Date: 2023-05-26 04:38+0000\n" "Last-Translator: Serhii Horichenko <m@sgg.im>\n" "Language-Team: Russian <https://translate.edugit.org/projects/aleksis/aleksis-app-chronos/ru/>\n" @@ -18,428 +18,143 @@ msgstr "" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" "X-Generator: Weblate 4.12.1\n" -#: aleksis/apps/chronos/mixins.py:25 -msgid "Linked validity range" -msgstr "СвÑзанный диапазон" - -#: aleksis/apps/chronos/model_extensions.py:145 +#: aleksis/apps/chronos/model_extensions.py:9 msgid "Can view group timetable" msgstr "Может проÑматривать групповые раÑпиÑаниÑ" -#: aleksis/apps/chronos/model_extensions.py:149 +#: aleksis/apps/chronos/model_extensions.py:13 msgid "Can view person timetable" msgstr "Может проÑматривать личные раÑпиÑаниÑ" -#: aleksis/apps/chronos/models.py:87 -msgid "School term" -msgstr "Учебный год" - -#: aleksis/apps/chronos/models.py:90 aleksis/apps/chronos/models.py:626 -#: aleksis/apps/chronos/models.py:1310 -msgid "Name" -msgstr "Полное имÑ" - -#: aleksis/apps/chronos/models.py:92 aleksis/apps/chronos/models.py:676 -#: aleksis/apps/chronos/models.py:747 aleksis/apps/chronos/models.py:979 -msgid "Start date" -msgstr "Дата начала" - -#: aleksis/apps/chronos/models.py:93 aleksis/apps/chronos/models.py:677 -#: aleksis/apps/chronos/models.py:748 aleksis/apps/chronos/models.py:980 -msgid "End date" -msgstr "Дата окончаниÑ" - -#: aleksis/apps/chronos/models.py:112 -msgid "The start date must be earlier than the end date." -msgstr "Дата начала должна быть ранее даты окончаниÑ." - -#: aleksis/apps/chronos/models.py:118 -msgid "The validity range must be within the school term." -msgstr "Контрольный период должен быть в рамках учебного года." - -#: aleksis/apps/chronos/models.py:125 -msgid "There is already a validity range for this time or a part of this time." -msgstr "Ðа Ñто Ð²Ñ€ÐµÐ¼Ñ Ð¸Ð»Ð¸ на его чаÑÑ‚ÑŒ контрольный период уже назначен." - -#: aleksis/apps/chronos/models.py:132 -msgid "Validity range" -msgstr "Контрольный период" - -#: aleksis/apps/chronos/models.py:133 -msgid "Validity ranges" -msgstr "Контрольные периоды" - -#: aleksis/apps/chronos/models.py:149 -msgid "Week day" -msgstr "День недели" - -#: aleksis/apps/chronos/models.py:151 -msgid "Number of period" -msgstr "Ðомер урока" - -#: aleksis/apps/chronos/models.py:153 -msgid "Start time" -msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð½Ð°Ñ‡Ð°Ð»Ð°" - -#: aleksis/apps/chronos/models.py:154 -msgid "End time" -msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ" - -#: aleksis/apps/chronos/models.py:324 aleksis/apps/chronos/models.py:496 -#: aleksis/apps/chronos/models.py:1122 -msgid "Time period" -msgstr "Ð’Ñ€ÐµÐ¼Ñ ÑƒÑ€Ð¾ÐºÐ°" - -#: aleksis/apps/chronos/models.py:325 -msgid "Time periods" -msgstr "Ð’Ñ€ÐµÐ¼Ñ ÑƒÑ€Ð¾ÐºÐ¾Ð²" - -#: aleksis/apps/chronos/models.py:329 aleksis/apps/chronos/models.py:625 -#: aleksis/apps/chronos/models.py:793 aleksis/apps/chronos/models.py:810 -msgid "Short name" -msgstr "Короткое имÑ" - -#: aleksis/apps/chronos/models.py:330 aleksis/apps/chronos/models.py:794 -#: aleksis/apps/chronos/models.py:811 -msgid "Long name" -msgstr "Длинное имÑ" - -#: aleksis/apps/chronos/models.py:332 -msgid "Foreground colour" -msgstr "ОÑновной цвет" - -#: aleksis/apps/chronos/models.py:333 -msgid "Background colour" -msgstr "Фоновый цвет" - -#: aleksis/apps/chronos/models.py:340 aleksis/apps/chronos/models.py:349 -#: aleksis/apps/chronos/models.py:416 aleksis/apps/chronos/models.py:1129 -#: aleksis/apps/chronos/models.py:1345 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 -msgid "Subject" -msgstr "Предмет" - -#: aleksis/apps/chronos/models.py:341 -msgid "Subjects" -msgstr "Предметы" - -#: aleksis/apps/chronos/models.py:352 aleksis/apps/chronos/models.py:422 -#: aleksis/apps/chronos/models.py:998 aleksis/apps/chronos/models.py:1137 -#: aleksis/apps/chronos/models.py:1338 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 -#: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 -msgid "Teachers" -msgstr "Преподаватели" - -#: aleksis/apps/chronos/models.py:358 -msgid "Periods" -msgstr "ЧаÑÑ‹" - -#: aleksis/apps/chronos/models.py:360 aleksis/apps/chronos/models.py:995 -#: aleksis/apps/chronos/models.py:1132 aleksis/apps/chronos/models.py:1326 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 -msgid "Groups" -msgstr "Группы" - -#: aleksis/apps/chronos/models.py:394 aleksis/apps/chronos/models.py:490 -#: aleksis/apps/chronos/models.py:716 aleksis/apps/chronos/models.py:1430 -#: aleksis/apps/chronos/models.py:1454 -msgid "Lesson" -msgstr "Урок" - -#: aleksis/apps/chronos/models.py:395 aleksis/apps/chronos/models.py:1306 -msgid "Lessons" -msgstr "Уроки" - -#: aleksis/apps/chronos/models.py:403 aleksis/apps/chronos/models.py:1116 -msgid "Week" -msgstr "ÐеделÑ" - -#: aleksis/apps/chronos/models.py:404 aleksis/apps/chronos/models.py:1117 -msgid "Year" -msgstr "Год" - -#: aleksis/apps/chronos/models.py:407 aleksis/apps/chronos/models.py:620 -msgid "Lesson period" -msgstr "Урок по порÑдку" - -#: aleksis/apps/chronos/models.py:425 aleksis/apps/chronos/models.py:504 -#: aleksis/apps/chronos/models.py:673 aleksis/apps/chronos/models.py:1144 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 -msgid "Room" -msgstr "Комната" - -#: aleksis/apps/chronos/models.py:428 -msgid "Cancelled?" -msgstr "Отменено?" - -#: aleksis/apps/chronos/models.py:430 -msgid "Cancelled for teachers?" -msgstr "Отменено Ð´Ð»Ñ Ð¿Ñ€ÐµÐ¿Ð¾Ð´Ð°Ð²Ð°Ñ‚ÐµÐ»ÐµÐ¹?" - -#: aleksis/apps/chronos/models.py:433 aleksis/apps/chronos/models.py:692 -#: aleksis/apps/chronos/models.py:734 aleksis/apps/chronos/models.py:1147 -#: aleksis/apps/chronos/models.py:1357 -msgid "Comment" -msgstr "Коментарий" - -#: aleksis/apps/chronos/models.py:437 -msgid "Lessons can only be either substituted or cancelled." -msgstr "Уроки могут быть только заменены или отменены." - -#: aleksis/apps/chronos/models.py:477 -msgid "Lesson substitution" -msgstr "Замена урока" - -#: aleksis/apps/chronos/models.py:478 -msgid "Lesson substitutions" -msgstr "Замены уроков" - -#: aleksis/apps/chronos/models.py:621 -msgid "Lesson periods" -msgstr "Учебные чаÑÑ‹" - -#: aleksis/apps/chronos/models.py:635 aleksis/apps/chronos/models.py:648 -msgid "Absence reason" -msgstr "Причина отÑутÑтвиÑ" - -#: aleksis/apps/chronos/models.py:636 -msgid "Absence reasons" -msgstr "Причины отÑутÑтвиÑ" - -#: aleksis/apps/chronos/models.py:657 aleksis/apps/chronos/models.py:889 -#: aleksis/apps/chronos/models.py:943 -msgid "Teacher" -msgstr "Преподаватель" - -#: aleksis/apps/chronos/models.py:665 -msgid "Group" -msgstr "Группа" - -#: aleksis/apps/chronos/models.py:681 aleksis/apps/chronos/models.py:723 -msgid "Start period" -msgstr "Ðачало уроков" - -#: aleksis/apps/chronos/models.py:688 aleksis/apps/chronos/models.py:729 -msgid "End period" -msgstr "Окончание уроков" - -#: aleksis/apps/chronos/models.py:702 -msgid "Unknown absence" -msgstr "ОтÑутÑтвие без уважительной причины" - -#: aleksis/apps/chronos/models.py:707 -msgid "Absence" -msgstr "ОтÑутÑтвие" - -#: aleksis/apps/chronos/models.py:708 -msgid "Absences" -msgstr "ПропуÑки" - -#: aleksis/apps/chronos/models.py:719 -msgid "Date of exam" -msgstr "Дата Ñкзамена" - -#: aleksis/apps/chronos/models.py:733 aleksis/apps/chronos/models.py:746 -#: aleksis/apps/chronos/models.py:977 -msgid "Title" -msgstr "Ðазвание" - -#: aleksis/apps/chronos/models.py:739 -msgid "Exam" -msgstr "Ðкзамен" - -#: aleksis/apps/chronos/models.py:740 -msgid "Exams" -msgstr "Ðкзамены" - -#: aleksis/apps/chronos/models.py:749 -msgid "Comments" -msgstr "Комментарии" - -#: aleksis/apps/chronos/models.py:788 -msgid "Holiday" -msgstr "Выходной" - -#: aleksis/apps/chronos/models.py:789 -msgid "Holidays" -msgstr "Выходные" - -#: aleksis/apps/chronos/models.py:803 aleksis/apps/chronos/models.py:879 -msgid "Supervision area" -msgstr "Зона контролÑ" - -#: aleksis/apps/chronos/models.py:804 -msgid "Supervision areas" -msgstr "Зоны контролÑ" - -#: aleksis/apps/chronos/models.py:816 -msgid "Time period after break starts" -msgstr "Учёба поÑле перерыва начинаетÑÑ" - -#: aleksis/apps/chronos/models.py:824 -msgid "Time period before break ends" -msgstr "Учёба перед перерывом заканчиваетÑÑ" - -#: aleksis/apps/chronos/models.py:864 aleksis/apps/chronos/models.py:883 -msgid "Break" -msgstr "Перерыв" - -#: aleksis/apps/chronos/models.py:865 -msgid "Breaks" -msgstr "Перерывы" - -#: aleksis/apps/chronos/models.py:923 aleksis/apps/chronos/models.py:936 -#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 -msgid "Supervision" -msgstr "Контроль" - -#: aleksis/apps/chronos/models.py:924 aleksis/apps/chronos/models.py:1622 -msgid "Supervisions" -msgstr "Контроли" - -#: aleksis/apps/chronos/models.py:932 -msgid "Date" -msgstr "Дата" - -#: aleksis/apps/chronos/models.py:966 -msgid "Supervision substitution" -msgstr "Замена контролÑ" - -#: aleksis/apps/chronos/models.py:967 -msgid "Supervision substitutions" -msgstr "Замены контролÑ" - -#: aleksis/apps/chronos/models.py:985 -msgid "Start time period" -msgstr "Ðачало урока" - -#: aleksis/apps/chronos/models.py:991 -msgid "End time period" -msgstr "Конец урока" - -#: aleksis/apps/chronos/models.py:996 aleksis/apps/chronos/models.py:1332 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 -msgid "Rooms" -msgstr "Комнаты" - -#: aleksis/apps/chronos/models.py:1005 -#, python-brace-format -msgid "Event {pk}" -msgstr "Событие {pk}" - -#: aleksis/apps/chronos/models.py:1103 -msgid "Event" -msgstr "Событие" - -#: aleksis/apps/chronos/models.py:1104 -msgid "Events" -msgstr "СобытиÑ" - -#: aleksis/apps/chronos/models.py:1152 -msgid "Related exam" -msgstr "СвÑзанные Ñкзамены" - -#: aleksis/apps/chronos/models.py:1181 -msgid "Extra lesson" -msgstr "Дополнительный урок" - -#: aleksis/apps/chronos/models.py:1182 -msgid "Extra lessons" -msgstr "Дополнительные уроки" - -#: aleksis/apps/chronos/models.py:1194 +#: aleksis/apps/chronos/models.py:48 msgid "Number of days shown in the plan" msgstr "КоличеÑтво дней Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð² плане" -#: aleksis/apps/chronos/models.py:1198 +#: aleksis/apps/chronos/models.py:52 msgid "Show header box" msgstr "Показать заголовок" -#: aleksis/apps/chronos/models.py:1199 aleksis/apps/chronos/preferences.py:100 +#: aleksis/apps/chronos/models.py:53 aleksis/apps/chronos/preferences.py:79 msgid "The header box shows affected teachers/groups." msgstr "Заголовок отображает преподавателей/группы, на которых влиÑет." -#: aleksis/apps/chronos/models.py:1206 +#: aleksis/apps/chronos/models.py:60 msgid "Revision which triggered the last update" msgstr "РевизиÑ, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð²Ñ‹Ð·Ð²Ð°Ð»Ð° поÑледнее обновление" -#: aleksis/apps/chronos/models.py:1278 +#: aleksis/apps/chronos/models.py:132 msgid "Automatic plan" msgstr "ÐвтоматичеÑкий план" -#: aleksis/apps/chronos/models.py:1279 +#: aleksis/apps/chronos/models.py:133 msgid "Automatic plans" msgstr "ÐвтоматичеÑкие планы" -#: aleksis/apps/chronos/models.py:1293 +#: aleksis/apps/chronos/models.py:147 msgid "Can view all room timetables" msgstr "Может проÑматривать раÑпиÑÐ°Ð½Ð¸Ñ Ð²Ñех комнат" -#: aleksis/apps/chronos/models.py:1294 +#: aleksis/apps/chronos/models.py:148 msgid "Can view all group timetables" msgstr "Может проÑматривать раÑпиÑÐ°Ð½Ð¸Ñ Ð²Ñех групп" -#: aleksis/apps/chronos/models.py:1295 +#: aleksis/apps/chronos/models.py:149 msgid "Can view all person timetables" msgstr "Может проÑматривать раÑпиÑÐ°Ð½Ð¸Ñ Ð²Ñех людей" -#: aleksis/apps/chronos/models.py:1296 +#: aleksis/apps/chronos/models.py:150 msgid "Can view timetable overview" msgstr "Может проÑматривать общее раÑпиÑание" -#: aleksis/apps/chronos/models.py:1297 -msgid "Can view all lessons per day" -msgstr "Может проÑматривать вÑе уроки за день" - -#: aleksis/apps/chronos/models.py:1298 +#: aleksis/apps/chronos/models.py:151 #, fuzzy -#| msgid "Can view all lessons per day" -msgid "Can view all supervisions per day" -msgstr "Может проÑматривать вÑе уроки за день" +#| msgid "No substitutions available." +msgid "Can view substitutions table" +msgstr "Замены недоÑтупны." + +#: aleksis/apps/chronos/models.py:159 +msgid "Lessons" +msgstr "Уроки" + +#: aleksis/apps/chronos/models.py:163 +msgid "Name" +msgstr "Полное имÑ" -#: aleksis/apps/chronos/models.py:1313 +#: aleksis/apps/chronos/models.py:166 #, fuzzy #| msgid "Start time" msgid "Start slot number" msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð½Ð°Ñ‡Ð°Ð»Ð°" -#: aleksis/apps/chronos/models.py:1316 +#: aleksis/apps/chronos/models.py:169 #, fuzzy #| msgid "End time" msgid "End slot number" msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ" -#: aleksis/apps/chronos/models.py:1320 +#: aleksis/apps/chronos/models.py:173 msgid "Course" msgstr "" -#: aleksis/apps/chronos/models.py:1353 +#: aleksis/apps/chronos/models.py:179 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 +msgid "Groups" +msgstr "Группы" + +#: aleksis/apps/chronos/models.py:185 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 +msgid "Rooms" +msgstr "Комнаты" + +#: aleksis/apps/chronos/models.py:191 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 +#: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 +msgid "Teachers" +msgstr "Преподаватели" + +#: aleksis/apps/chronos/models.py:198 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 +msgid "Subject" +msgstr "Предмет" + +#: aleksis/apps/chronos/models.py:206 #: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 msgid "Cancelled" msgstr "Отменено" -#: aleksis/apps/chronos/models.py:1401 aleksis/apps/chronos/models.py:1413 -#: aleksis/apps/chronos/models.py:1425 +#: aleksis/apps/chronos/models.py:210 +msgid "Comment" +msgstr "Коментарий" + +#: aleksis/apps/chronos/models.py:254 aleksis/apps/chronos/models.py:266 +#: aleksis/apps/chronos/models.py:278 msgid "{} (instead of {})" msgstr "" -#: aleksis/apps/chronos/models.py:1614 +#: aleksis/apps/chronos/models.py:283 aleksis/apps/chronos/models.py:307 +msgid "Lesson" +msgstr "Урок" + +#: aleksis/apps/chronos/models.py:467 #, fuzzy #| msgid "Lesson" msgid "Lesson Event" msgstr "Урок" -#: aleksis/apps/chronos/models.py:1615 +#: aleksis/apps/chronos/models.py:468 #, fuzzy #| msgid "Lessons" msgid "Lesson Events" msgstr "Уроки" -#: aleksis/apps/chronos/models.py:1630 +#: aleksis/apps/chronos/models.py:475 +msgid "Supervisions" +msgstr "Контроли" + +#: aleksis/apps/chronos/models.py:483 #, fuzzy #| msgid "Supervision" msgid "Supervision: {}" @@ -459,78 +174,43 @@ msgstr "ИÑпользовать родительÑкие группы в обз msgid "If a lesson or substitution has only one group and this group has parent groups, show the parent groups instead of the original group." msgstr "ЕÑли у урока или у замены только одна группа и у Ñтой группы еÑÑ‚ÑŒ родительÑкие группы, отображать их вмеÑто оригинальной группы." -#: aleksis/apps/chronos/preferences.py:42 -msgid "Shorten groups in timetable views" -msgstr "Сокращать группы в обзорах раÑпиÑаниÑ" - -#: aleksis/apps/chronos/preferences.py:43 -msgid "If there are more groups than the set limit, they will be collapsed." -msgstr "ЕÑли количеÑтво групп больше уÑтановленного лимита, они будут Ñгруппированы." - -#: aleksis/apps/chronos/preferences.py:51 -msgid "Limit of groups for shortening of groups" -msgstr "Лимит количеÑтва групп Ð´Ð»Ñ ÑÐ¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ð¿" - -#: aleksis/apps/chronos/preferences.py:53 -#, fuzzy -#| msgid "If an user activates shortening of groups,they will be collapsed if there are more groups than this limit." -msgid "If a user activates shortening of groups,they will be collapsed if there are more groups than this limit." -msgstr "ЕÑли пользователь активирует Ñокращение групп и количеÑтво больше уÑтановленного лимита, они группируютÑÑ." - -#: aleksis/apps/chronos/preferences.py:65 +#: aleksis/apps/chronos/preferences.py:44 #, fuzzy #| msgid "Lesson substitutions" msgid "Relevant days for substitution plans" msgstr "Замены уроков" -#: aleksis/apps/chronos/preferences.py:82 +#: aleksis/apps/chronos/preferences.py:61 msgid "Time when substitution plans switch to the next day" msgstr "" -#: aleksis/apps/chronos/preferences.py:91 +#: aleksis/apps/chronos/preferences.py:70 msgid "Number of days shown on substitutions print view" msgstr "КоличеÑтво дней Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð² печатном виде замен" -#: aleksis/apps/chronos/preferences.py:99 +#: aleksis/apps/chronos/preferences.py:78 msgid "Show header box in substitution views" msgstr "Отображать заголовок в обзоре замен" -#: aleksis/apps/chronos/preferences.py:109 +#: aleksis/apps/chronos/preferences.py:88 msgid "Show parent groups in header box in substitution views instead of original groups" msgstr "Ð’ заголовке обзора замен отображать родительÑкие группы вмеÑто оригинальных" -#: aleksis/apps/chronos/preferences.py:118 -msgid "How many days in advance users should be notified about timetable changes?" -msgstr "За Ñколько дней до изменений в раÑпиÑании необходимо уведомлÑÑ‚ÑŒ пользователей?" - -#: aleksis/apps/chronos/preferences.py:126 -msgid "Time for sending notifications about timetable changes" -msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð´Ð»Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸ уведомлений об изменениÑÑ… в раÑпиÑании" - -#: aleksis/apps/chronos/preferences.py:129 -msgid "This is only used for scheduling notifications which doesn't affect the time period configured above. All other notifications affecting the next days are sent immediately." -msgstr "Ðто иÑпользуетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ Ð¿Ð»Ð°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ð¹, которые не влиÑÑŽÑ‚ на Ð²Ñ€ÐµÐ¼Ñ ÑƒÑ€Ð¾ÐºÐ¾Ð², наÑтроенных выше. Ð’Ñе другие уведомлениÑ, которые влиÑÑŽÑ‚ на ближайшие дни, отправлÑÑŽÑ‚ÑÑ Ð½ÐµÐ·Ð°Ð¼ÐµÐ´Ð»Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾." - -#: aleksis/apps/chronos/preferences.py:140 -#: aleksis/apps/chronos/preferences.py:148 -msgid "Send notifications for current timetable changes" -msgstr "Отправить ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾ текущих изменениÑÑ… в раÑпиÑании" - -#: aleksis/apps/chronos/preferences.py:158 +#: aleksis/apps/chronos/preferences.py:99 #, fuzzy #| msgid "Shorten groups in timetable views" msgid "Group types to show in timetables" msgstr "Сокращать группы в обзорах раÑпиÑаниÑ" -#: aleksis/apps/chronos/preferences.py:159 +#: aleksis/apps/chronos/preferences.py:100 msgid "If you leave it empty, all groups will be shown." msgstr "" -#: aleksis/apps/chronos/preferences.py:169 +#: aleksis/apps/chronos/preferences.py:110 msgid "Lesson calendar feed color" msgstr "" -#: aleksis/apps/chronos/preferences.py:181 +#: aleksis/apps/chronos/preferences.py:122 #, fuzzy #| msgid "Supervision area" msgid "Supervision calendar feed color" @@ -556,6 +236,10 @@ msgstr "Ðа какие группы влиÑет" msgid "all day" msgstr "" +#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 +msgid "Supervision" +msgstr "Контроль" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:11 msgid "Print: Substitutions" msgstr "Печать: Замены" @@ -570,6 +254,10 @@ msgstr "Замены" msgid "Time" msgstr "РаÑпиÑание" +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 +msgid "Room" +msgstr "Комната" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:31 msgid "Notes" msgstr "Заметки" @@ -582,99 +270,303 @@ msgstr "Замены недоÑтупны." msgid "Areas" msgstr "" -#: aleksis/apps/chronos/util/notifications.py:48 +#~ msgid "Linked validity range" +#~ msgstr "СвÑзанный диапазон" + +#~ msgid "School term" +#~ msgstr "Учебный год" + +#~ msgid "Start date" +#~ msgstr "Дата начала" + +#~ msgid "End date" +#~ msgstr "Дата окончаниÑ" + +#~ msgid "The start date must be earlier than the end date." +#~ msgstr "Дата начала должна быть ранее даты окончаниÑ." + +#~ msgid "The validity range must be within the school term." +#~ msgstr "Контрольный период должен быть в рамках учебного года." + +#~ msgid "There is already a validity range for this time or a part of this time." +#~ msgstr "Ðа Ñто Ð²Ñ€ÐµÐ¼Ñ Ð¸Ð»Ð¸ на его чаÑÑ‚ÑŒ контрольный период уже назначен." + +#~ msgid "Validity range" +#~ msgstr "Контрольный период" + +#~ msgid "Validity ranges" +#~ msgstr "Контрольные периоды" + +#~ msgid "Week day" +#~ msgstr "День недели" + +#~ msgid "Number of period" +#~ msgstr "Ðомер урока" + +#~ msgid "Start time" +#~ msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð½Ð°Ñ‡Ð°Ð»Ð°" + +#~ msgid "End time" +#~ msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ" + +#~ msgid "Time period" +#~ msgstr "Ð’Ñ€ÐµÐ¼Ñ ÑƒÑ€Ð¾ÐºÐ°" + +#~ msgid "Time periods" +#~ msgstr "Ð’Ñ€ÐµÐ¼Ñ ÑƒÑ€Ð¾ÐºÐ¾Ð²" + +#~ msgid "Short name" +#~ msgstr "Короткое имÑ" + +#~ msgid "Long name" +#~ msgstr "Длинное имÑ" + +#~ msgid "Foreground colour" +#~ msgstr "ОÑновной цвет" + +#~ msgid "Background colour" +#~ msgstr "Фоновый цвет" + +#~ msgid "Subjects" +#~ msgstr "Предметы" + +#~ msgid "Periods" +#~ msgstr "ЧаÑÑ‹" + +#~ msgid "Week" +#~ msgstr "ÐеделÑ" + +#~ msgid "Year" +#~ msgstr "Год" + +#~ msgid "Lesson period" +#~ msgstr "Урок по порÑдку" + +#~ msgid "Cancelled?" +#~ msgstr "Отменено?" + +#~ msgid "Cancelled for teachers?" +#~ msgstr "Отменено Ð´Ð»Ñ Ð¿Ñ€ÐµÐ¿Ð¾Ð´Ð°Ð²Ð°Ñ‚ÐµÐ»ÐµÐ¹?" + +#~ msgid "Lessons can only be either substituted or cancelled." +#~ msgstr "Уроки могут быть только заменены или отменены." + +#~ msgid "Lesson substitution" +#~ msgstr "Замена урока" + +#~ msgid "Lesson substitutions" +#~ msgstr "Замены уроков" + +#~ msgid "Lesson periods" +#~ msgstr "Учебные чаÑÑ‹" + +#~ msgid "Absence reason" +#~ msgstr "Причина отÑутÑтвиÑ" + +#~ msgid "Absence reasons" +#~ msgstr "Причины отÑутÑтвиÑ" + +#~ msgid "Teacher" +#~ msgstr "Преподаватель" + +#~ msgid "Group" +#~ msgstr "Группа" + +#~ msgid "Start period" +#~ msgstr "Ðачало уроков" + +#~ msgid "End period" +#~ msgstr "Окончание уроков" + +#~ msgid "Unknown absence" +#~ msgstr "ОтÑутÑтвие без уважительной причины" + +#~ msgid "Absence" +#~ msgstr "ОтÑутÑтвие" + +#~ msgid "Absences" +#~ msgstr "ПропуÑки" + +#~ msgid "Date of exam" +#~ msgstr "Дата Ñкзамена" + +#~ msgid "Title" +#~ msgstr "Ðазвание" + +#~ msgid "Exam" +#~ msgstr "Ðкзамен" + +#~ msgid "Exams" +#~ msgstr "Ðкзамены" + +#~ msgid "Comments" +#~ msgstr "Комментарии" + +#~ msgid "Holiday" +#~ msgstr "Выходной" + +#~ msgid "Holidays" +#~ msgstr "Выходные" + +#~ msgid "Supervision area" +#~ msgstr "Зона контролÑ" + +#~ msgid "Supervision areas" +#~ msgstr "Зоны контролÑ" + +#~ msgid "Time period after break starts" +#~ msgstr "Учёба поÑле перерыва начинаетÑÑ" + +#~ msgid "Time period before break ends" +#~ msgstr "Учёба перед перерывом заканчиваетÑÑ" + +#~ msgid "Break" +#~ msgstr "Перерыв" + +#~ msgid "Breaks" +#~ msgstr "Перерывы" + +#~ msgid "Date" +#~ msgstr "Дата" + +#~ msgid "Supervision substitution" +#~ msgstr "Замена контролÑ" + +#~ msgid "Supervision substitutions" +#~ msgstr "Замены контролÑ" + +#~ msgid "Start time period" +#~ msgstr "Ðачало урока" + +#~ msgid "End time period" +#~ msgstr "Конец урока" + #, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has been cancelled." -msgstr "Урок {subject} на {period} уроке {day} отменён." +#~ msgid "Event {pk}" +#~ msgstr "Событие {pk}" + +#~ msgid "Event" +#~ msgstr "Событие" + +#~ msgid "Events" +#~ msgstr "СобытиÑ" + +#~ msgid "Related exam" +#~ msgstr "СвÑзанные Ñкзамены" + +#~ msgid "Extra lesson" +#~ msgstr "Дополнительный урок" + +#~ msgid "Extra lessons" +#~ msgstr "Дополнительные уроки" + +#~ msgid "Can view all lessons per day" +#~ msgstr "Может проÑматривать вÑе уроки за день" + +#, fuzzy +#~| msgid "Can view all lessons per day" +#~ msgid "Can view all supervisions per day" +#~ msgstr "Может проÑматривать вÑе уроки за день" + +#~ msgid "Shorten groups in timetable views" +#~ msgstr "Сокращать группы в обзорах раÑпиÑаниÑ" + +#~ msgid "If there are more groups than the set limit, they will be collapsed." +#~ msgstr "ЕÑли количеÑтво групп больше уÑтановленного лимита, они будут Ñгруппированы." + +#~ msgid "Limit of groups for shortening of groups" +#~ msgstr "Лимит количеÑтва групп Ð´Ð»Ñ ÑÐ¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ð¿" + +#, fuzzy +#~| msgid "If an user activates shortening of groups,they will be collapsed if there are more groups than this limit." +#~ msgid "If a user activates shortening of groups,they will be collapsed if there are more groups than this limit." +#~ msgstr "ЕÑли пользователь активирует Ñокращение групп и количеÑтво больше уÑтановленного лимита, они группируютÑÑ." + +#~ msgid "How many days in advance users should be notified about timetable changes?" +#~ msgstr "За Ñколько дней до изменений в раÑпиÑании необходимо уведомлÑÑ‚ÑŒ пользователей?" + +#~ msgid "Time for sending notifications about timetable changes" +#~ msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð´Ð»Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸ уведомлений об изменениÑÑ… в раÑпиÑании" + +#~ msgid "This is only used for scheduling notifications which doesn't affect the time period configured above. All other notifications affecting the next days are sent immediately." +#~ msgstr "Ðто иÑпользуетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ Ð¿Ð»Ð°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ð¹, которые не влиÑÑŽÑ‚ на Ð²Ñ€ÐµÐ¼Ñ ÑƒÑ€Ð¾ÐºÐ¾Ð², наÑтроенных выше. Ð’Ñе другие уведомлениÑ, которые влиÑÑŽÑ‚ на ближайшие дни, отправлÑÑŽÑ‚ÑÑ Ð½ÐµÐ·Ð°Ð¼ÐµÐ´Ð»Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾." + +#~ msgid "Send notifications for current timetable changes" +#~ msgstr "Отправить ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾ текущих изменениÑÑ… в раÑпиÑании" -#: aleksis/apps/chronos/util/notifications.py:55 #, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has some current changes." -msgstr "Ð’ урок {subject} на {period} уроке {day} внеÑены некоторые изменениÑ." +#~ msgid "The {subject} lesson in the {period}. period on {day} has been cancelled." +#~ msgstr "Урок {subject} на {period} уроке {day} отменён." -#: aleksis/apps/chronos/util/notifications.py:64 #, python-brace-format -msgid "The teacher {old} is substituted by {new}." -msgid_plural "The teachers {old} are substituted by {new}." -msgstr[0] "Преподаватель {old} заменён преподавателем {new}." -msgstr[1] "Преподаватели {old} заменены преподавателÑми {new}." -msgstr[2] "Преподаватели {old} заменены преподавателÑми {new}." -msgstr[3] "Преподаватели {old} заменены преподавателÑми {new}." - -#: aleksis/apps/chronos/util/notifications.py:76 +#~ msgid "The {subject} lesson in the {period}. period on {day} has some current changes." +#~ msgstr "Ð’ урок {subject} на {period} уроке {day} внеÑены некоторые изменениÑ." + #, python-brace-format -msgid "The subject is changed to {subject}." -msgstr "Предмет заменён на {subject}." +#~ msgid "The teacher {old} is substituted by {new}." +#~ msgid_plural "The teachers {old} are substituted by {new}." +#~ msgstr[0] "Преподаватель {old} заменён преподавателем {new}." +#~ msgstr[1] "Преподаватели {old} заменены преподавателÑми {new}." +#~ msgstr[2] "Преподаватели {old} заменены преподавателÑми {new}." +#~ msgstr[3] "Преподаватели {old} заменены преподавателÑми {new}." -#: aleksis/apps/chronos/util/notifications.py:82 #, python-brace-format -msgid "The lesson is moved from {old} to {new}." -msgstr "Урок перенеÑён Ñ {old} на {new}." +#~ msgid "The subject is changed to {subject}." +#~ msgstr "Предмет заменён на {subject}." -#: aleksis/apps/chronos/util/notifications.py:91 #, python-brace-format -msgid "There is an additional comment: {comment}." -msgstr "ПриÑутÑтвует дополнительный комментарий: {comment}." +#~ msgid "The lesson is moved from {old} to {new}." +#~ msgstr "Урок перенеÑён Ñ {old} на {new}." -#: aleksis/apps/chronos/util/notifications.py:99 #, python-brace-format -msgid "There is an event that starts on {date_start}, {period_from}. period and ends on {date_end}, {period_to}. period:" -msgstr "Запланировано мероприÑтие, которое начинаетÑÑ {date_start}, на {period_from}. уроке и заканчиваетÑÑ {date_end}, на {period_to}. уроке:" +#~ msgid "There is an additional comment: {comment}." +#~ msgstr "ПриÑутÑтвует дополнительный комментарий: {comment}." -#: aleksis/apps/chronos/util/notifications.py:112 #, python-brace-format -msgid "There is an event on {date} from the {period_from}. period to the {period_to}. period:" -msgstr "Запланировано мероприÑтие {date} Ñ {period_from}. урока до {period_to}. урока:" +#~ msgid "There is an event that starts on {date_start}, {period_from}. period and ends on {date_end}, {period_to}. period:" +#~ msgstr "Запланировано мероприÑтие, которое начинаетÑÑ {date_start}, на {period_from}. уроке и заканчиваетÑÑ {date_end}, на {period_to}. уроке:" -#: aleksis/apps/chronos/util/notifications.py:123 -#: aleksis/apps/chronos/util/notifications.py:143 #, python-brace-format -msgid "Groups: {groups}" -msgstr "Группы: {groups}" +#~ msgid "There is an event on {date} from the {period_from}. period to the {period_to}. period:" +#~ msgstr "Запланировано мероприÑтие {date} Ñ {period_from}. урока до {period_to}. урока:" -#: aleksis/apps/chronos/util/notifications.py:125 -#: aleksis/apps/chronos/util/notifications.py:147 #, python-brace-format -msgid "Teachers: {teachers}" -msgstr "Преподаватели: {teachers}" +#~ msgid "Groups: {groups}" +#~ msgstr "Группы: {groups}" -#: aleksis/apps/chronos/util/notifications.py:128 #, python-brace-format -msgid "Rooms: {rooms}" -msgstr "Комнаты: {rooms}" +#~ msgid "Teachers: {teachers}" +#~ msgstr "Преподаватели: {teachers}" -#: aleksis/apps/chronos/util/notifications.py:135 #, python-brace-format -msgid "There is an extra lesson on {date} in the {period}. period:" -msgstr "Запланирован дополнительный урок {date} на {period}. уроке:" +#~ msgid "Rooms: {rooms}" +#~ msgstr "Комнаты: {rooms}" -#: aleksis/apps/chronos/util/notifications.py:145 #, python-brace-format -msgid "Subject: {subject}" -msgstr "Предмет: {subject}" +#~ msgid "There is an extra lesson on {date} in the {period}. period:" +#~ msgstr "Запланирован дополнительный урок {date} на {period}. уроке:" -#: aleksis/apps/chronos/util/notifications.py:149 #, python-brace-format -msgid "Room: {room}" -msgstr "Комната: {room}" +#~ msgid "Subject: {subject}" +#~ msgstr "Предмет: {subject}" -#: aleksis/apps/chronos/util/notifications.py:151 #, python-brace-format -msgid "Comment: {comment}." -msgstr "Комментарий: {comment}." +#~ msgid "Room: {room}" +#~ msgstr "Комната: {room}" -#: aleksis/apps/chronos/util/notifications.py:154 #, python-brace-format -msgid "The supervision of {old} on {date} between the {period_from}. period and the {period_to}. period in the area {area} is substituted by {new}." -msgstr "Контроль {old}, {date} между {period_from}. и {period_to}. уроками в зоне {area} заменён на {new}." +#~ msgid "Comment: {comment}." +#~ msgstr "Комментарий: {comment}." -#: aleksis/apps/chronos/util/notifications.py:204 -msgid "Timetable" -msgstr "РаÑпиÑание" +#, python-brace-format +#~ msgid "The supervision of {old} on {date} between the {period_from}. period and the {period_to}. period in the area {area} is substituted by {new}." +#~ msgstr "Контроль {old}, {date} между {period_from}. и {period_to}. уроками в зоне {area} заменён на {new}." + +#~ msgid "Timetable" +#~ msgstr "РаÑпиÑание" -#: aleksis/apps/chronos/util/notifications.py:205 -msgid "There are current changes to your timetable." -msgstr "У Ð’Ð°Ñ Ð² раÑпиÑании произошли изменениÑ." +#~ msgid "There are current changes to your timetable." +#~ msgstr "У Ð’Ð°Ñ Ð² раÑпиÑании произошли изменениÑ." #~ msgid "Options for timetables" #~ msgstr "Опции Ð´Ð»Ñ Ñ€Ð°ÑпиÑаниÑ" diff --git a/aleksis/apps/chronos/locale/tr_TR/LC_MESSAGES/django.po b/aleksis/apps/chronos/locale/tr_TR/LC_MESSAGES/django.po index cfc760f01b3206aaf7123340eccf529e9e4db354..e7015c68a7c47522f5d628d897256ea935a950ed 100644 --- a/aleksis/apps/chronos/locale/tr_TR/LC_MESSAGES/django.po +++ b/aleksis/apps/chronos/locale/tr_TR/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-18 14:23+0200\n" +"POT-Creation-Date: 2024-10-17 10:48+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,418 +17,133 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: aleksis/apps/chronos/mixins.py:25 -msgid "Linked validity range" -msgstr "" - -#: aleksis/apps/chronos/model_extensions.py:145 +#: aleksis/apps/chronos/model_extensions.py:9 msgid "Can view group timetable" msgstr "" -#: aleksis/apps/chronos/model_extensions.py:149 +#: aleksis/apps/chronos/model_extensions.py:13 msgid "Can view person timetable" msgstr "" -#: aleksis/apps/chronos/models.py:87 -msgid "School term" -msgstr "" - -#: aleksis/apps/chronos/models.py:90 aleksis/apps/chronos/models.py:626 -#: aleksis/apps/chronos/models.py:1310 -msgid "Name" -msgstr "" - -#: aleksis/apps/chronos/models.py:92 aleksis/apps/chronos/models.py:676 -#: aleksis/apps/chronos/models.py:747 aleksis/apps/chronos/models.py:979 -msgid "Start date" -msgstr "" - -#: aleksis/apps/chronos/models.py:93 aleksis/apps/chronos/models.py:677 -#: aleksis/apps/chronos/models.py:748 aleksis/apps/chronos/models.py:980 -msgid "End date" +#: aleksis/apps/chronos/models.py:48 +msgid "Number of days shown in the plan" msgstr "" -#: aleksis/apps/chronos/models.py:112 -msgid "The start date must be earlier than the end date." +#: aleksis/apps/chronos/models.py:52 +msgid "Show header box" msgstr "" -#: aleksis/apps/chronos/models.py:118 -msgid "The validity range must be within the school term." +#: aleksis/apps/chronos/models.py:53 aleksis/apps/chronos/preferences.py:79 +msgid "The header box shows affected teachers/groups." msgstr "" -#: aleksis/apps/chronos/models.py:125 -msgid "There is already a validity range for this time or a part of this time." +#: aleksis/apps/chronos/models.py:60 +msgid "Revision which triggered the last update" msgstr "" #: aleksis/apps/chronos/models.py:132 -msgid "Validity range" +msgid "Automatic plan" msgstr "" #: aleksis/apps/chronos/models.py:133 -msgid "Validity ranges" +msgid "Automatic plans" msgstr "" -#: aleksis/apps/chronos/models.py:149 -msgid "Week day" +#: aleksis/apps/chronos/models.py:147 +msgid "Can view all room timetables" msgstr "" -#: aleksis/apps/chronos/models.py:151 -msgid "Number of period" +#: aleksis/apps/chronos/models.py:148 +msgid "Can view all group timetables" msgstr "" -#: aleksis/apps/chronos/models.py:153 -msgid "Start time" +#: aleksis/apps/chronos/models.py:149 +msgid "Can view all person timetables" msgstr "" -#: aleksis/apps/chronos/models.py:154 -msgid "End time" +#: aleksis/apps/chronos/models.py:150 +msgid "Can view timetable overview" msgstr "" -#: aleksis/apps/chronos/models.py:324 aleksis/apps/chronos/models.py:496 -#: aleksis/apps/chronos/models.py:1122 -msgid "Time period" +#: aleksis/apps/chronos/models.py:151 +msgid "Can view substitutions table" msgstr "" -#: aleksis/apps/chronos/models.py:325 -msgid "Time periods" +#: aleksis/apps/chronos/models.py:159 +msgid "Lessons" msgstr "" -#: aleksis/apps/chronos/models.py:329 aleksis/apps/chronos/models.py:625 -#: aleksis/apps/chronos/models.py:793 aleksis/apps/chronos/models.py:810 -msgid "Short name" +#: aleksis/apps/chronos/models.py:163 +msgid "Name" msgstr "" -#: aleksis/apps/chronos/models.py:330 aleksis/apps/chronos/models.py:794 -#: aleksis/apps/chronos/models.py:811 -msgid "Long name" +#: aleksis/apps/chronos/models.py:166 +msgid "Start slot number" msgstr "" -#: aleksis/apps/chronos/models.py:332 -msgid "Foreground colour" +#: aleksis/apps/chronos/models.py:169 +msgid "End slot number" msgstr "" -#: aleksis/apps/chronos/models.py:333 -msgid "Background colour" +#: aleksis/apps/chronos/models.py:173 +msgid "Course" msgstr "" -#: aleksis/apps/chronos/models.py:340 aleksis/apps/chronos/models.py:349 -#: aleksis/apps/chronos/models.py:416 aleksis/apps/chronos/models.py:1129 -#: aleksis/apps/chronos/models.py:1345 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 -msgid "Subject" +#: aleksis/apps/chronos/models.py:179 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 +msgid "Groups" msgstr "" -#: aleksis/apps/chronos/models.py:341 -msgid "Subjects" +#: aleksis/apps/chronos/models.py:185 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 +msgid "Rooms" msgstr "" -#: aleksis/apps/chronos/models.py:352 aleksis/apps/chronos/models.py:422 -#: aleksis/apps/chronos/models.py:998 aleksis/apps/chronos/models.py:1137 -#: aleksis/apps/chronos/models.py:1338 +#: aleksis/apps/chronos/models.py:191 #: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 #: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 msgid "Teachers" msgstr "" -#: aleksis/apps/chronos/models.py:358 -msgid "Periods" -msgstr "" - -#: aleksis/apps/chronos/models.py:360 aleksis/apps/chronos/models.py:995 -#: aleksis/apps/chronos/models.py:1132 aleksis/apps/chronos/models.py:1326 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 -msgid "Groups" -msgstr "" - -#: aleksis/apps/chronos/models.py:394 aleksis/apps/chronos/models.py:490 -#: aleksis/apps/chronos/models.py:716 aleksis/apps/chronos/models.py:1430 -#: aleksis/apps/chronos/models.py:1454 -msgid "Lesson" -msgstr "" - -#: aleksis/apps/chronos/models.py:395 aleksis/apps/chronos/models.py:1306 -msgid "Lessons" -msgstr "" - -#: aleksis/apps/chronos/models.py:403 aleksis/apps/chronos/models.py:1116 -msgid "Week" -msgstr "" - -#: aleksis/apps/chronos/models.py:404 aleksis/apps/chronos/models.py:1117 -msgid "Year" -msgstr "" - -#: aleksis/apps/chronos/models.py:407 aleksis/apps/chronos/models.py:620 -msgid "Lesson period" -msgstr "" - -#: aleksis/apps/chronos/models.py:425 aleksis/apps/chronos/models.py:504 -#: aleksis/apps/chronos/models.py:673 aleksis/apps/chronos/models.py:1144 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 -msgid "Room" -msgstr "" - -#: aleksis/apps/chronos/models.py:428 -msgid "Cancelled?" +#: aleksis/apps/chronos/models.py:198 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 +msgid "Subject" msgstr "" -#: aleksis/apps/chronos/models.py:430 -msgid "Cancelled for teachers?" +#: aleksis/apps/chronos/models.py:206 +#: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 +msgid "Cancelled" msgstr "" -#: aleksis/apps/chronos/models.py:433 aleksis/apps/chronos/models.py:692 -#: aleksis/apps/chronos/models.py:734 aleksis/apps/chronos/models.py:1147 -#: aleksis/apps/chronos/models.py:1357 +#: aleksis/apps/chronos/models.py:210 msgid "Comment" msgstr "" -#: aleksis/apps/chronos/models.py:437 -msgid "Lessons can only be either substituted or cancelled." -msgstr "" - -#: aleksis/apps/chronos/models.py:477 -msgid "Lesson substitution" -msgstr "" - -#: aleksis/apps/chronos/models.py:478 -msgid "Lesson substitutions" -msgstr "" - -#: aleksis/apps/chronos/models.py:621 -msgid "Lesson periods" -msgstr "" - -#: aleksis/apps/chronos/models.py:635 aleksis/apps/chronos/models.py:648 -msgid "Absence reason" -msgstr "" - -#: aleksis/apps/chronos/models.py:636 -msgid "Absence reasons" -msgstr "" - -#: aleksis/apps/chronos/models.py:657 aleksis/apps/chronos/models.py:889 -#: aleksis/apps/chronos/models.py:943 -msgid "Teacher" -msgstr "" - -#: aleksis/apps/chronos/models.py:665 -msgid "Group" -msgstr "" - -#: aleksis/apps/chronos/models.py:681 aleksis/apps/chronos/models.py:723 -msgid "Start period" -msgstr "" - -#: aleksis/apps/chronos/models.py:688 aleksis/apps/chronos/models.py:729 -msgid "End period" -msgstr "" - -#: aleksis/apps/chronos/models.py:702 -msgid "Unknown absence" -msgstr "" - -#: aleksis/apps/chronos/models.py:707 -msgid "Absence" -msgstr "" - -#: aleksis/apps/chronos/models.py:708 -msgid "Absences" -msgstr "" - -#: aleksis/apps/chronos/models.py:719 -msgid "Date of exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:733 aleksis/apps/chronos/models.py:746 -#: aleksis/apps/chronos/models.py:977 -msgid "Title" -msgstr "" - -#: aleksis/apps/chronos/models.py:739 -msgid "Exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:740 -msgid "Exams" -msgstr "" - -#: aleksis/apps/chronos/models.py:749 -msgid "Comments" -msgstr "" - -#: aleksis/apps/chronos/models.py:788 -msgid "Holiday" -msgstr "" - -#: aleksis/apps/chronos/models.py:789 -msgid "Holidays" -msgstr "" - -#: aleksis/apps/chronos/models.py:803 aleksis/apps/chronos/models.py:879 -msgid "Supervision area" -msgstr "" - -#: aleksis/apps/chronos/models.py:804 -msgid "Supervision areas" -msgstr "" - -#: aleksis/apps/chronos/models.py:816 -msgid "Time period after break starts" -msgstr "" - -#: aleksis/apps/chronos/models.py:824 -msgid "Time period before break ends" +#: aleksis/apps/chronos/models.py:254 aleksis/apps/chronos/models.py:266 +#: aleksis/apps/chronos/models.py:278 +msgid "{} (instead of {})" msgstr "" -#: aleksis/apps/chronos/models.py:864 aleksis/apps/chronos/models.py:883 -msgid "Break" +#: aleksis/apps/chronos/models.py:283 aleksis/apps/chronos/models.py:307 +msgid "Lesson" msgstr "" -#: aleksis/apps/chronos/models.py:865 -msgid "Breaks" +#: aleksis/apps/chronos/models.py:467 +msgid "Lesson Event" msgstr "" -#: aleksis/apps/chronos/models.py:923 aleksis/apps/chronos/models.py:936 -#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 -msgid "Supervision" +#: aleksis/apps/chronos/models.py:468 +msgid "Lesson Events" msgstr "" -#: aleksis/apps/chronos/models.py:924 aleksis/apps/chronos/models.py:1622 +#: aleksis/apps/chronos/models.py:475 msgid "Supervisions" msgstr "" -#: aleksis/apps/chronos/models.py:932 -msgid "Date" -msgstr "" - -#: aleksis/apps/chronos/models.py:966 -msgid "Supervision substitution" -msgstr "" - -#: aleksis/apps/chronos/models.py:967 -msgid "Supervision substitutions" -msgstr "" - -#: aleksis/apps/chronos/models.py:985 -msgid "Start time period" -msgstr "" - -#: aleksis/apps/chronos/models.py:991 -msgid "End time period" -msgstr "" - -#: aleksis/apps/chronos/models.py:996 aleksis/apps/chronos/models.py:1332 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 -msgid "Rooms" -msgstr "" - -#: aleksis/apps/chronos/models.py:1005 -#, python-brace-format -msgid "Event {pk}" -msgstr "" - -#: aleksis/apps/chronos/models.py:1103 -msgid "Event" -msgstr "" - -#: aleksis/apps/chronos/models.py:1104 -msgid "Events" -msgstr "" - -#: aleksis/apps/chronos/models.py:1152 -msgid "Related exam" -msgstr "" - -#: aleksis/apps/chronos/models.py:1181 -msgid "Extra lesson" -msgstr "" - -#: aleksis/apps/chronos/models.py:1182 -msgid "Extra lessons" -msgstr "" - -#: aleksis/apps/chronos/models.py:1194 -msgid "Number of days shown in the plan" -msgstr "" - -#: aleksis/apps/chronos/models.py:1198 -msgid "Show header box" -msgstr "" - -#: aleksis/apps/chronos/models.py:1199 aleksis/apps/chronos/preferences.py:100 -msgid "The header box shows affected teachers/groups." -msgstr "" - -#: aleksis/apps/chronos/models.py:1206 -msgid "Revision which triggered the last update" -msgstr "" - -#: aleksis/apps/chronos/models.py:1278 -msgid "Automatic plan" -msgstr "" - -#: aleksis/apps/chronos/models.py:1279 -msgid "Automatic plans" -msgstr "" - -#: aleksis/apps/chronos/models.py:1293 -msgid "Can view all room timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1294 -msgid "Can view all group timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1295 -msgid "Can view all person timetables" -msgstr "" - -#: aleksis/apps/chronos/models.py:1296 -msgid "Can view timetable overview" -msgstr "" - -#: aleksis/apps/chronos/models.py:1297 -msgid "Can view all lessons per day" -msgstr "" - -#: aleksis/apps/chronos/models.py:1298 -msgid "Can view all supervisions per day" -msgstr "" - -#: aleksis/apps/chronos/models.py:1313 -msgid "Start slot number" -msgstr "" - -#: aleksis/apps/chronos/models.py:1316 -msgid "End slot number" -msgstr "" - -#: aleksis/apps/chronos/models.py:1320 -msgid "Course" -msgstr "" - -#: aleksis/apps/chronos/models.py:1353 -#: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 -msgid "Cancelled" -msgstr "" - -#: aleksis/apps/chronos/models.py:1401 aleksis/apps/chronos/models.py:1413 -#: aleksis/apps/chronos/models.py:1425 -msgid "{} (instead of {})" -msgstr "" - -#: aleksis/apps/chronos/models.py:1614 -msgid "Lesson Event" -msgstr "" - -#: aleksis/apps/chronos/models.py:1615 -msgid "Lesson Events" -msgstr "" - -#: aleksis/apps/chronos/models.py:1630 +#: aleksis/apps/chronos/models.py:483 msgid "Supervision: {}" msgstr "" @@ -444,72 +159,39 @@ msgstr "" msgid "If a lesson or substitution has only one group and this group has parent groups, show the parent groups instead of the original group." msgstr "" -#: aleksis/apps/chronos/preferences.py:42 -msgid "Shorten groups in timetable views" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:43 -msgid "If there are more groups than the set limit, they will be collapsed." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:51 -msgid "Limit of groups for shortening of groups" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:53 -msgid "If a user activates shortening of groups,they will be collapsed if there are more groups than this limit." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:65 +#: aleksis/apps/chronos/preferences.py:44 msgid "Relevant days for substitution plans" msgstr "" -#: aleksis/apps/chronos/preferences.py:82 +#: aleksis/apps/chronos/preferences.py:61 msgid "Time when substitution plans switch to the next day" msgstr "" -#: aleksis/apps/chronos/preferences.py:91 +#: aleksis/apps/chronos/preferences.py:70 msgid "Number of days shown on substitutions print view" msgstr "" -#: aleksis/apps/chronos/preferences.py:99 +#: aleksis/apps/chronos/preferences.py:78 msgid "Show header box in substitution views" msgstr "" -#: aleksis/apps/chronos/preferences.py:109 +#: aleksis/apps/chronos/preferences.py:88 msgid "Show parent groups in header box in substitution views instead of original groups" msgstr "" -#: aleksis/apps/chronos/preferences.py:118 -msgid "How many days in advance users should be notified about timetable changes?" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:126 -msgid "Time for sending notifications about timetable changes" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:129 -msgid "This is only used for scheduling notifications which doesn't affect the time period configured above. All other notifications affecting the next days are sent immediately." -msgstr "" - -#: aleksis/apps/chronos/preferences.py:140 -#: aleksis/apps/chronos/preferences.py:148 -msgid "Send notifications for current timetable changes" -msgstr "" - -#: aleksis/apps/chronos/preferences.py:158 +#: aleksis/apps/chronos/preferences.py:99 msgid "Group types to show in timetables" msgstr "" -#: aleksis/apps/chronos/preferences.py:159 +#: aleksis/apps/chronos/preferences.py:100 msgid "If you leave it empty, all groups will be shown." msgstr "" -#: aleksis/apps/chronos/preferences.py:169 +#: aleksis/apps/chronos/preferences.py:110 msgid "Lesson calendar feed color" msgstr "" -#: aleksis/apps/chronos/preferences.py:181 +#: aleksis/apps/chronos/preferences.py:122 msgid "Supervision calendar feed color" msgstr "" @@ -533,6 +215,10 @@ msgstr "" msgid "all day" msgstr "" +#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 +msgid "Supervision" +msgstr "" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:11 msgid "Print: Substitutions" msgstr "" @@ -545,6 +231,10 @@ msgstr "" msgid "Time" msgstr "" +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 +msgid "Room" +msgstr "" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:31 msgid "Notes" msgstr "" @@ -556,95 +246,3 @@ msgstr "" #: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:2 msgid "Areas" msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:48 -#, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has been cancelled." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:55 -#, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has some current changes." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:64 -#, python-brace-format -msgid "The teacher {old} is substituted by {new}." -msgid_plural "The teachers {old} are substituted by {new}." -msgstr[0] "" -msgstr[1] "" - -#: aleksis/apps/chronos/util/notifications.py:76 -#, python-brace-format -msgid "The subject is changed to {subject}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:82 -#, python-brace-format -msgid "The lesson is moved from {old} to {new}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:91 -#, python-brace-format -msgid "There is an additional comment: {comment}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:99 -#, python-brace-format -msgid "There is an event that starts on {date_start}, {period_from}. period and ends on {date_end}, {period_to}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:112 -#, python-brace-format -msgid "There is an event on {date} from the {period_from}. period to the {period_to}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:123 -#: aleksis/apps/chronos/util/notifications.py:143 -#, python-brace-format -msgid "Groups: {groups}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:125 -#: aleksis/apps/chronos/util/notifications.py:147 -#, python-brace-format -msgid "Teachers: {teachers}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:128 -#, python-brace-format -msgid "Rooms: {rooms}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:135 -#, python-brace-format -msgid "There is an extra lesson on {date} in the {period}. period:" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:145 -#, python-brace-format -msgid "Subject: {subject}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:149 -#, python-brace-format -msgid "Room: {room}" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:151 -#, python-brace-format -msgid "Comment: {comment}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:154 -#, python-brace-format -msgid "The supervision of {old} on {date} between the {period_from}. period and the {period_to}. period in the area {area} is substituted by {new}." -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:204 -msgid "Timetable" -msgstr "" - -#: aleksis/apps/chronos/util/notifications.py:205 -msgid "There are current changes to your timetable." -msgstr "" diff --git a/aleksis/apps/chronos/locale/uk/LC_MESSAGES/django.po b/aleksis/apps/chronos/locale/uk/LC_MESSAGES/django.po index 2a59feb53f2476f5088389967bf5de5ad9dbdd99..37c39e991658af7a313c6d082abb5bad239f057c 100644 --- a/aleksis/apps/chronos/locale/uk/LC_MESSAGES/django.po +++ b/aleksis/apps/chronos/locale/uk/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-18 14:23+0200\n" +"POT-Creation-Date: 2024-10-17 10:48+0200\n" "PO-Revision-Date: 2022-06-22 19:59+0000\n" "Last-Translator: Serhii Horichenko <m@sgg.im>\n" "Language-Team: Ukrainian <https://translate.edugit.org/projects/aleksis/aleksis-app-chronos/uk/>\n" @@ -18,428 +18,143 @@ msgstr "" "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" "X-Generator: Weblate 4.12.1\n" -#: aleksis/apps/chronos/mixins.py:25 -msgid "Linked validity range" -msgstr "Пов'Ñзаний діапазон" - -#: aleksis/apps/chronos/model_extensions.py:145 +#: aleksis/apps/chronos/model_extensions.py:9 msgid "Can view group timetable" msgstr "Може переглÑдати групові розклади" -#: aleksis/apps/chronos/model_extensions.py:149 +#: aleksis/apps/chronos/model_extensions.py:13 msgid "Can view person timetable" msgstr "Може переглÑдати оÑобиÑÑ‚Ñ– розклади" -#: aleksis/apps/chronos/models.py:87 -msgid "School term" -msgstr "Ðавчальний рік" - -#: aleksis/apps/chronos/models.py:90 aleksis/apps/chronos/models.py:626 -#: aleksis/apps/chronos/models.py:1310 -msgid "Name" -msgstr "Повне ім'Ñ" - -#: aleksis/apps/chronos/models.py:92 aleksis/apps/chronos/models.py:676 -#: aleksis/apps/chronos/models.py:747 aleksis/apps/chronos/models.py:979 -msgid "Start date" -msgstr "Дата початку" - -#: aleksis/apps/chronos/models.py:93 aleksis/apps/chronos/models.py:677 -#: aleksis/apps/chronos/models.py:748 aleksis/apps/chronos/models.py:980 -msgid "End date" -msgstr "Дата закінченнÑ" - -#: aleksis/apps/chronos/models.py:112 -msgid "The start date must be earlier than the end date." -msgstr "Початкова дата повинна бути раніше кінцевої." - -#: aleksis/apps/chronos/models.py:118 -msgid "The validity range must be within the school term." -msgstr "Контрольний період повинен бути в рамках навчального року." - -#: aleksis/apps/chronos/models.py:125 -msgid "There is already a validity range for this time or a part of this time." -msgstr "Ðа цей Ñ‡Ð°Ñ Ð°Ð±Ð¾ на чаÑтину цього чаÑу контрольний період вже призначений." - -#: aleksis/apps/chronos/models.py:132 -msgid "Validity range" -msgstr "Контрольний період" - -#: aleksis/apps/chronos/models.py:133 -msgid "Validity ranges" -msgstr "Контрольні періоди" - -#: aleksis/apps/chronos/models.py:149 -msgid "Week day" -msgstr "День тижнÑ" - -#: aleksis/apps/chronos/models.py:151 -msgid "Number of period" -msgstr "Ðомер урока" - -#: aleksis/apps/chronos/models.py:153 -msgid "Start time" -msgstr "Ð§Ð°Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ" - -#: aleksis/apps/chronos/models.py:154 -msgid "End time" -msgstr "Ð§Ð°Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ" - -#: aleksis/apps/chronos/models.py:324 aleksis/apps/chronos/models.py:496 -#: aleksis/apps/chronos/models.py:1122 -msgid "Time period" -msgstr "Ð§Ð°Ñ ÑƒÑ€Ð¾ÐºÑƒ" - -#: aleksis/apps/chronos/models.py:325 -msgid "Time periods" -msgstr "Ð§Ð°Ñ ÑƒÑ€Ð¾ÐºÑ–Ð²" - -#: aleksis/apps/chronos/models.py:329 aleksis/apps/chronos/models.py:625 -#: aleksis/apps/chronos/models.py:793 aleksis/apps/chronos/models.py:810 -msgid "Short name" -msgstr "Коротке ім'Ñ" - -#: aleksis/apps/chronos/models.py:330 aleksis/apps/chronos/models.py:794 -#: aleksis/apps/chronos/models.py:811 -msgid "Long name" -msgstr "Довге ім'Ñ" - -#: aleksis/apps/chronos/models.py:332 -msgid "Foreground colour" -msgstr "ОÑновний колір" - -#: aleksis/apps/chronos/models.py:333 -msgid "Background colour" -msgstr "Колір фону" - -#: aleksis/apps/chronos/models.py:340 aleksis/apps/chronos/models.py:349 -#: aleksis/apps/chronos/models.py:416 aleksis/apps/chronos/models.py:1129 -#: aleksis/apps/chronos/models.py:1345 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 -msgid "Subject" -msgstr "Предмет" - -#: aleksis/apps/chronos/models.py:341 -msgid "Subjects" -msgstr "Предмети" - -#: aleksis/apps/chronos/models.py:352 aleksis/apps/chronos/models.py:422 -#: aleksis/apps/chronos/models.py:998 aleksis/apps/chronos/models.py:1137 -#: aleksis/apps/chronos/models.py:1338 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 -#: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 -msgid "Teachers" -msgstr "Викладачі" - -#: aleksis/apps/chronos/models.py:358 -msgid "Periods" -msgstr "Години" - -#: aleksis/apps/chronos/models.py:360 aleksis/apps/chronos/models.py:995 -#: aleksis/apps/chronos/models.py:1132 aleksis/apps/chronos/models.py:1326 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 -msgid "Groups" -msgstr "Групи" - -#: aleksis/apps/chronos/models.py:394 aleksis/apps/chronos/models.py:490 -#: aleksis/apps/chronos/models.py:716 aleksis/apps/chronos/models.py:1430 -#: aleksis/apps/chronos/models.py:1454 -msgid "Lesson" -msgstr "Урок" - -#: aleksis/apps/chronos/models.py:395 aleksis/apps/chronos/models.py:1306 -msgid "Lessons" -msgstr "Уроки" - -#: aleksis/apps/chronos/models.py:403 aleksis/apps/chronos/models.py:1116 -msgid "Week" -msgstr "Тиждень" - -#: aleksis/apps/chronos/models.py:404 aleksis/apps/chronos/models.py:1117 -msgid "Year" -msgstr "Рік" - -#: aleksis/apps/chronos/models.py:407 aleksis/apps/chronos/models.py:620 -msgid "Lesson period" -msgstr "Урок за розкладом" - -#: aleksis/apps/chronos/models.py:425 aleksis/apps/chronos/models.py:504 -#: aleksis/apps/chronos/models.py:673 aleksis/apps/chronos/models.py:1144 -#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 -msgid "Room" -msgstr "Кімната" - -#: aleksis/apps/chronos/models.py:428 -msgid "Cancelled?" -msgstr "СкаÑовано?" - -#: aleksis/apps/chronos/models.py:430 -msgid "Cancelled for teachers?" -msgstr "СкаÑовано Ð´Ð»Ñ Ð²Ð¸ÐºÐ»Ð°Ð´Ð°Ñ‡Ñ–Ð²?" - -#: aleksis/apps/chronos/models.py:433 aleksis/apps/chronos/models.py:692 -#: aleksis/apps/chronos/models.py:734 aleksis/apps/chronos/models.py:1147 -#: aleksis/apps/chronos/models.py:1357 -msgid "Comment" -msgstr "Коментар" - -#: aleksis/apps/chronos/models.py:437 -msgid "Lessons can only be either substituted or cancelled." -msgstr "Уроки можуть бути лише або замінені, або ÑкаÑовані." - -#: aleksis/apps/chronos/models.py:477 -msgid "Lesson substitution" -msgstr "Заміна уроку" - -#: aleksis/apps/chronos/models.py:478 -msgid "Lesson substitutions" -msgstr "Заміни уроків" - -#: aleksis/apps/chronos/models.py:621 -msgid "Lesson periods" -msgstr "Ðавчальні години" - -#: aleksis/apps/chronos/models.py:635 aleksis/apps/chronos/models.py:648 -msgid "Absence reason" -msgstr "Причина відÑутноÑÑ‚Ñ–" - -#: aleksis/apps/chronos/models.py:636 -msgid "Absence reasons" -msgstr "Причини відÑутноÑÑ‚Ñ–" - -#: aleksis/apps/chronos/models.py:657 aleksis/apps/chronos/models.py:889 -#: aleksis/apps/chronos/models.py:943 -msgid "Teacher" -msgstr "Викладач" - -#: aleksis/apps/chronos/models.py:665 -msgid "Group" -msgstr "Група" - -#: aleksis/apps/chronos/models.py:681 aleksis/apps/chronos/models.py:723 -msgid "Start period" -msgstr "Початок уроків" - -#: aleksis/apps/chronos/models.py:688 aleksis/apps/chronos/models.py:729 -msgid "End period" -msgstr "Ð—Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ ÑƒÑ€Ð¾ÐºÑ–Ð²" - -#: aleksis/apps/chronos/models.py:702 -msgid "Unknown absence" -msgstr "ВідÑутніÑÑ‚ÑŒ без поÑÑнень" - -#: aleksis/apps/chronos/models.py:707 -msgid "Absence" -msgstr "ВідÑутніÑÑ‚ÑŒ" - -#: aleksis/apps/chronos/models.py:708 -msgid "Absences" -msgstr "ВідÑутноÑÑ‚Ñ–" - -#: aleksis/apps/chronos/models.py:719 -msgid "Date of exam" -msgstr "Дата Ñ–Ñпиту" - -#: aleksis/apps/chronos/models.py:733 aleksis/apps/chronos/models.py:746 -#: aleksis/apps/chronos/models.py:977 -msgid "Title" -msgstr "Ðазва" - -#: aleksis/apps/chronos/models.py:739 -msgid "Exam" -msgstr "ІÑпит" - -#: aleksis/apps/chronos/models.py:740 -msgid "Exams" -msgstr "ІÑпити" - -#: aleksis/apps/chronos/models.py:749 -msgid "Comments" -msgstr "Коментарі" - -#: aleksis/apps/chronos/models.py:788 -msgid "Holiday" -msgstr "Вихідний" - -#: aleksis/apps/chronos/models.py:789 -msgid "Holidays" -msgstr "Вихідні" - -#: aleksis/apps/chronos/models.py:803 aleksis/apps/chronos/models.py:879 -msgid "Supervision area" -msgstr "Зона контролю" - -#: aleksis/apps/chronos/models.py:804 -msgid "Supervision areas" -msgstr "Зони контролю" - -#: aleksis/apps/chronos/models.py:816 -msgid "Time period after break starts" -msgstr "ÐÐ°Ð²Ñ‡Ð°Ð½Ð½Ñ Ð¿Ñ–ÑÐ»Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð¸ починаєтьÑÑ" - -#: aleksis/apps/chronos/models.py:824 -msgid "Time period before break ends" -msgstr "ÐÐ°Ð²Ñ‡Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ´ перервою закінчуєтьÑÑ" - -#: aleksis/apps/chronos/models.py:864 aleksis/apps/chronos/models.py:883 -msgid "Break" -msgstr "Перерва" - -#: aleksis/apps/chronos/models.py:865 -msgid "Breaks" -msgstr "Перерви" - -#: aleksis/apps/chronos/models.py:923 aleksis/apps/chronos/models.py:936 -#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 -msgid "Supervision" -msgstr "Контроль" - -#: aleksis/apps/chronos/models.py:924 aleksis/apps/chronos/models.py:1622 -msgid "Supervisions" -msgstr "Контролі" - -#: aleksis/apps/chronos/models.py:932 -msgid "Date" -msgstr "Дата" - -#: aleksis/apps/chronos/models.py:966 -msgid "Supervision substitution" -msgstr "Заміна контролю" - -#: aleksis/apps/chronos/models.py:967 -msgid "Supervision substitutions" -msgstr "Заміни контролю" - -#: aleksis/apps/chronos/models.py:985 -msgid "Start time period" -msgstr "Початок навчаннÑ" - -#: aleksis/apps/chronos/models.py:991 -msgid "End time period" -msgstr "Ð—Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ð½Ð°Ð²Ñ‡Ð°Ð½Ð½Ñ" - -#: aleksis/apps/chronos/models.py:996 aleksis/apps/chronos/models.py:1332 -#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 -msgid "Rooms" -msgstr "Кімнати" - -#: aleksis/apps/chronos/models.py:1005 -#, python-brace-format -msgid "Event {pk}" -msgstr "ÐŸÐ¾Ð´Ñ–Ñ {pk}" - -#: aleksis/apps/chronos/models.py:1103 -msgid "Event" -msgstr "ПодіÑ" - -#: aleksis/apps/chronos/models.py:1104 -msgid "Events" -msgstr "Події" - -#: aleksis/apps/chronos/models.py:1152 -msgid "Related exam" -msgstr "Пов'Ñзані Ñ–Ñпити" - -#: aleksis/apps/chronos/models.py:1181 -msgid "Extra lesson" -msgstr "Додатковий урок" - -#: aleksis/apps/chronos/models.py:1182 -msgid "Extra lessons" -msgstr "Додаткові уроки" - -#: aleksis/apps/chronos/models.py:1194 +#: aleksis/apps/chronos/models.py:48 msgid "Number of days shown in the plan" msgstr "КількіÑÑ‚ÑŒ днів Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñƒ плані" -#: aleksis/apps/chronos/models.py:1198 +#: aleksis/apps/chronos/models.py:52 msgid "Show header box" msgstr "Показати заголовок" -#: aleksis/apps/chronos/models.py:1199 aleksis/apps/chronos/preferences.py:100 +#: aleksis/apps/chronos/models.py:53 aleksis/apps/chronos/preferences.py:79 msgid "The header box shows affected teachers/groups." msgstr "Заголовок показує викладачів/групи на Ñких це впливає." -#: aleksis/apps/chronos/models.py:1206 +#: aleksis/apps/chronos/models.py:60 msgid "Revision which triggered the last update" msgstr "РевізіÑ, Ñка запуÑтила оÑтаннє оновленнÑ" -#: aleksis/apps/chronos/models.py:1278 +#: aleksis/apps/chronos/models.py:132 msgid "Automatic plan" msgstr "Ðвтоматичний план" -#: aleksis/apps/chronos/models.py:1279 +#: aleksis/apps/chronos/models.py:133 msgid "Automatic plans" msgstr "Ðвтоматичні плани" -#: aleksis/apps/chronos/models.py:1293 +#: aleksis/apps/chronos/models.py:147 msgid "Can view all room timetables" msgstr "Може переглÑдати розклади уÑÑ–Ñ… кімнат" -#: aleksis/apps/chronos/models.py:1294 +#: aleksis/apps/chronos/models.py:148 msgid "Can view all group timetables" msgstr "Може переглÑдати розклади уÑÑ–Ñ… груп" -#: aleksis/apps/chronos/models.py:1295 +#: aleksis/apps/chronos/models.py:149 msgid "Can view all person timetables" msgstr "Може переглÑдати розклади уÑÑ–Ñ… оÑіб" -#: aleksis/apps/chronos/models.py:1296 +#: aleksis/apps/chronos/models.py:150 msgid "Can view timetable overview" msgstr "Може переглÑдати загальний розклад" -#: aleksis/apps/chronos/models.py:1297 -msgid "Can view all lessons per day" -msgstr "Може переглÑдати уÑÑ– уроки окремого днÑ" - -#: aleksis/apps/chronos/models.py:1298 +#: aleksis/apps/chronos/models.py:151 #, fuzzy -#| msgid "Can view all lessons per day" -msgid "Can view all supervisions per day" -msgstr "Може переглÑдати уÑÑ– уроки окремого днÑ" +#| msgid "No substitutions available." +msgid "Can view substitutions table" +msgstr "Заміни недоÑтупні." + +#: aleksis/apps/chronos/models.py:159 +msgid "Lessons" +msgstr "Уроки" + +#: aleksis/apps/chronos/models.py:163 +msgid "Name" +msgstr "Повне ім'Ñ" -#: aleksis/apps/chronos/models.py:1313 +#: aleksis/apps/chronos/models.py:166 #, fuzzy #| msgid "Start time" msgid "Start slot number" msgstr "Ð§Ð°Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ" -#: aleksis/apps/chronos/models.py:1316 +#: aleksis/apps/chronos/models.py:169 #, fuzzy #| msgid "End time" msgid "End slot number" msgstr "Ð§Ð°Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ" -#: aleksis/apps/chronos/models.py:1320 +#: aleksis/apps/chronos/models.py:173 msgid "Course" msgstr "" -#: aleksis/apps/chronos/models.py:1353 +#: aleksis/apps/chronos/models.py:179 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:1 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:26 +msgid "Groups" +msgstr "Групи" + +#: aleksis/apps/chronos/models.py:185 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:4 +msgid "Rooms" +msgstr "Кімнати" + +#: aleksis/apps/chronos/models.py:191 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:3 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:28 +#: aleksis/apps/chronos/templates/chronos/supervision_event_description.txt:1 +msgid "Teachers" +msgstr "Викладачі" + +#: aleksis/apps/chronos/models.py:198 +#: aleksis/apps/chronos/templates/chronos/lesson_event_description.txt:2 +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:29 +msgid "Subject" +msgstr "Предмет" + +#: aleksis/apps/chronos/models.py:206 #: aleksis/apps/chronos/templates/chronos/partials/subs/badge.html:4 msgid "Cancelled" msgstr "СкаÑовано" -#: aleksis/apps/chronos/models.py:1401 aleksis/apps/chronos/models.py:1413 -#: aleksis/apps/chronos/models.py:1425 +#: aleksis/apps/chronos/models.py:210 +msgid "Comment" +msgstr "Коментар" + +#: aleksis/apps/chronos/models.py:254 aleksis/apps/chronos/models.py:266 +#: aleksis/apps/chronos/models.py:278 msgid "{} (instead of {})" msgstr "" -#: aleksis/apps/chronos/models.py:1614 +#: aleksis/apps/chronos/models.py:283 aleksis/apps/chronos/models.py:307 +msgid "Lesson" +msgstr "Урок" + +#: aleksis/apps/chronos/models.py:467 #, fuzzy #| msgid "Lesson" msgid "Lesson Event" msgstr "Урок" -#: aleksis/apps/chronos/models.py:1615 +#: aleksis/apps/chronos/models.py:468 #, fuzzy #| msgid "Lessons" msgid "Lesson Events" msgstr "Уроки" -#: aleksis/apps/chronos/models.py:1630 +#: aleksis/apps/chronos/models.py:475 +msgid "Supervisions" +msgstr "Контролі" + +#: aleksis/apps/chronos/models.py:483 #, fuzzy #| msgid "Supervision" msgid "Supervision: {}" @@ -459,78 +174,43 @@ msgstr "ВикориÑтовувати батьківÑькі групи в ог msgid "If a lesson or substitution has only one group and this group has parent groups, show the parent groups instead of the original group." msgstr "Якщо урок або заміна має лише одну групу Ñ– Ñ†Ñ Ð³Ñ€ÑƒÐ¿Ð° має батьківÑькі групи, відображати ці батьківÑькі групи заміÑÑ‚ÑŒ дійÑної групи." -#: aleksis/apps/chronos/preferences.py:42 -msgid "Shorten groups in timetable views" -msgstr "Скорочувати групи в оглÑді розкладу" - -#: aleksis/apps/chronos/preferences.py:43 -msgid "If there are more groups than the set limit, they will be collapsed." -msgstr "Якщо груп більше за вÑтановлений ліміт, вони будуть згруповані." - -#: aleksis/apps/chronos/preferences.py:51 -msgid "Limit of groups for shortening of groups" -msgstr "Ліміт груп Ð´Ð»Ñ ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Ð³Ñ€ÑƒÐ¿" - -#: aleksis/apps/chronos/preferences.py:53 -#, fuzzy -#| msgid "If an user activates shortening of groups,they will be collapsed if there are more groups than this limit." -msgid "If a user activates shortening of groups,they will be collapsed if there are more groups than this limit." -msgstr "Якщо кориÑтувач активує ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Ð³Ñ€ÑƒÐ¿, Ñ– кількіÑÑ‚ÑŒ груп буде більше за ліміт, вони згруповуватимутьÑÑ." - -#: aleksis/apps/chronos/preferences.py:65 +#: aleksis/apps/chronos/preferences.py:44 #, fuzzy #| msgid "Lesson substitutions" msgid "Relevant days for substitution plans" msgstr "Заміни уроків" -#: aleksis/apps/chronos/preferences.py:82 +#: aleksis/apps/chronos/preferences.py:61 msgid "Time when substitution plans switch to the next day" msgstr "" -#: aleksis/apps/chronos/preferences.py:91 +#: aleksis/apps/chronos/preferences.py:70 msgid "Number of days shown on substitutions print view" msgstr "КількіÑÑ‚ÑŒ днів Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñƒ друкованому виглÑді замін" -#: aleksis/apps/chronos/preferences.py:99 +#: aleksis/apps/chronos/preferences.py:78 msgid "Show header box in substitution views" msgstr "Відображати заголовок в оглÑді замін" -#: aleksis/apps/chronos/preferences.py:109 +#: aleksis/apps/chronos/preferences.py:88 msgid "Show parent groups in header box in substitution views instead of original groups" msgstr "У заголовку в оглÑді замін відображати батьківÑькі групи заміÑÑ‚ÑŒ дійÑних груп" -#: aleksis/apps/chronos/preferences.py:118 -msgid "How many days in advance users should be notified about timetable changes?" -msgstr "За Ñкільки днів потрібно інформувати кориÑтувачів щодо змін у розкладі?" - -#: aleksis/apps/chronos/preferences.py:126 -msgid "Time for sending notifications about timetable changes" -msgstr "Ð§Ð°Ñ Ð´Ð»Ñ Ð½Ð°Ð´ÑÐ¸Ð»Ð°Ð½Ð½Ñ Ñповіщень щодо змін у розкладі" - -#: aleksis/apps/chronos/preferences.py:129 -msgid "This is only used for scheduling notifications which doesn't affect the time period configured above. All other notifications affecting the next days are sent immediately." -msgstr "Це викориÑтовуєтьÑÑ Ð»Ð¸ÑˆÐµ Ð´Ð»Ñ Ð¿Ð»Ð°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñповіщень, Ñкі не впливають на навчальний чаÑ, вÑтановлений вище. УÑÑ– інші ÑповіщеннÑ, Ñкі впливають на найближчі дні, надÑилаютьÑÑ Ð½ÐµÐ³Ð°Ð¹Ð½Ð¾." - -#: aleksis/apps/chronos/preferences.py:140 -#: aleksis/apps/chronos/preferences.py:148 -msgid "Send notifications for current timetable changes" -msgstr "ÐадіÑлати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ñ‰Ð¾Ð´Ð¾ поточних змін у розкладі" - -#: aleksis/apps/chronos/preferences.py:158 +#: aleksis/apps/chronos/preferences.py:99 #, fuzzy #| msgid "Shorten groups in timetable views" msgid "Group types to show in timetables" msgstr "Скорочувати групи в оглÑді розкладу" -#: aleksis/apps/chronos/preferences.py:159 +#: aleksis/apps/chronos/preferences.py:100 msgid "If you leave it empty, all groups will be shown." msgstr "" -#: aleksis/apps/chronos/preferences.py:169 +#: aleksis/apps/chronos/preferences.py:110 msgid "Lesson calendar feed color" msgstr "" -#: aleksis/apps/chronos/preferences.py:181 +#: aleksis/apps/chronos/preferences.py:122 #, fuzzy #| msgid "Supervision area" msgid "Supervision calendar feed color" @@ -556,6 +236,10 @@ msgstr "Ðа Ñкі групи впливає" msgid "all day" msgstr "" +#: aleksis/apps/chronos/templates/chronos/partials/subs/subject.html:3 +msgid "Supervision" +msgstr "Контроль" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:11 msgid "Print: Substitutions" msgstr "Друк: Заміни" @@ -570,6 +254,10 @@ msgstr "Заміни" msgid "Time" msgstr "Розклад" +#: aleksis/apps/chronos/templates/chronos/substitutions_print.html:30 +msgid "Room" +msgstr "Кімната" + #: aleksis/apps/chronos/templates/chronos/substitutions_print.html:31 msgid "Notes" msgstr "Ðотатки" @@ -582,99 +270,303 @@ msgstr "Заміни недоÑтупні." msgid "Areas" msgstr "" -#: aleksis/apps/chronos/util/notifications.py:48 +#~ msgid "Linked validity range" +#~ msgstr "Пов'Ñзаний діапазон" + +#~ msgid "School term" +#~ msgstr "Ðавчальний рік" + +#~ msgid "Start date" +#~ msgstr "Дата початку" + +#~ msgid "End date" +#~ msgstr "Дата закінченнÑ" + +#~ msgid "The start date must be earlier than the end date." +#~ msgstr "Початкова дата повинна бути раніше кінцевої." + +#~ msgid "The validity range must be within the school term." +#~ msgstr "Контрольний період повинен бути в рамках навчального року." + +#~ msgid "There is already a validity range for this time or a part of this time." +#~ msgstr "Ðа цей Ñ‡Ð°Ñ Ð°Ð±Ð¾ на чаÑтину цього чаÑу контрольний період вже призначений." + +#~ msgid "Validity range" +#~ msgstr "Контрольний період" + +#~ msgid "Validity ranges" +#~ msgstr "Контрольні періоди" + +#~ msgid "Week day" +#~ msgstr "День тижнÑ" + +#~ msgid "Number of period" +#~ msgstr "Ðомер урока" + +#~ msgid "Start time" +#~ msgstr "Ð§Ð°Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ" + +#~ msgid "End time" +#~ msgstr "Ð§Ð°Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ" + +#~ msgid "Time period" +#~ msgstr "Ð§Ð°Ñ ÑƒÑ€Ð¾ÐºÑƒ" + +#~ msgid "Time periods" +#~ msgstr "Ð§Ð°Ñ ÑƒÑ€Ð¾ÐºÑ–Ð²" + +#~ msgid "Short name" +#~ msgstr "Коротке ім'Ñ" + +#~ msgid "Long name" +#~ msgstr "Довге ім'Ñ" + +#~ msgid "Foreground colour" +#~ msgstr "ОÑновний колір" + +#~ msgid "Background colour" +#~ msgstr "Колір фону" + +#~ msgid "Subjects" +#~ msgstr "Предмети" + +#~ msgid "Periods" +#~ msgstr "Години" + +#~ msgid "Week" +#~ msgstr "Тиждень" + +#~ msgid "Year" +#~ msgstr "Рік" + +#~ msgid "Lesson period" +#~ msgstr "Урок за розкладом" + +#~ msgid "Cancelled?" +#~ msgstr "СкаÑовано?" + +#~ msgid "Cancelled for teachers?" +#~ msgstr "СкаÑовано Ð´Ð»Ñ Ð²Ð¸ÐºÐ»Ð°Ð´Ð°Ñ‡Ñ–Ð²?" + +#~ msgid "Lessons can only be either substituted or cancelled." +#~ msgstr "Уроки можуть бути лише або замінені, або ÑкаÑовані." + +#~ msgid "Lesson substitution" +#~ msgstr "Заміна уроку" + +#~ msgid "Lesson substitutions" +#~ msgstr "Заміни уроків" + +#~ msgid "Lesson periods" +#~ msgstr "Ðавчальні години" + +#~ msgid "Absence reason" +#~ msgstr "Причина відÑутноÑÑ‚Ñ–" + +#~ msgid "Absence reasons" +#~ msgstr "Причини відÑутноÑÑ‚Ñ–" + +#~ msgid "Teacher" +#~ msgstr "Викладач" + +#~ msgid "Group" +#~ msgstr "Група" + +#~ msgid "Start period" +#~ msgstr "Початок уроків" + +#~ msgid "End period" +#~ msgstr "Ð—Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ ÑƒÑ€Ð¾ÐºÑ–Ð²" + +#~ msgid "Unknown absence" +#~ msgstr "ВідÑутніÑÑ‚ÑŒ без поÑÑнень" + +#~ msgid "Absence" +#~ msgstr "ВідÑутніÑÑ‚ÑŒ" + +#~ msgid "Absences" +#~ msgstr "ВідÑутноÑÑ‚Ñ–" + +#~ msgid "Date of exam" +#~ msgstr "Дата Ñ–Ñпиту" + +#~ msgid "Title" +#~ msgstr "Ðазва" + +#~ msgid "Exam" +#~ msgstr "ІÑпит" + +#~ msgid "Exams" +#~ msgstr "ІÑпити" + +#~ msgid "Comments" +#~ msgstr "Коментарі" + +#~ msgid "Holiday" +#~ msgstr "Вихідний" + +#~ msgid "Holidays" +#~ msgstr "Вихідні" + +#~ msgid "Supervision area" +#~ msgstr "Зона контролю" + +#~ msgid "Supervision areas" +#~ msgstr "Зони контролю" + +#~ msgid "Time period after break starts" +#~ msgstr "ÐÐ°Ð²Ñ‡Ð°Ð½Ð½Ñ Ð¿Ñ–ÑÐ»Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð¸ починаєтьÑÑ" + +#~ msgid "Time period before break ends" +#~ msgstr "ÐÐ°Ð²Ñ‡Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ´ перервою закінчуєтьÑÑ" + +#~ msgid "Break" +#~ msgstr "Перерва" + +#~ msgid "Breaks" +#~ msgstr "Перерви" + +#~ msgid "Date" +#~ msgstr "Дата" + +#~ msgid "Supervision substitution" +#~ msgstr "Заміна контролю" + +#~ msgid "Supervision substitutions" +#~ msgstr "Заміни контролю" + +#~ msgid "Start time period" +#~ msgstr "Початок навчаннÑ" + +#~ msgid "End time period" +#~ msgstr "Ð—Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ð½Ð°Ð²Ñ‡Ð°Ð½Ð½Ñ" + #, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has been cancelled." -msgstr "Урок {subject} на {period} уроці у {day} ÑкаÑований." +#~ msgid "Event {pk}" +#~ msgstr "ÐŸÐ¾Ð´Ñ–Ñ {pk}" + +#~ msgid "Event" +#~ msgstr "ПодіÑ" + +#~ msgid "Events" +#~ msgstr "Події" + +#~ msgid "Related exam" +#~ msgstr "Пов'Ñзані Ñ–Ñпити" + +#~ msgid "Extra lesson" +#~ msgstr "Додатковий урок" + +#~ msgid "Extra lessons" +#~ msgstr "Додаткові уроки" + +#~ msgid "Can view all lessons per day" +#~ msgstr "Може переглÑдати уÑÑ– уроки окремого днÑ" + +#, fuzzy +#~| msgid "Can view all lessons per day" +#~ msgid "Can view all supervisions per day" +#~ msgstr "Може переглÑдати уÑÑ– уроки окремого днÑ" + +#~ msgid "Shorten groups in timetable views" +#~ msgstr "Скорочувати групи в оглÑді розкладу" + +#~ msgid "If there are more groups than the set limit, they will be collapsed." +#~ msgstr "Якщо груп більше за вÑтановлений ліміт, вони будуть згруповані." + +#~ msgid "Limit of groups for shortening of groups" +#~ msgstr "Ліміт груп Ð´Ð»Ñ ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Ð³Ñ€ÑƒÐ¿" + +#, fuzzy +#~| msgid "If an user activates shortening of groups,they will be collapsed if there are more groups than this limit." +#~ msgid "If a user activates shortening of groups,they will be collapsed if there are more groups than this limit." +#~ msgstr "Якщо кориÑтувач активує ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Ð³Ñ€ÑƒÐ¿, Ñ– кількіÑÑ‚ÑŒ груп буде більше за ліміт, вони згруповуватимутьÑÑ." + +#~ msgid "How many days in advance users should be notified about timetable changes?" +#~ msgstr "За Ñкільки днів потрібно інформувати кориÑтувачів щодо змін у розкладі?" + +#~ msgid "Time for sending notifications about timetable changes" +#~ msgstr "Ð§Ð°Ñ Ð´Ð»Ñ Ð½Ð°Ð´ÑÐ¸Ð»Ð°Ð½Ð½Ñ Ñповіщень щодо змін у розкладі" + +#~ msgid "This is only used for scheduling notifications which doesn't affect the time period configured above. All other notifications affecting the next days are sent immediately." +#~ msgstr "Це викориÑтовуєтьÑÑ Ð»Ð¸ÑˆÐµ Ð´Ð»Ñ Ð¿Ð»Ð°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñповіщень, Ñкі не впливають на навчальний чаÑ, вÑтановлений вище. УÑÑ– інші ÑповіщеннÑ, Ñкі впливають на найближчі дні, надÑилаютьÑÑ Ð½ÐµÐ³Ð°Ð¹Ð½Ð¾." + +#~ msgid "Send notifications for current timetable changes" +#~ msgstr "ÐадіÑлати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ñ‰Ð¾Ð´Ð¾ поточних змін у розкладі" -#: aleksis/apps/chronos/util/notifications.py:55 #, python-brace-format -msgid "The {subject} lesson in the {period}. period on {day} has some current changes." -msgstr "Урок {subject} на {period} уроці у {day} зараз дещо змінений." +#~ msgid "The {subject} lesson in the {period}. period on {day} has been cancelled." +#~ msgstr "Урок {subject} на {period} уроці у {day} ÑкаÑований." -#: aleksis/apps/chronos/util/notifications.py:64 #, python-brace-format -msgid "The teacher {old} is substituted by {new}." -msgid_plural "The teachers {old} are substituted by {new}." -msgstr[0] "Викладач {old} замінений викладачем {new}." -msgstr[1] "Викладачі {old} замінені викладачами {new}." -msgstr[2] "Викладачі {old} замінені викладачами {new}." -msgstr[3] "Викладачі {old} замінені викладачами {new}." - -#: aleksis/apps/chronos/util/notifications.py:76 +#~ msgid "The {subject} lesson in the {period}. period on {day} has some current changes." +#~ msgstr "Урок {subject} на {period} уроці у {day} зараз дещо змінений." + #, python-brace-format -msgid "The subject is changed to {subject}." -msgstr "Предмет замінений на {subject}." +#~ msgid "The teacher {old} is substituted by {new}." +#~ msgid_plural "The teachers {old} are substituted by {new}." +#~ msgstr[0] "Викладач {old} замінений викладачем {new}." +#~ msgstr[1] "Викладачі {old} замінені викладачами {new}." +#~ msgstr[2] "Викладачі {old} замінені викладачами {new}." +#~ msgstr[3] "Викладачі {old} замінені викладачами {new}." -#: aleksis/apps/chronos/util/notifications.py:82 #, python-brace-format -msgid "The lesson is moved from {old} to {new}." -msgstr "Урок перенеÑений з {old} до {new}." +#~ msgid "The subject is changed to {subject}." +#~ msgstr "Предмет замінений на {subject}." -#: aleksis/apps/chronos/util/notifications.py:91 #, python-brace-format -msgid "There is an additional comment: {comment}." -msgstr "Маємо додатковий коментар: {comment}." +#~ msgid "The lesson is moved from {old} to {new}." +#~ msgstr "Урок перенеÑений з {old} до {new}." -#: aleksis/apps/chronos/util/notifications.py:99 #, python-brace-format -msgid "There is an event that starts on {date_start}, {period_from}. period and ends on {date_end}, {period_to}. period:" -msgstr "Маємо подію, Ñка розпочинаєтьÑÑ {date_start}, на {period_from}. уроці та закінчуєтьÑÑ {date_end}, на {period_to}. уроці:" +#~ msgid "There is an additional comment: {comment}." +#~ msgstr "Маємо додатковий коментар: {comment}." -#: aleksis/apps/chronos/util/notifications.py:112 #, python-brace-format -msgid "There is an event on {date} from the {period_from}. period to the {period_to}. period:" -msgstr "Маємо подію на {date} з {period_from}. уроку до ÐºÑ–Ð½Ñ†Ñ {period_to}. уроку:" +#~ msgid "There is an event that starts on {date_start}, {period_from}. period and ends on {date_end}, {period_to}. period:" +#~ msgstr "Маємо подію, Ñка розпочинаєтьÑÑ {date_start}, на {period_from}. уроці та закінчуєтьÑÑ {date_end}, на {period_to}. уроці:" -#: aleksis/apps/chronos/util/notifications.py:123 -#: aleksis/apps/chronos/util/notifications.py:143 #, python-brace-format -msgid "Groups: {groups}" -msgstr "Групи: {groups}" +#~ msgid "There is an event on {date} from the {period_from}. period to the {period_to}. period:" +#~ msgstr "Маємо подію на {date} з {period_from}. уроку до ÐºÑ–Ð½Ñ†Ñ {period_to}. уроку:" -#: aleksis/apps/chronos/util/notifications.py:125 -#: aleksis/apps/chronos/util/notifications.py:147 #, python-brace-format -msgid "Teachers: {teachers}" -msgstr "Викладачі: {teachers}" +#~ msgid "Groups: {groups}" +#~ msgstr "Групи: {groups}" -#: aleksis/apps/chronos/util/notifications.py:128 #, python-brace-format -msgid "Rooms: {rooms}" -msgstr "Кімнати: {rooms}" +#~ msgid "Teachers: {teachers}" +#~ msgstr "Викладачі: {teachers}" -#: aleksis/apps/chronos/util/notifications.py:135 #, python-brace-format -msgid "There is an extra lesson on {date} in the {period}. period:" -msgstr "Маємо додатковий урок {date} на {period}. уроці:" +#~ msgid "Rooms: {rooms}" +#~ msgstr "Кімнати: {rooms}" -#: aleksis/apps/chronos/util/notifications.py:145 #, python-brace-format -msgid "Subject: {subject}" -msgstr "Предмет: {subject}" +#~ msgid "There is an extra lesson on {date} in the {period}. period:" +#~ msgstr "Маємо додатковий урок {date} на {period}. уроці:" -#: aleksis/apps/chronos/util/notifications.py:149 #, python-brace-format -msgid "Room: {room}" -msgstr "Кімната: {room}" +#~ msgid "Subject: {subject}" +#~ msgstr "Предмет: {subject}" -#: aleksis/apps/chronos/util/notifications.py:151 #, python-brace-format -msgid "Comment: {comment}." -msgstr "Коментар: {comment}." +#~ msgid "Room: {room}" +#~ msgstr "Кімната: {room}" -#: aleksis/apps/chronos/util/notifications.py:154 #, python-brace-format -msgid "The supervision of {old} on {date} between the {period_from}. period and the {period_to}. period in the area {area} is substituted by {new}." -msgstr "Контроль {old}, {date} між {period_from}. та {period_to}. уроками у зоні {area} замінений на {new}." +#~ msgid "Comment: {comment}." +#~ msgstr "Коментар: {comment}." -#: aleksis/apps/chronos/util/notifications.py:204 -msgid "Timetable" -msgstr "Розклад" +#, python-brace-format +#~ msgid "The supervision of {old} on {date} between the {period_from}. period and the {period_to}. period in the area {area} is substituted by {new}." +#~ msgstr "Контроль {old}, {date} між {period_from}. та {period_to}. уроками у зоні {area} замінений на {new}." + +#~ msgid "Timetable" +#~ msgstr "Розклад" -#: aleksis/apps/chronos/util/notifications.py:205 -msgid "There are current changes to your timetable." -msgstr "У Вашому розкладі Ñ” зміни." +#~ msgid "There are current changes to your timetable." +#~ msgstr "У Вашому розкладі Ñ” зміни." #~ msgid "Options for timetables" #~ msgstr "Опції Ð´Ð»Ñ Ñ€Ð¾Ð·ÐºÐ»Ð°Ð´Ñ–Ð²" diff --git a/aleksis/apps/chronos/managers.py b/aleksis/apps/chronos/managers.py index 1b41f62fb640e93902269dffedbfff401a6e368b..9689ef0b167a6706de08e037c1a1a747a6f3a1b7 100644 --- a/aleksis/apps/chronos/managers.py +++ b/aleksis/apps/chronos/managers.py @@ -1,77 +1,13 @@ -from collections.abc import Iterable -from datetime import date, datetime, timedelta from enum import Enum -from typing import TYPE_CHECKING, Optional, Union +from typing import Optional, Union -from django.db import models -from django.db.models import ExpressionWrapper, F, Func, Q, QuerySet, Value -from django.db.models.fields import DateField -from django.db.models.functions import Concat +from django.db.models import Q -from calendarweek import CalendarWeek - -from aleksis.apps.chronos.util.date import week_weekday_from_date, week_weekday_to_date from aleksis.apps.cursus.models import Course from aleksis.core.managers import ( - AlekSISBaseManagerWithoutMigrations, - DateRangeQuerySetMixin, RecurrencePolymorphicQuerySet, - SchoolTermRelatedQuerySet, ) from aleksis.core.models import Group, Person, Room -from aleksis.core.util.core_helpers import get_site_preferences - -if TYPE_CHECKING: - from .models import Holiday, LessonPeriod, ValidityRange - - -class ValidityRangeQuerySet(QuerySet, DateRangeQuerySetMixin): - """Custom query set for validity ranges.""" - - -class ValidityRangeRelatedQuerySet(QuerySet): - """Custom query set for all models related to validity ranges.""" - - def within_dates(self, start: date, end: date) -> "ValidityRangeRelatedQuerySet": - """Filter for all objects within a date range.""" - return self.filter(validity__date_start__lte=end, validity__date_end__gte=start) - - def in_week(self, wanted_week: CalendarWeek) -> "ValidityRangeRelatedQuerySet": - """Filter for all objects within a calendar week.""" - return self.within_dates(wanted_week[0], wanted_week[6]) - - def on_day(self, day: date) -> "ValidityRangeRelatedQuerySet": - """Filter for all objects on a certain day.""" - return self.within_dates(day, day) - - def for_validity_range(self, validity_range: "ValidityRange") -> "ValidityRangeRelatedQuerySet": - return self.filter(validity=validity_range) - - def for_current_or_all(self) -> "ValidityRangeRelatedQuerySet": - """Get all objects related to current validity range. - - If there is no current validity range, it will return all objects. - """ - from aleksis.apps.chronos.models import ValidityRange - - current_validity_range = ValidityRange.current - if current_validity_range: - return self.for_validity_range(current_validity_range) - else: - return self - - def for_current_or_none(self) -> Union["ValidityRangeRelatedQuerySet", None]: - """Get all objects related to current validity range. - - If there is no current validity range, it will return `None`. - """ - from aleksis.apps.chronos.models import ValidityRange - - current_validity_range = ValidityRange.current - if current_validity_range: - return self.for_validity_range(current_validity_range) - else: - return None class TimetableType(Enum): @@ -88,834 +24,157 @@ class TimetableType(Enum): return cls.__members__.get(s.upper()) -class LessonPeriodManager(AlekSISBaseManagerWithoutMigrations): - """Manager adding specific methods to lesson periods.""" - - def get_queryset(self): - """Ensure all related lesson data is loaded as well.""" - return ( - super() - .get_queryset() - .select_related( - "lesson", - "lesson__subject", - "period", - "room", - "lesson__validity", - "lesson__validity__school_term", - ) - .prefetch_related( - "lesson__groups", - "lesson__groups__parent_groups", - "lesson__teachers", - "substitutions", - ) - ) +class LessonEventQuerySet(RecurrencePolymorphicQuerySet): + """Queryset with special query methods for lesson events.""" + @staticmethod + def for_teacher_q(teacher: Union[int, Person]) -> Q: + """Get all lesson events for a certain person as teacher (including amends).""" + from .models import LessonEvent -class LessonSubstitutionManager(AlekSISBaseManagerWithoutMigrations): - """Manager adding specific methods to lesson substitutions.""" - - def get_queryset(self): - """Ensure all related lesson data is loaded as well.""" - return ( - super() - .get_queryset() - .select_related( - "lesson_period", - "lesson_period__lesson", - "lesson_period__lesson__subject", - "subject", - "lesson_period__period", - "room", - "lesson_period__room", - ) - .prefetch_related( - "lesson_period__lesson__groups", - "lesson_period__lesson__groups__parent_groups", - "teachers", - "lesson_period__lesson__teachers", - ) + amended = ( + LessonEvent.objects.filter(amended_by__isnull=False, teachers=teacher) + .values_list("amended_by__pk", flat=True) + .union(LessonEvent.objects.filter(teachers=teacher).values_list("pk", flat=True)) ) + return Q(pk__in=amended) + def for_teacher(self, teacher: Union[int, Person]) -> "LessonEventQuerySet": + """Get all lesson events for a certain person as teacher (including amends).""" + return self.filter(self.for_teacher_q(teacher)).distinct() -class SupervisionManager(AlekSISBaseManagerWithoutMigrations): - """Manager adding specific methods to supervisions.""" - - def get_queryset(self): - """Ensure all related data is loaded as well.""" - return ( - super() - .get_queryset() - .select_related( - "teacher", - "area", - "break_item", - "break_item__after_period", - "break_item__before_period", - ) - ) - + @staticmethod + def for_participant_q(person: Union[int, Person]) -> Q: + """Get all lesson events the person participates in (including amends).""" + from .models import LessonEvent -class SupervisionSubstitutionManager(AlekSISBaseManagerWithoutMigrations): - """Manager adding specific methods to supervision substitutions.""" - - def get_queryset(self): - """Ensure all related data is loaded as well.""" - return ( - super() - .get_queryset() - .select_related( - "teacher", - "supervision", - "supervision__teacher", - "supervision__area", - "supervision__break_item", - "supervision__break_item__after_period", - "supervision__break_item__before_period", - ) + amended = ( + LessonEvent.objects.filter(amended_by__isnull=False, groups__members=person) + .values_list("amended_by__pk", flat=True) + .union(LessonEvent.objects.filter(groups__members=person).values_list("pk", flat=True)) ) + return Q(pk__in=amended) + def for_participant(self, person: Union[int, Person]) -> "LessonEventQuerySet": + """Get all lesson events the person participates in (including amends).""" + return self.filter(self.for_participant_q(person)).distinct() -class EventManager(AlekSISBaseManagerWithoutMigrations): - """Manager adding specific methods to events.""" - - def get_queryset(self): - """Ensure all related data is loaded as well.""" - return ( - super() - .get_queryset() - .select_related("period_from", "period_to") - .prefetch_related( - "groups", - "groups__school_term", - "groups__parent_groups", - "teachers", - "rooms", - ) - ) - + @staticmethod + def for_group_q(group: Union[int, Group]) -> Q: + """Get all lesson events for a certain group (including amends/as parent group).""" + from .models import LessonEvent -class ExtraLessonManager(AlekSISBaseManagerWithoutMigrations): - """Manager adding specific methods to extra lessons.""" - - def get_queryset(self): - """Ensure all related data is loaded as well.""" - return ( - super() - .get_queryset() - .select_related("room", "period", "subject") - .prefetch_related( - "groups", - "groups__school_term", - "groups__parent_groups", - "teachers", + amended = ( + LessonEvent.objects.filter(amended_by__isnull=False, groups=group) + .values_list("amended_by__pk", flat=True) + .union( + LessonEvent.objects.filter( + amended_by__isnull=False, groups__parent_groups=group + ).values_list("amended_by__pk", flat=True) + ) + .union(LessonEvent.objects.filter(groups=group).values_list("pk", flat=True)) + .union( + LessonEvent.objects.filter(groups__parent_groups=group).values_list("pk", flat=True) ) ) + return Q(pk__in=amended) + def for_group(self, group: Union[int, Group]) -> "LessonEventQuerySet": + """Get all lesson events for a certain group (including amends/as parent group).""" + return self.filter(self.for_group_q(group)).distinct() -class BreakManager(AlekSISBaseManagerWithoutMigrations): - """Manager adding specific methods to breaks.""" - - def get_queryset(self): - """Ensure all related data is loaded as well.""" - return super().get_queryset().select_related("before_period", "after_period") - - -class WeekQuerySetMixin: - def annotate_week(self, week: Union[CalendarWeek]): - """Annotate all lessons in the QuerySet with the number of the provided calendar week.""" - return self.annotate( - _week=models.Value(week.week, models.IntegerField()), - _year=models.Value(week.year, models.IntegerField()), - ) + @staticmethod + def for_room_q(room: Union[int, Room]) -> Q: + """Get all lesson events for a certain room (including amends).""" + from .models import LessonEvent - def alias_week(self, week: Union[CalendarWeek]): - """Add an alias to all lessons in the QuerySet with the number of the calendar week.""" - return self.alias( - _week=models.Value(week.week, models.IntegerField()), - _year=models.Value(week.year, models.IntegerField()), + amended = ( + LessonEvent.objects.filter(amended_by__isnull=False, rooms=room) + .values_list("amended_by__pk", flat=True) + .union(LessonEvent.objects.filter(rooms=room).values_list("pk", flat=True)) ) + return Q(pk__in=amended) + def for_room(self, room: Union[int, Room]) -> "LessonEventQuerySet": + """Get all lesson events for a certain room (including amends).""" + return self.filter(self.for_room_q(room)).distinct() -class GroupByPeriodsMixin: - def group_by_periods(self, is_week: bool = False) -> dict: - """Group a QuerySet of objects with attribute period by period numbers and weekdays.""" - per_period = {} - for obj in self: - period = obj.period.period - weekday = obj.period.weekday - - if period not in per_period: - per_period[period] = [] if not is_week else {} - - if is_week and weekday not in per_period[period]: - per_period[period][weekday] = [] - - if not is_week: - per_period[period].append(obj) - else: - per_period[period][weekday].append(obj) - - return per_period - - -class LessonDataQuerySet(models.QuerySet, WeekQuerySetMixin): - """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 + @staticmethod + def for_course_q(course: Union[int, Course]) -> Q: + """Get all lesson events for a certain course (including amends).""" + from .models import LessonEvent - def within_dates(self, start: date, end: date): - """Filter for all lessons within a date range.""" - return self.filter( - **{ - self._period_path + "lesson__validity__date_start__lte": start, - self._period_path + "lesson__validity__date_end__gte": end, - } + amended = ( + LessonEvent.objects.filter(amended_by__isnull=False, course=course) + .values_list("amended_by__pk", flat=True) + .union(LessonEvent.objects.filter(course=course).values_list("pk", flat=True)) ) + return Q(pk__in=amended) - 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) * (F(self._period_path + "period__weekday")), - wanted_week[0] + timedelta(days=1) * (F(self._period_path + "period__weekday")), - ).annotate_week(wanted_week) - - def on_day(self, day: date): - """Filter for all lessons on a certain day.""" - week, weekday = week_weekday_from_date(day) - - return ( - self.within_dates(day, day) - .filter(**{self._period_path + "period__weekday": weekday}) - .annotate_week(week) - ) + def for_course(self, course: Union[int, Course]) -> "LessonEventQuerySet": + """Get all lesson events for a certain course (including amends).""" + return self.filter(self.for_course_q(course)).distinct() - def at_time(self, when: Optional[datetime] = None): - """Filter for the lessons taking place at a certain point in time.""" - now = when or datetime.now() - week, weekday = week_weekday_from_date(now.date()) - - return self.filter( - **{ - self._period_path + "lesson__validity__date_start__lte": now.date(), - self._period_path + "lesson__validity__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(**{self._period_path + "lesson__groups__members": person})) - - def filter_group(self, group: Union[Group, int]): - """Filter for all lessons a group (class) regularly attends.""" - if isinstance(group, int): - group = Group.objects.get(pk=group) - - if group.parent_groups.all(): - # Prevent to show lessons multiple times - return self.filter(Q(**{self._period_path + "lesson__groups": group})) - else: - return self.filter( - Q(**{self._period_path + "lesson__groups": group}) - | Q(**{self._period_path + "lesson__groups__parent_groups": group}) - ) + @staticmethod + def for_person_q(person: Union[int, Person]) -> Q: + """Get all lesson events for a certain person (as teacher/participant, including amends).""" + from .models import LessonEvent - def filter_groups(self, groups: Iterable[Group]) -> QuerySet: - """Filter for all lessons one of the groups regularly attends.""" - return self.filter( - Q(**{self._period_path + "lesson__groups__in": groups}) - | Q(**{self._period_path + "lesson__groups__parent_groups__in": groups}) + amended = ( + LessonEvent.objects.filter(amended_by__isnull=False, teachers=person) + .values_list("amended_by__pk", flat=True) + .union( + LessonEvent.objects.filter( + amended_by__isnull=False, groups__members=person + ).values_list("amended_by__pk", flat=True) + ) + .union(LessonEvent.objects.filter(teachers=person).values_list("pk", flat=True)) + .union(LessonEvent.objects.filter(groups__members=person).values_list("pk", flat=True)) ) + return Q(pk__in=amended) - def filter_teacher(self, teacher: Union[Person, int], is_smart: bool = True): - """Filter for all lessons given by a certain teacher.""" - qs1 = self.filter(**{self._period_path + "lesson__teachers": teacher}) - qs2 = self.filter( - **{ - self._subst_path + "teachers": teacher, - self._subst_path + "week": F("_week"), - self._subst_path + "year": F("_year"), - } - ) + def for_person(self, person: Union[int, Person]) -> "LessonEventQuerySet": + """Get all lesson events for a certain person (as teacher/participant, including amends).""" + return self.filter(self.for_person_q(person)).distinct() - if is_smart: - return qs1.union(qs2) - else: - return qs1 - - def filter_room(self, room: Union["Room", int], is_smart: bool = True): - """Filter for all lessons taking part in a certain room.""" - qs1 = self.filter(**{self._period_path + "room": room}) - qs2 = self.filter( - **{ - self._subst_path + "room": room, - self._subst_path + "week": F("_week"), - self._subst_path + "year": F("_year"), - } - ) + @staticmethod + def related_to_person_q(person: Union[int, Person]) -> Q: + """Get all lesson events a certain person is allowed to see. - if is_smart: - return qs1.union(qs2) - else: - return qs1 - - def filter_from_type( - self, type_: TimetableType, obj: Union[Person, Group, "Room", int], is_smart: bool = True - ) -> Optional[models.QuerySet]: - """Filter lesson data for a group, teacher or room by provided type.""" - if type_ == TimetableType.GROUP: - return self.filter_group(obj) - elif type_ == TimetableType.TEACHER: - return self.filter_teacher(obj, is_smart=is_smart) - elif type_ == TimetableType.ROOM: - return self.filter_room(obj, is_smart=is_smart) - else: - return None - - def filter_from_person(self, person: Person) -> Optional[models.QuerySet]: - """Filter lesson data for a person.""" - type_ = person.timetable_type - - if type_ == TimetableType.TEACHER: - # Teacher - - return self.filter_teacher(person) - - elif type_ == TimetableType.GROUP: - # Student - - return self.filter_participant(person) - - else: - # If no student or teacher - return None - - def daily_lessons_for_person( - self, person: Person, wanted_day: date - ) -> Optional[models.QuerySet]: - """Filter lesson data on a day by a person.""" - if person.timetable_type is None: - return None - - lesson_periods = self.on_day(wanted_day).filter_from_person(person) - - return lesson_periods - - def group_by_validity(self) -> dict["ValidityRange", list["LessonPeriod"]]: - """Group lesson periods by validity range as dictionary.""" - lesson_periods_by_validity = {} - for lesson_period in self: - lesson_periods_by_validity.setdefault(lesson_period.lesson.validity, []) - lesson_periods_by_validity[lesson_period.lesson.validity].append(lesson_period) - return lesson_periods_by_validity - - def next_lesson( - self, reference: "LessonPeriod", offset: Optional[int] = 1 - ) -> Optional["LessonPeriod"]: - """Get another lesson in an ordered set of lessons. - - By default, it returns the next lesson in the set. By passing the offset argument, - the n-th next lesson can be selected. By passing a negative number, the n-th - previous lesson can be selected. - - This function will handle week, year and validity range changes automatically - if the queryset contains enough lesson data. + This includes all lesson events the person is assigned to as + teacher/participant/group owner/parent group owner, + including those amended. """ - # Group lesson periods by validity to handle validity range changes correctly - lesson_periods_by_validity = self.group_by_validity() - validity_ranges = list(lesson_periods_by_validity.keys()) - - # List with lesson periods in the validity range of the reference lesson period - current_lesson_periods = lesson_periods_by_validity[reference.lesson.validity] - pks = [lesson_period.pk for lesson_period in current_lesson_periods] - - # Position of the reference lesson period - index = pks.index(reference.id) - - next_index = index + offset - if next_index > len(pks) - 1: - next_index %= len(pks) - week = reference._week + 1 - elif next_index < 0: - next_index = len(pks) + next_index - week = reference._week - 1 - else: - week = reference._week - - # Check if selected week makes a year change necessary - year = reference._year - if week < 1: - year -= 1 - week = CalendarWeek.get_last_week_of_year(year).week - elif week > CalendarWeek.get_last_week_of_year(year).week: - year += 1 - week = 1 - - # Get the next lesson period in this validity range and it's date - # to check whether the validity range has to be changed - week = CalendarWeek(week=week, year=year) - next_lesson_period = current_lesson_periods[next_index] - next_lesson_period_date = week_weekday_to_date(week, next_lesson_period.period.weekday) - - validity_index = validity_ranges.index(next_lesson_period.lesson.validity) - - # If date of next lesson period is out of validity range (smaller) ... - if next_lesson_period_date < next_lesson_period.lesson.validity.date_start: - # ... we have to get the lesson period from the previous validity range - if validity_index == 0: - # There are no validity ranges (and thus no lessons) - # in the school term before this lesson period - return None - - # Get new validity range and last lesson period of this validity range - new_validity = validity_ranges[validity_index - 1] - next_lesson_period = lesson_periods_by_validity[new_validity][-1] - - # Build new week with the date from the new validity range/lesson period - week = CalendarWeek( - week=new_validity.date_end.isocalendar()[1], year=new_validity.date_end.year - ) + from .models import LessonEvent - # If date of next lesson period is out of validity range (larger) ... - elif next_lesson_period_date > next_lesson_period.lesson.validity.date_end: - # ... we have to get the lesson period from the next validity range - if validity_index >= len(validity_ranges) - 1: - # There are no validity ranges (and thus no lessons) - # in the school term after this lesson period - return None - - # Get new validity range and first lesson period of this validity range - new_validity = validity_ranges[validity_index + 1] - next_lesson_period = lesson_periods_by_validity[new_validity][0] - - # Build new week with the date from the new validity range/lesson period - week = CalendarWeek( - week=new_validity.date_start.isocalendar()[1], year=new_validity.date_start.year + amended = ( + LessonEvent.objects.filter(amended_by__isnull=False, teachers=person) + .values_list("amended_by__pk", flat=True) + .union( + LessonEvent.objects.filter( + amended_by__isnull=False, groups__members=person + ).values_list("amended_by__pk", flat=True) ) - - # Do a new query here to be able to annotate the new week - return self.annotate_week(week).get(pk=next_lesson_period.pk) - - -class LessonPeriodQuerySet(LessonDataQuerySet, GroupByPeriodsMixin): - """QuerySet with custom query methods for lesson periods.""" - - _period_path = "" - _subst_path = "substitutions__" - - -class LessonSubstitutionQuerySet(LessonDataQuerySet): - """QuerySet with custom query methods for substitutions.""" - - _period_path = "lesson_period__" - _subst_path = "" - - def within_dates(self, start: date, end: date): - """Filter for all substitutions within a date range.""" - start_week = CalendarWeek.from_date(start) - end_week = CalendarWeek.from_date(end) - return self.filter( - week__gte=start_week.week, - week__lte=end_week.week, - year__gte=start_week.year, - year__lte=end_week.year, - ).filter( - Q( - week=start_week.week, - year=start_week.year, - lesson_period__period__weekday__gte=start.weekday(), + .union( + LessonEvent.objects.filter( + amended_by__isnull=False, groups__owners=person + ).values_list("amended_by__pk", flat=True) ) - | Q( - week=end_week.week, - year=end_week.year, - lesson_period__period__weekday__lte=end.weekday(), + .union( + LessonEvent.objects.filter( + amended_by__isnull=False, groups__parent_groups__owners=person + ).values_list("amended_by__pk", flat=True) ) - | ( - ~Q(week=start_week.week, year=start_week.year) - & ~Q(week=end_week.week, year=end_week.year) + .union(LessonEvent.objects.filter(teachers=person).values_list("pk", flat=True)) + .union(LessonEvent.objects.filter(groups__members=person).values_list("pk", flat=True)) + .union(LessonEvent.objects.filter(groups__owners=person).values_list("pk", flat=True)) + .union( + LessonEvent.objects.filter(groups__parent_groups__owners=person).values_list( + "pk", flat=True + ) ) ) - - def in_week(self, wanted_week: CalendarWeek): - """Filter for all lessons within a calendar week.""" - return self.filter(week=wanted_week.week, year=wanted_week.year).annotate_week(wanted_week) - - def on_day(self, day: date): - """Filter for all lessons on a certain day.""" - week, weekday = week_weekday_from_date(day) - - return self.in_week(week).filter(lesson_period__period__weekday=weekday) - - def at_time(self, when: Optional[datetime] = None): - """Filter for the lessons taking place at a certain point in time.""" - now = when or datetime.now() - - return self.on_day(now.date()).filter( - lesson_period__period__time_start__lte=now.time(), - lesson_period__period__time_end__gte=now.time(), - ) - - def affected_lessons(self): - """Return all lessons which are affected by selected substitutions.""" - from .models import Lesson # noaq - - return Lesson.objects.filter(lesson_periods__substitutions__in=self).distinct() - - def affected_teachers(self): - """Get affected teachers. - - Return all teachers which are affected by - selected substitutions (as substituted or substituting). - """ - return ( - Person.objects.filter( - Q(lessons_as_teacher__in=self.affected_lessons()) | Q(lesson_substitutions__in=self) - ) - .distinct() - .order_by("short_name") - ) - - def affected_groups(self): - """Return all groups which are affected by selected substitutions.""" - return ( - Group.objects.filter(lessons__in=self.affected_lessons()) - .distinct() - .order_by("short_name") - ) - - -class DateRangeQuerySetMixin: - """QuerySet with custom query methods for models with date and period ranges. - - Filterable fields: date_start, date_end, period_from, period_to - """ - - def within_dates(self, start: date, end: date): - """Filter for all events within a date range.""" - return self.filter(date_start__lte=end, date_end__gte=start) - - def in_week(self, wanted_week: CalendarWeek): - """Filter for all events within a calendar week.""" - return self.within_dates(wanted_week[0], wanted_week[6]) - - def on_day(self, day: date): - """Filter for all events on a certain day.""" - return self.within_dates(day, day) - - def at_time(self, when: Optional[datetime] = None): - """Filter for the events taking place at a certain point in time.""" - now = when or datetime.now() - - return self.on_day(now.date()).filter( - period_from__time_start__lte=now.time(), period_to__time_end__gte=now.time() - ) - - def exclude_holidays(self, holidays: Iterable["Holiday"]) -> QuerySet: - """Exclude all objects which are in the provided holidays.""" - q = Q() - for holiday in holidays: - q = q | Q(date_start__lte=holiday.date_end, date_end__gte=holiday.date_start) - return self.exclude(q) - - -class AbsenceQuerySet(DateRangeQuerySetMixin, SchoolTermRelatedQuerySet): - """QuerySet with custom query methods for absences.""" - - def absent_teachers(self): - return Person.objects.filter(absences__in=self).distinct().order_by("short_name") - - def absent_groups(self): - return Group.objects.filter(absences__in=self).distinct().order_by("short_name") - - def absent_rooms(self): - return Person.objects.filter(absences__in=self).distinct().order_by("short_name") - - -class HolidayQuerySet(QuerySet, DateRangeQuerySetMixin): - """QuerySet with custom query methods for holidays.""" - - def get_all_days(self) -> list[date]: - """Get all days included in the selected holidays.""" - holiday_days = [] - for holiday in self: - holiday_days += list(holiday.get_days()) - return holiday_days - - -class SupervisionQuerySet(ValidityRangeRelatedQuerySet, WeekQuerySetMixin): - """QuerySet with custom query methods for supervisions.""" - - def filter_by_weekday(self, weekday: int): - """Filter supervisions by weekday.""" - return self.filter( - Q(break_item__before_period__weekday=weekday) - | Q(break_item__after_period__weekday=weekday) - ) - - def filter_by_teacher(self, teacher: Union[Person, int]): - """Filter for all supervisions given by a certain teacher.""" - if self.count() > 0: - if hasattr(self[0], "_week"): - week = CalendarWeek(week=self[0]._week, year=self[0]._year) - else: - week = CalendarWeek.current_week() - - dates = [week[w] for w in range(0, 7)] - - return self.filter( - Q(substitutions__teacher=teacher, substitutions__date__in=dates) - | Q(teacher=teacher) - ) - - return self - - -class TimetableQuerySet(models.QuerySet): - """Common query set methods for objects in timetables. - - Models need following fields: - - groups - - teachers - - rooms (_multiple_rooms=True)/room (_multiple_rooms=False) - """ - - _multiple_rooms = True - - def filter_participant(self, person: Union[Person, int]): - """Filter for all objects a participant (student) attends.""" - return self.filter(Q(groups__members=person)) - - def filter_group(self, group: Union[Group, int]): - """Filter for all objects a group (class) attends.""" - if isinstance(group, int): - group = Group.objects.get(pk=group) - - if group.parent_groups.all(): - # Prevent to show lessons multiple times - return self.filter(groups=group) - else: - return self.filter(Q(groups=group) | Q(groups__parent_groups=group)) - - def filter_groups(self, groups: Iterable[Group]) -> QuerySet: - """Filter for all objects one of the groups attends.""" - return self.filter(Q(groups__in=groups) | Q(groups__parent_groups__in=groups)).distinct() - - def filter_teacher(self, teacher: Union[Person, int]): - """Filter for all lessons given by a certain teacher.""" - return self.filter(teachers=teacher) - - def filter_room(self, room: Union["Room", int]): - """Filter for all objects taking part in a certain room.""" - if self._multiple_rooms: - return self.filter(rooms=room) - else: - return self.filter(room=room) - - def filter_from_type( - self, type_: TimetableType, obj: Union[Group, Person, "Room", int] - ) -> Optional[models.QuerySet]: - """Filter data for a group, teacher or room by provided type.""" - if type_ == TimetableType.GROUP: - return self.filter_group(obj) - elif type_ == TimetableType.TEACHER: - return self.filter_teacher(obj) - elif type_ == TimetableType.ROOM: - return self.filter_room(obj) - else: - return None - - def filter_from_person(self, person: Person) -> Optional[models.QuerySet]: - """Filter data by person.""" - type_ = person.timetable_type - - if type_ == TimetableType.TEACHER: - # Teacher - - return self.filter_teacher(person) - - elif type_ == TimetableType.GROUP: - # Student - - return self.filter_participant(person) - - else: - # If no student or teacher - return None - - -class EventQuerySet(DateRangeQuerySetMixin, SchoolTermRelatedQuerySet, TimetableQuerySet): - """QuerySet with custom query methods for events.""" - - def annotate_day(self, day: date): - """Annotate all events in the QuerySet with the provided date.""" - return self.annotate(_date=models.Value(day, models.DateField())) - - def alias_day(self, day: date): - """Add an alias to all events in the QuerySet with the provided date.""" - return self.alias(_date=models.Value(day, models.DateField())) - - -class ExtraLessonQuerySet(TimetableQuerySet, SchoolTermRelatedQuerySet, GroupByPeriodsMixin): - """QuerySet with custom query methods for extra lessons.""" - - _multiple_rooms = False - - def within_dates(self, start: date, end: date): - """Filter all extra lessons within a specific time range.""" - return self.alias_day().filter(day__gte=start, day__lte=end) - - def on_day(self, day: date): - """Filter all extra lessons on a day.""" - return self.within_dates(day, day) - - def _get_weekday_to_date(self): - """Get DB function to convert a weekday to a date.""" - return ExpressionWrapper( - Func( - Concat(F("year"), F("week")), - Value("IYYYIW"), - output_field=DateField(), - function="TO_DATE", - ) - + F("period__weekday"), - output_field=DateField(), - ) - - def annotate_day(self): - return self.annotate(day=self._get_weekday_to_date()) - - def alias_day(self): - return self.alias(day=self._get_weekday_to_date()) - - def exclude_holidays(self, holidays: Iterable["Holiday"]) -> QuerySet: - """Exclude all extra lessons which are in the provided holidays.""" - q = Q() - for holiday in holidays: - q = q | Q(day__lte=holiday.date_end, day__gte=holiday.date_start) - return self.alias_day().exclude(q) - - -class GroupPropertiesMixin: - """Mixin for common group properties. - - Necessary method: `get_groups` - """ - - @property - def group_names(self, sep: Optional[str] = ", ") -> str: - return sep.join([group.short_name for group in self.get_groups()]) - - @property - def group_short_names(self, sep: Optional[str] = ", ") -> str: - return sep.join([group.short_name for group in self.get_groups()]) - - @property - def groups_to_show(self) -> QuerySet[Group]: - groups = self.get_groups() - if ( - groups.count() == 1 - and groups[0].parent_groups.all() - and get_site_preferences()["chronos__use_parent_groups"] - ): - return groups[0].parent_groups.all() - else: - return groups - - @property - def groups_to_show_names(self, sep: Optional[str] = ", ") -> str: - return sep.join([group.short_name for group in self.groups_to_show]) - - @property - def groups_to_show_short_names(self, sep: Optional[str] = ", ") -> str: - return sep.join([group.short_name for group in self.groups_to_show]) - - -class TeacherPropertiesMixin: - """Mixin for common teacher properties. - - Necessary method: `get_teachers` - """ - - @property - def teacher_names(self, sep: Optional[str] = ", ") -> str: - return sep.join([teacher.full_name for teacher in self.get_teachers()]) - - @property - def teacher_short_names(self, sep: str = ", ") -> str: - return sep.join([teacher.short_name for teacher in self.get_teachers()]) - - -class RoomPropertiesMixin: - """Mixin for common room properties. - - Necessary method: `get_rooms` - """ - - @property - def room_names(self, sep: Optional[str] = ", ") -> str: - return sep.join([room.name for room in self.get_rooms()]) - - @property - def room_short_names(self, sep: str = ", ") -> str: - return sep.join([room.short_name for room in self.get_rooms()]) - - -class LessonEventQuerySet(RecurrencePolymorphicQuerySet): - """Queryset with special query methods for lesson events.""" - - def for_teacher(self, teacher: Union[int, Person]) -> "LessonEventQuerySet": - """Get all lesson events for a certain person as teacher (including amends).""" - amended = self.filter(Q(amended_by__isnull=False) & (Q(teachers=teacher))).values_list( - "amended_by__pk", flat=True - ) - return self.filter(Q(teachers=teacher) | Q(pk__in=amended)).distinct() - - def for_participant(self, person: Union[int, Person]) -> "LessonEventQuerySet": - """Get all lesson events the person participates in (including amends).""" - amended = self.filter(Q(amended_by__isnull=False) & Q(groups__members=person)).values_list( - "amended_by__pk", flat=True - ) - return self.filter(Q(groups__members=person) | Q(pk__in=amended)).distinct() - - def for_group(self, group: Union[int, Group]) -> "LessonEventQuerySet": - """Get all lesson events for a certain group (including amends/as parent group).""" - amended = self.filter( - Q(amended_by__isnull=False) & (Q(groups=group) | Q(groups__parent_groups=group)) - ).values_list("amended_by__pk", flat=True) - return self.filter( - Q(groups=group) | Q(groups__parent_groups=group) | Q(pk__in=amended) - ).distinct() - - def for_room(self, room: Union[int, Room]) -> "LessonEventQuerySet": - """Get all lesson events for a certain room (including amends).""" - amended = self.filter(Q(amended_by__isnull=False) & (Q(rooms=room))).values_list( - "amended_by__pk", flat=True - ) - return self.filter(Q(rooms=room) | Q(pk__in=amended)).distinct() - - def for_course(self, course: Union[int, Course]) -> "LessonEventQuerySet": - """Get all lesson events for a certain course (including amends).""" - amended = self.filter(Q(amended_by__isnull=False) & (Q(course=course))).values_list( - "amended_by__pk", flat=True - ) - return self.filter(Q(course=course) | Q(pk__in=amended)).distinct() - - def for_person(self, person: Union[int, Person]) -> "LessonEventQuerySet": - """Get all lesson events for a certain person (as teacher/participant, including amends).""" - amended = self.filter( - Q(amended_by__isnull=False) & (Q(teachers=person) | Q(groups__members=person)) - ).values_list("amended_by__pk", flat=True) - return self.filter( - Q(teachers=person) | Q(groups__members=person) | Q(pk__in=amended) - ).distinct() + return Q(pk__in=amended) def related_to_person(self, person: Union[int, Person]) -> "LessonEventQuerySet": """Get all lesson events a certain person is allowed to see. @@ -924,34 +183,34 @@ class LessonEventQuerySet(RecurrencePolymorphicQuerySet): teacher/participant/group owner/parent group owner, including those amended. """ - amended = self.filter( - Q(amended_by__isnull=False) - & ( - Q(teachers=person) - | Q(groups__members=person) - | Q(groups__owners=person) - | Q(groups__parent_groups__owners=person) - ) - ).values_list("amended_by__pk", flat=True) - return self.filter( - Q(teachers=person) - | Q(groups__members=person) - | Q(groups__owners=person) - | Q(groups__parent_groups__owners=person) - | Q(pk__in=amended) - ).distinct() + return self.filter(self.related_to_person_q(person)).distinct() + + @staticmethod + def not_amended_q() -> Q: + """Get all lesson events that are not amended.""" + return Q(amended_by__isnull=True) def not_amended(self) -> "LessonEventQuerySet": """Get all lesson events that are not amended.""" - return self.filter(amended_by__isnull=True) + return self.filter(self.not_amended_q()) + + @staticmethod + def not_amending_q() -> Q: + """Get all lesson events that are not amending other events.""" + return Q(amends__isnull=True) def not_amending(self) -> "LessonEventQuerySet": """Get all lesson events that are not amending other events.""" - return self.filter(amends__isnull=True) + return self.filter(self.not_amending_q()) + + @staticmethod + def amending_q() -> Q: + """Get all lesson events that are amending other events.""" + return Q(amends__isnull=False) def amending(self) -> "LessonEventQuerySet": """Get all lesson events that are amending other events.""" - return self.filter(amends__isnull=False) + return self.filter(self.amending_q()) class SupervisionEventQuerySet(LessonEventQuerySet): diff --git a/aleksis/apps/chronos/migrations/0001_initial.py b/aleksis/apps/chronos/migrations/0001_initial.py index 758f099cade786ea7cbab3cb6a4616847fa2c2d6..cf9aea3066896d3724d8f7757a00cb136f3ffc80 100644 --- a/aleksis/apps/chronos/migrations/0001_initial.py +++ b/aleksis/apps/chronos/migrations/0001_initial.py @@ -116,8 +116,6 @@ class Migration(migrations.Migration): }, bases=( models.Model, - aleksis.apps.chronos.managers.GroupPropertiesMixin, - aleksis.apps.chronos.managers.TeacherPropertiesMixin, ), managers=[("objects", aleksis.core.managers.AlekSISBaseManager()),], ), @@ -699,7 +697,7 @@ class Migration(migrations.Migration): "verbose_name": "Extra lesson", "verbose_name_plural": "Extra lessons", }, - bases=(models.Model, aleksis.apps.chronos.managers.GroupPropertiesMixin), + bases=(models.Model,), ), migrations.CreateModel( name="Exam", @@ -832,8 +830,6 @@ class Migration(migrations.Migration): }, bases=( models.Model, - aleksis.apps.chronos.managers.GroupPropertiesMixin, - aleksis.apps.chronos.managers.TeacherPropertiesMixin, ), ), migrations.AddField( diff --git a/aleksis/apps/chronos/migrations/0004_substitution_extra_lesson_year.py b/aleksis/apps/chronos/migrations/0004_substitution_extra_lesson_year.py index cb7383592d167f7d3aa4bf203aaa8cee503d7551..eeb02c4822fd609781ac8b02839511237114a0a2 100644 --- a/aleksis/apps/chronos/migrations/0004_substitution_extra_lesson_year.py +++ b/aleksis/apps/chronos/migrations/0004_substitution_extra_lesson_year.py @@ -1,8 +1,7 @@ # Generated by Django 3.0.9 on 2020-08-13 14:06 from django.db import migrations, models - -import aleksis.apps.chronos.util.date +from django.utils import timezone def migrate_data(apps, schema_editor): @@ -19,7 +18,7 @@ def migrate_data(apps, schema_editor): sub.save() for extra_lesson in ExtraLesson.objects.using(db_alias).all(): - year = aleksis.apps.chronos.util.date.get_current_year() + year = timezone.now().year extra_lesson.year = year extra_lesson.save() @@ -36,7 +35,7 @@ class Migration(migrations.Migration): model_name="extralesson", name="year", field=models.IntegerField( - default=aleksis.apps.chronos.util.date.get_current_year, + default=lambda: timezone.now().year, verbose_name="Year", ), ), @@ -44,7 +43,7 @@ class Migration(migrations.Migration): model_name="lessonsubstitution", name="year", field=models.IntegerField( - default=aleksis.apps.chronos.util.date.get_current_year, + default=lambda: timezone.now().year, verbose_name="Year", ), ), diff --git a/aleksis/apps/chronos/migrations/0018_check_new_models.py b/aleksis/apps/chronos/migrations/0018_check_new_models.py new file mode 100644 index 0000000000000000000000000000000000000000..144a1af5ce41b1313157ac45c75aa55facf4f224 --- /dev/null +++ b/aleksis/apps/chronos/migrations/0018_check_new_models.py @@ -0,0 +1,30 @@ +from django.db import migrations, models + +from django.apps import apps as global_apps + +def check_for_migration(apps, schema_editor): + if global_apps.is_installed('aleksis.apps.lesrooster'): + return + + ValidityRange = apps.get_model('chronos', 'ValidityRange') + Subject = apps.get_model('chronos', 'Subject') + AbsenceReason = apps.get_model('chronos', 'AbsenceReason') + Absence = apps.get_model('chronos', 'Absence') + Holiday = apps.get_model('chronos', 'Holiday') + SupervisionArea = apps.get_model('chronos', 'SupervisionArea') + + model_types = [ValidityRange, Subject, AbsenceReason, Absence, Holiday, SupervisionArea] + + for model in model_types: + if model.objects.exists(): + raise RuntimeError("You have legacy data. Please install AlekSIS-App-Lesrooster to migrate them.") + +class Migration(migrations.Migration): + + dependencies = [ + ('chronos', '0017_optional_slot_number'), + ] + + operations = [ + migrations.RunPython(check_for_migration), + ] diff --git a/aleksis/apps/chronos/migrations/0019_remove_old_models.py b/aleksis/apps/chronos/migrations/0019_remove_old_models.py new file mode 100644 index 0000000000000000000000000000000000000000..f6668aabe3248e1717a2b08fb9cdf90f8a37a4c9 --- /dev/null +++ b/aleksis/apps/chronos/migrations/0019_remove_old_models.py @@ -0,0 +1,260 @@ +# Generated by Django 5.0.8 on 2024-08-14 13:08 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('chronos', '0018_check_new_models'), + ] + + operations = [ + migrations.RemoveField( + model_name='absence', + name='group', + ), + migrations.RemoveField( + model_name='absence', + name='period_from', + ), + migrations.RemoveField( + model_name='absence', + name='period_to', + ), + migrations.RemoveField( + model_name='absence', + name='reason', + ), + migrations.RemoveField( + model_name='absence', + name='room', + ), + migrations.RemoveField( + model_name='absence', + name='school_term', + ), + migrations.RemoveField( + model_name='absence', + name='teacher', + ), + migrations.RemoveField( + model_name='break', + name='after_period', + ), + migrations.RemoveField( + model_name='break', + name='before_period', + ), + migrations.RemoveField( + model_name='break', + name='validity', + ), + migrations.RemoveField( + model_name='supervision', + name='break_item', + ), + migrations.RemoveField( + model_name='event', + name='groups', + ), + migrations.RemoveField( + model_name='event', + name='period_from', + ), + migrations.RemoveField( + model_name='event', + name='period_to', + ), + migrations.RemoveField( + model_name='event', + name='rooms', + ), + migrations.RemoveField( + model_name='event', + name='school_term', + ), + migrations.RemoveField( + model_name='event', + name='teachers', + ), + migrations.RemoveField( + model_name='exam', + name='lesson', + ), + migrations.RemoveField( + model_name='exam', + name='period_from', + ), + migrations.RemoveField( + model_name='exam', + name='period_to', + ), + migrations.RemoveField( + model_name='exam', + name='school_term', + ), + migrations.RemoveField( + model_name='extralesson', + name='exam', + ), + migrations.RemoveField( + model_name='extralesson', + name='groups', + ), + migrations.RemoveField( + model_name='extralesson', + name='period', + ), + migrations.RemoveField( + model_name='extralesson', + name='room', + ), + migrations.RemoveField( + model_name='extralesson', + name='school_term', + ), + migrations.RemoveField( + model_name='extralesson', + name='subject', + ), + migrations.RemoveField( + model_name='extralesson', + name='teachers', + ), + migrations.DeleteModel( + name='Holiday', + ), + migrations.RemoveField( + model_name='lesson', + name='groups', + ), + migrations.RemoveField( + model_name='lesson', + name='periods', + ), + migrations.RemoveField( + model_name='lesson', + name='subject', + ), + migrations.RemoveField( + model_name='lesson', + name='teachers', + ), + migrations.RemoveField( + model_name='lesson', + name='validity', + ), + migrations.RemoveField( + model_name='lessonperiod', + name='lesson', + ), + migrations.RemoveField( + model_name='lessonperiod', + name='period', + ), + migrations.RemoveField( + model_name='lessonperiod', + name='room', + ), + migrations.RemoveField( + model_name='lessonsubstitution', + name='lesson_period', + ), + migrations.RemoveField( + model_name='lessonsubstitution', + name='room', + ), + migrations.RemoveField( + model_name='lessonsubstitution', + name='subject', + ), + migrations.RemoveField( + model_name='lessonsubstitution', + name='teachers', + ), + migrations.RemoveField( + model_name='supervision', + name='area', + ), + migrations.RemoveField( + model_name='supervision', + name='teacher', + ), + migrations.RemoveField( + model_name='supervision', + name='validity', + ), + migrations.RemoveField( + model_name='supervisionsubstitution', + name='supervision', + ), + migrations.RemoveField( + model_name='supervisionsubstitution', + name='teacher', + ), + migrations.RemoveField( + model_name='timeperiod', + name='validity', + ), + migrations.RemoveField( + model_name='validityrange', + name='school_term', + ), + migrations.DeleteModel( + name='TimetableWidget', + ), + migrations.AlterModelOptions( + name='chronosglobalpermissions', + options={'managed': False, 'permissions': (('view_all_room_timetables', 'Can view all room timetables'), ('view_all_group_timetables', 'Can view all group timetables'), ('view_all_person_timetables', 'Can view all person timetables'), ('view_timetable_overview', 'Can view timetable overview'), ('view_substitutions', 'Can view substitutions table'))}, + ), + migrations.AlterModelOptions( + name='supervisionevent', + options={'base_manager_name': 'objects'}, + ), + migrations.DeleteModel( + name='AbsenceReason', + ), + migrations.DeleteModel( + name='Absence', + ), + migrations.DeleteModel( + name='Break', + ), + migrations.DeleteModel( + name='Event', + ), + migrations.DeleteModel( + name='Exam', + ), + migrations.DeleteModel( + name='ExtraLesson', + ), + migrations.DeleteModel( + name='Lesson', + ), + migrations.DeleteModel( + name='LessonPeriod', + ), + migrations.DeleteModel( + name='Subject', + ), + migrations.DeleteModel( + name='LessonSubstitution', + ), + migrations.DeleteModel( + name='SupervisionArea', + ), + migrations.DeleteModel( + name='Supervision', + ), + migrations.DeleteModel( + name='SupervisionSubstitution', + ), + migrations.DeleteModel( + name='TimePeriod', + ), + migrations.DeleteModel( + name='ValidityRange', + ), + ] diff --git a/aleksis/apps/chronos/migrations/0018_add_global_permissions.py b/aleksis/apps/chronos/migrations/0020_add_global_permissions.py similarity index 93% rename from aleksis/apps/chronos/migrations/0018_add_global_permissions.py rename to aleksis/apps/chronos/migrations/0020_add_global_permissions.py index c51cd016c8b70e9c07818465589068168101bd61..4cb067f833d364df0b8869de44ac427a8522fe58 100644 --- a/aleksis/apps/chronos/migrations/0018_add_global_permissions.py +++ b/aleksis/apps/chronos/migrations/0020_add_global_permissions.py @@ -6,7 +6,7 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('chronos', '0017_optional_slot_number'), + ('chronos', '0019_remove_old_models'), ] operations = [ diff --git a/aleksis/apps/chronos/mixins.py b/aleksis/apps/chronos/mixins.py deleted file mode 100644 index 8cb4dab29467236f950b5b9ff56f5fa7ab7da435..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/mixins.py +++ /dev/null @@ -1,60 +0,0 @@ -from datetime import date -from typing import Union - -from django.db import models -from django.utils.translation import gettext as _ - -from calendarweek import CalendarWeek - -from aleksis.apps.chronos.util.date import week_weekday_to_date -from aleksis.core.managers import AlekSISBaseManagerWithoutMigrations -from aleksis.core.mixins import ExtensibleModel - -from .managers import ValidityRangeRelatedQuerySet - - -class ValidityRangeRelatedExtensibleModel(ExtensibleModel): - """Add relation to validity range.""" - - objects = AlekSISBaseManagerWithoutMigrations.from_queryset(ValidityRangeRelatedQuerySet)() - - validity = models.ForeignKey( - "chronos.ValidityRange", - on_delete=models.CASCADE, - related_name="+", - verbose_name=_("Linked validity range"), - null=True, - blank=True, - ) - - class Meta: - abstract = True - - -class WeekRelatedMixin: - @property - def date(self) -> date: - period = self.lesson_period.period if hasattr(self, "lesson_period") else self.period - return week_weekday_to_date(self.calendar_week, period.weekday) - - @property - def calendar_week(self) -> CalendarWeek: - return CalendarWeek(week=self.week, year=self.year) - - -class WeekAnnotationMixin: - def annotate_week(self, week: CalendarWeek): - """Annotate this lesson with the number of the provided calendar week.""" - self._week = week.week - self._year = week.year - - @property - def week(self) -> Union[CalendarWeek, None]: - """Get annotated week as `CalendarWeek`. - - Defaults to `None` if no week is annotated. - """ - if hasattr(self, "_week"): - return CalendarWeek(week=self._week, year=self._year) - else: - return None diff --git a/aleksis/apps/chronos/model_extensions.py b/aleksis/apps/chronos/model_extensions.py index 8046899166ec21096401b3f5aa3f7ee930989112..81f9260450b626fa56dd6b51a4e1b4cb5b579d83 100644 --- a/aleksis/apps/chronos/model_extensions.py +++ b/aleksis/apps/chronos/model_extensions.py @@ -1,143 +1,7 @@ -from datetime import date -from typing import Optional, Union - -from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ -from reversion.models import Revision - from aleksis.apps.cursus.models import Course -from aleksis.core.models import Announcement, Group, Person -from aleksis.core.util.core_helpers import get_site_preferences - -from .managers import TimetableType -from .models import Lesson, LessonPeriod -from .util.change_tracker import timetable_data_changed -from .util.notifications import send_notifications_for_object - - -@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 timetable_type(self) -> Optional[TimetableType]: - """Return which type of timetable this user has.""" - if self.is_teacher: - return TimetableType.TEACHER - elif self.primary_group: - return TimetableType.GROUP - else: - return None - - -@Person.property_ -def timetable_object(self) -> Optional[Union[Group, Person]]: - """Return the object which has the user's timetable.""" - type_ = self.timetable_type - - if type_ == TimetableType.TEACHER: - return self - elif type_ == TimetableType.GROUP: - return self.primary_group - else: - return None - - -@Person.property_ -def lessons_as_participant(self): - """Return a `QuerySet` containing all `Lesson`s this person participates in (as student). - - .. note:: Only available when AlekSIS-App-Chronos is installed. - - :Date: 2019-11-07 - :Authors: - - Dominik George <dominik.george@teckids.org> - """ - return Lesson.objects.filter(groups__members=self) - - -@Person.property_ -def lesson_periods_as_participant(self): - """Return a `QuerySet` containing all `LessonPeriod`s this person participates in (as student). - - .. note:: Only available when AlekSIS-App-Chronos is installed. - - :Date: 2019-11-07 - :Authors: - - Dominik George <dominik.george@teckids.org> - """ - return LessonPeriod.objects.filter(lesson__groups__members=self) - - -@Person.property_ -def lesson_periods_as_teacher(self): - """Return a `QuerySet` containing all `Lesson`s this person gives (as teacher). - - .. note:: Only available when AlekSIS-App-Chronos is installed. - - :Date: 2019-11-07 - :Authors: - - Dominik George <dominik.george@teckids.org> - """ - return LessonPeriod.objects.filter(lesson__teachers=self) - - -@Person.method -def lessons_on_day(self, day: date): - """Get all lessons of this person (either as participant or teacher) on the given day.""" - qs = LessonPeriod.objects.on_day(day).filter_from_person(self) - if qs: - # This is a union queryset, so order by must be after the union. - return qs.order_by("period__period") - return None - - -@Person.method -def _adjacent_lesson( - self, lesson_period: "LessonPeriod", day: date, offset: int = 1 -) -> Union["LessonPeriod", None]: - """Get next/previous lesson of the person (either as participant or teacher) on the same day.""" - daily_lessons = self.lessons_on_day(day) - - if not daily_lessons: - return None - - ids = list(daily_lessons.values_list("id", flat=True)) - - # Check if the lesson period is one of the person's lesson periods on this day - # and return None if it's not so - if lesson_period.pk not in ids: - return None - - index = ids.index(lesson_period.pk) - - if (offset > 0 and index + offset < len(ids)) or (offset < 0 and index >= -offset): - return daily_lessons[index + offset] - else: - return None - - -@Person.method -def next_lesson(self, lesson_period: "LessonPeriod", day: date) -> Union["LessonPeriod", None]: - """Get next lesson of the person (either as participant or teacher) on the same day.""" - return self._adjacent_lesson(lesson_period, day) - - -@Person.method -def previous_lesson(self, lesson_period: "LessonPeriod", day: date) -> Union["LessonPeriod", None]: - """Get previous lesson of the person (either as participant or teacher) on the same day.""" - return self._adjacent_lesson(lesson_period, day, offset=-1) - - -def for_timetables(cls): - """Return all announcements that should be shown in timetable views.""" - return cls.objects.all() - - -Announcement.class_method(for_timetables) +from aleksis.core.models import Group, Person # Dynamically add extra permissions to Group and Person models in core and Course model in cursus # Note: requires migrate afterwards @@ -153,16 +17,3 @@ Course.add_permission( "view_course_timetable", _("Can view course timetable"), ) - - -@receiver(timetable_data_changed) -def send_notifications(sender: Revision, **kwargs): - """Send notifications to users about the changes.""" - if not get_site_preferences()["chronos__send_notifications_site"]: - return - - for change in sender.changes.values(): - if change.deleted: - continue - - send_notifications_for_object(change.instance) diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py index 880b92fcf361c9d690130202747101ad062debdb..d4856154598b401a0c5136cca0c77119842c625a 100644 --- a/aleksis/apps/chronos/models.py +++ b/aleksis/apps/chronos/models.py @@ -2,1188 +2,41 @@ from __future__ import annotations import itertools -from collections.abc import Iterable, Iterator -from datetime import date, datetime, time, timedelta -from itertools import chain +from collections.abc import Iterable +from datetime import date from typing import Any from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import PermissionDenied, ValidationError from django.core.validators import MinValueValidator from django.db import models -from django.db.models import Max, Min, Q, QuerySet -from django.db.models.functions import Coalesce +from django.db.models import Q, QuerySet from django.dispatch import receiver from django.http import HttpRequest from django.template.loader import render_to_string -from django.urls import reverse from django.utils import timezone -from django.utils.formats import date_format -from django.utils.functional import classproperty from django.utils.translation import gettext_lazy as _ -from cache_memoize import cache_memoize -from calendarweek.django import CalendarWeek, i18n_day_abbr_choices_lazy, i18n_day_name_choices_lazy -from colorfield.fields import ColorField -from model_utils import FieldTracker from reversion.models import Revision, Version from aleksis.apps.chronos.managers import ( - AbsenceQuerySet, - BreakManager, - EventManager, - EventQuerySet, - ExtraLessonManager, - ExtraLessonQuerySet, - GroupPropertiesMixin, - HolidayQuerySet, LessonEventQuerySet, - LessonPeriodManager, - LessonPeriodQuerySet, - LessonSubstitutionManager, - LessonSubstitutionQuerySet, SupervisionEventQuerySet, - SupervisionManager, - SupervisionQuerySet, - SupervisionSubstitutionManager, - TeacherPropertiesMixin, - ValidityRangeQuerySet, -) -from aleksis.apps.chronos.mixins import ( - ValidityRangeRelatedExtensibleModel, - WeekAnnotationMixin, - WeekRelatedMixin, ) from aleksis.apps.chronos.util.change_tracker import _get_substitution_models, substitutions_changed -from aleksis.apps.chronos.util.date import get_current_year -from aleksis.apps.chronos.util.format import format_m2m from aleksis.apps.cursus import models as cursus_models from aleksis.apps.cursus.models import Course from aleksis.apps.resint.models import LiveDocument from aleksis.core.managers import ( - AlekSISBaseManagerWithoutMigrations, RecurrencePolymorphicManager, ) from aleksis.core.mixins import ( - ExtensibleModel, GlobalPermissionModel, - SchoolTermRelatedExtensibleModel, ) -from aleksis.core.models import CalendarEvent, Group, Person, Room, SchoolTerm +from aleksis.core.models import CalendarEvent, Group, Person, Room from aleksis.core.util.core_helpers import get_site_preferences, has_person from aleksis.core.util.predicates import check_global_permission -class ValidityRange(ExtensibleModel): - """Validity range model. - - This is used to link data to a validity range. - """ - - objects = AlekSISBaseManagerWithoutMigrations.from_queryset(ValidityRangeQuerySet)() - - school_term = models.ForeignKey( - SchoolTerm, - on_delete=models.CASCADE, - verbose_name=_("School term"), - related_name="validity_ranges", - ) - name = models.CharField(verbose_name=_("Name"), max_length=255, blank=True) - - date_start = models.DateField(verbose_name=_("Start date")) - date_end = models.DateField(verbose_name=_("End date")) - - @classmethod - @cache_memoize(3600) - def get_current(cls, day: date | None = None): - if not day: - day = timezone.now().date() - try: - return cls.objects.on_day(day).first() - except ValidityRange.DoesNotExist: - return None - - @classproperty - def current(cls): - return cls.get_current() - - def clean(self): - """Ensure there is only one validity range at each point of time.""" - if self.date_end < self.date_start: - raise ValidationError(_("The start date must be earlier than the end date.")) - - if self.school_term and ( - self.date_end > self.school_term.date_end - or self.date_start < self.school_term.date_start - ): - raise ValidationError(_("The validity range must be within the school term.")) - - qs = ValidityRange.objects.within_dates(self.date_start, self.date_end) - if self.pk: - qs = qs.exclude(pk=self.pk) - if qs.exists(): - raise ValidationError( - _("There is already a validity range for this time or a part of this time.") - ) - - def __str__(self): - return self.name or f"{date_format(self.date_start)}–{date_format(self.date_end)}" - - class Meta: - verbose_name = _("Validity range") - verbose_name_plural = _("Validity ranges") - constraints = [ - models.UniqueConstraint( - fields=["school_term", "date_start", "date_end"], name="unique_dates_per_term" - ), - ] - indexes = [ - models.Index(fields=["date_start", "date_end"], name="validity_date_start_date_end") - ] - - -class TimePeriod(ValidityRangeRelatedExtensibleModel): - WEEKDAY_CHOICES = i18n_day_name_choices_lazy() - WEEKDAY_CHOICES_SHORT = i18n_day_abbr_choices_lazy() - - weekday = models.PositiveSmallIntegerField( - verbose_name=_("Week day"), choices=i18n_day_name_choices_lazy() - ) - period = models.PositiveSmallIntegerField(verbose_name=_("Number of period")) - - time_start = models.TimeField(verbose_name=_("Start time")) - time_end = models.TimeField(verbose_name=_("End time")) - - def __str__(self) -> str: - return f"{self.get_weekday_display()}, {self.period}." - - @classmethod - def get_times_dict(cls) -> dict[int, tuple[datetime, datetime]]: - periods = {} - for period in cls.objects.for_current_or_all().all(): - periods[period.period] = (period.time_start, period.time_end) - - return periods - - def get_date(self, week: CalendarWeek | None = None) -> date: - if isinstance(week, CalendarWeek): - wanted_week = week - else: - year = getattr(self, "_year", None) or date.today().year - week_number = getattr(self, "_week", None) or CalendarWeek().week - - wanted_week = CalendarWeek(year=year, week=week_number) - - return wanted_week[self.weekday] - - def get_datetime_start(self, date_ref: CalendarWeek | int | date | None = None) -> datetime: - """Get datetime of lesson start in a specific week.""" - day = date_ref if isinstance(date_ref, date) else self.get_date(date_ref) - return datetime.combine(day, self.time_start) - - def get_datetime_end(self, date_ref: CalendarWeek | int | date | None = None) -> datetime: - """Get datetime of lesson end in a specific week.""" - day = date_ref if isinstance(date_ref, date) else self.get_date(date_ref) - return datetime.combine(day, self.time_end) - - @classmethod - def get_next_relevant_day( - cls, day: date | None = None, time: time | None = None, prev: bool = False - ) -> date: - """Return next (previous) day with lessons depending on date and time.""" - if day is None: - day = timezone.now().date() - - if time is not None and cls.time_max and not prev and time > cls.time_max: - day += timedelta(days=1) - - cw = CalendarWeek.from_date(day) - - if day.weekday() > cls.weekday_max: - if prev: - day = cw[cls.weekday_max] - else: - cw += 1 - day = cw[cls.weekday_min] - elif day.weekday() < TimePeriod.weekday_min: - if prev: - cw -= 1 - day = cw[cls.weekday_max] - else: - day = cw[cls.weekday_min] - - return day - - @classmethod - def get_relevant_week_from_datetime(cls, when: datetime | None = None) -> CalendarWeek: - """Return currently relevant week depending on current date and time.""" - if not when: - when = timezone.now() - - day = when.date() - time = when.time() - - week = CalendarWeek.from_date(day) - - if (cls.weekday_max and day.weekday() > cls.weekday_max) or ( - cls.time_max and time > cls.time_max and day.weekday() == cls.weekday_max - ): - week += 1 - - return week - - @classmethod - def get_prev_next_by_day(cls, day: date, url: str) -> tuple[str, str]: - """Build URLs for previous/next day.""" - day_prev = cls.get_next_relevant_day(day - timedelta(days=1), prev=True) - day_next = cls.get_next_relevant_day(day + timedelta(days=1)) - - url_prev = reverse(url, args=[day_prev.year, day_prev.month, day_prev.day]) - url_next = reverse(url, args=[day_next.year, day_next.month, day_next.day]) - - return url_prev, url_next - - @classmethod - def from_period(cls, period: int, day: date) -> TimePeriod: - """Get `TimePeriod` object for a period on a specific date. - - This will respect the relation to validity ranges. - """ - return cls.objects.on_day(day).filter(period=period, weekday=day.weekday()).first() - - @classproperty - @cache_memoize(3600) - def period_min(cls) -> int: - return ( - cls.objects.for_current_or_all() - .aggregate(period__min=Coalesce(Min("period"), 1)) - .get("period__min") - ) - - @classproperty - @cache_memoize(3600) - def period_max(cls) -> int: - return ( - cls.objects.for_current_or_all() - .aggregate(period__max=Coalesce(Max("period"), 7)) - .get("period__max") - ) - - @classproperty - @cache_memoize(3600) - def time_min(cls) -> time | None: - return cls.objects.for_current_or_all().aggregate(Min("time_start")).get("time_start__min") - - @classproperty - @cache_memoize(3600) - def time_max(cls) -> time | None: - return cls.objects.for_current_or_all().aggregate(Max("time_end")).get("time_end__max") - - @classproperty - @cache_memoize(3600) - def weekday_min(cls) -> int: - return ( - cls.objects.for_current_or_all() - .aggregate(weekday__min=Coalesce(Min("weekday"), 0)) - .get("weekday__min") - ) - - @classproperty - @cache_memoize(3600) - def weekday_max(cls) -> int: - return ( - cls.objects.for_current_or_all() - .aggregate(weekday__max=Coalesce(Max("weekday"), 6)) - .get("weekday__max") - ) - - @classproperty - @cache_memoize(3600) - def period_choices(cls) -> list[tuple[str | int, str]]: - """Build choice list of periods for usage within Django.""" - time_periods = ( - cls.objects.filter(weekday=cls.weekday_min) - .for_current_or_all() - .values("period", "time_start", "time_end") - .distinct() - ) - - period_choices = [("", "")] + [ - (period, f"{period}.") for period in time_periods.values_list("period", flat=True) - ] - - return period_choices - - class Meta: - constraints = [ - models.UniqueConstraint( - fields=["weekday", "period", "validity"], name="unique_period_per_range" - ), - ] - ordering = ["weekday", "period"] - indexes = [models.Index(fields=["time_start", "time_end"])] - verbose_name = _("Time period") - verbose_name_plural = _("Time periods") - - -class Subject(ExtensibleModel): - short_name = models.CharField(verbose_name=_("Short name"), max_length=255, unique=True) - name = models.CharField(verbose_name=_("Long name"), max_length=255) - - colour_fg = ColorField(verbose_name=_("Foreground colour"), blank=True) - colour_bg = ColorField(verbose_name=_("Background colour"), blank=True) - - def __str__(self) -> str: - return f"{self.short_name} ({self.name})" - - class Meta: - ordering = ["name", "short_name"] - verbose_name = _("Subject") - verbose_name_plural = _("Subjects") - - -class Lesson(ValidityRangeRelatedExtensibleModel, GroupPropertiesMixin, TeacherPropertiesMixin): - subject = models.ForeignKey( - "Subject", - on_delete=models.CASCADE, - related_name="lessons", - verbose_name=_("Subject"), - ) - teachers = models.ManyToManyField( - "core.Person", related_name="lessons_as_teacher", verbose_name=_("Teachers") - ) - periods = models.ManyToManyField( - "TimePeriod", - related_name="lessons", - through="LessonPeriod", - verbose_name=_("Periods"), - ) - groups = models.ManyToManyField("core.Group", related_name="lessons", verbose_name=_("Groups")) - - def get_year(self, week: int) -> int: - year = self.validity.date_start.year - if week < int(self.validity.date_start.strftime("%V")): - year += 1 - return year - - def get_calendar_week(self, week: int): - year = self.get_year(week) - - return CalendarWeek(year=year, week=week) - - def get_teachers(self) -> models.query.QuerySet: - """Get teachers relation.""" - return self.teachers - - @property - def _equal_lessons(self): - """Get all lesson periods with equal lessons in the whole school term.""" - - qs = Lesson.objects.filter( - subject=self.subject, - validity__school_term=self.validity.school_term, - ) - for group in self.groups.all(): - qs = qs.filter(groups=group) - return qs - - def __str__(self): - return f"{format_m2m(self.groups)}, {self.subject.short_name}, {format_m2m(self.teachers)}" - - class Meta: - ordering = ["validity__date_start", "subject"] - verbose_name = _("Lesson") - verbose_name_plural = _("Lessons") - - -class LessonSubstitution(ExtensibleModel, TeacherPropertiesMixin, WeekRelatedMixin): - objects = LessonSubstitutionManager.from_queryset(LessonSubstitutionQuerySet)() - - tracker = FieldTracker() - - week = models.IntegerField(verbose_name=_("Week"), default=CalendarWeek.current_week) - year = models.IntegerField(verbose_name=_("Year"), default=get_current_year) - - lesson_period = models.ForeignKey( - "LessonPeriod", models.CASCADE, "substitutions", verbose_name=_("Lesson period") - ) - - subject = models.ForeignKey( - "Subject", - on_delete=models.CASCADE, - related_name="lesson_substitutions", - null=True, - blank=True, - verbose_name=_("Subject"), - ) - teachers = models.ManyToManyField( - "core.Person", - related_name="lesson_substitutions", - blank=True, - verbose_name=_("Teachers"), - ) - room = models.ForeignKey( - "core.Room", models.CASCADE, null=True, blank=True, verbose_name=_("Room") - ) - - cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled?")) - cancelled_for_teachers = models.BooleanField( - default=False, verbose_name=_("Cancelled for teachers?") - ) - - comment = models.TextField(verbose_name=_("Comment"), blank=True) - - def clean(self) -> None: - if self.subject and self.cancelled: - raise ValidationError(_("Lessons can only be either substituted or cancelled.")) - - @property - def date(self): - week = CalendarWeek(week=self.week, year=self.year) - return week[self.lesson_period.period.weekday] - - @property - def time_range(self) -> (timezone.datetime, timezone.datetime): - """Get the time range of this substitution.""" - return timezone.datetime.combine( - self.date, self.lesson_period.period.time_start - ), timezone.datetime.combine(self.date, self.lesson_period.period.time_end) - - def get_teachers(self): - return self.teachers - - def __str__(self): - return f"{self.lesson_period}, {date_format(self.date)}" - - class Meta: - ordering = [ - "year", - "week", - "lesson_period__period__weekday", - "lesson_period__period__period", - ] - constraints = [ - models.CheckConstraint( - check=~Q(cancelled=True, subject__isnull=False), - name="either_substituted_or_cancelled", - ), - models.UniqueConstraint( - fields=["lesson_period", "week", "year"], name="unique_period_per_week" - ), - ] - indexes = [ - models.Index(fields=["week", "year"], name="substitution_week_year"), - models.Index(fields=["lesson_period"], name="substitution_lesson_period"), - ] - verbose_name = _("Lesson substitution") - verbose_name_plural = _("Lesson substitutions") - - -class LessonPeriod(WeekAnnotationMixin, TeacherPropertiesMixin, ExtensibleModel): - label_ = "lesson_period" - - objects = LessonPeriodManager.from_queryset(LessonPeriodQuerySet)() - - lesson = models.ForeignKey( - "Lesson", - models.CASCADE, - related_name="lesson_periods", - verbose_name=_("Lesson"), - ) - period = models.ForeignKey( - "TimePeriod", - models.CASCADE, - related_name="lesson_periods", - verbose_name=_("Time period"), - ) - - room = models.ForeignKey( - "core.Room", - models.CASCADE, - null=True, - related_name="lesson_periods", - verbose_name=_("Room"), - ) - - def get_substitution(self, week: CalendarWeek | None = None) -> LessonSubstitution: - wanted_week = week or self.week or CalendarWeek() - - # We iterate over all substitutions because this can make use of - # prefetching when this model is loaded from outside, in contrast - # to .filter() - for substitution in self.substitutions.all(): - if substitution.week == wanted_week.week and substitution.year == wanted_week.year: - return substitution - return None - - def get_subject(self) -> Subject | None: - sub = self.get_substitution() - if sub and sub.subject: - return sub.subject - else: - return self.lesson.subject - - def get_teachers(self) -> models.query.QuerySet: - sub = self.get_substitution() - if sub and sub.teachers.all(): - return sub.teachers - else: - return self.lesson.teachers - - def get_room(self) -> Room | None: - if self.get_substitution() and self.get_substitution().room: - return self.get_substitution().room - else: - return self.room - - def get_groups(self) -> models.query.QuerySet: - return self.lesson.groups - - @property - def group_names(self): - """Get group names as joined string.""" - return self.lesson.group_names - - @property - def group_short_names(self): - """Get group short names as joined string.""" - return self.lesson.group_short_names - - def __str__(self) -> str: - return f"{self.period}, {self.lesson}" - - @property - def _equal_lesson_periods(self): - """Get all lesson periods with equal lessons in the whole school term.""" - - return LessonPeriod.objects.filter(lesson__in=self.lesson._equal_lessons) - - @property - def next(self) -> LessonPeriod: # noqa - """Get next lesson period of this lesson. - - .. warning:: - To use this property, the provided lesson period must be annotated with a week. - """ - return self._equal_lesson_periods.next_lesson(self) - - @property - def prev(self) -> LessonPeriod: - """Get previous lesson period of this lesson. - - .. warning:: - To use this property, the provided lesson period must be annotated with a week. - """ - return self._equal_lesson_periods.next_lesson(self, -1) - - def is_replaced_by_event( - self, events: Iterable[Event], groups: Iterable[Group] | None = None - ) -> bool: - """Check if this lesson period is replaced by an event.""" - groups_of_event = set(chain(*[event.groups.all() for event in events])) - - if groups: - # If the current group is a part of the event, - # there are no other lessons for the group. - groups = set(groups) - if groups.issubset(groups_of_event): - return True - else: - groups_lesson_period = set(self.lesson.groups.all()) - - # The lesson period isn't replacable if the lesson has no groups at all - if not groups_lesson_period: - return False - - # This lesson period is replaced by an event ... - # ... if all groups of this lesson period are a part of the event ... - if groups_lesson_period.issubset(groups_of_event): - return True - - all_parent_groups = set( - chain(*[group.parent_groups.all() for group in groups_lesson_period]) - ) - # ... or if all parent groups of this lesson period are a part of the event. - if all_parent_groups.issubset(groups_of_event): - return True - - class Meta: - ordering = [ - "lesson__validity__date_start", - "period__weekday", - "period__period", - "lesson__subject", - ] - indexes = [ - models.Index(fields=["lesson", "period"], name="lesson_period_lesson_period"), - models.Index(fields=["room"], include=["lesson", "period"], name="lesson_period_room"), - ] - verbose_name = _("Lesson period") - verbose_name_plural = _("Lesson periods") - - -class AbsenceReason(ExtensibleModel): - short_name = models.CharField(verbose_name=_("Short name"), max_length=255, unique=True) - name = models.CharField(verbose_name=_("Name"), blank=True, max_length=255) - - def __str__(self): - if self.name: - return f"{self.short_name} ({self.name})" - else: - return self.short_name - - class Meta: - verbose_name = _("Absence reason") - verbose_name_plural = _("Absence reasons") - - -class Absence(SchoolTermRelatedExtensibleModel): - objects = AlekSISBaseManagerWithoutMigrations.from_queryset(AbsenceQuerySet)() - - reason = models.ForeignKey( - "AbsenceReason", - on_delete=models.SET_NULL, - related_name="absences", - blank=True, - null=True, - verbose_name=_("Absence reason"), - ) - - teacher = models.ForeignKey( - "core.Person", - on_delete=models.CASCADE, - related_name="absences", - null=True, - blank=True, - verbose_name=_("Teacher"), - ) - group = models.ForeignKey( - "core.Group", - on_delete=models.CASCADE, - related_name="absences", - null=True, - blank=True, - verbose_name=_("Group"), - ) - room = models.ForeignKey( - "core.Room", - on_delete=models.CASCADE, - related_name="absences", - null=True, - blank=True, - verbose_name=_("Room"), - ) - - date_start = models.DateField(verbose_name=_("Start date"), null=True) - date_end = models.DateField(verbose_name=_("End date"), null=True) - period_from = models.ForeignKey( - "TimePeriod", - on_delete=models.CASCADE, - verbose_name=_("Start period"), - null=True, - related_name="+", - ) - period_to = models.ForeignKey( - "TimePeriod", - on_delete=models.CASCADE, - verbose_name=_("End period"), - null=True, - related_name="+", - ) - comment = models.TextField(verbose_name=_("Comment"), blank=True) - - def __str__(self): - if self.teacher: - return str(self.teacher) - elif self.group: - return str(self.group) - elif self.room: - return str(self.room) - else: - return _("Unknown absence") - - class Meta: - ordering = ["date_start"] - indexes = [models.Index(fields=["date_start", "date_end"])] - verbose_name = _("Absence") - verbose_name_plural = _("Absences") - - -class Exam(SchoolTermRelatedExtensibleModel): - lesson = models.ForeignKey( - "Lesson", - on_delete=models.CASCADE, - related_name="exams", - verbose_name=_("Lesson"), - ) - - date = models.DateField(verbose_name=_("Date of exam")) - period_from = models.ForeignKey( - "TimePeriod", - on_delete=models.CASCADE, - verbose_name=_("Start period"), - related_name="+", - ) - period_to = models.ForeignKey( - "TimePeriod", - on_delete=models.CASCADE, - verbose_name=_("End period"), - related_name="+", - ) - - title = models.CharField(verbose_name=_("Title"), max_length=255, blank=True) - comment = models.TextField(verbose_name=_("Comment"), blank=True) - - class Meta: - ordering = ["date"] - indexes = [models.Index(fields=["date"])] - verbose_name = _("Exam") - verbose_name_plural = _("Exams") - - -class Holiday(ExtensibleModel): - objects = AlekSISBaseManagerWithoutMigrations.from_queryset(HolidayQuerySet)() - - title = models.CharField(verbose_name=_("Title"), max_length=255) - date_start = models.DateField(verbose_name=_("Start date"), null=True) - date_end = models.DateField(verbose_name=_("End date"), null=True) - comments = models.TextField(verbose_name=_("Comments"), blank=True) - - def get_days(self) -> Iterator[date]: - delta = self.date_end - self.date_start - for i in range(delta.days + 1): - yield self.date_start + timedelta(days=i) - - @classmethod - def on_day(cls, day: date) -> Holiday | None: - holidays = cls.objects.on_day(day) - if holidays.exists(): - return holidays[0] - else: - return None - - @classmethod - def in_week(cls, week: CalendarWeek) -> dict[int, Holiday | None]: - per_weekday = {} - holidays = Holiday.objects.in_week(week) - - for weekday in range(TimePeriod.weekday_min, TimePeriod.weekday_max + 1): - holiday_date = week[weekday] - filtered_holidays = list( - filter( - lambda h: holiday_date >= h.date_start and holiday_date <= h.date_end, - holidays, - ) - ) - if filtered_holidays: - per_weekday[weekday] = filtered_holidays[0] - - return per_weekday - - def __str__(self): - return self.title - - class Meta: - ordering = ["date_start"] - indexes = [models.Index(fields=["date_start", "date_end"])] - verbose_name = _("Holiday") - verbose_name_plural = _("Holidays") - - -class SupervisionArea(ExtensibleModel): - short_name = models.CharField(verbose_name=_("Short name"), max_length=255, unique=True) - name = models.CharField(verbose_name=_("Long name"), max_length=255) - colour_fg = ColorField(default="#000000") - colour_bg = ColorField() - - def __str__(self): - return f"{self.name} ({self.short_name})" - - class Meta: - ordering = ["name"] - verbose_name = _("Supervision area") - verbose_name_plural = _("Supervision areas") - - -class Break(ValidityRangeRelatedExtensibleModel): - objects = BreakManager() - - short_name = models.CharField(verbose_name=_("Short name"), max_length=255) - name = models.CharField(verbose_name=_("Long name"), max_length=255) - - after_period = models.ForeignKey( - "TimePeriod", - on_delete=models.CASCADE, - verbose_name=_("Time period after break starts"), - related_name="break_after", - blank=True, - null=True, - ) - before_period = models.ForeignKey( - "TimePeriod", - on_delete=models.CASCADE, - verbose_name=_("Time period before break ends"), - related_name="break_before", - blank=True, - null=True, - ) - - @property - def weekday(self): - return self.after_period.weekday if self.after_period else self.before_period.weekday - - @property - def after_period_number(self): - return self.after_period.period if self.after_period else self.before_period.period - 1 - - @property - def before_period_number(self): - return self.before_period.period if self.before_period else self.after_period.period + 1 - - @property - def time_start(self): - return self.after_period.time_end if self.after_period else None - - @property - def time_end(self): - return self.before_period.time_start if self.before_period else None - - @classmethod - def get_breaks_dict(cls) -> dict[int, tuple[datetime, datetime]]: - breaks = {} - for break_ in cls.objects.all(): - breaks[break_.before_period_number] = break_ - - return breaks - - def __str__(self): - return f"{self.name} ({self.short_name})" - - class Meta: - ordering = ["after_period"] - indexes = [models.Index(fields=["after_period", "before_period"])] - verbose_name = _("Break") - verbose_name_plural = _("Breaks") - constraints = [ - models.UniqueConstraint( - fields=["validity", "short_name"], name="unique_short_name_per_validity_break" - ), - ] - - -class Supervision(ValidityRangeRelatedExtensibleModel, WeekAnnotationMixin): - objects = SupervisionManager.from_queryset(SupervisionQuerySet)() - - area = models.ForeignKey( - SupervisionArea, - models.CASCADE, - verbose_name=_("Supervision area"), - related_name="supervisions", - ) - break_item = models.ForeignKey( - Break, models.CASCADE, verbose_name=_("Break"), related_name="supervisions" - ) - teacher = models.ForeignKey( - "core.Person", - models.CASCADE, - related_name="supervisions", - verbose_name=_("Teacher"), - ) - - def get_year(self, week: int) -> int: - year = self.validity.date_start.year - if week < int(self.validity.date_start.strftime("%V")): - year += 1 - return year - - def get_calendar_week(self, week: int): - year = self.get_year(week) - - return CalendarWeek(year=year, week=week) - - def get_substitution(self, week: CalendarWeek | None = None) -> SupervisionSubstitution | None: - wanted_week = week or self.week or CalendarWeek() - # We iterate over all substitutions because this can make use of - # prefetching when this model is loaded from outside, in contrast - # to .filter() - for substitution in self.substitutions.all(): - for weekday in range(0, 7): - if substitution.date == wanted_week[weekday]: - return substitution - return None - - @property - def teachers(self): - return [self.teacher] - - def __str__(self): - return f"{self.break_item}, {self.area}, {self.teacher}" - - class Meta: - ordering = ["area", "break_item"] - verbose_name = _("Supervision") - verbose_name_plural = _("Supervisions") - - -class SupervisionSubstitution(ExtensibleModel): - objects = SupervisionSubstitutionManager() - - tracker = FieldTracker() - - date = models.DateField(verbose_name=_("Date")) - supervision = models.ForeignKey( - Supervision, - models.CASCADE, - verbose_name=_("Supervision"), - related_name="substitutions", - ) - teacher = models.ForeignKey( - "core.Person", - models.CASCADE, - related_name="substituted_supervisions", - verbose_name=_("Teacher"), - ) - - @property - def teachers(self): - return [self.teacher] - - @property - def time_range(self) -> (timezone.datetime, timezone.datetime): - """Get the time range of this supervision substitution.""" - return timezone.datetime.combine( - self.date, - self.supervision.break_item.time_start or self.supervision.break_item.time_end, - ), timezone.datetime.combine( - self.date, - self.supervision.break_item.time_end or self.supervision.break_item.time_start, - ) - - def __str__(self): - return f"{self.supervision}, {date_format(self.date)}" - - class Meta: - ordering = ["date", "supervision"] - verbose_name = _("Supervision substitution") - verbose_name_plural = _("Supervision substitutions") - - -class Event(SchoolTermRelatedExtensibleModel, GroupPropertiesMixin, TeacherPropertiesMixin): - label_ = "event" - - tracker = FieldTracker() - - objects = EventManager.from_queryset(EventQuerySet)() - - title = models.CharField(verbose_name=_("Title"), max_length=255, blank=True) - - date_start = models.DateField(verbose_name=_("Start date"), null=True) - date_end = models.DateField(verbose_name=_("End date"), null=True) - - period_from = models.ForeignKey( - "TimePeriod", - on_delete=models.CASCADE, - verbose_name=_("Start time period"), - related_name="+", - ) - period_to = models.ForeignKey( - "TimePeriod", - on_delete=models.CASCADE, - verbose_name=_("End time period"), - related_name="+", - ) - - groups = models.ManyToManyField("core.Group", related_name="events", verbose_name=_("Groups")) - rooms = models.ManyToManyField("core.Room", related_name="events", verbose_name=_("Rooms")) - teachers = models.ManyToManyField( - "core.Person", related_name="events", verbose_name=_("Teachers") - ) - - def __str__(self): - if self.title: - return self.title - else: - return _("Event {pk}").format(pk=self.pk) - - def get_period_min(self, day) -> int: - return ( - TimePeriod.objects.on_day(day) - .aggregate(period__min=Coalesce(Min("period"), 1)) - .get("period__min") - ) - - def get_period_max(self, day) -> int: - return ( - TimePeriod.objects.on_day(day) - .aggregate(period__max=Coalesce(Max("period"), 7)) - .get("period__max") - ) - - @property - def raw_period_from_on_day(self) -> TimePeriod: - """Get start period on the annotated day (as TimePeriod object). - - If there is no date annotated, it will use the current date. - """ - day = getattr(self, "_date", timezone.now().date()) - if day != self.date_start: - return TimePeriod.from_period(self.get_period_min(day), day) - else: - return self.period_from - - @property - def raw_period_to_on_day(self) -> TimePeriod: - """Get end period on the annotated day (as TimePeriod object). - - If there is no date annotated, it will use the current date. - """ - day = getattr(self, "_date", timezone.now().date()) - if day != self.date_end: - return TimePeriod.from_period(self.get_period_max(day), day) - else: - return self.period_to - - @property - def period_from_on_day(self) -> int: - """Get start period on the annotated day (as period number). - - If there is no date annotated, it will use the current date. - """ - return self.raw_period_from_on_day.period - - @property - def period_to_on_day(self) -> int: - """Get end period on the annotated day (as period number). - - If there is no date annotated, it will use the current date. - """ - return self.raw_period_to_on_day.period - - def get_start_weekday(self, week: CalendarWeek) -> int: - """Get start date of an event in a specific week.""" - if self.date_start < week[TimePeriod.weekday_min]: - return TimePeriod.weekday_min - else: - return self.date_start.weekday() - - def get_end_weekday(self, week: CalendarWeek) -> int: - """Get end date of an event in a specific week.""" - if self.date_end > week[TimePeriod.weekday_max]: - return TimePeriod.weekday_max - else: - return self.date_end.weekday() - - def annotate_day(self, day: date): - """Annotate event with the provided date.""" - self._date = day - - def get_groups(self) -> models.query.QuerySet: - """Get groups relation.""" - return self.groups - - def get_teachers(self) -> models.query.QuerySet: - """Get teachers relation.""" - return self.teachers - - @property - def time_range(self) -> (timezone.datetime, timezone.datetime): - """Get the time range of this event.""" - return timezone.datetime.combine( - self.date_start, self.period_from.time_start - ), timezone.datetime.combine(self.date_end, self.period_to.time_end) - - class Meta: - ordering = ["date_start"] - indexes = [ - models.Index( - fields=["date_start", "date_end"], - include=["period_from", "period_to"], - name="event_date_start_date_end", - ) - ] - verbose_name = _("Event") - verbose_name_plural = _("Events") - - -class ExtraLesson( - GroupPropertiesMixin, TeacherPropertiesMixin, WeekRelatedMixin, SchoolTermRelatedExtensibleModel -): - label_ = "extra_lesson" - - tracker = FieldTracker() - - objects = ExtraLessonManager.from_queryset(ExtraLessonQuerySet)() - - week = models.IntegerField(verbose_name=_("Week"), default=CalendarWeek.current_week) - year = models.IntegerField(verbose_name=_("Year"), default=get_current_year) - period = models.ForeignKey( - "TimePeriod", - models.CASCADE, - related_name="extra_lessons", - verbose_name=_("Time period"), - ) - - subject = models.ForeignKey( - "Subject", - on_delete=models.CASCADE, - related_name="extra_lessons", - verbose_name=_("Subject"), - ) - groups = models.ManyToManyField( - "core.Group", related_name="extra_lessons", verbose_name=_("Groups") - ) - teachers = models.ManyToManyField( - "core.Person", - related_name="extra_lessons_as_teacher", - verbose_name=_("Teachers"), - ) - room = models.ForeignKey( - "core.Room", - models.CASCADE, - null=True, - related_name="extra_lessons", - verbose_name=_("Room"), - ) - - comment = models.CharField(verbose_name=_("Comment"), blank=True, max_length=255) - - exam = models.ForeignKey( - "Exam", - on_delete=models.CASCADE, - verbose_name=_("Related exam"), - related_name="extra_lessons", - blank=True, - null=True, - ) - - def __str__(self): - return f"{self.week}, {self.period}, {self.subject}" - - def get_groups(self) -> models.query.QuerySet: - """Get groups relation.""" - return self.groups - - def get_teachers(self) -> models.query.QuerySet: - """Get teachers relation.""" - return self.teachers - - def get_subject(self) -> Subject: - """Get subject.""" - return self.subject - - @property - def time_range(self) -> (timezone.datetime, timezone.datetime): - """Get the time range of this extra lesson.""" - return timezone.datetime.combine( - self.date, self.period.time_start - ), timezone.datetime.combine(self.date, self.period.time_end) - - class Meta: - verbose_name = _("Extra lesson") - verbose_name_plural = _("Extra lessons") - indexes = [models.Index(fields=["week", "year"], name="extra_lesson_week_year")] - - class AutomaticPlan(LiveDocument): """Model for configuring automatically updated PDF substitution plans.""" @@ -1296,8 +149,7 @@ class ChronosGlobalPermissions(GlobalPermissionModel): ("view_all_person_timetables", _("Can view all person timetables")), ("view_all_course_timetables", _("Can view all course timetables")), ("view_timetable_overview", _("Can view timetable overview")), - ("view_lessons_day", _("Can view all lessons per day")), - ("view_supervisions_day", _("Can view all supervisions per day")), + ("view_substitutions", _("Can view substitutions table")), ) @@ -1556,23 +408,22 @@ class LessonEvent(CalendarEvent): params: dict[str, any] | None = None, no_effect: bool = False, **kwargs, - ) -> Iterable: + ) -> QuerySet: """Return all objects that should be included in the calendar.""" if no_effect: return super().get_objects(request, params, **kwargs) - objs = ( - super() - .get_objects(request, params, **kwargs) - .not_instance_of(SupervisionEvent) - .select_related("subject", "course") - .prefetch_related("groups", "teachers", "rooms", "groups__members") - ) if request and not has_person(request.user): - raise PermissionDenied() + return cls.objects.none() + + q = Q() if params: - obj_id = int(params.get("id", 0)) + try: + obj_id = int(params.get("id", 0)) + except ValueError: + obj_id = None + type_ = params.get("type", None) not_amended = params.get("not_amended", False) not_amending = params.get("not_amending", False) @@ -1580,19 +431,19 @@ class LessonEvent(CalendarEvent): own = params.get("own", False) if not_amended: - objs = objs.not_amended() + q = q & LessonEventQuerySet.not_amended_q() if not_amending: - objs = objs.not_amending() + q = q & LessonEventQuerySet.not_amending_q() if amending: - objs = objs.amending() + q = q & LessonEventQuerySet.amending_q() if request and "own" in params: if own: - objs = objs.for_person(request.user.person) + q = q & LessonEventQuerySet.for_person_q(request.user.person) else: - objs = objs.related_to_person(request.user.person) + q = q & LessonEventQuerySet.related_to_person_q(request.user.person) if type_ and obj_id: if request and not ( @@ -1629,20 +480,28 @@ class LessonEvent(CalendarEvent): if not request.user.has_perm("chronos.view_timetable_rule", obj): raise PermissionDenied() if type_ == "TEACHER": - return objs.for_teacher(obj_id) + q = q & LessonEventQuerySet.for_teacher_q(obj_id) elif type_ == "PARTICIPANT": - return objs.for_participant(obj_id) + q = q & LessonEventQuerySet.for_participant_q(obj_id) elif type_ == "GROUP": - return objs.for_group(obj_id) + q = q & LessonEventQuerySet.for_group_q(obj_id) elif type_ == "ROOM": - return objs.for_room(obj_id) + q = q & LessonEventQuerySet.for_room_q(obj_id) elif type_ == "COURSE": - return objs.for_course(obj_id) - - if "own" in params: - return objs - if request: - return objs.for_person(request.user.person) + q = q & LessonEventQuerySet.for_course_q(obj_id) + + elif request: + q = q & LessonEventQuerySet.for_person_q(request.user.person) + + objs = super().get_objects( + request, + params, + start_qs=cls.objects.not_instance_of(SupervisionEvent), + additional_filter=q, + select_related=["subject", "course"], + prefetch_related=["groups", "teachers", "rooms", "groups__members"], + **kwargs, + ) return objs class Meta: @@ -1681,7 +540,7 @@ class SupervisionEvent(LessonEvent): cls, request: HttpRequest | None = None, params: dict[str, any] | None = None, **kwargs ) -> Iterable: """Return all objects that should be included in the calendar.""" - objs = super().get_objects(request, params, no_effect=True, **kwargs) + q = Q() if params: obj_id = int(params.get("id", 0)) type_ = params.get("type", None) @@ -1690,21 +549,30 @@ class SupervisionEvent(LessonEvent): amending = params.get("amending", False) if not_amended: - objs = objs.not_amended() + q = q & SupervisionEventQuerySet.not_amended_q() if not_amending: - objs = objs.not_amending() + q = q & SupervisionEventQuerySet.not_amending_q() if amending: - objs = objs.amending() + q = q & SupervisionEventQuerySet.amending_q() if type_ and obj_id: if type_ == "TEACHER": - return objs.for_teacher(obj_id) + q = q & SupervisionEventQuerySet.for_teacher_q(obj_id) elif type_ == "GROUP": - return objs.for_group(obj_id) + q = q & SupervisionEventQuerySet.for_group_q(obj_id) elif type_ == "ROOM": - return objs.for_room(obj_id) - if request: - return objs.for_person(request.user.person) - return objs + q = q & SupervisionEventQuerySet.for_room_q(obj_id) + elif request: + q = q & SupervisionEventQuerySet.for_person_q(request.user.person) + + return super().get_objects( + request, + params, + no_effect=True, + additional_filter=q, + select_related=["subject"], + prefetch_related=["teachers", "rooms"], + **kwargs, + ) diff --git a/aleksis/apps/chronos/preferences.py b/aleksis/apps/chronos/preferences.py index b3dc8362091693b313bda2d5f9493d8afa7e7701..a2bd26ef9eac55d0ddca38091619b3d008279803 100644 --- a/aleksis/apps/chronos/preferences.py +++ b/aleksis/apps/chronos/preferences.py @@ -16,7 +16,7 @@ from dynamic_preferences.types import ( ) from aleksis.core.models import GroupType -from aleksis.core.registries import person_preferences_registry, site_preferences_registry +from aleksis.core.registries import site_preferences_registry chronos = Section("chronos", verbose_name=_("Timetables")) @@ -34,27 +34,6 @@ class UseParentGroups(BooleanPreference): ) -@person_preferences_registry.register -class ShortenGroups(BooleanPreference): - section = chronos - name = "shorten_groups" - default = True - verbose_name = _("Shorten groups in timetable views") - help_text = _("If there are more groups than the set limit, they will be collapsed.") - - -@site_preferences_registry.register -class ShortenGroupsLimit(IntegerPreference): - section = chronos - name = "shorten_groups_limit" - default = 4 - verbose_name = _("Limit of groups for shortening of groups") - help_text = _( - "If a user activates shortening of groups," - "they will be collapsed if there are more groups than this limit." - ) - - @site_preferences_registry.register class SubstitutionsRelevantDays(MultipleChoicePreference): """Relevant days which have substitution plans.""" @@ -110,44 +89,6 @@ class AffectedGroupsUseParentGroups(BooleanPreference): ) -@site_preferences_registry.register -class DaysInAdvanceNotifications(IntegerPreference): - section = chronos - name = "days_in_advance_notifications" - default = 1 - verbose_name = _("How many days in advance users should be notified about timetable changes?") - - -@site_preferences_registry.register -class TimeForSendingNotifications(TimePreference): - section = chronos - name = "time_for_sending_notifications" - default = time(17, 00) - verbose_name = _("Time for sending notifications about timetable changes") - required = True - help_text = _( - "This is only used for scheduling notifications " - "which doesn't affect the time period configured above. " - "All other notifications affecting the next days are sent immediately." - ) - - -@site_preferences_registry.register -class SendNotifications(BooleanPreference): - section = chronos - name = "send_notifications_site" - default = True - verbose_name = _("Send notifications for current timetable changes") - - -@person_preferences_registry.register -class SendNotificationsPerson(BooleanPreference): - section = chronos - name = "send_notifications" - default = True - verbose_name = _("Send notifications for current timetable changes") - - @site_preferences_registry.register class GroupTypesTimetables(ModelMultipleChoicePreference): section = chronos diff --git a/aleksis/apps/chronos/rules.py b/aleksis/apps/chronos/rules.py index 34d58360ce18353231f56acccf1a00818f479fbb..c7d0065c6bc2c6180a0fa81188484472e6ed8af7 100644 --- a/aleksis/apps/chronos/rules.py +++ b/aleksis/apps/chronos/rules.py @@ -6,7 +6,7 @@ from aleksis.core.util.predicates import ( has_person, ) -from .util.predicates import has_any_timetable_object, has_room_timetable_perm, has_timetable_perm +from .util.predicates import has_any_timetable_object, has_timetable_perm # View timetable overview view_timetable_overview_predicate = has_person & ( @@ -14,51 +14,27 @@ view_timetable_overview_predicate = has_person & ( ) add_perm("chronos.view_timetable_overview_rule", view_timetable_overview_predicate) -# View my timetable -add_perm("chronos.view_my_timetable_rule", has_person) - # View timetable view_timetable_predicate = has_person & has_timetable_perm add_perm("chronos.view_timetable_rule", view_timetable_predicate) -# View all lessons per day -view_lessons_day_predicate = has_person & has_global_perm("chronos.view_lessons_day") -add_perm("chronos.view_lessons_day_rule", view_lessons_day_predicate) # Edit substition edit_substitution_predicate = has_person & ( - has_global_perm("chronos.change_lessonsubstitution") - | has_object_perm("chronos.change_lessonsubstitution") + has_global_perm("chronos.change_lessonevent") | has_object_perm("chronos.change_lessonevent") ) add_perm("chronos.edit_substitution_rule", edit_substitution_predicate) # Delete substitution delete_substitution_predicate = has_person & ( - has_global_perm("chronos.delete_lessonsubstitution") - | has_object_perm("chronos.delete_lessonsubstitution") + has_global_perm("chronos.delete_lessonevent") | has_object_perm("chronos.delete_lessonevent") ) add_perm("chronos.delete_substitution_rule", delete_substitution_predicate) # View substitutions -view_substitutions_predicate = has_person & (has_global_perm("chronos.view_lessonsubstitution")) +view_substitutions_predicate = has_person & (has_global_perm("chronos.view_substitutions")) add_perm("chronos.view_substitutions_rule", view_substitutions_predicate) -# View all supervisions per day -view_supervisions_day_predicate = has_person & has_global_perm("chronos.view_supervisions_day") -add_perm("chronos.view_supervisions_day_rule", view_supervisions_day_predicate) - -# Edit supervision substitution -edit_supervision_substitution_predicate = has_person & ( - has_global_perm("chronos.change_supervisionsubstitution") -) -add_perm("chronos.edit_supervision_substitution_rule", edit_supervision_substitution_predicate) - -# Delete supervision substitution -delete_supervision_substitution_predicate = has_person & ( - has_global_perm("chronos.delete_supervisionsubstitution") -) -add_perm("chronos.delete_supervision_substitution_rule", delete_supervision_substitution_predicate) - -# View room (timetable) -view_room_predicate = has_person & has_room_timetable_perm -add_perm("chronos.view_room_rule", view_room_predicate) +# View parent menu entry +view_menu_predicate = has_person & (view_timetable_overview_predicate) +add_perm("chronos.view_menu_rule", view_menu_predicate) diff --git a/aleksis/apps/chronos/schema/__init__.py b/aleksis/apps/chronos/schema/__init__.py index d9194e6718112025b4e41713cd00ff5d043bebf6..d23eb0c7c8bf056b66f3b7c074f2a73e6b6f1182 100644 --- a/aleksis/apps/chronos/schema/__init__.py +++ b/aleksis/apps/chronos/schema/__init__.py @@ -7,9 +7,13 @@ from aleksis.core.schema.base import ( BaseBatchDeleteMutation, BaseBatchPatchMutation, ) +from aleksis.core.schema.group import GroupType +from aleksis.core.schema.person import PersonType +from aleksis.core.schema.room import RoomType from ..models import LessonEvent -from ..util.chronos_helpers import get_groups, get_rooms, get_teachers +from ..util.build import build_substitutions_list +from ..util.chronos_helpers import get_groups, get_next_relevant_day, get_rooms, get_teachers class TimetablePersonType(DjangoObjectType): @@ -122,11 +126,114 @@ class TimetableObjectType(graphene.ObjectType): return f"{root.type.value}-{root.id}" +class SubstitutionType(graphene.ObjectType): + """This type contains the logic also contained in the pdf templates.""" + + old_groups = graphene.List(GroupType) + new_groups = graphene.List(GroupType) + start_slot = graphene.Int() + end_slot = graphene.Int() + start_time = graphene.DateTime() + end_time = graphene.DateTime() + old_teachers = graphene.List(PersonType) + new_teachers = graphene.List(PersonType) + old_subject = graphene.String() + new_subject = graphene.String() + old_rooms = graphene.List(RoomType) + new_rooms = graphene.List(RoomType) + cancelled = graphene.Boolean() + notes = graphene.String() + + # TODO: Extract old/new-pattern into own method and reuse? + + def resolve_old_groups(root, info): + le = root["REFERENCE_OBJECT"] + return le.amends.groups.all() or le.groups.all() + + def resolve_new_groups(root, info): + le = root["REFERENCE_OBJECT"] + if le.groups.all() and le.amends.groups.all(): + return le.groups.all() + else: + return [] + + def resolve_start_slot(root, info): + return root["REFERENCE_OBJECT"].slot_number_start + + def resolve_end_slot(root, info): + return root["REFERENCE_OBJECT"].slot_number_end + + def resolve_start_time(root, info): + return root["DTSTART"].dt + + def resolve_end_time(root, info): + return root["DTEND"].dt + + def resolve_old_teachers(root, info): + le = root["REFERENCE_OBJECT"] + return le.amends.teachers.all() or le.teachers.all() + + def resolve_new_teachers(root, info): + le = root["REFERENCE_OBJECT"] + if le.teachers.all() and le.amends.teachers.all(): + return le.teachers.all() + else: + return [] + + def resolve_old_subject(root, info): + le = root["REFERENCE_OBJECT"] + if le.name == "supervision": + return "SUPERVISION" + elif not le.amends.subject and not le.subject: + return le.amends.title + else: + subject = le.amends.subject or le.subject + return subject.short_name or subject.name + + def resolve_new_subject(root, info): + le = root["REFERENCE_OBJECT"] + if le.name == "supervision": + return None + elif not le.amends.subject and not le.subject: + return le.title + elif le.subject and le.amends.subject: + return le.subject.short_name or le.subject.name + else: + return None + + def resolve_old_rooms(root, info): + le = root["REFERENCE_OBJECT"] + return le.amends.rooms.all() or le.rooms.all() + + def resolve_new_rooms(root, info): + le = root["REFERENCE_OBJECT"] + if le.rooms.all() and le.amends.rooms.all(): + return le.rooms.all() + else: + return [] + + def resolve_cancelled(root, info): + return root["REFERENCE_OBJECT"].cancelled + + def resolve_notes(root, info): + return root["REFERENCE_OBJECT"].title or root["REFERENCE_OBJECT"].comment + + +class SubstitutionsForDateType(graphene.ObjectType): + affected_teachers = graphene.List(PersonType) + affected_groups = graphene.List(GroupType) + substitutions = graphene.List(SubstitutionType) + + class Query(graphene.ObjectType): timetable_teachers = graphene.List(TimetablePersonType) timetable_groups = graphene.List(TimetableGroupType) timetable_rooms = graphene.List(TimetableRoomType) available_timetables = graphene.List(TimetableObjectType) + substitutions_for_date = graphene.Field( + SubstitutionsForDateType, + date=graphene.Date(), + ) def resolve_timetable_teachers(self, info, **kwargs): return get_teachers(info.context.user) @@ -168,6 +275,16 @@ class Query(graphene.ObjectType): return all_timetables + def resolve_substitutions_for_date(root, info, date): + substitutions, affected_teachers, affected_groups = build_substitutions_list( + get_next_relevant_day(date) + ) + return SubstitutionsForDateType( + affected_teachers=affected_teachers, + affected_groups=affected_groups, + substitutions=[sub["el"] for sub in substitutions], + ) + class Mutation(graphene.ObjectType): create_amend_lessons = AmendLessonBatchCreateMutation.Field() diff --git a/aleksis/apps/chronos/static/css/chronos/timetable_print.css b/aleksis/apps/chronos/static/css/chronos/timetable_print.css deleted file mode 100644 index 343864ae919cb68ab0bb4571d1736fd87aa5a573..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/static/css/chronos/timetable_print.css +++ /dev/null @@ -1,34 +0,0 @@ -.timetable-plan .row, -.timetable-plan .col { - display: flex; - padding: 0rem; -} - -.timetable-plan .row { - margin-bottom: 0rem; -} - -.lesson-card, -.timetable-title-card { - margin: 0; - display: flex; - flex-grow: 1; - min-height: 40px; - box-shadow: none; - border: 1px solid black; - margin-right: -1px; - margin-top: -1px; - border-radius: 0px; - font-size: 11px; -} -.lesson-card .card-content > div { - padding: 1px; -} - -.card .card-title { - font-size: 18px; -} - -.timetable-title-card .card-content { - padding: 7px; -} diff --git a/aleksis/apps/chronos/static/js/chronos/date_select.js b/aleksis/apps/chronos/static/js/chronos/date_select.js deleted file mode 100644 index 04a0cf0e6623c772c007ca8f4a85e41038f0c48c..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/static/js/chronos/date_select.js +++ /dev/null @@ -1,21 +0,0 @@ -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() { - activeDate = M.Datepicker.getInstance($("#date")).date; - 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 deleted file mode 100644 index 1854b17d284b4bcb8953a97a3e049d51821d3368..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/static/js/chronos/week_select.js +++ /dev/null @@ -1,21 +0,0 @@ -var data = getJSONScript("week_select"); - -function goToCalendarWeek(cw, year) { - window.location.href = data.dest.replace("year", year).replace("cw", 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/templatetags/__init__.py b/aleksis/apps/chronos/templatetags/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/aleksis/apps/chronos/templatetags/common.py b/aleksis/apps/chronos/templatetags/common.py deleted file mode 100644 index f3b4ca74d952a6f9fda5034360aa517f2fea475c..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templatetags/common.py +++ /dev/null @@ -1,33 +0,0 @@ -from django import template - -register = template.Library() - - -class SetVarNode(template.Node): - def __init__(self, var_name, var_value): - self.var_name = var_name - self.var_value = var_value - - def render(self, context): - try: - value = template.Variable(self.var_value).resolve(context) - except template.VariableDoesNotExist: - value = "" - context[self.var_name] = value - - return "" - - -@register.tag(name="set") -def set_var(parser, token): - """Set var. - - {% set some_var = '123' %} - """ - parts = token.split_contents() - if len(parts) < 4: - raise template.TemplateSyntaxError( - "'set' tag must be of the form: {% set <var_name> = <var_value> %}" - ) - - return SetVarNode(parts[1], parts[3]) diff --git a/aleksis/apps/chronos/templatetags/week_helpers.py b/aleksis/apps/chronos/templatetags/week_helpers.py deleted file mode 100644 index cdba9b3f014913a2525b3ca59bfc514dc0e98ef1..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templatetags/week_helpers.py +++ /dev/null @@ -1,55 +0,0 @@ -from datetime import date, datetime -from typing import Optional, Union - -from django import template -from django.db.models.query import QuerySet - -from aleksis.apps.chronos.util.date import CalendarWeek, week_period_to_date, week_weekday_to_date - -register = template.Library() - - -@register.filter -def week_start(week: CalendarWeek) -> date: - return week[0] - - -@register.filter -def week_end(week: CalendarWeek) -> date: - return week[-1] - - -@register.filter -def only_week(qs: QuerySet, week: Optional[CalendarWeek]) -> QuerySet: - wanted_week = week or CalendarWeek() - return qs.filter(week=wanted_week.week, year=wanted_week.year) - - -@register.simple_tag -def weekday_to_date(week: CalendarWeek, weekday: int) -> date: - return week_weekday_to_date(week, weekday) - - -@register.simple_tag -def period_to_date(week: CalendarWeek, period) -> date: - return week_period_to_date(week, period) - - -@register.simple_tag -def period_to_time_start(date_ref: Union[CalendarWeek, int, date], period) -> date: - return period.get_datetime_start(date_ref) - - -@register.simple_tag -def period_to_time_end(date_ref: Union[CalendarWeek, int, date], period) -> date: - return period.get_datetime_end(date_ref) - - -@register.simple_tag -def today() -> date: - return date.today() - - -@register.simple_tag -def now_datetime() -> datetime: - return datetime.now() diff --git a/aleksis/apps/chronos/util/build.py b/aleksis/apps/chronos/util/build.py index dd24a0452b93b3e34785e71f5ccdd5b58b908baa..66168ce2496758b6525c98fb31afbd5c686a84b5 100644 --- a/aleksis/apps/chronos/util/build.py +++ b/aleksis/apps/chronos/util/build.py @@ -1,385 +1,7 @@ -from collections import OrderedDict from datetime import date, datetime, time -from typing import Union -from django.apps import apps - -from calendarweek import CalendarWeek - -from aleksis.apps.chronos.managers import TimetableType -from aleksis.apps.chronos.models import SupervisionEvent -from aleksis.core.models import Group, Person, Room - -LessonPeriod = apps.get_model("chronos", "LessonPeriod") -LessonEvent = apps.get_model("chronos", "LessonEvent") -TimePeriod = apps.get_model("chronos", "TimePeriod") -Break = apps.get_model("chronos", "Break") -Supervision = apps.get_model("chronos", "Supervision") -LessonSubstitution = apps.get_model("chronos", "LessonSubstitution") -SupervisionSubstitution = apps.get_model("chronos", "SupervisionSubstitution") -Event = apps.get_model("chronos", "Event") -Holiday = apps.get_model("chronos", "Holiday") -ExtraLesson = apps.get_model("chronos", "ExtraLesson") - - -def build_timetable( - type_: Union[TimetableType, str], - obj: Union[Group, Room, Person], - date_ref: Union[CalendarWeek, date], - with_holidays: bool = True, -): - needed_breaks = [] - - is_person = False - if type_ == "person": - is_person = True - type_ = obj.timetable_type - - is_week = False - if isinstance(date_ref, CalendarWeek): - is_week = True - - if type_ is None: - return None - - # Get matching holidays - if is_week: - holidays_per_weekday = Holiday.in_week(date_ref) if with_holidays else {} - else: - holiday = Holiday.on_day(date_ref) if with_holidays else None - - # Get matching lesson periods - lesson_periods = LessonPeriod.objects - lesson_periods = ( - lesson_periods.select_related(None) - .select_related("lesson", "lesson__subject", "period", "room") - .only( - "lesson", - "period", - "room", - "lesson__subject", - "period__weekday", - "period__period", - "lesson__subject__short_name", - "lesson__subject__name", - "lesson__subject__colour_fg", - "lesson__subject__colour_bg", - "room__short_name", - "room__name", - ) - ) - - if is_week: - lesson_periods = lesson_periods.in_week(date_ref) - else: - lesson_periods = lesson_periods.on_day(date_ref) - - if is_person: - lesson_periods = lesson_periods.filter_from_person(obj) - else: - lesson_periods = lesson_periods.filter_from_type(type_, obj, is_smart=with_holidays) - - # Sort lesson periods in a dict - lesson_periods_per_period = lesson_periods.group_by_periods(is_week=is_week) - - # Get events - extra_lessons = ExtraLesson.objects - if is_week: - extra_lessons = extra_lessons.filter(week=date_ref.week, year=date_ref.year) - else: - extra_lessons = extra_lessons.on_day(date_ref) - if is_person: - extra_lessons = extra_lessons.filter_from_person(obj) - else: - extra_lessons = extra_lessons.filter_from_type(type_, obj) - - extra_lessons = extra_lessons.only( - "week", - "year", - "period", - "subject", - "room", - "comment", - "period__weekday", - "period__period", - "subject__short_name", - "subject__name", - "subject__colour_fg", - "subject__colour_bg", - "room__short_name", - "room__name", - ) - - # Sort lesson periods in a dict - extra_lessons_per_period = extra_lessons.group_by_periods(is_week=is_week) - - # Get events - events = Event.objects - events = events.in_week(date_ref) if is_week else events.on_day(date_ref) - - events = events.only( - "id", - "title", - "date_start", - "date_end", - "period_from", - "period_to", - "period_from__weekday", - "period_from__period", - "period_to__weekday", - "period_to__period", - ) - - if is_person: - events_to_display = events.filter_from_person(obj) - else: - events_to_display = events.filter_from_type(type_, obj) - - # Sort events in a dict - events_per_period = {} - events_for_replacement_per_period = {} - for event in events: - if is_week and event.date_start < date_ref[TimePeriod.weekday_min]: - # If start date not in current week, set weekday and period to min - weekday_from = TimePeriod.weekday_min - period_from_first_weekday = TimePeriod.period_min - else: - weekday_from = event.date_start.weekday() - period_from_first_weekday = event.period_from.period - - if is_week and event.date_end > date_ref[TimePeriod.weekday_max]: - # If end date not in current week, set weekday and period to max - weekday_to = TimePeriod.weekday_max - period_to_last_weekday = TimePeriod.period_max - else: - weekday_to = event.date_end.weekday() - period_to_last_weekday = event.period_to.period - - for weekday in range(weekday_from, weekday_to + 1): - if not is_week and weekday != date_ref.weekday(): - # If daily timetable for person, skip other weekdays - continue - - # If start day, use start period else use min period - period_from = ( - period_from_first_weekday if weekday == weekday_from else TimePeriod.period_min - ) - - # If end day, use end period else use max period - period_to = period_to_last_weekday if weekday == weekday_to else TimePeriod.periox_max - - for period in range(period_from, period_to + 1): - # The following events are possibly replacing some lesson periods - if period not in events_for_replacement_per_period: - events_for_replacement_per_period[period] = {} if is_week else [] - - if is_week and weekday not in events_for_replacement_per_period[period]: - events_for_replacement_per_period[period][weekday] = [] - - if not is_week: - events_for_replacement_per_period[period].append(event) - else: - events_for_replacement_per_period[period][weekday].append(event) - - # and the following will be displayed in the timetable - if event in events_to_display: - if period not in events_per_period: - events_per_period[period] = {} if is_week else [] - - if is_week and weekday not in events_per_period[period]: - events_per_period[period][weekday] = [] - - if not is_week: - events_per_period[period].append(event) - else: - events_per_period[period][weekday].append(event) - - if type_ == TimetableType.TEACHER: - # Get matching supervisions - week = CalendarWeek.from_date(date_ref) if not is_week else date_ref - supervisions = ( - Supervision.objects.in_week(week) - .all() - .annotate_week(week) - .filter_by_teacher(obj) - .only( - "area", - "break_item", - "teacher", - "area", - "area__short_name", - "area__name", - "area__colour_fg", - "area__colour_bg", - "break_item__short_name", - "break_item__name", - "break_item__after_period__period", - "break_item__after_period__weekday", - "break_item__before_period__period", - "break_item__before_period__weekday", - "teacher__short_name", - "teacher__first_name", - "teacher__last_name", - ) - ) - - if not is_week: - supervisions = supervisions.filter_by_weekday(date_ref.weekday()) - - supervisions_per_period_after = {} - for supervision in supervisions: - weekday = supervision.break_item.weekday - period_after_break = supervision.break_item.before_period_number - - if period_after_break not in needed_breaks: - needed_breaks.append(period_after_break) - - if is_week and period_after_break not in supervisions_per_period_after: - supervisions_per_period_after[period_after_break] = {} - - if not is_week: - supervisions_per_period_after[period_after_break] = supervision - else: - supervisions_per_period_after[period_after_break][weekday] = supervision - - # Get ordered breaks - breaks = OrderedDict(sorted(Break.get_breaks_dict().items())) - - rows = [] - for period, break_ in breaks.items(): # period is period after break - # Break - if type_ == TimetableType.TEACHER and period in needed_breaks: - row = { - "type": "break", - "after_period": break_.after_period_number, - "before_period": break_.before_period_number, - "time_start": break_.time_start, - "time_end": break_.time_end, - } - - if is_week: - cols = [] - - for weekday in range(TimePeriod.weekday_min, TimePeriod.weekday_max + 1): - col = None - if ( - period in supervisions_per_period_after - and weekday not in holidays_per_weekday - ) and weekday in supervisions_per_period_after[period]: - col = supervisions_per_period_after[period][weekday] - cols.append(col) - - row["cols"] = cols - else: - col = None - if period in supervisions_per_period_after and not holiday: - col = supervisions_per_period_after[period] - row["col"] = col - rows.append(row) - - # Period - if period <= TimePeriod.period_max: - row = { - "type": "period", - "period": period, - "time_start": break_.before_period.time_start, - "time_end": break_.before_period.time_end, - } - - if is_week: - cols = [] - for weekday in range(TimePeriod.weekday_min, TimePeriod.weekday_max + 1): - # Skip this period if there are holidays - if weekday in holidays_per_weekday: - cols.append([]) - continue - - col = [] - - events_for_this_period = ( - events_per_period[period].get(weekday, []) - if period in events_per_period - else [] - ) - events_for_replacement_for_this_period = ( - events_for_replacement_per_period[period].get(weekday, []) - if period in events_for_replacement_per_period - else [] - ) - lesson_periods_for_this_period = ( - lesson_periods_per_period[period].get(weekday, []) - if period in lesson_periods_per_period - else [] - ) - - # Add lesson periods - if lesson_periods_for_this_period: - if events_for_replacement_for_this_period: - # If there is a event in this period, - # we have to check whether the actual lesson is taking place. - - for lesson_period in lesson_periods_for_this_period: - replaced_by_event = lesson_period.is_replaced_by_event( - events_for_replacement_for_this_period, - [obj] if type_ == TimetableType.GROUP else None, - ) - lesson_period.replaced_by_event = replaced_by_event - if not replaced_by_event or ( - replaced_by_event and type_ != TimetableType.GROUP - ): - col.append(lesson_period) - - else: - col += lesson_periods_for_this_period - - # Add extra lessons - if period in extra_lessons_per_period: - col += extra_lessons_per_period[period].get(weekday, []) - - # Add events - col += events_for_this_period - - cols.append(col) - - row["cols"] = cols - else: - col = [] - - # Skip this period if there are holidays - if holiday: - continue - - events_for_this_period = events_per_period.get(period, []) - events_for_replacement_for_this_period = events_for_replacement_per_period.get( - period, [] - ) - lesson_periods_for_this_period = lesson_periods_per_period.get(period, []) - - # Add lesson periods - if lesson_periods_for_this_period: - if events_for_replacement_for_this_period: - # If there is a event in this period, - # we have to check whether the actual lesson is taking place. - - lesson_periods_to_keep = [] - for lesson_period in lesson_periods_for_this_period: - if not lesson_period.is_replaced_by_event( - events_for_replacement_for_this_period - ): - lesson_periods_to_keep.append(lesson_period) - col += lesson_periods_to_keep - else: - col += lesson_periods_for_this_period - - # Add events and extra lessons - col += extra_lessons_per_period.get(period, []) - col += events_for_this_period - - row["col"] = col - - rows.append(row) - - return rows +from aleksis.apps.chronos.models import LessonEvent, SupervisionEvent +from aleksis.core.models import Group, Person def build_substitutions_list(wanted_day: date) -> tuple[list[dict], set[Person], set[Group]]: @@ -433,23 +55,3 @@ def build_substitutions_list(wanted_day: date) -> tuple[list[dict], set[Person], rows.sort(key=lambda row: row["sort_a"] + row["sort_b"]) return rows, affected_teachers, affected_groups - - -def build_weekdays( - base: list[tuple[int, str]], wanted_week: CalendarWeek, with_holidays: bool = True -) -> list[dict]: - if with_holidays: - holidays_per_weekday = Holiday.in_week(wanted_week) - - weekdays = [] - for key, name in base[TimePeriod.weekday_min : TimePeriod.weekday_max + 1]: - weekday = { - "key": key, - "name": name, - "date": wanted_week[key], - } - if with_holidays: - weekday["holiday"] = holidays_per_weekday[key] if key in holidays_per_weekday else None - weekdays.append(weekday) - - return weekdays diff --git a/aleksis/apps/chronos/util/chronos_helpers.py b/aleksis/apps/chronos/util/chronos_helpers.py index f76fa4663585a5be61570d7afffbdc5264ee13be..7f3ab9a5ddf8e3d3ed3b5d58f6185d17b12bb930 100644 --- a/aleksis/apps/chronos/util/chronos_helpers.py +++ b/aleksis/apps/chronos/util/chronos_helpers.py @@ -2,24 +2,17 @@ from datetime import date, datetime, timedelta from typing import TYPE_CHECKING, Optional from django.db.models import Count, Q -from django.http import HttpRequest, HttpResponseNotFound +from django.http import HttpRequest from django.shortcuts import get_object_or_404 -from guardian.core import ObjectPermissionChecker +from guardian.shortcuts import get_objects_for_user from aleksis.apps.cursus.models import Course from aleksis.core.models import Announcement, Group, Person, Room from aleksis.core.util.core_helpers import get_site_preferences -from aleksis.core.util.predicates import check_global_permission -from ..managers import TimetableType -from ..models import ( - LessonPeriod, - LessonSubstitution, - Supervision, - SupervisionSubstitution, -) from .build import build_substitutions_list +from ..managers import TimetableType if TYPE_CHECKING: from django.contrib.auth import get_user_model @@ -50,41 +43,20 @@ def get_el_by_pk( return HttpResponseNotFound() -def get_substitution_by_id(request: HttpRequest, id_: int, week: int): - lesson_period = get_object_or_404(LessonPeriod, pk=id_) - wanted_week = lesson_period.lesson.get_calendar_week(week) - - return LessonSubstitution.objects.filter( - week=wanted_week.week, year=wanted_week.year, lesson_period=lesson_period - ).first() - - -def get_supervision_substitution_by_id(request: HttpRequest, id_: int, date: datetime.date): - supervision = get_object_or_404(Supervision, pk=id_) - - return SupervisionSubstitution.objects.filter(date=date, supervision=supervision).first() - - def get_teachers(user: "User"): """Get the teachers whose timetables are allowed to be seen by current user.""" - checker = ObjectPermissionChecker(user) teachers = ( - Person.objects.annotate(lessons_count=Count("lesson_events_as_teacher")) - .filter(lessons_count__gt=0) + Person.objects.annotate(course_count=Count("courses_as_teacher")) + .filter(course_count__gt=0) .order_by("short_name", "last_name") ) - if not check_global_permission(user, "chronos.view_all_person_timetables"): - checker.prefetch_perms(teachers) - - wanted_teachers = set() - - for teacher in teachers: - if checker.has_perm("core.view_person_timetable", teacher): - wanted_teachers.add(teacher.pk) - - teachers = teachers.filter(Q(pk=user.person.pk) | Q(pk__in=wanted_teachers)) + if not user.has_perm("chronos.view_all_person_timetables"): + teachers.filter( + Q(pk=user.person.pk) + | Q(pk__in=get_objects_for_user(user, "core.view_person_timetable", teachers)) + ) teachers = teachers.distinct() @@ -93,16 +65,8 @@ def get_teachers(user: "User"): def get_groups(user: "User"): """Get the groups whose timetables are allowed to be seen by current user.""" - checker = ObjectPermissionChecker(user) - groups = ( - Group.objects.for_current_school_term_or_all() - .annotate( - lessons_count=Count("lesson_events"), - child_lessons_count=Count("child_groups__lesson_events"), - ) - .filter(Q(lessons_count__gt=0) | Q(child_lessons_count__gt=0)) - ) + groups = Group.objects.for_current_school_term_or_all() group_types = get_site_preferences()["chronos__group_types_timetables"] @@ -111,20 +75,15 @@ def get_groups(user: "User"): groups = groups.order_by("short_name", "name") - if not check_global_permission(user, "chronos.view_all_group_timetables"): - checker.prefetch_perms(groups) - - wanted_classes = set() - - for _class in groups: - if checker.has_perm("core.view_group_timetable", _class): - wanted_classes.add(_class.pk) + if not user.has_perm("chronos.view_all_group_timetables"): + wanted_groups = get_objects_for_user(user, "core.view_group_timetable", groups) groups = groups.filter( - Q(pk__in=wanted_classes) | Q(members=user.person) | Q(owners=user.person) + Q(pk__in=wanted_groups) + | Q(members=user.person) + | Q(owners=user.person) + | Q(pk=user.person.primary_group.pk if user.person.primary_group else None) ) - if user.person.primary_group: - groups = groups.filter(Q(pk=user.person.primary_group.pk)) groups = groups.distinct() @@ -133,24 +92,11 @@ def get_groups(user: "User"): def get_rooms(user: "User"): """Get the rooms whose timetables are allowed to be seen by current user.""" - checker = ObjectPermissionChecker(user) - - rooms = ( - Room.objects.annotate(lessons_count=Count("lesson_events")) - .filter(lessons_count__gt=0) - .order_by("short_name", "name") - ) - - if not check_global_permission(user, "chronos.view_all_room_timetables"): - checker.prefetch_perms(rooms) - - wanted_rooms = set() - for room in rooms: - if checker.has_perm("core.view_room_timetable", room): - wanted_rooms.add(room.pk) + rooms = Room.objects.all().order_by("short_name", "name") - rooms = rooms.filter(Q(pk__in=wanted_rooms)) + if not user.has_perm("chronos.view_all_room_timetables"): + rooms = get_objects_for_user(user, "core.view_room_timetable", rooms) rooms = rooms.distinct() diff --git a/aleksis/apps/chronos/util/date.py b/aleksis/apps/chronos/util/date.py deleted file mode 100644 index 9a2eb10592509b12f4e915a5e115b96d95ce939a..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/util/date.py +++ /dev/null @@ -1,39 +0,0 @@ -from datetime import date - -from django.utils import timezone - -from calendarweek import CalendarWeek - - -def week_weekday_from_date(when: date) -> tuple[CalendarWeek, int]: - """Return a tuple of week and weekday from a given date.""" - return (CalendarWeek.from_date(when), when.weekday()) - - -def week_weekday_to_date(week: CalendarWeek, weekday: int) -> date: - """Return a date object for one day in a calendar week.""" - return week[weekday] - - -def week_period_to_date(week: CalendarWeek, period) -> date: - """Return the date of a lesson period in a given week.""" - return period.get_date(week) - - -def get_weeks_for_year(year: int) -> list[CalendarWeek]: - """Generate 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 - - -def get_current_year() -> int: - """Get current year.""" - return timezone.now().year diff --git a/aleksis/apps/chronos/util/format.py b/aleksis/apps/chronos/util/format.py deleted file mode 100644 index 0dd640f3c6e892741eb81d8de61421681160c9c9..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/util/format.py +++ /dev/null @@ -1,17 +0,0 @@ -from datetime import date -from typing import TYPE_CHECKING - -from django.utils.formats import date_format - -if TYPE_CHECKING: - from ..models import TimePeriod - - -def format_m2m(f, attr: str = "short_name") -> str: - """Join a attribute of all elements of a ManyToManyField.""" - return ", ".join([getattr(x, attr) for x in f.all()]) - - -def format_date_period(day: date, period: "TimePeriod") -> str: - """Format date and time period.""" - return f"{date_format(day)}, {period.period}." diff --git a/aleksis/apps/chronos/util/js.py b/aleksis/apps/chronos/util/js.py deleted file mode 100644 index 96612411ac9d4c25772c258f688706ab583b22cb..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/util/js.py +++ /dev/null @@ -1,7 +0,0 @@ -from datetime import date, datetime, time - - -def date_unix(value: date) -> int: - """Convert 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/notifications.py b/aleksis/apps/chronos/util/notifications.py deleted file mode 100644 index 847dc599ab47699ec4867d22822f81c8f1ec00ff..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/util/notifications.py +++ /dev/null @@ -1,210 +0,0 @@ -from datetime import datetime, timedelta -from typing import Union -from urllib.parse import urljoin - -from django.conf import settings -from django.urls import reverse -from django.utils import timezone -from django.utils.formats import date_format -from django.utils.translation import gettext_lazy as _ -from django.utils.translation import ngettext - -import zoneinfo - -from aleksis.core.models import Notification, Person -from aleksis.core.util.core_helpers import get_site_preferences - -from ..models import Event, ExtraLesson, LessonSubstitution, SupervisionSubstitution - - -def send_notifications_for_object( - instance: Union[ExtraLesson, LessonSubstitution, Event, SupervisionSubstitution], -): - """Send notifications for a change object.""" - recipients = [] - if isinstance(instance, LessonSubstitution): - recipients += instance.lesson_period.lesson.teachers.all() - recipients += instance.teachers.all() - recipients += Person.objects.filter( - member_of__in=instance.lesson_period.lesson.groups.all() - ) - elif isinstance(instance, (Event, ExtraLesson)): - recipients += instance.teachers.all() - recipients += Person.objects.filter(member_of__in=instance.groups.all()) - elif isinstance(instance, SupervisionSubstitution): - recipients.append(instance.teacher) - recipients.append(instance.supervision.teacher) - - description = "" - if isinstance(instance, LessonSubstitution): - # Date, lesson, subject - subject = instance.lesson_period.lesson.subject - day = instance.date - period = instance.lesson_period.period - - if instance.cancelled: - description += ( - _( - "The {subject} lesson in the {period}. period on {day} has been cancelled." - ).format(subject=subject.name, period=period.period, day=date_format(day)) - + " " - ) - else: - description += ( - _( - "The {subject} lesson in the {period}. period " - "on {day} has some current changes." - ).format(subject=subject.name, period=period.period, day=date_format(day)) - + " " - ) - - if instance.teachers.all(): - description += ( - ngettext( - "The teacher {old} is substituted by {new}.", - "The teachers {old} are substituted by {new}.", - instance.teachers.count(), - ).format( - old=instance.lesson_period.lesson.teacher_names, - new=instance.teacher_names, - ) - + " " - ) - - if instance.subject: - description += ( - _("The subject is changed to {subject}.").format(subject=instance.subject.name) - + " " - ) - - if instance.room: - description += ( - _("The lesson is moved from {old} to {new}.").format( - old=instance.lesson_period.room.name, - new=instance.room.name, - ) - + " " - ) - - if instance.comment: - description += ( - _("There is an additional comment: {comment}.").format(comment=instance.comment) - + " " - ) - - elif isinstance(instance, Event): - if instance.date_start != instance.date_end: - description += ( - _( - "There is an event that starts on {date_start}, {period_from}. period " - "and ends on {date_end}, {period_to}. period:" - ).format( - date_start=date_format(instance.date_start), - date_end=date_format(instance.date_end), - period_from=instance.period_from.period, - period_to=instance.period_to.period, - ) - + "\n" - ) - else: - description += ( - _( - "There is an event on {date} from the " - "{period_from}. period to the {period_to}. period:" - ).format( - date=date_format(instance.date_start), - period_from=instance.period_from.period, - period_to=instance.period_to.period, - ) - + "\n" - ) - - if instance.groups.all(): - description += _("Groups: {groups}").format(groups=instance.group_names) + "\n" - if instance.teachers.all(): - description += _("Teachers: {teachers}").format(teachers=instance.teacher_names) + "\n" - if instance.rooms.all(): - description += ( - _("Rooms: {rooms}").format( - rooms=", ".join([room.name for room in instance.rooms.all()]) - ) - + "\n" - ) - elif isinstance(instance, ExtraLesson): - description += ( - _("There is an extra lesson on {date} in the {period}. period:").format( - date=date_format(instance.date), - period=instance.period.period, - ) - + "\n" - ) - - if instance.groups.all(): - description += _("Groups: {groups}").format(groups=instance.group_names) + "\n" - if instance.room: - description += _("Subject: {subject}").format(subject=instance.subject.name) + "\n" - if instance.teachers.all(): - description += _("Teachers: {teachers}").format(teachers=instance.teacher_names) + "\n" - if instance.room: - description += _("Room: {room}").format(room=instance.room.name) + "\n" - if instance.comment: - description += _("Comment: {comment}.").format(comment=instance.comment) + "\n" - elif isinstance(instance, SupervisionSubstitution): - description += _( - "The supervision of {old} on {date} between the {period_from}. period " - "and the {period_to}. period in the area {area} is substituted by {new}." - ).format( - old=instance.supervision.teacher.full_name, - date=date_format(instance.date), - period_from=instance.supervision.break_item.after_period_number, - period_to=instance.supervision.break_item.before_period_number, - area=instance.supervision.area.name, - new=instance.teacher.full_name, - ) - - day = instance.date if hasattr(instance, "date") else instance.date_start - - url = urljoin( - settings.BASE_URL, - reverse( - "my_timetable_by_date", - args=[day.year, day.month, day.day], - ), - ) - - dt_start, dt_end = instance.time_range - dt_start = dt_start.replace(tzinfo=zoneinfo.ZoneInfo(settings.TIME_ZONE)) - dt_end = dt_end.replace(tzinfo=zoneinfo.ZoneInfo(settings.TIME_ZONE)) - - send_time = get_site_preferences()["chronos__time_for_sending_notifications"] - number_of_days = get_site_preferences()["chronos__days_in_advance_notifications"] - - start_range = timezone.now().replace(hour=send_time.hour, minute=send_time.minute) - if timezone.now().time() > send_time: - start_range = start_range - timedelta(days=1) - end_range = start_range + timedelta(days=number_of_days) - - if dt_start < start_range and dt_end < end_range: - # Skip this, because the change is in the past - return - - if dt_start <= end_range and dt_end >= start_range: - # Send immediately - send_at = timezone.now() - else: - # Schedule for later - send_at = datetime.combine( - dt_start.date() - timedelta(days=number_of_days), send_time - ).replace(tzinfo=zoneinfo.ZoneInfo(settings.TIME_ZONE)) - - for recipient in recipients: - if recipient.preferences["chronos__send_notifications"]: - n = Notification( - recipient=recipient, - sender=_("Timetable"), - title=_("There are current changes to your timetable."), - description=description, - link=url, - send_at=send_at, - ) - n.save() diff --git a/pyproject.toml b/pyproject.toml index bc23360f5990c46b80900f56cf2cf62d7c516fbc..923710bea23b988bf914f23afba9c2f6f98c0c3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "AlekSIS-App-Chronos" -version = "4.0.0.dev6" +version = "4.0.0.dev9" packages = [ { include = "aleksis" } ] @@ -51,9 +51,9 @@ priority = "supplemental" [tool.poetry.dependencies] python = "^3.10" calendarweek = "^0.5.0" -aleksis-core = "^4.0.0.dev11" +aleksis-core = "^4.0.0.dev13" aleksis-app-resint = "^4.0.0.dev1" -aleksis-app-cursus = "^0.1.0.dev3" +aleksis-app-cursus = "^0.1.0.dev4" [tool.poetry.plugins."aleksis.app"]