Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] [feature/OS-1178 => DEV] IUFC : conflits de contexte #2071

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
from base.models.enums.education_group_categories import Categories
from base.models.person import Person
from base.models.person_merge_proposal import PersonMergeStatus
from education_group.auth.scope import Scope
from admission.auth.scope import Scope
from education_group.contrib.admin import EducationGroupRoleModelAdmin
from epc.models.inscription_programme_cycle import InscriptionProgrammeCycle
from osis_profile.models import EducationalExperience, ProfessionalExperience
Expand Down Expand Up @@ -684,15 +684,15 @@ def queryset(self, request, queryset):
| Q(
checklist__current__financabilite__status='GEST_REUSSITE',
checklist__current__financanbilite__extra__reussite='financable',
generaleducationadmission__financability_rule=''
generaleducationadmission__financability_rule='',
)
| Q(
checklist__current__financabilite__status='GEST_REUSSITE',
generaleducationadmission__financability_rule_established_on__isnull=True
generaleducationadmission__financability_rule_established_on__isnull=True,
)
| Q(
checklist__current__financabilite__status='GEST_REUSSITE',
generaleducationadmission__financability_rule_established_by_id__isnull=True
generaleducationadmission__financability_rule_established_by_id__isnull=True,
),
generaleducationadmission__isnull=False,
then=Value(False),
Expand Down
2 changes: 1 addition & 1 deletion auth/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
# Training choice
'training-choice': 'admission.change_admission_training_choice',
# Previous experience
'curriculum': 'admission.change_admission_curriculum',
'curriculum': 'admission.change_admission_global_curriculum',
'educational': '',
'educational_create': '',
'non_educational': '',
Expand Down
44 changes: 43 additions & 1 deletion auth/predicates/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@
from waffle import switch_is_active

from admission.contrib.models import DoctorateAdmission, GeneralEducationAdmission
from admission.constants import CONTEXT_GENERAL, CONTEXT_DOCTORATE, CONTEXT_CONTINUING
from admission.contrib.models.base import BaseAdmission
from admission.contrib.models.epc_injection import EPCInjectionStatus
from base.models.person_creation_ticket import PersonTicketCreation, PersonTicketCreationStatus
from admission.auth.scope import Scope
from osis_role.errors import predicate_failed_msg


Expand All @@ -43,6 +44,28 @@ def is_admission_request_author(self, user: User, obj: BaseAdmission):
return obj.candidate == user.person


@predicate(bind=True)
@predicate_failed_msg(
message=_(
"This action cannot be performed as an admission or an internal experience is related to a general education."
),
)
def candidate_has_other_general_admissions(self, user: User, obj: BaseAdmission):
return bool(obj.other_candidate_trainings[CONTEXT_GENERAL])


@predicate(bind=True)
@predicate_failed_msg(
message=_(
"This action cannot be performed as an admission or an internal experience is related to a general "
"or a doctorate education."
),
)
def candidate_has_other_doctorate_or_general_admissions(self, user: User, obj: BaseAdmission):
other_admissions = obj.other_candidate_trainings
return bool(other_admissions[CONTEXT_GENERAL]) or bool(other_admissions[CONTEXT_DOCTORATE])


@predicate(bind=True)
@predicate_failed_msg(message=_("Another admission has been submitted."))
def does_not_have_a_submitted_admission(self, user: User, obj: DoctorateAdmission):
Expand Down Expand Up @@ -109,6 +132,25 @@ def is_entity_manager(self, user: User, obj: BaseAdmission):
return obj.training.management_entity_id in getattr(user, cache_key)


@predicate(bind=True)
def is_scoped_entity_manager(self, user: User, obj: BaseAdmission):
"""
Check that the user is a manager of the admission training management entity with the correct scope.
"""
scope = {
CONTEXT_GENERAL: Scope.GENERAL,
CONTEXT_DOCTORATE: Scope.DOCTORAT,
CONTEXT_CONTINUING: Scope.IUFC,
}[obj.admission_context]

cache_key = _build_queryset_cache_key_from_role_qs(self.context['role_qs'], f'entities_ids_by_scope_{scope.name}')

if not hasattr(user, cache_key):
setattr(user, cache_key, self.context['role_qs'].filter(scopes__contains=[scope.name]).get_entities_ids())

return obj.training.management_entity_id in getattr(user, cache_key)


def has_education_group_of_types(*education_group_types):
name = 'has_education_group_of_types:%s' % ','.join(education_group_types)

