Skip to content

Commit

Permalink
RF: declare our own metaclass so we could centralize population of sc…
Browse files Browse the repository at this point in the history
…hemaKey

Overloading is allowed by simply specifying the name to be used while declaring
the class in the source code.
  • Loading branch information
yarikoptic committed May 25, 2021
1 parent f8821a1 commit ecaeb88
Showing 1 changed file with 18 additions and 61 deletions.
79 changes: 18 additions & 61 deletions dandischema/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Any, Dict, List, Optional, Type, Union

from pydantic import UUID4, BaseModel, ByteSize, EmailStr, Field, HttpUrl, validator
from pydantic.main import ModelMetaclass

from .consts import DANDI_SCHEMA_VERSION
from .model_types import (
Expand Down Expand Up @@ -94,9 +95,16 @@ def encode(self, o):
return super().encode(_sanitize(o))


class DandiBaseModel(BaseModel):
class DandiBaseModelMetaclass(ModelMetaclass):
def __new__(cls, name, bases, dct):
sk_name = dct.pop("schemaKey", None) or name
dct["schemaKey"]: Literal[sk_name] = Field(sk_name, readOnly=True)
objcls = super().__new__(cls, name, bases, dct)
return objcls


class DandiBaseModel(BaseModel, metaclass=DandiBaseModelMetaclass):
id: Optional[str] = Field(description="Uniform resource identifier", readOnly=True)
schemaKey: Literal["DandiBaseModel"] = Field("DandiBaseModel", readOnly=True)

@classmethod
def unvalidated(__pydantic_cls__: Type[BaseModel], **data: Any) -> BaseModel:
Expand Down Expand Up @@ -183,7 +191,6 @@ class PropertyValue(DandiBaseModel):
nskey="schema",
)

schemaKey: Literal["PropertyValue"] = Field("PropertyValue", readOnly=True)
_ldmeta = {"nskey": "schema"}


Expand All @@ -208,45 +215,32 @@ class BaseType(DandiBaseModel):
name: Optional[str] = Field(
description="The name of the item.", max_length=150, nskey="schema"
)
schemaKey: Literal["BaseType"] = Field("BaseType", readOnly=True)
_ldmeta = {"rdfs:subClassOf": ["prov:Entity", "schema:Thing"], "nskey": "dandi"}


class AssayType(BaseType):
"""OBI based identifier for the assay(s) used"""

schemaKey: Literal["AssayType"] = Field("AssayType", readOnly=True)


class SampleType(BaseType):
"""OBI based identifier for the sample type used"""

schemaKey: Literal["SampleType"] = Field("SampleType", readOnly=True)


class Anatomy(BaseType):
"""UBERON or other identifier for anatomical part studied"""

schemaKey: Literal["Anatomy"] = Field("Anatomy", readOnly=True)


class StrainType(BaseType):
"""Identifier for the strain of the sample"""

schemaKey: Literal["StrainType"] = Field("StrainType", readOnly=True)


class SexType(BaseType):
"""Identifier for the sex of the sample"""

schemaKey: Literal["SexType"] = Field("SexType", readOnly=True)


class SpeciesType(BaseType):
"""Identifier for species of the sample"""

schemaKey: Literal["SpeciesType"] = Field("SpeciesType", readOnly=True)


class Disorder(BaseType):
"""Biolink, SNOMED, or other identifier for disorder studied"""
Expand All @@ -258,40 +252,28 @@ class Disorder(BaseType):
nskey="dandi",
rangeIncludes="schema:Date",
)
schemaKey: Literal["Disorder"] = Field("Disorder", readOnly=True)


class GenericType(BaseType):
"""An object to capture any type for about"""

schemaKey: Literal["GenericType"] = Field("GenericType", readOnly=True)


class ApproachType(BaseType):
"""Identifier for approach used"""

schemaKey: Literal["ApproachType"] = Field("ApproachType", readOnly=True)


class MeasurementTechniqueType(BaseType):
"""Identifier for measurement technique used"""

schemaKey: Literal["MeasurementTechniqueType"] = Field(
"MeasurementTechniqueType", readOnly=True
)


class StandardsType(BaseType):
"""Identifier for data standard used"""

schemaKey: Literal["StandardsType"] = Field("StandardsType", readOnly=True)


class ContactPoint(DandiBaseModel):
email: Optional[EmailStr] = Field(None, nskey="schema")
url: Optional[HttpUrl] = Field(None, nskey="schema")

schemaKey: Literal["ContactPoint"] = Field("ContactPoint", readOnly=True)
_ldmeta = {"nskey": "schema"}


