Skip to content
Snippets Groups Projects
Commit c39201a3 authored by Frank Lange's avatar Frank Lange
Browse files

add endpoint /suggest/disciplines

parent bfe6083d
Branches
No related tags found
No related merge requests found
Pipeline #1511840 passed
......@@ -215,3 +215,14 @@ class Event(BaseItem, EventSpecial):
Item = Union[Event, Resource]
@dataclass
class CurationSuggestResultItem:
label: str
value: str
@dataclass
class CurationSuggestDisciplinesResultItem(CurationSuggestResultItem):
children: List[CurationSuggestDisciplinesResultItem]
from typing import List, Tuple
from rdflib import Literal, RDF, SKOS, URIRef, Variable
from project.dalia.api_models.api_models import CurationSuggestDisciplinesResultItem
from project.dalia.query.utils import query_ontologies_dataset
from project.dalia.query_builder.query_builder import FILTER, FunctionExpressions, Operators, QueryBuilder
# Note: This algorithm is quite inefficient and results in a lot of SPARQL queries.
# Another idea would be to pull all disciplines and their narrower/broader nodes at once, e.g. via
#
# PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
# SELECT ?broaderDiscipline ?discipline ?label ?notation
# WHERE {
# {
# ?discipline skos:topConceptOf <https://w3id.org/kim/hochschulfaechersystematik/scheme> .
# }
# UNION {
# ?broaderDiscipline skos:narrower ?discipline .
# }
# ?discipline a skos:Concept .
# ?discipline skos:prefLabel ?label .
# FILTER(LANG(?label) = "en") .
# ?discipline skos:notation ?notation .
# }
# ORDER BY ?notation
#
# and then try to construct the object tree.
# data for endpoint /suggest/disciplines
def get_disciplines_suggestions() -> List[CurationSuggestDisciplinesResultItem]:
return [
CurationSuggestDisciplinesResultItem(
value=discipline[0],
label=discipline[1],
children=get_child_disciplines_of_discipline(discipline[0])
) for discipline in get_top_disciplines()
]
_VARIABLES = {
"discipline": Variable("discipline"),
"label": Variable("label"),
"notation": Variable("notation"),
}
def _prepare_query_to_get_disciplines_metadata(discipline_selection_bgp: Tuple) -> str:
var_discipline = _VARIABLES["discipline"]
var_label = _VARIABLES["label"]
var_notation = _VARIABLES["notation"]
return QueryBuilder().SELECT(
var_discipline,
var_label,
).WHERE(
discipline_selection_bgp,
(var_discipline, RDF.type, SKOS.Concept),
(var_discipline, SKOS.prefLabel, var_label),
FILTER(
Operators.EQ(
FunctionExpressions.LANG(var_label),
Literal("en")
)
),
(var_discipline, SKOS.notation, var_notation)
).ORDER_BY(
var_notation
).build()
def get_top_disciplines() -> List[Tuple[URIRef, str]]:
query = prepare_query_to_get_all_top_disciplines()
results = query_ontologies_dataset(query)
return _disciplines_data_from_results(results)
def prepare_query_to_get_all_top_disciplines() -> str:
return _prepare_query_to_get_disciplines_metadata(
(_VARIABLES["discipline"], SKOS.topConceptOf, URIRef("https://w3id.org/kim/hochschulfaechersystematik/scheme"))
)
def _disciplines_data_from_results(results) -> List[Tuple[URIRef, str]]:
return [(result.discipline, str(result.label)) for result in results]
def get_child_disciplines_of_discipline(discipline: URIRef) -> List[CurationSuggestDisciplinesResultItem]:
query = prepare_query_to_get_all_narrower_disciplines_of_discipline(discipline)
results = query_ontologies_dataset(query)
return [
CurationSuggestDisciplinesResultItem(
value=discipline[0],
label=discipline[1],
children=get_child_disciplines_of_discipline(discipline[0])
) for discipline in _disciplines_data_from_results(results)
]
def prepare_query_to_get_all_narrower_disciplines_of_discipline(discipline: URIRef) -> str:
return _prepare_query_to_get_disciplines_metadata(
(discipline, SKOS.narrower, _VARIABLES["discipline"])
)
......@@ -3,10 +3,10 @@ from rest_framework_dataclasses.serializers import DataclassSerializer
from project.dalia.api_models.api_models import (
BasicSearchFilter,
Community,
CurationSuggestDisciplinesResultItem,
ItemSearchRequest,
ItemSearchResult,
Resource,
SelectedFacet,
)
......@@ -33,3 +33,8 @@ class ItemSearchResultSerializer(DataclassSerializer):
class ItemSearchRequestSerializer(DataclassSerializer):
class Meta:
dataclass = ItemSearchRequest
class CurationSuggestDisciplinesResultItemSerializer(DataclassSerializer):
class Meta:
dataclass = CurationSuggestDisciplinesResultItem
......@@ -12,4 +12,5 @@ urlpatterns = [
path('v1/items/<uuid:resource_id>', views.ItemView.as_view(), name="dalia_item"),
path('v1/items', views.ItemSearchView.as_view(), name="dalia_item_search"),
path('v1/items/<uuid:resource_id>/suggestions', views.ItemSuggestionsView.as_view(), name="item_suggestions"),
path('v1/suggest/disciplines', views.CurationSuggestDisciplinesView.as_view(), name="curation_suggest_disciplines"),
]
......@@ -6,14 +6,16 @@ from rest_framework.response import Response
from rest_framework.views import APIView
from project.dalia.api_models.api_models import ItemSearchResult
from project.dalia.query.items.basic_search_filters.basic_search_filters import get_basic_search_filters
from project.dalia.curation.suggest.disciplines import get_disciplines_suggestions
from project.dalia.query.communities.communities import get_metadata_for_community
from project.dalia.query.communities.community_items import get_items_for_community
from project.dalia.query.items.basic_search_filters.basic_search_filters import get_basic_search_filters
from project.dalia.query.items.metadata.items import get_metadata_for_learning_resource
from project.dalia.query.items.search.text_search import search_items
from project.dalia.serializers import (
BasicSearchFilterSerializer,
CommunitySerializer,
CurationSuggestDisciplinesResultItemSerializer,
ItemSearchRequestSerializer,
ItemSearchResultSerializer,
ItemSerializer,
......@@ -75,6 +77,7 @@ class ItemSearchView(APIView):
return Response(result_serializer.data)
# endpoint /items/{itemId}/suggestions
class ItemSuggestionsView(APIView):
def get(self, request: Request, resource_id: UUID) -> HttpResponse:
data = request.GET
......@@ -88,3 +91,10 @@ class ItemSuggestionsView(APIView):
serializer = ItemSearchResultSerializer(search_result)
return Response(serializer.data)
# endpoint /suggest/disciplines
class CurationSuggestDisciplinesView(APIView):
def get(self, request: Request):
serializer = CurationSuggestDisciplinesResultItemSerializer(get_disciplines_suggestions(), many=True)
return Response(serializer.data)
from django.urls import reverse
from rdflib import URIRef
from rest_framework import status
from project.dalia.api_models.api_models import CurationSuggestDisciplinesResultItem
from project.dalia.curation.suggest.disciplines import get_child_disciplines_of_discipline, get_top_disciplines, \
prepare_query_to_get_all_narrower_disciplines_of_discipline, prepare_query_to_get_all_top_disciplines
from project.dalia.serializers import CurationSuggestDisciplinesResultItemSerializer
from tests.project.dalia.utils import dedent_and_normalize, normalize
def test_prepare_query_to_get_all_top_disciplines():
query = prepare_query_to_get_all_top_disciplines()
assert normalize(query) == dedent_and_normalize("""
SELECT ?discipline ?label
WHERE {
?discipline <http://www.w3.org/2004/02/skos/core#topConceptOf> <https://w3id.org/kim/hochschulfaechersystematik/scheme> .
?discipline <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2004/02/skos/core#Concept> .
?discipline <http://www.w3.org/2004/02/skos/core#prefLabel> ?label .
FILTER ( LANG ( ?label ) = "en" ) .
?discipline <http://www.w3.org/2004/02/skos/core#notation> ?notation .
}
ORDER BY ?notation
""")
def test_prepare_query_to_get_all_narrower_disciplines_of_discipline():
query = prepare_query_to_get_all_narrower_disciplines_of_discipline(URIRef("abc"))
assert normalize(query) == dedent_and_normalize("""
SELECT ?discipline ?label
WHERE {
<abc> <http://www.w3.org/2004/02/skos/core#narrower> ?discipline .
?discipline <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2004/02/skos/core#Concept> .
?discipline <http://www.w3.org/2004/02/skos/core#prefLabel> ?label .
FILTER ( LANG ( ?label ) = "en" ) .
?discipline <http://www.w3.org/2004/02/skos/core#notation> ?notation .
}
ORDER BY ?notation
""")
def test_get_top_disciplines(triplestore):
top_disciplines = get_top_disciplines()
assert top_disciplines == [
(URIRef('https://w3id.org/kim/hochschulfaechersystematik/n0'), 'Interdisciplinary'),
(URIRef('https://w3id.org/kim/hochschulfaechersystematik/n1'), 'Humanities'),
(URIRef('https://w3id.org/kim/hochschulfaechersystematik/n2'), 'Sports'),
(URIRef('https://w3id.org/kim/hochschulfaechersystematik/n3'), 'Law, Economics and Social Sciences'),
(URIRef('https://w3id.org/kim/hochschulfaechersystematik/n4'), 'Mathematics, Natural Sciences'),
(URIRef('https://w3id.org/kim/hochschulfaechersystematik/n5'), 'Human Medicine / Health Sciences'),
(
URIRef('https://w3id.org/kim/hochschulfaechersystematik/n7'),
'Agricultural, Forest and Nutritional Sciences, Veterinary medicine',
),
(URIRef('https://w3id.org/kim/hochschulfaechersystematik/n8'), 'Engineering Sciences'),
(URIRef('https://w3id.org/kim/hochschulfaechersystematik/n9'), 'Art, Art Theory')
]
def test_get_child_disciplines_of_discipline(triplestore):
disciplines = get_child_disciplines_of_discipline(URIRef("https://w3id.org/kim/hochschulfaechersystematik/n5"))
assert disciplines == [
CurationSuggestDisciplinesResultItem(
label='Health Sciences (general)',
value=URIRef('https://w3id.org/kim/hochschulfaechersystematik/n48'),
children=[
CurationSuggestDisciplinesResultItem(
label='Health Education',
value=URIRef('https://w3id.org/kim/hochschulfaechersystematik/n195'),
children=[],
),
CurationSuggestDisciplinesResultItem(
label='Health Science/Health Management',
value=URIRef('https://w3id.org/kim/hochschulfaechersystematik/n232'),
children=[],
),
CurationSuggestDisciplinesResultItem(
label='Non-medical Healthcare Professions/Therapies',
value=URIRef('https://w3id.org/kim/hochschulfaechersystematik/n233'),
children=[],
),
CurationSuggestDisciplinesResultItem(
label='Nursing Science/Nursing Management',
value=URIRef('https://w3id.org/kim/hochschulfaechersystematik/n234'),
children=[],
)
],
),
CurationSuggestDisciplinesResultItem(
label='Human Medicine (excl. Dentistry)',
value=URIRef('https://w3id.org/kim/hochschulfaechersystematik/n49'),
children=[
CurationSuggestDisciplinesResultItem(
label='Medicine (General Medicine)',
value=URIRef('https://w3id.org/kim/hochschulfaechersystematik/n107'),
children=[],
),
],
),
CurationSuggestDisciplinesResultItem(
label='Dentistry',
value=URIRef('https://w3id.org/kim/hochschulfaechersystematik/n50'),
children=[
CurationSuggestDisciplinesResultItem(
label='Dentistry',
value=URIRef('https://w3id.org/kim/hochschulfaechersystematik/n185'),
children=[],
),
],
),
]
def test_get_on_CurationSuggestDisciplinesView_returns_200_and_the_disciplines_trees(triplestore, api_client):
response = api_client.get(reverse("curation_suggest_disciplines"))
assert response.status_code == status.HTTP_200_OK
serializer = CurationSuggestDisciplinesResultItemSerializer(data=response.data, many=True)
assert serializer.is_valid()
data = serializer.validated_data
assert len(data) == 9
n4 = data[4]
assert n4.label == "Mathematics, Natural Sciences"
assert n4.value == "https://w3id.org/kim/hochschulfaechersystematik/n4"
assert len(n4.children) == 8
n06 = data[1].children[5]
assert n06.label == "Information and Library Sciences"
assert n06.value == "https://w3id.org/kim/hochschulfaechersystematik/n06"
assert len(n06.children) == 2
n242 = data[7].children[5].children[1]
assert n242.label == "Interior Architecture"
assert n242.value == "https://w3id.org/kim/hochschulfaechersystematik/n242"
assert len(n242.children) == 0
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment