Skip to content

Commit

Permalink
moving app/resource es indexing apis under /indexes/ namespaces
Browse files Browse the repository at this point in the history
  • Loading branch information
snyaggarwal committed Feb 4, 2021
1 parent 6dac55d commit 1b361b0
Show file tree
Hide file tree
Showing 15 changed files with 123 additions and 87 deletions.
7 changes: 1 addition & 6 deletions core/collections/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
include_facets_header, sort_asc_param, sort_desc_param, updated_since_param, include_retired_param, limit_param
from core.common.tasks import add_references, export_collection
from core.common.utils import compact_dict_by_values, parse_boolean_query_param
from core.common.views import BaseAPIView, BaseLogoView, ResourceIndexView
from core.common.views import BaseAPIView, BaseLogoView

logger = logging.getLogger('oclapi')

Expand Down Expand Up @@ -597,11 +597,6 @@ def handle_export_version(self):
return status.HTTP_409_CONFLICT


class CollectionsIndexView(ResourceIndexView):
serializer_class = CollectionListSerializer
model = Collection


class CollectionSummaryView(CollectionBaseView, RetrieveAPIView):
serializer_class = CollectionSummaryDetailSerializer
permission_classes = (CanViewConceptDictionary,)
Expand Down
4 changes: 4 additions & 0 deletions core/common/swagger_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@
ids_param = openapi.Parameter(
'ids', openapi.IN_FORM, description="Resource Ids", type=openapi.TYPE_STRING
)
resources_body_param = openapi.Parameter(
'resource', openapi.IN_PATH, type=openapi.TYPE_STRING,
enum=['mappings', 'concepts', 'sources', 'orgs', 'users', 'collections']
)
parallel_threads_param = openapi.Parameter(
'parallel', openapi.IN_FORM, description="Parallel threads count (default: 5, max: 10)", type=openapi.TYPE_INTEGER
)
14 changes: 13 additions & 1 deletion core/common/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
from core.common.utils import (
compact_dict_by_values, to_snake_case, flower_get, task_exists, parse_bulk_import_task_id,
to_camel_case,
drop_version, is_versioned_uri, separate_version, to_parent_uri, jsonify_safe, es_get)
drop_version, is_versioned_uri, separate_version, to_parent_uri, jsonify_safe, es_get,
get_resource_class_from_resource_name)
from core.concepts.models import Concept, LocalizedText
from core.mappings.models import Mapping
from core.orgs.models import Organization
Expand Down Expand Up @@ -626,6 +627,17 @@ def test_jsonify_safe(self):
self.assertEqual(jsonify_safe('foobar'), 'foobar')
self.assertEqual(jsonify_safe('{"foo": "bar"}'), dict(foo='bar'))

def test_get_resource_class_from_resource_name(self):
self.assertEqual(get_resource_class_from_resource_name('mappings').__name__, 'Mapping')
self.assertEqual(get_resource_class_from_resource_name('sources').__name__, 'Source')
self.assertEqual(get_resource_class_from_resource_name('source').__name__, 'Source')
self.assertEqual(get_resource_class_from_resource_name('collections').__name__, 'Collection')
self.assertEqual(get_resource_class_from_resource_name('collection').__name__, 'Collection')
for name in ['orgs', 'organizations', 'org', 'ORG']:
self.assertEqual(get_resource_class_from_resource_name(name).__name__, 'Organization')
for name in ['user', 'USer', 'user_profile', 'USERS']:
self.assertEqual(get_resource_class_from_resource_name(name).__name__, 'UserProfile')


class BaseModelTest(OCLTestCase):
def test_model_name(self):
Expand Down
27 changes: 27 additions & 0 deletions core/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,3 +458,30 @@ def web_url():
if not settings.ENV or settings.ENV in ['development', 'ci']:
return 'http://localhost:4000'
return settings.API_BASE_URL.replace('api.', '')


def get_resource_class_from_resource_name(resource): # pylint: disable=too-many-return-statements
if not resource:
return resource

name = resource.lower()
if name in ['concepts', 'concept']:
from core.concepts.models import Concept
return Concept
if name in ['mappings', 'mapping']:
from core.mappings.models import Mapping
return Mapping
if name in ['users', 'user', 'user_profiles', 'user_profile', 'userprofiles', 'userprofile']:
from core.users.models import UserProfile
return UserProfile
if name in ['orgs', 'org', 'organizations', 'organization']:
from core.orgs.models import Organization
return Organization
if name in ['sources', 'source']:
from core.sources.models import Source
return Source
if name in ['collections', 'collection']:
from core.collections.models import Collection
return Collection

return None
25 changes: 1 addition & 24 deletions core/common/views.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.utils.functional import cached_property
from drf_yasg.utils import swagger_auto_schema
from elasticsearch_dsl import Q
from pydash import get, compact
from pydash import get
from rest_framework import response, generics, status
from rest_framework.generics import ListAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.parsers import MultiPartParser
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView

from core.common.constants import SEARCH_PARAM, LIST_DEFAULT_LIMIT, CSV_DEFAULT_LIMIT, \
LIMIT_PARAM, NOT_FOUND, MUST_SPECIFY_EXTRA_PARAM_IN_BODY, INCLUDE_RETIRED_PARAM, VERBOSE_PARAM, HEAD
from core.common.mixins import PathWalkerMixin
from core.common.permissions import IsSuperuser
from core.common.serializers import RootSerializer
from core.common.swagger_parameters import ids_param
from core.common.utils import compact_dict_by_values, to_snake_case, to_camel_case
from core.concepts.permissions import CanViewParentDictionary, CanEditParentDictionary
from core.orgs.constants import ORG_OBJECT_TYPE
Expand Down Expand Up @@ -552,21 +547,3 @@ def post(self, request, *args, **kwargs): # pylint: disable=unused-argument
obj.upload_base64_logo(data.get('base64'), 'logo.png')

return Response(self.get_serializer_class()(obj).data, status=status.HTTP_200_OK)


class ResourceIndexView(APIView):
permission_classes = (IsSuperuser,)
parser_classes = (MultiPartParser,)
model = None
filter_attr = 'mnemonic__in'

@swagger_auto_schema(manual_parameters=[ids_param])
def post(self, request):
ids = request.data.get('ids', None)
if ids:
ids = [i.strip() for i in compact(ids.split(','))]

for resource in self.model.objects.filter(**{self.filter_attr: ids}):
resource.save()

return Response(status=status.HTTP_200_OK)
7 changes: 1 addition & 6 deletions core/concepts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
include_facets_header, updated_since_param, include_inverse_mappings_param, include_retired_param,
compress_header)
from core.common.views import SourceChildCommonBaseView, SourceChildExtrasView, \
SourceChildExtraRetrieveUpdateDestroyView, ResourceIndexView
SourceChildExtraRetrieveUpdateDestroyView
from core.concepts.constants import PARENT_VERSION_NOT_LATEST_CANNOT_UPDATE_CONCEPT
from core.concepts.documents import ConceptDocument
from core.concepts.models import Concept, LocalizedText
Expand Down Expand Up @@ -389,8 +389,3 @@ class ConceptExtrasView(SourceChildExtrasView, ConceptBaseView):
class ConceptExtraRetrieveUpdateDestroyView(SourceChildExtraRetrieveUpdateDestroyView, ConceptBaseView):
serializer_class = ConceptDetailSerializer
model = Concept


