diff --git a/.cruft.json b/.cruft.json new file mode 100644 index 0000000..a095c77 --- /dev/null +++ b/.cruft.json @@ -0,0 +1,27 @@ +{ + "template": "https://github.com/linkml/linkml-project-cookiecutter", + "commit": "d9b85bbbdcc49163416a3e3e18e23fe2ecce501b", + "checkout": null, + "context": { + "cookiecutter": { + "project_name": "thing-description-schema", + "__project_slug": "thing_description_schema", + "github_org": "mahdanoura", + "__source_path": "src/thing_description_schema/schema/thing_description_schema.yaml", + "project_description": "Thing Description Information Model as LinkML schema", + "full_name": "Mahda Noura", + "email": "mahdanoura@gmail.com", + "__author": "Mahda Noura ", + "license": "MIT", + "main_schema_class": "Thing", + "create_python_classes": "Yes", + "use_schemasheets": "No", + "google_sheet_id": "1wVoaiFg47aT9YWNeRfTZ8tYHN8s8PAuDx5i2HUcDpvQ", + "google_sheet_tabs": "personinfo enums", + "__google_sheet_module": "personinfo_enums", + "github_token_for_pypi_deployment": "PYPI_PASSWORD", + "_template": "https://github.com/linkml/linkml-project-cookiecutter" + } + }, + "directory": null +} diff --git a/.env.public b/.env.public new file mode 100644 index 0000000..6a5d7e7 --- /dev/null +++ b/.env.public @@ -0,0 +1,27 @@ +# This file is public in git. No sensitive info allowed. + +###### schema definition variables, used by makefile + +LINKML_SCHEMA_NAME="thing_description_schema" +LINKML_SCHEMA_AUTHOR="Mahda Noura " +LINKML_SCHEMA_DESCRIPTION="Thing Description Standard" +LINKML_SCHEMA_SOURCE_PATH="src/thing_description_schema/schema/thing_description_schema.yaml" +LINKML_SCHEMA_GOOGLE_SHEET_ID="1wVoaiFg47aT9YWNeRfTZ8tYHN8s8PAuDx5i2HUcDpvQ" +LINKML_SCHEMA_GOOGLE_SHEET_TABS="personinfo enums" + +###### linkml generator variables, used by makefile + +## gen-project configuration file +LINKML_GENERATORS_CONFIG_YAML= --config-file config.yaml + +## pass args if gendoc ignores config.yaml (i.e. --no-mergeimports) +LINKML_GENERATORS_DOC_ARGS= + +## pass args to workaround genowl rdfs config bug (linkml#1453) +## (i.e. --no-type-objects --no-metaclasses --metadata-profile rdfs) +LINKML_GENERATORS_OWL_ARGS= + +## pass args to trigger experimental java/typescript generation +LINKML_GENERATORS_JAVA_ARGS= +LINKML_GENERATORS_TYPESCRIPT_ARGS= + diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml new file mode 100644 index 0000000..5aa3c60 --- /dev/null +++ b/.github/workflows/deploy-docs.yaml @@ -0,0 +1,40 @@ +--- +name: Auto-deployment of thing_description_schema Documentation +on: + push: + branches: [master] + +jobs: + build-docs: + runs-on: ubuntu-latest + + # Grant GITHUB_TOKEN the permissions required to make a Pages deployment + permissions: + contents: write # to let mkdocs write the new docs + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 # otherwise, you will failed to push refs to dest repo + + - name: Set up Python. + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry. + run: pipx install poetry + + - name: Install dependencies. + run: poetry install -E docs + + - name: Build documentation. + run: | + mkdir -p docs + touch docs/.nojekyll + make gendoc + ([ ! -f docs/about.md ] && cp src/docs/about.md docs/) || true + make mkd-gh-deploy diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..c87a3da --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,36 @@ +# Built from: +# https://docs.github.com/en/actions/guides/building-and-testing-python +--- +name: Build and test thing_description_schema + +on: [pull_request] + +jobs: + test: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.11"] + + steps: + + - name: Check out repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry + run: pipx install poetry + + - name: Install dependencies + run: poetry install --no-interaction --no-root + + - name: Install project + run: poetry install --no-interaction + + - name: Run test suite + run: make test diff --git a/.github/workflows/pypi-publish.yaml b/.github/workflows/pypi-publish.yaml new file mode 100644 index 0000000..c39c2eb --- /dev/null +++ b/.github/workflows/pypi-publish.yaml @@ -0,0 +1,36 @@ +--- +name: Publish Python Package + +on: + release: + types: [created] + +jobs: + build-n-publish: + name: Build and publish Python 🐍 distributions 📦 to PyPI + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install Poetry + run: | + pipx install poetry + poetry self add "poetry-dynamic-versioning[plugin]" + + # - name: Install dependencies + # run: poetry install --no-interaction + + - name: Build source and wheel archives + run: poetry build + + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@v1.2.2 + with: + user: __token__ + password: ${{ secrets.PYPI_PASSWORD }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e110be5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,133 @@ +/docs/ +/project/docs/ +/tmp/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..849580c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,112 @@ +# Contributing to thing-description-schema + +:+1: First of all: Thank you for taking the time to contribute! + +The following is a set of guidelines for contributing to +thing-description-schema. These guidelines are not strict rules. +Use your best judgment, and feel free to propose changes to this document +in a pull request. + +## Table Of Contents + +* [Guidelines for Contributions and Requests](#contributions) + * [Reporting issues and making requests](#reporting-issues) + * [Questions and Discussion](#questions-and-discussion) + * [Adding new elements yourself](#adding-elements) +* [Best Practices](#best-practices) + * [How to write a great issue](#great-issues) + * [How to create a great pull/merge request](#great-pulls) + + + + +## Guidelines for Contributions and Requests + + + +### Reporting problems and suggesting changes to with the data model + +Please use our [Issue Tracker][issues] for any of the following: + +- Reporting problems +- Requesting new schema elements + + + +### Questions and Discussions + +Please use our [Discussions forum][discussions] to ask general questions or contribute to discussions. + + + +### Adding new elements yourself + +Please submit a [Pull Request][pulls] to submit a new term for consideration. + + + +## Best Practices + + + +### GitHub Best Practice + +- Creating and curating issues + - Read ["About Issues"][[about-issues]] + - Issues should be focused and actionable + - Complex issues should be broken down into simpler issues where possible +- Pull Requests + - Read ["About Pull Requests"][about-pulls] + - Read [GitHub Pull Requests: 10 Tips to Know](https://blog.mergify.com/github-pull-requests-10-tips-to-know/) + - Pull Requests (PRs) should be atomic and aim to close a single issue + - Long running PRs should be avoided where possible + - PRs should reference issues following standard conventions (e.g. “fixes #123”) + - Schema developers should always be working on a single issue at any one time + - Never work on the main branch, always work on an issue/feature branch + - Core developers can work on branches off origin rather than forks + - Always create a PR on a branch to maximize transparency of what you are doing + - PRs should be reviewed and merged in a timely fashion by the thing-description-schema technical leads + - PRs that do not pass GitHub actions should never be merged + - In the case of git conflicts, the contributor should try and resolve the conflict + - If a PR fails a GitHub action check, the contributor should try and resolve the issue in a timely fashion + +### Understanding LinkML + +Core developers should read the material on the [LinkML site](https://linkml.io/linkml), in particular: + +- [Overview](https://linkml.io/linkml/intro/overview.html) +- [Tutorial](https://linkml.io/linkml/intro/tutorial.html) +- [Schemas](https://linkml.io/linkml/schemas/index.html) +- [FAQ](https://linkml.io/linkml/faq/index.html) + +### Modeling Best Practice + +- Follow Naming conventions + - Standard LinkML naming conventions should be followed (UpperCamelCase for classes and enums, snake_case for slots) + - Know how to use the LinkML linter to check style and conventions + - The names for classes should be nouns or noun-phrases: Person, GenomeAnnotation, Address, Sample + - Spell out abbreviations and short forms, except where this goes against convention (e.g. do not spell out DNA) + - Elements that are imported from outside (e.g. schema.org) need not follow the same naming conventions + - Multivalued slots should be named as plurals +- Document model elements + - All model elements should have documentation (descriptions) and other textual annotations (e.g. comments, notes) + - Textual annotations on classes, slots and enumerations should be written with minimal jargon, clear grammar and no misspellings +- Include examples and counter-examples (intentionally invalid examples) + - Rationale: these serve as documentation and unit tests + - These will be used by the automated test suite + - All elements of the nmdc-schema must be illustrated with valid and invalid data examples in src/data. New schema elements will not be merged into the main branch until examples are provided + - Invalid example data files should be invalid for one single reason, which should be reflected in the filename. It should be possible to render the invalid example files valid by addressing that single fault. +- Use enums for categorical values + - Rationale: Open-ended string ranges encourage multiple values to represent the same entity, like “water”, “H2O” and “HOH” + - Any slot whose values could be constrained to a finite set should use an Enum + - Non-categorical values, e.g. descriptive fields like `name` or `description` fall outside of this. +- Reuse + - Existing scheme elements should be reused where appropriate, rather than making duplicative elements + - More specific classes can be created by refinining classes using inheritance (`is_a`) + +[about-branches]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches +[about-issues]: https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues +[about-pulls]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests +[issues]: https://github.com/mahda/thing-description-schema/issues/ +[pulls]: https://github.com/mahda/thing-description-schema/pulls/ + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ddc3029 --- /dev/null +++ b/Makefile @@ -0,0 +1,225 @@ +MAKEFLAGS += --warn-undefined-variables +SHELL := bash +.SHELLFLAGS := -eu -o pipefail -c +.DEFAULT_GOAL := help +.DELETE_ON_ERROR: +.SUFFIXES: +.SECONDARY: + +# environment variables +.EXPORT_ALL_VARIABLES: +ifdef LINKML_ENVIRONMENT_FILENAME +include ${LINKML_ENVIRONMENT_FILENAME} +else +include .env.public +endif + +RUN = poetry run +SCHEMA_NAME = $(LINKML_SCHEMA_NAME) +SOURCE_SCHEMA_PATH = $(LINKML_SCHEMA_SOURCE_PATH) +SOURCE_SCHEMA_DIR = $(dir $(SOURCE_SCHEMA_PATH)) +SRC = src +DEST = project +PYMODEL = $(SRC)/$(SCHEMA_NAME)/datamodel +DOCDIR = docs +EXAMPLEDIR = examples +SHEET_MODULE = personinfo_enums +SHEET_ID = $(LINKML_SCHEMA_GOOGLE_SHEET_ID) +SHEET_TABS = $(LINKML_SCHEMA_GOOGLE_SHEET_TABS) +SHEET_MODULE_PATH = $(SOURCE_SCHEMA_DIR)/$(SHEET_MODULE).yaml + +CONFIG_YAML = +ifdef LINKML_GENERATORS_CONFIG_YAML +CONFIG_YAML = ${LINKML_GENERATORS_CONFIG_YAML} +endif + +GEN_DOC_ARGS = +ifdef LINKML_GENERATORS_DOC_ARGS +GEN_DOC_ARGS = ${LINKML_GENERATORS_DOC_ARGS} +endif + +GEN_OWL_ARGS = +ifdef LINKML_GENERATORS_OWL_ARGS +GEN_OWL_ARGS = ${LINKML_GENERATORS_OWL_ARGS} +endif + +GEN_JAVA_ARGS = +ifdef LINKML_GENERATORS_JAVA_ARGS +GEN_JAVA_ARGS = ${LINKML_GENERATORS_JAVA_ARGS} +endif + +GEN_TS_ARGS = +ifdef LINKML_GENERATORS_TYPESCRIPT_ARGS +GEN_TS_ARGS = ${LINKML_GENERATORS_TYPESCRIPT_ARGS} +endif + + +# basename of a YAML file in model/ +.PHONY: all clean setup gen-project gen-examples gendoc git-init-add git-init git-add git-commit git-status + +# note: "help" MUST be the first target in the file, +# when the user types "make" they should get help info +help: status + @echo "" + @echo "make setup -- initial setup (run this first)" + @echo "make site -- makes site locally" + @echo "make install -- install dependencies" + @echo "make test -- runs tests" + @echo "make lint -- perform linting" + @echo "make testdoc -- builds docs and runs local test server" + @echo "make deploy -- deploys site" + @echo "make update -- updates linkml version" + @echo "make help -- show this help" + @echo "" + +status: check-config + @echo "Project: $(SCHEMA_NAME)" + @echo "Source: $(SOURCE_SCHEMA_PATH)" + +# generate products and add everything to github +setup: check-config git-init install gen-project gen-examples gendoc git-add git-commit + +# install any dependencies required for building +install: + poetry install +.PHONY: install + +# --- +# Project Synchronization +# --- +# +# check we are up to date +check: cruft-check +cruft-check: + cruft check +cruft-diff: + cruft diff + +update: update-template update-linkml +update-template: + cruft update + +# todo: consider pinning to template +update-linkml: + poetry add -D linkml@latest + +# EXPERIMENTAL +create-data-harmonizer: + npm init data-harmonizer $(SOURCE_SCHEMA_PATH) + +all: site +site: gen-project gendoc +%.yaml: gen-project +deploy: all mkd-gh-deploy + +compile-sheets: + $(RUN) sheets2linkml --gsheet-id $(SHEET_ID) $(SHEET_TABS) > $(SHEET_MODULE_PATH).tmp && mv $(SHEET_MODULE_PATH).tmp $(SHEET_MODULE_PATH) + +# In future this will be done by conversion +gen-examples: + cp src/data/examples/* $(EXAMPLEDIR) + +# generates all project files + +gen-project: $(PYMODEL) + $(RUN) gen-project ${CONFIG_YAML} -d $(DEST) $(SOURCE_SCHEMA_PATH) && mv $(DEST)/*.py $(PYMODEL) + + +# non-empty arg triggers owl (workaround https://github.com/linkml/linkml/issues/1453) +ifneq ($(strip ${GEN_OWL_ARGS}),) + mkdir -p ${DEST}/owl || true + $(RUN) gen-owl ${GEN_OWL_ARGS} $(SOURCE_SCHEMA_PATH) >${DEST}/owl/${SCHEMA_NAME}.owl.ttl +endif +# non-empty arg triggers java +ifneq ($(strip ${GEN_JAVA_ARGS}),) + $(RUN) gen-java ${GEN_JAVA_ARGS} --output-directory ${DEST}/java/ $(SOURCE_SCHEMA_PATH) +endif +# non-empty arg triggers typescript +ifneq ($(strip ${GEN_TS_ARGS}),) + mkdir -p ${DEST}/typescript || true + $(RUN) gen-typescript ${GEN_TS_ARGS} $(SOURCE_SCHEMA_PATH) >${DEST}/typescript/${SCHEMA_NAME}.ts +endif + +test: test-schema test-python test-examples + +test-schema: + $(RUN) gen-project ${CONFIG_YAML} -d tmp $(SOURCE_SCHEMA_PATH) + +test-python: + $(RUN) python -m unittest discover + +lint: + $(RUN) linkml-lint $(SOURCE_SCHEMA_PATH) + +check-config: +ifndef LINKML_SCHEMA_NAME + $(error **Project not configured**:\n\n - See '.env.public'\n\n) +else + $(info Ok) +endif + +convert-examples-to-%: + $(patsubst %, $(RUN) linkml-convert % -s $(SOURCE_SCHEMA_PATH) -C Person, $(shell ${SHELL} find src/data/examples -name "*.yaml")) + +examples/%.yaml: src/data/examples/%.yaml + $(RUN) linkml-convert -s $(SOURCE_SCHEMA_PATH) -C Person $< -o $@ +examples/%.json: src/data/examples/%.yaml + $(RUN) linkml-convert -s $(SOURCE_SCHEMA_PATH) -C Person $< -o $@ +examples/%.ttl: src/data/examples/%.yaml + $(RUN) linkml-convert -P EXAMPLE=http://example.org/ -s $(SOURCE_SCHEMA_PATH) -C Person $< -o $@ + +test-examples: examples/output + +examples/output: src/thing_description_schema/schema/thing_description_schema.yaml + mkdir -p $@ + $(RUN) linkml-run-examples \ + --output-formats json \ + --output-formats yaml \ + --counter-example-input-directory src/data/examples/invalid \ + --input-directory src/data/examples/valid \ + --output-directory $@ \ + --schema $< > $@/README.md + +# Test documentation locally +serve: mkd-serve + +# Python datamodel +$(PYMODEL): + mkdir -p $@ + + +$(DOCDIR): + mkdir -p $@ + +gendoc: $(DOCDIR) + cp -rf $(SRC)/docs/* $(DOCDIR) ; \ + $(RUN) gen-doc ${GEN_DOC_ARGS} -d $(DOCDIR) $(SOURCE_SCHEMA_PATH) + +testdoc: gendoc serve + +MKDOCS = $(RUN) mkdocs +mkd-%: + $(MKDOCS) $* + +git-init-add: git-init git-add git-commit git-status +git-init: + git init +git-add: .cruft.json + git add . +git-commit: + git commit -m 'chore: make setup was run' -a +git-status: + git status + +# only necessary if setting up via cookiecutter +.cruft.json: + echo "creating a stub for .cruft.json. IMPORTANT: setup via cruft not cookiecutter recommended!" ; \ + touch $@ + +clean: + rm -rf $(DEST) + rm -rf tmp + rm -fr docs/* + rm -fr $(PYMODEL)/* + +include project.Makefile diff --git a/README.md b/README.md index 7013afd..05a8f01 100644 --- a/README.md +++ b/README.md @@ -1 +1,28 @@ +<<<<<<< HEAD # Temporary WoT TD Tooling + +The thing-description-schema is a LinkML-based schema for modelling the [Web of Things Thing Description](https://www.w3.org/TR/wot-thing-description11/) information model. +The aim is simplify the current WoT specification generation process. +For more information please refer to the [WoT github repo](https://github.com/w3c/wot-thing-description/tree/main/toolchain). + +## Repository Structure + +* [examples/](examples/) - example data +* [project/](project/) - project files +* [src/](src/) - source files + * [thing_description_schema](src/thing_description_schema) + * [schema](src/thing_description_schema/schema) -- LinkML schema + * [datamodel](src/thing_description_schema/datamodel) -- generated + Python datamodel +* [tests/](tests/) - Python tests + +## Developer Documentation + +
+Use the `make` command to generate project artefacts: + +* `make test`: validate the LinkML schema on test instances +* `make all`: make everything +* `make deploy`: deploys site +
+>>>>>>> thing-description-schema/master diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..56da66d --- /dev/null +++ b/config.yaml @@ -0,0 +1,41 @@ +# Configuration of generators (defaults illustrated) +--- +generator_args: + excel: + mergeimports: true + owl: + mergeimports: true + metaclasses: true + type_objects: true + # throws 'Cannot handle metadata profile: rdfs' + # metadata_profile: rdfs + markdown: + mergeimports: true + graphql: + mergeimports: true + java: + mergeimports: true + metadata: true + jsonld: + mergeimports: true + jsonschema: + mergeimports: true + jsonldcontext: + mergeimports: true + python: + mergeimports: true + prefixmap: + mergeimports: true + proto: + mergeimports: true + shacl: + mergeimports: true + shex: + mergeimports: true + sqlddl: + mergeimports: true + typescript: + mergeimports: true + metadata: true + +... diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..e474ec4 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,5 @@ +# Examples of use of thing_description_schema + +This folder contains example data conforming to thing_description_schema + +The source for these is in [src/data](../src/data/examples) diff --git a/examples/Thing-001.yaml b/examples/Thing-001.yaml new file mode 100644 index 0000000..e904b23 --- /dev/null +++ b/examples/Thing-001.yaml @@ -0,0 +1,7 @@ +# Example data object +--- +entries: + - id: example:Thing001 + name: foo bar + primary_email: foo.bar@example.com + age_in_years: 33 diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..ce285e8 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,25 @@ +--- +site_name: "thing-description-schema" +theme: + name: material + # palette: + # scheme: slate + # primary: cyan + features: + - content.tabs.link +plugins: + - search + - mermaid2 +nav: + # - Home: home.md + - Index: index.md + - About: about.md +site_url: https://mahdanoura.github.io/thing-description-schema +repo_url: https://github.com/mahdanoura/thing-description-schema + +# Uncomment this block to enable use of Google Analytics. +# Replace the property value with your own ID. +# extra: +# analytics: +# provider: google +# property: G-XXXXXXXXXX diff --git a/project.Makefile b/project.Makefile new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/project.Makefile @@ -0,0 +1 @@ + diff --git a/project/excel/thing_description_schema.xlsx b/project/excel/thing_description_schema.xlsx new file mode 100644 index 0000000..315b7f1 Binary files /dev/null and b/project/excel/thing_description_schema.xlsx differ diff --git a/project/graphql/thing_description_schema.graphql b/project/graphql/thing_description_schema.graphql new file mode 100644 index 0000000..b189f33 --- /dev/null +++ b/project/graphql/thing_description_schema.graphql @@ -0,0 +1,23 @@ +type NamedThing + { + id: Uriorcurie! + name: String + description: String + } + +type Thing + { + id: Uriorcurie! + name: String + description: String + primaryEmail: String + birthDate: Date + ageInYears: Integer + vitalStatus: PersonStatus + } + +type ThingCollection + { + entries: [Thing] + } + diff --git a/project/jsonld/thing_description_schema.context.jsonld b/project/jsonld/thing_description_schema.context.jsonld new file mode 100644 index 0000000..a9b4eaf --- /dev/null +++ b/project/jsonld/thing_description_schema.context.jsonld @@ -0,0 +1,60 @@ +{ + "comments": { + "description": "Auto generated by LinkML jsonld context generator", + "generation_date": "2024-04-15T09:31:15", + "source": "thing_description_schema.yaml" + }, + "@context": { + "PATO": { + "@id": "http://purl.obolibrary.org/obo/PATO_", + "@prefix": true + }, + "biolink": "https://w3id.org/biolink/", + "example": "https://example.org/", + "linkml": "https://w3id.org/linkml/", + "schema": "http://schema.org/", + "skos": "http://www.w3.org/2004/02/skos/core#", + "thing_description_schema": "https://w3id.org/mahda/thing-description-schema/", + "@vocab": "https://w3id.org/mahda/thing-description-schema/", + "age_in_years": { + "@type": "xsd:integer", + "@id": "age_in_years" + }, + "birth_date": { + "@type": "xsd:date", + "@id": "schema:birthDate" + }, + "description": { + "@id": "schema:description" + }, + "id": "@id", + "name": { + "@id": "schema:name" + }, + "primary_email": { + "@id": "schema:email" + }, + "entries": { + "@type": "@id", + "@id": "entries" + }, + "vital_status": { + "@context": { + "@vocab": "@null", + "text": "skos:notation", + "description": "skos:prefLabel", + "meaning": "@id" + }, + "@id": "vital_status" + }, + "NamedThing": { + "@id": "schema:Thing" + }, + "Thing": { + "@id": "Thing" + }, + "ThingCollection": { + "@id": "ThingCollection" + } + } +} diff --git a/project/jsonld/thing_description_schema.jsonld b/project/jsonld/thing_description_schema.jsonld new file mode 100644 index 0000000..079c4d9 --- /dev/null +++ b/project/jsonld/thing_description_schema.jsonld @@ -0,0 +1,596 @@ +{ + "name": "thing-description-schema", + "description": "Thing Description Standard", + "title": "thing-description-schema", + "see_also": [ + "https://mahda.github.io/thing-description-schema" + ], + "id": "https://w3id.org/mahda/thing-description-schema", + "imports": [ + "linkml:types" + ], + "license": "MIT", + "prefixes": [ + { + "prefix_prefix": "thing_description_schema", + "prefix_reference": "https://w3id.org/mahda/thing-description-schema/" + }, + { + "prefix_prefix": "linkml", + "prefix_reference": "https://w3id.org/linkml/" + }, + { + "prefix_prefix": "biolink", + "prefix_reference": "https://w3id.org/biolink/" + }, + { + "prefix_prefix": "schema", + "prefix_reference": "http://schema.org/" + }, + { + "prefix_prefix": "PATO", + "prefix_reference": "http://purl.obolibrary.org/obo/PATO_" + }, + { + "prefix_prefix": "example", + "prefix_reference": "https://example.org/" + } + ], + "default_prefix": "thing_description_schema", + "default_range": "string", + "types": [ + { + "name": "string", + "definition_uri": "https://w3id.org/linkml/String", + "description": "A character string", + "notes": [ + "In RDF serializations, a slot with range of string is treated as a literal or type xsd:string. If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"string\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "exact_mappings": [ + "schema:Text" + ], + "base": "str", + "uri": "http://www.w3.org/2001/XMLSchema#string", + "@type": "TypeDefinition" + }, + { + "name": "integer", + "definition_uri": "https://w3id.org/linkml/Integer", + "description": "An integer", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"integer\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "exact_mappings": [ + "schema:Integer" + ], + "base": "int", + "uri": "http://www.w3.org/2001/XMLSchema#integer", + "@type": "TypeDefinition" + }, + { + "name": "boolean", + "definition_uri": "https://w3id.org/linkml/Boolean", + "description": "A binary (true or false) value", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"boolean\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "exact_mappings": [ + "schema:Boolean" + ], + "base": "Bool", + "uri": "http://www.w3.org/2001/XMLSchema#boolean", + "repr": "bool", + "@type": "TypeDefinition" + }, + { + "name": "float", + "definition_uri": "https://w3id.org/linkml/Float", + "description": "A real number that conforms to the xsd:float specification", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"float\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "exact_mappings": [ + "schema:Float" + ], + "base": "float", + "uri": "http://www.w3.org/2001/XMLSchema#float", + "@type": "TypeDefinition" + }, + { + "name": "double", + "definition_uri": "https://w3id.org/linkml/Double", + "description": "A real number that conforms to the xsd:double specification", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"double\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "close_mappings": [ + "schema:Float" + ], + "base": "float", + "uri": "http://www.w3.org/2001/XMLSchema#double", + "@type": "TypeDefinition" + }, + { + "name": "decimal", + "definition_uri": "https://w3id.org/linkml/Decimal", + "description": "A real number with arbitrary precision that conforms to the xsd:decimal specification", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"decimal\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "broad_mappings": [ + "schema:Number" + ], + "base": "Decimal", + "uri": "http://www.w3.org/2001/XMLSchema#decimal", + "@type": "TypeDefinition" + }, + { + "name": "time", + "definition_uri": "https://w3id.org/linkml/Time", + "description": "A time object represents a (local) time of day, independent of any particular day", + "notes": [ + "URI is dateTime because OWL reasoners do not work with straight date or time", + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"time\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "exact_mappings": [ + "schema:Time" + ], + "base": "XSDTime", + "uri": "http://www.w3.org/2001/XMLSchema#time", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "date", + "definition_uri": "https://w3id.org/linkml/Date", + "description": "a date (year, month and day) in an idealized calendar", + "notes": [ + "URI is dateTime because OWL reasoners don't work with straight date or time", + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"date\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "exact_mappings": [ + "schema:Date" + ], + "base": "XSDDate", + "uri": "http://www.w3.org/2001/XMLSchema#date", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "datetime", + "definition_uri": "https://w3id.org/linkml/Datetime", + "description": "The combination of a date and time", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"datetime\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "exact_mappings": [ + "schema:DateTime" + ], + "base": "XSDDateTime", + "uri": "http://www.w3.org/2001/XMLSchema#dateTime", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "date_or_datetime", + "definition_uri": "https://w3id.org/linkml/DateOrDatetime", + "description": "Either a date or a datetime", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"date_or_datetime\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "base": "str", + "uri": "https://w3id.org/linkml/DateOrDatetime", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "uriorcurie", + "definition_uri": "https://w3id.org/linkml/Uriorcurie", + "description": "a URI or a CURIE", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"uriorcurie\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "base": "URIorCURIE", + "uri": "http://www.w3.org/2001/XMLSchema#anyURI", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "curie", + "definition_uri": "https://w3id.org/linkml/Curie", + "conforms_to": "https://www.w3.org/TR/curie/", + "description": "a compact URI", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"curie\"." + ], + "comments": [ + "in RDF serializations this MUST be expanded to a URI", + "in non-RDF serializations MAY be serialized as the compact representation" + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "base": "Curie", + "uri": "http://www.w3.org/2001/XMLSchema#string", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "uri", + "definition_uri": "https://w3id.org/linkml/Uri", + "conforms_to": "https://www.ietf.org/rfc/rfc3987.txt", + "description": "a complete URI", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"uri\"." + ], + "comments": [ + "in RDF serializations a slot with range of uri is treated as a literal or type xsd:anyURI unless it is an identifier or a reference to an identifier, in which case it is translated directly to a node" + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "close_mappings": [ + "schema:URL" + ], + "base": "URI", + "uri": "http://www.w3.org/2001/XMLSchema#anyURI", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "ncname", + "definition_uri": "https://w3id.org/linkml/Ncname", + "description": "Prefix part of CURIE", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"ncname\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "base": "NCName", + "uri": "http://www.w3.org/2001/XMLSchema#string", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "objectidentifier", + "definition_uri": "https://w3id.org/linkml/Objectidentifier", + "description": "A URI or CURIE that represents an object in the model.", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"objectidentifier\"." + ], + "comments": [ + "Used for inheritance and type checking" + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "base": "ElementIdentifier", + "uri": "http://www.w3.org/ns/shex#iri", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "nodeidentifier", + "definition_uri": "https://w3id.org/linkml/Nodeidentifier", + "description": "A URI, CURIE or BNODE that represents a node in a model.", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"nodeidentifier\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "base": "NodeIdentifier", + "uri": "http://www.w3.org/ns/shex#nonLiteral", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "jsonpointer", + "definition_uri": "https://w3id.org/linkml/Jsonpointer", + "conforms_to": "https://datatracker.ietf.org/doc/html/rfc6901", + "description": "A string encoding a JSON Pointer. The value of the string MUST conform to JSON Point syntax and SHOULD dereference to a valid object within the current instance document when encoded in tree form.", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"jsonpointer\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "base": "str", + "uri": "http://www.w3.org/2001/XMLSchema#string", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "jsonpath", + "definition_uri": "https://w3id.org/linkml/Jsonpath", + "conforms_to": "https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html", + "description": "A string encoding a JSON Path. The value of the string MUST conform to JSON Point syntax and SHOULD dereference to zero or more valid objects within the current instance document when encoded in tree form.", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"jsonpath\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "base": "str", + "uri": "http://www.w3.org/2001/XMLSchema#string", + "repr": "str", + "@type": "TypeDefinition" + }, + { + "name": "sparqlpath", + "definition_uri": "https://w3id.org/linkml/Sparqlpath", + "conforms_to": "https://www.w3.org/TR/sparql11-query/#propertypaths", + "description": "A string encoding a SPARQL Property Path. The value of the string MUST conform to SPARQL syntax and SHOULD dereference to zero or more valid objects within the current instance document when encoded as RDF.", + "notes": [ + "If you are authoring schemas in LinkML YAML, the type is referenced with the lower case \"sparqlpath\"." + ], + "from_schema": "https://w3id.org/linkml/types", + "imported_from": "linkml:types", + "base": "str", + "uri": "http://www.w3.org/2001/XMLSchema#string", + "repr": "str", + "@type": "TypeDefinition" + } + ], + "enums": [ + { + "name": "PersonStatus", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/PersonStatus", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "permissible_values": [ + { + "text": "ALIVE", + "description": "the person is living", + "meaning": "PATO:0001421" + }, + { + "text": "DEAD", + "description": "the person is deceased", + "meaning": "PATO:0001422" + }, + { + "text": "UNKNOWN", + "description": "the vital status is not known", + "todos": [ + "map this to an ontology" + ] + } + ] + } + ], + "slots": [ + { + "name": "id", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/id", + "description": "A unique identifier for a thing", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "mappings": [ + "http://schema.org/identifier" + ], + "slot_uri": "http://schema.org/identifier", + "identifier": true, + "owner": "NamedThing", + "domain_of": [ + "NamedThing" + ], + "range": "uriorcurie", + "required": true, + "@type": "SlotDefinition" + }, + { + "name": "name", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/name", + "description": "A human-readable name for a thing", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "mappings": [ + "http://schema.org/name" + ], + "slot_uri": "http://schema.org/name", + "owner": "NamedThing", + "domain_of": [ + "NamedThing" + ], + "range": "string", + "@type": "SlotDefinition" + }, + { + "name": "description", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/description", + "description": "A human-readable description for a thing", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "mappings": [ + "http://schema.org/description" + ], + "slot_uri": "http://schema.org/description", + "owner": "NamedThing", + "domain_of": [ + "NamedThing" + ], + "range": "string", + "@type": "SlotDefinition" + }, + { + "name": "primary_email", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/primary_email", + "description": "The main email address of a person", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "mappings": [ + "http://schema.org/email" + ], + "slot_uri": "http://schema.org/email", + "owner": "Thing", + "domain_of": [ + "Thing" + ], + "range": "string", + "@type": "SlotDefinition" + }, + { + "name": "birth_date", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/birth_date", + "description": "Date on which a person is born", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "mappings": [ + "http://schema.org/birthDate" + ], + "slot_uri": "http://schema.org/birthDate", + "owner": "Thing", + "domain_of": [ + "Thing" + ], + "range": "date", + "@type": "SlotDefinition" + }, + { + "name": "age_in_years", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/age_in_years", + "description": "Number of years since birth", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "slot_uri": "https://w3id.org/mahda/thing-description-schema/age_in_years", + "owner": "Thing", + "domain_of": [ + "Thing" + ], + "range": "integer", + "@type": "SlotDefinition" + }, + { + "name": "vital_status", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/vital_status", + "description": "living or dead status", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "slot_uri": "https://w3id.org/mahda/thing-description-schema/vital_status", + "owner": "Thing", + "domain_of": [ + "Thing" + ], + "range": "PersonStatus", + "@type": "SlotDefinition" + }, + { + "name": "thingCollection__entries", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "slot_uri": "https://w3id.org/mahda/thing-description-schema/entries", + "multivalued": true, + "alias": "entries", + "owner": "ThingCollection", + "domain_of": [ + "ThingCollection" + ], + "range": "Thing", + "inlined": true, + "@type": "SlotDefinition" + }, + { + "name": "Thing_primary_email", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/primary_email", + "description": "The main email address of a person", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "mappings": [ + "http://schema.org/email" + ], + "is_a": "primary_email", + "domain": "Thing", + "slot_uri": "http://schema.org/email", + "alias": "primary_email", + "owner": "Thing", + "domain_of": [ + "Thing" + ], + "is_usage_slot": true, + "usage_slot_name": "primary_email", + "range": "string", + "pattern": "^\\S+@[\\S+\\.]+\\S+", + "@type": "SlotDefinition" + } + ], + "classes": [ + { + "name": "NamedThing", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/NamedThing", + "description": "A generic grouping for any identifiable entity", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "mappings": [ + "schema:Thing" + ], + "slots": [ + "id", + "name", + "description" + ], + "slot_usage": {}, + "class_uri": "http://schema.org/Thing", + "@type": "ClassDefinition" + }, + { + "name": "Thing", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/Thing", + "description": "Represents a Thing", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "is_a": "NamedThing", + "slots": [ + "id", + "name", + "description", + "Thing_primary_email", + "birth_date", + "age_in_years", + "vital_status" + ], + "slot_usage": {}, + "class_uri": "https://w3id.org/mahda/thing-description-schema/Thing", + "@type": "ClassDefinition" + }, + { + "name": "ThingCollection", + "definition_uri": "https://w3id.org/mahda/thing-description-schema/ThingCollection", + "description": "A holder for Thing objects", + "from_schema": "https://w3id.org/mahda/thing-description-schema", + "slots": [ + "thingCollection__entries" + ], + "slot_usage": {}, + "attributes": [ + { + "name": "entries", + "multivalued": true, + "range": "Thing", + "inlined": true, + "@type": "SlotDefinition" + } + ], + "class_uri": "https://w3id.org/mahda/thing-description-schema/ThingCollection", + "tree_root": true, + "@type": "ClassDefinition" + } + ], + "metamodel_version": "1.7.0", + "source_file": "thing_description_schema.yaml", + "source_file_date": "2024-04-15T09:28:11", + "source_file_size": 2246, + "generation_date": "2024-04-15T09:31:15", + "@type": "SchemaDefinition", + "@context": [ + "project/jsonld/thing_description_schema.context.jsonld", + "https://w3id.org/linkml/types.context.jsonld", + { + "@base": "https://w3id.org/mahda/thing-description-schema/" + } + ] +} diff --git a/project/jsonschema/thing_description_schema.schema.json b/project/jsonschema/thing_description_schema.schema.json new file mode 100644 index 0000000..433bcff --- /dev/null +++ b/project/jsonschema/thing_description_schema.schema.json @@ -0,0 +1,161 @@ +{ + "$defs": { + "NamedThing": { + "additionalProperties": false, + "description": "A generic grouping for any identifiable entity", + "properties": { + "description": { + "description": "A human-readable description for a thing", + "type": "string" + }, + "id": { + "description": "A unique identifier for a thing", + "type": "string" + }, + "name": { + "description": "A human-readable name for a thing", + "type": "string" + } + }, + "required": [ + "id" + ], + "title": "NamedThing", + "type": "object" + }, + "PersonStatus": { + "description": "", + "enum": [ + "ALIVE", + "DEAD", + "UNKNOWN" + ], + "title": "PersonStatus", + "type": "string" + }, + "Thing": { + "additionalProperties": false, + "description": "Represents a Thing", + "properties": { + "age_in_years": { + "description": "Number of years since birth", + "type": "integer" + }, + "birth_date": { + "description": "Date on which a person is born", + "format": "date", + "type": "string" + }, + "description": { + "description": "A human-readable description for a thing", + "type": "string" + }, + "id": { + "description": "A unique identifier for a thing", + "type": "string" + }, + "name": { + "description": "A human-readable name for a thing", + "type": "string" + }, + "primary_email": { + "description": "The main email address of a person", + "pattern": "^\\S+@[\\S+\\.]+\\S+", + "type": "string" + }, + "vital_status": { + "$ref": "#/$defs/PersonStatus", + "description": "living or dead status" + } + }, + "required": [ + "id" + ], + "title": "Thing", + "type": "object" + }, + "ThingCollection": { + "additionalProperties": false, + "description": "A holder for Thing objects", + "properties": { + "entries": { + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/$defs/Thing__identifier_optional" + }, + { + "type": "null" + } + ] + }, + "type": "object" + } + }, + "title": "ThingCollection", + "type": "object" + }, + "Thing__identifier_optional": { + "additionalProperties": false, + "description": "Represents a Thing", + "properties": { + "age_in_years": { + "description": "Number of years since birth", + "type": "integer" + }, + "birth_date": { + "description": "Date on which a person is born", + "format": "date", + "type": "string" + }, + "description": { + "description": "A human-readable description for a thing", + "type": "string" + }, + "id": { + "description": "A unique identifier for a thing", + "type": "string" + }, + "name": { + "description": "A human-readable name for a thing", + "type": "string" + }, + "primary_email": { + "description": "The main email address of a person", + "pattern": "^\\S+@[\\S+\\.]+\\S+", + "type": "string" + }, + "vital_status": { + "$ref": "#/$defs/PersonStatus", + "description": "living or dead status" + } + }, + "required": [], + "title": "Thing", + "type": "object" + } + }, + "$id": "https://w3id.org/mahda/thing-description-schema", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "additionalProperties": true, + "description": "A holder for Thing objects", + "metamodel_version": "1.7.0", + "properties": { + "entries": { + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/$defs/Thing__identifier_optional" + }, + { + "type": "null" + } + ] + }, + "type": "object" + } + }, + "title": "thing-description-schema", + "type": "object", + "version": null +} \ No newline at end of file diff --git a/project/owl/thing_description_schema.owl.ttl b/project/owl/thing_description_schema.owl.ttl new file mode 100644 index 0000000..168401c --- /dev/null +++ b/project/owl/thing_description_schema.owl.ttl @@ -0,0 +1,182 @@ +@prefix PATO: . +@prefix dcterms: . +@prefix linkml: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix schema1: . +@prefix skos: . +@prefix thing_description_schema: . +@prefix xsd: . + +thing_description_schema:ThingCollection a owl:Class, + linkml:ClassDefinition ; + rdfs:label "ThingCollection" ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty thing_description_schema:entries ], + [ a owl:Restriction ; + owl:allValuesFrom thing_description_schema:Thing ; + owl:onProperty thing_description_schema:entries ] ; + skos:definition "A holder for Thing objects" ; + skos:inScheme . + +thing_description_schema:NamedThing a owl:Class, + linkml:ClassDefinition ; + rdfs:label "NamedThing" ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 1 ; + owl:onProperty thing_description_schema:id ], + [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty thing_description_schema:description ], + [ a owl:Restriction ; + owl:allValuesFrom linkml:String ; + owl:onProperty thing_description_schema:description ], + [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty thing_description_schema:name ], + [ a owl:Restriction ; + owl:allValuesFrom linkml:Uriorcurie ; + owl:onProperty thing_description_schema:id ], + [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty thing_description_schema:id ], + [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty thing_description_schema:name ], + [ a owl:Restriction ; + owl:allValuesFrom linkml:String ; + owl:onProperty thing_description_schema:name ], + [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty thing_description_schema:description ] ; + skos:definition "A generic grouping for any identifiable entity" ; + skos:exactMatch schema1:Thing ; + skos:inScheme . + +thing_description_schema:Thing a owl:Class, + linkml:ClassDefinition ; + rdfs:label "Thing" ; + rdfs:subClassOf [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty thing_description_schema:birth_date ], + [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty thing_description_schema:vital_status ], + [ a owl:Restriction ; + owl:allValuesFrom [ a rdfs:Datatype ; + owl:onDatatype xsd:string ; + owl:withRestrictions ( [ xsd:pattern "^\\S+@[\\S+\\.]+\\S+" ] ) ] ; + owl:onProperty thing_description_schema:primary_email ], + [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty thing_description_schema:age_in_years ], + [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty thing_description_schema:primary_email ], + [ a owl:Restriction ; + owl:maxCardinality 1 ; + owl:onProperty thing_description_schema:birth_date ], + [ a owl:Restriction ; + owl:allValuesFrom linkml:Date ; + owl:onProperty thing_description_schema:birth_date ], + [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty thing_description_schema:age_in_years ], + [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty thing_description_schema:primary_email ], + [ a owl:Restriction ; + owl:allValuesFrom linkml:Integer ; + owl:onProperty thing_description_schema:age_in_years ], + [ a owl:Restriction ; + owl:minCardinality 0 ; + owl:onProperty thing_description_schema:vital_status ], + [ a owl:Restriction ; + owl:allValuesFrom thing_description_schema:PersonStatus ; + owl:onProperty thing_description_schema:vital_status ], + thing_description_schema:NamedThing ; + skos:definition "Represents a Thing" ; + skos:inScheme . + +PATO:0001421 a owl:Class, + thing_description_schema:PersonStatus ; + rdfs:label "ALIVE" ; + rdfs:subClassOf thing_description_schema:PersonStatus . + +PATO:0001422 a owl:Class, + thing_description_schema:PersonStatus ; + rdfs:label "DEAD" ; + rdfs:subClassOf thing_description_schema:PersonStatus . + + a owl:Class, + thing_description_schema:PersonStatus ; + rdfs:label "UNKNOWN" ; + rdfs:subClassOf thing_description_schema:PersonStatus . + +thing_description_schema:entries a owl:ObjectProperty, + linkml:SlotDefinition ; + rdfs:label "entries" ; + skos:inScheme . + +thing_description_schema:age_in_years a owl:ObjectProperty, + linkml:SlotDefinition ; + rdfs:label "age_in_years" ; + rdfs:range linkml:Integer ; + skos:definition "Number of years since birth" ; + skos:inScheme . + +thing_description_schema:birth_date a owl:ObjectProperty, + linkml:SlotDefinition ; + rdfs:label "birth_date" ; + rdfs:range linkml:Date ; + skos:definition "Date on which a person is born" ; + skos:inScheme . + +thing_description_schema:description a owl:ObjectProperty, + linkml:SlotDefinition ; + rdfs:label "description" ; + skos:definition "A human-readable description for a thing" ; + skos:inScheme . + +thing_description_schema:id a owl:ObjectProperty, + linkml:SlotDefinition ; + rdfs:label "id" ; + rdfs:range linkml:Uriorcurie ; + skos:definition "A unique identifier for a thing" ; + skos:inScheme . + +thing_description_schema:name a owl:ObjectProperty, + linkml:SlotDefinition ; + rdfs:label "name" ; + skos:definition "A human-readable name for a thing" ; + skos:inScheme . + +thing_description_schema:primary_email a owl:ObjectProperty, + linkml:SlotDefinition ; + rdfs:label "primary_email" ; + skos:definition "The main email address of a person" ; + skos:inScheme . + +thing_description_schema:vital_status a owl:ObjectProperty, + linkml:SlotDefinition ; + rdfs:label "vital_status" ; + rdfs:range thing_description_schema:PersonStatus ; + skos:definition "living or dead status" ; + skos:inScheme . + +thing_description_schema:PersonStatus a owl:Class, + linkml:EnumDefinition ; + owl:unionOf ( PATO:0001421 PATO:0001422 ) ; + linkml:permissible_values PATO:0001421, + PATO:0001422, + . + + a owl:Ontology ; + rdfs:label "thing-description-schema" ; + dcterms:license "MIT" ; + dcterms:title "thing-description-schema" ; + rdfs:seeAlso ; + skos:definition "Thing Description Standard" . + diff --git a/project/prefixmap/thing_description_schema.yaml b/project/prefixmap/thing_description_schema.yaml new file mode 100644 index 0000000..2441b0d --- /dev/null +++ b/project/prefixmap/thing_description_schema.yaml @@ -0,0 +1,11 @@ +{ + "PATO": "http://purl.obolibrary.org/obo/PATO_", + "biolink": "https://w3id.org/biolink/", + "example": "https://example.org/", + "linkml": "https://w3id.org/linkml/", + "schema": "http://schema.org/", + "thing_description_schema": "https://w3id.org/mahda/thing-description-schema/", + "NamedThing": { + "@id": "schema:Thing" + } +} diff --git a/project/protobuf/thing_description_schema.proto b/project/protobuf/thing_description_schema.proto new file mode 100644 index 0000000..33f6ae9 --- /dev/null +++ b/project/protobuf/thing_description_schema.proto @@ -0,0 +1,23 @@ +// A generic grouping for any identifiable entity +message NamedThing + { + uriorcurie id = 0 + string name = 0 + string description = 0 + } +// Represents a Thing +message Thing + { + uriorcurie id = 0 + string name = 0 + string description = 0 + string primaryEmail = 0 + date birthDate = 0 + integer ageInYears = 0 + personStatus vitalStatus = 0 + } +// A holder for Thing objects +message ThingCollection + { + repeated thing entries = 0 + } diff --git a/project/shacl/thing_description_schema.shacl.ttl b/project/shacl/thing_description_schema.shacl.ttl new file mode 100644 index 0000000..ba220e5 --- /dev/null +++ b/project/shacl/thing_description_schema.shacl.ttl @@ -0,0 +1,79 @@ +@prefix PATO: . +@prefix rdf: . +@prefix schema1: . +@prefix sh: . +@prefix thing_description_schema: . +@prefix xsd: . + +schema1:Thing a sh:NodeShape ; + sh:closed true ; + sh:description "A generic grouping for any identifiable entity" ; + sh:ignoredProperties ( rdf:type ) ; + sh:property [ sh:description "A unique identifier for a thing" ; + sh:maxCount 1 ; + sh:order 0 ; + sh:path schema1:identifier ], + [ sh:datatype xsd:string ; + sh:description "A human-readable description for a thing" ; + sh:maxCount 1 ; + sh:order 2 ; + sh:path schema1:description ], + [ sh:datatype xsd:string ; + sh:description "A human-readable name for a thing" ; + sh:maxCount 1 ; + sh:order 1 ; + sh:path schema1:name ] ; + sh:targetClass schema1:Thing . + +thing_description_schema:ThingCollection a sh:NodeShape ; + sh:closed true ; + sh:description "A holder for Thing objects" ; + sh:ignoredProperties ( rdf:type ) ; + sh:property [ sh:class thing_description_schema:Thing ; + sh:nodeKind sh:IRI ; + sh:order 0 ; + sh:path thing_description_schema:entries ] ; + sh:targetClass thing_description_schema:ThingCollection . + +thing_description_schema:Thing a sh:NodeShape ; + sh:closed true ; + sh:description "Represents a Thing" ; + sh:ignoredProperties ( rdf:type ) ; + sh:property [ sh:datatype xsd:string ; + sh:description "The main email address of a person" ; + sh:maxCount 1 ; + sh:order 0 ; + sh:path schema1:email ; + sh:pattern "^\\S+@[\\S+\\.]+\\S+" ], + [ sh:datatype xsd:date ; + sh:description "Date on which a person is born" ; + sh:maxCount 1 ; + sh:order 1 ; + sh:path schema1:birthDate ], + [ sh:datatype xsd:string ; + sh:description "A human-readable name for a thing" ; + sh:maxCount 1 ; + sh:order 5 ; + sh:path schema1:name ], + [ sh:description "A unique identifier for a thing" ; + sh:maxCount 1 ; + sh:minCount 1 ; + sh:order 4 ; + sh:path schema1:identifier ], + [ sh:datatype xsd:integer ; + sh:description "Number of years since birth" ; + sh:maxCount 1 ; + sh:order 2 ; + sh:path thing_description_schema:age_in_years ], + [ sh:description "living or dead status" ; + sh:in ( PATO:0001421 PATO:0001422 "UNKNOWN" ) ; + sh:maxCount 1 ; + sh:order 3 ; + sh:path thing_description_schema:vital_status ], + [ sh:datatype xsd:string ; + sh:description "A human-readable description for a thing" ; + sh:maxCount 1 ; + sh:order 6 ; + sh:path schema1:description ] ; + sh:targetClass thing_description_schema:Thing . + diff --git a/project/shex/thing_description_schema.shex b/project/shex/thing_description_schema.shex new file mode 100644 index 0000000..79020b7 --- /dev/null +++ b/project/shex/thing_description_schema.shex @@ -0,0 +1,75 @@ +BASE +PREFIX rdf: +PREFIX xsd: +PREFIX linkml: +PREFIX schema1: + + +linkml:String xsd:string + +linkml:Integer xsd:integer + +linkml:Boolean xsd:boolean + +linkml:Float xsd:float + +linkml:Double xsd:double + +linkml:Decimal xsd:decimal + +linkml:Time xsd:time + +linkml:Date xsd:date + +linkml:Datetime xsd:dateTime + +linkml:DateOrDatetime linkml:DateOrDatetime + +linkml:Uriorcurie IRI + +linkml:Curie xsd:string + +linkml:Uri IRI + +linkml:Ncname xsd:string + +linkml:Objectidentifier IRI + +linkml:Nodeidentifier NONLITERAL + +linkml:Jsonpointer xsd:string + +linkml:Jsonpath xsd:string + +linkml:Sparqlpath xsd:string + + ( + CLOSED { + ( $ ( schema1:name @linkml:String ? ; + schema1:description @linkml:String ? + ) ; + rdf:type [ schema1:Thing ] + ) + } OR @ +) + + CLOSED { + ( $ ( & ; + rdf:type [ schema1:Thing ] ? ; + schema1:email @linkml:String ? ; + schema1:birthDate @linkml:Date ? ; + @linkml:Integer ? ; + [ + ] ? + ) ; + rdf:type [ ] + ) +} + + CLOSED { + ( $ @ * ; + rdf:type [ ] ? + ) +} + + diff --git a/project/sqlschema/thing_description_schema.sql b/project/sqlschema/thing_description_schema.sql new file mode 100644 index 0000000..b16ae3c --- /dev/null +++ b/project/sqlschema/thing_description_schema.sql @@ -0,0 +1,38 @@ +-- # Class: "NamedThing" Description: "A generic grouping for any identifiable entity" +-- * Slot: id Description: A unique identifier for a thing +-- * Slot: name Description: A human-readable name for a thing +-- * Slot: description Description: A human-readable description for a thing +-- # Class: "Thing" Description: "Represents a Thing" +-- * Slot: primary_email Description: The main email address of a person +-- * Slot: birth_date Description: Date on which a person is born +-- * Slot: age_in_years Description: Number of years since birth +-- * Slot: vital_status Description: living or dead status +-- * Slot: id Description: A unique identifier for a thing +-- * Slot: name Description: A human-readable name for a thing +-- * Slot: description Description: A human-readable description for a thing +-- * Slot: ThingCollection_id Description: Autocreated FK slot +-- # Class: "ThingCollection" Description: "A holder for Thing objects" +-- * Slot: id Description: + +CREATE TABLE "NamedThing" ( + id TEXT NOT NULL, + name TEXT, + description TEXT, + PRIMARY KEY (id) +); +CREATE TABLE "ThingCollection" ( + id INTEGER NOT NULL, + PRIMARY KEY (id) +); +CREATE TABLE "Thing" ( + primary_email TEXT, + birth_date DATE, + age_in_years INTEGER, + vital_status VARCHAR(7), + id TEXT NOT NULL, + name TEXT, + description TEXT, + "ThingCollection_id" INTEGER, + PRIMARY KEY (id), + FOREIGN KEY("ThingCollection_id") REFERENCES "ThingCollection" (id) +); \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d03b8e5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,31 @@ +[tool.poetry] +name = "thing_description_schema" +version = "0.0.0.post1.dev0+dfeaf46" +description = "Thing Description Information Model as a LinkML schema" +authors = ["Mahda Noura "] +license = "MIT" +readme = "README.md" +include = ["README.md", "src/thing_description_schema/schema", "project"] + +[tool.poetry.dependencies] +python = "^3.11" +linkml-runtime = "^1.1.24" + +[tool.poetry-dynamic-versioning] +enable = false +vcs = "git" +style = "pep440" + +[tool.poetry.dev-dependencies] +linkml = "^1.3.5" +mkdocs-material = "^8.2.8" +mkdocs-mermaid2-plugin = "^0.6.0" +schemasheets = "^0.1.14" + +[build-system] +requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"] +build-backend = "poetry_dynamic_versioning.backend" + +[tool.poetry.extras] +docs = ["linkml", "mkdocs-material"] + diff --git a/src/data/examples/Thing-001.yaml b/src/data/examples/Thing-001.yaml new file mode 100644 index 0000000..e904b23 --- /dev/null +++ b/src/data/examples/Thing-001.yaml @@ -0,0 +1,7 @@ +# Example data object +--- +entries: + - id: example:Thing001 + name: foo bar + primary_email: foo.bar@example.com + age_in_years: 33 diff --git a/src/docs/about.md b/src/docs/about.md new file mode 100644 index 0000000..bd22788 --- /dev/null +++ b/src/docs/about.md @@ -0,0 +1,3 @@ +# thing-description-schema + +Thing Description Standard diff --git a/src/thing_description_schema/_version.py b/src/thing_description_schema/_version.py new file mode 100644 index 0000000..7edc9ee --- /dev/null +++ b/src/thing_description_schema/_version.py @@ -0,0 +1,7 @@ +from importlib.metadata import version, PackageNotFoundError + +try: + __version__ = version(__name__) +except PackageNotFoundError: + # package not installed + __version__ = "0.0.0" diff --git a/src/thing_description_schema/datamodel/__init__.py b/src/thing_description_schema/datamodel/__init__.py new file mode 100644 index 0000000..7f5ea55 --- /dev/null +++ b/src/thing_description_schema/datamodel/__init__.py @@ -0,0 +1 @@ +from .thing_description_schema import * diff --git a/src/thing_description_schema/datamodel/thing_description_schema.py b/src/thing_description_schema/datamodel/thing_description_schema.py new file mode 100644 index 0000000..7d199c7 --- /dev/null +++ b/src/thing_description_schema/datamodel/thing_description_schema.py @@ -0,0 +1,194 @@ +# Auto generated from thing_description_schema.yaml by pythongen.py version: 0.0.1 +# Generation date: 2024-04-15T09:31:15 +# Schema: thing-description-schema +# +# id: https://w3id.org/mahda/thing-description-schema +# description: Thing Description Standard +# license: MIT + +import dataclasses +import re +from jsonasobj2 import JsonObj, as_dict +from typing import Optional, List, Union, Dict, ClassVar, Any +from dataclasses import dataclass +from datetime import date, datetime +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, Integer, String, Uriorcurie +from linkml_runtime.utils.metamodelcore import URIorCURIE, 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 +PATO = CurieNamespace('PATO', 'http://purl.obolibrary.org/obo/PATO_') +BIOLINK = CurieNamespace('biolink', 'https://w3id.org/biolink/') +EXAMPLE = CurieNamespace('example', 'https://example.org/') +LINKML = CurieNamespace('linkml', 'https://w3id.org/linkml/') +SCHEMA = CurieNamespace('schema', 'http://schema.org/') +THING_DESCRIPTION_SCHEMA = CurieNamespace('thing_description_schema', 'https://w3id.org/mahda/thing-description-schema/') +DEFAULT_ = THING_DESCRIPTION_SCHEMA + + +# Types + +# Class references +class NamedThingId(URIorCURIE): + pass + + +class ThingId(NamedThingId): + pass + + +@dataclass +class NamedThing(YAMLRoot): + """ + A generic grouping for any identifiable entity + """ + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = SCHEMA["Thing"] + class_class_curie: ClassVar[str] = "schema:Thing" + class_name: ClassVar[str] = "NamedThing" + class_model_uri: ClassVar[URIRef] = THING_DESCRIPTION_SCHEMA.NamedThing + + id: Union[str, NamedThingId] = None + name: Optional[str] = None + description: Optional[str] = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.id): + self.MissingRequiredField("id") + if not isinstance(self.id, NamedThingId): + self.id = NamedThingId(self.id) + + if self.name is not None and not isinstance(self.name, str): + self.name = str(self.name) + + if self.description is not None and not isinstance(self.description, str): + self.description = str(self.description) + + super().__post_init__(**kwargs) + + +@dataclass +class Thing(NamedThing): + """ + Represents a Thing + """ + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = THING_DESCRIPTION_SCHEMA["Thing"] + class_class_curie: ClassVar[str] = "thing_description_schema:Thing" + class_name: ClassVar[str] = "Thing" + class_model_uri: ClassVar[URIRef] = THING_DESCRIPTION_SCHEMA.Thing + + id: Union[str, ThingId] = None + primary_email: Optional[str] = None + birth_date: Optional[Union[str, XSDDate]] = None + age_in_years: Optional[int] = None + vital_status: Optional[Union[str, "PersonStatus"]] = None + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + if self._is_empty(self.id): + self.MissingRequiredField("id") + if not isinstance(self.id, ThingId): + self.id = ThingId(self.id) + + if self.primary_email is not None and not isinstance(self.primary_email, str): + self.primary_email = str(self.primary_email) + + if self.birth_date is not None and not isinstance(self.birth_date, XSDDate): + self.birth_date = XSDDate(self.birth_date) + + if self.age_in_years is not None and not isinstance(self.age_in_years, int): + self.age_in_years = int(self.age_in_years) + + if self.vital_status is not None and not isinstance(self.vital_status, PersonStatus): + self.vital_status = PersonStatus(self.vital_status) + + super().__post_init__(**kwargs) + + +@dataclass +class ThingCollection(YAMLRoot): + """ + A holder for Thing objects + """ + _inherited_slots: ClassVar[List[str]] = [] + + class_class_uri: ClassVar[URIRef] = THING_DESCRIPTION_SCHEMA["ThingCollection"] + class_class_curie: ClassVar[str] = "thing_description_schema:ThingCollection" + class_name: ClassVar[str] = "ThingCollection" + class_model_uri: ClassVar[URIRef] = THING_DESCRIPTION_SCHEMA.ThingCollection + + entries: Optional[Union[Dict[Union[str, ThingId], Union[dict, Thing]], List[Union[dict, Thing]]]] = empty_dict() + + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + self._normalize_inlined_as_dict(slot_name="entries", slot_type=Thing, key_name="id", keyed=True) + + super().__post_init__(**kwargs) + + +# Enumerations +class PersonStatus(EnumDefinitionImpl): + + ALIVE = PermissibleValue( + text="ALIVE", + description="the person is living", + meaning=PATO["0001421"]) + DEAD = PermissibleValue( + text="DEAD", + description="the person is deceased", + meaning=PATO["0001422"]) + UNKNOWN = PermissibleValue( + text="UNKNOWN", + description="the vital status is not known") + + _defn = EnumDefinition( + name="PersonStatus", + ) + +# Slots +class slots: + pass + +slots.id = Slot(uri=SCHEMA.identifier, name="id", curie=SCHEMA.curie('identifier'), + model_uri=THING_DESCRIPTION_SCHEMA.id, domain=None, range=URIRef) + +slots.name = Slot(uri=SCHEMA.name, name="name", curie=SCHEMA.curie('name'), + model_uri=THING_DESCRIPTION_SCHEMA.name, domain=None, range=Optional[str]) + +slots.description = Slot(uri=SCHEMA.description, name="description", curie=SCHEMA.curie('description'), + model_uri=THING_DESCRIPTION_SCHEMA.description, domain=None, range=Optional[str]) + +slots.primary_email = Slot(uri=SCHEMA.email, name="primary_email", curie=SCHEMA.curie('email'), + model_uri=THING_DESCRIPTION_SCHEMA.primary_email, domain=None, range=Optional[str]) + +slots.birth_date = Slot(uri=SCHEMA.birthDate, name="birth_date", curie=SCHEMA.curie('birthDate'), + model_uri=THING_DESCRIPTION_SCHEMA.birth_date, domain=None, range=Optional[Union[str, XSDDate]]) + +slots.age_in_years = Slot(uri=THING_DESCRIPTION_SCHEMA.age_in_years, name="age_in_years", curie=THING_DESCRIPTION_SCHEMA.curie('age_in_years'), + model_uri=THING_DESCRIPTION_SCHEMA.age_in_years, domain=None, range=Optional[int]) + +slots.vital_status = Slot(uri=THING_DESCRIPTION_SCHEMA.vital_status, name="vital_status", curie=THING_DESCRIPTION_SCHEMA.curie('vital_status'), + model_uri=THING_DESCRIPTION_SCHEMA.vital_status, domain=None, range=Optional[Union[str, "PersonStatus"]]) + +slots.thingCollection__entries = Slot(uri=THING_DESCRIPTION_SCHEMA.entries, name="thingCollection__entries", curie=THING_DESCRIPTION_SCHEMA.curie('entries'), + model_uri=THING_DESCRIPTION_SCHEMA.thingCollection__entries, domain=None, range=Optional[Union[Dict[Union[str, ThingId], Union[dict, Thing]], List[Union[dict, Thing]]]]) + +slots.Thing_primary_email = Slot(uri=SCHEMA.email, name="Thing_primary_email", curie=SCHEMA.curie('email'), + model_uri=THING_DESCRIPTION_SCHEMA.Thing_primary_email, domain=Thing, range=Optional[str], + pattern=re.compile(r'^\S+@[\S+\.]+\S+')) \ No newline at end of file diff --git a/src/thing_description_schema/schema/thing_description_schema.yaml b/src/thing_description_schema/schema/thing_description_schema.yaml new file mode 100644 index 0000000..6fd74ba --- /dev/null +++ b/src/thing_description_schema/schema/thing_description_schema.yaml @@ -0,0 +1,577 @@ +--- +id: td +name: thing-description-schema +title: thing-description-schema +description: |- + LinkML schema for modelling the Web of Things Thing Description information model. This schema is used to generate + JSON Schema, SHACL shapes, and RDF. +license: MIT +see_also: + - https://www.w3.org/TR/wot-thing-description11/ + +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml/ + td: + prefix_prefix: td + prefix_reference: https://www.w3.org/2019/wot/td# + jsonschema: + prefix_prefix: jsonschema + prefix_reference: https://www.w3.org/2019/wot/json-schema# + wotsec: + prefix_prefix: wotsec + prefix_reference: https://www.w3.org/2019/wot/security# + hctl: + prefix_prefix: hctl + prefix_reference: https://www.w3.org/2019/wot/hypermedia# + rdfs: + prefix_prefix: rdfs + prefix_reference: http://www.w3.org/2000/01/rdf-schema# + rdf: + prefix_prefix: rdf + prefix_reference: http://www.w3.org/1999/02/22-rdf-syntax-ns# + dcterms: + prefix_prefix: dcterms + prefix_reference: http://purl.org/dc/terms/ + schema: + prefix_prefix: schema + prefix_reference: http://schema.org/ + xsd: + prefix_prefix: xsd + prefix_reference: http://www.w3.org/2001/XMLSchema# + dct: + prefix_prefix: dct + prefix_reference: http://purl.org/dc/terms/ + htv: + prefix_prefix: htv + prefix_reference: http://www.w3.org/2011/http# + tm: + prefix_prefix: tm + prefix_reference: https://www.w3.org/2019/wot/tm# +default_prefix: td +default_range: string + +imports: + - linkml:types + +types: + anyUri: + name: anyUri + description: a complete URI + uri: xsd:anyURI + base: URI + +slots: + id: + identifier: true + description: TODO + range: anyUri + slot_uri: td:id + title: + description: >- + Provides a human-readable title (e.g., display a text for UI representation) based on a default language. + range: MultiLanguage + required: + slot_uri: td:title + description: + range: MultiLanguage + titles: + multivalued: true + range: MultiLanguage + inlined: true + descriptions: + description: >- + TODO, check, according to the description a description should not contain a lang tag. + multivalued: true + range: MultiLanguage + inlined: true + descriptionInLanguage: + description: >- + description of the TD element (Thing, interaction affordance, security scheme or data scheme) with language tag. By + convention, a language tag must be added to the object of descriptionInLanguage. Otherwise use description. + range: MultiLanguage + titleInLanguage: + description: >- + title of the TD element (Thing, interaction affordance, security scheme or data scheme) with language tag. By + convention, a language tag must be added to the object of descriptionInLanguage. Otherwise use description. + range: MultiLanguage + "@type": + multivalued: true + target: + description: >- + Target IRI of a link or submission target of a Form + slot_uri: hctl:target + required: true + range: anyUri + +classes: + + VersionInfo: + class_uri: schema:version + description: >- + Provides version information. + attributes: + instance: + required: true + model: + + MultiLanguage: + attributes: + key: + identifier: true + pattern: "^(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)|((en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)))$" + + Link: + class_uri: hctl:Link + description: >- + A link can be viewed as a statement of the form link context that has a relation type resource at link target", where the optional target attributes may further describe the resource. + attributes: + hintsAtMediaType: + description: Target attribute providing a hint indicating what the media type [IANA-MEDIA-TYPES] of the result of dereferencing the link should be. + from_schema: hctl + type: + relation: + description: >- + A link relation type identifies the semantics of a link. + from_schema: hctl + anchor: + description: >- + By default, the context, or anchor, of a link conveyed in the Link header field is the URL of the representation it is associated with, as defined in RFC7231, Section 3.1.4.1, and is serialized as a URI. + from_schema: hctl + range: anyUri + sizes: + description: >- + Target attribute that specifies one or more sizes for the referenced icon. Only applicable for relation type 'icon'. The value pattern follows {Height}x{Width} (e.g., \"16x16\", \"16x16 32x32\"). + from_schema: hctl + hreflang: + description: >- + The hreflang attribute specifies the language of a linked document. The value of this must be a valid language tag [[BCP47]]. + from_schema: hctl + pattern: "^(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)|((en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)))$" + slots: + - target + + ExpectedResponse: + class_uri: hctl:ExpectedResponse + description: >- + Communication metadata describing the expected response message for the primary response. + attributes: + contentType: + description: >- + TODO Check, was not in hctl ontology, if not could be source of discrepancy + required: true + + AdditionalExpectedResponse: + class_uri: hctl:AdditionalExpectedResponse + description: >- + Communication metadata describing the expected response message for additional responses. + is_a: ExpectedResponse + attributes: + additionalOutputSchema: + description: >- + This optional term can be used to define a data schema for an additional response if it differs from the default + output data schema. Rather than a DataSchema object, the name of a previous definition given in a SchemaDefinitions + map must be used. + success: + description: >- + Signals if the additional response should not be considered an error. + range: boolean + schema: + description: >- + TODO Check, was not in hctl ontology, if not could be source of discrepancy + + + Form: + class_uri: hctl:Form + description: >- + A form can be viewed as a statement of to perform an operation type on form context, + make a request method to submission target, where the optional form fields + may further describe the required request. In Thing Descriptions, the form context is the surrounding Object, + such as Properties, Actions, and Events or the Thing itself for meta-interactions. + attributes: + href: + required: true + range: anyUri + contentType: + from_schema: hctl + description: >- + Assign a content type based on a media type IANA-MEDIA-TYPES (e.g., 'text/plain') and potential parameters + (e.g., 'charset=utf-8') for the media type. + contentCoding: + description: >- + Content coding values indicate an encoding transformation that has been or can be applied to a representation. + Content codings are primarily used to allow a representation to be compressed or otherwise usefully transformed + without losing the identity of its underlying media type and without loss of information. Examples of content + coding include \"gzip\", \"deflate\", etc. + from_schema: hctl + securityDefinitions: + description: >- + A security schema applied to a (set of) affordance(s). + from_schema: td:hasSecurityConfiguration + scopes: + description: >- + TODO Check, was not in hctl ontology, if not could be source of discrepancy + returns: + description: >- + This optional term can be used if, e.g., the output communication metadata differ from input metadata (e.g., output contentType differ from the + input contentType). The response name contains metadata that is only valid for the response messages. + range: ExpectedResponse + additionalReturns: + description: >- + This optional term can be used if additional expected responses are possible, e.g. for error reporting. Each + additional response needs to be distinguished from others in some way (for example, by specifying a protocol-specific + response code), and may also have its own data schema. + from_schema: hctl + range: AdditionalExpectedResponse + multivalued: true + subprotocol: + from_schema: hctl + description: >- + Indicates the exact mechanism by which an interaction will be accomplished for a given protocol when there + are multiple options. + operationType: + description: + Indicates the semantic intention of performing the operation(s) described by the form. + from_schema: hctl + multivalued: true + range: OperationTypes + slots: + - target + + SecurityScheme: + slots: + - "@type" + - descriptions + attributes: + description: + proxy: + description: >- + URI of the proxy server this security configuration provides access to. If not given, the corresponding security configuration + is for the endpoint. + range: anyUri + scheme: + required: true + range: SecuritySchemeType + + DataSchema: + class_uri: jsonschema:DataSchema + description: >- + Metadata that describes the data format used. It can be used for validation. + attributes: + propertyName: + description: >- + Used to store the indexing name in the parent object when this schema appears as a property of an object schema. + writeOnly: + description: >- + Boolean value that is a hint to indicate whether a property interaction/value is write only (=true) or not (=false). + readonly: + description: >- + Boolean value that is a hint to indicate whether a property interaction/value is read only (=true) or not (=false). + slots: + - description + - title + - titleInLanguage + - descriptionInLanguage + + + InteractionAffordance: + description: >- + TOOD + class_uri: td:InteractionAffordance + attributes: + name: + description: >- + Indexing property to store entity names when serializing them in a JSON-LD @index container. + identifier: true + uriVariables: + description: >- + Define URI template variables according to RFC6570 as collection based on schema specifications. The individual + variables DataSchema cannot be an ObjectSchema or an ArraySchema. TODO: range is not obvious from the ontology. + from_schema: td:hasUriTemplateSchema + multivalued: true + range: DataSchema + forms: + from_schema: td:hasForm + description: >- + Set of form hypermedia controls that describe how an operation can be performed. + required: + multivalued: true + range: Form + slots: + - titles + - descriptions + - title + - description + - titleInLanguage + - descriptionInLanguage + + PropertyAffordance: + is_a: InteractionAffordance + class_uri: td:PropertyAffordance + description: >- + An Interaction Affordance that exposes state of the Thing. This state can be retrieved (read) and/or updated. + mixins: DataSchema + attributes: + observable: + description: >- + A hint that indicates whether Servients hosting the Thing and Intermediaries should probide a Protocol Binding + that supports the observeproperty and unobserveproperty operations for this Property. + range: boolean + + ActionAffordance: + is_a: InteractionAffordance + class_uri: td:ActionAffordance + description: >- + An Interaction Affordance that allows to invoke a function of the Thing, which manipulates state (e.g., toggling a lamp + on or off) or triggers a process on the Thing (e.g., dim a lamp over time). + attributes: + safe: + description: >- + Signals if the action is safe (=true) or not. Used to signal if there is no internal state (cf. resource state) + is changed when invoking an Action. + from_schema: td:isSafe + range: boolean + synchronous: + description: >- + Indicates whether the action is synchronous (=true) or not. A synchronous action means that the response of action + contains all the information about the result of the action and no further querying about the status of the action + is needed. Lack of this keyword means that no claim on the synchronicity of the action can be made. + from_schema: td:isSynchronous + range: boolean + idempotent: + description: >- + Indicates whether the action is idempotent (=true) or not. Informs whether the action can be called repeatedly with the same + results, if present, based on the same input. + from_schema: td:isIdempotent + range: boolean + input: + description: >- + Used to define the input data schema of the action. + from_schema: td:hasInputSchema + range: DataSchema + output: + description: >- + Used to define the output data schema of the action. + from_schema: td:hasOutputSchema + range: DataSchema + + EventAffordance: + is_a: InteractionAffordance + class_uri: td:EventAffordance + description: >- + An Interaction Affordance that describes an event source, which asynchronously pushes event data to Consumers + (e.g., overhearing alerts). + attributes: + subscription: + description: >- + Defines data that needs to be passed upon subscription, e.g., filters or message format for setting up Webhooks. + from_schema: td:hasSubscriptionSchema + range: DataSchema + cancellation: + description: >- + Defines any data that needs to be passed to cancel a subscription, e.g., a specific message to remove a Webhook. + from_schema: td:hasCancellationSchema + range: DataSchema + notification: + description: >- + Defines the data schema of the Event instance messages pushed by the Thing. + from_schema: td:hasNotificationSchema + range: DataSchema + notificationResponse: + description: >- + Defines the data schema of the Event response messages sent by the consumer in a response to a data message. + from_schema: td:hasNotificationResponseSchema + range: DataSchema + + Thing: + description: >- + An abstraction of a physical or a virtual entity whose metadata and interfaces are described by a WoT Thing + Description, whereas a virtual entity is the composition of one or more Things. + class_uri: td:Thing + attributes: + securityDefinitions: + description: >- + A security scheme applied to a (set of) affordance(s). TODO check + from_schema: td:hasSecurityConfiguration + required: + multivalued: true + any_of: + - range: string + - range: SecuritySchemeType + security: + description: >- + A Thing may define abstract security schemes, used to configure the secure access of (a set of) affordance(s). + TODO: check + from_schema: td:definesSecurityScheme + required: + multivalued: true + schemaDefinitions: + description: TODO CHECK + multivalued: true + range: DataSchema + profile: + description: >- + Indicates the WoT Profile mechanisms followed by this Thing Description and the corresponding Thing implementation. + from_schema: td:followsProfile + multivalued: true + range: anyUri + instance: + description: >- + Provides a version identicator of this TD instance. + created: + from_schema: dcterms + description: >- + Provides information when the TD instance was created. + range: datetime + modified: + from_schema: dcterms + description: >- + Provides information when the TD instance was last modified. + range: datetime + supportContact: + description: >- + Provides information about the TD maintainer as URI scheme (e.g., mailto [[RFC6068]],tel [[RFC3966]],https [[RFC9112]]). + range: anyUri + from_schema: schema:contactPoint + base: + description: >- + Define the base URI that is used for all relative URI references throughout a TD document. + from_schema: td:baseURI + range: anyUri + version: + range: VersionInfo + forms: + from_schema: td:hasForm + description: >- + Set of form hypermedia controls that describe how an operation can be performed. Forms are serializations of + Protocol Bindings. + multivalued: true + range: Form + links: + from_schema: td:hasLink + description: >- + Provides Web links to arbitrary resources that relate to the specified Thing Description. + multivalued: true + range: Link + properties: + description: >- + All Property-based interaction affordances of the Thing. + from_schema: td:hasPropertyAffordance + multivalued: true + inlined: true + range: PropertyAffordance + actions: + description: >- + All Action-based interaction affordances of the Thing. + from_schema: td:hasActionAffordance + multivalued: true + inlined: true + range: ActionAffordance + events: + description: >- + All Event-based interaction affordances of the Thing. + from_schema: td:hasEventAffordance + multivalued: true + inlined: true + range: EventAffordance + slots: + - id + - title + - description + - titles + - descriptions + - "@type" + - titleInLanguage + - descriptionInLanguage + + +enums: + OperationTypes: + description: >- + Enumerations of well-known operation types necessary to implement the WoT interaction model. + permissible_values: + readproperty: + description: Identifies the read operation on Property Affordances to retrieve the corresponding data. + meaning: td:readProperty + writeproperty: + description: Identifies the write operation on Property Affordances to update the corresponding data. + meaning: td:writeProperty + observeproperty: + description: Identifies the observe operation on Property Affordances to be notified with the new data when the Property is updated. + meaning: td:observeProperty + unobserveproperty: + description: Identifies the unobserve operation on Property Affordances to stop the corresponding notifications. + meaning: td:unobserveProperty + invokeaction: + description: Identifies the invoke operation on Action Affordances to perform the corresponding action. + meaning: td:invokeAction + queryaction: + description: Identifies the querying operation on Action Affordances to get the status of the corresponding action. + meaning: td:queryAction + cancelaction: + description: Identifies the cancel operation on Action Affordances to cancel the ongoing corresponding action. + meaning: td:cancelAction + subscribeevent: + description: Identifies the subscribe operation on Event Affordances to be notified by the Thing when the event occurs. + meaning: td:subscribeEvent + unsubscribeevent: + description: Identifies the unsubscribe operation on Event Affordances to stop the corresponding notifications. + meaning: td:unsubscribeEvent + readallproperties: + description: Identifies the readallproperties operation on a Thing to retrieve the data of all Properties in a single interaction. + meaning: td:readAllProperties + writeallproperties: + description: Identifies the writeallproperties operation on a Thing to update the data of all writable Properties in a single interaction. + meaning: writeAllProperties + readmultipleproperties: + description: Identifies the readmultipleproperties operation on a Thing to retrieve the data of selected Properties in a single interaction. + meaning: td:readMultipleProperties + writemultipleproperties: + description: Identifies the writemultipleproperties operation on a Thing to update the data of selected writable Properties in a single interaction. + meaning: td:writeMultipleProperties + observeallproperties: + description: Identifies the observeallproperties operation on Properties to be notified with new data when any Property is updated. + meaning: td:observeAllProperties + unobserveallproperties: + description: Identifies the unobserveallproperties operation on Properties to stop notifications from all Properties in a single interaction. + meaning: td:unobserveAllProperties + subscribeallevents: + description: Identifies the subscribeallevents operation on Events to subscribe to notifications from all Events in a single interaction. + meaning: td:subscribeAllEvents + unsubscribeallevents: + description: Identifies the unsubscribeallevents operation on Events to unsubscribe from notifications from all Events in a single interaction. + meaning: td:unsubscribeAllEvents + queryallactions: + description: Identifies the queryallactions operation on a Thing to get the status of all Actions in a single interaction. + meaning: td:queryAllActions + SecuritySchemeType: + permissible_values: + nosec: + description: A security configuration corresponding to identified by the Vocabulary Term nosec, indicating there is no authentication or other mechanism required to access the resource. + meaning: wotsec:NoSecurityScheme + combo: + description: Elements of this scheme define various ways in which other named schemes defined in securityDefinitions, including other ComboSecurityScheme definitions, are to be combined to create a new scheme definition. + meaning: wotsec:ComboSecurityScheme + basic: + description: Uses an unencrypted username and password. + meaning: wotsec:BasicSecurityScheme + digest: + description: This scheme is similar to basic authentication but with added features to avoid man-in-the-middle attacks. + meaning: wotsec:DigestSecurityScheme + bearer: + description: Bearer tokens are used independently of OAuth2. + meaning: wotsec:BearerSecurityScheme + psk: + description: This is meant to identify that a standard is used for pre-shared keys such as TLS-PSK [RFC4279], and that the ciphersuite used for keys will be established during protocol negotiation. + meaning: wotsec:PSKSecurityScheme + oauth2: + description: OAuth 2.0 authentication security configuration for systems conformant with [RFC6749] and [RFC8252]. + meaning: wotsec:OAuth2SecurityScheme + apikey: + description: This scheme is to be used when the access token is opaque. + meaning: wotsec:APIKeySecurityScheme + auto: + description: This scheme indicates that the security parameters are going to be negotiated by the underlying protocols at runtime + meaning: wotsec:AutoSecurityScheme + diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..a1919f7 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for thing-description-schema.""" diff --git a/tests/test_data.py b/tests/test_data.py new file mode 100644 index 0000000..f7364a9 --- /dev/null +++ b/tests/test_data.py @@ -0,0 +1,22 @@ +"""Data test.""" +import os +import glob +import unittest + +from linkml_runtime.loaders import yaml_loader +from thing_description_schema.datamodel.thing_description_schema import ThingCollection + +ROOT = os.path.join(os.path.dirname(__file__), '..') +DATA_DIR = os.path.join(ROOT, "src", "data", "examples") + +EXAMPLE_FILES = glob.glob(os.path.join(DATA_DIR, '*.yaml')) + + +class TestData(unittest.TestCase): + """Test data and datamodel.""" + + def test_data(self): + """Data test.""" + for path in EXAMPLE_FILES: + obj = yaml_loader.load(path, target_class=ThingCollection) + assert obj