Expand Down
4 changes: 3 additions & 1 deletion auth/predicates/continuing.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ def is_continuing(self, user: User, obj: ContinuingEducationAdmission):
@predicate(bind=True)
@predicate_failed_msg(message=_('The proposition must be in draft form to realize this action.'))
def in_progress(self, user: User, obj: ContinuingEducationAdmission):
return obj.status == ChoixStatutPropositionContinue.EN_BROUILLON.name
return (
isinstance(obj, ContinuingEducationAdmission) and obj.status == ChoixStatutPropositionContinue.EN_BROUILLON.name
)


@predicate(bind=True)
Expand Down
2 changes: 1 addition & 1 deletion auth/predicates/doctorate.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
@predicate(bind=True)
@predicate_failed_msg(message=_("Invitations must have been sent"))
def in_progress(self, user: User, obj: DoctorateAdmission):
return obj.status == ChoixStatutPropositionDoctorale.EN_BROUILLON.name
return isinstance(obj, DoctorateAdmission) and obj.status == ChoixStatutPropositionDoctorale.EN_BROUILLON.name


@predicate(bind=True)
Expand Down
2 changes: 1 addition & 1 deletion auth/predicates/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
@predicate(bind=True)
@predicate_failed_msg(message=_('The proposition must be in draft form to realize this action.'))
def in_progress(self, user: User, obj: GeneralEducationAdmission):
return obj.status == ChoixStatutPropositionGenerale.EN_BROUILLON.name
return isinstance(obj, GeneralEducationAdmission) and obj.status == ChoixStatutPropositionGenerale.EN_BROUILLON.name


@predicate(bind=True)
Expand Down
56 changes: 46 additions & 10 deletions auth/roles/central_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@
from admission.auth.predicates.common import (
has_scope,
is_debug,
is_entity_manager,
is_entity_manager as is_entity_manager_without_scope,
is_scoped_entity_manager,
is_sent_to_epc,
pending_digit_ticket_response,
past_experiences_checklist_tab_is_not_sufficient,
candidate_has_other_doctorate_or_general_admissions,
candidate_has_other_general_admissions,
)
from education_group.auth.scope import Scope
from admission.auth.scope import Scope
from osis_role.contrib.models import EntityRoleModel


Expand All @@ -59,11 +62,19 @@ class Meta:
verbose_name_plural = _("Role: Central managers")
group_name = "admission_central_managers"

@classmethod
def rule_set_without_scope(cls):
return cls.common_rule_set(is_entity_manager_without_scope)

@classmethod
def rule_set(cls):
return cls.common_rule_set(is_scoped_entity_manager)

