diff --git a/README.md b/README.md index 72494f5..d2c6522 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,20 @@ # LinkML Project Cookiecutter -A [Cookiecutter](https://cookiecutter.readthedocs.io/en/stable/) template for projects using [`Linkml`](https://github.com/linkml/linkml). - +A [Cookiecutter](https://cookiecutter.readthedocs.io/en/stable/) template for +projects using [LinkML](https://github.com/linkml/linkml). ## Standard Protocol ### Step 1: Create a virtual environment -Create a Python virtual environment. You can read [this -guide](https://realpython.com/python-virtual-environments-a-primer/) to learn more about them and how to create one. -We suggest using poetry, but you can use any tool you like. Please note, most LinkML tools work best in Python 3.8 or higher. +Create a Python virtual environment. +You can read [this guide](https://realpython.com/python-virtual-environments-a-primer/) +to learn more about them and how to create one. We suggest using poetry, but +you can use any tool you like. Please note, most LinkML tools work best in +Python 3.8 or higher. An example using poetry: + ```bash curl -sSL https://install.python-poetry.org | python3 - ``` @@ -19,17 +22,22 @@ curl -sSL https://install.python-poetry.org | python3 - ```bash mkdir linkml-projects cd linkml-projects -poetry init # creates a new poetry project with pyproject.toml. Note this is not a new linkml project, it is just a virtual environment to install cruft. +poetry init # creates a new poetry project with pyproject.toml + # Note this is not a new linkml project, + # it is just a virtual environment to install cruft. poetry add click==8.1.3 # this creates your virtual environment. ``` Add `poetry-dynamic-versioning` as a plugin -``` + +```bash poetry self add "poetry-dynamic-versioning[plugin]" ``` ### Step 2: Install the cruft tool in your virtual environment -This tool will help you keep your project up to date with the latest LinkML tools and best practices. + +This tool will help you keep your project up to date with the latest LinkML +tools and best practices. In your poetry virtual environment: @@ -38,7 +46,7 @@ poetry shell poetry add cruft ``` -### Step 3: Use cruft to create your brand new LinkML project: +### Step 3: Use cruft to create your brand new LinkML project In your poetry virtual environment: @@ -46,53 +54,65 @@ In your poetry virtual environment: cruft create https://github.com/linkml/linkml-project-cookiecutter ``` -You will be prompted for a few values. The defaults are fine for most projects, but do name your project something -that makes sense to you! The interactive session will guide you through the process: +You will be prompted for a few values. The defaults are fine for most +projects, but do name your project something that makes sense to you! +The interactive session will guide you through the process: -- `project_name`: Name of the project, use kebab-case with no spaces. Suggestions: - - `patient-observation-schema` - - `sample-collection-metadata` - - `resume-standard` +- `project_name`: Name of the project, use kebab-case with no spaces. + Suggestions: + - `patient-observation-schema` + - `sample-collection-metadata` + - `resume-standard` - `project_description`: Description of the project. - - A single brief sentence is recommended - - Can easily be modified later + - A single brief sentence is recommended + - Can easily be modified later - `full_name`: Your name - `email`: your email - `main_schema_class`: - - This is used to generate an example schema which you can edit - - The value of this field is ignored if this is a schemasheets project - - You can always change this later + - This is used to generate an example schema which you can edit + - The value of this field is ignored if this is a schemasheets project + - You can always change this later - Examples: `Person`, `Observation`, `Sample`, `Entry`, `Gene`, `Event` - `create_python_project` - - If "Yes", set this up as a python project - - Select Yes if you want to make data classes that can be used by developers + - If "Yes", set this up as a python project + - Select Yes if you want to make data classes that can be used by developers - `use_schemasheets` - - If "Yes", set this to obtain your schema from [schemasheets](https://linkml.io/schemasheets) + - If "Yes", set this to obtain your schema from + [schemasheets](https://linkml.io/schemasheets) - `google_sheet_id` - - Ignore/use default value if answer to previous question was "No" - - If you are using schemasheets then enter your google doc ID here - - If you like you can leave the default value, and this will use the demo schema + - Ignore/use default value if answer to previous question was "No" + - If you are using schemasheets then enter your google doc ID here + - If you like you can leave the default value, and this will use the demo + schema - `google_sheet_tabs` - - Ignore/use default value if not using schemasheets - - If you are using schemasheets, enter a space-separated list of tabs - - your tabs in your sheet MUST NOT have spaces in them + - Ignore/use default value if not using schemasheets + - If you are using schemasheets, enter a space-separated list of tabs + - your tabs in your sheet MUST NOT have spaces in them - `github_token_for_pypi_deployment`: - - The github token name which aligns with your autogenerated PyPI token for making releases. - - This helps automated releases to PyPI - - This should be ignored if this is not a python project - - Even if this is a python project, you can leave blank and fill in later + - The github token name which aligns with your autogenerated PyPI token for + making releases. + - This helps automated releases to PyPI + - This should be ignored if this is not a python project + - Even if this is a python project, you can leave blank and fill in later -This will generate the project folder abiding by the template configuration specified by `linkml-project-cookiecutter` in the [`cookiecutter.json`](https://github.com/linkml/linkml-project-cookiecutter/blob/main/cookiecutter.json) file. +This will generate the project folder abiding by the template configuration +specified by `linkml-project-cookiecutter` in the +[`cookiecutter.json`](https://github.com/linkml/linkml-project-cookiecutter/blob/main/cookiecutter.json) +file. -This will generate the project folder very similar to what is mentioned in the [linkml-project-template](https://github.com/linkml/linkml-project-template) project. +This will generate the project folder very similar to what is mentioned in the +[linkml-project-template](https://github.com/linkml/linkml-project-template) +project. -See [linkml/linkml-project-cookiecutter](https://github.com/linkml/linkml-project-cookiecutter) for more docs. +For more docs, see +[linkml/linkml-project-cookiecutter](https://github.com/linkml/linkml-project-cookiecutter). ### Step 4: Setup the LinkML project -Optionally set project generation environment variables (see `gen-doc --help` and `gen-project --help`): +Optionally set project generation environment variables +(see `gen-doc --help` and `gen-project --help`): -``` +```bash export LINKML_COOKIECUTTER_GEN_DOC_ARGS=--no-mergeimports # example export LINKML_COOKIECUTTER_GEN_PROJECT_ARGS=--no-mergeimports # example ``` @@ -106,7 +126,9 @@ make setup ### Step 5: Edit the schema -Edit the schema (the .yaml file) in the linkml-projects/my-awesome-schema/src/my_awesome_schema/schema folder +Edit the schema (the .yaml file) in the +`linkml-projects/my-awesome-schema/src/my_awesome_schema/schema` folder + ```bash nano src/my_awesome_schema/schema/my_awesome_schema.yaml ``` @@ -117,11 +139,13 @@ nano src/my_awesome_schema/schema/my_awesome_schema.yaml make test ``` -### Step 7: Auto-generate your documentation locally. -LinkML generates schema documenation automatically. Step 7 here, allows you to preview the documentation -that LinkML generates before pushing to GitHub. Note, this template comes with GitHub -Actions that autogenerate this documentation on release of your schema repository at a URL like this one: -https://my-user-or-organization.github.io/my-awesome-schema/ +### Step 7: Auto-generate your documentation locally + +LinkML generates schema documenation automatically. Step 7 here, allows you to +preview the documentation that LinkML generates before pushing to GitHub. +Note, this template comes with GitHub Actions that autogenerate this +documentation on release of your schema repository at a URL like this one: +[https://my-user-or-organization.github.io/my-awesome-schema/] ```bash make serve @@ -129,16 +153,17 @@ make serve ### Step 8: Create a github project -8a: Go to https://github.com/new and follow the instructions, being sure to NOT add a README or .gitignore file (this -cookiecutter template will take care of this for you) +1. Go to [https://github.com/new] and follow the instructions, being sure to + NOT add a README or .gitignore file (this cookiecutter template will take + care of this for you) -8b: Add the remote to your local git repository` +2. Add the remote to your local git repository -```bash -git remote add origin https://github.com/my-user-or-organization/my-awesome-schema.git -git branch -M main -git push -u origin main -``` + ```bash + git remote add origin https://github.com/my-user-or-organization/my-awesome-schema.git + git branch -M main + git push -u origin main + ``` ### Step 9: Deploy documentation @@ -150,29 +175,31 @@ See [How to register a schema](../faq/contributing) ## Keeping your project up to date -In order to be up-to-date with the template, first check if there is a mismatch between the project's boilerplate -code and the template by running: +In order to be up-to-date with the template, first check if there is a mismatch +between the project's boilerplate code and the template by running: ```bash poetry shell cruft check ``` -This indicates if there is a difference between the current project's boilerplate code and the latest version of the -project template. If the project is up-to-date with the template: +This indicates if there is a difference between the current project's +boilerplate code and the latest version of the project template. If the project +is up-to-date with the template: -``` +```output SUCCESS: Good work! Project's cruft is up to date and as clean as possible :). ``` -Otherwise, it will indicate that the project's boilerplate code is not up-to-date by the following: +Otherwise, it will indicate that the project's boilerplate code is not +up-to-date by the following: -``` +```output FAILURE: Project's cruft is out of date! Run `cruft update` to clean this mess up. ``` -For viewing the difference, run `cruft diff`. This shows the difference between the project's boilerplate code and the -template's latest version. - -After running `cruft update`, the project's boilerplate code will be updated to the latest version of the template. +For viewing the difference, run `cruft diff`. This shows the difference between +the project's boilerplate code and the template's latest version. +After running `cruft update`, the project's boilerplate code will be updated to +the latest version of the template. diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 0339905..bf01e65 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -1,9 +1,6 @@ """Code to run after generating the project.""" -import re -import sys import shutil -from pathlib import Path shutil.rmtree("licenses") diff --git a/hooks/pre_gen_project.py b/hooks/pre_gen_project.py index 117217d..2097a8f 100644 --- a/hooks/pre_gen_project.py +++ b/hooks/pre_gen_project.py @@ -2,7 +2,6 @@ import re import sys -from pathlib import Path MODULE_REGEX = re.compile(r'^[_a-zA-Z][_a-zA-Z0-9\-]+$') project_name = '{{ cookiecutter.project_name }}' diff --git a/{{cookiecutter.project_name}}/.github/workflows/deploy-docs.yaml b/{{cookiecutter.project_name}}/.github/workflows/deploy-docs.yaml index 5d394ab..13ce953 100644 --- a/{{cookiecutter.project_name}}/.github/workflows/deploy-docs.yaml +++ b/{{cookiecutter.project_name}}/.github/workflows/deploy-docs.yaml @@ -1,31 +1,32 @@ +--- name: Auto-deployment of {{cookiecutter.__project_slug}} Documentation on: push: - branches: [ main ] + branches: [main] jobs: build-docs: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@main - with: - fetch-depth: 0 # otherwise, you will failed to push refs to dest repo - - - name: Set up Python 3. - uses: actions/setup-python@v3 - with: - python-version: 3.9 + - name: Checkout + uses: actions/checkout@main + with: + fetch-depth: 0 # otherwise, you will failed to push refs to dest repo - - name: Install Poetry. - uses: snok/install-poetry@v1.3 + - name: Set up Python 3. + uses: actions/setup-python@v3 + with: + python-version: 3.9 - - name: Install dependencies. - run: poetry install -E docs - - - name: Build documentation. - run: | - mkdir -p docs - touch docs/.nojekyll - poetry run gen-doc -d docs src/{{cookiecutter.__project_slug}}/schema/{{cookiecutter.__project_slug}}.yaml - poetry run mkdocs gh-deploy \ No newline at end of file + - name: Install Poetry. + uses: snok/install-poetry@v1.3 + + - name: Install dependencies. + run: poetry install -E docs + + - name: Build documentation. + run: | + mkdir -p docs + touch docs/.nojekyll + poetry run gen-doc -d docs src/{{cookiecutter.__project_slug}}/schema/{{cookiecutter.__project_slug}}.yaml + poetry run mkdocs gh-deploy diff --git a/{{cookiecutter.project_name}}/.github/workflows/main.yaml b/{{cookiecutter.project_name}}/.github/workflows/main.yaml index dd56182..1b2ffa9 100644 --- a/{{cookiecutter.project_name}}/.github/workflows/main.yaml +++ b/{{cookiecutter.project_name}}/.github/workflows/main.yaml @@ -1,7 +1,7 @@ # Built from: # https://docs.github.com/en/actions/guides/building-and-testing-python # https://github.com/snok/install-poetry#workflows-and-tips - +--- name: Build and test {{cookiecutter.__project_slug}} on: [pull_request] @@ -16,9 +16,6 @@ jobs: steps: - #---------------------------------------------- - # check-out repo and set-up python - #---------------------------------------------- - name: Check out repository uses: actions/checkout@v2 @@ -27,27 +24,14 @@ jobs: with: python-version: ${{ "{{" }} matrix.python-version {{ "}}" }} - #---------------------------------------------- - # install & configure poetry - #---------------------------------------------- - name: Install Poetry uses: snok/install-poetry@v1.3 - #---------------------------------------------- - # install dependencies if cache does not exist - #---------------------------------------------- - name: Install dependencies run: poetry install --no-interaction --no-root - #---------------------------------------------- - # install your root project, if required - #---------------------------------------------- - - name: Install library + - name: Install project run: poetry install --no-interaction - #---------------------------------------------- - # run test suite - #---------------------------------------------- - - name: Run tests + - name: Run test suite run: make test - diff --git a/{{cookiecutter.project_name}}/.github/workflows/pypi-publish.yaml b/{{cookiecutter.project_name}}/.github/workflows/pypi-publish.yaml index 074d912..63d028d 100644 --- a/{{cookiecutter.project_name}}/.github/workflows/pypi-publish.yaml +++ b/{{cookiecutter.project_name}}/.github/workflows/pypi-publish.yaml @@ -1,3 +1,4 @@ +--- name: Publish Python Package on: @@ -10,30 +11,30 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2.2.2 - with: - python-version: 3.9 - - - name: Install Poetry - run: | - pip install poetry - poetry self add "poetry-dynamic-versioning[plugin]" - # uses: snok/install-poetry@v1.1.6 - # with: - # virtualenvs-create: true - # virtualenvs-in-project: true - - # - 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.{{cookiecutter.github_token_for_pypi_deployment}} {{ "}}" }} + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2.2.2 + with: + python-version: 3.9 + + - name: Install Poetry + run: | + pip install poetry + poetry self add "poetry-dynamic-versioning[plugin]" + # uses: snok/install-poetry@v1.1.6 + # with: + # virtualenvs-create: true + # virtualenvs-in-project: true + + # - 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.{{cookiecutter.github_token_for_pypi_deployment}} {{ "}}" }} diff --git a/{{cookiecutter.project_name}}/CODE_OF_CONDUCT.md b/{{cookiecutter.project_name}}/CODE_OF_CONDUCT.md index b547f39..2b301c6 100644 --- a/{{cookiecutter.project_name}}/CODE_OF_CONDUCT.md +++ b/{{cookiecutter.project_name}}/CODE_OF_CONDUCT.md @@ -2,11 +2,17 @@ ## Our Pledge -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and +our community a harassment-free experience for everyone, regardless of age, +body size, disability, ethnicity, gender identity and expression, level of +experience, nationality, personal appearance, race, religion, or sexual +identity and orientation. ## Our Standards -Examples of behavior that contributes to creating a positive environment include: +Examples of behavior that contributes to creating a positive environment +include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences @@ -16,31 +22,55 @@ Examples of behavior that contributes to creating a positive environment include Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or advances +* The use of sexualized language or imagery and unwelcome sexual attention or + advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting ## Our Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. ## Scope -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an +appointed representative at an online or offline event. Representation of a +project may be further defined and clarified by project maintainers. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by [contacting the project team](contact.md). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by [contacting the project team](contact.md). All complaints will be +reviewed and investigated and will result in a response that is deemed +necessary and appropriate to the circumstances. The project team is obligated +to maintain confidentiality with regard to the reporter of an incident. Further +details of specific enforcement policies may be posted separately. -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. ## Attribution -This code of conduct has been derived from the excellent code of conduct of the [ATOM project](https://github.com/atom/atom/blob/master/CODE_OF_CONDUCT.md) which in turn is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] +This code of conduct has been derived from the excellent code of conduct of the +[ATOM project](https://github.com/atom/atom/blob/master/CODE_OF_CONDUCT.md) +which in turn is adapted from the [Contributor Covenant][homepage], version +1.4, available at [https://contributor-covenant.org/version/1/4][version] [homepage]: https://contributor-covenant.org [version]: https://contributor-covenant.org/version/1/4/ diff --git a/{{cookiecutter.project_name}}/CONTRIBUTING.md b/{{cookiecutter.project_name}}/CONTRIBUTING.md index 89dc02c..bfa48f3 100644 --- a/{{cookiecutter.project_name}}/CONTRIBUTING.md +++ b/{{cookiecutter.project_name}}/CONTRIBUTING.md @@ -2,20 +2,21 @@ :+1: First of all: Thank you for taking the time to contribute! -The following is a set of guidelines for contributing to {{ cookiecutter.project_name }}. -These guidelines are not strict rules. Use your best judgment, and feel free to propose -changes to this document in a pull request. +The following is a set of guidelines for contributing to +{{ cookiecutter.project_name }}. 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 -- [Code of Conduct](#code-of-conduct) -- [Guidelines for Contributions and Requests](#contributions) - * [Reporting problems with the ontology](#reporting-bugs) - * [Requesting new terms](#requesting-terms) - * [Adding new terms by yourself](#adding-terms) -- [Best practices](#best-practices) - * [How to write a great issue?](#great-issues) - * [How to create a great pull/merge request?](#great-pulls) +* [Code of Conduct](#code-of-conduct) +* [Guidelines for Contributions and Requests](#contributions) + * [Reporting problems with the data model](#reporting-bugs) + * [Requesting new terms](#requesting-terms) + * [Adding new terms yourself](#adding-terms) +* [Best Practices](#best-practices) + * [How to write a great issue](#great-issues) + * [How to create a great pull/merge request](#great-pulls) @@ -33,5 +34,40 @@ Please carefully read our [Code of Conduct](CODE_OF_CONDUCT.md). ### Reporting problems with the data model -Please use our [Issue Tracker](https://github.com/{{ cookiecutter.github_org }}/{{ cookiecutter.project_name }}/issues/) for reporting problems with the ontology. +Please use our [Issue Tracker][issues] to report problems with the ontology. + + +### Requesting new terms + +Please use our [Issue Tracker][issues] to request a new term for the ontology. + + + +### Adding new terms yourself + +Please submit a [Pull Request][pulls] to submit a new term for consideration. + + + +## Best Practices + + + +### How to write a great issue + +Please review GitHub's overview article, +["Tracking Your Work with Issues"][about-issues]. + + + +### How to create a great pull/merge request + +Please review GitHub's article, ["About Pull Requests"][about-pulls], +and make your changes on a [new branch][about-branches]. + +[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/{{ cookiecutter.github_org }}/{{ cookiecutter.project_name }}/issues/ +[pulls]: https://github.com/{{ cookiecutter.github_org }}/{{ cookiecutter.project_name }}/pulls/ diff --git a/{{cookiecutter.project_name}}/README.md b/{{cookiecutter.project_name}}/README.md index f3f5ed9..6d7742d 100644 --- a/{{cookiecutter.project_name}}/README.md +++ b/{{cookiecutter.project_name}}/README.md @@ -4,18 +4,20 @@ ## Website -* [https://{{cookiecutter.github_org}}.github.io/{{cookiecutter.project_name}}](https://{{cookiecutter.github_org}}.github.io/{{cookiecutter.project_name}}) +[https://{{cookiecutter.github_org}}.github.io/{{cookiecutter.project_name}}](https://{{cookiecutter.github_org}}.github.io/{{cookiecutter.project_name}}) ## Repository Structure * [examples/](examples/) - example data * [project/](project/) - project files (do not edit these) * [src/](src/) - source files (edit these) - * [{{cookiecutter.__project_slug}}](src/{{cookiecutter.__project_slug}}) - * [schema](src/{{cookiecutter.__project_slug}}/schema) -- LinkML schema (edit this) -{% if cookiecutter.create_python_classes == "Yes" -%} - * [datamodel](src/{{cookiecutter.__project_slug}}/datamodel) -- Generated python datamodel -* [tests](tests/) - python tests + * [{{cookiecutter.__project_slug}}](src/{{cookiecutter.__project_slug}}) + * [schema](src/{{cookiecutter.__project_slug}}/schema) -- LinkML schema + (edit this) +{%- if cookiecutter.create_python_classes == "Yes" %} + * [datamodel](src/{{cookiecutter.__project_slug}}/datamodel) -- generated + Python datamodel +* [tests/](tests/) - Python tests {%- endif %} ## Developer Documentation @@ -23,11 +25,11 @@
Use the `make` command to generate project artefacts: -- `make all`: make everything -- `make deploy`: deploys site - +* `make all`: make everything +* `make deploy`: deploys site
## Credits -this project was made with [linkml-project-cookiecutter](https://github.com/linkml/linkml-project-cookiecutter) +This project was made with +[linkml-project-cookiecutter](https://github.com/linkml/linkml-project-cookiecutter). diff --git a/{{cookiecutter.project_name}}/about.yaml b/{{cookiecutter.project_name}}/about.yaml index 69b3d39..b7647b3 100644 --- a/{{cookiecutter.project_name}}/about.yaml +++ b/{{cookiecutter.project_name}}/about.yaml @@ -1,3 +1,4 @@ +--- name: {{cookiecutter.__project_slug}} author: {{cookiecutter.__author}} description: {{cookiecutter.project_description}} diff --git a/{{cookiecutter.project_name}}/examples/README.md b/{{cookiecutter.project_name}}/examples/README.md index ecb2103..a9e8be2 100644 --- a/{{cookiecutter.project_name}}/examples/README.md +++ b/{{cookiecutter.project_name}}/examples/README.md @@ -2,4 +2,4 @@ This folder contains example data conforming to {{cookiecutter.__project_slug}} -The source for these is in [src/data](../src/data/examples) \ No newline at end of file +The source for these is in [src/data](../src/data/examples) diff --git a/{{cookiecutter.project_name}}/mkdocs.yml b/{{cookiecutter.project_name}}/mkdocs.yml index 8534773..4a01fff 100644 --- a/{{cookiecutter.project_name}}/mkdocs.yml +++ b/{{cookiecutter.project_name}}/mkdocs.yml @@ -1,22 +1,23 @@ +--- site_name: "{{cookiecutter.project_name}}" theme: name: material -# palette: -# scheme: slate -# primary: cyan + # palette: + # scheme: slate + # primary: cyan features: - content.tabs.link plugins: - search - mermaid2 nav: -# - Home: home.md + # - Home: home.md - Index: index.md - About: about.md site_url: https://{{cookiecutter.github_org}}.github.io/{{cookiecutter.project_name}} repo_url: https://github.com/{{cookiecutter.github_org}}/{{cookiecutter.project_name}} -# Uncomment this block to enable use of Google Analytics. +# Uncomment this block to enable use of Google Analytics. # Replace the property value with your own ID. # extra: # analytics: diff --git a/{{cookiecutter.project_name}}/project/graphql/{{cookiecutter.__project_slug}}.graphql b/{{cookiecutter.project_name}}/project/graphql/{{cookiecutter.__project_slug}}.graphql index 584536e..8680e33 100644 --- a/{{cookiecutter.project_name}}/project/graphql/{{cookiecutter.__project_slug}}.graphql +++ b/{{cookiecutter.project_name}}/project/graphql/{{cookiecutter.__project_slug}}.graphql @@ -65,4 +65,3 @@ type Relationship relatedTo: String type: String } - diff --git a/{{cookiecutter.project_name}}/project/owl/{{cookiecutter.__project_slug}}.owl.ttl b/{{cookiecutter.project_name}}/project/owl/{{cookiecutter.__project_slug}}.owl.ttl index 6cb9fd5..7ea962e 100644 --- a/{{cookiecutter.project_name}}/project/owl/{{cookiecutter.__project_slug}}.owl.ttl +++ b/{{cookiecutter.project_name}}/project/owl/{{cookiecutter.__project_slug}}.owl.ttl @@ -341,5 +341,3 @@ linkml:ClassDefinition a owl:Class ; linkml:SlotDefinition a owl:Class ; rdfs:label "slot_definition" . - - diff --git a/{{cookiecutter.project_name}}/project/prefixmap/{{cookiecutter.__project_slug}}.yaml b/{{cookiecutter.project_name}}/project/prefixmap/{{cookiecutter.__project_slug}}.yaml index b624cff..c498d30 100644 --- a/{{cookiecutter.project_name}}/project/prefixmap/{{cookiecutter.__project_slug}}.yaml +++ b/{{cookiecutter.project_name}}/project/prefixmap/{{cookiecutter.__project_slug}}.yaml @@ -1,3 +1,4 @@ +--- { "PATO": "http://purl.obolibrary.org/obo/PATO_", "biolink": "https://w3id.org/biolink/", diff --git a/{{cookiecutter.project_name}}/project/shacl/{{cookiecutter.__project_slug}}.shacl.ttl b/{{cookiecutter.project_name}}/project/shacl/{{cookiecutter.__project_slug}}.shacl.ttl index 38378ed..9af53eb 100644 --- a/{{cookiecutter.project_name}}/project/shacl/{{cookiecutter.__project_slug}}.shacl.ttl +++ b/{{cookiecutter.project_name}}/project/shacl/{{cookiecutter.__project_slug}}.shacl.ttl @@ -162,4 +162,3 @@ my_datamodel:FamilialRelationship a sh:NodeShape ; sh:order 2 ; sh:path my_datamodel:related_to ] ; sh:targetClass my_datamodel:FamilialRelationship . - diff --git a/{{cookiecutter.project_name}}/project/shex/{{cookiecutter.__project_slug}}.shex b/{{cookiecutter.project_name}}/project/shex/{{cookiecutter.__project_slug}}.shex index 8c4a7d9..6cd9b11 100644 --- a/{{cookiecutter.project_name}}/project/shex/{{cookiecutter.__project_slug}}.shex +++ b/{{cookiecutter.project_name}}/project/shex/{{cookiecutter.__project_slug}}.shex @@ -122,5 +122,3 @@ linkml:Nodeidentifier NONLITERAL ) } OR @ ) - - diff --git a/{{cookiecutter.project_name}}/project/sqlschema/{{cookiecutter.__project_slug}}.sql b/{{cookiecutter.project_name}}/project/sqlschema/{{cookiecutter.__project_slug}}.sql index a4d44f5..6a24581 100644 --- a/{{cookiecutter.project_name}}/project/sqlschema/{{cookiecutter.__project_slug}}.sql +++ b/{{cookiecutter.project_name}}/project/sqlschema/{{cookiecutter.__project_slug}}.sql @@ -1,77 +1,75 @@ - - CREATE TABLE "Address" ( - street TEXT, - city TEXT, - postal_code TEXT, - PRIMARY KEY (street, city, postal_code) + street TEXT, + city TEXT, + postal_code TEXT, + PRIMARY KEY (street, city, postal_code) ); CREATE TABLE "NamedThing" ( - id TEXT NOT NULL, - name TEXT, - description TEXT, - image TEXT, - PRIMARY KEY (id) + id TEXT NOT NULL, + name TEXT, + description TEXT, + image TEXT, + PRIMARY KEY (id) ); CREATE TABLE "Organization" ( - id TEXT NOT NULL, - name TEXT, - description TEXT, - image TEXT, - mission_statement TEXT, - founding_date TEXT, - PRIMARY KEY (id) + id TEXT NOT NULL, + name TEXT, + description TEXT, + image TEXT, + mission_statement TEXT, + founding_date TEXT, + PRIMARY KEY (id) ); CREATE TABLE "Person" ( - id TEXT NOT NULL, - name TEXT, - description TEXT, - image TEXT, - primary_email TEXT, - birth_date TEXT, - age_in_years INTEGER, - current_address TEXT, - PRIMARY KEY (id) + id TEXT NOT NULL, + name TEXT, + description TEXT, + image TEXT, + primary_email TEXT, + birth_date TEXT, + age_in_years INTEGER, + current_address TEXT, + PRIMARY KEY (id) ); CREATE TABLE "Registry" ( - persons TEXT, - organizations TEXT, - PRIMARY KEY (persons, organizations) + persons TEXT, + organizations TEXT, + PRIMARY KEY (persons, organizations) ); CREATE TABLE "Relationship" ( - started_at_time DATE, - ended_at_time DATE, - related_to TEXT, - type TEXT, - PRIMARY KEY (started_at_time, ended_at_time, related_to, type) + started_at_time DATE, + ended_at_time DATE, + related_to TEXT, + type TEXT, + PRIMARY KEY (started_at_time, ended_at_time, related_to, type) ); CREATE TABLE "FamilialRelationship" ( - started_at_time DATE, - ended_at_time DATE, - related_to TEXT NOT NULL, - type VARCHAR(10) NOT NULL, - "Person_id" TEXT, - PRIMARY KEY (started_at_time, ended_at_time, related_to, type, "Person_id"), - FOREIGN KEY(related_to) REFERENCES "Person" (id), - FOREIGN KEY("Person_id") REFERENCES "Person" (id) + started_at_time DATE, + ended_at_time DATE, + related_to TEXT NOT NULL, + type VARCHAR(10) NOT NULL, + "Person_id" TEXT, + PRIMARY KEY (started_at_time, ended_at_time, related_to, type, "Person_id"), + FOREIGN KEY(related_to) REFERENCES "Person" (id), + FOREIGN KEY("Person_id") REFERENCES "Person" (id) ); CREATE TABLE "Organization_aliases" ( - backref_id TEXT, - aliases TEXT, - PRIMARY KEY (backref_id, aliases), - FOREIGN KEY(backref_id) REFERENCES "Organization" (id) + backref_id TEXT, + aliases TEXT, + PRIMARY KEY (backref_id, aliases), + FOREIGN KEY(backref_id) REFERENCES "Organization" (id) ); CREATE TABLE "Person_aliases" ( - backref_id TEXT, - aliases TEXT, - PRIMARY KEY (backref_id, aliases), - FOREIGN KEY(backref_id) REFERENCES "Person" (id) + backref_id TEXT, + aliases TEXT, + PRIMARY KEY (backref_id, aliases), + FOREIGN KEY(backref_id) REFERENCES "Person" (id) ); diff --git a/{{cookiecutter.project_name}}/src/data/examples/{{cookiecutter.main_schema_class}}-001.yaml b/{{cookiecutter.project_name}}/src/data/examples/{{cookiecutter.main_schema_class}}-001.yaml index 4b6f023..c22e612 100644 --- a/{{cookiecutter.project_name}}/src/data/examples/{{cookiecutter.main_schema_class}}-001.yaml +++ b/{{cookiecutter.project_name}}/src/data/examples/{{cookiecutter.main_schema_class}}-001.yaml @@ -1,4 +1,5 @@ # Example data object +--- id: example:{{cookiecutter.main_schema_class}}001 name: foo bar primary_email: foo.bar@example.com diff --git a/{{cookiecutter.project_name}}/src/docs/about.md b/{{cookiecutter.project_name}}/src/docs/about.md index 3e3e986..37f3107 100644 --- a/{{cookiecutter.project_name}}/src/docs/about.md +++ b/{{cookiecutter.project_name}}/src/docs/about.md @@ -1,5 +1,3 @@ # {{cookiecutter.project_name}} {{cookiecutter.project_description}} - - diff --git a/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/_version.py b/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/_version.py index f28d1ae..7edc9ee 100644 --- a/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/_version.py +++ b/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/_version.py @@ -4,4 +4,4 @@ __version__ = version(__name__) except PackageNotFoundError: # package not installed - __version__ = "0.0.0" \ No newline at end of file + __version__ = "0.0.0" diff --git a/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/datamodel/{{cookiecutter.__project_slug}}.py b/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/datamodel/{{cookiecutter.__project_slug}}.py index 8f28606..72489dc 100644 --- a/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/datamodel/{{cookiecutter.__project_slug}}.py +++ b/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/datamodel/{{cookiecutter.__project_slug}}.py @@ -7,22 +7,19 @@ # license: https://creativecommons.org/publicdomain/zero/1.0/ import dataclasses -import sys import re -from jsonasobj2 import JsonObj, as_dict +from jsonasobj2 import as_dict from typing import Optional, List, Union, Dict, ClassVar, Any from dataclasses import dataclass -from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue, PvFormulaOptions +from linkml_runtime.linkml_model.meta import EnumDefinition, PermissibleValue 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.metamodelcore import empty_list, empty_dict +from linkml_runtime.utils.yamlutils import YAMLRoot, extended_str 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 rdflib import URIRef from linkml_runtime.utils.curienamespace import CurieNamespace -from linkml_runtime.linkml_model.types import Boolean, Date, Integer, String from linkml_runtime.utils.metamodelcore import Bool, XSDDate metamodel_version = "1.7.0" @@ -32,18 +29,25 @@ 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/') -FAMREL = CurieNamespace('famrel', 'http://example.org/famrel/') -LINKML = CurieNamespace('linkml', 'https://w3id.org/linkml/') -MY_DATAMODEL = CurieNamespace('my_datamodel', 'https://w3id.org/my_org/my_datamodel') -PROV = CurieNamespace('prov', 'http://www.w3.org/ns/prov#') -SCHEMA = CurieNamespace('schema', 'http://schema.org/') +PATO = CurieNamespace('PATO', + 'http://purl.obolibrary.org/obo/PATO_') +BIOLINK = CurieNamespace('biolink', + 'https://w3id.org/biolink/') +FAMREL = CurieNamespace('famrel', + 'http://example.org/famrel/') +LINKML = CurieNamespace('linkml', + 'https://w3id.org/linkml/') +MY_DATAMODEL = CurieNamespace('my_datamodel', + 'https://w3id.org/my_org/my_datamodel') +PROV = CurieNamespace('prov', + 'http://www.w3.org/ns/prov#') +SCHEMA = CurieNamespace('schema', + 'http://schema.org/') DEFAULT_ = MY_DATAMODEL - # Types + # Class references class NamedThingId(extended_str): pass @@ -69,13 +73,30 @@ class Registry(YAMLRoot): class_name: ClassVar[str] = "Registry" class_model_uri: ClassVar[URIRef] = MY_DATAMODEL.Registry - persons: Optional[Union[Dict[Union[str, PersonId], Union[dict, "Person"]], List[Union[dict, "Person"]]]] = empty_dict() - organizations: Optional[Union[Dict[Union[str, OrganizationId], Union[dict, "Organization"]], List[Union[dict, "Organization"]]]] = empty_dict() + persons: Optional[Union[ + Dict[Union[str, PersonId], Union[dict, "Person"]], + List[Union[dict, "Person"]] + ]] = empty_dict() - def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): - self._normalize_inlined_as_list(slot_name="persons", slot_type=Person, key_name="id", keyed=True) + organizations: Optional[Union[ + Dict[Union[str, OrganizationId], Union[dict, "Organization"]], + List[Union[dict, "Organization"]] + ]] = empty_dict() - self._normalize_inlined_as_list(slot_name="organizations", slot_type=Organization, key_name="id", keyed=True) + def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): + self._normalize_inlined_as_list( + slot_name="persons", + slot_type=Person, + key_name="id", + keyed=True + ) + + self._normalize_inlined_as_list( + slot_name="organizations", + slot_type=Organization, + key_name="id", + keyed=True + ) super().__post_init__(**kwargs) @@ -106,7 +127,8 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): 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): + if self.description is not None \ + and not isinstance(self.description, str): self.description = str(self.description) if self.image is not None and not isinstance(self.image, str): @@ -132,7 +154,10 @@ class Person(NamedThing): birth_date: Optional[str] = None age_in_years: Optional[int] = None current_address: Optional[Union[dict, "Address"]] = None - has_familial_relationships: Optional[Union[Union[dict, "FamilialRelationship"], List[Union[dict, "FamilialRelationship"]]]] = empty_list() + has_familial_relationships: Optional[Union[ + Union[dict, "FamilialRelationship"], + List[Union[dict, "FamilialRelationship"]] + ]] = empty_list() aliases: Optional[Union[str, List[str]]] = empty_list() def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): @@ -141,25 +166,35 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): if not isinstance(self.id, PersonId): self.id = PersonId(self.id) - if self.primary_email is not None and not isinstance(self.primary_email, str): + 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, str): + if self.birth_date is not None \ + and not isinstance(self.birth_date, str): self.birth_date = str(self.birth_date) - if self.age_in_years is not None and not isinstance(self.age_in_years, int): + 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.current_address is not None and not isinstance(self.current_address, Address): + if self.current_address is not None \ + and not isinstance(self.current_address, Address): self.current_address = Address(**as_dict(self.current_address)) if not isinstance(self.has_familial_relationships, list): - self.has_familial_relationships = [self.has_familial_relationships] if self.has_familial_relationships is not None else [] - self.has_familial_relationships = [v if isinstance(v, FamilialRelationship) else FamilialRelationship(**as_dict(v)) for v in self.has_familial_relationships] + self.has_familial_relationships = [self.has_familial_relationships] \ + if self.has_familial_relationships is not None \ + else [] + self.has_familial_relationships = [ + v if isinstance(v, FamilialRelationship) + else FamilialRelationship(**as_dict(v)) + for v in self.has_familial_relationships] if not isinstance(self.aliases, list): self.aliases = [self.aliases] if self.aliases is not None else [] - self.aliases = [v if isinstance(v, str) else str(v) for v in self.aliases] + self.aliases = [v if isinstance(v, str) else str(v) + for v in self.aliases] super().__post_init__(**kwargs) @@ -181,7 +216,8 @@ class HasAliases(YAMLRoot): def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): if not isinstance(self.aliases, list): self.aliases = [self.aliases] if self.aliases is not None else [] - self.aliases = [v if isinstance(v, str) else str(v) for v in self.aliases] + self.aliases = [v if isinstance(v, str) else str(v) + for v in self.aliases] super().__post_init__(**kwargs) @@ -209,15 +245,18 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): if not isinstance(self.id, OrganizationId): self.id = OrganizationId(self.id) - if self.mission_statement is not None and not isinstance(self.mission_statement, str): + if self.mission_statement is not None \ + and not isinstance(self.mission_statement, str): self.mission_statement = str(self.mission_statement) - if self.founding_date is not None and not isinstance(self.founding_date, str): + if self.founding_date is not None \ + and not isinstance(self.founding_date, str): self.founding_date = str(self.founding_date) if not isinstance(self.aliases, list): self.aliases = [self.aliases] if self.aliases is not None else [] - self.aliases = [v if isinstance(v, str) else str(v) for v in self.aliases] + self.aliases = [v if isinstance(v, str) else str(v) + for v in self.aliases] super().__post_init__(**kwargs) @@ -242,7 +281,8 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): if self.city is not None and not isinstance(self.city, str): self.city = str(self.city) - if self.postal_code is not None and not isinstance(self.postal_code, str): + if self.postal_code is not None \ + and not isinstance(self.postal_code, str): self.postal_code = str(self.postal_code) super().__post_init__(**kwargs) @@ -263,13 +303,16 @@ class Relationship(YAMLRoot): type: Optional[str] = None def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): - if self.started_at_time is not None and not isinstance(self.started_at_time, XSDDate): + if self.started_at_time is not None \ + and not isinstance(self.started_at_time, XSDDate): self.started_at_time = XSDDate(self.started_at_time) - if self.ended_at_time is not None and not isinstance(self.ended_at_time, XSDDate): + if self.ended_at_time is not None \ + and not isinstance(self.ended_at_time, XSDDate): self.ended_at_time = XSDDate(self.ended_at_time) - if self.related_to is not None and not isinstance(self.related_to, str): + if self.related_to is not None \ + and not isinstance(self.related_to, str): self.related_to = str(self.related_to) if self.type is not None and not isinstance(self.type, str): @@ -308,113 +351,282 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]): class PersonStatus(EnumDefinitionImpl): ALIVE = PermissibleValue(text="ALIVE", - description="the person is living", - meaning=PATO["0001421"]) + description="the person is living", + meaning=PATO["0001421"]) DEAD = PermissibleValue(text="DEAD", - description="the person is deceased", - meaning=PATO["0001422"]) + description="the person is deceased", + meaning=PATO["0001422"]) UNKNOWN = PermissibleValue(text="UNKNOWN", - description="the vital status is not known") + description="the vital status is not known") _defn = EnumDefinition( name="PersonStatus", ) + class FamilialRelationshipType(EnumDefinitionImpl): SIBLING_OF = PermissibleValue(text="SIBLING_OF", - meaning=FAMREL["01"]) + meaning=FAMREL["01"]) PARENT_OF = PermissibleValue(text="PARENT_OF", - meaning=FAMREL["02"]) + meaning=FAMREL["02"]) CHILD_OF = PermissibleValue(text="CHILD_OF", - meaning=FAMREL["01"]) + meaning=FAMREL["01"]) _defn = EnumDefinition( name="FamilialRelationshipType", ) + # Slots class slots: pass -slots.id = Slot(uri=SCHEMA.identifier, name="id", curie=SCHEMA.curie('identifier'), - model_uri=MY_DATAMODEL.id, domain=None, range=URIRef) - -slots.name = Slot(uri=SCHEMA.name, name="name", curie=SCHEMA.curie('name'), - model_uri=MY_DATAMODEL.name, domain=None, range=Optional[str]) - -slots.description = Slot(uri=SCHEMA.description, name="description", curie=SCHEMA.curie('description'), - model_uri=MY_DATAMODEL.description, domain=None, range=Optional[str]) - -slots.image = Slot(uri=SCHEMA.image, name="image", curie=SCHEMA.curie('image'), - model_uri=MY_DATAMODEL.image, domain=None, range=Optional[str]) - -slots.primary_email = Slot(uri=SCHEMA.email, name="primary_email", curie=SCHEMA.curie('email'), - model_uri=MY_DATAMODEL.primary_email, domain=None, range=Optional[str]) - -slots.birth_date = Slot(uri=SCHEMA.birthDate, name="birth_date", curie=SCHEMA.curie('birthDate'), - model_uri=MY_DATAMODEL.birth_date, domain=None, range=Optional[str]) - -slots.employed_at = Slot(uri=MY_DATAMODEL.employed_at, name="employed_at", curie=MY_DATAMODEL.curie('employed_at'), - model_uri=MY_DATAMODEL.employed_at, domain=None, range=Optional[Union[str, OrganizationId]]) - -slots.is_current = Slot(uri=MY_DATAMODEL.is_current, name="is_current", curie=MY_DATAMODEL.curie('is_current'), - model_uri=MY_DATAMODEL.is_current, domain=None, range=Optional[Union[bool, Bool]]) - -slots.has_familial_relationships = Slot(uri=MY_DATAMODEL.has_familial_relationships, name="has_familial_relationships", curie=MY_DATAMODEL.curie('has_familial_relationships'), - model_uri=MY_DATAMODEL.has_familial_relationships, domain=None, range=Optional[Union[Union[dict, FamilialRelationship], List[Union[dict, FamilialRelationship]]]]) - -slots.current_address = Slot(uri=MY_DATAMODEL.current_address, name="current_address", curie=MY_DATAMODEL.curie('current_address'), - model_uri=MY_DATAMODEL.current_address, domain=None, range=Optional[Union[dict, Address]]) - -slots.age_in_years = Slot(uri=MY_DATAMODEL.age_in_years, name="age_in_years", curie=MY_DATAMODEL.curie('age_in_years'), - model_uri=MY_DATAMODEL.age_in_years, domain=None, range=Optional[int]) - -slots.related_to = Slot(uri=MY_DATAMODEL.related_to, name="related_to", curie=MY_DATAMODEL.curie('related_to'), - model_uri=MY_DATAMODEL.related_to, domain=None, range=Optional[str]) - -slots.type = Slot(uri=MY_DATAMODEL.type, name="type", curie=MY_DATAMODEL.curie('type'), - model_uri=MY_DATAMODEL.type, domain=None, range=Optional[str]) - -slots.street = Slot(uri=MY_DATAMODEL.street, name="street", curie=MY_DATAMODEL.curie('street'), - model_uri=MY_DATAMODEL.street, domain=None, range=Optional[str]) - -slots.city = Slot(uri=MY_DATAMODEL.city, name="city", curie=MY_DATAMODEL.curie('city'), - model_uri=MY_DATAMODEL.city, domain=None, range=Optional[str]) - -slots.mission_statement = Slot(uri=MY_DATAMODEL.mission_statement, name="mission_statement", curie=MY_DATAMODEL.curie('mission_statement'), - model_uri=MY_DATAMODEL.mission_statement, domain=None, range=Optional[str]) - -slots.founding_date = Slot(uri=MY_DATAMODEL.founding_date, name="founding_date", curie=MY_DATAMODEL.curie('founding_date'), - model_uri=MY_DATAMODEL.founding_date, domain=None, range=Optional[str]) - -slots.postal_code = Slot(uri=MY_DATAMODEL.postal_code, name="postal_code", curie=MY_DATAMODEL.curie('postal_code'), - model_uri=MY_DATAMODEL.postal_code, domain=None, range=Optional[str]) - -slots.started_at_time = Slot(uri=PROV.startedAtTime, name="started_at_time", curie=PROV.curie('startedAtTime'), - model_uri=MY_DATAMODEL.started_at_time, domain=None, range=Optional[Union[str, XSDDate]]) - -slots.ended_at_time = Slot(uri=PROV.endedAtTime, name="ended_at_time", curie=PROV.curie('endedAtTime'), - model_uri=MY_DATAMODEL.ended_at_time, domain=None, range=Optional[Union[str, XSDDate]]) - -slots.registry__persons = Slot(uri=MY_DATAMODEL.persons, name="registry__persons", curie=MY_DATAMODEL.curie('persons'), - model_uri=MY_DATAMODEL.registry__persons, domain=None, range=Optional[Union[Dict[Union[str, PersonId], Union[dict, Person]], List[Union[dict, Person]]]]) - -slots.registry__organizations = Slot(uri=MY_DATAMODEL.organizations, name="registry__organizations", curie=MY_DATAMODEL.curie('organizations'), - model_uri=MY_DATAMODEL.registry__organizations, domain=None, range=Optional[Union[Dict[Union[str, OrganizationId], Union[dict, Organization]], List[Union[dict, Organization]]]]) - -slots.hasAliases__aliases = Slot(uri=MY_DATAMODEL.aliases, name="hasAliases__aliases", curie=MY_DATAMODEL.curie('aliases'), - model_uri=MY_DATAMODEL.hasAliases__aliases, domain=None, range=Optional[Union[str, List[str]]]) - -slots.related_to = Slot(uri=MY_DATAMODEL.related_to, name="related to", curie=MY_DATAMODEL.curie('related_to'), - model_uri=MY_DATAMODEL.related_to, domain=None, range=Union[str, PersonId]) - -slots.Person_primary_email = Slot(uri=SCHEMA.email, name="Person_primary_email", curie=SCHEMA.curie('email'), - model_uri=MY_DATAMODEL.Person_primary_email, domain=Person, range=Optional[str], - pattern=re.compile(r'^\S+@[\S+\.]+\S+')) - -slots.FamilialRelationship_type = Slot(uri=MY_DATAMODEL.type, name="FamilialRelationship_type", curie=MY_DATAMODEL.curie('type'), - model_uri=MY_DATAMODEL.FamilialRelationship_type, domain=FamilialRelationship, range=Union[str, "FamilialRelationshipType"]) -slots.FamilialRelationship_related_to = Slot(uri=MY_DATAMODEL.related_to, name="FamilialRelationship_related to", curie=MY_DATAMODEL.curie('related_to'), - model_uri=MY_DATAMODEL.FamilialRelationship_related_to, domain=FamilialRelationship, range=Union[str, PersonId]) \ No newline at end of file +slots.id = Slot( + uri=SCHEMA.identifier, + name="id", + curie=SCHEMA.curie('identifier'), + model_uri=MY_DATAMODEL.id, + domain=None, + range=URIRef +) + +slots.name = Slot( + uri=SCHEMA.name, + name="name", + curie=SCHEMA.curie('name'), + model_uri=MY_DATAMODEL.name, + domain=None, + range=Optional[str] +) + +slots.description = Slot( + uri=SCHEMA.description, + name="description", + curie=SCHEMA.curie('description'), + model_uri=MY_DATAMODEL.description, + domain=None, + range=Optional[str] +) + +slots.image = Slot( + uri=SCHEMA.image, + name="image", + curie=SCHEMA.curie('image'), + model_uri=MY_DATAMODEL.image, + domain=None, + range=Optional[str] +) + +slots.primary_email = Slot( + uri=SCHEMA.email, + name="primary_email", + curie=SCHEMA.curie('email'), + model_uri=MY_DATAMODEL.primary_email, + domain=None, + range=Optional[str] +) + +slots.birth_date = Slot( + uri=SCHEMA.birthDate, + name="birth_date", + curie=SCHEMA.curie('birthDate'), + model_uri=MY_DATAMODEL.birth_date, + domain=None, + range=Optional[str] +) + +slots.employed_at = Slot( + uri=MY_DATAMODEL.employed_at, + name="employed_at", + curie=MY_DATAMODEL.curie('employed_at'), + model_uri=MY_DATAMODEL.employed_at, + domain=None, + range=Optional[Union[str, OrganizationId]] +) + +slots.is_current = Slot( + uri=MY_DATAMODEL.is_current, + name="is_current", + curie=MY_DATAMODEL.curie('is_current'), + model_uri=MY_DATAMODEL.is_current, + domain=None, + range=Optional[Union[bool, Bool]] +) + +slots.has_familial_relationships = Slot( + uri=MY_DATAMODEL.has_familial_relationships, + name="has_familial_relationships", + curie=MY_DATAMODEL.curie('has_familial_relationships'), + model_uri=MY_DATAMODEL.has_familial_relationships, + domain=None, + range=Optional[Union[ + Union[dict, FamilialRelationship], + List[Union[dict, FamilialRelationship]] + ]] +) + +slots.current_address = Slot( + uri=MY_DATAMODEL.current_address, name="current_address", + curie=MY_DATAMODEL.curie('current_address'), + model_uri=MY_DATAMODEL.current_address, domain=None, + range=Optional[Union[dict, Address]] +) + +slots.age_in_years = Slot( + uri=MY_DATAMODEL.age_in_years, name="age_in_years", + curie=MY_DATAMODEL.curie('age_in_years'), + model_uri=MY_DATAMODEL.age_in_years, domain=None, range=Optional[int] +) + +slots.related_to = Slot( + uri=MY_DATAMODEL.related_to, + name="related_to", + curie=MY_DATAMODEL.curie('related_to'), + model_uri=MY_DATAMODEL.related_to, + domain=None, + range=Optional[str] +) + +slots.type = Slot( + uri=MY_DATAMODEL.type, + name="type", + curie=MY_DATAMODEL.curie('type'), + model_uri=MY_DATAMODEL.type, + domain=None, + range=Optional[str] +) + +slots.street = Slot( + uri=MY_DATAMODEL.street, + name="street", + curie=MY_DATAMODEL.curie('street'), + model_uri=MY_DATAMODEL.street, + domain=None, + range=Optional[str] +) + +slots.city = Slot( + uri=MY_DATAMODEL.city, + name="city", + curie=MY_DATAMODEL.curie('city'), + model_uri=MY_DATAMODEL.city, + domain=None, + range=Optional[str] +) + +slots.mission_statement = Slot( + uri=MY_DATAMODEL.mission_statement, + name="mission_statement", + curie=MY_DATAMODEL.curie('mission_statement'), + model_uri=MY_DATAMODEL.mission_statement, + domain=None, + range=Optional[str] +) + +slots.founding_date = Slot( + uri=MY_DATAMODEL.founding_date, + name="founding_date", + curie=MY_DATAMODEL.curie('founding_date'), + model_uri=MY_DATAMODEL.founding_date, + domain=None, + range=Optional[str] +) + +slots.postal_code = Slot( + uri=MY_DATAMODEL.postal_code, + name="postal_code", + curie=MY_DATAMODEL.curie('postal_code'), + model_uri=MY_DATAMODEL.postal_code, + domain=None, + range=Optional[str] +) + +slots.started_at_time = Slot( + uri=PROV.startedAtTime, + name="started_at_time", + curie=PROV.curie('startedAtTime'), + model_uri=MY_DATAMODEL.started_at_time, + domain=None, + range=Optional[Union[str, XSDDate]] +) + +slots.ended_at_time = Slot( + uri=PROV.endedAtTime, + name="ended_at_time", + curie=PROV.curie('endedAtTime'), + model_uri=MY_DATAMODEL.ended_at_time, + domain=None, + range=Optional[Union[str, XSDDate]] +) + +slots.registry__persons = Slot( + uri=MY_DATAMODEL.persons, + name="registry__persons", + curie=MY_DATAMODEL.curie('persons'), + model_uri=MY_DATAMODEL.registry__persons, + domain=None, + range=Optional[Union[ + Dict[Union[str, PersonId], Union[dict, Person]], + List[Union[dict, Person]] + ]] +) + +slots.registry__organizations = Slot( + uri=MY_DATAMODEL.organizations, + name="registry__organizations", + curie=MY_DATAMODEL.curie('organizations'), + model_uri=MY_DATAMODEL.registry__organizations, + domain=None, + range=Optional[Union[ + Dict[Union[str, OrganizationId], Union[dict, Organization]], + List[Union[dict, Organization]] + ]] +) + +slots.hasAliases__aliases = Slot( + uri=MY_DATAMODEL.aliases, + name="hasAliases__aliases", + curie=MY_DATAMODEL.curie('aliases'), + model_uri=MY_DATAMODEL.hasAliases__aliases, + domain=None, + range=Optional[Union[str, List[str]]] +) + +slots.related_to = Slot( + uri=MY_DATAMODEL.related_to, + name="related to", + curie=MY_DATAMODEL.curie('related_to'), + model_uri=MY_DATAMODEL.related_to, + domain=None, + range=Union[str, PersonId] +) + +slots.Person_primary_email = Slot( + uri=SCHEMA.email, + name="Person_primary_email", + curie=SCHEMA.curie('email'), + model_uri=MY_DATAMODEL.Person_primary_email, + domain=Person, + range=Optional[str], + pattern=re.compile(r'^\S+@[\S+\.]+\S+') +) + +slots.FamilialRelationship_type = Slot( + uri=MY_DATAMODEL.type, + name="FamilialRelationship_type", + curie=MY_DATAMODEL.curie('type'), + model_uri=MY_DATAMODEL.FamilialRelationship_type, + domain=FamilialRelationship, + range=Union[str, "FamilialRelationshipType"] +) + +slots.FamilialRelationship_related_to = Slot( + uri=MY_DATAMODEL.related_to, + name="FamilialRelationship_related to", + curie=MY_DATAMODEL.curie('related_to'), + model_uri=MY_DATAMODEL.FamilialRelationship_related_to, + domain=FamilialRelationship, + range=Union[str, PersonId] +) diff --git a/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/schema/{{cookiecutter.__project_slug}}.yaml b/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/schema/{{cookiecutter.__project_slug}}.yaml index 0b95e33..c55ecc4 100644 --- a/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/schema/{{cookiecutter.__project_slug}}.yaml +++ b/{{cookiecutter.project_name}}/src/{{cookiecutter.__project_slug}}/schema/{{cookiecutter.__project_slug}}.yaml @@ -1,3 +1,4 @@ +--- id: https://w3id.org/{{cookiecutter.github_org}}/{{cookiecutter.project_name}} name: {{cookiecutter.project_name}} title: {{cookiecutter.project_name}} @@ -46,7 +47,7 @@ classes: slot_usage: primary_email: pattern: "^\\S+@[\\S+\\.]+\\S+" - + {{cookiecutter.main_schema_class}}Collection: tree_root: true description: >- @@ -82,13 +83,13 @@ slots: vital_status: range: PersonStatus description: living or dead status - + enums: PersonStatus: permissible_values: ALIVE: description: the person is living - meaning: PATO:0001421 + meaning: PATO:0001421 DEAD: description: the person is deceased meaning: PATO:0001422 diff --git a/{{cookiecutter.project_name}}/tests/__init__.py b/{{cookiecutter.project_name}}/tests/__init__.py index 504dd18..36120ba 100644 --- a/{{cookiecutter.project_name}}/tests/__init__.py +++ b/{{cookiecutter.project_name}}/tests/__init__.py @@ -1 +1 @@ -"""Tests for {{cookiecutter.project_name}}.""" \ No newline at end of file +"""Tests for {{cookiecutter.project_name}}.""" diff --git a/{{cookiecutter.project_name}}/tests/test_data.py b/{{cookiecutter.project_name}}/tests/test_data.py index 32b348d..3cf037e 100644 --- a/{{cookiecutter.project_name}}/tests/test_data.py +++ b/{{cookiecutter.project_name}}/tests/test_data.py @@ -11,6 +11,7 @@ EXAMPLE_FILES = glob.glob(os.path.join(DATA_DIR, '*.yaml')) + class TestData(unittest.TestCase): """Test data and datamodel.""" diff --git a/{{cookiecutter.project_name}}/utils/get-value.sh b/{{cookiecutter.project_name}}/utils/get-value.sh index eb6c875..88f3b67 100644 --- a/{{cookiecutter.project_name}}/utils/get-value.sh +++ b/{{cookiecutter.project_name}}/utils/get-value.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # get the value of a key in the about.yaml file # https://stackoverflow.com/questions/1221833/pipe-output-and-capture-exit-status-in-bash -grep ^$1: about.yaml | sed "s/$1:[[:space:]]//" ; test ${PIPESTATUS[0]} -eq 0 +grep "^$1:" about.yaml | sed "s/$1:[[:space:]]//" ; test "${PIPESTATUS[0]}" -eq 0