Adding Closure Table pour Educative prerequisites
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Gregory Trullemans 2022-01-13 18:43:43 +01:00
parent 1146b97411
commit 6c1f62b168
6 changed files with 96 additions and 67 deletions

View File

@ -87,6 +87,9 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
<br />
<a href="{% url 'skill_tree' skill.id %}">Learning Line</a><br />
<br />
</div> </div>
<div class="col-md-6 alert {% if request.session.template == 0 %}skill-info{% else %}alert-secondary{% endif %} mr-0"> <div class="col-md-6 alert {% if request.session.template == 0 %}skill-info{% else %}alert-secondary{% endif %} mr-0">
<p>From <a href="#">{{ skill.departure }}</a>, {% if skill.rotation %} <a href="#">{{ skill.rotation }}</a> quart of <a href="#">{{ skill.get_rotation_type_display }}</a> rotation {% endif %}{% if skill.twist %} with <a href="#">{{ skill.twist }} half-twist</a> {% endif %} in a <a href="#">{{ skill.get_position_display }}</a> position, landing to <a href="#">{{ skill.landing }}</a></p> <p>From <a href="#">{{ skill.departure }}</a>, {% if skill.rotation %} <a href="#">{{ skill.rotation }}</a> quart of <a href="#">{{ skill.get_rotation_type_display }}</a> rotation {% endif %}{% if skill.twist %} with <a href="#">{{ skill.twist }} half-twist</a> {% endif %} in a <a href="#">{{ skill.get_position_display }}</a> position, landing to <a href="#">{{ skill.landing }}</a></p>

View File

