Skip to content

Commit

Permalink
Merge pull request #13 from dandi/fix/schemakey-metaclass
Browse files Browse the repository at this point in the history
Fix/schemakey metaclass
  • Loading branch information
satra authored May 26, 2021
2 parents 5c238b6 + 3f557ea commit da1c7bf
Showing 1 changed file with 22 additions and 62 deletions.
84 changes: 22 additions & 62 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 @@ -162,10 +170,13 @@ def schema_extra(schema: Dict[str, Any], model) -> None:
# In pydantic 1.8+ all Literals are mapped on to enum
# This presently breaks the schema editor UI. Revert
# to const when generating the schema.
# Note: this no longer happens with custom metaclass
if prop == "schemaKey":
if len(value["enum"]) == 1:
if "enum" in value and len(value["enum"]) == 1:
value["const"] = value["enum"][0]
del value["enum"]
else:
value["const"] = value["default"]


class PropertyValue(DandiBaseModel):
Expand All @@ -183,7 +194,6 @@ class PropertyValue(DandiBaseModel):
nskey="schema",
)

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


Expand All @@ -208,45 +218,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 +255,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 +306,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 +330,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 @@ -372,7 +355,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 @@ -390,7 +372,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 @@ -407,7 +388,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 @@ -426,7 +406,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 @@ -447,7 +426,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 @@ -483,9 +461,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 @@ -514,7 +489,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 @@ -535,8 +509,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 @@ -566,8 +538,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 @@ -581,7 +551,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 @@ -594,19 +563,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 @@ -616,15 +583,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 @@ -640,9 +605,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 @@ -696,8 +658,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 @@ -729,8 +689,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 @@ -807,7 +765,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 @@ -887,8 +844,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 @@ -945,7 +900,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 @@ -984,7 +940,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 @@ -998,8 +953,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 da1c7bf

Please sign in to comment.