Expand Down Expand Up @@ -321,7 +303,6 @@ class Contributor(DandiBaseModel):
description="Identifier associated with a sponsored or gift award",
nskey="dandi",
)
schemaKey: Literal["Contributor"] = Field("Contributor", readOnly=True)


class Organization(Contributor):
Expand All @@ -346,7 +327,6 @@ class Organization(Contributor):
description="Contact for the organization",
nskey="schema",
)
schemaKey: Literal["Organization"] = Field("Organization", readOnly=True)
_ldmeta = {
"rdfs:subClassOf": ["schema:Organization", "prov:Organization"],
"nskey": "dandi",
Expand All @@ -371,7 +351,6 @@ class Person(Contributor):
description="An organization that this person is affiliated with.",
nskey="schema",
)
schemaKey: Literal["Person"] = Field("Person", readOnly=True)

_ldmeta = {"rdfs:subClassOf": ["schema:Person", "prov:Person"], "nskey": "dandi"}

Expand All @@ -389,7 +368,6 @@ class Software(DandiBaseModel):
url: Optional[HttpUrl] = Field(
None, description="Web page for the software", nskey="schema"
)
schemaKey: Literal["Software"] = Field("Software", readOnly=True)

_ldmeta = {
"rdfs:subClassOf": ["schema:SoftwareApplication", "prov:Software"],
Expand All @@ -406,7 +384,6 @@ class Agent(DandiBaseModel):
)
name: str = Field(nskey="schema")
url: Optional[HttpUrl] = Field(None, nskey="schema")
schemaKey: Literal["Agent"] = Field("Agent", readOnly=True)

_ldmeta = {
"rdfs:subClassOf": ["prov:Agent"],
Expand All @@ -425,7 +402,6 @@ class EthicsApproval(DandiBaseModel):
contactPoint: ContactPoint = Field(
description="Information about the ethics approval committee.", nskey="schema"
)
schemaKey: Literal["EthicsApproval"] = Field("EthicsApproval", readOnly=True)

_ldmeta = {"rdfs:subClassOf": ["schema:Thing", "prov:Entity"], "nskey": "dandi"}

Expand All @@ -446,7 +422,6 @@ class Resource(DandiBaseModel):
"This relation should satisfy: dandiset <relation> resource",
nskey="dandi",
)
schemaKey: Literal["Resource"] = Field("Resource", readOnly=True)

_ldmeta = {
"rdfs:subClassOf": ["schema:CreativeWork", "prov:Entity"],
Expand Down Expand Up @@ -482,9 +457,6 @@ class AccessRequirements(DandiBaseModel):
nskey="dandi",
rangeIncludes="schema:Date",
)
schemaKey: Literal["AccessRequirements"] = Field(
"AccessRequirements", readOnly=True
)

_ldmeta = {"rdfs:subClassOf": ["schema:Thing", "prov:Entity"], "nskey": "dandi"}

Expand Down Expand Up @@ -513,7 +485,6 @@ class AssetsSummary(DandiBaseModel):
variableMeasured: Optional[List[str]] = Field(None, readOnly=True, nskey="schema")

species: Optional[List[SpeciesType]] = Field(readOnly=True)
schemaKey: Literal["AssetsSummary"] = Field("AssetsSummary", readOnly=True)

_ldmeta = {
"rdfs:subClassOf": ["schema:CreativeWork", "prov:Entity"],
Expand All @@ -534,8 +505,6 @@ class Equipment(DandiBaseModel):
None, description="The description of the activity.", nskey="schema"
)

schemaKey: Literal["Equipment"] = Field("Equipment", readOnly=True)

_ldmeta = {
"rdfs:subClassOf": ["schema:CreativeWork", "prov:Entity"],
"nskey": "dandi",
Expand Down Expand Up @@ -565,8 +534,6 @@ class Activity(DandiBaseModel):
] = Field(None, nskey="prov")
used: Optional[List[Equipment]] = Field(None, nskey="prov")

schemaKey: Literal["Activity"] = Field("Activity", readOnly=True)

_ldmeta = {"rdfs:subClassOf": ["prov:Activity", "schema:Thing"], "nskey": "dandi"}


Expand All @@ -580,7 +547,6 @@ class Project(Activity):
description: Optional[str] = Field(
None, description="A brief description of the project.", nskey="schema"
)
schemaKey: Literal["Project"] = Field("Project", readOnly=True)


class Session(Activity):
Expand All @@ -593,19 +559,17 @@ class Session(Activity):
description: Optional[str] = Field(
None, description="A brief description of the session.", nskey="schema"
)
schemaKey: Literal["Session"] = Field("Session", readOnly=True)