@ -1,70 +1,45 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %}
{% block header %} {% block header %}
<style> <style>
svg { svg {
width: 50%; width: 25%;
height: 50%; height: 25%;
} }
</style> </style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="card mb-0"> <div class="card mb-0">
<svg></svg> <div class="card-header">
<h3 class="mb-0">{{ skill.short_label }}</h3>
<h4 class="card-title"> {{ skill.notation }}</h4>
</div>
<div class="card-body pb-0 mb-0">
<div class="row mr-1 ml-1 pb-0 mb-0">
<svg></svg>
</div>
</div>
</div> </div>
{% endblock %} {% endblock %}
{% block footerscript %} {% block footerscript %}
<script src="{% static "js/plugins/D3-dag/d3.min.js%}"></script> <script src="{% static "js/plugins/D3-dag/d3.min.js" %}"></script>
<script src="{% static "js/plugins/D3-dag/d3-dag.0.8.2.min.js%}"></script> <script src="{% static "js/plugins/D3-dag/d3-dag.0.8.2.min.js" %}"></script>
<script> <script>
(async () => { (async () => {
// fetch data and render const data = [
// const resp = await fetch( {% for key, value in node_dict.items %}
// "https://raw.githubusercontent.com/erikbrinkman/d3-dag/main/examples/grafo.json"
// );
const data = [{
// Rudy
"id": "43/",
"parentIds": ["-2", ".41/"]
},
{ {
// vrille "id": "{{ key.short_label }}",
"id": "-2", "parentIds": [
"parentIds": ["-1"] {% for prerequisite in value %}
"{{ prerequisite.short_label }}",
{% endfor %}
]
}, },
{ {% endfor %}
// Barani ]
"id": ".41/",
"parentIds": ["-1", ".3-/"]
},
{
// 1/2 vrille
"id": "-1",
"parentIds": ["|"]
},
{
// Chandelle
"id": "|",
"parentIds": []
},
{
// 3/4 Avant Tendu
"id": ".3-/",
"parentIds": [".1"]
},
{
// Ventre
"id": ".1",
"parentIds": ["4p"]
},
{
// 4 pattes
"id": "4p",
"parentIds": []
}];
// const data = await resp.json();
const dag = d3.dagStratify()(data); const dag = d3.dagStratify()(data);
const nodeRadius = 20; const nodeRadius = 20;
@ -95,6 +70,7 @@
// const size = node ? base : 5; // const size = node ? base : 5;
// return [1.2 * size, size]; // return [1.2 * size, size];
// }); // });
// .attr("r", function(d) {return d.name.length * 2.5;})
const { width, height } = layout(dag); const { width, height } = layout(dag);
// -------------------------------- // --------------------------------
@ -169,6 +145,10 @@
nodes nodes
.append("text") .append("text")
.text((d) => d.data.id) .text((d) => d.data.id)
// .attr("font-size", "8")
// .attr("font-size", function(d) { return Math.min(2 * d.r, (2 * d.r - 8) / this.getComputedTextLength() * 24) + "px"; })
// .attr("font-size", function(d) {return (1 / d.data.id.length) * 60;})
.attr("font-size", function(d) {return (1 / d.data.id.replace(/ /g,'').length) * 60;})
.attr("font-weight", "bold") .attr("font-weight", "bold")
.attr("font-family", "sans-serif") .attr("font-family", "sans-serif")
.attr("text-anchor", "middle") .attr("text-anchor", "middle")

View File

@ -8,7 +8,13 @@ from django_admin_listfilter_dropdown.filters import (
RelatedDropdownFilter RelatedDropdownFilter
) )
from .models import TouchPosition, Skill, Routine, RoutineSkill from .models import (
TouchPosition,
Skill,
Routine,
RoutineSkill,
PrerequisiteClosure,
)
class TouchPositionAdmin(admin.ModelAdmin): class TouchPositionAdmin(admin.ModelAdmin):
@ -159,7 +165,20 @@ class RoutineSkillAdmin(admin.ModelAdmin):
autocomplete_fields = ("routine", "skill") autocomplete_fields = ("routine", "skill")
class PrerequisiteClosureAdmin(admin.ModelAdmin):
model = PrerequisiteClosure()
list_display = ("descendant", "ancestor", "level", "path")
search_fields = (
"descendant__long_label",
"descendant__short_label",
"ancestor__long_label",
"ancestor__short_label",
)
admin.site.register(TouchPosition, TouchPositionAdmin) admin.site.register(TouchPosition, TouchPositionAdmin)
admin.site.register(Skill, SkillAdmin) admin.site.register(Skill, SkillAdmin)
admin.site.register(Routine, RoutineAdmin) admin.site.register(Routine, RoutineAdmin)
admin.site.register(RoutineSkill, RoutineSkillAdmin) admin.site.register(RoutineSkill, RoutineSkillAdmin)
admin.site.register(PrerequisiteClosure, PrerequisiteClosureAdmin)

View File

@ -12,33 +12,21 @@ from ultron.objective.models import Educative, PrerequisiteClosure
class Command(BaseCommand): class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
# educative_list = Educative.objects.all() educative_list = Educative.objects.all()
educative_list = [Educative.objects.get(pk=44)]
for educative in educative_list: for educative in educative_list:
# print('__________________________________________________________________________')
# print('Traitement de ' + str(educative))
# breadcrumb = [node for node in educative.breadcrumb()]
breadcrumb = educative.breadcrumb() breadcrumb = educative.breadcrumb()
for path in range(0, len(breadcrumb)): for path in range(0, len(breadcrumb)):
# print(' ˪ ' + str(path + 1) + 'ème chemin')
tree = set(PrerequisiteClosure.objects.filter(descendant=educative, path=path)) tree = set(PrerequisiteClosure.objects.filter(descendant=educative, path=path))
# print(' ' + str(tree))
for position, ancestor in enumerate(breadcrumb[path]): 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( tree_path, _ = PrerequisiteClosure.objects.get_or_create(
ancestor=ancestor, descendant=educative, level=position, path=path 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: if tree_path in tree:
tree.remove(tree_path) tree.remove(tree_path)
for tree_path in tree: for tree_path in tree:
# print(' DELETE : ' + str(tree_path))
tree_path.delete() tree_path.delete()

View File

@ -0,0 +1,27 @@
# Generated by Django 3.2.8 on 2022-01-12 20:30
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('objective', '0011_auto_20220109_1016'),
]
operations = [
migrations.CreateModel(
name='PrerequisiteClosure',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('level', models.PositiveIntegerField()),
('path', models.PositiveIntegerField()),
('ancestor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='descendants', to='objective.educative')),
('descendant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ancestor', to='objective.educative')),
],
options={
'unique_together': {('descendant', 'ancestor', 'level', 'path')},
},
),
]

View File

@ -75,10 +75,22 @@ def skill_tree(request, skill_id):
""" """
""" """
skill = get_object_or_404(Skill, pk=skill_id) skill = get_object_or_404(Skill, pk=skill_id)
print(skill) node_dict = {}
skill_breadcrumb = PrerequisiteClosure.objects.filter(descendant=skill).order_by("path", "level") # node_list[skill] = list(skill.prerequisites.all())
print(skill_breadcrumb)
context = {"skill": skill} skill_closure = PrerequisiteClosure.objects.filter(descendant=skill)
for closure in skill_closure:
node_dict[closure.ancestor] = closure.ancestor.prerequisites.all()
# if closure.descendant != closure.ancestor:
# print(closure)
# print(node_dict)
# edge_list = {}
# for skill in node_list.values():
# edge_list[skill.long_label] = list(skill.prerequisites.all())
# print(edge_list)
context = {"skill": skill, "node_dict": node_dict}
return render(request, "objectives/skills/tree.html", context) return render(request, "objectives/skills/tree.html", context)