Adding Closure Table pour Educative prerequisites
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
1146b97411
commit
6c1f62b168
|
@ -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>
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue