__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"
from django.contrib import messages
from django.http import HttpResponse
from django.urls import reverse, reverse_lazy
from django.db.models import Q
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.html import format_html
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.list import ListView
from dal import autocomplete
from guardian.decorators import permission_required
from .models import AcademicField, Specialty, Tag, Topic, RelationAsym
from .forms import (
SessionAcademicFieldForm,
SessionSpecialtyForm,
SelectTagsForm,
SelectLinkedTopicForm,
AddRelationAsymForm,
)
from scipost.forms import SearchTextForm
from scipost.mixins import PaginationMixin, PermissionsMixin
[docs]def set_session_acad_field(request):
"""Set the Academic Field to be viewed in the current user session."""
form = SessionAcademicFieldForm(request.GET or None)
if form.is_valid():
if (
form.cleaned_data["acad_field_slug"]
!= request.session["session_acad_field_slug"]
):
request.session["session_specialty_slug"] = ""
request.session["session_acad_field_slug"] = form.cleaned_data[
"acad_field_slug"
]
try:
initial = {
"acad_field_slug": AcademicField.objects.get(
slug=request.session["session_acad_field_slug"]
).slug
}
except AcademicField.DoesNotExist:
initial = {}
form = SessionAcademicFieldForm(initial=initial)
response = render(
request,
"ontology/session_acad_field_form.html",
context={"session_acad_field_form": form},
)
response["HX-Trigger"] = "session-acad-field-set"
return response
def _hx_session_specialty_form(request):
"""Serve the session Specialty choice form."""
context = {
"session_specialty_form": SessionSpecialtyForm(
acad_field_slug=request.session.get("session_acad_field_slug", None),
initial={
"specialty_slug": request.session.get("session_specialty_slug", None)
},
)
}
return render(request, "ontology/session_specialty_form.html", context)
[docs]def set_session_specialty(request):
"""Set the Specialty to be viewed in the current user session."""
form = SessionSpecialtyForm(
request.GET or None,
acad_field_slug=request.session.get("session_acad_field_slug", ""),
)
if form.is_valid():
if form.cleaned_data["specialty_slug"] == "all":
request.session["session_specialty_slug"] = ""
else:
request.session["session_specialty_slug"] = form.cleaned_data[
"specialty_slug"
]
try:
initial = {
"specialty_slug": Specialty.objects.get(
slug=request.session["session_specialty_slug"]
).slug
}
except (KeyError, Specialty.DoesNotExist):
initial = {}
form = SessionSpecialtyForm(
acad_field_slug=request.session["session_acad_field_slug"], initial=initial
)
response = render(
request,
"ontology/session_specialty_form.html",
context={"session_specialty_form": form},
)
response["HX-Trigger"] = "session-specialty-set"
return response
[docs]def ontology(request):
context = {
"select_linked_topic_form": SelectLinkedTopicForm(),
}
return render(request, "ontology/ontology.html", context=context)
[docs]class AcademicFieldAutocompleteView(autocomplete.Select2QuerySetView):
"""To feed the Select2 widget."""
[docs] def get_queryset(self):
qs = AcademicField.objects.all()
if self.request.GET.get("exclude"):
qs = qs.exclude(slug=self.request.GET["exclude"])
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs.order_by("name")
[docs]class SpecialtyAutocompleteView(autocomplete.Select2QuerySetView):
"""To feed the Select2 widget."""
[docs] def get_queryset(self):
qs = Specialty.objects.all()
if self.request.GET.get("acad_field_id"):
qs = qs.filter(acad_field__id=self.request.GET["acad_field_id"])
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs.order_by("name")
[docs]class TagAutocompleteView(autocomplete.Select2QuerySetView):
"""To feed the Select2 widget."""
[docs] def get_queryset(self):
qs = Tag.objects.all()
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs
[docs]class TopicAutocompleteView(autocomplete.Select2QuerySetView):
"""To feed the Select2 widget."""
[docs] def get_queryset(self):
qs = Topic.objects.all()
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs
[docs]class TopicLinkedAutocompleteView(TopicAutocompleteView):
"""To feed the Select2 widget."""
[docs] def get_result_label(self, item):
return format_html(
'<a href="{}">{}</a>',
reverse("ontology:topic_details", kwargs={"slug": item.slug}),
item,
)
[docs]class TopicCreateView(PermissionsMixin, CreateView):
"""
Create a new Topic for an Ontology.
"""
permission_required = "scipost.can_manage_ontology"
model = Topic
fields = "__all__"
template_name = "ontology/topic_form.html"
success_url = reverse_lazy("ontology:topics")
[docs]class TopicUpdateView(PermissionsMixin, UpdateView):
"""
Update a Topic for an Ontology.
"""
permission_required = "scipost.can_manage_ontology"
model = Topic
fields = "__all__"
template_name = "ontology/topic_form.html"
success_url = reverse_lazy("ontology:topics")
[docs]@permission_required("scipost.can_manage_ontology", return_403=True)
def topic_remove_tag(request, slug, tag_id):
topic = get_object_or_404(Topic, slug=slug)
tag = get_object_or_404(Tag, pk=tag_id)
topic.tags.remove(tag)
topic.save()
return redirect(reverse("ontology:topic_details", kwargs={"slug": topic.slug}))
[docs]class TopicListView(PaginationMixin, ListView):
model = Topic
paginate_by = 100
[docs] def get_queryset(self):
"""
Return a queryset of Topics using optional GET data.
"""
queryset = Topic.objects.all()
if self.request.GET.get("text"):
queryset = queryset.filter(name__icontains=self.request.GET["text"])
return queryset
[docs] def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(
{
"searchform": SearchTextForm(
initial={"text": self.request.GET.get("text")}
),
"select_linked_topic_form": SelectLinkedTopicForm(),
}
)
return context
[docs]class TopicDetailView(DetailView):
model = Topic
[docs] def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context["select_tags_form"] = SelectTagsForm()
context["add_relation_asym_form"] = AddRelationAsymForm(
initial={"A": self.object, "B": self.object}
)
context["relations_asym"] = RelationAsym.objects.filter(
Q(A=self.object) | Q(B=self.object)
)
return context
[docs]@permission_required("scipost.can_manage_ontology", return_403=True)
def add_relation_asym(request, slug):
form = AddRelationAsymForm(request.POST or None)
if form.is_valid():
relation, created = RelationAsym.objects.get_or_create(
A=form.cleaned_data["A"],
relation=form.cleaned_data["relation"],
B=form.cleaned_data["B"],
)
if created:
messages.success(request, "Relation successfully created")
else:
messages.info(request, "This relation already exists")
else:
for error_messages in form.errors.values():
messages.warning(request, *error_messages)
return redirect(reverse("ontology:topic_details", kwargs={"slug": slug}))
[docs]@permission_required("scipost.can_manage_ontology", return_403=True)
def delete_relation_asym(request, relation_id, slug):
relation = get_object_or_404(RelationAsym, pk=relation_id)
relation.delete()
messages.success(request, "Relation deleted")
return redirect(reverse("ontology:topic_details", kwargs={"slug": slug}))