Exemplo n.º 1
0
    def _get_missing_options(self):
        """
            In context of MA/MD/MS when we add an option [or group which contains options],
            this options must exist in parent context (2m)
        """
        options_missing_by_finality = {}

        options_to_add = EducationGroupHierarchy(
            root=self.child).get_option_list()
        if self.child.education_group_type.name == MiniTrainingType.OPTION.name:
            options_to_add += [self.child]

        finalities_qs = self.parents | EducationGroupYear.objects.filter(
            pk=self.parent.pk)
        finalities_pks = finalities_qs.filter(
            education_group_type__name__in=TrainingType.finality_types(
            )).values_list('pk', flat=True)
        if self.child.education_group_type.name in TrainingType.finality_types(
        ):
            finalities_pks = list(finalities_pks) + [self.parent.pk]
        if finalities_pks:
            root_2m_qs = EducationGroupYear.hierarchy.filter(
                pk__in=finalities_pks).get_parents().filter(
                    education_group_type__name__in=TrainingType.
                    root_master_2m_types())

            for root in root_2m_qs:
                options_in_2m = EducationGroupHierarchy(
                    root=root).get_option_list()
                options_missing_by_finality[root] = set(options_to_add) - set(
                    options_in_2m)
        return options_missing_by_finality
Exemplo n.º 2
0
    def is_valid(self):
        if self.parent.education_group_type.name in TrainingType.root_master_2m_types() or \
                self.parents.filter(education_group_type__name__in=TrainingType.root_master_2m_types()).exists():
            self._check_end_year_constraints_on_2m()
            self._check_attach_options_rules()

            if not self.instance:
                self._check_new_attach_is_not_duplication()
        return True
Exemplo n.º 3
0
    def _check_end_year_constraints_on_2m(self):
        qs = self.educationgroupyear_set.all().filter(
            education_group_type__name__in=TrainingType.finality_types() + TrainingType.root_master_2m_types()
        ).select_related('education_group_type')

        for education_group_year in qs:
            if education_group_year.type in TrainingType.finality_types():
                self._check_end_year_finality_are_in_range_of_root_2m(education_group_year)
            elif education_group_year.type in TrainingType.root_master_2m_types() and self.end_year is not None:
                self._check_end_year_root_2m_cover_all_finalities(education_group_year)
Exemplo n.º 4
0
    def _check_end_year_constraints_on_2m(self):
        qs = self.educationgroupyear_set.all().filter(
            education_group_type__name__in=TrainingType.finality_types() + TrainingType.root_master_2m_types()
        ).select_related('education_group_type')

        for education_group_year in qs:
            if education_group_year.type in TrainingType.finality_types():
                self._check_end_year_finality_are_in_range_of_root_2m(education_group_year)
            elif education_group_year.type in TrainingType.root_master_2m_types() and self.end_year is not None:
                self._check_end_year_root_2m_cover_all_finalities(education_group_year)
Exemplo n.º 5
0
def create_partial_title(apps, schema_editor):
    education_group_year_mdl = apps.get_model("base", "EducationGroupYear")
    education_group_years = education_group_year_mdl.objects.filter(
        academic_year__year__gte=2019,
        education_group_type__name__in=TrainingType.finality_types())
    for education_group_year in education_group_years:
        if education_group_year.education_group_type.name in [
                TrainingType.MASTER_MA_120.name,
                TrainingType.MASTER_MA_180_240.name
        ]:
            education_group_year.partial_title = "Finalité approfondie"
            education_group_year.partial_title_english = "Research Focus"
        if education_group_year.education_group_type.name in [
                TrainingType.MASTER_MD_120.name,
                TrainingType.MASTER_MD_180_240.name
        ]:
            education_group_year.partial_title = "Finalité didactique"
            education_group_year.partial_title_english = "Teaching Focus"
        if education_group_year.education_group_type.name in [
                TrainingType.MASTER_MS_120.name,
                TrainingType.MASTER_MS_180_240.name
        ]:
            education_group_year.partial_title = "Finalité spécialisée"
            education_group_year.partial_title_english = "Professional Focus"
        education_group_year.save()
