Skip to content

Commit

Permalink
first attempt for dynamic datalink
Browse files Browse the repository at this point in the history
  • Loading branch information
agy-why committed Sep 1, 2023
1 parent acbf274 commit 587dfef
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 25 deletions.
63 changes: 59 additions & 4 deletions daiquiri/datalink/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from daiquiri.core.constants import ACCESS_LEVEL_PUBLIC
from daiquiri.core.utils import get_doi_url, import_class

from .constants import DATALINK_RELATION_TYPES
from .constants import DATALINK_RELATION_TYPES, DATALINK_FIELDS
from .models import Datalink


Expand All @@ -30,6 +30,14 @@ class BaseDatalinkAdapter(object):
* get_<resource_type>_links(self, resource): returns a resource into a list
of rows of the datalink table (list of python dicts)
There are two further adapters, which do not declare resources:
* DynamicDatalinkAdapter: the latter does not declare a resource, but will inject on the fly
extra datalink entries according to the method: get_dyn_datalink_links()
* QueryJobDatalinkAdapterMixin: The latter does not declare a resource either, but it injects
extra context information for the datalink viewer.
See the mixins below for an example.
"""

Expand All @@ -56,14 +64,38 @@ def get_links(self, resource_type, resource):
return getattr(self, 'get_%s_links' % resource_type)(resource)

def get_context_data(self, request, **kwargs):
'''Get the datalink related context data for a given request
'''
context = {}

if 'ID' in kwargs:
context['datalinks'] = Datalink.objects.filter(ID=kwargs['ID']).order_by('semantics')
field_names = [field['name'] for field in DATALINK_FIELDS]
# more precise would be to use a serializer instead of list(QuerySet.values())
context['datalinks'] = list(Datalink.objects.filter(ID=kwargs['ID']).order_by('semantics').values())
context['ID'] = kwargs['ID']

return context

def get_datalink_rows(self, identifiers, **kwargs):
'''Get the list of datalink entries for the provided identifiers
'''

# get the datalink entries from Datalink Table and metadata (Table and Schema)
field_names = [field['name'] for field in DATALINK_FIELDS]
rows = list(Datalink.objects.filter(ID__in=identifiers).values_list(*field_names))

# get the dynamic datalink entries
dyn_rows = [tuple(link.values()) for link in self.get_dyn_datalink_links(identifiers)]
rows = rows + dyn_rows

# check for missing IDs and return error message
for identifier in identifiers:
if not any(filter(lambda row: row[0] == identifier, rows)):
rows.append((identifier, None, None, 'NotFoundFault: {}'.format(identifier), None, None, None, None))

return rows



class TablesDatalinkAdapterMixin(object):
'''
Expand Down Expand Up @@ -226,6 +258,29 @@ def get_table_links(self, table):
return table_links


class DynamicDatalinkAdapterMixin(object):
'''Define the interface to dynamically add datalink entries
'''

def get_dyn_datalink_links(self, IDs, **kwargs):
'''No dynamically generated entries. Can be overwriten.
this method should return a list of dict with the following keys:
ID, access_url, service_def, error_message, description, semantics, content_type, content_length
'''
return([])

def get_context_data(self, request, **kwargs):
'''Inject dynamically generated Datalink entries into the context for the daiquiri.datalinks.views.datalink View
'''
context = super().get_context_data(request, **kwargs)

if 'ID' in kwargs:
context['datalinks'] = context['datalinks'] + self.get_dyn_datalink_links([kwargs['ID']], **kwargs)

return context


class QueryJobDatalinkAdapterMixin(object):
'''
Injects the query job into the context data for the daiquiri.datalinks.views.datalink view
Expand All @@ -244,8 +299,8 @@ def get_context_data(self, request, **kwargs):

return context


class DefaultDatalinkAdapter(MetadataDatalinkAdapterMixin,
class DefaultDatalinkAdapter(DynamicDatalinkAdapterMixin,
MetadataDatalinkAdapterMixin,
TablesDatalinkAdapterMixin,
QueryJobDatalinkAdapterMixin,
BaseDatalinkAdapter):
Expand Down
16 changes: 7 additions & 9 deletions daiquiri/datalink/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
from daiquiri.core.generators import generate_votable

from .constants import DATALINK_FIELDS, DATALINK_CONTENT_TYPE

from .adapter import DatalinkAdapter
from .models import Datalink


class SyncDatalinkJobViewSet(viewsets.GenericViewSet):
'''Generate the datalink VOTable
'''

def list(self, request):
return self.perform_sync_job(request, request.GET)
Expand All @@ -20,17 +21,14 @@ def create(self, request):
return self.perform_sync_job(request, request.POST)

def perform_sync_job(self, request, data):
rows = []

if 'ID' in data:
identifiers = data.getlist('ID')
field_names = [field['name'] for field in DATALINK_FIELDS]
rows = list(Datalink.objects.filter(ID__in=identifiers).values_list(*field_names))

# check for missing IDs and return error message
for identifier in identifiers:
if not any(filter(lambda row: row[0] == identifier, rows)):
rows.append((identifier, None, None, 'NotFoundFault: {}'.format(identifier), None, None, None, None))
adapter = DatalinkAdapter()

# get all datalink entries (DatalinkTable, Metadata and Dynamic)
rows = adapter.get_datalink_rows(identifiers)

if data.get('RESPONSEFORMAT') == 'application/json':
return JsonResponse({
Expand Down
25 changes: 13 additions & 12 deletions daiquiri/oai/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from daiquiri.core.adapter import DatabaseAdapter
from daiquiri.core.constants import ACCESS_LEVEL_PUBLIC
from daiquiri.core.utils import get_doi, import_class
from daiquiri.datalink.adapter import DatalinkAdapter


def OaiAdapter():
Expand Down Expand Up @@ -219,21 +220,21 @@ def get_datacite_datalink_serializer_class(self):
return import_class('daiquiri.datalink.serializers.DataciteSerializer')

def get_datalink_list(self):
for table in self.tables:
schema_name, table_name = table.split('.')
rows = DatabaseAdapter().fetch_rows(schema_name, table_name, column_names=['ID', 'access_url'],
page_size=0, filters={'semantics': '#doi'})

for ID, access_url in rows:
adapter = DatalinkAdapter()
# get all datalink entries: DatalinkTables, Metadata and Dynamic
rows = adapter.get_datalink_rows([pk])

for ID, access_url, _, _, _, semantics, _, _ in rows:
if semantics == '#doi':
yield 'datalink', {'id': str(ID), 'doi': get_doi(access_url)}


def get_datalink(self, pk):
rows = []
for table in self.tables:
schema_name, table_name = table.split('.')
rows += DatabaseAdapter().fetch_rows(schema_name, table_name, column_names=[
'access_url', 'description', 'semantics', 'content_type', 'content_length'
], filters={'ID': str(pk)})

adapter = DatalinkAdapter()
# get all datalink entries: DatalinkTables, Metadata and Dynamic
rows = adapter.get_datalink_rows([pk])

datalink = {
'formats': [],
Expand All @@ -245,7 +246,7 @@ def get_datalink(self, pk):
],
'related_identifiers': []
}
for access_url, description, semantics, content_type, content_length in rows:
for _, access_url, _, _, description, semantics, content_type, content_length in rows:
if semantics == '#doi':
datalink['doi'] = get_doi(access_url)
datalink['title'] = description
Expand Down

0 comments on commit 587dfef

Please sign in to comment.