class ConceptsIndexView(ResourceIndexView):
serializer_class = ConceptListSerializer
model = Concept
2 changes: 0 additions & 2 deletions core/importers/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,4 @@
views.BulkImportView.as_view(),
name='bulk-import-detail'
),
re_path(r"^populate-indexes/$", views.PopulateESIndexView.as_view(), name='populate-es-indexes'),
re_path(r"^rebuild-indexes/$", views.RebuildESIndexView.as_view(), name='rebuild-es-indexes'),
]
30 changes: 1 addition & 29 deletions core/importers/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from core.common.permissions import IsSuperuser
from core.common.services import RedisService
from core.common.swagger_parameters import update_if_exists_param, task_param, result_param, username_param, \
file_upload_param, file_url_param, apps_param, parallel_threads_param
from core.common.tasks import rebuild_indexes, populate_indexes
file_upload_param, file_url_param, parallel_threads_param
from core.common.utils import parse_bulk_import_task_id, task_exists, flower_get, queue_bulk_import
from core.importers.constants import ALREADY_QUEUED, INVALID_UPDATE_IF_EXISTS, NO_CONTENT_TO_IMPORT

Expand Down Expand Up @@ -202,29 +200,3 @@ def post(self, request, import_queue=None):
return Response(dict(exception=NO_CONTENT_TO_IMPORT), status=status.HTTP_400_BAD_REQUEST)

return import_response(self.request, import_queue, file.read(), None, True)


class BaseESIndexView(APIView): # pragma: no cover
permission_classes = (IsSuperuser,)
parser_classes = (MultiPartParser,)
task = None

@swagger_auto_schema(manual_parameters=[apps_param])
def post(self, request):
apps = request.data.get('apps', None)
if apps:
apps = apps.split(',')
result = self.task.delay(apps)

return Response(
dict(state=result.state, username=self.request.user.username, task=result.task_id, queue='default'),
status=status.HTTP_202_ACCEPTED
)


class RebuildESIndexView(BaseESIndexView): # pragma: no cover
task = rebuild_indexes


class PopulateESIndexView(BaseESIndexView): # pragma: no cover
task = populate_indexes
Empty file added core/indexes/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions core/indexes/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.urls import path

from core.indexes import views

urlpatterns = [
path("apps/populate/", views.PopulateESIndexView.as_view(), name='populate-indexes'),
path("apps/rebuild/", views.RebuildESIndexView.as_view(), name='rebuild-indexes'),
path("resources/<str:resource>/", views.ResourceIndexView.as_view(), name='resource-indexes'),
]
61 changes: 61 additions & 0 deletions core/indexes/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from drf_yasg.utils import swagger_auto_schema
from pydash import compact
from rest_framework import status
from rest_framework.parsers import MultiPartParser
from rest_framework.response import Response
from rest_framework.views import APIView

from core.common.permissions import IsSuperuser
from core.common.swagger_parameters import apps_param, ids_param, resources_body_param
from core.common.tasks import rebuild_indexes, populate_indexes
from core.common.utils import get_resource_class_from_resource_name


class BaseESIndexView(APIView): # pragma: no cover
permission_classes = (IsSuperuser,)
parser_classes = (MultiPartParser,)
task = None

@swagger_auto_schema(manual_parameters=[apps_param])
def post(self, request):
apps = request.data.get('apps', None)
if apps:
apps = apps.split(',')
result = self.task.delay(apps)

return Response(
dict(state=result.state, username=self.request.user.username, task=result.task_id, queue='default'),
status=status.HTTP_202_ACCEPTED
)


class RebuildESIndexView(BaseESIndexView): # pragma: no cover
task = rebuild_indexes


class PopulateESIndexView(BaseESIndexView): # pragma: no cover
task = populate_indexes


class ResourceIndexView(APIView):
permission_classes = (IsSuperuser,)
parser_classes = (MultiPartParser,)

@swagger_auto_schema(manual_parameters=[ids_param, resources_body_param])
def post(self, _, resource):
model = get_resource_class_from_resource_name(resource)

if not model:
return Response(status.HTTP_404_NOT_FOUND)

ids = self.request.data.get('ids', None)
if ids:
ids = [i.strip() for i in compact(ids.split(','))]

if not ids:
return Response(status.HTTP_400_BAD_REQUEST)

for instance in model.objects.filter(**{"{}__in".format(model.mnemonic_attr): ids}):
instance.save()

