From 744b5f9303ab53458be6e4a7f47eeb041d59c4a5 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Wed, 10 Apr 2024 20:40:13 +0530 Subject: [PATCH 1/3] #35 renaming of methods and functions --- backend/composer/admin.py | 11 ++++- backend/composer/api/filtersets.py | 62 +++++++++++++++++++++++++++++ backend/composer/api/serializers.py | 15 +++++++ backend/composer/api/urls.py | 2 + backend/composer/api/views.py | 32 +++++++++++++++ backend/composer/models.py | 3 ++ 6 files changed, 124 insertions(+), 1 deletion(-) diff --git a/backend/composer/admin.py b/backend/composer/admin.py index ea779d1d..34af55a4 100644 --- a/backend/composer/admin.py +++ b/backend/composer/admin.py @@ -100,7 +100,7 @@ class SentenceAdmin( class AnatomicalEntityAdmin(admin.ModelAdmin): search_fields = ('simple_entity__name', 'region_layer__layer__name', 'region_layer__region__name') autocomplete_fields = ('simple_entity', 'region_layer') - list_display = ('simple_entity', 'region_layer', "synonyms") + list_display = ('simple_entity', 'region_layer', "synonyms", "ontology_uri") list_display_links = ('simple_entity', 'region_layer') inlines = (SynonymInline,) @@ -114,6 +114,15 @@ def get_queryset(self, request: HttpRequest) -> QuerySet[Any]: def synonyms(self, obj): synonyms = obj.synonyms.all() return ', '.join([synonym.name for synonym in synonyms]) + + @admin.display(description="Ontology URI") + def ontology_uri(self, obj): + if obj.simple_entity: + return obj.simple_entity.ontology_uri + elif obj.region_layer: + return f'{obj.region_layer.region.ontology_uri}, {obj.region_layer.layer.ontology_uri}' + return None + class AnatomicalEntityMetaAdmin(admin.ModelAdmin): diff --git a/backend/composer/api/filtersets.py b/backend/composer/api/filtersets.py index 6fe1bc3b..db53eb88 100644 --- a/backend/composer/api/filtersets.py +++ b/backend/composer/api/filtersets.py @@ -13,6 +13,8 @@ Via, Specie, Destination, ) +from django_filters import rest_framework +from django_filters import CharFilter, BaseInFilter def field_has_content(queryset, name, value): @@ -95,6 +97,66 @@ class Meta: fields = [] +class ListCharFilter(BaseInFilter, CharFilter): + pass + + +class KnowledgeStatementFilterSet(rest_framework.FilterSet): + via_uris = ListCharFilter(method='filter_via_uris', label='Via URI') + destination_uris = ListCharFilter(method='filter_destination_uris', label='Destination URI') + origin_uris = ListCharFilter(method='filter_origin_uris', label='Origin URI') + reference_uris = ListCharFilter(method='filter_reference_uris', label='Reference URI') + + class Meta: + model = ConnectivityStatement + fields = ['via_uris', 'destination_uris', 'origin_uris', 'reference_uris'] + distinct = True + + @property + def qs(self): + return super().qs.distinct() + + def filter_reference_uris(self, queryset, name, value): + return queryset.filter(reference_uri__in=value) + + def filter_via_uris(self, queryset, name, value): + via_uris = value + via_ids = Via.objects.none() + for uri in via_uris: + via_ids = via_ids.union( + Via.objects.filter(anatomical_entities__simple_entity__ontology_uri=uri).prefetch_related('anatomical_entities__simple_entity') + .union(Via.objects.filter(anatomical_entities__region_layer__layer__ontology_uri=uri).prefetch_related('anatomical_entities__region_layer__layer')) + .union(Via.objects.filter(anatomical_entities__region_layer__region__ontology_uri=uri).prefetch_related('anatomical_entities__region_layer__region')) + .values_list("id", flat=True) + ) + return queryset.filter(via__in=via_ids) + + def filter_destination_uris(self, queryset, name, value): + destination_uris = value + destination_ids = Destination.objects.none() + for uri in destination_uris: + destination_ids = destination_ids.union( + Destination.objects.filter(anatomical_entities__simple_entity__ontology_uri=uri).prefetch_related('anatomical_entities__simple_entity') + .union(Destination.objects.filter(anatomical_entities__region_layer__layer__ontology_uri=uri).prefetch_related('anatomical_entities__region_layer__layer')) + .union(Destination.objects.filter(anatomical_entities__region_layer__region__ontology_uri=uri).prefetch_related('anatomical_entities__region_layer__region')) + .values_list("id", flat=True) + ) + return queryset.filter(destinations__in=destination_ids) + + def filter_origin_uris(self, queryset, name, value): + origin_uris = value + origin_ids = AnatomicalEntity.objects.none() + for uri in origin_uris: + origin_ids = origin_ids.union( + AnatomicalEntity.objects.filter(simple_entity__ontology_uri=uri).prefetch_related('simple_entity') + .union(AnatomicalEntity.objects.filter(region_layer__layer__ontology_uri=uri).prefetch_related('region_layer__layer')) + .union(AnatomicalEntity.objects.filter(region_layer__region__ontology_uri=uri).prefetch_related('region_layer__region')) + .values_list("id", flat=True) + ) + return queryset.filter(origins__in=origin_ids) + + + class AnatomicalEntityFilter(django_filters.FilterSet): name = django_filters.CharFilter(method="filter_name") exclude_ids = NumberInFilter(field_name='id', exclude=True) diff --git a/backend/composer/api/serializers.py b/backend/composer/api/serializers.py index 0f57045b..89fca4a3 100644 --- a/backend/composer/api/serializers.py +++ b/backend/composer/api/serializers.py @@ -680,3 +680,18 @@ class Meta: "errors" ) + +class KnowledgeStatementSerializer(ConnectivityStatementSerializer): + """Knowledge Statement""" + class Meta(ConnectivityStatementSerializer.Meta): + fields = ( + "id", + "sentence_id", + "species", + "origins", + "vias", + "destinations", + "apinatomy_model", + "phenotype_id", + "phenotype", + ) diff --git a/backend/composer/api/urls.py b/backend/composer/api/urls.py index 0a9c9455..4e7d61e3 100644 --- a/backend/composer/api/urls.py +++ b/backend/composer/api/urls.py @@ -5,6 +5,7 @@ AnatomicalEntityViewSet, PhenotypeViewSet, ConnectivityStatementViewSet, + KnowledgeStatementViewSet, jsonschemas, NoteViewSet, ProfileViewSet, @@ -42,4 +43,5 @@ urlpatterns = [ path("", include(router.urls)), path("jsonschemas/", jsonschemas, name="jsonschemas"), + path("knowledge-statement/", KnowledgeStatementViewSet.as_view(), name="knowledge-statement"), ] diff --git a/backend/composer/api/views.py b/backend/composer/api/views.py index 4eda5a9c..0919556c 100644 --- a/backend/composer/api/views.py +++ b/backend/composer/api/views.py @@ -9,6 +9,8 @@ from rest_framework.renderers import INDENT_SEPARATORS from rest_framework.response import Response from rest_framework.serializers import ValidationError +from rest_framework import generics +from django_filters.rest_framework import DjangoFilterBackend from composer.services.state_services import ( ConnectivityStatementStateService, @@ -17,6 +19,7 @@ from .filtersets import ( SentenceFilter, ConnectivityStatementFilter, + KnowledgeStatementFilterSet, AnatomicalEntityFilter, NoteFilter, ViaFilter, @@ -26,6 +29,7 @@ AnatomicalEntitySerializer, PhenotypeSerializer, ConnectivityStatementSerializer, + KnowledgeStatementSerializer, NoteSerializer, ProfileSerializer, SentenceSerializer, @@ -345,6 +349,34 @@ def partial_update(self, request, *args, **kwargs): return super().partial_update(request, *args, **kwargs) +@extend_schema(tags=["public"]) +class KnowledgeStatementViewSet( + generics.ListAPIView, +): + """ + KnowledgeStatement that only allows GET to get the list of ConnectivityStatements + """ + model = ConnectivityStatement + queryset = ConnectivityStatement.objects.exported() + serializer_class = KnowledgeStatementSerializer + permission_classes = [ + permissions.AllowAny, + ] + filter_backends = [DjangoFilterBackend] + filterset_class = KnowledgeStatementFilterSet + + @property + def allowed_methods(self): + return ['GET'] + + def get_serializer_class(self): + return KnowledgeStatementSerializer + + + def list(self, request, *args, **kwargs): + return super().list(request, *args, **kwargs) + + class TagViewSet(viewsets.ReadOnlyModelViewSet): """ Tag diff --git a/backend/composer/models.py b/backend/composer/models.py index 0f0daf00..95eca7a3 100644 --- a/backend/composer/models.py +++ b/backend/composer/models.py @@ -97,6 +97,9 @@ def get_queryset(self): def excluding_draft(self): return self.get_queryset().exclude(state=CSState.DRAFT) + + def exported(self): + return self.get_queryset().filter(state=CSState.EXPORTED) class SentenceStatementManager(models.Manager): From 159bb06e7968555bda2997760e140c38202bc5a8 Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Wed, 10 Apr 2024 20:40:37 +0530 Subject: [PATCH 2/3] #35 rename reference uri to population uri --- backend/composer/api/filtersets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/composer/api/filtersets.py b/backend/composer/api/filtersets.py index db53eb88..04ebe8e1 100644 --- a/backend/composer/api/filtersets.py +++ b/backend/composer/api/filtersets.py @@ -105,18 +105,18 @@ class KnowledgeStatementFilterSet(rest_framework.FilterSet): via_uris = ListCharFilter(method='filter_via_uris', label='Via URI') destination_uris = ListCharFilter(method='filter_destination_uris', label='Destination URI') origin_uris = ListCharFilter(method='filter_origin_uris', label='Origin URI') - reference_uris = ListCharFilter(method='filter_reference_uris', label='Reference URI') + population_uris = ListCharFilter(method='filter_population_uris', label='Reference URI') class Meta: model = ConnectivityStatement - fields = ['via_uris', 'destination_uris', 'origin_uris', 'reference_uris'] + fields = ['via_uris', 'destination_uris', 'origin_uris', 'population_uris'] distinct = True @property def qs(self): return super().qs.distinct() - def filter_reference_uris(self, queryset, name, value): + def filter_population_uris(self, queryset, name, value): return queryset.filter(reference_uri__in=value) def filter_via_uris(self, queryset, name, value): From a500abb7ee88639f18106078e3dd7c2d42581dcf Mon Sep 17 00:00:00 2001 From: "D. Gopal Krishna" Date: Wed, 10 Apr 2024 20:55:32 +0530 Subject: [PATCH 3/3] #35 use model property instead of repeat logic --- backend/composer/admin.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/backend/composer/admin.py b/backend/composer/admin.py index 34af55a4..108e27b1 100644 --- a/backend/composer/admin.py +++ b/backend/composer/admin.py @@ -117,11 +117,7 @@ def synonyms(self, obj): @admin.display(description="Ontology URI") def ontology_uri(self, obj): - if obj.simple_entity: - return obj.simple_entity.ontology_uri - elif obj.region_layer: - return f'{obj.region_layer.region.ontology_uri}, {obj.region_layer.layer.ontology_uri}' - return None + return obj.ontology_uri