Exemplo n.º 6
0
    def _postpone_child_branch(self, old_gr: GroupElementYear,
                               new_gr: GroupElementYear) -> GroupElementYear:
        """
        Unlike child leaf, the child branch must be postponed (recursively)
        """
        old_egy = old_gr.child_branch
        new_egy = old_egy.next_year()
        if new_egy:
            is_empty = self._is_empty(new_egy)
            if new_gr.link_type == LinkTypes.REFERENCE.name and is_empty:
                self.warnings.append(ReferenceLinkEmptyWarning(new_egy))
            elif not is_empty:
                if not (new_egy.is_training()
                        or new_egy.education_group_type.name
                        in MiniTrainingType.to_postpone()):
                    self.warnings.append(
                        EducationGroupYearNotEmptyWarning(
                            new_egy, self.next_academic_year))
            else:
                self._postpone(old_egy, new_egy)
        else:
            # If the education group does not exists for the next year, we have to postpone.
            new_egy = self._duplication_education_group_year(old_gr, old_egy)
            self.number_elements_created += 1

        new_gr.child_branch = new_egy
        if new_egy and new_egy.education_group_type.name == MiniTrainingType.OPTION.name:
            self.postponed_options[new_egy.id] = new_gr
        if new_egy and new_gr.parent.education_group_type.name in TrainingType.finality_types(
        ):
            self.postponed_finalities.append(new_gr)
        return new_gr
Exemplo n.º 7
0
    def _postpone_child_branch(self, old_gr: GroupElementYear, new_gr: GroupElementYear) -> GroupElementYear:
        """
        Unlike child leaf, the child branch must be postponed (recursively)
        """
        old_egy = old_gr.child_branch
        new_egy = old_egy.next_year()
        if new_egy:
            is_empty = self._is_empty(new_egy)
            if new_gr.link_type == LinkTypes.REFERENCE.name and is_empty:
                self.warnings.append(ReferenceLinkEmptyWarning(new_egy))
            elif not is_empty:
                if not (new_egy.is_training() or new_egy.is_mini_training()):
                    self.warnings.append(EducationGroupYearNotEmptyWarning(new_egy, self.next_academic_year))
            else:
                self._postpone(old_egy, new_egy)
        else:
            # If the education group does not exists for the next year, we have to postpone.
            new_egy = self._duplication_education_group_year(old_gr, old_egy)

        new_gr.child_branch = new_egy
        if new_egy and new_egy.education_group_type.name == MiniTrainingType.OPTION.name:
            self.postponed_options[new_egy.id] = new_gr
        if new_egy and new_gr.parent.education_group_type.name in TrainingType.finality_types():
            self.postponed_finalities.append(new_gr)
        return new_gr
Exemplo n.º 8
0
 def set_initial_diploma_values(self):
     if self.education_group_type and \
             self.education_group_type.name in TrainingType.with_diploma_values_set_initially_as_true():
         self.fields['joint_diploma'].initial = True
         self.fields['diploma_printing_title'].required = True
     else:
         self.fields['joint_diploma'].initial = False
         self.fields['diploma_printing_title'].required = False
Exemplo n.º 9
0
 def set_initial_diploma_values(self):
     if self.education_group_type and \
             self.education_group_type.name in TrainingType.with_diploma_values_set_initially_as_true():
         self.fields['joint_diploma'].initial = True
         self.fields['diploma_printing_title'].required = True
     else:
         self.fields['joint_diploma'].initial = False
         self.fields['diploma_printing_title'].required = False
Exemplo n.º 10
0
def reverse_migration(apps, schema_editor):
    education_group_year_mdl = apps.get_model("base", "EducationGroupYear")
    education_group_years = education_group_year_mdl.objects.filter(
        academic_year__year__gte=2019,
        education_group_type__name__in=TrainingType.finality_types())
    for education_group_year in education_group_years:
        education_group_year.partial_title = ""
        education_group_year.partial_title_english = ""
        education_group_year.save()
Exemplo n.º 11
0
 def test_get_publish_url_case_not_common_and_finality_case(self):
     training = TrainingFactory(education_group_type__name=random.choice(
         TrainingType.finality_types()))
     expected_url = "{api_url}/{endpoint}".format(
         api_url=settings.ESB_API_URL,
         endpoint=settings.ESB_REFRESH_PEDAGOGY_ENDPOINT.format(
             year=training.academic_year.year,
             code=training.partial_acronym),
     )
     self.assertEqual(expected_url, _get_url_to_publish(training))
