From 4ffcf5baf897c066a96fd7ece820be8b82c25b9b Mon Sep 17 00:00:00 2001 From: Julien Cougnaud Date: Tue, 17 Sep 2024 17:46:46 +0200 Subject: [PATCH] [OS-1245] Fix the way to compute the admission injections of the cv experiences --- .../domain/service/profil_candidat.py | 6 +- .../curriculum/global/test_continuing.py | 213 +++++++++++++++++- views/common/form_tabs/curriculum.py | 79 ++++--- 3 files changed, 254 insertions(+), 44 deletions(-) diff --git a/infrastructure/admission/domain/service/profil_candidat.py b/infrastructure/admission/domain/service/profil_candidat.py index c6bd6f9ec..e63886824 100644 --- a/infrastructure/admission/domain/service/profil_candidat.py +++ b/infrastructure/admission/domain/service/profil_candidat.py @@ -409,7 +409,9 @@ def _get_academic_experiences_dtos( educational_experience_years = educational_experience_years.annotate( injecte_par_admission=Exists( AdmissionEPCInjection.objects.filter( - admission__uuid=OuterRef('educational_experience__valuated_from_admission__uuid'), + admission__admissioneducationalvaluatedexperiences__educationalexperience_id=OuterRef( + 'educational_experience__uuid' + ), type=EPCInjectionType.DEMANDE.name, status__in=AdmissionEPCInjectionStatus.blocking_statuses_for_experience(), ) @@ -645,7 +647,7 @@ def get_experiences_non_academiques( ), injecte_par_admission=Exists( AdmissionEPCInjection.objects.filter( - admission__uuid=OuterRef('valuated_from_admission__uuid'), + admission__admissionprofessionalvaluatedexperiences__professionalexperience_id=OuterRef('uuid'), type=EPCInjectionType.DEMANDE.name, status__in=AdmissionEPCInjectionStatus.blocking_statuses_for_experience(), ) diff --git a/tests/views/common/detail_tabs/curriculum/global/test_continuing.py b/tests/views/common/detail_tabs/curriculum/global/test_continuing.py index 56e53f385..56adba2b3 100644 --- a/tests/views/common/detail_tabs/curriculum/global/test_continuing.py +++ b/tests/views/common/detail_tabs/curriculum/global/test_continuing.py @@ -30,9 +30,12 @@ from django.shortcuts import resolve_url from django.test import TestCase -from admission.contrib.models import ContinuingEducationAdmission +from admission.contrib.models import ContinuingEducationAdmission, EPCInjection as AdmissionEPCInjection +from admission.contrib.models.epc_injection import EPCInjectionType, EPCInjectionStatus as AdmissionEPCInjectionStatus from admission.ddd import FR_ISO_CODE from admission.ddd.admission.doctorat.preparation.dtos.curriculum import CurriculumAdmissionDTO + +from admission.services.injection_epc.injection_dossier import InjectionEPCAdmission from admission.tests.factories.continuing_education import ( ContinuingEducationAdmissionFactory, ) @@ -62,6 +65,11 @@ from reference.tests.factories.diploma_title import DiplomaTitleFactory from reference.tests.factories.language import LanguageFactory from reference.tests.factories.superior_non_university import SuperiorNonUniversityFactory +from osis_profile.models.epc_injection import ( + EPCInjection as CurriculumEPCInjection, + ExperienceType, + EPCInjectionStatus as CurriculumEPCInjectionStatus, +) @freezegun.freeze_time('2024-06-01') @@ -191,8 +199,8 @@ def test_get_curriculum_with_academic_experience(self): is_102_change_of_course=True, ) - AdmissionEducationalValuatedExperiencesFactory( - baseadmission=self.continuing_admission, + other_valuation = AdmissionEducationalValuatedExperiencesFactory( + baseadmission=ContinuingEducationAdmissionFactory(candidate=self.continuing_admission.candidate), educationalexperience=educational_experience, ) @@ -233,7 +241,7 @@ def test_get_curriculum_with_academic_experience(self): self.assertEqual(experience.cycle_formation, '') self.assertEqual(experience.type_enseignement, educational_experience.study_system) self.assertEqual(experience.injectee, False) - self.assertEqual(experience.valorisee_par_admissions, [self.continuing_admission.uuid]) + self.assertEqual(experience.valorisee_par_admissions, [other_valuation.baseadmission.uuid]) self.assertEqual(experience.identifiant_externe, None) self.assertEqual(len(experience.annees), 1) @@ -295,6 +303,105 @@ def test_get_curriculum_with_academic_experience(self): self.assertEqual(experience.cycle_formation, self.other_diploma.cycle) self.assertEqual(experience.est_autre_formation, False) + self.assertEqual(len(experience.annees), 1) + + # With valuation by the current admission + valuation = AdmissionEducationalValuatedExperiencesFactory( + baseadmission=self.continuing_admission, + educationalexperience=educational_experience, + ) + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + + curriculum = response.context['curriculum'] + + self.assertEqual(len(curriculum.experiences_academiques), 1) + + experience = curriculum.experiences_academiques[0] + + self.assertEqual(experience.uuid, educational_experience.uuid) + self.assertCountEqual( + experience.valorisee_par_admissions, + [self.continuing_admission.uuid, other_valuation.baseadmission.uuid], + ) + self.assertEqual(len(experience.annees), 1) + + # Admission injection + admission_injection = AdmissionEPCInjection( + admission=self.continuing_admission, + type=EPCInjectionType.SIGNALETIQUE.name, + status=AdmissionEPCInjectionStatus.OK.name, + ) + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + + curriculum = response.context['curriculum'] + + self.assertEqual(len(curriculum.experiences_academiques), 1) + + experience = curriculum.experiences_academiques[0] + + self.assertEqual(experience.uuid, educational_experience.uuid) + self.assertEqual(experience.injectee, False) + self.assertEqual(len(experience.annees), 1) + + admission_injection.type = EPCInjectionType.DEMANDE.name + admission_injection.save() + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + + curriculum = response.context['curriculum'] + + self.assertEqual(len(curriculum.experiences_academiques), 1) + + experience = curriculum.experiences_academiques[0] + + self.assertEqual(experience.uuid, educational_experience.uuid) + self.assertEqual(experience.injectee, True) + self.assertEqual(len(experience.annees), 1) + + admission_injection.delete() + + # Curriculum injection + curriculum_injection = CurriculumEPCInjection( + person=self.continuing_admission.candidate, + experience_uuid=educational_experience.uuid, + type_experience=ExperienceType.ACADEMIC.name, + status=CurriculumEPCInjectionStatus.ERROR.name, + ) + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + + curriculum = response.context['curriculum'] + + self.assertEqual(len(curriculum.experiences_academiques), 1) + + experience = curriculum.experiences_academiques[0] + + self.assertEqual(experience.uuid, educational_experience.uuid) + self.assertEqual(experience.injectee, False) + self.assertEqual(len(experience.annees), 1) + + curriculum_injection.status = CurriculumEPCInjectionStatus.OK.name + curriculum_injection.save() + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + + curriculum = response.context['curriculum'] + + self.assertEqual(len(curriculum.experiences_academiques), 1) + + experience = curriculum.experiences_academiques[0] + + self.assertEqual(experience.uuid, educational_experience.uuid) + self.assertEqual(experience.injectee, True) + self.assertEqual(len(experience.annees), 1) + def test_get_curriculum_with_non_academic_experience(self): self.client.force_login(user=self.sic_manager_user) @@ -310,8 +417,8 @@ def test_get_curriculum_with_non_academic_experience(self): activity='My custom activity', ) - AdmissionProfessionalValuatedExperiencesFactory( - baseadmission=self.continuing_admission, + other_valuation = AdmissionProfessionalValuatedExperiencesFactory( + baseadmission=ContinuingEducationAdmissionFactory(candidate=self.continuing_admission.candidate), professionalexperience=non_academic_experience, ) @@ -333,5 +440,97 @@ def test_get_curriculum_with_non_academic_experience(self): self.assertEqual(experience.secteur, non_academic_experience.sector) self.assertEqual(experience.autre_activite, non_academic_experience.activity) self.assertEqual(experience.injectee, False) - self.assertEqual(experience.valorisee_par_admissions, [self.continuing_admission.uuid]) + self.assertEqual(experience.valorisee_par_admissions, [other_valuation.baseadmission.uuid]) self.assertEqual(experience.identifiant_externe, None) + + # With valuation by the current admission + valuation = AdmissionProfessionalValuatedExperiencesFactory( + baseadmission=self.continuing_admission, + professionalexperience=non_academic_experience, + ) + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + + curriculum = response.context['curriculum'] + + self.assertEqual(len(curriculum.experiences_non_academiques), 1) + + experience = curriculum.experiences_non_academiques[0] + + self.assertEqual(experience.uuid, non_academic_experience.uuid) + self.assertCountEqual( + experience.valorisee_par_admissions, + [self.continuing_admission.uuid, other_valuation.baseadmission.uuid], + ) + + # Admission injection + admission_injection = AdmissionEPCInjection( + admission=self.continuing_admission, + type=EPCInjectionType.SIGNALETIQUE.name, + status=AdmissionEPCInjectionStatus.OK.name, + ) + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + + curriculum = response.context['curriculum'] + + self.assertEqual(len(curriculum.experiences_non_academiques), 1) + + experience = curriculum.experiences_non_academiques[0] + + self.assertEqual(experience.uuid, non_academic_experience.uuid) + self.assertEqual(experience.injectee, False) + + admission_injection.type = EPCInjectionType.DEMANDE.name + admission_injection.save() + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + + curriculum = response.context['curriculum'] + + self.assertEqual(len(curriculum.experiences_non_academiques), 1) + + experience = curriculum.experiences_non_academiques[0] + + self.assertEqual(experience.uuid, non_academic_experience.uuid) + self.assertEqual(experience.injectee, True) + + admission_injection.delete() + + # Curriculum injection + curriculum_injection = CurriculumEPCInjection( + person=self.continuing_admission.candidate, + experience_uuid=non_academic_experience.uuid, + type_experience=ExperienceType.ACADEMIC.name, + status=CurriculumEPCInjectionStatus.ERROR.name, + ) + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + + curriculum = response.context['curriculum'] + + self.assertEqual(len(curriculum.experiences_non_academiques), 1) + + experience = curriculum.experiences_non_academiques[0] + + self.assertEqual(experience.uuid, non_academic_experience.uuid) + self.assertEqual(experience.injectee, False) + + curriculum_injection.status = CurriculumEPCInjectionStatus.OK.name + curriculum_injection.save() + + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + + curriculum = response.context['curriculum'] + + self.assertEqual(len(curriculum.experiences_non_academiques), 1) + + experience = curriculum.experiences_non_academiques[0] + + self.assertEqual(experience.uuid, non_academic_experience.uuid) + self.assertEqual(experience.injectee, True) diff --git a/views/common/form_tabs/curriculum.py b/views/common/form_tabs/curriculum.py index a3a90123d..e15397772 100644 --- a/views/common/form_tabs/curriculum.py +++ b/views/common/form_tabs/curriculum.py @@ -103,13 +103,9 @@ def educational_experience_filter_uuid(self): @property def educational_experience_annotations(self): return { - 'valuated_from_admissions': ArrayAgg( - 'valuated_from_admission__uuid', - filter=Q(valuated_from_admission__isnull=False), - ), 'admission_injection': Exists( AdmissionEPCInjection.objects.filter( - admission__uuid=OuterRef('valuated_from_admission__uuid'), + admission__admissioneducationalvaluatedexperiences__educationalexperience_id=OuterRef('uuid'), type=EPCInjectionType.DEMANDE.name, status__in=AdmissionEPCInjectionStatus.blocking_statuses_for_experience(), ) @@ -188,13 +184,9 @@ class CurriculumNonEducationalExperienceFormView( @property def non_educational_experience_annotations(self): return { - 'valuated_from_admissions': ArrayAgg( - 'valuated_from_admission__uuid', - filter=Q(valuated_from_admission__isnull=False), - ), 'admission_injection': Exists( AdmissionEPCInjection.objects.filter( - admission__uuid=OuterRef('valuated_from_admission__uuid'), + admission__admissionprofessionalvaluatedexperiences__professionalexperience_id=OuterRef('uuid'), type=EPCInjectionType.DEMANDE.name, status__in=AdmissionEPCInjectionStatus.blocking_statuses_for_experience(), ) @@ -273,31 +265,6 @@ class CurriculumBaseDeleteView(LoadDossierViewMixin, DeleteEducationalExperience permission_required = 'admission.delete_admission_curriculum' template_name = 'admission/empty_template.html' - def get_queryset(self): - return ( - super() - .get_queryset() - .annotate( - valuated_from_admissions=ArrayAgg( - 'valuated_from_admission__uuid', - filter=Q(valuated_from_admission__isnull=False), - ), - admission_injection=Exists( - AdmissionEPCInjection.objects.filter( - admission__uuid=OuterRef('valuated_from_admission__uuid'), - type=EPCInjectionType.DEMANDE.name, - status__in=AdmissionEPCInjectionStatus.blocking_statuses_for_experience(), - ) - ), - cv_injection=Exists( - CurriculumEPCInjection.objects.filter( - experience_uuid=self.experience_id, - status__in=CurriculumEPCInjectionStatus.blocking_statuses_for_experience(), - ), - ), - ) - ) - def has_permission(self): self.object = self.get_object() return super().has_permission() and not any( @@ -361,6 +328,27 @@ def get_success_url(self): class CurriculumEducationalExperienceDeleteView(CurriculumBaseDeleteView, DeleteExperienceAcademiqueView): urlpatterns = {'educational_delete': 'educational//delete'} + def get_queryset(self): + return ( + super() + .get_queryset() + .annotate( + admission_injection=Exists( + AdmissionEPCInjection.objects.filter( + admission__admissioneducationalvaluatedexperiences__educationalexperience_id=OuterRef('uuid'), + type=EPCInjectionType.DEMANDE.name, + status__in=AdmissionEPCInjectionStatus.blocking_statuses_for_experience(), + ) + ), + cv_injection=Exists( + CurriculumEPCInjection.objects.filter( + experience_uuid=self.experience_id, + status__in=CurriculumEPCInjectionStatus.blocking_statuses_for_experience(), + ), + ), + ) + ) + def traitement_specifique(self, experience_uuid: UUID, experiences_supprimees: List[UUID]): pass @@ -377,6 +365,27 @@ def get_failure_url(self): class CurriculumNonEducationalExperienceDeleteView(CurriculumBaseDeleteView, DeleteExperienceNonAcademiqueView): urlpatterns = {'non_educational_delete': 'non_educational//delete'} + def get_queryset(self): + return ( + super() + .get_queryset() + .annotate( + admission_injection=Exists( + AdmissionEPCInjection.objects.filter( + admission__admissionprofessionalvaluatedexperiences__professionalexperience_id=OuterRef('uuid'), + type=EPCInjectionType.DEMANDE.name, + status__in=AdmissionEPCInjectionStatus.blocking_statuses_for_experience(), + ) + ), + cv_injection=Exists( + CurriculumEPCInjection.objects.filter( + experience_uuid=self.experience_id, + status__in=CurriculumEPCInjectionStatus.blocking_statuses_for_experience(), + ), + ), + ) + ) + def traitement_specifique(self, experience_uuid: UUID, experiences_supprimees: List[UUID]): pass