Updating skill tree view and template.
This commit is contained in:
parent
706343cc6a
commit
83245b1216
|
@ -1,108 +1,179 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block header %}
|
||||
<style>
|
||||
svg {
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card mb-0">
|
||||
<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">
|
||||
<div class="col-md-6">
|
||||
<!-- <h4 class="mb-1">Détails</h4> -->
|
||||
<div class="row">
|
||||
<div class="col-3 text-center">
|
||||
<p>Notation : <a href="#">{{ skill.notation }}</a></p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p>Difficulty : <a href="/skill/difficulty/exact/{{ skill.difficulty }}">{{ skill.difficulty }}</a></p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p>Level : <a href="{% url 'skill_listing_by_key' 'level' 'exact' skill.level %}">{{ skill.level }}</a></p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p>Rank : <a href="{% url 'skill_listing_by_key' 'rank' 'exact' skill.rank %}">{{ skill.level }}</a></p>
|
||||
<!-- Age girl : <a href="/skill/difficulty/exact/{{ skill.difficulty }}">{{ skill.age_girl_masterised }}</a><br /> -->
|
||||
<!-- Age boy : <a href="{% url 'skill_listing_by_key' 'level' 'exact' skill.level %}">{{ skill.age_boy_masterised }}</a> -->
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<table class="table table-striped table-condensed" data-sort="table" id="skill_age_table">
|
||||
<thead>
|
||||
<th></th>
|
||||
<th>With help</th>
|
||||
<th>Without help</th>
|
||||
<th>Chained</th>
|
||||
<th>Masterised</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Girl</td>
|
||||
<td><a href="#">{{ skill.get_age_girl_with_help_display }}</a></td>
|
||||
<td><a href="#">{{ skill.get_age_girl_without_help_display }}</a></td>
|
||||
<td><a href="#">{{ skill.get_age_girl_chained_display }}</a></td>
|
||||
<td><a href="#">{{ skill.get_age_girl_masterised_display }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Boy</td>
|
||||
<td><a href="#">{{ skill.get_age_boy_with_help_display }}</a></td>
|
||||
<td><a href="#">{{ skill.get_age_boy_without_help_display }}</a></td>
|
||||
<td><a href="#">{{ skill.get_age_boy_chained_display }}</a></td>
|
||||
<td><a href="#">{{ skill.get_age_boy_masterised_display }}</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- Age girl : <a href="/skill/difficulty/exact/{{ skill.difficulty }}">{{ skill.age_girl }}</a><br /> -->
|
||||
<!-- Age boy : <a href="{% url 'skill_listing_by_key' 'level' 'exact' skill.level %}">{{ skill.age_boy }}</a> -->
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
{% if skill.prerequisites.all or skill.educatives.all %}
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h4>Prerequisites</h4>
|
||||
<ul>
|
||||
{% if skill.prerequisites.all %}
|
||||
{% for prerequisites in skill.prerequisites.all %}
|
||||
<li><a href="{% url 'skill_details' prerequisites.id %}">{{ prerequisites.short_label }}</a></li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="text-muted">No prerequisites defined.</p>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h4>Educatives</h4>
|
||||
<ul>
|
||||
{% if skill.educatives.all %}
|
||||
{% for educatives in skill.educatives.all %}
|
||||
<li><a href="{% url 'skill_details' educatives.id %}">{{ educatives.short_label }}</a></li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="text-muted">No educative defined.</p>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<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>
|
||||
<br />
|
||||
{% if skill.informations %}
|
||||
{{ skill.to_markdown | safe }}
|
||||
{% else %}
|
||||
<p class="text-muted">No more informations provided for this skill.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<svg></svg>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block footerscript %}
|
||||
<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>
|
||||
(async () => {
|
||||
// fetch data and render
|
||||
// const resp = await fetch(
|
||||
// "https://raw.githubusercontent.com/erikbrinkman/d3-dag/main/examples/grafo.json"
|
||||
// );
|
||||
const data = [{
|
||||
// Rudy
|
||||
"id": "43/",
|
||||
"parentIds": ["-2", ".41/"]
|
||||
},
|
||||
{
|
||||
// vrille
|
||||
"id": "-2",
|
||||
"parentIds": ["-1"]
|
||||
},
|
||||
{
|
||||
// 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 nodeRadius = 20;
|
||||
const layout = d3
|
||||
//
|
||||
// Base layout
|
||||
.sugiyama()
|
||||
//
|
||||
// Layering
|
||||
.layering(d3.layeringSimplex()) // Simplex (shortest edges)
|
||||
// .layering(d3.layeringLongestPath()) // Longest Path (minimum height)
|
||||
// .layering(d3.layeringCoffmanGraham()) // Coffman Graham (constrained width)
|
||||
//
|
||||
// Decrossing
|
||||
.decross(d3.decrossOpt()) // Optimal (can be very slow)
|
||||
// .decross(d3.decrossTwoLayer().order(d3.twolayerAgg())) // Two Layer Agg (fast)
|
||||
// .decross(d3.decrossTwoLayer().order(d3.twolayerOpt())) // Two Layer Opt (can be very slow)
|
||||
//
|
||||
// Coords
|
||||
// .coord(d3.coordCenter()) // Center (fast)
|
||||
.coord(d3.coordGreedy()) // Greedy (fast)
|
||||
// .coord(d3.coordQuad()) // Quadradtic (can be slow)
|
||||
// .coord(d3.coordTopological()) //
|
||||
.nodeSize(
|
||||
(node) => [(node ? 3.6 : 0.25) * nodeRadius, 3 * nodeRadius]
|
||||
); // set node size instead of constraining to fit
|
||||
// .nodeSize((node) => {
|
||||
// const size = node ? base : 5;
|
||||
// return [1.2 * size, size];
|
||||
// });
|
||||
const { width, height } = layout(dag);
|
||||
|
||||
// --------------------------------
|
||||
// This code only handles rendering
|
||||
// --------------------------------
|
||||
const svgSelection = d3.select("svg");
|
||||
svgSelection.attr("viewBox", [0, 0, width, height].join(" "));
|
||||
const defs = svgSelection.append("defs"); // For gradients
|
||||
|
||||
const steps = dag.size();
|
||||
const interp = d3.interpolateRainbow;
|
||||
const colorMap = new Map();
|
||||
for (const [i, node] of dag.idescendants().entries()) {
|
||||
colorMap.set(node.data.id, interp(i / steps));
|
||||
}
|
||||
|
||||
// How to draw edges
|
||||
const line = d3
|
||||
.line()
|
||||
.curve(d3.curveCatmullRom)
|
||||
.x((d) => d.x)
|
||||
.y((d) => d.y);
|
||||
|
||||
// Plot edges
|
||||
svgSelection
|
||||
.append("g")
|
||||
.selectAll("path")
|
||||
.data(dag.links())
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("d", ({ points }) => line(points))
|
||||
.attr("fill", "none")
|
||||
.attr("stroke-width", 3)
|
||||
.attr("stroke", ({ source, target }) => {
|
||||
// encodeURIComponents for spaces, hope id doesn't have a `--` in it
|
||||
const gradId = encodeURIComponent(`${source.data.id}--${target.data.id}`);
|
||||
const grad = defs
|
||||
.append("linearGradient")
|
||||
.attr("id", gradId)
|
||||
.attr("gradientUnits", "userSpaceOnUse")
|
||||
.attr("x1", source.x)
|
||||
.attr("x2", target.x)
|
||||
.attr("y1", source.y)
|
||||
.attr("y2", target.y);
|
||||
grad
|
||||
.append("stop")
|
||||
.attr("offset", "0%")
|
||||
.attr("stop-color", colorMap.get(source.data.id));
|
||||
grad
|
||||
.append("stop")
|
||||
.attr("offset", "100%")
|
||||
.attr("stop-color", colorMap.get(target.data.id));
|
||||
return `url(#${gradId})`;
|
||||
});
|
||||
|
||||
// Select nodes
|
||||
const nodes = svgSelection
|
||||
.append("g")
|
||||
.selectAll("g")
|
||||
.data(dag.descendants())
|
||||
.enter()
|
||||
.append("g")
|
||||
.attr("transform", ({ x, y }) => `translate(${x}, ${y})`);
|
||||
|
||||
// Plot node circles
|
||||
nodes
|
||||
.append("circle")
|
||||
.attr("r", nodeRadius)
|
||||
.attr("fill", (n) => colorMap.get(n.data.id));
|
||||
|
||||
// Add text to nodes
|
||||
nodes
|
||||
.append("text")
|
||||
.text((d) => d.data.id)
|
||||
.attr("font-weight", "bold")
|
||||
.attr("font-family", "sans-serif")
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("alignment-baseline", "middle")
|
||||
.attr("fill", "white");
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -10,7 +10,8 @@ skill_urlpatterns = [
|
|||
),
|
||||
path(r"lookup/", views.skill_lookup),
|
||||
path(r"search/", views.skill_listing),
|
||||
path(r"<int:skillid>/", views.skill_details, name="skill_details"),
|
||||
path(r"<int:skill_id>/", views.skill_details, name="skill_details"),
|
||||
path(r"<int:skill_id>/tree/", views.skill_tree, name="skill_tree"),
|
||||
path(r"", views.skill_listing, name="skill_list"),
|
||||
]
|
||||
|
||||
|
|
|
@ -66,27 +66,32 @@ def skill_listing(request, field=None, expression=None, value=None, level=None):
|
|||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def skill_details(request, skillid):
|
||||
def skill_tree(request, skill_id):
|
||||
"""
|
||||
"""
|
||||
skill = get_object_or_404(Skill, pk=skill_id)
|
||||
context = {"skill": skill}
|
||||
return render(request, "objectives/skills/tree.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def skill_details(request, skill_id):
|
||||
"""Récupère toutes les informations d'un skill.
|
||||
|
||||
La méthode en profite pour vérifier les champs level, rank, age_boy et age_girl
|
||||
par rapport aux pré-requis.
|
||||
|
||||
:param skillig: id d'un `skill`
|
||||
:type skillid: int
|
||||
:param skill_id: id d'un `skill`
|
||||
:type skill_id: int
|
||||
|
||||
:return: skill
|
||||
"""
|
||||
skill = get_object_or_404(Skill, pk=skillid)
|
||||
skill = get_object_or_404(Skill, pk=skill_id)
|
||||
|
||||
for prerequisite in skill.prerequisites.all():
|
||||
skill.level = max(prerequisite.level + 1, skill.level)
|
||||
# if prerequisite.level >= skill.level:
|
||||
# skill.level = prerequisite.level + 1
|
||||
|
||||
skill.rank = max(prerequisite.rank + 1, skill.rank)
|
||||
# if prerequisite.rank >= skill.rank:
|
||||
# skill.rank = prerequisite.rank + 1
|
||||
|
||||
skill.age_boy_with_help = max(skill.age_boy_with_help, prerequisite.age_boy_with_help)
|
||||
skill.age_boy_without_help = max(
|
||||
|
@ -95,8 +100,6 @@ def skill_details(request, skillid):
|
|||
)
|
||||
skill.age_boy_chained = max(skill.age_boy_chained, prerequisite.age_boy_chained)
|
||||
skill.age_boy_masterised = max(skill.age_boy_masterised, prerequisite.age_boy_masterised)
|
||||
# if prerequisite.age_boy > skill.age_boy:
|
||||
# skill.age_boy = prerequisite.age_boy
|
||||
|
||||
skill.age_girl_with_help = max(skill.age_girl_with_help, prerequisite.age_girl_with_help)
|
||||
skill.age_girl_without_help = max(
|
||||
|
@ -105,8 +108,6 @@ def skill_details(request, skillid):
|
|||
)
|
||||
skill.age_girl_chained = max(skill.age_girl_chained, prerequisite.age_girl_chained)
|
||||
skill.age_girl_masterised = max(skill.age_girl_masterised, prerequisite.age_girl_masterised)
|
||||
# if prerequisite.age_girl > skill.age_girl:
|
||||
# skill.age_girl = prerequisite.age_girl
|
||||
|
||||
skill.save()
|
||||
|
||||
|
|
Loading…
Reference in New Issue