Exemplo n.º 12
0
    def _check_end_year_constraints_on_2m(self):
        """
        In context of 2M, when we add a finality [or group which contains finality], we must ensure that
        the end date of all 2M is greater or equals of all finalities
        """
        finalities_to_add_qs = EducationGroupYear.objects.filter(
            pk=self.child.pk) | EducationGroupYear.hierarchy.filter(
                pk=self.child.pk).get_children()
        finalities_to_add_qs = finalities_to_add_qs.filter(
            education_group_type__name__in=TrainingType.finality_types())

        root_2m_qs = self.parents | EducationGroupYear.objects.filter(
            pk=self.parent.pk)
        root_2m_qs = root_2m_qs.filter(
            education_group_type__name__in=TrainingType.root_master_2m_types(),
            education_group__end_year__isnull=False,
        ).order_by('education_group__end_year')

        errors = []
        if finalities_to_add_qs.exists() and root_2m_qs.exists():
            root_2m_early_end_date = root_2m_qs.first()
            invalid_finalities_acronyms = finalities_to_add_qs.filter(
                Q(education_group__end_year__gt=root_2m_early_end_date.
                  education_group.end_year)
                | Q(education_group__end_year__isnull=True)).values_list(
                    'acronym', flat=True)

            if invalid_finalities_acronyms:
                errors.append(
                    ValidationError(
                        ngettext(
                            "Finality \"%(acronym)s\" has an end date greater than %(root_acronym)s program.",
                            "Finalities \"%(acronym)s\" have an end date greater than %(root_acronym)s program.",
                            len(invalid_finalities_acronyms)) %
                        {
                            "acronym": ', '.join(invalid_finalities_acronyms),
                            "root_acronym": root_2m_early_end_date.acronym
                        }))

        if errors:
            raise ValidationError(errors)
Exemplo n.º 13
0
    def _check_end_year_root_2m_cover_all_finalities(self, root_2m_egy):
        qs = EducationGroupYear.hierarchy.filter(pk=root_2m_egy.pk) \
            .get_children() \
            .filter(
                Q(education_group__end_year__gt=self.end_year) | Q(education_group__end_year__isnull=True),
                education_group_type__name__in=TrainingType.finality_types(),
            )

        for invalid_finality in qs:
            raise ValidationError({
                'end_year':
                    _('The end date must be greater or equals to the finality %(acronym)s') %
                    {'acronym': invalid_finality.acronym}
            })
Exemplo n.º 14
0
    def _check_end_year_root_2m_cover_all_finalities(self, root_2m_egy):
        qs = EducationGroupYear.hierarchy.filter(pk=root_2m_egy.pk) \
            .get_children() \
            .filter(
                Q(education_group__end_year__gt=self.end_year) | Q(education_group__end_year__isnull=True),
                education_group_type__name__in=TrainingType.finality_types(),
            )

        for invalid_finality in qs:
            raise ValidationError({
                'end_year':
                    _('The end date must be greater or equals to the finality %(acronym)s') %
                    {'acronym': invalid_finality.acronym}
            })
Exemplo n.º 15
0
    def _check_end_year_finality_are_in_range_of_root_2m(self, finality_egy):
        qs = EducationGroupYear.hierarchy.filter(pk=finality_egy.pk) \
            .get_parents().filter(education_group_type__name__in=TrainingType.root_master_2m_types())

        if self.end_year is None:
            qs = qs.filter(education_group__end_year__isnull=False)
        else:
            qs = qs.filter(education_group__end_year__lt=self.end_year)

        for invalid_root_2m in qs:
            raise ValidationError({
                'end_year':
                    _('The end date must be less or equals to the root %(acronym)s') %
                    {'acronym': invalid_root_2m.acronym}
            })
Exemplo n.º 16
0
    def _check_end_year_finality_are_in_range_of_root_2m(self, finality_egy):
        qs = EducationGroupYear.hierarchy.filter(pk=finality_egy.pk)\
            .get_parents().filter(education_group_type__name__in=TrainingType.root_master_2m_types())

        if self.end_year is None:
            qs = qs.filter(education_group__end_year__isnull=False)
        else:
            qs = qs.filter(education_group__end_year__lt=self.end_year)

        for invalid_root_2m in qs:
            raise ValidationError({
                'end_year':
                    _('The end date must be less or equals to the root %(acronym)s') %
                    {'acronym': invalid_root_2m.acronym}
            })
Exemplo n.º 17
0
    def _get_missing_options(self):
        """
            In context of MA/MD/MS when we add an option [or group which contains options],
            this options must exist in parent context (2m)
        """
        options_missing_by_finality = {}

        options_to_add = EducationGroupHierarchy(root=self.child).get_option_list()
        if self.child.education_group_type.name == MiniTrainingType.OPTION.name:
            options_to_add += [self.child]

        finalities_qs = self.parents | EducationGroupYear.objects.filter(pk=self.parent.pk)
        finalities_pks = finalities_qs.filter(
            education_group_type__name__in=TrainingType.finality_types()
        ).values_list('pk', flat=True)
        if finalities_pks:
            root_2m_qs = EducationGroupYear.hierarchy.filter(pk__in=finalities_pks).get_parents().filter(
                education_group_type__name__in=TrainingType.root_master_2m_types()
            )

            for root in root_2m_qs:
                options_in_2m = EducationGroupHierarchy(root=root).get_option_list()
                options_missing_by_finality[root] = set(options_to_add) - set(options_in_2m)
        return options_missing_by_finality
