Skip to content

Commit

Permalink
conversion proof of concept #265
Browse files Browse the repository at this point in the history
  • Loading branch information
Viktor Bozhinov committed Jan 7, 2022
1 parent 8267de0 commit 85c4bfc
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 24 deletions.
113 changes: 89 additions & 24 deletions datagateway_api/src/search_api/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from abc import ABC, abstractclassmethod
import abc
from abc import ABC
from datetime import datetime
import sys
from typing import ClassVar, List, Optional, Union

from pydantic import (
Expand All @@ -12,11 +14,74 @@
StrictStr,
)

from datagateway_api.src.search_api.panosc_mappings import mappings


# TODO - Merge this with `get_icat_mapping` from src\search_api\filters.py
def _get_icat_mapping(panosc_entity_name, field_name):
icat_mapping = mappings.mappings[panosc_entity_name][field_name]

if isinstance(icat_mapping, str):
# Field name
icat_field_name = icat_mapping
if isinstance(icat_mapping, dict):
# Relation - JSON format: {PaNOSC entity name: ICAT related field name}
panosc_entity_name = list(icat_mapping.keys())[0]
icat_field_name = icat_mapping[panosc_entity_name]

return panosc_entity_name, icat_field_name


def _get_icat_field_value(icat_field_name, icat_data):
icat_field_name = icat_field_name.split(".")
value = icat_data
for f in icat_field_name:
value = value[f]

return value


class PaNOSCAttribute(ABC, BaseModel):
@abstractclassmethod
def from_icat(self):
pass
@classmethod
@abc.abstractmethod
def from_icat(cls, icat_data): # noqa: B902, N805
model_fields = cls.__fields__

model_data = {}
for field in model_fields:
# Some fields have aliases so we must use them when creating a model instance.
# If a field does not have an alias then the `alias` property holds the name
# of the field
field_alias = cls.__fields__[field].alias

panosc_entity_name, icat_field_name = _get_icat_mapping(
cls.__name__, field_alias,
)
try:
field_value = _get_icat_field_value(icat_field_name, icat_data)
except KeyError:
continue

if panosc_entity_name != cls.__name__:
# If we are here, it means that the field references another model so we
# have to get hold of its class definition and call its `from_icat` method
# to create an instance of itself with the ICAT data provided. Doing this
# allows for recursion.
data = icat_data[icat_field_name]
if not isinstance(data, list):
data = [data]

# Get the class of the referenced model
panosc_model_attr = getattr(sys.modules[__name__], panosc_entity_name)
field_value = [panosc_model_attr.from_icat(d) for d in data]

field_type = cls.__fields__[field].outer_type_._name
if field_type != "List":
field_value = field_value[0]

model_data[field_alias] = field_value

return cls(**model_data)


class Affiliation(PaNOSCAttribute):
Expand All @@ -33,8 +98,8 @@ class Affiliation(PaNOSCAttribute):
members: Optional[List["Member"]]

@classmethod
def from_icat(cls):
pass
def from_icat(cls, icat_query_data):
return super(Affiliation, cls).from_icat(icat_query_data)


class Dataset(PaNOSCAttribute):
Expand All @@ -59,8 +124,8 @@ class Dataset(PaNOSCAttribute):
samples: Optional[List["Sample"]]

@classmethod
def from_icat(cls):
pass
def from_icat(cls, icat_query_data):
return super(Dataset, cls).from_icat(icat_query_data)


class Document(PaNOSCAttribute):
Expand All @@ -87,8 +152,8 @@ class Document(PaNOSCAttribute):
parameters: Optional[List["Parameter"]]

@classmethod
def from_icat(cls):
pass
def from_icat(cls, icat_query_data):
return super(Document, cls).from_icat(icat_query_data)


class File(PaNOSCAttribute):
Expand All @@ -104,8 +169,8 @@ class File(PaNOSCAttribute):
dataset: Dataset

@classmethod
def from_icat(cls):
pass
def from_icat(cls, icat_query_data):
return super(File, cls).from_icat(icat_query_data)


class Instrument(PaNOSCAttribute):
Expand All @@ -120,8 +185,8 @@ class Instrument(PaNOSCAttribute):
datasets: Optional[List[Dataset]]

@classmethod
def from_icat(cls):
pass
def from_icat(cls, icat_query_data):
return super(Instrument, cls).from_icat(icat_query_data)


class Member(PaNOSCAttribute):
Expand All @@ -137,8 +202,8 @@ class Member(PaNOSCAttribute):
affiliation: Optional[Affiliation]

@classmethod
def from_icat(cls):
pass
def from_icat(cls, icat_query_data):
return super(Member, cls).from_icat(icat_query_data)


class Parameter(PaNOSCAttribute):
Expand Down Expand Up @@ -169,8 +234,8 @@ def validate_dataset_and_document(cls, values): # noqa: B902, N805
return values

@classmethod
def from_icat(cls):
pass
def from_icat(cls, icat_query_data):
return super(Parameter, cls).from_icat(icat_query_data)


class Person(PaNOSCAttribute):
Expand All @@ -188,8 +253,8 @@ class Person(PaNOSCAttribute):
members: Optional[List[Member]]

@classmethod
def from_icat(cls):
pass
def from_icat(cls, icat_query_data):
return super(Person, cls).from_icat(icat_query_data)


class Sample(PaNOSCAttribute):
Expand All @@ -204,8 +269,8 @@ class Sample(PaNOSCAttribute):
datasets: Optional[List[Dataset]]

@classmethod
def from_icat(cls):
pass
def from_icat(cls, icat_query_data):
return super(Sample, cls).from_icat(icat_query_data)


class Technique(PaNOSCAttribute):
Expand All @@ -219,8 +284,8 @@ class Technique(PaNOSCAttribute):
datasets: Optional[List[Dataset]]

@classmethod
def from_icat(cls):
pass
def from_icat(cls, icat_query_data):
return super(Technique, cls).from_icat(icat_query_data)


# The below models reference other models that may not be defined during their
Expand Down
28 changes: 28 additions & 0 deletions test/search_api/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import json

import datagateway_api.src.search_api.models as models


class TestModels:
def test_from_icat_person_model(self):
expected_model_data = {
"id": "1",
"fullName": "Test fullname",
"orcid": "1111",
"researcherId": None,
"firstName": "Test given name",
"lastName": "Test family name",
"members": None,
}
# TODO: `id` is returned as `int` from ICAT whereas Person model expects `str`
icat_data = {
"id": "1",
"fullName": "Test fullname",
"orcidId": "1111",
"givenName": "Test given name",
"familyName": "Test family name",
}

person_model = models.Person.from_icat(icat_data)

assert person_model.json(by_alias=True) == json.dumps(expected_model_data)

0 comments on commit 85c4bfc

Please sign in to comment.