From 516b3f11ef550b5ee3bcf878d56b22ce91bc6ce0 Mon Sep 17 00:00:00 2001 From: Sooyoung Ahn Date: Wed, 14 Dec 2022 14:08:23 -0500 Subject: [PATCH 1/2] basic kg API service setup w participant and biosample --- .gitignore | 0 __init__.py | 0 dependencies.py | 11 +++++++++ internal/__init__.py | 0 internal/admin.py | 0 main.py | 23 +++++++++++++++++ routers/__init__.py | 0 routers/biosamples.py | 55 +++++++++++++++++++++++++++++++++++++++++ routers/participants.py | 38 ++++++++++++++++++++++++++++ 9 files changed, 127 insertions(+) create mode 100644 .gitignore create mode 100644 __init__.py create mode 100644 dependencies.py create mode 100644 internal/__init__.py create mode 100644 internal/admin.py create mode 100644 main.py create mode 100644 routers/__init__.py create mode 100644 routers/biosamples.py create mode 100644 routers/participants.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dependencies.py b/dependencies.py new file mode 100644 index 0000000..163e040 --- /dev/null +++ b/dependencies.py @@ -0,0 +1,11 @@ +from fastapi import Header, HTTPException + + +async def get_token_header(x_token: str = Header()): + if x_token != "test-token": + raise HTTPException(status_code=400, detail="X-Token header invalid") + + +async def get_query_token(token: str): + if token != "sooyoung": + raise HTTPException(status_code=400, detail="No Sooyoung token provided") diff --git a/internal/__init__.py b/internal/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/internal/admin.py b/internal/admin.py new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py new file mode 100644 index 0000000..e3e5573 --- /dev/null +++ b/main.py @@ -0,0 +1,23 @@ +from fastapi import Depends, FastAPI + +from dependencies import get_query_token, get_token_header +# from internal import admin +from routers import biosamples, participants + +app = FastAPI(dependencies=[Depends(get_query_token)]) + + +app.include_router(participants.router) +app.include_router(biosamples.router) +# app.include_router( +# admin.router, +# prefix="/admin", +# tags=["admin"], +# dependencies=[Depends(get_token_header)], +# responses={418: {"description": "I'm a teapot"}}, +# ) + + +@app.get("/") +async def root(): + return {"message": "Knowledge Graph API"} diff --git a/routers/__init__.py b/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/routers/biosamples.py b/routers/biosamples.py new file mode 100644 index 0000000..782177b --- /dev/null +++ b/routers/biosamples.py @@ -0,0 +1,55 @@ +from fastapi import APIRouter, Depends, HTTPException +from pydantic import BaseModel + +from dependencies import get_token_header + +router = APIRouter( + prefix="/biosamples", + tags=["biosamples"], + dependencies=[Depends(get_token_header)], + responses={404: {"description": "Not found"}}, +) + + +class Biosample(BaseModel): + name: str + + +fake_biosamples_db = {"first": {"name": "Sample 1"}, "second": {"name": "Sample 2"}} + + +@router.get("/") +async def read_biosamples(): + return fake_biosamples_db + + +@router.post("/") +async def create_biosample(biosample: Biosample): + if biosample.name in fake_biosamples_db: + raise HTTPException(status_code=404, detail="Biosample already created") + return {"name": fake_biosamples_db[biosample.name]["name"], "biosample_id": biosample.name} + + +@router.get("/{biosample_id}") +async def read_biosample(biosample_id: str): + if biosample_id not in fake_biosamples_db: + raise HTTPException(status_code=404, detail="Biosample not found") + return {"name": fake_biosamples_db[biosample_id]["name"], "biosample_id": biosample_id} + + +@router.put( + "/{biosample_id}", + tags=["custom"], + responses={403: {"description": "Operation forbidden"}}, +) +async def update_biosample(biosample_id: str): + if biosample_id != "first": + raise HTTPException( + status_code=403, detail="You can only update the biosample: first" + ) + return {"biosample_id": biosample_id, "name": "Sample 1"} + + +@router.delete("/{biosample_id}") +async def delete_biosample(biosample_id: str): + return None diff --git a/routers/participants.py b/routers/participants.py new file mode 100644 index 0000000..9b3a5c6 --- /dev/null +++ b/routers/participants.py @@ -0,0 +1,38 @@ +from typing import Union + +from fastapi import APIRouter, Depends, HTTPException +from pydantic import BaseModel + +from dependencies import get_token_header + +router = APIRouter( + prefix="/participants", + tags=["participants"], + dependencies=[Depends(get_token_header)], + responses={404: {"description": "Not found"}}, +) + + +class Participant(BaseModel): + name: str + id: int + + +@router.post("/", response_model=Participant) +async def create_participant(participant: Participant): + return {"participant_name": participant.name, "participant_id": participant_id} + + +@router.get("/{participant_id}") +async def read_participant(participant_id: int, q: Union[str, None] = None): + return {"participant_id": participant_id, "q": q} + + +@router.put("/{participant_id}") +async def update_participant(participant_id: int, participant: Participant): + return {"participant_name": participant.name, "participant_id": participant_id} + + +@router.delete("/{participant_id}") +async def delete_participant(participant_id: int): + return None From 24dfcd2d220ce768d3cbb5d0c060e88c88335d4c Mon Sep 17 00:00:00 2001 From: Sooyoung Ahn Date: Fri, 13 Jan 2023 11:54:38 -0500 Subject: [PATCH 2/2] other models and linkML representations for AtOM --- main.py | 14 ++ models/__init__.py | 0 models/annotations.py | 5 + models/assay.py | 5 + models/atlas.py | 5 + models/biosample.py | 39 ++++ models/cell.py | 6 + models/cellbygene.py | 5 + models/celltype.py | 5 + models/instrument.py | 5 + models/location.py | 9 + models/participant.py | 74 +++++++ models/participant.yaml | 23 +++ models/protocol.py | 5 + models/provenance.py | 5 + models/semantic/__init__.py | 0 models/semantic/annotationset.py | 201 ++++++++++++++++++++ models/semantic/annotationset.schema.json | 117 ++++++++++++ models/semantic/annotationset.yaml | 48 +++++ models/semantic/terminology.py | 181 ++++++++++++++++++ models/semantic/terminology.schema.json | 114 +++++++++++ models/semantic/terminology.yaml | 43 +++++ models/spatial/__init__.py | 0 models/spatial/coordinatesystem.py | 167 ++++++++++++++++ models/spatial/coordinatesystem.schema.json | 78 ++++++++ models/spatial/coordinatesystem.yaml | 39 ++++ models/spatial/referencedata.py | 186 ++++++++++++++++++ models/spatial/referencedata.schema.json | 99 ++++++++++ models/spatial/referencedata.yaml | 55 ++++++ routers/biosamples.py | 12 +- routers/participants.py | 13 +- services/__init__.py | 0 services/biosampleservice.py | 17 ++ services/participantservice.py | 17 ++ 34 files changed, 1574 insertions(+), 18 deletions(-) create mode 100644 models/__init__.py create mode 100644 models/annotations.py create mode 100644 models/assay.py create mode 100644 models/atlas.py create mode 100644 models/biosample.py create mode 100644 models/cell.py create mode 100644 models/cellbygene.py create mode 100644 models/celltype.py create mode 100644 models/instrument.py create mode 100644 models/location.py create mode 100644 models/participant.py create mode 100644 models/participant.yaml create mode 100644 models/protocol.py create mode 100644 models/provenance.py create mode 100644 models/semantic/__init__.py create mode 100644 models/semantic/annotationset.py create mode 100644 models/semantic/annotationset.schema.json create mode 100644 models/semantic/annotationset.yaml create mode 100644 models/semantic/terminology.py create mode 100644 models/semantic/terminology.schema.json create mode 100644 models/semantic/terminology.yaml create mode 100644 models/spatial/__init__.py create mode 100644 models/spatial/coordinatesystem.py create mode 100644 models/spatial/coordinatesystem.schema.json create mode 100644 models/spatial/coordinatesystem.yaml create mode 100644 models/spatial/referencedata.py create mode 100644 models/spatial/referencedata.schema.json create mode 100644 models/spatial/referencedata.yaml create mode 100644 services/__init__.py create mode 100644 services/biosampleservice.py create mode 100644 services/participantservice.py diff --git a/main.py b/main.py index e3e5573..33f98c6 100644 --- a/main.py +++ b/main.py @@ -4,6 +4,13 @@ # from internal import admin from routers import biosamples, participants +import uvicorn +from fastapi_sqlalchemy import DBSessionMiddleware, db +import os +from dotenv import load_dotenv + +load_dotenv('.env') + app = FastAPI(dependencies=[Depends(get_query_token)]) @@ -17,7 +24,14 @@ # responses={418: {"description": "I'm a teapot"}}, # ) +# to avoid csrftokenError +app.add_middleware(DBSessionMiddleware, db_url=os.environ['DATABASE_URL']) @app.get("/") async def root(): return {"message": "Knowledge Graph API"} + + +# To run locally +if __name__ == '__main__': + uvicorn.run(app, host='0.0.0.0', port=8000) \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/annotations.py b/models/annotations.py new file mode 100644 index 0000000..46a91b8 --- /dev/null +++ b/models/annotations.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class Annotations(BaseModel): + name: str \ No newline at end of file diff --git a/models/assay.py b/models/assay.py new file mode 100644 index 0000000..d746e70 --- /dev/null +++ b/models/assay.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class Assay(BaseModel): + name: str diff --git a/models/atlas.py b/models/atlas.py new file mode 100644 index 0000000..88004ca --- /dev/null +++ b/models/atlas.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class Atlas(BaseModel): + type: Spatial, Dimensional diff --git a/models/biosample.py b/models/biosample.py new file mode 100644 index 0000000..5001401 --- /dev/null +++ b/models/biosample.py @@ -0,0 +1,39 @@ +from dandischema import DandiBaseModel + + +class BioSample(DandiBaseModel): + """Description of the sample that was studied""" + + identifier: Identifier = Field(nskey="schema") + sampleType: SampleType = Field( + description="Identifier for the sample characteristics (e.g., from OBI, Encode).", + nskey="dandi", + ) + assayType: Optional[List[AssayType]] = Field( + None, description="Identifier for the assay(s) used (e.g., OBI).", nskey="dandi" + ) + anatomy: Optional[List[Anatomy]] = Field( + None, + description="Identifier for what organ the sample belongs " + "to. Use the most specific descriptor from sources such as UBERON.", + nskey="dandi", + ) + + wasDerivedFrom: Optional[List["BioSample"]] = Field( + None, + description="Describes the hierarchy of sample derivation or aggregation.", + nskey="prov", + ) + wasAttributedTo: Optional[List[Participant]] = Field( + None, + description="Participant(s) or Subject(s) associated with this sample.", + nskey="prov", + ) + sameAs: Optional[List[Identifier]] = Field(None, nskey="schema") + hasMember: Optional[List[Identifier]] = Field(None, nskey="prov") + + _ldmeta = { + "rdfs:subClassOf": ["schema:Thing", "prov:Entity"], + "rdfs:label": "Information about the biosample.", + "nskey": "dandi", + } \ No newline at end of file diff --git a/models/cell.py b/models/cell.py new file mode 100644 index 0000000..e18bf7a --- /dev/null +++ b/models/cell.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel + + +class Cell(BaseModel): + location: str + cellType: CellType diff --git a/models/cellbygene.py b/models/cellbygene.py new file mode 100644 index 0000000..32de071 --- /dev/null +++ b/models/cellbygene.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class CellbyGene(BaseModel): + cell: Cell diff --git a/models/celltype.py b/models/celltype.py new file mode 100644 index 0000000..d0ee7d0 --- /dev/null +++ b/models/celltype.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class CellType(BaseModel): + name: str diff --git a/models/instrument.py b/models/instrument.py new file mode 100644 index 0000000..90067ae --- /dev/null +++ b/models/instrument.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class Instrument(BaseModel): + name: str diff --git a/models/location.py b/models/location.py new file mode 100644 index 0000000..dc305fa --- /dev/null +++ b/models/location.py @@ -0,0 +1,9 @@ +from pydantic import BaseModel + + +class Location(BaseModel): + photo: Photo + coordinates: [str, str] + atlasMap: dict(Atlas) + atlas: Atlas + overlappingLabels: [str, str] diff --git a/models/participant.py b/models/participant.py new file mode 100644 index 0000000..46b471d --- /dev/null +++ b/models/participant.py @@ -0,0 +1,74 @@ +from dandischema import DandiBaseModel + + +class Participant(DandiBaseModel): + """Description about the Participant or Subject studied. + The Participant or Subject can be any individual or synthesized Agent. The + properties of the Participant or Subject refers to information at the timepoint + when the Participant or Subject engaged in the production of data being described. + """ + + identifier: Identifier = Field(nskey="schema") + altName: Optional[List[Identifier]] = Field(None, nskey="dandi") + + strain: Optional[StrainType] = Field( + None, + description="Identifier for the strain of the participant or subject.", + nskey="dandi", + ) + cellLine: Optional[Identifier] = Field( + None, + description="Cell line associated with the participant or subject.", + nskey="dandi", + ) + vendor: Optional[Organization] = Field(None, nskey="dandi") + age: Optional[PropertyValue] = Field( + None, + description="A representation of age using ISO 8601 duration. This " + "should include a valueReference if anything other than " + "date of birth is used.", + nskey="dandi", + rangeIncludes="schema:Duration", + ) + + sex: Optional[SexType] = Field( + None, + description="Identifier for sex of the participant or subject if " + "available. (e.g. from OBI)", + nskey="dandi", + ) + genotype: Optional[Union[List[GenotypeInfo], Identifier]] = Field( + None, + description="Genotype descriptor of participant or subject if available", + nskey="dandi", + ) + species: Optional[SpeciesType] = Field( + None, + description="An identifier indicating the taxonomic classification of " + "the participant or subject.", + nskey="dandi", + ) + disorder: Optional[List[Disorder]] = Field( + None, + description="Any current diagnosed disease or disorder associated with " + "the participant or subject.", + nskey="dandi", + ) + + relatedParticipant: Optional[List[RelatedParticipant]] = Field( + None, + description="Information about related participants or subjects in a " + "study or across studies.", + nskey="dandi", + ) + sameAs: Optional[List[Identifier]] = Field( + None, + description="An identifier to link participants or subjects across datasets.", + nskey="schema", + ) + + _ldmeta = { + "rdfs:subClassOf": ["prov:Agent"], + "rdfs:label": "Information about the participant or subject.", + "nskey": "dandi", + } diff --git a/models/participant.yaml b/models/participant.yaml new file mode 100644 index 0000000..d28223d --- /dev/null +++ b/models/participant.yaml @@ -0,0 +1,23 @@ +id: https://bican.org/examples/participant +name: participant +prefixes: + linkml: https://w3id.org/linkml/ +imports: + - linkml:types +default_range: string + +classes: + Participant: + attributes: + identifier: + altName: + strain: + cellLine: + vendor: + age: + sex: + genotype: + species: + disorder: + relatedParticipant: + sameAs: diff --git a/models/protocol.py b/models/protocol.py new file mode 100644 index 0000000..5e8dd21 --- /dev/null +++ b/models/protocol.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class Protocol(BaseModel): + name: str diff --git a/models/provenance.py b/models/provenance.py new file mode 100644 index 0000000..9da915c --- /dev/null +++ b/models/provenance.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class Provenance(BaseModel): + name: str diff --git a/models/semantic/__init__.py b/models/semantic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/semantic/annotationset.py b/models/semantic/annotationset.py new file mode 100644 index 0000000..74372cf --- /dev/null +++ b/models/semantic/annotationset.py @@ -0,0 +1,201 @@ +# Auto generated from annotationset.yaml by pythongen.py version: 0.9.0 +# Generation date: 2023-03-02T17:09:32 +# Schema: annotationset +# +# id: https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1 +# description: +# license: https://creativecommons.org/publicdomain/zero/1.0/ + +import dataclasses +import sys +import re +from jsonasobj2 import JsonObj, as_dict +from typing import Optional, List, Union, Dict, ClassVar, Any +from dataclasses import dataclass +from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue, PvFormulaOptions + +from linkml_runtime.utils.slot import Slot +from linkml_runtime.utils.metamodelcore import empty_list, empty_dict, bnode +from linkml_runtime.utils.yamlutils import YAMLRoot, extended_str, extended_float, extended_int +from linkml_runtime.utils.dataclass_extensions_376 import dataclasses_init_fn_with_kwargs +from linkml_runtime.utils.formatutils import camelcase, underscore, sfx +from linkml_runtime.utils.enumerations import EnumDefinitionImpl +from rdflib import Namespace, URIRef +from linkml_runtime.utils.curienamespace import CurieNamespace +from linkml_runtime.linkml_model.types import Boolean, String +from linkml_runtime.utils.metamodelcore import Bool + +metamodel_version = "1.7.0" +version = None + +# Overwrite dataclasses _init_fn to add **kwargs in __init__ +dataclasses._init_fn = dataclasses_init_fn_with_kwargs + +# Namespaces +LINKML = CurieNamespace('linkml', 'https://w3id.org/linkml/') +DEFAULT_ = CurieNamespace('', 'https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/') + + +# Types + +# Class references + + + +@dataclass +class Annotation(YAMLRoot): + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/Annotation") + class_class_curie: ClassVar[str] = None + class_name: ClassVar[str] = "Annotation" + class_model_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/Annotation") + + internal_identifier: str = None + laterality: str = None + criteria_type: str = None + visualized_in: str = None + criteria: Optional[str] = None + display_color: Optional[str] = None + inspired_by: Optional[str] = None + best_view_point: Optional[str] = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.internal_identifier): + self.MissingRequiredField("internal_identifier") + if not isinstance(self.internal_identifier, str): + self.internal_identifier = str(self.internal_identifier) + + if self._is_empty(self.laterality): + self.MissingRequiredField("laterality") + if not isinstance(self.laterality, str): + self.laterality = str(self.laterality) + + if self._is_empty(self.criteria_type): + self.MissingRequiredField("criteria_type") + if not isinstance(self.criteria_type, str): + self.criteria_type = str(self.criteria_type) + + if self._is_empty(self.visualized_in): + self.MissingRequiredField("visualized_in") + if not isinstance(self.visualized_in, str): + self.visualized_in = str(self.visualized_in) + + if self.criteria is not None and not isinstance(self.criteria, str): + self.criteria = str(self.criteria) + + if self.display_color is not None and not isinstance(self.display_color, str): + self.display_color = str(self.display_color) + + if self.inspired_by is not None and not isinstance(self.inspired_by, str): + self.inspired_by = str(self.inspired_by) + + if self.best_view_point is not None and not isinstance(self.best_view_point, str): + self.best_view_point = str(self.best_view_point) + + super().__post_init__(**kwargs) + + +@dataclass +class AnnotationSet(YAMLRoot): + """ + Graphical marks or labels referring to spatial locations determined by features observed in, inferred from, or + mapped onto the reference data, specifying structures or boundaries + """ + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/AnnotationSet") + class_class_curie: ClassVar[str] = None + class_name: ClassVar[str] = "AnnotationSet" + class_model_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/AnnotationSet") + + full_name: str = None + short_name: str = None + annotations: Union[Union[dict, Annotation], List[Union[dict, Annotation]]] = None + delineation: Union[bool, Bool] = None + parcellation: Union[bool, Bool] = None + probabilistic_map: Union[bool, Bool] = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.full_name): + self.MissingRequiredField("full_name") + if not isinstance(self.full_name, str): + self.full_name = str(self.full_name) + + if self._is_empty(self.short_name): + self.MissingRequiredField("short_name") + if not isinstance(self.short_name, str): + self.short_name = str(self.short_name) + + if self._is_empty(self.annotations): + self.MissingRequiredField("annotations") + if not isinstance(self.annotations, list): + self.annotations = [self.annotations] if self.annotations is not None else [] + self.annotations = [v if isinstance(v, Annotation) else Annotation(**as_dict(v)) for v in self.annotations] + + if self._is_empty(self.delineation): + self.MissingRequiredField("delineation") + if not isinstance(self.delineation, Bool): + self.delineation = Bool(self.delineation) + + if self._is_empty(self.parcellation): + self.MissingRequiredField("parcellation") + if not isinstance(self.parcellation, Bool): + self.parcellation = Bool(self.parcellation) + + if self._is_empty(self.probabilistic_map): + self.MissingRequiredField("probabilistic_map") + if not isinstance(self.probabilistic_map, Bool): + self.probabilistic_map = Bool(self.probabilistic_map) + + super().__post_init__(**kwargs) + + +# Enumerations + + +# Slots +class slots: + pass + +slots.annotation__internal_identifier = Slot(uri=DEFAULT_.internal_identifier, name="annotation__internal_identifier", curie=DEFAULT_.curie('internal_identifier'), + model_uri=DEFAULT_.annotation__internal_identifier, domain=None, range=str) + +slots.annotation__laterality = Slot(uri=DEFAULT_.laterality, name="annotation__laterality", curie=DEFAULT_.curie('laterality'), + model_uri=DEFAULT_.annotation__laterality, domain=None, range=str) + +slots.annotation__criteria_type = Slot(uri=DEFAULT_.criteria_type, name="annotation__criteria_type", curie=DEFAULT_.curie('criteria_type'), + model_uri=DEFAULT_.annotation__criteria_type, domain=None, range=str) + +slots.annotation__visualized_in = Slot(uri=DEFAULT_.visualized_in, name="annotation__visualized_in", curie=DEFAULT_.curie('visualized_in'), + model_uri=DEFAULT_.annotation__visualized_in, domain=None, range=str) + +slots.annotation__criteria = Slot(uri=DEFAULT_.criteria, name="annotation__criteria", curie=DEFAULT_.curie('criteria'), + model_uri=DEFAULT_.annotation__criteria, domain=None, range=Optional[str]) + +slots.annotation__display_color = Slot(uri=DEFAULT_.display_color, name="annotation__display_color", curie=DEFAULT_.curie('display_color'), + model_uri=DEFAULT_.annotation__display_color, domain=None, range=Optional[str]) + +slots.annotation__inspired_by = Slot(uri=DEFAULT_.inspired_by, name="annotation__inspired_by", curie=DEFAULT_.curie('inspired_by'), + model_uri=DEFAULT_.annotation__inspired_by, domain=None, range=Optional[str]) + +slots.annotation__best_view_point = Slot(uri=DEFAULT_.best_view_point, name="annotation__best_view_point", curie=DEFAULT_.curie('best_view_point'), + model_uri=DEFAULT_.annotation__best_view_point, domain=None, range=Optional[str]) + +slots.annotationSet__full_name = Slot(uri=DEFAULT_.full_name, name="annotationSet__full_name", curie=DEFAULT_.curie('full_name'), + model_uri=DEFAULT_.annotationSet__full_name, domain=None, range=str) + +slots.annotationSet__short_name = Slot(uri=DEFAULT_.short_name, name="annotationSet__short_name", curie=DEFAULT_.curie('short_name'), + model_uri=DEFAULT_.annotationSet__short_name, domain=None, range=str) + +slots.annotationSet__annotations = Slot(uri=DEFAULT_.annotations, name="annotationSet__annotations", curie=DEFAULT_.curie('annotations'), + model_uri=DEFAULT_.annotationSet__annotations, domain=None, range=Union[Union[dict, Annotation], List[Union[dict, Annotation]]]) + +slots.annotationSet__delineation = Slot(uri=DEFAULT_.delineation, name="annotationSet__delineation", curie=DEFAULT_.curie('delineation'), + model_uri=DEFAULT_.annotationSet__delineation, domain=None, range=Union[bool, Bool]) + +slots.annotationSet__parcellation = Slot(uri=DEFAULT_.parcellation, name="annotationSet__parcellation", curie=DEFAULT_.curie('parcellation'), + model_uri=DEFAULT_.annotationSet__parcellation, domain=None, range=Union[bool, Bool]) + +slots.annotationSet__probabilistic_map = Slot(uri=DEFAULT_.probabilistic_map, name="annotationSet__probabilistic_map", curie=DEFAULT_.curie('probabilistic_map'), + model_uri=DEFAULT_.annotationSet__probabilistic_map, domain=None, range=Union[bool, Bool]) diff --git a/models/semantic/annotationset.schema.json b/models/semantic/annotationset.schema.json new file mode 100644 index 0000000..79ed02c --- /dev/null +++ b/models/semantic/annotationset.schema.json @@ -0,0 +1,117 @@ +{ + "$defs": { + "Annotation": { + "additionalProperties": false, + "description": "", + "properties": { + "best_view_point": { + "type": "string" + }, + "criteria": { + "type": "string" + }, + "criteria_type": { + "type": "string" + }, + "display_color": { + "type": "string" + }, + "inspired_by": { + "type": "string" + }, + "internal_identifier": { + "type": "string" + }, + "laterality": { + "type": "string" + }, + "visualized_in": { + "type": "string" + } + }, + "required": [ + "internal_identifier", + "laterality", + "criteria_type", + "visualized_in" + ], + "title": "Annotation", + "type": "object" + }, + "AnnotationSet": { + "additionalProperties": false, + "description": "Graphical marks or labels referring to spatial locations determined by features observed in, inferred from, or mapped onto the reference data, specifying structures or boundaries", + "properties": { + "annotations": { + "items": { + "$ref": "#/$defs/Annotation" + }, + "type": "array" + }, + "delineation": { + "type": "boolean" + }, + "full_name": { + "type": "string" + }, + "parcellation": { + "type": "boolean" + }, + "probabilistic_map": { + "type": "boolean" + }, + "short_name": { + "type": "string" + } + }, + "required": [ + "full_name", + "short_name", + "annotations", + "delineation", + "parcellation", + "probabilistic_map" + ], + "title": "AnnotationSet", + "type": "object" + } + }, + "$id": "https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "metamodel_version": "1.7.0", + "properties": { + "annotations": { + "items": { + "$ref": "#/$defs/Annotation" + }, + "type": "array" + }, + "delineation": { + "type": "boolean" + }, + "full_name": { + "type": "string" + }, + "parcellation": { + "type": "boolean" + }, + "probabilistic_map": { + "type": "boolean" + }, + "short_name": { + "type": "string" + } + }, + "required": [ + "full_name", + "short_name", + "annotations", + "delineation", + "parcellation", + "probabilistic_map" + ], + "title": "annotationset", + "type": "object", + "version": null +} diff --git a/models/semantic/annotationset.yaml b/models/semantic/annotationset.yaml new file mode 100644 index 0000000..f25ab62 --- /dev/null +++ b/models/semantic/annotationset.yaml @@ -0,0 +1,48 @@ +id: https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1 +name: annotationset +prefixes: + linkml: https://w3id.org/linkml/ +imports: + - linkml:types +default_range: string + +classes: + + Annotation: + attributes: + internal_identifier: + required: true + laterality: + required: true + criteria_type: + required: true + visualized_in: + required: true + criteria: + display_color: + inspired_by: + best_view_point: + + AnnotationSet: + description: >- + Graphical marks or labels referring to spatial locations determined by features observed in, inferred from, or mapped onto the reference data, specifying structures or boundaries + tree_root: true + attributes: + full_name: + required: true + short_name: + required: true + annotations: + multivalued: true + inlined_as_list: true + range: Annotation + required: true + delineation: + range: boolean + required: true + parcellation: + range: boolean + required: true + probabilistic_map: + range: boolean + required: true \ No newline at end of file diff --git a/models/semantic/terminology.py b/models/semantic/terminology.py new file mode 100644 index 0000000..4890f70 --- /dev/null +++ b/models/semantic/terminology.py @@ -0,0 +1,181 @@ +# Auto generated from terminology.yaml by pythongen.py version: 0.9.0 +# Generation date: 2023-03-02T17:09:59 +# Schema: terminology +# +# id: https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1 +# description: +# license: https://creativecommons.org/publicdomain/zero/1.0/ + +import dataclasses +import sys +import re +from jsonasobj2 import JsonObj, as_dict +from typing import Optional, List, Union, Dict, ClassVar, Any +from dataclasses import dataclass +from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue, PvFormulaOptions + +from linkml_runtime.utils.slot import Slot +from linkml_runtime.utils.metamodelcore import empty_list, empty_dict, bnode +from linkml_runtime.utils.yamlutils import YAMLRoot, extended_str, extended_float, extended_int +from linkml_runtime.utils.dataclass_extensions_376 import dataclasses_init_fn_with_kwargs +from linkml_runtime.utils.formatutils import camelcase, underscore, sfx +from linkml_runtime.utils.enumerations import EnumDefinitionImpl +from rdflib import Namespace, URIRef +from linkml_runtime.utils.curienamespace import CurieNamespace +from linkml_runtime.linkml_model.types import Boolean, String +from linkml_runtime.utils.metamodelcore import Bool + +metamodel_version = "1.7.0" +version = None + +# Overwrite dataclasses _init_fn to add **kwargs in __init__ +dataclasses._init_fn = dataclasses_init_fn_with_kwargs + +# Namespaces +LINKML = CurieNamespace('linkml', 'https://w3id.org/linkml/') +DEFAULT_ = CurieNamespace('', 'https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/') + + +# Types + +# Class references + + + +@dataclass +class Term(YAMLRoot): + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/Term") + class_class_curie: ClassVar[str] = None + class_name: ClassVar[str] = "Term" + class_model_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/Term") + + name_of_location: str = None + ontology_identifier: Optional[str] = None + other_anatomical_relations: Optional[str] = None + parent_structure: Optional[str] = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.name_of_location): + self.MissingRequiredField("name_of_location") + if not isinstance(self.name_of_location, str): + self.name_of_location = str(self.name_of_location) + + if self.ontology_identifier is not None and not isinstance(self.ontology_identifier, str): + self.ontology_identifier = str(self.ontology_identifier) + + if self.other_anatomical_relations is not None and not isinstance(self.other_anatomical_relations, str): + self.other_anatomical_relations = str(self.other_anatomical_relations) + + if self.parent_structure is not None and not isinstance(self.parent_structure, str): + self.parent_structure = str(self.parent_structure) + + super().__post_init__(**kwargs) + + +@dataclass +class Terminology(YAMLRoot): + """ + A set of terms that identifies the annotations, providing human readability and context, and allowing + communication about brain locations and structural properties + """ + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/Terminology") + class_class_curie: ClassVar[str] = None + class_name: ClassVar[str] = "Terminology" + class_model_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/Terminology") + + full_name: str = None + short_name: str = None + terms: Union[Union[dict, Term], List[Union[dict, Term]]] = None + controlled_vocabulary: Union[bool, Bool] = None + taxonomy: Union[bool, Bool] = None + ontology: Union[bool, Bool] = None + defined_in: Optional[str] = None + ontology_identifier: Optional[str] = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.full_name): + self.MissingRequiredField("full_name") + if not isinstance(self.full_name, str): + self.full_name = str(self.full_name) + + if self._is_empty(self.short_name): + self.MissingRequiredField("short_name") + if not isinstance(self.short_name, str): + self.short_name = str(self.short_name) + + if self._is_empty(self.terms): + self.MissingRequiredField("terms") + if not isinstance(self.terms, list): + self.terms = [self.terms] if self.terms is not None else [] + self.terms = [v if isinstance(v, Term) else Term(**as_dict(v)) for v in self.terms] + + if self._is_empty(self.controlled_vocabulary): + self.MissingRequiredField("controlled_vocabulary") + if not isinstance(self.controlled_vocabulary, Bool): + self.controlled_vocabulary = Bool(self.controlled_vocabulary) + + if self._is_empty(self.taxonomy): + self.MissingRequiredField("taxonomy") + if not isinstance(self.taxonomy, Bool): + self.taxonomy = Bool(self.taxonomy) + + if self._is_empty(self.ontology): + self.MissingRequiredField("ontology") + if not isinstance(self.ontology, Bool): + self.ontology = Bool(self.ontology) + + if self.defined_in is not None and not isinstance(self.defined_in, str): + self.defined_in = str(self.defined_in) + + if self.ontology_identifier is not None and not isinstance(self.ontology_identifier, str): + self.ontology_identifier = str(self.ontology_identifier) + + super().__post_init__(**kwargs) + + +# Enumerations + + +# Slots +class slots: + pass + +slots.term__name_of_location = Slot(uri=DEFAULT_.name_of_location, name="term__name_of_location", curie=DEFAULT_.curie('name_of_location'), + model_uri=DEFAULT_.term__name_of_location, domain=None, range=str) + +slots.term__ontology_identifier = Slot(uri=DEFAULT_.ontology_identifier, name="term__ontology_identifier", curie=DEFAULT_.curie('ontology_identifier'), + model_uri=DEFAULT_.term__ontology_identifier, domain=None, range=Optional[str]) + +slots.term__other_anatomical_relations = Slot(uri=DEFAULT_.other_anatomical_relations, name="term__other_anatomical_relations", curie=DEFAULT_.curie('other_anatomical_relations'), + model_uri=DEFAULT_.term__other_anatomical_relations, domain=None, range=Optional[str]) + +slots.term__parent_structure = Slot(uri=DEFAULT_.parent_structure, name="term__parent_structure", curie=DEFAULT_.curie('parent_structure'), + model_uri=DEFAULT_.term__parent_structure, domain=None, range=Optional[str]) + +slots.terminology__full_name = Slot(uri=DEFAULT_.full_name, name="terminology__full_name", curie=DEFAULT_.curie('full_name'), + model_uri=DEFAULT_.terminology__full_name, domain=None, range=str) + +slots.terminology__short_name = Slot(uri=DEFAULT_.short_name, name="terminology__short_name", curie=DEFAULT_.curie('short_name'), + model_uri=DEFAULT_.terminology__short_name, domain=None, range=str) + +slots.terminology__terms = Slot(uri=DEFAULT_.terms, name="terminology__terms", curie=DEFAULT_.curie('terms'), + model_uri=DEFAULT_.terminology__terms, domain=None, range=Union[Union[dict, Term], List[Union[dict, Term]]]) + +slots.terminology__defined_in = Slot(uri=DEFAULT_.defined_in, name="terminology__defined_in", curie=DEFAULT_.curie('defined_in'), + model_uri=DEFAULT_.terminology__defined_in, domain=None, range=Optional[str]) + +slots.terminology__ontology_identifier = Slot(uri=DEFAULT_.ontology_identifier, name="terminology__ontology_identifier", curie=DEFAULT_.curie('ontology_identifier'), + model_uri=DEFAULT_.terminology__ontology_identifier, domain=None, range=Optional[str]) + +slots.terminology__controlled_vocabulary = Slot(uri=DEFAULT_.controlled_vocabulary, name="terminology__controlled_vocabulary", curie=DEFAULT_.curie('controlled_vocabulary'), + model_uri=DEFAULT_.terminology__controlled_vocabulary, domain=None, range=Union[bool, Bool]) + +slots.terminology__taxonomy = Slot(uri=DEFAULT_.taxonomy, name="terminology__taxonomy", curie=DEFAULT_.curie('taxonomy'), + model_uri=DEFAULT_.terminology__taxonomy, domain=None, range=Union[bool, Bool]) + +slots.terminology__ontology = Slot(uri=DEFAULT_.ontology, name="terminology__ontology", curie=DEFAULT_.curie('ontology'), + model_uri=DEFAULT_.terminology__ontology, domain=None, range=Union[bool, Bool]) diff --git a/models/semantic/terminology.schema.json b/models/semantic/terminology.schema.json new file mode 100644 index 0000000..bfff8a1 --- /dev/null +++ b/models/semantic/terminology.schema.json @@ -0,0 +1,114 @@ +{ + "$defs": { + "Term": { + "additionalProperties": false, + "description": "", + "properties": { + "name_of_location": { + "type": "string" + }, + "ontology_identifier": { + "type": "string" + }, + "other_anatomical_relations": { + "type": "string" + }, + "parent_structure": { + "type": "string" + } + }, + "required": [ + "name_of_location" + ], + "title": "Term", + "type": "object" + }, + "Terminology": { + "additionalProperties": false, + "description": "A set of terms that identifies the annotations, providing human readability and context, and allowing communication about brain locations and structural properties", + "properties": { + "controlled_vocabulary": { + "type": "boolean" + }, + "defined_in": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "ontology": { + "type": "boolean" + }, + "ontology_identifier": { + "type": "string" + }, + "short_name": { + "type": "string" + }, + "taxonomy": { + "type": "boolean" + }, + "terms": { + "items": { + "$ref": "#/$defs/Term" + }, + "type": "array" + } + }, + "required": [ + "full_name", + "short_name", + "terms", + "controlled_vocabulary", + "taxonomy", + "ontology" + ], + "title": "Terminology", + "type": "object" + } + }, + "$id": "https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "metamodel_version": "1.7.0", + "properties": { + "controlled_vocabulary": { + "type": "boolean" + }, + "defined_in": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "ontology": { + "type": "boolean" + }, + "ontology_identifier": { + "type": "string" + }, + "short_name": { + "type": "string" + }, + "taxonomy": { + "type": "boolean" + }, + "terms": { + "items": { + "$ref": "#/$defs/Term" + }, + "type": "array" + } + }, + "required": [ + "full_name", + "short_name", + "terms", + "controlled_vocabulary", + "taxonomy", + "ontology" + ], + "title": "terminology", + "type": "object", + "version": null +} diff --git a/models/semantic/terminology.yaml b/models/semantic/terminology.yaml new file mode 100644 index 0000000..c4fe858 --- /dev/null +++ b/models/semantic/terminology.yaml @@ -0,0 +1,43 @@ +id: https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1 +name: terminology +prefixes: + linkml: https://w3id.org/linkml/ +imports: + - linkml:types +default_range: string + +classes: + + Term: + attributes: + name_of_location: + required: true + ontology_identifier: + other_anatomical_relations: + parent_structure: + + Terminology: + description: >- + A set of terms that identifies the annotations, providing human readability and context, and allowing communication about brain locations and structural properties + tree_root: true + attributes: + full_name: + required: true + short_name: + required: true + terms: + multivalued: true + inlined_as_list: true + range: Term + required: true + defined_in: + ontology_identifier: + controlled_vocabulary: + range: boolean + required: true + taxonomy: + range: boolean + required: true + ontology: + range: boolean + required: true \ No newline at end of file diff --git a/models/spatial/__init__.py b/models/spatial/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/spatial/coordinatesystem.py b/models/spatial/coordinatesystem.py new file mode 100644 index 0000000..8a09a65 --- /dev/null +++ b/models/spatial/coordinatesystem.py @@ -0,0 +1,167 @@ +# Auto generated from coordinatesystem.yaml by pythongen.py version: 0.9.0 +# Generation date: 2023-03-02T16:41:48 +# Schema: coordinatesystem +# +# id: https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1 +# description: +# license: https://creativecommons.org/publicdomain/zero/1.0/ + +import dataclasses +import sys +import re +from jsonasobj2 import JsonObj, as_dict +from typing import Optional, List, Union, Dict, ClassVar, Any +from dataclasses import dataclass +from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue, PvFormulaOptions + +from linkml_runtime.utils.slot import Slot +from linkml_runtime.utils.metamodelcore import empty_list, empty_dict, bnode +from linkml_runtime.utils.yamlutils import YAMLRoot, extended_str, extended_float, extended_int +from linkml_runtime.utils.dataclass_extensions_376 import dataclasses_init_fn_with_kwargs +from linkml_runtime.utils.formatutils import camelcase, underscore, sfx +from linkml_runtime.utils.enumerations import EnumDefinitionImpl +from rdflib import Namespace, URIRef +from linkml_runtime.utils.curienamespace import CurieNamespace +from linkml_runtime.linkml_model.types import Date, String +from linkml_runtime.utils.metamodelcore import XSDDate + +metamodel_version = "1.7.0" +version = None + +# Overwrite dataclasses _init_fn to add **kwargs in __init__ +dataclasses._init_fn = dataclasses_init_fn_with_kwargs + +# Namespaces +LINKML = CurieNamespace('linkml', 'https://w3id.org/linkml/') +DEFAULT_ = CurieNamespace('', 'https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/') + + +# Types + +# Class references + + + +@dataclass +class ThreeDimensionalCartesian(YAMLRoot): + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/ThreeDimensionalCartesian") + class_class_curie: ClassVar[str] = None + class_name: ClassVar[str] = "ThreeDimensionalCartesian" + class_model_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/ThreeDimensionalCartesian") + + full_name: str = None + short_name: str = None + native_unit: str = None + origin: str = None + anatomical_axes_representation: str = None + release_date: Union[str, XSDDate] = None + version_information: str = None + default_image: Optional[str] = None + ontology_identifier: Optional[str] = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.full_name): + self.MissingRequiredField("full_name") + if not isinstance(self.full_name, str): + self.full_name = str(self.full_name) + + if self._is_empty(self.short_name): + self.MissingRequiredField("short_name") + if not isinstance(self.short_name, str): + self.short_name = str(self.short_name) + + if self._is_empty(self.native_unit): + self.MissingRequiredField("native_unit") + if not isinstance(self.native_unit, str): + self.native_unit = str(self.native_unit) + + if self._is_empty(self.origin): + self.MissingRequiredField("origin") + if not isinstance(self.origin, str): + self.origin = str(self.origin) + + if self._is_empty(self.anatomical_axes_representation): + self.MissingRequiredField("anatomical_axes_representation") + if not isinstance(self.anatomical_axes_representation, str): + self.anatomical_axes_representation = str(self.anatomical_axes_representation) + + if self._is_empty(self.release_date): + self.MissingRequiredField("release_date") + if not isinstance(self.release_date, XSDDate): + self.release_date = XSDDate(self.release_date) + + if self._is_empty(self.version_information): + self.MissingRequiredField("version_information") + if not isinstance(self.version_information, str): + self.version_information = str(self.version_information) + + if self.default_image is not None and not isinstance(self.default_image, str): + self.default_image = str(self.default_image) + + if self.ontology_identifier is not None and not isinstance(self.ontology_identifier, str): + self.ontology_identifier = str(self.ontology_identifier) + + super().__post_init__(**kwargs) + + +@dataclass +class CoordinateSystem(YAMLRoot): + """ + A framework for specifying locations with units, origin, direction, and orientation + """ + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/CoordinateSystem") + class_class_curie: ClassVar[str] = None + class_name: ClassVar[str] = "CoordinateSystem" + class_model_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/CoordinateSystem") + + threedimensionalcartesian: Union[dict, ThreeDimensionalCartesian] = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.threedimensionalcartesian): + self.MissingRequiredField("threedimensionalcartesian") + if not isinstance(self.threedimensionalcartesian, ThreeDimensionalCartesian): + self.threedimensionalcartesian = ThreeDimensionalCartesian(**as_dict(self.threedimensionalcartesian)) + + super().__post_init__(**kwargs) + + +# Enumerations + + +# Slots +class slots: + pass + +slots.threeDimensionalCartesian__full_name = Slot(uri=DEFAULT_.full_name, name="threeDimensionalCartesian__full_name", curie=DEFAULT_.curie('full_name'), + model_uri=DEFAULT_.threeDimensionalCartesian__full_name, domain=None, range=str) + +slots.threeDimensionalCartesian__short_name = Slot(uri=DEFAULT_.short_name, name="threeDimensionalCartesian__short_name", curie=DEFAULT_.curie('short_name'), + model_uri=DEFAULT_.threeDimensionalCartesian__short_name, domain=None, range=str) + +slots.threeDimensionalCartesian__native_unit = Slot(uri=DEFAULT_.native_unit, name="threeDimensionalCartesian__native_unit", curie=DEFAULT_.curie('native_unit'), + model_uri=DEFAULT_.threeDimensionalCartesian__native_unit, domain=None, range=str) + +slots.threeDimensionalCartesian__origin = Slot(uri=DEFAULT_.origin, name="threeDimensionalCartesian__origin", curie=DEFAULT_.curie('origin'), + model_uri=DEFAULT_.threeDimensionalCartesian__origin, domain=None, range=str) + +slots.threeDimensionalCartesian__anatomical_axes_representation = Slot(uri=DEFAULT_.anatomical_axes_representation, name="threeDimensionalCartesian__anatomical_axes_representation", curie=DEFAULT_.curie('anatomical_axes_representation'), + model_uri=DEFAULT_.threeDimensionalCartesian__anatomical_axes_representation, domain=None, range=str) + +slots.threeDimensionalCartesian__release_date = Slot(uri=DEFAULT_.release_date, name="threeDimensionalCartesian__release_date", curie=DEFAULT_.curie('release_date'), + model_uri=DEFAULT_.threeDimensionalCartesian__release_date, domain=None, range=Union[str, XSDDate]) + +slots.threeDimensionalCartesian__version_information = Slot(uri=DEFAULT_.version_information, name="threeDimensionalCartesian__version_information", curie=DEFAULT_.curie('version_information'), + model_uri=DEFAULT_.threeDimensionalCartesian__version_information, domain=None, range=str) + +slots.threeDimensionalCartesian__default_image = Slot(uri=DEFAULT_.default_image, name="threeDimensionalCartesian__default_image", curie=DEFAULT_.curie('default_image'), + model_uri=DEFAULT_.threeDimensionalCartesian__default_image, domain=None, range=Optional[str]) + +slots.threeDimensionalCartesian__ontology_identifier = Slot(uri=DEFAULT_.ontology_identifier, name="threeDimensionalCartesian__ontology_identifier", curie=DEFAULT_.curie('ontology_identifier'), + model_uri=DEFAULT_.threeDimensionalCartesian__ontology_identifier, domain=None, range=Optional[str]) + +slots.coordinateSystem__threedimensionalcartesian = Slot(uri=DEFAULT_.threedimensionalcartesian, name="coordinateSystem__threedimensionalcartesian", curie=DEFAULT_.curie('threedimensionalcartesian'), + model_uri=DEFAULT_.coordinateSystem__threedimensionalcartesian, domain=None, range=Union[dict, ThreeDimensionalCartesian]) diff --git a/models/spatial/coordinatesystem.schema.json b/models/spatial/coordinatesystem.schema.json new file mode 100644 index 0000000..f42db07 --- /dev/null +++ b/models/spatial/coordinatesystem.schema.json @@ -0,0 +1,78 @@ +{ + "$defs": { + "CoordinateSystem": { + "additionalProperties": false, + "description": "A framework for specifying locations with units, origin, direction, and orientation", + "properties": { + "threedimensionalcartesian": { + "$ref": "#/$defs/ThreeDimensionalCartesian" + } + }, + "required": [ + "threedimensionalcartesian" + ], + "title": "CoordinateSystem", + "type": "object" + }, + "ThreeDimensionalCartesian": { + "additionalProperties": false, + "description": "", + "properties": { + "anatomical_axes_representation": { + "type": "string" + }, + "default_image": { + "type": "string" + }, + "full_name": { + "type": "string" + }, + "native_unit": { + "type": "string" + }, + "ontology_identifier": { + "type": "string" + }, + "origin": { + "type": "string" + }, + "release_date": { + "format": "date", + "type": "string" + }, + "short_name": { + "type": "string" + }, + "version_information": { + "type": "string" + } + }, + "required": [ + "full_name", + "short_name", + "native_unit", + "origin", + "anatomical_axes_representation", + "release_date", + "version_information" + ], + "title": "ThreeDimensionalCartesian", + "type": "object" + } + }, + "$id": "https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "metamodel_version": "1.7.0", + "properties": { + "threedimensionalcartesian": { + "$ref": "#/$defs/ThreeDimensionalCartesian" + } + }, + "required": [ + "threedimensionalcartesian" + ], + "title": "coordinatesystem", + "type": "object", + "version": null +} diff --git a/models/spatial/coordinatesystem.yaml b/models/spatial/coordinatesystem.yaml new file mode 100644 index 0000000..1d5b301 --- /dev/null +++ b/models/spatial/coordinatesystem.yaml @@ -0,0 +1,39 @@ +id: https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1 +name: coordinatesystem +prefixes: + linkml: https://w3id.org/linkml/ +imports: + - linkml:types +default_range: string + +classes: + + ThreeDimensionalCartesian: + attributes: + full_name: + required: true + short_name: + required: true + native_unit: + required: true + origin: + required: true + anatomical_axes_representation: + required: true + release_date: + range: date + required: true + version_information: + required: true + default_image: + ontology_identifier: + + CoordinateSystem: + description: >- + A framework for specifying locations with units, origin, direction, and orientation + tree_root: true + attributes: + threedimensionalcartesian: + multivalued: false + range: ThreeDimensionalCartesian + required: true \ No newline at end of file diff --git a/models/spatial/referencedata.py b/models/spatial/referencedata.py new file mode 100644 index 0000000..03bbf8d --- /dev/null +++ b/models/spatial/referencedata.py @@ -0,0 +1,186 @@ +# Auto generated from referencedata.yaml by pythongen.py version: 0.9.0 +# Generation date: 2023-03-02T16:50:54 +# Schema: referencedata +# +# id: https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1 +# description: +# license: https://creativecommons.org/publicdomain/zero/1.0/ + +import dataclasses +import sys +import re +from jsonasobj2 import JsonObj, as_dict +from typing import Optional, List, Union, Dict, ClassVar, Any +from dataclasses import dataclass +from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue, PvFormulaOptions + +from linkml_runtime.utils.slot import Slot +from linkml_runtime.utils.metamodelcore import empty_list, empty_dict, bnode +from linkml_runtime.utils.yamlutils import YAMLRoot, extended_str, extended_float, extended_int +from linkml_runtime.utils.dataclass_extensions_376 import dataclasses_init_fn_with_kwargs +from linkml_runtime.utils.formatutils import camelcase, underscore, sfx +from linkml_runtime.utils.enumerations import EnumDefinitionImpl +from rdflib import Namespace, URIRef +from linkml_runtime.utils.curienamespace import CurieNamespace +from linkml_runtime.linkml_model.types import Integer, String + +metamodel_version = "1.7.0" +version = None + +# Overwrite dataclasses _init_fn to add **kwargs in __init__ +dataclasses._init_fn = dataclasses_init_fn_with_kwargs + +# Namespaces +LINKML = CurieNamespace('linkml', 'https://w3id.org/linkml/') +DEFAULT_ = CurieNamespace('', 'https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/') + + +# Types + +# Class references + + + +@dataclass +class Subject(YAMLRoot): + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/Subject") + class_class_curie: ClassVar[str] = None + class_name: ClassVar[str] = "Subject" + class_model_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/Subject") + + identifier: str = None + species: str = None + biological_sex: str = None + method_information: str = None + processing_information: str = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.identifier): + self.MissingRequiredField("identifier") + if not isinstance(self.identifier, str): + self.identifier = str(self.identifier) + + if self._is_empty(self.species): + self.MissingRequiredField("species") + if not isinstance(self.species, str): + self.species = str(self.species) + + if self._is_empty(self.biological_sex): + self.MissingRequiredField("biological_sex") + if not isinstance(self.biological_sex, str): + self.biological_sex = str(self.biological_sex) + + if self._is_empty(self.method_information): + self.MissingRequiredField("method_information") + if not isinstance(self.method_information, str): + self.method_information = str(self.method_information) + + if self._is_empty(self.processing_information): + self.MissingRequiredField("processing_information") + if not isinstance(self.processing_information, str): + self.processing_information = str(self.processing_information) + + super().__post_init__(**kwargs) + + +@dataclass +class Image(YAMLRoot): + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/Image") + class_class_curie: ClassVar[str] = None + class_name: ClassVar[str] = "Image" + class_model_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/Image") + + defined_in: str = None + voxel_or_pixel_size: int = None + coordinate_space: str = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.defined_in): + self.MissingRequiredField("defined_in") + if not isinstance(self.defined_in, str): + self.defined_in = str(self.defined_in) + + if self._is_empty(self.voxel_or_pixel_size): + self.MissingRequiredField("voxel_or_pixel_size") + if not isinstance(self.voxel_or_pixel_size, int): + self.voxel_or_pixel_size = int(self.voxel_or_pixel_size) + + if self._is_empty(self.coordinate_space): + self.MissingRequiredField("coordinate_space") + if not isinstance(self.coordinate_space, str): + self.coordinate_space = str(self.coordinate_space) + + super().__post_init__(**kwargs) + + +@dataclass +class ReferenceData(YAMLRoot): + """ + Graphical representations of one or several brains, or parts of brains, chosen as the biological reference for + that atlas + """ + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/ReferenceData") + class_class_curie: ClassVar[str] = None + class_name: ClassVar[str] = "ReferenceData" + class_model_uri: ClassVar[URIRef] = URIRef("https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1/ReferenceData") + + subjects: Union[Union[dict, Subject], List[Union[dict, Subject]]] = None + image: Union[dict, Image] = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.subjects): + self.MissingRequiredField("subjects") + if not isinstance(self.subjects, list): + self.subjects = [self.subjects] if self.subjects is not None else [] + self.subjects = [v if isinstance(v, Subject) else Subject(**as_dict(v)) for v in self.subjects] + + if self._is_empty(self.image): + self.MissingRequiredField("image") + if not isinstance(self.image, Image): + self.image = Image(**as_dict(self.image)) + + super().__post_init__(**kwargs) + + +# Enumerations + + +# Slots +class slots: + pass + +slots.subject__identifier = Slot(uri=DEFAULT_.identifier, name="subject__identifier", curie=DEFAULT_.curie('identifier'), + model_uri=DEFAULT_.subject__identifier, domain=None, range=str) + +slots.subject__species = Slot(uri=DEFAULT_.species, name="subject__species", curie=DEFAULT_.curie('species'), + model_uri=DEFAULT_.subject__species, domain=None, range=str) + +slots.subject__biological_sex = Slot(uri=DEFAULT_.biological_sex, name="subject__biological_sex", curie=DEFAULT_.curie('biological_sex'), + model_uri=DEFAULT_.subject__biological_sex, domain=None, range=str) + +slots.subject__method_information = Slot(uri=DEFAULT_.method_information, name="subject__method_information", curie=DEFAULT_.curie('method_information'), + model_uri=DEFAULT_.subject__method_information, domain=None, range=str) + +slots.subject__processing_information = Slot(uri=DEFAULT_.processing_information, name="subject__processing_information", curie=DEFAULT_.curie('processing_information'), + model_uri=DEFAULT_.subject__processing_information, domain=None, range=str) + +slots.image__defined_in = Slot(uri=DEFAULT_.defined_in, name="image__defined_in", curie=DEFAULT_.curie('defined_in'), + model_uri=DEFAULT_.image__defined_in, domain=None, range=str) + +slots.image__voxel_or_pixel_size = Slot(uri=DEFAULT_.voxel_or_pixel_size, name="image__voxel_or_pixel_size", curie=DEFAULT_.curie('voxel_or_pixel_size'), + model_uri=DEFAULT_.image__voxel_or_pixel_size, domain=None, range=int) + +slots.image__coordinate_space = Slot(uri=DEFAULT_.coordinate_space, name="image__coordinate_space", curie=DEFAULT_.curie('coordinate_space'), + model_uri=DEFAULT_.image__coordinate_space, domain=None, range=str) + +slots.referenceData__subjects = Slot(uri=DEFAULT_.subjects, name="referenceData__subjects", curie=DEFAULT_.curie('subjects'), + model_uri=DEFAULT_.referenceData__subjects, domain=None, range=Union[Union[dict, Subject], List[Union[dict, Subject]]]) + +slots.referenceData__image = Slot(uri=DEFAULT_.image, name="referenceData__image", curie=DEFAULT_.curie('image'), + model_uri=DEFAULT_.referenceData__image, domain=None, range=Union[dict, Image]) diff --git a/models/spatial/referencedata.schema.json b/models/spatial/referencedata.schema.json new file mode 100644 index 0000000..da4d594 --- /dev/null +++ b/models/spatial/referencedata.schema.json @@ -0,0 +1,99 @@ +{ + "$defs": { + "Image": { + "additionalProperties": false, + "description": "", + "properties": { + "coordinate_space": { + "type": "string" + }, + "defined_in": { + "type": "string" + }, + "voxel_or_pixel_size": { + "type": "integer" + } + }, + "required": [ + "defined_in", + "voxel_or_pixel_size", + "coordinate_space" + ], + "title": "Image", + "type": "object" + }, + "ReferenceData": { + "additionalProperties": false, + "description": "Graphical representations of one or several brains, or parts of brains, chosen as the biological reference for that atlas", + "properties": { + "image": { + "$ref": "#/$defs/Image" + }, + "subjects": { + "items": { + "$ref": "#/$defs/Subject" + }, + "type": "array" + } + }, + "required": [ + "subjects", + "image" + ], + "title": "ReferenceData", + "type": "object" + }, + "Subject": { + "additionalProperties": false, + "description": "", + "properties": { + "biological_sex": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "method_information": { + "type": "string" + }, + "processing_information": { + "type": "string" + }, + "species": { + "type": "string" + } + }, + "required": [ + "identifier", + "species", + "biological_sex", + "method_information", + "processing_information" + ], + "title": "Subject", + "type": "object" + } + }, + "$id": "https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "metamodel_version": "1.7.0", + "properties": { + "image": { + "$ref": "#/$defs/Image" + }, + "subjects": { + "items": { + "$ref": "#/$defs/Subject" + }, + "type": "array" + } + }, + "required": [ + "subjects", + "image" + ], + "title": "referencedata", + "type": "object", + "version": null +} diff --git a/models/spatial/referencedata.yaml b/models/spatial/referencedata.yaml new file mode 100644 index 0000000..dfc3154 --- /dev/null +++ b/models/spatial/referencedata.yaml @@ -0,0 +1,55 @@ +id: https://www.biorxiv.org/content/10.1101/2023.01.22.525049v1 +name: referencedata +prefixes: + linkml: https://w3id.org/linkml/ +imports: + - linkml:types +default_range: string + +classes: + + Subject: + attributes: + identifier: + required: true + species: + required: true + biological_sex: +# range: sex_enum + required: true + method_information: + required: true + processing_information: + required: true + + Image: + attributes: + defined_in: + required: true + voxel_or_pixel_size: + range: integer + required: true + coordinate_space: + required: true + + ReferenceData: + description: >- + Graphical representations of one or several brains, or parts of brains, chosen as the biological reference for that atlas + tree_root: true + attributes: + subjects: + multivalued: true + inlined_as_list: true + range: Subject + required: true + image: + multivalued: false + range: Image + required: true + +#enums: +# SexType: +# permissible_values: +# F: +# M: +# Other: \ No newline at end of file diff --git a/routers/biosamples.py b/routers/biosamples.py index 782177b..6ed29be 100644 --- a/routers/biosamples.py +++ b/routers/biosamples.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Depends, HTTPException -from pydantic import BaseModel - from dependencies import get_token_header +from models import BioSample + router = APIRouter( prefix="/biosamples", @@ -11,10 +11,6 @@ ) -class Biosample(BaseModel): - name: str - - fake_biosamples_db = {"first": {"name": "Sample 1"}, "second": {"name": "Sample 2"}} @@ -24,10 +20,10 @@ async def read_biosamples(): @router.post("/") -async def create_biosample(biosample: Biosample): +async def create_biosample(biosample: BioSample): if biosample.name in fake_biosamples_db: raise HTTPException(status_code=404, detail="Biosample already created") - return {"name": fake_biosamples_db[biosample.name]["name"], "biosample_id": biosample.name} + return {"name": fake_biosamples_db[biosample.name]["name"], "biosample_id": biosample.identifier} @router.get("/{biosample_id}") diff --git a/routers/participants.py b/routers/participants.py index 9b3a5c6..e6db4f1 100644 --- a/routers/participants.py +++ b/routers/participants.py @@ -1,9 +1,7 @@ from typing import Union - from fastapi import APIRouter, Depends, HTTPException -from pydantic import BaseModel - from dependencies import get_token_header +from models import Participant router = APIRouter( prefix="/participants", @@ -13,14 +11,9 @@ ) -class Participant(BaseModel): - name: str - id: int - - @router.post("/", response_model=Participant) async def create_participant(participant: Participant): - return {"participant_name": participant.name, "participant_id": participant_id} + return {"participant_name": participant.altName, "participant_id": participant.identifier} @router.get("/{participant_id}") @@ -30,7 +23,7 @@ async def read_participant(participant_id: int, q: Union[str, None] = None): @router.put("/{participant_id}") async def update_participant(participant_id: int, participant: Participant): - return {"participant_name": participant.name, "participant_id": participant_id} + return {"participant_name": participant.altName, "participant_id": participant_id} @router.delete("/{participant_id}") diff --git a/services/__init__.py b/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services/biosampleservice.py b/services/biosampleservice.py new file mode 100644 index 0000000..4fc773b --- /dev/null +++ b/services/biosampleservice.py @@ -0,0 +1,17 @@ +from fastapi_sqlalchemy import DBSessionMiddleware, db + +from schema import BioSample as SchemaBioSample +from models import BioSample as ModelBioSample + + +@app.post('/biosample/', response_model=SchemaBioSample) +async def biosample(biosample: SchemaBioSample): + db_biosample = ModelBioSample(name=biosample.name, biosample_id = biosample.biosample_id) + db.session.add(db_biosample) + db.session.commit() + return db_biosample + +@app.get('/biosample/') +async def biosample(): + biosample = db.session.query(ModelBioSample).all() + return biosample diff --git a/services/participantservice.py b/services/participantservice.py new file mode 100644 index 0000000..ea7f115 --- /dev/null +++ b/services/participantservice.py @@ -0,0 +1,17 @@ +from fastapi_sqlalchemy import DBSessionMiddleware, db + +from schema import Participant as SchemaParticipant +from models import Participant as ModelParticipant + + +@app.post('/participant/', response_model=SchemaParticipant) +async def participant(participant:SchemaParticipant): + db_participant = ModelParticipant(name=participant.name, age=participant.age) + db.session.add(db_participant) + db.session.commit() + return db_participant + +@app.get('/participant/') +async def participant(): + participant = db.session.query(ModelParticipant).all() + return participant \ No newline at end of file