Exemplo n.º 18
0
    def _check_detatch_options_rules(self):
        """
        In context of 2M when we detach an option [or group which contains option], we must ensure that
        these options are not present in MA/MD/MS
        """
        options_to_detach = self._get_options_to_detach()

        errors = []
        for master_2m in self.get_parents_program_master():
            master_2m_tree = EducationGroupHierarchy(root=master_2m)

            counter_options = Counter(master_2m_tree.get_option_list())
            counter_options.subtract(options_to_detach)
            options_to_check = [
                opt for opt, count in counter_options.items() if count == 0
            ]
            if not options_to_check:
                continue

            finality_list = [
                elem.child for elem in master_2m_tree.to_list(flat=True)
                if isinstance(elem.child, EducationGroupYear) and elem.child.
                education_group_type.name in TrainingType.finality_types()
            ]
            for finality in finality_list:
                mandatory_options = EducationGroupHierarchy(
                    root=finality).get_option_list()
                missing_options = set(options_to_check) & set(
                    mandatory_options)

                if missing_options:
                    errors.append(
                        ValidationError(
                            ngettext(
                                "Option \"%(acronym)s\" cannot be detach because it is contained in"
                                " %(finality_acronym)s program.",
                                "Options \"%(acronym)s\" cannot be detach because they are contained in"
                                " %(finality_acronym)s program.",
                                len(missing_options)) % {
                                    "acronym":
                                    ', '.join(option.acronym
                                              for option in missing_options),
                                    "finality_acronym":
                                    finality.acronym
                                }))

        if errors:
            raise ValidationError(errors)
Exemplo n.º 19
0
 def test_get_publish_url_case_not_common_and_finality_or_option_case(self):
     training = EducationGroupYearFactory(
         education_group_type__name=random.choice(
             TrainingType.finality_types() +
             [MiniTrainingType.OPTION.name]))
     parent = TrainingFactory(
         education_group_type__name=TrainingType.PGRM_MASTER_120.name,
         academic_year=training.academic_year)
     GroupElementYearFactory(parent=parent, child_branch=training)
     expected_url = "{api_url}/{endpoint}".format(
         api_url=settings.ESB_API_URL,
         endpoint=settings.ESB_REFRESH_PEDAGOGY_ENDPOINT.format(
             year=training.academic_year.year,
             code="{parent}-{partial_acronym}".format(
                 parent=parent.acronym,
                 partial_acronym=training.partial_acronym)),
     )
     self.assertEqual(expected_url, _get_url_to_publish(training))
Exemplo n.º 20
0
    def _check_detatch_options_rules(self):
        """
        In context of 2M when we detach an option [or group which contains option], we must ensure that
        these options are not present in MA/MD/MS
        """
        options_to_detach = self._get_options_to_detach()

        errors = []
        for master_2m in self.get_parents_program_master():
            master_2m_tree = EducationGroupHierarchy(root=master_2m)

            counter_options = Counter(master_2m_tree.get_option_list())
            counter_options.subtract(options_to_detach)
            options_to_check = [opt for opt, count in counter_options.items() if count == 0]
            if not options_to_check:
                continue

            finality_list = [elem.child for elem in master_2m_tree.to_list(flat=True)
                             if isinstance(elem.child, EducationGroupYear)
                             and elem.child.education_group_type.name in TrainingType.finality_types()]
            for finality in finality_list:
                mandatory_options = EducationGroupHierarchy(root=finality).get_option_list()
                missing_options = set(options_to_check) & set(mandatory_options)

                if missing_options:
                    errors.append(
                        ValidationError(
                            ngettext(
                                "Option \"%(acronym)s\" cannot be detach because it is contained in"
                                " %(finality_acronym)s program.",
                                "Options \"%(acronym)s\" cannot be detach because they are contained in"
                                " %(finality_acronym)s program.",
                                len(missing_options)
                            ) % {
                                "acronym": ', '.join(option.acronym for option in missing_options),
                                "finality_acronym": finality.acronym
                            })
                    )

        if errors:
            raise ValidationError(errors)