@classmethod
def common_rule_set(cls, is_entity_manager: callable):
ruleset = {
# Listings
'admission.view_enrolment_applications': has_scope(Scope.ALL),
'admission.view_enrolment_applications': has_scope(Scope.GENERAL),
'admission.view_doctorate_enrolment_applications': has_scope(Scope.DOCTORAT),
'admission.view_continuing_enrolment_applications': has_scope(Scope.IUFC),
# Access a single application
Expand All @@ -74,14 +85,26 @@ def rule_set(cls):
'admission.appose_sic_notice': is_entity_manager,
'admission.view_admission_person': is_entity_manager,
'admission.change_admission_person': is_entity_manager
& (general.in_sic_status | continuing.in_manager_status | doctorate.in_sic_status
| general.in_progress | continuing.in_progress | doctorate.in_progress)
& (
(general.in_sic_status | general.in_progress)
| (
(continuing.in_manager_status | continuing.in_progress)
& ~candidate_has_other_doctorate_or_general_admissions
)
| ((doctorate.in_sic_status | doctorate.in_progress) & ~candidate_has_other_general_admissions)
)
& ~is_sent_to_epc
& ~pending_digit_ticket_response,
'admission.view_admission_coordinates': is_entity_manager,
'admission.change_admission_coordinates': is_entity_manager
& (general.in_sic_status | continuing.in_manager_status | doctorate.in_sic_status
| general.in_progress | continuing.in_progress | doctorate.in_progress)
& (
general.in_sic_status
| continuing.in_manager_status
| doctorate.in_sic_status
| general.in_progress
| continuing.in_progress
| doctorate.in_progress
)
& ~is_sent_to_epc
& ~pending_digit_ticket_response,
'admission.view_admission_training_choice': is_entity_manager,
Expand All @@ -93,14 +116,27 @@ def rule_set(cls):
'admission.change_admission_languages': is_entity_manager & doctorate.in_sic_status & ~is_sent_to_epc,
'admission.view_admission_secondary_studies': is_entity_manager,
'admission.change_admission_secondary_studies': is_entity_manager
& (general.in_sic_status | continuing.in_manager_status)
& (
general.in_sic_status
| (continuing.in_manager_status & ~candidate_has_other_doctorate_or_general_admissions)
)
& ~is_sent_to_epc,
'admission.view_admission_curriculum': is_entity_manager,
'admission.change_admission_curriculum': is_entity_manager
'admission.change_admission_global_curriculum': is_entity_manager
& (general.in_sic_status | continuing.in_manager_status | doctorate.in_sic_status)
& ~is_sent_to_epc,
'admission.change_admission_curriculum': is_entity_manager
& (
general.in_sic_status
| (continuing.in_manager_status & ~candidate_has_other_doctorate_or_general_admissions)
| doctorate.in_sic_status
)
& ~is_sent_to_epc,
'admission.delete_admission_curriculum': is_entity_manager
& (general.in_sic_status | continuing.in_manager_status | doctorate.in_sic_status)
& (
general.in_sic_status
| (continuing.in_manager_status & ~candidate_has_other_doctorate_or_general_admissions)
)
& ~is_sent_to_epc,
'admission.view_admission_project': is_entity_manager,
'admission.change_admission_project': is_entity_manager & doctorate.in_sic_status & ~is_sent_to_epc,
Expand Down
18 changes: 14 additions & 4 deletions auth/roles/program_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
is_sent_to_epc,
pending_digit_ticket_response,
past_experiences_checklist_tab_is_not_sufficient,
candidate_has_other_doctorate_or_general_admissions,
)
from admission.auth.predicates import general, continuing, doctorate
from admission.infrastructure.admission.domain.service.annee_inscription_formation import (
Expand Down Expand Up @@ -86,7 +87,8 @@ def rule_set(cls):
'admission.change_admission_person': is_part_of_education_group
& continuing.in_manager_status
& ~is_sent_to_epc
& ~pending_digit_ticket_response,
& ~pending_digit_ticket_response
& ~candidate_has_other_doctorate_or_general_admissions,
'admission.view_admission_coordinates': is_part_of_education_group,
'admission.change_admission_coordinates': is_part_of_education_group
& continuing.in_manager_status
Expand All @@ -95,18 +97,26 @@ def rule_set(cls):
'admission.view_admission_secondary_studies': is_part_of_education_group,
'admission.change_admission_secondary_studies': is_part_of_education_group
& continuing.in_manager_status
& ~is_sent_to_epc,
& ~is_sent_to_epc
& ~candidate_has_other_doctorate_or_general_admissions,
'admission.view_admission_languages': is_part_of_education_group,
'admission.change_admission_languages': is_part_of_education_group
& doctorate.in_fac_status
& ~is_sent_to_epc,
'admission.view_admission_curriculum': is_part_of_education_group,
'admission.change_admission_curriculum': is_part_of_education_group
'admission.change_admission_global_curriculum': is_part_of_education_group
& (continuing.in_manager_status | doctorate.in_fac_status)
& ~is_sent_to_epc,
'admission.change_admission_curriculum': is_part_of_education_group
& (
(continuing.in_manager_status & ~candidate_has_other_doctorate_or_general_admissions)
| doctorate.in_fac_status
)
& ~is_sent_to_epc,
'admission.delete_admission_curriculum': is_part_of_education_group
& continuing.in_manager_status
& ~is_sent_to_epc,
& ~is_sent_to_epc
& ~candidate_has_other_doctorate_or_general_admissions,
# Project
'admission.view_admission_project': is_part_of_education_group,
'admission.view_admission_cotutelle': is_part_of_education_group,
Expand Down
2 changes: 1 addition & 1 deletion auth/roles/sic_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Meta:
@classmethod
def rule_set(cls):
ruleset = {
**CentralManager.rule_set(),
**CentralManager.rule_set_without_scope(),
# Listings
'admission.checklist_change_sic_decision': rules.always_allow & ~is_sent_to_epc,
'admission.view_enrolment_applications': rules.always_allow,
Expand Down
32 changes: 32 additions & 0 deletions auth/scope.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
##############################################################################
#
# OSIS stands for Open Student Information System. It's an application
# designed to manage the core business of higher education institutions,
# such as universities, faculties, institutes and professional schools.
# The core business involves the administration of students, teachers,
# courses, programs and so on.
#
# Copyright (C) 2015-2024 Université catholique de Louvain (http://www.uclouvain.be)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of this license - GNU General Public License - is available
# at the root of the source code of this program. If not,
# see http://www.gnu.org/licenses/.
#
##############################################################################
from base.models.utils.utils import ChoiceEnum


class Scope(ChoiceEnum):
GENERAL = 'GENERAL'
IUFC = 'IUFC'
DOCTORAT = 'DOCTORAT'
Loading