diff --git a/contrib/forms/cotutelle.py b/contrib/forms/cotutelle.py index dfe43acb..c4bcc87e 100644 --- a/contrib/forms/cotutelle.py +++ b/contrib/forms/cotutelle.py @@ -120,7 +120,7 @@ def clean(self): cleaned_data = super().clean() if cleaned_data.get("cotutelle") == "YES": - for field in ['motivation', 'institution', 'demande_ouverture']: + for field in ['motivation', 'demande_ouverture']: if not cleaned_data.get(field): self.add_error(field, FIELD_REQUIRED_MESSAGE) if cleaned_data.get('institution_fwb') is None: @@ -130,4 +130,6 @@ def clean(self): self.add_error('autre_institution_nom', FIELD_REQUIRED_MESSAGE) if not cleaned_data.get('autre_institution_adresse'): self.add_error('autre_institution_adresse', FIELD_REQUIRED_MESSAGE) + elif not cleaned_data.get('institution'): + self.add_error('institution', FIELD_REQUIRED_MESSAGE) return cleaned_data diff --git a/contrib/views/doctorate/form_tabs/cotutelle.py b/contrib/views/doctorate/form_tabs/cotutelle.py index 4bd5fe3b..e303c99b 100644 --- a/contrib/views/doctorate/form_tabs/cotutelle.py +++ b/contrib/views/doctorate/form_tabs/cotutelle.py @@ -68,6 +68,8 @@ def prepare_data(self, data: dict): convention=[], autres_documents=[], ) + if not data['institution']: + data['institution'] = "" del data['cotutelle'] data['uuid'] = str(self.kwargs['pk']) return data diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 5a10a69c..ab84cf34 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -2520,9 +2520,6 @@ msgstr "" msgid "Previous experience" msgstr "" -msgid "Previous research experience" -msgstr "" - msgid "Private" msgstr "" diff --git a/locale/fr_BE/LC_MESSAGES/django.po b/locale/fr_BE/LC_MESSAGES/django.po index 5318963b..f0a066a9 100644 --- a/locale/fr_BE/LC_MESSAGES/django.po +++ b/locale/fr_BE/LC_MESSAGES/django.po @@ -2873,9 +2873,6 @@ msgstr "Épreuves de confirmation passées" msgid "Previous experience" msgstr "Parcours antérieur" -msgid "Previous research experience" -msgstr "Expérience précédente de recherche" - msgid "Private" msgstr "Privé" diff --git a/templates/admission/doctorate/details/cotutelle.html b/templates/admission/doctorate/details/cotutelle.html index cbd711ea..f3955cf9 100644 --- a/templates/admission/doctorate/details/cotutelle.html +++ b/templates/admission/doctorate/details/cotutelle.html @@ -8,7 +8,7 @@ * The core business involves the administration of students, teachers, * courses, programs and so on. * -* Copyright (C) 2015-2022 Université catholique de Louvain (http://www.uclouvain.be) +* 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 @@ -31,7 +31,13 @@ {% trans "Choice of joint supervision is not yet defined" %} {% elif cotutelle.cotutelle %} {% field_data _("Is it a Wallonia-Brussels Federation institution?") cotutelle.institution_fwb|yesno %} - {% field_data _("Partner institution") cotutelle.institution %} + {% get_superior_institute_name cotutelle.institution as institution %} + {% if institution %} + {% field_data _("Partner institution") institution %} + {% elif cotutelle.autre_institution %} + {% field_data _("Institute name") cotutelle.autre_institution_nom %} + {% field_data _("Institute address") cotutelle.autre_institution_adresse %} + {% endif %} {% field_data _("Motivation for joint supervision") cotutelle.motivation|linebreaks %} {% field_data _("Joint supervision request") cotutelle.demande_ouverture %} {% field_data _("Joint supervision agreement") cotutelle.convention %} diff --git a/templates/admission/doctorate/details/project.html b/templates/admission/doctorate/details/project.html index 50db8d5d..5145b557 100644 --- a/templates/admission/doctorate/details/project.html +++ b/templates/admission/doctorate/details/project.html @@ -8,7 +8,7 @@ * The core business involves the administration of students, teachers, * courses, programs and so on. * -* Copyright (C) 2015-2022 Université catholique de Louvain (http://www.uclouvain.be) +* 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 @@ -58,13 +58,16 @@ {% endif %} {% field_data _("Estimated time to complete the PhD (in months)") admission.duree_prevue|default_if_none:'' %} {% field_data allocated_time_label admission.temps_consacre|default_if_none:'' %} + {% field_data _("Is your admission request linked with a FNRS, FRIA, FRESH or CSC application?") admission.est_lie_fnrs_fria_fresh_csc %} + {% field_data _("Comment") admission.commentaire_financement|linebreaks %} {% endif %} {% endpanel %} {% panel _("PhD research project") %} {% field_data _("Project title") admission.titre_projet %} {% field_data _("Project resume") admission.resume_projet|linebreaks %} - {% field_data _("Thesis language") admission.langue_redaction_these|enum_display:'ChoixLangueRedactionThese' %} + {% get_language_name admission.langue_redaction_these as langue_redaction_these %} + {% field_data _("Thesis language") langue_redaction_these %} {% if admission.institut_these %} {% display admission.nom_institut_these ' ' '(' admission.sigle_institut_these ')' as institut_these %} {% endif %} @@ -78,7 +81,12 @@ {% field_data _("Letters of recommendation") admission.lettres_recommandation %} {% endpanel %} - {% panel _("Previous research experience") %} + {% panel _("PhD research experience") %} + {% field_data _("Has your PhD project already started?") admission.projet_doctoral_deja_commence %} + {% if admission.projet_doctoral_deja_commence %} + {% field_data _("Institution") admission.projet_doctoral_institution %} + {% field_data _("Work start date") admission.projet_doctoral_date_debut %} + {% endif %} {% field_data _("Have you previously enrolled for a PhD?") admission.doctorat_deja_realise|enum_display:'ChoixDoctoratDejaRealise' %} {% if admission.doctorat_deja_realise != "NO" %} {% field_data _("Institution") admission.institution %} diff --git a/templatetags/admission.py b/templatetags/admission.py index 57e74e7c..253cb9f7 100644 --- a/templatetags/admission.py +++ b/templatetags/admission.py @@ -41,7 +41,7 @@ from django.shortcuts import resolve_url from django.template.defaultfilters import unordered_list from django.test import override_settings -from django.utils.safestring import SafeString +from django.utils.safestring import SafeString, mark_safe from django.utils.translation import get_language, gettext_lazy as _, pgettext, pgettext_lazy from admission.constants import READ_ACTIONS_BY_TAB, UPDATE_ACTIONS_BY_TAB @@ -60,8 +60,8 @@ from admission.contrib.enums.training_choice import ADMISSION_EDUCATION_TYPE_BY_OSIS_TYPE from admission.contrib.forms.supervision import DoctorateAdmissionMemberSupervisionForm from admission.services.proposition import BUSINESS_EXCEPTIONS_BY_TAB -from admission.services.reference import CountriesService -from admission.utils import get_uuid_value, to_snake_case, format_academic_year +from admission.services.reference import CountriesService, LanguageService, SuperiorInstituteService +from admission.utils import get_uuid_value, to_snake_case, format_academic_year, format_school_title from osis_admission_sdk.exceptions import ForbiddenException, NotFoundException, UnauthorizedException from osis_admission_sdk.model.supervision_dto_promoteur import SupervisionDTOPromoteur from osis_admission_sdk.model.supervision_dto_signatures_membres_ca import SupervisionDTOSignaturesMembresCA @@ -831,3 +831,26 @@ def sport_affiliation_value(affiliation: Optional[str], campus_name: Optional[st return ChoixAffiliationSport.get_value(affiliation) return LABEL_AFFILIATION_SPORT_SI_NEGATIF_SELON_SITE.get(campus_name, ChoixAffiliationSport.NON.value) + + +@register.simple_tag(takes_context=True) +def get_language_name(context, code): + """Return the label of the language associated to the iso code.""" + if not code: + return '' + language = LanguageService.get_language(code=code, person=context['request'].user.person) + if get_language() == settings.LANGUAGE_CODE: + return language.name + return language.name_en + + +@register.simple_tag(takes_context=True) +def get_superior_institute_name(context, organisation_uuid): + """Return the label of the institute associated to the uuid.""" + if not organisation_uuid: + return '' + institute = SuperiorInstituteService.get_superior_institute( + person=context['request'].user.person, + uuid=organisation_uuid, + ) + return mark_safe(format_school_title(institute)) diff --git a/tests/views/test_project.py b/tests/views/test_project.py index dbe4bfff..b28ececa 100644 --- a/tests/views/test_project.py +++ b/tests/views/test_project.py @@ -179,6 +179,11 @@ def setUp(self): self.mock_scholarship_api.return_value.retrieve_scholarship.side_effect = self.get_scholarship self.addCleanup(scholarships_api_patcher.stop) + # Mock language sdk api + languages_api_patcher = patch("osis_reference_sdk.api.languages_api.LanguagesApi") + self.mock_scholarship_api = languages_api_patcher.start() + self.addCleanup(languages_api_patcher.stop) + self.client.force_login(self.person.user) def test_update(self): diff --git a/utils/superior_institute.py b/utils/superior_institute.py new file mode 100644 index 00000000..f3a991b9 --- /dev/null +++ b/utils/superior_institute.py @@ -0,0 +1,47 @@ +# ############################################################################## +# +# 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.entity_version import EntityVersion + + +def format_address(street='', street_number='', postal_code='', city='', country=''): + """Return the concatenation of the specified street, street number, postal code, city and country.""" + address_parts = [ + f'{street} {street_number}', + f'{postal_code} {city}', + country, + ] + return ', '.join(filter(lambda part: part and len(part) > 1, address_parts)) + + +def format_school_title(school): + """Return the concatenation of the school name and city.""" + address = format_address( + street=school.street, + street_number=school.street_number, + postal_code=school.zipcode, + city=school.city, + ) + return f'{school.name} {address}'