Exemplo n.º 21
0
    def _check_end_year_constraints_on_2m(self):
        """
        In context of 2M, when we add a finality [or group which contains finality], we must ensure that
        the end date of all 2M is greater or equals of all finalities
        """
        finalities_to_add_qs = EducationGroupYear.objects.filter(pk=self.child.pk) | \
            EducationGroupYear.hierarchy.filter(pk=self.child.pk).get_children()
        finalities_to_add_qs = finalities_to_add_qs.filter(education_group_type__name__in=TrainingType.finality_types())

        root_2m_qs = self.parents | EducationGroupYear.objects.filter(pk=self.parent.pk)
        root_2m_qs = root_2m_qs.filter(
            education_group_type__name__in=TrainingType.root_master_2m_types(),
            education_group__end_year__isnull=False,
        ).order_by('education_group__end_year')

        errors = []
        if finalities_to_add_qs.exists() and root_2m_qs.exists():
            root_2m_early_end_date = root_2m_qs.first()
            invalid_finalities_acronyms = finalities_to_add_qs.filter(
                Q(education_group__end_year__gt=root_2m_early_end_date.education_group.end_year) |
                Q(education_group__end_year__isnull=True)
            ).values_list('acronym', flat=True)

            if invalid_finalities_acronyms:
                errors.append(
                    ValidationError(
                        ngettext(
                            "Finality \"%(acronym)s\" has an end date greater than %(root_acronym)s program.",
                            "Finalities \"%(acronym)s\" have an end date greater than %(root_acronym)s program.",
                            len(invalid_finalities_acronyms)
                        ) % {
                            "acronym": ', '.join(invalid_finalities_acronyms),
                            "root_acronym": root_2m_early_end_date.acronym
                        }
                    )
                )

        if errors:
            raise ValidationError(errors)
Exemplo n.º 22
0
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.views.generic import UpdateView, DetailView

from base.models.education_group_year import EducationGroupYear
from base.models.enums.education_group_types import TrainingType, MiniTrainingType, GroupType
from base.models.group_element_year import GroupElementYear
from base.models.learning_unit_year import LearningUnitYear
from base.models.person import Person
from base.views.education_groups import perms
from base.views.education_groups.detail import CatalogGenericDetailView
from base.views.mixins import RulesRequiredMixin, FlagMixin, AjaxTemplateMixin
from program_management.business.group_element_years.group_element_year_tree import EducationGroupHierarchy

NO_PREREQUISITES = TrainingType.finality_types() + [
    MiniTrainingType.OPTION.name,
    MiniTrainingType.MOBILITY_PARTNERSHIP.name,
] + GroupType.get_names()


@method_decorator(login_required, name='dispatch')
class GenericGroupElementYearMixin(FlagMixin, RulesRequiredMixin, SuccessMessageMixin, AjaxTemplateMixin):
    model = GroupElementYear
    context_object_name = "group_element_year"
    pk_url_kwarg = "group_element_year_id"

    # FlagMixin
    flag = "education_group_update"

    # RulesRequiredMixin
Exemplo n.º 23
0
 def is_attestation(self):
     return self.type in TrainingType.attestation_types()
Exemplo n.º 24
0
 def get_finality_list(self):
     return [
         element.child_branch for element in self.to_list(flat=True)
         if element.child_branch and element.child_branch.
         education_group_type.name in TrainingType.finality_types()
     ]
Exemplo n.º 25
0
 def show_skills_and_achievements(self):
     return not self.object.is_common and \
            self.object.education_group_type.name in itertools.chain(TrainingType.with_skills_achievements(),
                                                                     MiniTrainingType.with_admission_condition()) \
            and self.is_general_info_and_condition_admission_in_display_range()
Exemplo n.º 26
0
 def show_admission_conditions(self):
     # @TODO: Need to refactor after business clarification
     return not self.object.is_main_common and \
            self.object.education_group_type.name in itertools.chain(TrainingType.with_admission_condition(),
                                                                     MiniTrainingType.with_admission_condition()) \
            and self.is_general_info_and_condition_admission_in_display_range()
Exemplo n.º 27
0
 def _show_free_text(self):
     return not self.object.is_common and self.object.education_group_type.name in itertools.chain(
                 TrainingType.with_admission_condition(),
                 MiniTrainingType.with_admission_condition()
             )
Exemplo n.º 28
0
 def is_valid(self):
     if self.parent.education_group_type.name in TrainingType.root_master_2m_types() or \
             self.parents.filter(education_group_type__name__in=TrainingType.root_master_2m_types()).exists():
         self._check_end_year_constraints_on_2m()
         self._check_attach_options_rules()
     return True
Exemplo n.º 29
0
 def is_finality(self):
     return self.type in TrainingType.finality_types()