Jarvis/jarvis/objective/views.py

461 lines
15 KiB
Python

from django.contrib.auth.decorators import login_required
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render, get_object_or_404
from django.views.decorators.http import require_http_methods
from django.urls import reverse
from jarvis.people.models import Gymnast
from .forms import (
SkillForm,
CombinationForm,
CombinationSkillForm,
)
from .models import (
Skill,
Routine,
RoutineSkill,
PrerequisiteClosure,
)
@login_required
@require_http_methods(["POST"])
def skill_lookup(request):
"""
Récupère la liste des skill à la volée suivant des caractères de recherche entrés (min 3
caractères).
"""
results = []
pattern = request.POST.get("pattern", None)
# Ignore queries shorter than length 2
if pattern is not None and len(pattern) > 2:
model_results = Skill.objects.filter(
Q(short_label__icontains=pattern)
| Q(long_label__icontains=pattern)
| Q(notation__icontains=pattern)
)
results = [
{"ID": x.id, "Name": str(x), "Notation": x.notation} for x in model_results
]
return JsonResponse(results, safe=False)
@login_required
@require_http_methods(["GET"])
def skill_without_prerequisite_listing(request):
"""
Récupère la liste des skills n'ayant aucun prérequis.
"""
skill_list = Skill.objects.filter(prerequisites=None)
context = {"skill_list": skill_list}
return render(request, "skills/list.html", context)
@login_required
@require_http_methods(["GET"])
def skill_listing(request, field=None, expression=None, value=None, level=None):
"""
Récupère la liste des skills suivant un pattern si celui-ci est définit.
Args:
field (str) champ sur lequel doit être effectué la recherche
expression (str) expression de comparaison : le, leq, ge, geq, …
value (str) valeur de la recherche
level (int) niveau requis pour les skills cherchés.
"""
pattern = None
if not field or not value or not expression:
pattern = request.GET.get("pattern", None)
if pattern:
skill_list = Skill.objects.filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
)
elif field and expression and value:
kwargs = {f"{field}__{expression}": value}
skill_list = Skill.objects.filter(**kwargs)
elif level is not None:
skill_list = Skill.objects.filter(level=level)
else:
skill_list = Skill.objects.all()
context = {"skill_list": skill_list}
return render(request, "skills/list.html", context)
@login_required
@require_http_methods(["GET"])
def skill_tree(request, skill_id):
"""Récupère tout l'arbre des prérequis pour une saut passé en paramètre
Args:
skill_id (int) identifiant de la classe Skill
"""
skill = get_object_or_404(Skill, pk=skill_id)
node_dict = {}
skill_closure = PrerequisiteClosure.objects.filter(descendant=skill)
for closure in skill_closure:
node_dict[closure.ancestor] = closure.ancestor.prerequisites.all()
context = {"skill": skill, "node_dict": node_dict}
return render(request, "skills/learning_line.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.
Args:
skill_id (int) identifiant de la classe Skill
"""
skill = get_object_or_404(Skill, pk=skill_id)
for prerequisite in skill.prerequisites.all():
skill.level = max(prerequisite.level + 1, skill.level)
skill.rank = max(prerequisite.rank + 1, skill.rank)
skill.age_boy_with_help = max(
skill.age_boy_with_help, prerequisite.age_boy_with_help
)
skill.age_boy_without_help = max(
skill.age_boy_without_help, prerequisite.age_boy_without_help
)
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
)
skill.age_girl_with_help = max(
skill.age_girl_with_help, prerequisite.age_girl_with_help
)
skill.age_girl_without_help = max(
skill.age_girl_without_help, prerequisite.age_girl_without_help
)
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
)
skill.save()
# Je sépare les educatifs de type "skill" et les éducatifs de type "combination" pour mieux
# gérer l'affichage. Du code bien dégueulasse !
# QTF : une idée pour faire ca de manière plus propre, plus élégante ?
combination_dict = {}
educative_skill = skill.educatives.filter(id__in=Skill.objects.all())
for educative in skill.educatives.filter(id__in=Routine.objects.all()):
combination = Routine.objects.get(pk=educative.id)
combination_dict[combination] = []
for educ_skill in combination.routine.skill_links.all():
combination_dict[combination].append(educ_skill)
context = {
"skill": skill,
"combination_dict": combination_dict,
"educative_skill": educative_skill,
}
return render(request, "skills/details.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def skill_create_or_update(request, skill_id=None):
"""Création ou modification d'un saut.
Args:
skill_id (int) identifiant d'un object de classe <Skill>.
"""
if skill_id:
skill = get_object_or_404(Skill, pk=skill_id)
else:
skill = None
if request.method == "POST":
form = SkillForm(request.POST, instance=skill)
if form.is_valid():
skill = form.save()
return HttpResponseRedirect(reverse("skill_details", args=(skill.pk,)))
return render(request, "skill/create.html", {"form": form})
form = SkillForm(instance=skill)
context = {"form": form, "skill_id": skill_id}
return render(request, "skills/create.html", context)
@login_required
@require_http_methods(["GET"])
def competition_routine_listing(request, gymnast_id=None):
"""Récupère la liste des routines (séries) de compétition suivant un pattern si celui-ci est
défini.
Args:
gymnast_id (int) identifiant de la classe Gymnast
"""
gymnast = None
pattern = request.GET.get("pattern", None)
if pattern is not None and len(pattern) > 2:
routine_list = Routine.objects.filter(
is_routine=True, is_competitive=True
).filter(Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern))
else:
if gymnast_id:
routine_list = Routine.objects.filter(
is_routine=True, is_competitive=True
).filter(done_by_gymnast__gymnast=gymnast_id)
gymnast = Gymnast.objects.get(pk=gymnast_id)
else:
routine_list = Routine.objects.filter(is_routine=True, is_competitive=True)
context = {
"title": "Competition Routines",
"routine_list": routine_list,
"gymnast_id": gymnast_id,
"gymnast": gymnast,
}
return render(request, "combinations/list.html", context)
@login_required
@require_http_methods(["GET"])
def routine_listing(request, gymnast_id=None):
"""Récupère la liste des routines (série) suivant un pattern si celui-ci est défini
Args:
gymnast_id (int) identifiant de la classe Gymnast
"""
gymnast = None
pattern = request.GET.get("pattern", None)
if pattern is not None and len(pattern) > 2:
routine_list = Routine.objects.filter(is_routine=True).filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
)
else:
if gymnast_id:
routine_list = Routine.objects.filter(is_routine=True).filter(
done_by_gymnast__gymnast=gymnast_id
)
gymnast = Gymnast.objects.get(pk=gymnast_id)
else:
routine_list = Routine.objects.filter(is_routine=True)
context = {
"title": "Routines",
"routine_list": routine_list,
"gymnast_id": gymnast_id,
"gymnast": gymnast,
}
return render(request, "combinations/list.html", context)
@login_required
@require_http_methods(["GET"])
def educative_combination_listing(request, gymnast_id=None):
"""Récupère la liste des educatifs suivant un pattern si celui-ci est défini
Args:
gymnast_id (int) identifiant de la classe Gymnast
"""
gymnast = None
pattern = request.GET.get("pattern", None)
if pattern is not None and len(pattern) > 2:
routine_list = Routine.objects.filter(is_routine=False).filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
)
else:
if gymnast_id:
routine_list = Routine.objects.filter(is_routine=False).filter(
done_by_gymnast__gymnast=gymnast_id
)
gymnast = Gymnast.objects.get(pk=gymnast_id)
else:
routine_list = Routine.objects.filter(is_routine=False)
context = {
"title": "Educative",
"routine_list": routine_list,
"gymnast_id": gymnast_id,
"gymnast": gymnast,
}
return render(request, "combinations/list.html", context)
@login_required
@require_http_methods(["GET"])
def combination_listing(request, gymnast_id=None):
"""Récupère la liste des combinaisons suivant un pattern si celui-ci est défini
Args:
gymnast_id (int) identifiant de la classe Gymnast
"""
gymnast = None
pattern = request.GET.get("pattern", None)
if pattern is not None and len(pattern) > 2:
routine_list = Routine.objects.filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
)
else:
if gymnast_id:
routine_list = Routine.objects.filter(done_by_gymnast__gymnast=gymnast_id)
gymnast = Gymnast.objects.get(pk=gymnast_id)
else:
routine_list = Routine.objects.all()
context = {
"title": "Combinations",
"routine_list": routine_list,
"gymnast_id": gymnast_id,
"gymnast": gymnast,
}
return render(request, "combinations/list.html", context)
@login_required
@require_http_methods(["POST"])
def routine_lookup(request):
"""
Récupère la liste des combinaison à la volée suivant des caractères de recherche entrés.
"""
pattern = request.POST.get("pattern", None)
if pattern is not None and len(pattern) > 2:
results = Routine.objects.filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
)
place_list = [{"id": x.id, "label": str(x)} for x in results]
return JsonResponse(place_list, safe=False)
@login_required
@require_http_methods(["GET"])
def combination_details(request, combination_id):
"""
Récupère toutes les informations d'une combinaison.
Args:
combination_id (int) identifiant d'une routine
"""
routine = get_object_or_404(Routine, pk=combination_id)
routine.compute_informations()
context = {"routine": routine, "skill_link_list": routine.skill_links.all()}
return render(request, "combinations/details.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def combination_create_or_update(request, combination_id=None):
"""Création d'une série.
Args:
combination_id (int) identifiant d'un object de classe <routine>.
"""
if combination_id:
combination = get_object_or_404(Routine, pk=combination_id)
else:
combination = None
if request.method == "POST":
form = CombinationForm(request.POST, instance=combination)
if form.is_valid():
combination = form.save()
# ici faire un FOR skill in form_skills_list:
# record.save() # ca sauve le record dans la table RoutineSkill
# something like this :
# http://stackoverflow.com/questions/3074938/django-m2m-form-save-through-table
# QTF : can you help me ?
return HttpResponseRedirect(
reverse("combination_details", args=(combination.pk,))
)
return render(request, "combinations/create.html", {"form": form})
form = CombinationForm(instance=combination)
context = {"form": form, "combination_id": combination_id}
return render(request, "combinations/create.html", context)
@login_required
@require_http_methods(["GET"])
def compose_combination(request, combination_id):
"""
Récupère une routine et les sauts associés sur base d'un id passé en paramètre.
Args:
combination_id (int) identifiant d'un object de classe <routine>.
"""
routine = get_object_or_404(Routine, pk=combination_id)
skill_link_list = routine.skill_links.all()
skill_list = Skill.objects.all()
context = {
"routine": routine,
"skill_link_list": skill_link_list,
"number_of_skill": skill_link_list.count(),
"skill_list": skill_list,
}
return render(request, "combinations/compose.html", context)
@require_http_methods(["POST"])
def link_skill_to_combination(request):
"""
Recoit dans request trois informations permettant de lier complètement un saut à une routine :
- combination_id (int) identifiant d'un object de classe <routine>
- skill_id (int) identifiant d'un object de classe <skill>
- rank (int) numéro de place du skill dans la routine
"""
data = {
"routine": get_object_or_404(Routine, pk=request.POST.get("combination_id", 0)),
"skill": get_object_or_404(Skill, pk=request.POST.get("skill_id", 0)),
"rank": request.POST.get("rank", 0),
}
form = CombinationSkillForm(data)
if form.is_valid():
link, created = RoutineSkill.objects.get_or_create(
routine=form.cleaned_data["routine"],
skill=form.cleaned_data["skill"],
rank=form.cleaned_data["rank"],
)
return HttpResponse(200, (link, created))
return HttpResponse(406)
@require_http_methods(["POST"])
def unlink_skill_from_combination(request):
"""
Recoit dans request deux informations permettant d'enlever un skill d'une routine :
- rank (int) numéro de place du skill dans la routine
- combination_id (int) identifiant d'un object de classe <routine>
"""
rank = request.POST.get("rank", None)
combination_id = request.POST.get("combination_id", None)
combination = get_object_or_404(Routine, pk=combination_id)
try:
RoutineSkill.objects.get(routine=combination, rank=rank).delete()
except Exception:
return HttpResponse(409)
return HttpResponse(200)