diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4fed9bc57cdf05f45aa7ba8faac411e9995fe4ff..c4910d94b5b1e282a9bb6b1ede5c930df33d3c6c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,6 +9,11 @@ and this project adheres to `Semantic Versioning`_. Unreleased ---------- +Fixed +~~~~~ + +* Remove lessons replaced by events from "My timetable" + `3.0.1`_ - 2023-07-20 --------------------- diff --git a/aleksis/apps/chronos/frontend/components/SelectTimetable.vue b/aleksis/apps/chronos/frontend/components/SelectTimetable.vue index 069d74c56942509bae47aaac844cdf5d2205d383..fb98d1f56c51d4261502df5d8ec2889e39e6445c 100644 --- a/aleksis/apps/chronos/frontend/components/SelectTimetable.vue +++ b/aleksis/apps/chronos/frontend/components/SelectTimetable.vue @@ -5,8 +5,9 @@ export default { name: "SelectTimetable", props: { value: { - type: String | null, - required: true, + type: String, + required: false, + default: null, }, availableTimetables: { type: Array, diff --git a/aleksis/apps/chronos/frontend/components/Timetable.vue b/aleksis/apps/chronos/frontend/components/Timetable.vue index d685684be91e9e63b7c1e2dc0e991ee7b098b7cf..e4fca5dfb63f26597cd534315fed8f32b9b8a2de 100644 --- a/aleksis/apps/chronos/frontend/components/Timetable.vue +++ b/aleksis/apps/chronos/frontend/components/Timetable.vue @@ -115,7 +115,7 @@ export default { <select-timetable v-model="selected" @input="selectDialog = false" - :availableTimetables="availableTimetables" + :available-timetables="availableTimetables" /> </v-card> </v-dialog> @@ -124,7 +124,7 @@ export default { <v-card> <select-timetable v-model="selected" - :availableTimetables="availableTimetables" + :available-timetables="availableTimetables" /> </v-card> </v-col> @@ -193,7 +193,7 @@ export default { </div> </div> <calendar-with-controls - :calendar-feeds="[{ name: 'lesson' }]" + :calendar-feeds="[{ name: 'lesson' }, { name: 'supervision' }]" :params="{ type: selected.type, id: selected.objId }" /> </v-card> diff --git a/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue index f11275541ae2f89febb63d5f9ed32485260232a1..0c9311450f0c7d8c3e922743f1403382da1148ce 100644 --- a/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue +++ b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/LessonDetails.vue @@ -3,7 +3,7 @@ v-bind="$props" :color="currentSubject ? currentSubject.colour_bg : null" without-location - > + > <template #title> <div :style="{ diff --git a/aleksis/apps/chronos/frontend/components/calendar_feeds/details/SupervisionDetails.vue b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/SupervisionDetails.vue new file mode 100644 index 0000000000000000000000000000000000000000..7d6455a054fd8d7a46433ea5ad4693e4c40ff43e --- /dev/null +++ b/aleksis/apps/chronos/frontend/components/calendar_feeds/details/SupervisionDetails.vue @@ -0,0 +1,109 @@ +<template> + <base-calendar-feed-details + v-bind="$props" + :color="currentSubject ? currentSubject.colour_bg : null" + without-location + > + <template #title> + <div + :style="{ + color: currentSubject ? currentSubject.colour_fg || 'white' : 'white', + }" + > + <lesson-event-subject :event="selectedEvent" /> + </div> + </template> + <template #badge> + <cancelled-calendar-status-chip + v-if="selectedEvent.meta.cancelled" + class="ml-4" + /> + <calendar-status-chip + color="warning" + icon="mdi-clipboard-alert-outline" + v-else-if="selectedEvent.meta.amended" + class="ml-4" + > + {{ $t("chronos.event.current_changes") }} + </calendar-status-chip> + </template> + <template #description> + <v-divider inset /> + <v-list-item> + <v-list-item-icon> + <v-icon color="primary">mdi-human-male-board </v-icon> + </v-list-item-icon> + <v-list-item-content> + <v-list-item-title> + <span v-if="teachers.length === 0" class="body-2 text--secondary">{{ + $t("chronos.event.no_teacher") + }}</span> + <lesson-related-object-chip + v-for="teacher in teachers" + :status="teacher.status" + :key="teacher.id" + new-icon="mdi-account-plus-outline" + >{{ teacher.full_name }}</lesson-related-object-chip + > + </v-list-item-title> + </v-list-item-content> + </v-list-item> + <v-list-item> + <v-list-item-icon> + <v-icon color="primary">mdi-door </v-icon> + </v-list-item-icon> + <v-list-item-content> + <v-list-item-title> + <span v-if="rooms.length === 0" class="body-2 text--secondary">{{ + $t("chronos.event.no_room") + }}</span> + <lesson-related-object-chip + v-for="room in rooms" + :status="room.status" + :key="room.id" + new-icon="mdi-door-open" + >{{ room.name }}</lesson-related-object-chip + > + </v-list-item-title> + </v-list-item-content> + </v-list-item> + <v-divider inset /> + <v-list-item v-if="selectedEvent.meta.comment"> + <v-list-item-content> + <v-list-item-title> + <v-alert + dense + outlined + type="warning" + icon="mdi-information-outline" + > + {{ selectedEvent.meta.comment }} + </v-alert> + </v-list-item-title> + </v-list-item-content> + </v-list-item> + </template> + </base-calendar-feed-details> +</template> + +<script> +import calendarFeedDetailsMixin from "aleksis.core/mixins/calendarFeedDetails.js"; +import BaseCalendarFeedDetails from "aleksis.core/components/calendar/BaseCalendarFeedDetails.vue"; +import CalendarStatusChip from "aleksis.core/components/calendar/CalendarStatusChip.vue"; +import CancelledCalendarStatusChip from "aleksis.core/components/calendar/CancelledCalendarStatusChip.vue"; + +import LessonRelatedObjectChip from "../../LessonRelatedObjectChip.vue"; +import lessonEvent from "../mixins/lessonEvent"; +import LessonEventSubject from "../../LessonEventSubject.vue"; +export default { + name: "LessonDetails", + components: { + LessonEventSubject, + LessonRelatedObjectChip, + BaseCalendarFeedDetails, + CalendarStatusChip, + CancelledCalendarStatusChip, + }, + mixins: [calendarFeedDetailsMixin, lessonEvent], +}; +</script> diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py index 165e9b10e0ee2e49595d4627c8f2642b2c179d5d..11ba2702e648d0eff7e6eb5c29a3748a8f8329c5 100644 --- a/aleksis/apps/chronos/models.py +++ b/aleksis/apps/chronos/models.py @@ -1583,7 +1583,7 @@ class LessonEvent(CalendarEvent): @classmethod def get_objects(cls, request, params=None) -> Iterable: """Return all objects that should be included in the calendar.""" - objs = super().get_objects(request, params) + objs = super().get_objects(request, params).not_instance_of(SupervisionEvent) if params: obj_id = int(params.get("id", 0)) type = params.get("type", None) @@ -1606,6 +1606,8 @@ class SupervisionEvent(LessonEvent): name = "supervision" verbose_name = _("Supervisions") + objects = PolymorphicCurrentSiteManager.from_queryset(LessonEventQuerySet)() + @classmethod def value_title(cls, reference_object: "LessonEvent", request) -> str: """Get the title of the event.""" @@ -1621,7 +1623,7 @@ class SupervisionEvent(LessonEvent): @classmethod def get_objects(cls, request, params=None) -> Iterable: """Return all objects that should be included in the calendar.""" - objs = super().get_objects(request, params) + objs = cls.objects.instance_of(cls) if params: obj_id = int(params.get("id", 0)) type = params.get("type", None) diff --git a/aleksis/apps/chronos/tests/regression/test_regression.py b/aleksis/apps/chronos/tests/regression/test_regression.py deleted file mode 100644 index 0b4a71838cdfb1fafe2600022868558d70b9bd14..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/tests/regression/test_regression.py +++ /dev/null @@ -1,76 +0,0 @@ -from datetime import time, timedelta - -from django.contrib.auth import get_user_model -from django.utils import timezone - -import pytest - -from aleksis.apps.chronos.util.chronos_helpers import get_rooms, get_teachers -from aleksis.core.models import Group, Person, Room, SchoolTerm - -pytestmark = pytest.mark.django_db - - -from aleksis.apps.chronos.models import Lesson, LessonPeriod, Subject, TimePeriod, ValidityRange - - -def test_rooms_teachers_only_from_current_school_term(): - User = get_user_model() - - user = User.objects.create(username="test", is_staff=True, is_superuser=True) - person_user = Person.objects.create(user=user, first_name="Test", last_name="User") - - correct_school_term = SchoolTerm.objects.create( - date_start=timezone.now() - timedelta(days=1), - date_end=timezone.now() + timedelta(days=1), - name="Correct school term", - ) - wrong_school_term = SchoolTerm.objects.create( - date_start=timezone.now() - timedelta(days=3), - date_end=timezone.now() - timedelta(days=2), - name="Wrong school term", - ) - - correct_validity = ValidityRange.objects.create( - school_term=correct_school_term, - date_start=correct_school_term.date_start, - date_end=correct_school_term.date_end, - name="Correct validity", - ) - wrong_validity = ValidityRange.objects.create( - school_term=wrong_school_term, - date_start=wrong_school_term.date_start, - date_end=wrong_school_term.date_end, - name="Wrong validity", - ) - - subject = Subject.objects.create(name="Test subject", short_name="TS") - time_period = TimePeriod.objects.create( - weekday=0, period=1, time_start=time(8, 0), time_end=time(9, 0) - ) - - correct_person = Person.objects.create(first_name="Correct", last_name="Person") - wrong_person = Person.objects.create(first_name="Wrong", last_name="Person") - - correct_lesson = Lesson.objects.create(validity=correct_validity, subject=subject) - correct_lesson.teachers.add(correct_person) - wrong_lesson = Lesson.objects.create(validity=wrong_validity, subject=subject) - wrong_lesson.teachers.add(wrong_person) - - correct_room = Room.objects.create(name="Correct room", short_name="cr") - wrong_room = Room.objects.create(name="Wrong room", short_name="wr") - - correct_lesson_period = LessonPeriod.objects.create( - lesson=correct_lesson, period=time_period, room=correct_room - ) - wrong_lesson_period = LessonPeriod.objects.create( - lesson=wrong_lesson, period=time_period, room=wrong_room - ) - - rooms = get_rooms(user) - assert correct_room in rooms - assert wrong_room not in rooms - - teachers = get_teachers(user) - assert correct_person in teachers - assert wrong_person not in teachers diff --git a/aleksis/apps/chronos/util/build.py b/aleksis/apps/chronos/util/build.py index a6c89a347ffdf2cc10e01b705fcad746eb7535e2..f79ecf6484dbaa18f22447ad9a0a0c57160add96 100644 --- a/aleksis/apps/chronos/util/build.py +++ b/aleksis/apps/chronos/util/build.py @@ -34,7 +34,7 @@ def build_timetable( type_ = obj.timetable_type is_week = False - if type(date_ref) == CalendarWeek: + if isinstance(date_ref, CalendarWeek): is_week = True if type_ is None: @@ -361,17 +361,22 @@ def build_timetable( 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_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_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: diff --git a/aleksis/apps/chronos/views.py b/aleksis/apps/chronos/views.py index fdb6def85b24e1f166ff7ee1139e04b0e4bfe9a5..a7ae9b77b322c00f41860c39a3f6cc7ae6376687 100644 --- a/aleksis/apps/chronos/views.py +++ b/aleksis/apps/chronos/views.py @@ -139,7 +139,7 @@ def timetable( el = get_el_by_pk(request, type_, pk, prefetch=True) - if type(el) == HttpResponseNotFound: + if isinstance(el, HttpResponseNotFound): return HttpResponseNotFound() type_ = TimetableType.from_string(type_) diff --git a/pyproject.toml b/pyproject.toml index 2ea4b3341d1f090797c2e14c377e3f33636d308c..32832395ff78f72b83016625a3bfd35612da15bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,6 @@ safety = "^2.3.5" flake8 = "^6.0.0" flake8-django = "~1.2" flake8-fixme = "^1.1.1" -flake8-mypy = "^17.8.0" flake8-bandit = "^4.1.1" flake8-builtins = "^2.0.0" flake8-docstrings = "^1.5.0" diff --git a/tox.ini b/tox.ini index 6e4b77ab1ded935257117696975c2150772cd85c..85c2494a5a2f5480bb05d48781edfaa74803eeab 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ skip_missing_interpreters = true envlist = py39,py310,py311 [testenv] -whitelist_externals = poetry +allowlist_externals = poetry skip_install = true envdir = {toxworkdir}/globalenv commands_pre = @@ -68,18 +68,6 @@ known_django = django skip = migrations sections = FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER -[mypy] -plugins = mypy_django_plugin.main -python_version = 3.8 -platform = linux -show_column_numbers = True -follow_imports = skip -ignore_missing_imports = True -cache_dir = /dev/null - -[mypy.plugins.django-stubs] -django_settings_module = aleksis.core.settings - [pytest] DJANGO_SETTINGS_MODULE = aleksis.core.settings junit_family = legacy