def test_get_option_list_case_ignore_finality_list_choice(self): """ This test ensure that the tree will be pruned when a child if type of finality list choice and option isn't considered as part of tree """ option_1 = EducationGroupYearFactory( academic_year=self.academic_year, education_group_type__name=MiniTrainingType.OPTION.name) GroupElementYearFactory(parent=self.root, child_branch=option_1) for finality_type in [ GroupType.FINALITY_120_LIST_CHOICE.name, GroupType.FINALITY_180_LIST_CHOICE.name ]: finality_group = EducationGroupYearFactory( academic_year=self.academic_year, education_group_type__name=finality_type) GroupElementYearFactory(parent=self.root, child_branch=finality_group) GroupElementYearFactory( parent=finality_group, child_branch=EducationGroupYearFactory( academic_year=self.academic_year, education_group_type__name=MiniTrainingType.OPTION.name)) node = EducationGroupHierarchy(self.root) self.assertListEqual(node.get_option_list(), [option_1])
def test_tree_luy_has_and_is_prerequisite(self): # self.learning_unit_year_1 is prerequisite PrerequisiteItemFactory( learning_unit=self.learning_unit_year_1.learning_unit, prerequisite=PrerequisiteFactory(education_group_year=self.parent) ) # self.learning_unit_year_1 has prerequisite PrerequisiteItemFactory( prerequisite=PrerequisiteFactory( learning_unit_year=self.learning_unit_year_1, education_group_year=self.parent ) ) node = EducationGroupHierarchy(self.parent) json = node.to_json() self.assertEqual( json['children'][1]['children'][0]['a_attr']['title'], "{}\n{}".format( self.learning_unit_year_1.complete_title, _("The learning unit has prerequisites and is a prerequisite") ) ) self.assertEqual( json['children'][1]['children'][0]['icon'], 'fa fa-exchange-alt' )
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
def test_node_to_list_flat(self): node = EducationGroupHierarchy(self.parent) list_children = node.to_list(flat=True) self.assertCountEqual(list_children, [ self.group_element_year_1, self.group_element_year_1_1, self.group_element_year_2, self.group_element_year_2_1 ])
def test_get_option_list_case_result_found(self): option_1 = EducationGroupYearFactory( academic_year=self.academic_year, education_group_type__name=MiniTrainingType.OPTION.name) GroupElementYearFactory(parent=self.root, child_branch=option_1) node = EducationGroupHierarchy(self.root) self.assertListEqual(node.get_option_list(), [option_1])
def test_node_to_list_with_pruning_function(self): """ This test ensure that if the parameter pruning function is specified we only get the tree without node which has been pruned """ node = EducationGroupHierarchy(self.parent) list_children = node.to_list( flat=True, pruning_function=lambda child: child.group_element_year.pk == self.group_element_year_2.pk ) self.assertCountEqual(list_children, [self.group_element_year_2])
def test_tree_to_json(self): node = EducationGroupHierarchy(self.parent) json = node.to_json() self.assertEqual(json['text'], self.parent.verbose) self.assertEqual(json['a_attr']['href'], reverse('education_group_read', args=[ self.parent.pk, self.parent.pk]) + "?group_to_parent=0") self.assertEqual( json['children'][1]['children'][0]['a_attr']['href'], reverse( 'learning_unit_utilization', args=[self.parent.pk, self.group_element_year_2_1.child_leaf.pk] ) + "?group_to_parent={}".format(self.group_element_year_2_1.pk))
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)
def test_get_verbose_children(self): result = EducationGroupHierarchy(self.education_group_year_1).to_list() context_waiting = [ self.group_element_year_1, [self.group_element_year_2], self.group_element_year_3, [self.group_element_year_4] ] self.assertEqual(result, context_waiting) credits = self.group_element_year_1.relative_credits or self.group_element_year_1.child_branch.credits or 0 verbose_branch = "{} ({} {})".format( self.group_element_year_1.child.title, credits, _("credits")) self.assertEqual(self.group_element_year_1.verbose, verbose_branch) components = LearningComponentYear.objects.filter( learning_unit_year=self.group_element_year_2.child_leaf).annotate( total=Case(When(hourly_volume_total_annual=None, then=0), default=F('hourly_volume_total_annual'))).values( 'type', 'total') verbose_leaf = "{} {} [{}] ({} {})".format( self.group_element_year_2.child_leaf.acronym, self.group_element_year_2.child_leaf.complete_title, volume_total_verbose(components), self.group_element_year_2.relative_credits or self.group_element_year_2.child_leaf.credits or 0, _("credits"), ) self.assertEqual(self.group_element_year_2.verbose, verbose_leaf)
def test_build_tree_reference(self): """ This tree contains a reference link. """ self.group_element_year_1.link_type = LinkTypes.REFERENCE.name self.group_element_year_1.save() node = EducationGroupHierarchy(self.parent) self.assertEqual(node.children[0]._get_icon(), static('img/reference.jpg')) list_children = node.to_list() self.assertEqual(list_children, [ self.group_element_year_1_1, self.group_element_year_2, [self.group_element_year_2_1] ])
class LearningUnitGenericDetailView(PermissionRequiredMixin, DetailView): model = LearningUnitYear context_object_name = "learning_unit_year" pk_url_kwarg = 'learning_unit_year_id' permission_required = 'base.can_access_education_group' raise_exception = True def get_person(self): return get_object_or_404(Person, user=self.request.user) def get_root(self): return get_object_or_404(EducationGroupYear, pk=self.kwargs.get("root_id")) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) root = self.get_root() self.hierarchy = EducationGroupHierarchy( root, tab_to_show=self.request.GET.get("tab_to_show")) # TODO remove parent in context context['person'] = self.get_person() context['root'] = root context['root_id'] = root.pk context['parent'] = root context['tree'] = json.dumps(self.hierarchy.to_json()) context['group_to_parent'] = self.request.GET.get( "group_to_parent") or '0' context['show_prerequisites'] = self.show_prerequisites(root) return context def show_prerequisites(self, education_group_year): return education_group_year.education_group_type.name not in NO_PREREQUISITES
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) root = self.get_root() self.hierarchy = EducationGroupHierarchy( root, tab_to_show=self.request.GET.get("tab_to_show")) # TODO remove parent in context context['person'] = self.get_person() context['root'] = root context['root_id'] = root.pk context['parent'] = root context['tree'] = json.dumps(self.hierarchy.to_json()) context['group_to_parent'] = self.request.GET.get( "group_to_parent") or '0' context['show_prerequisites'] = self.show_prerequisites(root) return context
def test_get_option_list_case_multiple_result_found_on_different_children( self): list_option = [] for _ in range(5): group_child = EducationGroupYearFactory( academic_year=self.academic_year, education_group_type__name=GroupType.SUB_GROUP.name) GroupElementYearFactory(parent=self.root, child_branch=group_child) option = EducationGroupYearFactory( academic_year=self.academic_year, education_group_type__name=MiniTrainingType.OPTION.name) list_option.append(option) GroupElementYearFactory(parent=group_child, child_branch=option) node = EducationGroupHierarchy(self.root) self.assertCountEqual(node.get_option_list(), list_option)
def test_tree_to_json_a_attr(self): """In this test, we ensure that a attr contains some url for tree contextual menu""" node = EducationGroupHierarchy(self.parent) json = node.to_json() child = self.group_element_year_1.child_branch expected_modify_url = reverse('group_element_year_update', args=[ self.parent.pk, child.pk, self.group_element_year_1.pk ]) self.assertEqual(json['children'][0]['a_attr']['modify_url'], expected_modify_url) expected_attach_url = reverse('education_group_attach', args=[self.parent.pk, child.pk]) self.assertEqual(json['children'][0]['a_attr']['attach_url'], expected_attach_url) expected_detach_url = reverse('group_element_year_delete', args=[ self.parent.pk, child.pk, self.group_element_year_1.pk ]) self.assertEqual(json['children'][0]['a_attr']['detach_url'], expected_detach_url)
def test_build_pdf_tree_with_mandatory(self): tree = EducationGroupHierarchy(self.education_group_year_1).to_list() out = Template( "{% load education_group_pdf %}" "{{ tree|pdf_tree_list }}" ).render(Context({ 'tree': tree })) self.assertEqual(out, _build_correct_tree_list(tree))
def test_tree_to_json_ids(self): node = EducationGroupHierarchy(self.parent) json = node.to_json() self.assertEquals( json['children'][1]['id'], "id_{}_{}".format( node.children[1].education_group_year.pk, node.children[1].group_element_year.pk if node.children[1].group_element_year else '#' ) ) self.assertEquals( json['children'][1]['children'][0]['id'], "id_{}_{}".format( node.children[1].children[0].learning_unit_year.pk, node.children[1].children[0].group_element_year.pk if node.children[1].group_element_year else '#' ) )
def test_tree_get_url(self): test_cases = [ {'name': 'with tab', 'node': EducationGroupHierarchy(self.parent, tab_to_show='show_identification'), 'correct_url': reverse('education_group_read', args=[self.parent.pk, self.parent.pk]) + "?group_to_parent=0&tab_to_show=show_identification"}, {'name': 'without tab', 'node': EducationGroupHierarchy(self.parent), 'correct_url': reverse('education_group_read', args=[self.parent.pk, self.parent.pk]) + "?group_to_parent=0"}, {'name': 'with wrong tab', 'node': EducationGroupHierarchy(self.parent, tab_to_show='not_existing'), 'correct_url': reverse('education_group_read', args=[self.parent.pk, self.parent.pk]) + "?group_to_parent=0"}, ] for case in test_cases: with self.subTest(type=case['name']): self.assertEqual(case['correct_url'], case['node'].get_url())
def test_build_pdf_tree_with_optional(self): self.group_element_year_1.is_mandatory = False self.group_element_year_1.save() self.group_element_year_2.is_mandatory = False self.group_element_year_2.save() tree = EducationGroupHierarchy(self.education_group_year_1).to_list() out = Template("{% load education_group_pdf %}" "{{ tree|pdf_tree_list }}").render( Context({'tree': tree})) self.assertEqual(out, _build_correct_tree_list(tree))
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)
def test_get_option_list_case_reference_link(self): """ This test ensure that the tree will not be pruned when the link of child is reference """ reference_group_child = EducationGroupYearFactory( academic_year=self.academic_year, education_group_type__name=GroupType.SUB_GROUP.name) GroupElementYearFactory( parent=self.root, child_branch=reference_group_child, link_type=LinkTypes.REFERENCE.name, ) option_1 = EducationGroupYearFactory( academic_year=self.academic_year, education_group_type__name=MiniTrainingType.OPTION.name) GroupElementYearFactory(parent=reference_group_child, child_branch=option_1) node = EducationGroupHierarchy(self.root) self.assertListEqual(node.get_option_list(), [option_1])
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) root = self.get_root() context['person'] = self.get_person() context['root'] = root context['root_id'] = self.kwargs.get("root_id") context['parent'] = root context['tree'] = json.dumps(EducationGroupHierarchy(root).to_json()) context['group_to_parent'] = self.request.GET.get("group_to_parent") or '0' return context
def test_init_tree(self): node = EducationGroupHierarchy(self.parent) self.assertEqual(node.education_group_year, self.parent) self.assertEqual(len(node.children), 2) self.assertEqual(node.children[0].group_element_year, self.group_element_year_1) self.assertEqual(node.children[1].group_element_year, self.group_element_year_2) self.assertEqual(node.children[0].children[0].group_element_year, self.group_element_year_1_1) self.assertEqual(node.children[1].children[0].group_element_year, self.group_element_year_2_1) self.assertEqual(node.children[0].children[0].education_group_year, self.group_element_year_1_1.child_branch) self.assertEqual(node.children[1].children[0].education_group_year, None) self.assertEqual(node.children[1].children[0].learning_unit_year, self.group_element_year_2_1.child_leaf)
def _check_detach_prerequisite_rules(self): for formation in self._parents: luys_inside_formation = Counter( EducationGroupHierarchy( root=formation).get_learning_unit_year_list()) luys_to_detach = Counter(self._learning_units_year_to_detach) luys_inside_formation_after_detach = luys_inside_formation - luys_to_detach luys_that_are_prerequisites = LearningUnitYear.objects.filter( learning_unit__prerequisiteitem__prerequisite__learning_unit_year__in =list(luys_inside_formation_after_detach.keys()), learning_unit__prerequisiteitem__prerequisite__education_group_year =formation, id__in=[luy.id for luy in self._learning_units_year_to_detach]) luys_that_cannot_be_detached = [ luy for luy in luys_that_are_prerequisites if luy not in luys_inside_formation_after_detach ] if luys_that_cannot_be_detached: self.errors.append( _("Cannot detach education group year %(acronym)s as the following learning units " "are prerequisite in %(formation)s: %(learning_units)s") % { "acronym": self.education_group_year.acronym, "formation": formation.acronym, "learning_units": ", ".join([ luy.acronym for luy in luys_that_cannot_be_detached ]) }) luys_that_have_prerequisites = LearningUnitYear.objects.filter( id__in=Prerequisite.objects.filter( education_group_year__in=self._parents, learning_unit_year__in=self._learning_units_year_to_detach). values("learning_unit_year__id")) if luys_that_have_prerequisites: self.warnings.append( _("The prerequisites for the following learning units contained in education group year " "%(acronym)s will we deleted: %(learning_units)s") % { "acronym": self.education_group_year.acronym, "learning_units": ", ".join( [luy.acronym for luy in luys_that_have_prerequisites]) })
def pdf_content(request, root_id, education_group_year_id, language): education_group_year = get_object_or_404(EducationGroupYear, pk=education_group_year_id) tree = EducationGroupHierarchy(education_group_year).to_list() context = { 'root': education_group_year, 'tree': tree, 'language': language, 'created': datetime.datetime.now(), } with translation.override(language): return render_pdf( request, context=context, filename=education_group_year.acronym, template='education_group/pdf_content.html', )
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # This objects are mandatory for all education group views context['person'] = self.person # FIXME same param context['root'] = self.root context['root_id'] = self.root.pk context['parent'] = self.root context['parent_training'] = self.object.parent_by_training() if self.with_tree: context['tree'] = json.dumps( EducationGroupHierarchy(self.root).to_json()) context['group_to_parent'] = self.request.GET.get( "group_to_parent") or '0' context[ 'can_change_education_group'] = perms.is_eligible_to_change_education_group( person=self.person, education_group=context['object'], ) context[ 'can_change_coorganization'] = perms.is_eligible_to_change_coorganization( person=self.person, education_group=context['object'], ) context['enums'] = mdl.enums.education_group_categories context['current_academic_year'] = self.current_academic_year context["show_identification"] = self.show_identification() context["show_diploma"] = self.show_diploma() context["show_general_information"] = self.show_general_information() context[ "show_skills_and_achievements"] = self.show_skills_and_achievements( ) context["show_administrative"] = self.show_administrative() context["show_content"] = self.show_content() context["show_utilization"] = self.show_utilization() context["show_admission_conditions"] = self.show_admission_conditions() return context
def test_get_option_list_case_no_result(self): node = EducationGroupHierarchy(self.root) self.assertListEqual(node.get_option_list(), [])
def _get_options_to_detach(self): options_to_detach = EducationGroupHierarchy( root=self.education_group_year).get_option_list() if self.education_group_year.education_group_type.name == MiniTrainingType.OPTION.name: options_to_detach += [self.education_group_year] return options_to_detach
def _learning_units_year_to_detach(self): return EducationGroupHierarchy( root=self.education_group_year).get_learning_unit_year_list()