class PublishActivity(Activity):
schemaKey: Literal["PublishActivity"] = Field("PublishActivity", readOnly=True)
pass


class Locus(DandiBaseModel):
identifier: Union[Identifier, List[Identifier]] = Field(
description="Identifier for genotyping locus", nskey="schema"
)
locusType: Optional[str] = Field(None)
schemaKey: Literal["Locus"] = Field("Locus", readOnly=True)
_ldmeta = {"nskey": "dandi"}


Expand All @@ -615,15 +579,13 @@ class Allele(DandiBaseModel):
)
alleleSymbol: Optional[str] = Field(None)
alleleType: Optional[str] = Field(None)
schemaKey: Literal["Allele"] = Field("Allele", readOnly=True)
_ldmeta = {"nskey": "dandi"}


class GenotypeInfo(DandiBaseModel):
locus: Locus = Field(description="Locus at which information was extracted")
alleles: List[Allele] = Field(description="Information about alleles at the locus")
wasGeneratedBy: Optional[List["Session"]] = Field(None, nskey="prov")
schemaKey: Literal["GenotypeInfo"] = Field("GenotypeInfo", readOnly=True)
_ldmeta = {"nskey": "dandi"}


Expand All @@ -639,9 +601,6 @@ class RelatedParticipant(DandiBaseModel):
"This relation should satisfy: Participant <relation> relatedParticipant",
nskey="dandi",
)
schemaKey: Literal["RelatedParticipant"] = Field(
"RelatedParticipant", readOnly=True
)

_ldmeta = {
"rdfs:subClassOf": ["schema:CreativeWork", "prov:Entity"],
Expand Down Expand Up @@ -695,8 +654,6 @@ class Participant(DandiBaseModel):
relatedParticipant: Optional[List[RelatedParticipant]] = Field(None, nskey="dandi")
sameAs: Optional[List[Identifier]] = Field(None, nskey="schema")

schemaKey: Literal["Participant"] = Field("Participant", readOnly=True)

_ldmeta = {
"rdfs:subClassOf": ["prov:Agent"],
"rdfs:label": "Information about the participant.",
Expand Down Expand Up @@ -728,8 +685,6 @@ class BioSample(DandiBaseModel):
sameAs: Optional[List[Identifier]] = Field(None, nskey="schema")
hasMember: Optional[List[Identifier]] = Field(None, nskey="prov")

schemaKey: Literal["BioSample"] = Field("BioSample", readOnly=True)

_ldmeta = {
"rdfs:subClassOf": ["schema:Thing", "prov:Entity"],
"rdfs:label": "Information about the biosample.",
Expand Down Expand Up @@ -806,7 +761,6 @@ class CommonModel(DandiBaseModel):
relatedResource: Optional[List[Resource]] = Field(None, nskey="dandi")

wasGeneratedBy: Optional[List[Activity]] = Field(None, nskey="prov")
schemaKey: Literal["CommonModel"] = Field("CommonModel", readOnly=True)

def json_dict(self):
"""
Expand Down Expand Up @@ -886,8 +840,6 @@ def contributor_musthave_contact(cls, values):
nskey="prov",
)

schemaKey: Literal["Dandiset"] = Field("Dandiset", readOnly=True)

_ldmeta = {
"rdfs:subClassOf": ["schema:Dataset", "prov:Entity"],
"rdfs:label": "Information about the dataset",
Expand Down Expand Up @@ -944,7 +896,8 @@ class BareAsset(CommonModel):
nskey="prov",
)

schemaKey: Literal["Asset"] = Field("Asset", readOnly=True)
# Bare asset is to be just Asset.
schemaKey = "Asset"

_ldmeta = {
"rdfs:subClassOf": ["schema:CreativeWork", "prov:Entity"],
Expand Down Expand Up @@ -979,7 +932,6 @@ class Publishable(DandiBaseModel):
nskey="dandi",
) # TODO: formalize "publish" activity to at least the Actor
datePublished: datetime = Field(readOnly=True, nskey="schema")
schemaKey: Literal["Publishable"] = Field("Publishable", readOnly=True)


class PublishedDandiset(Dandiset, Publishable):
Expand All @@ -993,8 +945,13 @@ class PublishedDandiset(Dandiset, Publishable):
readOnly=True, description="permalink to the item", nskey="schema"
)

schemaKey = "Dandiset"


class PublishedAsset(Asset, Publishable):

schemaKey = "Asset"

@validator("digest")
def digest_bothhashes(cls, values):
try:
Expand Down

0 comments on commit ecaeb88

Please sign in to comment.