return Response(status=status.HTTP_202_ACCEPTED)
7 changes: 1 addition & 6 deletions core/mappings/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
include_facets_header, updated_since_param, include_retired_param,
compress_header)
from core.common.views import SourceChildCommonBaseView, SourceChildExtrasView, \
SourceChildExtraRetrieveUpdateDestroyView, ResourceIndexView
SourceChildExtraRetrieveUpdateDestroyView
from core.concepts.permissions import CanEditParentDictionary, CanViewParentDictionary
from core.mappings.constants import PARENT_VERSION_NOT_LATEST_CANNOT_UPDATE_MAPPING
from core.mappings.documents import MappingDocument
Expand Down Expand Up @@ -251,8 +251,3 @@ class MappingExtrasView(SourceChildExtrasView, MappingBaseView):
class MappingExtraRetrieveUpdateDestroyView(SourceChildExtraRetrieveUpdateDestroyView, MappingBaseView):
serializer_class = MappingDetailSerializer
model = Mapping


class MappingsIndexView(ResourceIndexView):
serializer_class = MappingListSerializer
model = Mapping
7 changes: 1 addition & 6 deletions core/sources/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
page_param, verbose_param, include_retired_param, updated_since_param, include_facets_header, compress_header
from core.common.tasks import export_source
from core.common.utils import parse_boolean_query_param, compact_dict_by_values
from core.common.views import BaseAPIView, BaseLogoView, ResourceIndexView
from core.common.views import BaseAPIView, BaseLogoView
from core.sources.constants import DELETE_FAILURE, DELETE_SUCCESS, VERSION_ALREADY_EXISTS
from core.sources.documents import SourceDocument
from core.sources.models import Source
Expand Down Expand Up @@ -379,11 +379,6 @@ def handle_export_version(self):
return status.HTTP_409_CONFLICT


class SourcesIndexView(ResourceIndexView):
serializer_class = SourceListSerializer
model = Source


class SourceSummaryView(SourceBaseView, RetrieveAPIView):
serializer_class = SourceSummaryDetailSerializer
permission_classes = (CanViewConceptDictionary,)
Expand Down
9 changes: 2 additions & 7 deletions core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@

import core.concepts.views as concept_views
import core.mappings.views as mapping_views
import core.collections.views as collection_views
import core.sources.views as sources_views
from core.common.constants import NAMESPACE_PATTERN
from core.common.utils import get_api_base_url
from core.importers.views import BulkImportView
from core.common.views import RootView
from core.importers.views import BulkImportView

SchemaView = get_schema_view(
openapi.Info(
Expand All @@ -50,15 +48,12 @@
path('users/', include('core.users.urls'), name='users_urls'),
path('user/', include('core.users.user_urls'), name='current_user_urls'),
path('orgs/', include('core.orgs.urls'), name='orgs_url'),
path('sources/indexes/', sources_views.SourcesIndexView.as_view(), name='sources-indexes'),
path('sources/', include('core.sources.urls'), name='sources_url'),
path('collections/indexes/', collection_views.CollectionsIndexView.as_view(), name='collections-indexes'),
path('collections/', include('core.collections.urls'), name='collections_urls'),
path('concepts/indexes/', concept_views.ConceptsIndexView.as_view(), name='concepts-indexes'),
path('concepts/', concept_views.ConceptVersionListAllView.as_view(), name='all_concepts_urls'),
path('mappings/indexes/', mapping_views.MappingsIndexView.as_view(), name='mappings-indexes'),
path('mappings/', mapping_views.MappingVersionListAllView.as_view(), name='all_mappings_urls'),
path('importers/', include('core.importers.urls'), name='importer_urls'),
path('indexes/', include('core.indexes.urls'), name='indexes_urls'),

# just for ocldev
re_path(
Expand Down
1 change: 1 addition & 0 deletions core/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Meta:
website = models.TextField(null=True, blank=True)
verified = models.BooleanField(default=True)
verification_token = models.TextField(null=True, blank=True)
mnemonic_attr = 'username'

@property
def user(self):
Expand Down

0 comments on commit 1b361b0

Please sign in to comment.