diff --git a/aleksis/apps/lesrooster/frontend/components/helper.graphql b/aleksis/apps/lesrooster/frontend/components/helper.graphql index 2c32fae1c109d2a8d86e3484ce9821405754a9f3..417870dfca049ce5c85c082555459e379b3ed274 100644 --- a/aleksis/apps/lesrooster/frontend/components/helper.graphql +++ b/aleksis/apps/lesrooster/frontend/components/helper.graphql @@ -29,14 +29,6 @@ query gqlClasses { } } -query gqlClassesByGrade($grade: ID!) { - groups: classesByGrade(grade: $grade) { - id - name - shortName - } -} - query gqlCourses { courses { id diff --git a/aleksis/apps/lesrooster/frontend/components/supervision/supervision.graphql b/aleksis/apps/lesrooster/frontend/components/supervision/supervision.graphql index b79998797602067598d0666fc3c5cba938111c77..88ccf9a4ba495178ca5e91080ca746d1318246c2 100644 --- a/aleksis/apps/lesrooster/frontend/components/supervision/supervision.graphql +++ b/aleksis/apps/lesrooster/frontend/components/supervision/supervision.graphql @@ -70,7 +70,7 @@ mutation deleteSupervisions($ids: [ID]!) { } } -mutation updateSupervisions($input: [BatchPatchBreakInput]!) { +mutation updateSupervisions($input: [BatchPatchSupervisionInput]!) { batchMutation: updateSupervisions(input: $input) { items: supervisions { ...supervisionFields diff --git a/aleksis/apps/lesrooster/frontend/index.js b/aleksis/apps/lesrooster/frontend/index.js index 67bcfca300b3193ee118311baa4c645246775cb3..1a82aec7706253dd24cc8dd41d0f29c0995abf73 100644 --- a/aleksis/apps/lesrooster/frontend/index.js +++ b/aleksis/apps/lesrooster/frontend/index.js @@ -11,14 +11,14 @@ export default { }, children: [ { - path: "validity-ranges/", + path: "validity_ranges/", component: () => import("./components/validity_range/ValidityRange.vue"), name: "lesrooster.validity_ranges", meta: { inMenu: true, titleKey: "lesrooster.validity_range.menu_title", icon: "mdi-calendar-expand-horizontal-outline", - permission: "lesrooster.view_validity_ranges_rule", + permission: "lesrooster.view_validityranges_rule", }, }, { @@ -30,7 +30,21 @@ export default { titleKey: "lesrooster.lesson_raster.menu_title", toolbarTitle: "lesrooster.lesson_raster.menu_title", icon: "mdi-grid-large", - permission: "lesrooster.view_lesson_raster_rule", + permission: "lesrooster.manage_lesson_raster_rule", + }, + }, + { + path: "timebound_course_configs/plan_courses/", + component: () => + import( + "./components/timebound_course_config/TimeboundCourseConfigRaster.vue" + ), + name: "lesrooster.planCourses", + meta: { + inMenu: true, + titleKey: "lesrooster.timebound_course_config.raster_menu_title", + icon: "mdi-clock-edit-outline", + permission: "lesrooster.view_timeboundcourseconfigs_rule", }, }, { @@ -43,7 +57,7 @@ export default { titleKey: "lesrooster.timetable_management.menu_title", toolbarTitle: "lesrooster.timetable_management.menu_title", icon: "mdi-magnet", - permission: "lesrooster.view_timetable_creation_rule", + permission: "lesrooster.plan_timetables_rule", }, children: [ { @@ -53,11 +67,22 @@ export default { name: "lesrooster.timetable_management", props: true, meta: { - permission: "lesrooster.view_timetable_creation_rule", + permission: "lesrooster.plan_timetables_rule", }, }, ], }, + { + path: "supervisions/", + component: () => import("./components/supervision/Supervision.vue"), + name: "lesrooster.supervisions", + meta: { + inMenu: true, + titleKey: "lesrooster.supervision.menu_title", + icon: "mdi-seesaw", + permission: "lesrooster.view_supervisions_rule", + }, + }, { path: "slots/", component: () => @@ -78,7 +103,7 @@ export default { inMenu: true, titleKey: "lesrooster.break.menu_title", icon: "mdi-timer-sand-paused", - permission: "lesrooster.view_break_slots_rule", + permission: "lesrooster.view_breakslots_rule", }, }, { @@ -92,32 +117,7 @@ export default { inMenu: true, titleKey: "lesrooster.timebound_course_config.crud_table_menu_title", icon: "mdi-timetable", - permission: "lesrooster.view_timebound_course_configs_rule", - }, - }, - { - path: "timebound_course_configs/plan_courses/", - component: () => - import( - "./components/timebound_course_config/TimeboundCourseConfigRaster.vue" - ), - name: "lesrooster.planCourses", - meta: { - inMenu: true, - titleKey: "lesrooster.timebound_course_config.raster_menu_title", - icon: "mdi-clock-edit-outline", - permission: "lesrooster.view_timebound_course_configs_rule", - }, - }, - { - path: "supervisions/", - component: () => import("./components/supervision/Supervision.vue"), - name: "lesrooster.supervisions", - meta: { - inMenu: true, - titleKey: "lesrooster.supervision.menu_title", - icon: "mdi-seesaw", - permission: "lesrooster.view_supervisions_rule", + permission: "lesrooster.view_timeboundcourseconfigs_rule", }, }, ], diff --git a/aleksis/apps/lesrooster/models.py b/aleksis/apps/lesrooster/models.py index 7d9ffafc72ef8c1930fbf2d6d3632894a8a5cc45..33b92ae379c89305567c1351bc5a549709259c70 100644 --- a/aleksis/apps/lesrooster/models.py +++ b/aleksis/apps/lesrooster/models.py @@ -93,7 +93,8 @@ class ValidityRange(ExtensibleModel): if qs.exists(): raise ValidationError( _( - "There is already a published validity range for this time or a part of this time." + "There is already a published validity range " + "for this time or a part of this time." ) ) @@ -692,6 +693,6 @@ class LesroosterGlobalPermissions(GlobalPermissionModel): class Meta: managed = False permissions = ( - ("view_lesson_raster", _("Can view lesson raster")), - ("view_timetable_creation", _("Can view timetable creation")), + ("manage_lesson_raster", _("Can manage lesson raster")), + ("plan_timetables", _("Can plan timetables")), ) diff --git a/aleksis/apps/lesrooster/rules.py b/aleksis/apps/lesrooster/rules.py index 973eeb7ad043b3e8c2a893b092f7cf3275548b94..ca93ba3f1e935ce6aa9482dce016c81c7cad7057 100644 --- a/aleksis/apps/lesrooster/rules.py +++ b/aleksis/apps/lesrooster/rules.py @@ -18,76 +18,123 @@ from .models import ( ValidityRange, ) +manage_lesson_raster_predicate = has_person | has_global_perm("lesrooster.manage_lesson_raster") +add_perm("lesrooster.manage_lesson_raster_rule", manage_lesson_raster_predicate) + +plan_timetables_predicate = has_person | has_global_perm("lesrooster.plan_timetables") +add_perm("lesrooster.plan_timetables_rule", plan_timetables_predicate) + + +# Slots +view_slots_predicate = has_person & ( + has_global_perm("lesrooster.view_slot") + | has_any_object("lesrooster.view_slot", Slot) + | manage_lesson_raster_predicate + | plan_timetables_predicate +) +add_perm("lesrooster.view_slots_rule", view_slots_predicate) + +view_slot_predicate = has_person & ( + has_global_perm("lesrooster.view_slot") + | has_object_perm("lesrooster.view_slot") + | manage_lesson_raster_predicate + | plan_timetables_predicate +) +add_perm("lesrooster.view_slot_rule", view_slot_predicate) + +create_slot_predicate = has_person & ( + has_global_perm("lesrooster.add_slot") | manage_lesson_raster_predicate +) +add_perm("lesrooster.create_slot_rule", create_slot_predicate) + +edit_slot_predicate = view_slot_predicate & ( + has_global_perm("lesrooster.change_slot") + | has_object_perm("lesrooster.change_slot") + | manage_lesson_raster_predicate +) +add_perm("lesrooster.edit_slot_rule", edit_slot_predicate) + +delete_slot_predicate = view_slot_predicate & ( + has_global_perm("lesrooster.delete_slot") + | has_object_perm("lesrooster.delete_slot") + | manage_lesson_raster_predicate +) +add_perm("lesrooster.delete_slot_rule", delete_slot_predicate) + +# Break slots + view_break_slots_predicate = has_person & ( has_global_perm("lesrooster.view_breakslot") | has_any_object("lesrooster.view_breakslot", BreakSlot) + | manage_lesson_raster_predicate + | plan_timetables_predicate ) -add_perm("lesrooster.view_break_slots_rule", view_break_slots_predicate) +add_perm("lesrooster.view_breakslots_rule", view_break_slots_predicate) view_break_slot_predicate = has_person & ( - has_global_perm("lesrooster.view_breakslot") | has_object_perm("lesrooster.view_breakslot") + has_global_perm("lesrooster.view_breakslot") + | has_object_perm("lesrooster.view_breakslot") + | manage_lesson_raster_predicate + | plan_timetables_predicate ) -add_perm("lesrooster.view_break_slot_rule", view_break_slot_predicate) +add_perm("lesrooster.view_breakslot_rule", view_break_slot_predicate) -create_break_slot_predicate = has_person & has_global_perm("lesrooster.add_breakslot") -add_perm("lesrooster.create_break_slot_rule", create_break_slot_predicate) +create_break_slot_predicate = has_person & ( + has_global_perm("lesrooster.add_breakslot") | manage_lesson_raster_predicate +) +add_perm("lesrooster.create_breakslot_rule", create_break_slot_predicate) edit_break_slot_predicate = view_break_slot_predicate & ( - has_global_perm("lesrooster.change_breakslot") | has_object_perm("lesrooster.change_breakslot") + has_global_perm("lesrooster.change_breakslot") + | has_object_perm("lesrooster.change_breakslot") + | manage_lesson_raster_predicate ) -add_perm("lesrooster.edit_break_slot_rule", edit_break_slot_predicate) +add_perm("lesrooster.edit_breakslot_rule", edit_break_slot_predicate) delete_break_slot_predicate = view_break_slot_predicate & ( - has_global_perm("lesrooster.delete_breakslot") | has_object_perm("lesrooster.delete_breakslot") + has_global_perm("lesrooster.delete_breakslot") + | has_object_perm("lesrooster.delete_breakslot") + | manage_lesson_raster_predicate ) -add_perm("lesrooster.delete_break_slot_rule", delete_break_slot_predicate) +add_perm("lesrooster.delete_breakslot_rule", delete_break_slot_predicate) + +# Lessons view_lessons_predicate = has_person & ( - has_global_perm("lesrooster.view_lesson") | has_any_object("lesrooster.view_lesson", Lesson) + has_global_perm("lesrooster.view_lesson") + | has_any_object("lesrooster.view_lesson", Lesson) + | plan_timetables_predicate ) add_perm("lesrooster.view_lessons_rule", view_lessons_predicate) view_lesson_predicate = has_person & ( - has_global_perm("lesrooster.view_lesson") | has_object_perm("lesrooster.view_lesson") + has_global_perm("lesrooster.view_lesson") + | has_object_perm("lesrooster.view_lesson") + | plan_timetables_predicate ) add_perm("lesrooster.view_lesson_rule", view_lesson_predicate) -create_lesson_predicate = has_person & has_global_perm("lesrooster.add_lesson") +create_lesson_predicate = has_person & ( + has_global_perm("lesrooster.add_lesson") | plan_timetables_predicate +) add_perm("lesrooster.create_lesson_rule", create_lesson_predicate) edit_lesson_predicate = view_lesson_predicate & ( - has_global_perm("lesrooster.change_lesson") | has_object_perm("lesrooster.change_lesson") + has_global_perm("lesrooster.change_lesson") + | has_object_perm("lesrooster.change_lesson") + | plan_timetables_predicate ) add_perm("lesrooster.edit_lesson_rule", edit_lesson_predicate) delete_lesson_predicate = view_lesson_predicate & ( - has_global_perm("lesrooster.delete_lesson") | has_object_perm("lesrooster.delete_lesson") + has_global_perm("lesrooster.delete_lesson") + | has_object_perm("lesrooster.delete_lesson") + | plan_timetables_predicate ) add_perm("lesrooster.delete_lesson_rule", delete_lesson_predicate) -view_slots_predicate = has_person & ( - has_global_perm("lesrooster.view_slot") | has_any_object("lesrooster.view_slot", Slot) -) -add_perm("lesrooster.view_slots_rule", view_slots_predicate) - -view_slot_predicate = has_person & ( - has_global_perm("lesrooster.view_slot") | has_object_perm("lesrooster.view_slot") -) -add_perm("lesrooster.view_slot_rule", view_slot_predicate) - -create_slot_predicate = has_person & has_global_perm("lesrooster.add_slot") -add_perm("lesrooster.create_slot_rule", create_slot_predicate) - -edit_slot_predicate = view_slot_predicate & ( - has_global_perm("lesrooster.change_slot") | has_object_perm("lesrooster.change_slot") -) -add_perm("lesrooster.edit_slot_rule", edit_slot_predicate) - -delete_slot_predicate = view_slot_predicate & ( - has_global_perm("lesrooster.delete_slot") | has_object_perm("lesrooster.delete_slot") -) -add_perm("lesrooster.delete_slot_rule", delete_slot_predicate) +# Supervisions view_supervisions_predicate = has_person & ( has_global_perm("lesrooster.view_supervision") | has_any_object("lesrooster.view_supervision", Supervision) @@ -114,135 +161,138 @@ delete_supervision_predicate = view_supervision_predicate & ( ) add_perm("lesrooster.delete_supervision_rule", delete_supervision_predicate) + +# Supervision substitutions view_supervision_substitutions_predicate = has_person & ( has_global_perm("lesrooster.view_supervisionsubstitution") | has_any_object("lesrooster.view_supervisionsubstitution", SupervisionSubstitution) ) -add_perm("lesrooster.view_supervision_substitutions_rule", view_supervision_substitutions_predicate) +add_perm("lesrooster.view_supervisionsubstitutions_rule", view_supervision_substitutions_predicate) view_supervision_substitution_predicate = has_person & ( has_global_perm("lesrooster.view_supervisionsubstitution") | has_object_perm("lesrooster.view_supervisionsubstitution") ) -add_perm("lesrooster.view_supervision_substitution_rule", view_supervision_substitution_predicate) +add_perm("lesrooster.view_supervisionsubstitution_rule", view_supervision_substitution_predicate) create_supervision_substitution_predicate = has_person & has_global_perm( "lesrooster.add_supervisionsubstitution" ) add_perm( - "lesrooster.create_supervision_substitution_rule", create_supervision_substitution_predicate + "lesrooster.create_supervisionsubstitution_rule", create_supervision_substitution_predicate ) edit_supervision_substitution_predicate = view_supervision_substitution_predicate & ( has_global_perm("lesrooster.change_supervisionsubstitution") | has_object_perm("lesrooster.change_supervisionsubstitution") ) -add_perm("lesrooster.edit_supervision_substitution_rule", edit_supervision_substitution_predicate) +add_perm("lesrooster.edit_supervisionsubstitution_rule", edit_supervision_substitution_predicate) delete_supervision_substitution_predicate = view_supervision_substitution_predicate & ( has_global_perm("lesrooster.delete_supervisionsubstitution") | has_object_perm("lesrooster.delete_supervisionsubstitution") ) add_perm( - "lesrooster.delete_supervision_substitution_rule", delete_supervision_substitution_predicate + "lesrooster.delete_supervisionsubstitution_rule", delete_supervision_substitution_predicate ) +# Timebound course configs + view_timebound_course_configs_predicate = has_person & ( has_global_perm("lesrooster.view_timeboundcourseconfig") | has_any_object("lesrooster.view_timeboundcourseconfig", TimeboundCourseConfig) ) -add_perm("lesrooster.view_timebound_course_configs_rule", view_timebound_course_configs_predicate) +add_perm("lesrooster.view_timeboundcourseconfigs_rule", view_timebound_course_configs_predicate) view_timebound_course_config_predicate = has_person & ( has_global_perm("lesrooster.view_timeboundcourseconfig") | has_object_perm("lesrooster.view_timeboundcourseconfig") + | plan_timetables_predicate ) -add_perm("lesrooster.view_timebound_course_config_rule", view_timebound_course_config_predicate) +add_perm("lesrooster.view_timeboundcourseconfig_rule", view_timebound_course_config_predicate) create_timebound_course_config_predicate = has_person & has_global_perm( "lesrooster.add_timeboundcourseconfig" ) -add_perm("lesrooster.create_timebound_course_config_rule", create_timebound_course_config_predicate) +add_perm("lesrooster.create_timeboundcourseconfig_rule", create_timebound_course_config_predicate) edit_timebound_course_config_predicate = view_timebound_course_config_predicate & ( has_global_perm("lesrooster.change_timeboundcourseconfig") | has_object_perm("lesrooster.change_timeboundcourseconfig") ) -add_perm("lesrooster.edit_timebound_course_config_rule", edit_timebound_course_config_predicate) +add_perm("lesrooster.edit_timeboundcourseconfig_rule", edit_timebound_course_config_predicate) delete_timebound_course_config_predicate = view_timebound_course_config_predicate & ( has_global_perm("lesrooster.delete_timeboundcourseconfig") | has_object_perm("lesrooster.delete_timeboundcourseconfig") ) -add_perm("lesrooster.delete_timebound_course_config_rule", delete_timebound_course_config_predicate) - -view_time_grids_predicate = has_person & ( - has_global_perm("lesrooster.view_timegrid") - | has_any_object("lesrooster.view_timegrid", TimeGrid) -) -add_perm("lesrooster.view_time_grids_rule", view_time_grids_predicate) - -view_time_grid_predicate = has_person & ( - has_global_perm("lesrooster.view_timegrid") | has_object_perm("lesrooster.view_timegrid") -) -add_perm("lesrooster.view_time_grid_rule", view_time_grid_predicate) - -create_time_grid_predicate = has_person & has_global_perm("lesrooster.add_timegrid") -add_perm("lesrooster.create_time_grid_rule", create_time_grid_predicate) +add_perm("lesrooster.delete_timeboundcourseconfig_rule", delete_timebound_course_config_predicate) -edit_time_grid_predicate = view_time_grid_predicate & ( - has_global_perm("lesrooster.change_timegrid") | has_object_perm("lesrooster.change_timegrid") -) -add_perm("lesrooster.edit_time_grid_rule", edit_time_grid_predicate) -delete_time_grid_predicate = view_time_grid_predicate & ( - has_global_perm("lesrooster.delete_timegrid") | has_object_perm("lesrooster.delete_timegrid") -) -add_perm("lesrooster.delete_time_grid_rule", delete_time_grid_predicate) +# Validity ranges view_validity_ranges_predicate = has_person & ( has_global_perm("lesrooster.view_validityrange") | has_any_object("lesrooster.view_validityrange", ValidityRange) ) -add_perm("lesrooster.view_validity_ranges_rule", view_validity_ranges_predicate) +add_perm("lesrooster.view_validityranges_rule", view_validity_ranges_predicate) view_validity_range_predicate = has_person & ( has_global_perm("lesrooster.view_validityrange") | has_object_perm("lesrooster.view_validityrange") + | plan_timetables_predicate ) -add_perm("lesrooster.view_validity_range_rule", view_validity_range_predicate) +add_perm("lesrooster.view_validityrange_rule", view_validity_range_predicate) create_validity_range_predicate = has_person & has_global_perm("lesrooster.add_validityrange") -add_perm("lesrooster.create_validity_range_rule", create_validity_range_predicate) +add_perm("lesrooster.create_validityrange_rule", create_validity_range_predicate) edit_validity_range_predicate = view_validity_range_predicate & ( has_global_perm("lesrooster.change_validityrange") | has_object_perm("lesrooster.change_validityrange") ) -add_perm("lesrooster.edit_validity_range_rule", edit_validity_range_predicate) +add_perm("lesrooster.edit_validityrange_rule", edit_validity_range_predicate) delete_validity_range_predicate = view_validity_range_predicate & ( has_global_perm("lesrooster.delete_validityrange") | has_object_perm("lesrooster.delete_validityrange") ) -add_perm("lesrooster.delete_validity_range_rule", delete_validity_range_predicate) +add_perm("lesrooster.delete_validityrange_rule", delete_validity_range_predicate) + +# Time grids +view_time_grids_predicate = has_person & ( + has_global_perm("lesrooster.view_timegrid") + | has_any_object("lesrooster.view_timegrid", TimeGrid) +) +add_perm("lesrooster.view_timegrids_rule", view_time_grids_predicate) + +view_time_grid_predicate = has_person & ( + has_global_perm("lesrooster.view_timegrid") + | has_object_perm("lesrooster.view_timegrid") + | plan_timetables_predicate +) +add_perm("lesrooster.view_timegrid_rule", view_time_grid_predicate) + +create_time_grid_predicate = has_person & has_global_perm("lesrooster.add_timegrid") +add_perm("lesrooster.create_timegrid_rule", create_time_grid_predicate) + +edit_time_grid_predicate = view_time_grid_predicate & ( + has_global_perm("lesrooster.change_timegrid") | has_object_perm("lesrooster.change_timegrid") +) +add_perm("lesrooster.edit_timegrid_rule", edit_time_grid_predicate) -view_lesson_raster_predicate = has_person | has_global_perm( - "lesrooster.view_lesson_raster" -) # FIXME -add_perm("lesrooster.view_lesson_raster_rule", view_lesson_raster_predicate) +delete_time_grid_predicate = view_time_grid_predicate & ( + has_global_perm("lesrooster.delete_timegrid") | has_object_perm("lesrooster.delete_timegrid") +) +add_perm("lesrooster.delete_timegrid_rule", delete_time_grid_predicate) -view_timetable_creation_predicate = has_person | has_global_perm( - "lesrooster.view_timetable_creation" -) # FIXME -add_perm("lesrooster.view_timetable_creation_rule", view_timetable_creation_predicate) view_lesrooster_menu_predicate = ( view_validity_ranges_predicate | view_slots_predicate | view_break_slots_predicate | view_timebound_course_configs_predicate - | view_lesson_raster_predicate - | view_timetable_creation_predicate + | manage_lesson_raster_predicate + | plan_timetables_predicate ) add_perm("lesrooster.view_lesrooster_menu_rule", view_lesrooster_menu_predicate) diff --git a/aleksis/apps/lesrooster/schema/__init__.py b/aleksis/apps/lesrooster/schema/__init__.py index 4e79ee518214f65cda7e50b4579686a597861df9..a1fe2e1c16fd83fdc5621740ba73516b105df8ae 100644 --- a/aleksis/apps/lesrooster/schema/__init__.py +++ b/aleksis/apps/lesrooster/schema/__init__.py @@ -7,12 +7,19 @@ from guardian.shortcuts import get_objects_for_user from aleksis.apps.cursus.models import Course, Subject from aleksis.apps.cursus.schema import CourseInterface -from aleksis.apps.cursus.schema import Query as CursusSchemaQuery from aleksis.core.models import Group from aleksis.core.schema.base import FilterOrderList from aleksis.core.schema.group import GroupType -from ..models import Lesson, Slot, TimeboundCourseConfig, ValidityRange +from ..models import ( + BreakSlot, + Lesson, + Slot, + Supervision, + TimeboundCourseConfig, + TimeGrid, + ValidityRange, +) from .break_slot import ( BreakSlotBatchCreateMutation, BreakSlotBatchDeleteMutation, @@ -47,7 +54,6 @@ from .supervision import ( ) from .time_grid import ( TimeGridBatchDeleteMutation, - TimeGridBatchPatchMutation, TimeGridCreateMutation, TimeGridDeleteMutation, TimeGridType, @@ -105,24 +111,56 @@ class Query(graphene.ObjectType): LesroosterExtendedSubjectType, groups=graphene.List(graphene.ID) ) - classes_by_grade = graphene.List(GroupType, grade=graphene.ID()) - groups_by_time_grid = graphene.List(GroupType, time_grid=graphene.ID(required=True)) + @staticmethod + def resolve_break_slots(root, info): + if not info.context.user.has_perm("lesrooster.view_breakslot_rule"): + return get_objects_for_user(info.context.user, "lesrooster.view_breakslot", BreakSlot) + return BreakSlot.objects.all() + @staticmethod def resolve_slots(root, info): # Note: This does also return `Break` objects (but with type set to Slot). This is intended - return Slot.objects.non_polymorphic() + slots = Slot.objects.non_polymorphic() + if not info.context.user.has_perm("lesrooster.view_slot_rule"): + return get_objects_for_user(info.context.user, "lesrooster.view_slot", slots) + return slots + + @staticmethod + def resolve_timebound_course_configs(root, info): + tccs = TimeboundCourseConfig.objects.all() + if not info.context.user.has_perm("lesrooster.view_timeboundcourseconfig_rule"): + return get_objects_for_user( + info.context.user, "lesrooster.view_timeboundcourseconfig", tccs + ) + return tccs @staticmethod - def resolve_time_course_configs(root, info): - return + def resolve_validity_ranges(root, info): + if not info.context.user.has_perm("lesrooster.view_validityrange_rule"): + return get_objects_for_user( + info.context.user, "lesrooster.view_validityrange", ValidityRange + ) + return ValidityRange.objects.all() + + @staticmethod + def resolve_time_grids(root, info): + if not info.context.user.has_perm("lesrooster.view_timegrid_rule"): + return get_objects_for_user(info.context.user, "lesrooster.view_timegrid", TimeGrid) + return TimeGrid.objects.all() + + @staticmethod + def resolve_supervisions(root, info): + if not info.context.user.has_perm("lesrooster.view_supervision_rule"): + return get_objects_for_user( + info.context.user, "lesrooster.view_supervision", Supervision + ) + return Supervision.objects.all() @staticmethod def resolve_lesrooster_extended_subjects(root, info, groups): - return get_objects_for_user( - info.context.user, "cursus.view_subject", Subject.objects.all() - ).prefetch_related( + subjects = Subject.objects.all().prefetch_related( Prefetch( "courses", queryset=get_objects_for_user( @@ -130,21 +168,20 @@ class Query(graphene.ObjectType): ).filter(groups__in=groups), ) ) + if not info.context.user.has_perm("lesrooster.view_subject_rule"): + return get_objects_for_user(info.context.user, "cursus.view_subject", subjects) + return subjects @staticmethod def resolve_current_validity_range(root, info): - return ValidityRange.current - - @staticmethod - def resolve_classes_by_grade(root, info, grade): - grade_group = Group.objects.prefetch_related("child_groups").get(id=grade) - return get_objects_for_user( - info.context.user, - "core.view_group", - grade_group.child_groups.filter(group_type__name="School class"), - ) + validity_range = ValidityRange.current + if info.context.user.has_perm("lesrooster.view_validityrange_rule", validity_range): + return validity_range def resolve_course_objects_for_group(root, info, group, time_grid): + if not info.context.user.has_perm("lesrooster.plan_timetables_rule"): + return [] + group = Group.objects.get(pk=group) if not group: @@ -164,6 +201,9 @@ class Query(graphene.ObjectType): @staticmethod def resolve_lesson_objects_for_group(root, info, group, time_grid): + if not info.context.user.has_perm("lesrooster.plan_timetables_rule"): + return [] + group = Group.objects.get(pk=group) if not group: @@ -179,6 +219,9 @@ class Query(graphene.ObjectType): @staticmethod def resolve_lesson_objects_for_teacher(root, info, teacher, time_grid): + if not info.context.user.has_perm("lesrooster.plan_timetables_rule"): + return [] + return Lesson.objects.filter( Q(teachers=teacher) | Q(course__teachers=teacher), slot_start__time_grid_id=time_grid, @@ -187,6 +230,9 @@ class Query(graphene.ObjectType): @staticmethod def resolve_lesson_objects_for_room(root, info, room, time_grid): + if not info.context.user.has_perm("lesrooster.plan_timetables_rule"): + return [] + return Lesson.objects.filter( rooms=room, slot_start__time_grid_id=time_grid, @@ -195,7 +241,9 @@ class Query(graphene.ObjectType): @staticmethod def resolve_groups_by_time_grid(root, info, time_grid=None, **kwargs): - # FIXME Permissions + if not info.context.user.has_perm("lesrooster.plan_timetables_rule"): + return [] + return ( Group.objects.filter(school_term__lr_validity_ranges__time_grids__id=time_grid) .annotate(has_cg=Q(child_groups__isnull=False)) diff --git a/aleksis/apps/lesrooster/schema/break_slot.py b/aleksis/apps/lesrooster/schema/break_slot.py index 167b8ca590f4cfebdb34c746b6a6820498338ba0..c2d39f182db373ad620810c47a0f7cd2c712f5be 100644 --- a/aleksis/apps/lesrooster/schema/break_slot.py +++ b/aleksis/apps/lesrooster/schema/break_slot.py @@ -6,6 +6,7 @@ from graphene_django_cud.mutations import ( DjangoBatchPatchMutation, DjangoCreateMutation, ) +from guardian.shortcuts import get_objects_for_user from aleksis.core.schema.base import ( DeleteMutation, @@ -40,7 +41,9 @@ class BreakSlotType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType): @classmethod def get_queryset(cls, queryset, info): - return queryset # FIXME filter this queryset based on permissions + if not info.context.user.has_perm("lesrooster.view_breakslot_rule"): + return get_objects_for_user(info.context.user, "lesrooster.view_breakslot", queryset) + return queryset class BreakSlotCreateMutation(DjangoCreateMutation): @@ -48,13 +51,20 @@ class BreakSlotCreateMutation(DjangoCreateMutation): model = BreakSlot return_field_name = "breakSlot" field_types = {"weekday": graphene.Int()} - exclude = ("managed_by_app_label",) - permissions = ("lesrooster.create_break_slot_rule",) + only_fields = ( + "time_grid", + "name", + "weekday", + "period_after", + "time_start", + "time_end", + ) + permissions = ("lesrooster.create_breakslot_rule",) class BreakSlotDeleteMutation(DeleteMutation): klass = BreakSlot - permission_required = "lesrooster.delete_breakslot" + permission_required = "lesrooster.delete_breakslot_rule" class BreakSlotBatchCreateMutation(PermissionBatchPatchMixin, DjangoBatchCreateMutation): @@ -62,14 +72,21 @@ class BreakSlotBatchCreateMutation(PermissionBatchPatchMixin, DjangoBatchCreateM model = BreakSlot return_field_name = "breakSlots" field_types = {"weekday": graphene.Int()} - exclude = ("managed_by_app_label",) - permissions = ("lesrooster.create_break_slot_rule",) + only_fields = ( + "time_grid", + "name", + "weekday", + "period_after", + "time_start", + "time_end", + ) + permissions = ("lesrooster.create_breakslot_rule",) class BreakSlotBatchDeleteMutation(PermissionBatchDeleteMixin, DjangoBatchDeleteMutation): class Meta: model = BreakSlot - permissions = ("lesrooster.delete_breakslot",) + permissions = ("lesrooster.delete_breakslot_rule",) class BreakSlotBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutation): @@ -77,4 +94,12 @@ class BreakSlotBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMut model = BreakSlot return_field_name = "breakSlots" field_types = {"weekday": graphene.Int()} - permissions = ("lesrooster.change_breakslot",) + permissions = ("lesrooster.edit_breakslot_rule",) + only_fields = ( + "time_grid", + "name", + "weekday", + "period_after", + "time_start", + "time_end", + ) diff --git a/aleksis/apps/lesrooster/schema/lesson.py b/aleksis/apps/lesrooster/schema/lesson.py index ee8edf3aec5a83ff238c32b3bf4a366913470542..c01743072fe3d6236e48bdbc7b6318d0c22e9be1 100644 --- a/aleksis/apps/lesrooster/schema/lesson.py +++ b/aleksis/apps/lesrooster/schema/lesson.py @@ -6,6 +6,7 @@ from graphene_django_cud.mutations import ( DjangoCreateMutation, DjangoPatchMutation, ) +from guardian.shortcuts import get_objects_for_user from recurrence import Recurrence, deserialize, serialize from aleksis.core.schema.base import ( @@ -37,7 +38,8 @@ class LessonType( @classmethod def get_queryset(cls, queryset, info): - # FIXME filter this queryset based on permissions + if not info.context.user.has_perm("lesrooster.view_lesson_rule"): + return get_objects_for_user(info.context.user, "lesrooster.view_lesson", queryset) return queryset @staticmethod @@ -48,8 +50,7 @@ class LessonType( class LessonCreateMutation(DjangoCreateMutation): class Meta: model = Lesson - fields = ( - "id", + only_fields = ( "course", "slot_start", "slot_end", @@ -59,7 +60,7 @@ class LessonCreateMutation(DjangoCreateMutation): "recurrence", ) field_types = {"recurrence": graphene.String()} - permissions = ("lesrooster.create_lesson",) + permissions = ("lesrooster.create_lesson_rule",) @classmethod def handle_recurrence(cls, value: str, name, info) -> Recurrence: @@ -80,8 +81,7 @@ class LessonBatchDeleteMutation(PermissionBatchDeleteMixin, DjangoBatchDeleteMut class LessonBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutation): class Meta: model = Lesson - fields = ( - "id", + only_fields = ( "course", "slot_start", "slot_end", @@ -91,7 +91,7 @@ class LessonBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutati "recurrence", ) field_types = {"recurrence": graphene.String()} - permissions = ("lesrooster.change_lesson",) + permissions = ("lesrooster.edit_lesson_rule",) @classmethod def handle_recurrence(cls, value: str, name, info) -> Recurrence: @@ -101,8 +101,7 @@ class LessonBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutati class LessonPatchMutation(PermissionPatchMixin, DjangoPatchMutation): class Meta: model = Lesson - fields = ( - "id", + only_fields = ( "course", "slot_start", "slot_end", @@ -112,7 +111,7 @@ class LessonPatchMutation(PermissionPatchMixin, DjangoPatchMutation): "recurrence", ) field_types = {"recurrence": graphene.String()} - permissions = ("lesrooster.change_lesson",) + permissions = ("lesrooster.edit_lesson_rule",) @classmethod def handle_recurrence(cls, value: str, name, info) -> Recurrence: diff --git a/aleksis/apps/lesrooster/schema/slot.py b/aleksis/apps/lesrooster/schema/slot.py index 9e7e4e190e4bf551b4c391aacdd769d7c6949e39..f9d62530b58107ba454537089656644ca35d3979 100644 --- a/aleksis/apps/lesrooster/schema/slot.py +++ b/aleksis/apps/lesrooster/schema/slot.py @@ -9,6 +9,7 @@ from graphene_django_cud.mutations import ( DjangoBatchPatchMutation, DjangoCreateMutation, ) +from guardian.shortcuts import get_objects_for_user from aleksis.core.schema.base import ( DeleteMutation, @@ -42,7 +43,9 @@ class SlotType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType): @classmethod def get_queryset(cls, queryset, info): - return queryset # FIXME filter this queryset based on permissions + if not info.context.user.has_perm("lesrooster.view_slot_rule"): + return get_objects_for_user(info.context.user, "lesrooster.view_slot", queryset) + return queryset @staticmethod def resolve_model(root, info): @@ -57,7 +60,7 @@ class SlotCreateMutation(DjangoCreateMutation): class Meta: model = Slot field_types = {"weekday": graphene.Int()} - exclude = ("managed_by_app_label",) + only_fields = ("time_grid", "name", "weekday", "period", "time_start", "time_end") permissions = ("lesrooster.create_slot_rule",) @@ -70,7 +73,7 @@ class SlotBatchCreateMutation(PermissionBatchPatchMixin, DjangoBatchCreateMutati class Meta: model = Slot field_types = {"weekday": graphene.Int()} - exclude = ("managed_by_app_label",) + only_fields = ("time_grid", "name", "weekday", "period", "time_start", "time_end") permissions = ("lesrooster.create_slot_rule",) @@ -85,6 +88,7 @@ class SlotBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutation model = Slot field_types = {"weekday": graphene.Int()} permissions = ("lesrooster.change_slot",) + only_fields = ("time_grid", "name", "weekday", "period", "time_start", "time_end") class CarryOverSlotsMutation(graphene.Mutation): @@ -100,7 +104,7 @@ class CarryOverSlotsMutation(graphene.Mutation): @classmethod def mutate(cls, root, info, time_grid, from_day, to_day, only=None): - if not info.context.user.has_perm("lesrooster.change_slot"): + if not info.context.user.has_perm("lesrooster.edit_slot_rule"): raise PermissionDenied() if only is None: @@ -164,7 +168,7 @@ class CopySlotsFromDifferentRangeMutation(graphene.Mutation): @classmethod def mutate(cls, root, info, time_grid, from_time_grid): - if not info.context.user.has_perm("lesrooster.change_slot"): + if not info.context.user.has_perm("lesrooster.edit_slot_rule"): raise PermissionDenied() time_grid = TimeGrid.objects.get(id=time_grid) diff --git a/aleksis/apps/lesrooster/schema/supervision.py b/aleksis/apps/lesrooster/schema/supervision.py index b0c249f1ad1d4e6e23e0172f4de30748d82897c7..ac1cb89ebc6badcffd9a40a231caa891c891f438 100644 --- a/aleksis/apps/lesrooster/schema/supervision.py +++ b/aleksis/apps/lesrooster/schema/supervision.py @@ -5,6 +5,7 @@ from graphene_django_cud.mutations import ( DjangoBatchPatchMutation, DjangoCreateMutation, ) +from guardian.shortcuts import get_objects_for_user from recurrence import Recurrence, deserialize, serialize from aleksis.core.schema.base import ( @@ -41,7 +42,9 @@ class SupervisionType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType) @classmethod def get_queryset(cls, queryset, info): - return queryset # FIXME filter this queryset based on permissions + if not info.context.user.has_perm("lesrooster.view_supervision_rule"): + return get_objects_for_user(info.context.user, "lesrooster.view_supervision", queryset) + return queryset @staticmethod def resolve_recurrence(root, info, **kwargs): @@ -51,16 +54,14 @@ class SupervisionType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType) class SupervisionCreateMutation(DjangoCreateMutation): class Meta: model = Supervision - fields = ( - "id", + field_types = {"recurrence": graphene.String()} + only_fields = ( "rooms", "teachers", "break_slot", "recurrence", ) - field_types = {"recurrence": graphene.String()} - exclude = ("managed_by_app_label",) - permissions = ("lesrooster.create_supervision",) + permissions = ("lesrooster.create_supervision_rule",) @classmethod def handle_recurrence(cls, value: str, name, info) -> Recurrence: @@ -69,20 +70,19 @@ class SupervisionCreateMutation(DjangoCreateMutation): class SupervisionDeleteMutation(DeleteMutation): klass = Supervision - permission_required = "lesrooster.delete_supervision" + permission_required = "lesrooster.delete_supervision_rule" class SupervisionBatchDeleteMutation(PermissionBatchDeleteMixin, DjangoBatchDeleteMutation): class Meta: model = Supervision - permissions = ("lesrooster.delete_supervision",) + permissions = ("lesrooster.delete_supervision_rule",) class SupervisionBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutation): class Meta: model = Supervision - fields = ( - "id", + only_fields = ( "rooms", "teachers", "break_slot", @@ -90,7 +90,7 @@ class SupervisionBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchM ) field_types = {"recurrence": graphene.String()} exclude = ("managed_by_app_label",) - permissions = ("lesrooster.create_supervision",) + permissions = ("lesrooster.create_supervision_rule",) @classmethod def handle_recurrence(cls, value: str, name, info) -> Recurrence: diff --git a/aleksis/apps/lesrooster/schema/time_grid.py b/aleksis/apps/lesrooster/schema/time_grid.py index 6c0796dab8e98219f679add3b6b50e2acc39e9b1..b9bfbaa9c2a3f9ea78e9818063548b5045bd6b31 100644 --- a/aleksis/apps/lesrooster/schema/time_grid.py +++ b/aleksis/apps/lesrooster/schema/time_grid.py @@ -4,6 +4,7 @@ from graphene_django_cud.mutations import ( DjangoBatchPatchMutation, DjangoCreateMutation, ) +from guardian.shortcuts import get_objects_for_user from aleksis.core.schema.base import ( DeleteMutation, @@ -34,30 +35,37 @@ class TimeGridType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType): @classmethod def get_queryset(cls, queryset, info): - # FIXME filter this queryset based on permissions - return queryset + if not info.context.user.has_perm("lesrooster.view_timegrid_rule"): + return get_objects_for_user(info.context.user, "lesrooster.view_timegrid", queryset) + return [] class TimeGridCreateMutation(DjangoCreateMutation): class Meta: model = TimeGrid - permissions = ("lesrooster.create_time_grid_rule",) - exclude = ("managed_by_app_label",) + permissions = ("lesrooster.create_timegrid_rule",) + only_fields = ( + "validity_range", + "group", + ) class TimeGridDeleteMutation(DeleteMutation): klass = TimeGrid - permission_required = "lesrooster.delete_timegrid" + permission_required = "lesrooster.delete_timegrid_rule" class TimeGridBatchDeleteMutation(PermissionBatchDeleteMixin, DjangoBatchDeleteMutation): class Meta: model = TimeGrid - permissions = ("lesrooster.delete_timegrid",) + permissions = ("lesrooster.delete_timegrid_rule",) class TimeGridBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutation): class Meta: model = TimeGrid - permissions = ("lesrooster.change_timegrid",) - exclude = ("managed_by_app_label",) + permissions = ("lesrooster.edit_timegrid_rule",) + only_fields = ( + "validity_range", + "group", + ) diff --git a/aleksis/apps/lesrooster/schema/timebound_course_config.py b/aleksis/apps/lesrooster/schema/timebound_course_config.py index d5710da79106f3337f549b25e449c92c05fdf8a5..dc5e37487829762633197d80cd079fed42d8cb5e 100644 --- a/aleksis/apps/lesrooster/schema/timebound_course_config.py +++ b/aleksis/apps/lesrooster/schema/timebound_course_config.py @@ -1,5 +1,4 @@ import graphene -from graphene_django import DjangoListField from graphene_django.types import DjangoObjectType from graphene_django_cud.mutations import ( DjangoBatchCreateMutation, @@ -31,7 +30,13 @@ class TimeboundCourseConfigType(PermissionsTypeMixin, DjangoFilterMixin, DjangoO @classmethod def get_queryset(cls, queryset, info): - return queryset # FIXME filter this queryset based on permissions + if not info.context.user.has_perm("lesrostter.view_timeboundcourseconfig_rule"): + return get_objects_for_user( + info.context.user, + "lesrooster.view_timeboundcourseconfig", + root.lr_timebound_course_configs.all(), + ) + return queryset @staticmethod def resolve_name(root, info, **kwargs): @@ -66,11 +71,12 @@ class LesroosterExtendedCourseType(CourseType): @staticmethod def resolve_lr_timebound_course_configs(root, info, **kwargs): - return get_objects_for_user( - info.context.user, - "lesrooster.view_timeboundcourseconfig", - root.lr_timebound_course_configs.all(), - ) + if not info.context.user.has_perm("lesrostter.view_timeboundcourseconfig_rule"): + return get_objects_for_user( + info.context.user, + "lesrooster.view_timeboundcourseconfig", + root.lr_timebound_course_configs.all(), + ) class LesroosterExtendedSubjectType(SubjectType): @@ -84,23 +90,23 @@ class TimeboundCourseConfigCreateMutation(DjangoCreateMutation): class Meta: model = TimeboundCourseConfig fields = ("id", "course", "validity_range", "lesson_quota", "teachers") - permissions = ("lesrooster.create_timebound_course_config_rule",) + permissions = ("lesrooster.create_timeboundcourseconfig_rule",) class TimeboundCourseConfigBatchCreateMutation(DjangoBatchCreateMutation): class Meta: model = TimeboundCourseConfig fields = ("id", "course", "validity_range", "lesson_quota", "teachers") - permissions = ("lesrooster.create_timebound_course_config_rule",) + permissions = ("lesrooster.create_timeboundcourseconfig_rule",) class TimeboundCourseConfigDeleteMutation(DeleteMutation): klass = TimeboundCourseConfig - permission_required = "lesrooster.delete_timeboundcourseconfig" + permission_required = "lesrooster.delete_timeboundcourseconfig_rule" class TimeboundCourseConfigBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutation): class Meta: model = TimeboundCourseConfig fields = ("id", "course", "validity_range", "lesson_quota", "teachers") - permissions = ("lesrooster.change_timeboundcourseconfig",) + permissions = ("lesrooster.change_timeboundcourseconfig_rule",) diff --git a/aleksis/apps/lesrooster/schema/validity_range.py b/aleksis/apps/lesrooster/schema/validity_range.py index 0d38943ee4c6ed9cb48825863b6e7f2e16d915e5..526a2a61272ca24c2d033566a72b618fafead87c 100644 --- a/aleksis/apps/lesrooster/schema/validity_range.py +++ b/aleksis/apps/lesrooster/schema/validity_range.py @@ -4,6 +4,7 @@ from graphene_django_cud.mutations import ( DjangoBatchPatchMutation, DjangoCreateMutation, ) +from guardian.shortcuts import get_objects_for_user from aleksis.core.schema.base import ( DeleteMutation, @@ -31,30 +32,33 @@ class ValidityRangeType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp @classmethod def get_queryset(cls, queryset, info): - # FIXME filter this queryset based on permissions + if not info.context.user.has_perm("lesrooster.view_validityrange_rule"): + return get_objects_for_user( + info.context.user, "lesrooster.view_validityrange", queryset + ) return queryset class ValidityRangeCreateMutation(DjangoCreateMutation): class Meta: model = ValidityRange - permissions = ("lesrooster.create_validity_range_rule",) - exclude = ("managed_by_app_label",) + permissions = ("lesrooster.create_validityrange_rule",) + only_fields = ("school_term", "name", "date_start", "date_end", "status", "time_grids") class ValidityRangeDeleteMutation(DeleteMutation): klass = ValidityRange - permission_required = "lesrooster.delete_validityrange" + permission_required = "lesrooster.delete_validityrange_rule" class ValidityRangeBatchDeleteMutation(PermissionBatchDeleteMixin, DjangoBatchDeleteMutation): class Meta: model = ValidityRange - permissions = ("lesrooster.delete_validityrange",) + permissions = ("lesrooster.delete_validityrange_rule",) class ValidityRangeBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutation): class Meta: model = ValidityRange - permissions = ("lesrooster.change_validityrange",) - exclude = ("managed_by_app_label",) + permissions = ("lesrooster.edit_validityrange_rule",) + only_fields = ("school_term", "name", "date_start", "date_end", "status", "time_grids") diff --git a/aleksis/apps/lesrooster/util/signal_handlers.py b/aleksis/apps/lesrooster/util/signal_handlers.py index 5ef815e552658636ec97796e5df904e27b5fc023..d4e7652460ab5529c014bd70b26eb6e3b43efb34 100644 --- a/aleksis/apps/lesrooster/util/signal_handlers.py +++ b/aleksis/apps/lesrooster/util/signal_handlers.py @@ -1,6 +1,7 @@ -from django.db.models import Q import logging +from django.db.models import Q + def post_save_handler(sender, instance, created, **kwargs): """Sync the instance with Chronos after it has been saved.""" @@ -25,7 +26,8 @@ def pre_delete_handler(sender, instance, **kwargs): del_obj = instance.lesson_event.delete() elif hasattr(instance, "supervision_event"): logging.debug( - f"Delete supervision event {instance.supervision_event} after deletion of lesson {instance}" + f"Delete supervision event {instance.supervision_event} " + f"after deletion of lesson {instance}" ) del_obj = instance.supervision_event.delete()