Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions fairscape_models/activity.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from pydantic import BaseModel, Field, ConfigDict, model_validator
from typing import Optional, List, Union
from pydantic import Field, ConfigDict, field_validator
from typing import Optional, List, Union, Any

from fairscape_models.fairscape_base import IdentifierValue
from fairscape_models.fairscape_base import IdentifierValue, Identifier, extractGUID
from fairscape_models._version import __version__

class Activity(BaseModel):
class Activity(Identifier):
"""Base class for Activity types (Computation, Annotation, Experiment)"""
guid: str = Field(alias="@id")
name: str
Expand All @@ -20,3 +20,9 @@ class Activity(BaseModel):
fairscapeVersion: str = __version__

model_config = ConfigDict(extra="allow", populate_by_name=True)

# TODO extract guids from all subfields
# @field_validator('generated', mode='before')
# @classmethod
# def extract_guid_generated(cls, value: Any) -> Any:
# pass
6 changes: 3 additions & 3 deletions fairscape_models/biochem_entity.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from pydantic import BaseModel, Field, ConfigDict, model_validator
from pydantic import Field, ConfigDict, model_validator
from typing import Optional, List, Union

from fairscape_models.fairscape_base import IdentifierValue, IdentifierPropertyValue
from fairscape_models.fairscape_base import IdentifierValue, IdentifierPropertyValue, Identifier
from fairscape_models._version import __version__

class BioChemEntity(BaseModel):
class BioChemEntity(Identifier):
""" Pydantic model for the Schema.org BioChemEntity datatype

This class can apply to Protiens, Genes, Chemical Entities, or Biological Samples
Expand Down
10 changes: 6 additions & 4 deletions fairscape_models/digital_object.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from pydantic import BaseModel, Field, ConfigDict, model_validator
from pydantic import Field, ConfigDict, field_validator
from typing import Optional, List, Union

from fairscape_models.fairscape_base import IdentifierValue
from fairscape_models.fairscape_base import IdentifierValue, Identifier
from fairscape_models._version import __version__

class DigitalObject(BaseModel):
class DigitalObject(Identifier):
"""Base class for DigitalObject types (Dataset, Software, MLModel)"""
guid: str = Field(alias="@id")
guid: str = Field(alias="@id", pattern="^ark:[0-9]{5}/.+$")
name: str
metadataType: Optional[Union[List[str], str]] = Field(default=['prov:Entity', "https://w3id.org/EVI#DigitalObject"], alias="@type")
author: Union[str, IdentifierValue, List[Union[str, IdentifierValue]]]
Expand All @@ -28,3 +28,5 @@ class DigitalObject(BaseModel):
wasAttributedTo: Optional[List[Union[str, IdentifierValue]]] = Field(default=[], alias="prov:wasAttributedTo")

model_config = ConfigDict(extra="allow", populate_by_name=True)

# TODO extract guids from all subfields
64 changes: 60 additions & 4 deletions fairscape_models/fairscape_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
BaseModel,
ConfigDict,
Field,
BeforeValidator
BeforeValidator,
field_validator
)
from pydantic.networks import AnyUrl
from typing import (
List,
Optional,
Dict,
Union
Union,
Any
)
import re
from typing_extensions import Annotated
from enum import Enum

Expand Down Expand Up @@ -67,6 +70,20 @@
}
}

def extractGUID(inputString: str | None) -> str|None:
"""
Given an input ARK extract the normalized ARK, if validation fails return the input.
"""
try:
match = re.search(
pattern="ark:[0-9]{5}/.+$",
string=inputString
)
return match.group()
except AttributeError:
return inputString


class ClassType(str, Enum):
DATASET = 'Dataset'
SOFTWARE = 'Software'
Expand Down Expand Up @@ -123,16 +140,55 @@ class IdentifierPropertyValue(BaseModel):


class Identifier(BaseModel):
"""
The Base Model for any Metadata element in FAIRSCAPE.

Every instance must have a GUID in the form of an ARK (archival resource key),
a metadata type (https://www.w3.org/TR/json-ld/#specifying-the-type), and a name specified as a string.
Every model must have these attributes, and may have any other attributes as specified by the `ConfigDict(extra='allow')`.

For the guid property, preprocessing is preformed by the field validator `Identifier.extract_guid`.
This method preforms a regex search to find the identifier within the passed value.
As ARKs may be specified as full IRIS or URLs pointing to several different resolvers, arks are stripped.
The guid for all fairscape_models clases should follow the regex `"ark:[0-9]{5}/.+$"`.

This guid preprocessing is also preformed on isPartOf.
"""
model_config = ConfigDict(extra='allow')
guid: str = Field(
title="guid",
alias="@id"
alias="@id",
pattern=IdentifierPattern
)
metadataType: ValidatedClassType = Field(
metadataType: Optional[Union[List[str], str]] = Field(
title="metadataType",
alias="@type"
)
name: str = Field(...)
isPartOf: Optional[Union[List[str], str]] = Field(default=[])

@field_validator('guid', mode='before')
@classmethod
def extract_guid(cls, value: Any)-> Any:
"""
Extract the ARK from the guid field, runs before validation against regex.
"""
return extractGUID(value)


@field_validator('isPartOf', mode='before')
@classmethod
def extract_guid_is_part_of(cls, value: Any)-> Any:
"""
Extract GUID from isPartOf Properties, normalizing the form of the ark.
"""
if value:
if isinstance(value, str):
return extractGUID(value)
if isinstance(value, list):
return [extractGUID(elem) for elem in value]
else:
return value


class FairscapeBaseModel(Identifier):
Expand Down
2 changes: 2 additions & 0 deletions fairscape_models/rocrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ def cleanIdentifiers(self):
def cleanGUID(metadata):
""" Clean metadata guid property from full urls to ark:{NAAN}/{postfix}
"""
# TODO should be removed

if hasattr(metadata, 'guid') and isinstance(metadata.guid, str) and "http" in metadata.guid:
# old metadata parsing
#metadata.guid = urllib.parse.urlparse(metadata.guid).path.lstrip('/')
Expand Down
Loading