From d6c3df1c3b09b6a73fbae61f725f6ab9524d8816 Mon Sep 17 00:00:00 2001 From: Gregory Trullemans Date: Wed, 12 Jan 2022 21:03:43 +0100 Subject: [PATCH] Adding rebuil_tree command, breadcrumb and PrerequisiteClosure model/table --- .../management/commands/rebuild_tree.py | 44 +++++++++++++++++ ultron/objective/models.py | 49 +++++++++++++++++++ ultron/objective/views.py | 10 +++- 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 ultron/objective/management/commands/rebuild_tree.py diff --git a/ultron/objective/management/commands/rebuild_tree.py b/ultron/objective/management/commands/rebuild_tree.py new file mode 100644 index 0000000000..471477a250 --- /dev/null +++ b/ultron/objective/management/commands/rebuild_tree.py @@ -0,0 +1,44 @@ +"""This command manages Closure Tables implementation + + It adds new levels and cleans links between Educatives. + This way, it's relatively easy to fetch an entire tree with just one tiny request. + + """ + + from django.core.management.base import BaseCommand + + from ultron.objective.models import Educative, PrerequisiteClosure + + + class Command(BaseCommand): + def handle(self, *args, **options): + # educative_list = Educative.objects.all() + educative_list = [Educative.objects.get(pk=44)] + + for educative in educative_list: + # print('__________________________________________________________________________') + # print('Traitement de ' + str(educative)) + # breadcrumb = [node for node in educative.breadcrumb()] + breadcrumb = educative.breadcrumb() + + for path in range(0, len(breadcrumb)): + # print(' ˪ ' + str(path + 1) + 'ème chemin') + tree = set(PrerequisiteClosure.objects.filter(descendant=educative, path=path)) + # print(' ' + str(tree)) + + for position, ancestor in enumerate(breadcrumb[path]): + # print(' ˪ Traitement de ' + str(ancestor.long_label) + ' : ' + str(ancestor.long_label) + ' -> ' + str(educative.long_label) + ' | ' + str(position) + ' | ' + str(path)) + tree_path, _ = PrerequisiteClosure.objects.get_or_create( + ancestor=ancestor, descendant=educative, level=position, path=path + ) + # if _: + # print(' -> CREATION : ' + str(tree_path)) + # else: + # print(' -> RECUPERATION : ' + str(tree_path)) + + if tree_path in tree: + tree.remove(tree_path) + + for tree_path in tree: + # print(' DELETE : ' + str(tree_path)) + tree_path.delete() diff --git a/ultron/objective/models.py b/ultron/objective/models.py index 5a4361877a..2dd8787d3d 100644 --- a/ultron/objective/models.py +++ b/ultron/objective/models.py @@ -78,6 +78,55 @@ class Educative(Markdownizable): self.level, self.difficulty, ) + + def breadcrumb(self, path=[]): + """ + Renvoie le breadcrumb pour l'édutatif courant. + Exemple : + >>> s = Skill.objects.get(pk=44) + >>> s.breadcrumb() + """ + # print('________________________________________________________________') + # print(self) + path = path + [self] + if self.prerequisites.all().count() == 0: + # print('Plus d\'ancetres') + return [path] + + # print('# ancetres : ' + str(self.prerequisites.all().count())) + path_list = [] + for prerequisite in self.prerequisites.all(): + # print(' ' + str(prerequisite)) + # Permet de gérer les cas de récursivité (qui ne devraient pas se produire dans notre cas) + if prerequisite.id == self.id: + return [self] + new_paths = prerequisite.breadcrumb(path) + for new_path in new_paths: + path_list.append(new_path) + + return path_list + + +class PrerequisiteClosure(models.Model): + """ + Closure table de prérequis + """ + + class Meta: + unique_together = ("descendant", "ancestor", "level", "path") + + descendant = models.ForeignKey(Educative, on_delete=models.CASCADE, related_name="ancestor") + ancestor = models.ForeignKey(Educative, on_delete=models.CASCADE, related_name="descendants") + level = models.PositiveIntegerField() + path = models.PositiveIntegerField() + + def __str__(self): + return "%s -> %s (%s|%s)" % ( + self.ancestor.long_label, + self.descendant.long_label, + self.level, + self.path + ) class TouchPosition(models.Model): diff --git a/ultron/objective/views.py b/ultron/objective/views.py index d306aa3b01..4f41e54ac2 100644 --- a/ultron/objective/views.py +++ b/ultron/objective/views.py @@ -8,7 +8,12 @@ from django.urls import reverse from ultron.people.models import Gymnast from .forms import RoutineForm -from .models import Skill, Routine, RoutineSkill +from .models import ( + Skill, + Routine, + RoutineSkill, + PrerequisiteClosure, +) @login_required @@ -70,6 +75,9 @@ def skill_tree(request, skill_id): """ """ skill = get_object_or_404(Skill, pk=skill_id) + print(skill) + skill_tree = PrerequisiteClosure.objects.filter(descendant=skill).order_by("path", "level") + print(skill_tree) context = {"skill": skill} return render(request, "objectives/skills/tree.html", context)