diff --git a/.cruft.json b/.cruft.json new file mode 100644 index 0000000..d8547ae --- /dev/null +++ b/.cruft.json @@ -0,0 +1,27 @@ +{ + "template": "https://github.com/Midnighter/cookiecutter-python-package", + "commit": "e8988f2331b6b12f2a78358fcbd78c013b1443c5", + "checkout": null, + "context": { + "cookiecutter": { + "full_name": "OpenTECR developers", + "email": "opentecr@googlegroups.com", + "dev_platform": "GitHub", + "__dev_platform_url": "https://github.com", + "dev_platform_username": "rgiessman", + "project_name": "OpenTECR Schema", + "project_slug": "opentecr-schema", + "project_module": "opentecr", + "project_short_description": "contains the code for the schema of the openTECR database", + "license": "MIT", + "release_date": "2023-12-12", + "year": "2023", + "_extensions": [ + "jinja2_time.TimeExtension", + "jinja2_strcase.StrcaseExtension" + ], + "_template": "https://github.com/Midnighter/cookiecutter-python-package" + } + }, + "directory": null +} diff --git a/.editorconfig b/.editorconfig index 711388e..179dfba 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,7 +10,7 @@ insert_final_newline = true trim_trailing_whitespace = true max_line_length = 88 -[*.{json,yml}] +[*.{json,yml,yaml}] indent_size = 2 [*.{md,rst}] diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..90272fe --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,130 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders 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, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. + diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..5554b1c --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,134 @@ +# Contributing + +Contributions are welcome, and they are greatly appreciated! Every little bit +helps, and credit will always be given. + +## Example Contributions + +You can contribute in many ways, for example: + +* [Report bugs](#report-bugs) +* [Fix Bugs](#fix-bugs) +* [Implement Features](#implement-features) +* [Write Documentation](#write-documentation) +* [Submit Feedback](#submit-feedback) + +### Report Bugs + +Report bugs at https://github.com/rgiessman/opentecr-schema/issues. + +**If you are reporting a bug, please follow the template guidelines. The more +detailed your report, the easier and thus faster we can help you.** + +### Fix Bugs + +Look through the GitHub issues for bugs. Anything labelled with `bug` and `help +wanted` is open to whoever wants to implement it. When you decide to work on +such an issue, please [assign yourself to +it](https://docs.github.com/en/issues/tracking-your-work-with-issues/assigning-issues-and-pull-requests-to-other-github-users) +and add a comment that you'll be working on that, too. If you see another issue +without the `help wanted` label, just post a comment, the maintainers are +usually happy for any support that they can get. + +### Implement Features + +Look through the GitHub issues for features. Anything labelled with +`enhancement` and `help wanted` is open to whoever wants to implement it. As for +[fixing bugs](#fix-bugs), please [assign yourself to the +issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/assigning-issues-and-pull-requests-to-other-github-users) +and add a comment that you'll be working on that, too. If another enhancement +catches your fancy, but it doesn't have the `help wanted` label, just post a +comment, the maintainers are usually happy for any support that they can get. + +### Write Documentation + +OpenTECR Schema could always use more documentation, whether as +part of the official documentation, in docstrings, or even on the web in blog +posts, articles, and such. Just [open an issue](https://github.com/rgiessman/opentecr-schema/issues) to let us know what you will be working on +so that we can provide you with guidance. + +### Submit Feedback + +The best way to send feedback is to file an issue at https://github.com/rgiessman/opentecr-schema/issues. If your feedback fits the format of one of +the issue templates, please use that. Remember that this is a volunteer-driven +project and everybody has limited time. + +## Get Started! + +Ready to contribute? Here's how to set up OpenTECR Schema for +local development. + +1. Fork the https://github.com/rgiessman/opentecr-schema + repository on GitHub. +2. Clone your fork locally + + ```shell + git clone git@github.com:your_name_here/opentecr-schema.git + ``` + +3. Install your local copy into a Python virtual environment. You can [read this + guide to learn + more](https://realpython.com/python-virtual-environments-a-primer) about them + and how to create one. Alternatively, particularly if you are a Windows or + Mac user, you can also use [Anaconda](https://docs.anaconda.com/anaconda/). + Once you have created a virtual environment and activated it, this is how you + set up your fork for local development + + ```shell + cd opentecr-schema + pip install -e '.[development]' + pre-commit install + ``` + + The commands above install the package with all of its normal and + development dependencies into your virtual environment. The package itself + is installed in editable mode (`-e`) such that any modifications that you + make are immediately reflected in the installed package. Furthermore, we use + pre-commit hooks to ensure consistent code formatting. They are installed + with the command above and will run when you try to `git commit` your + changes. + +4. Create a branch for local development using the `devel` branch as a starting + point. Use `fix` or `feat` as a prefix for your branch name. + + ```shell + git checkout devel + git checkout -b fix-name-of-your-bugfix + ``` + + Now you can make your changes locally. + +5. When you're done making changes, apply the quality assurance tools and check + that your changes pass our test suite. This is all included with tox + + ```shell + tox + ``` + + You can also run tox in parallel to speed this up. + + ```shell + tox -p auto + ``` + +6. Commit your changes and push your branch to GitHub. Please use [semantic + commit messages](https://www.conventionalcommits.org/). + + ```shell + git add . + git commit -m "fix: summarize your changes" + git push origin fix-name-of-your-bugfix + ``` + +7. Open the link displayed in the message when pushing your new branch in order + to submit a pull request. + +### Pull Request Guidelines + +Before you submit a pull request, check that it meets these guidelines: + +1. The pull request should include tests. +2. If the pull request adds functionality, the docs should be updated. Put your + new functionality into a function with a docstring. +3. Your pull request will automatically be checked by the full tox test suite. + It needs to pass all of them before it can be considered for merging. diff --git a/.github/ISSUE_TEMPLATE/01-bug-report.yml b/.github/ISSUE_TEMPLATE/01-bug-report.yml new file mode 100644 index 0000000..a1e6f38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01-bug-report.yml @@ -0,0 +1,64 @@ +name: 🐞 Bug report +description: Report a problem to help improve this project +title: "[BUG] " +labels: [bug, triage] +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues + required: true + - type: textarea + attributes: + label: Problem description + description: | + A concise description of what you're experiencing. + + Please explain: + + * **what** you tried to achieve, + * **how** you went about it (referring to the code sample), and + * **why** the current behaviour is a problem and what output you expected instead. + validations: + required: false + - type: textarea + attributes: + label: Code sample + description: > + Create a [minimal, complete, verifiable example](https://stackoverflow.com/help/mcve). + Please, paste your code between the ``` tickmarks below or link to a [gist](https://gist.github.com/). + value: | + Code run: + + ```python + ``` + + Traceback: + + ```text + ``` + validations: + required: false + - type: textarea + attributes: + label: Environment + description: > + Please paste the output of running `depinfo --markdown opentecr-schema` + in your environment between the `details` tags below. + value: | +
+ +
+ validations: + required: true + - type: textarea + attributes: + label: Anything else? + description: | + Links? References? Anything that will give us more context about the issue you are encountering! + + Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/02-question.yml b/.github/ISSUE_TEMPLATE/02-question.yml new file mode 100644 index 0000000..aa82060 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02-question.yml @@ -0,0 +1,26 @@ +name: Question +description: Ask a question +title: "[Question] " +labels: [question] +body: + - type: checkboxes + attributes: + label: Checklist + description: > + To help keep this issue tracker clean and focused, please make sure that you have + tried *all* of the following resources before submitting your question. + options: + - label: I searched the [documentation](https://opentecr-schema.readthedocs.io). + required: true + - label: I looked through existing [discussion topics](https://github.com/rgiessman/opentecr-schema/discussions). + required: true + - label: I looked for [similar issues](https://github.com/rgiessman/opentecr-schema/issues). + required: true + - label: I looked up my question/problem in a search engine. + required: true + - type: textarea + attributes: + label: Question + description: Please ask your question + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/03-feature-request.yml b/.github/ISSUE_TEMPLATE/03-feature-request.yml new file mode 100644 index 0000000..7fcd56a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03-feature-request.yml @@ -0,0 +1,45 @@ +name: Feature request +description: Suggest an idea for this project +title: "[Feature] " +labels: [enhancement] +body: + - type: checkboxes + attributes: + label: Checklist + description: > + Please make sure you check all these items before submitting your feature request. + options: + - label: There are [no similar issues or pull requests](https://github.com/rgiessman/opentecr-schema/issues) for this yet. + required: true + - type: textarea + attributes: + label: Problem + description: > + A clear and concise description of what you are trying to achieve. + placeholder: > + "I want to be able to [...] but I can't because [...]". + validations: + required: false + - type: textarea + attributes: + label: Solution + description: > + A clear and concise description of what you would want to happen. + For API changes, try to provide a code snippet of what you would like the new API to look like. + validations: + required: false + - type: textarea + attributes: + label: Alternatives + description: > + Please describe any alternative solutions or features you've considered to solve + your problem and why they didn't help. + validations: + required: false + - type: textarea + attributes: + label: Anything else? + description: > + Provide any additional context, screenshots, tracebacks, etc. about the feature here. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..0b2042f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Community Support + url: https://github.com/rgiessman/opentecr-schema/discussions + about: Please ask and answer questions here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..81900b0 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,4 @@ +* [ ] fix #(issue number) +* [ ] description of feature/fix +* [ ] tests added/passed +* [ ] add an entry to the [changelog](../CHANGELOG.md) diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000..fc1a74f --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,4 @@ +# Support + +* Email the authors or maintainers +* [opentecr-schema Discussions](https://github.com/rgiessman/opentecr-schema/discussions) diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml new file mode 100644 index 0000000..15341ac --- /dev/null +++ b/.github/workflows/cron.yml @@ -0,0 +1,32 @@ +name: Cron Test + +on: + schedule: + # Run every Tuesday at 11:00. + - cron: '0 11 * * 2' + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ['3.8', '3.9', '3.10', '3.11'] + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + python -m pip install tox tox-gh-actions + + - name: Test with tox + run: tox run-parallel --parallel auto --parallel-no-spinner + diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..f88e824 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,88 @@ +name: CI-CD + +on: + push: + branches: + - main + - devel + tags: + - '[0-9]+.[0-9]+.[0-9]+' + - '[0-9]+.[0-9]+.[0-9]+(a|b|rc|post|dev)[0-9]+' + pull_request: + branches: + - main + - devel + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ['3.11', '3.12'] + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + python -m pip install tox tox-gh-actions + + - name: Test with tox + run: tox run-parallel --parallel auto --parallel-no-spinner -- --cov-report=xml + + - name: Report coverage + shell: bash + run: bash <(curl -s https://codecov.io/bash) + + release: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + python-version: ['3.11'] + needs: test + if: github.ref_type == 'tag' + permissions: + contents: write + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + python -m pip install hatch + + - name: Build package + run: hatch build + + - name: Publish to PyPI + env: + HATCH_INDEX_USER: ${{ secrets.PYPI_USERNAME }} + HATCH_INDEX_AUTH: ${{ secrets.PYPI_PASSWORD }} + run: + hatch publish --no-prompt + + - name: GH release + uses: softprops/action-gh-release@v1 + with: + body: > + Please see + https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/CHANGELOG.md + for the full release notes. + draft: false + prerelease: false + diff --git a/.gitignore b/.gitignore index 1123d83..d5e55fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,45 +1,4 @@ -### macOS template -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### JupyterNotebooks template -# gitignore template for Jupyter Notebooks -# website: http://jupyter.org/ - -.ipynb_checkpoints -*/.ipynb_checkpoints/* - -# IPython -profile_default/ -ipython_config.py - -# Remove previous ipynb_checkpoints -# git rm -r .ipynb_checkpoints/ - +# Created by .ignore support plugin (hsz.mobi) ### Python template # Byte-compiled / optimized / DLL files __pycache__/ @@ -63,6 +22,7 @@ parts/ sdist/ var/ wheels/ +pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg @@ -89,10 +49,8 @@ htmlcov/ nosetests.xml coverage.xml *.cover -*.py,cover .hypothesis/ .pytest_cache/ -cover/ # Translations *.mo @@ -102,7 +60,6 @@ cover/ *.log local_settings.py db.sqlite3 -db.sqlite3-journal # Flask stuff: instance/ @@ -115,17 +72,17 @@ instance/ docs/_build/ # PyBuilder -.pybuilder/ target/ # Jupyter Notebook +.ipynb_checkpoints # IPython +profile_default/ +ipython_config.py # pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. @@ -134,12 +91,8 @@ target/ # install all needed dependencies. #Pipfile.lock -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff +# celery beat schedule file celerybeat-schedule -celerybeat.pid # SageMath parsed files *.sage.py @@ -171,11 +124,10 @@ dmypy.json # Pyre type checker .pyre/ -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ +### TextMate template +*.tmproj +*.tmproject +tmtags ### Windows template # Windows thumbnail cache files @@ -203,8 +155,530 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -### Linux template +### JupyterNotebooks template +# gitignore template for Jupyter Notebooks +# website: http://jupyter.org/ + +*/.ipynb_checkpoints/* + +# IPython + +# Remove previous ipynb_checkpoints +# git rm -r .ipynb_checkpoints/ + +### Vim template +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist *~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### VirtualEnv template +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +pip-selfcheck.json + +### Dropbox template +# Dropbox settings and caches +.dropbox +.dropbox.attr +.dropbox.cache + +### VisualStudioCode template +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +### VisualStudio template +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python template +# Byte-compiled / optimized / DLL files + +# C extensions + +# Distribution / packaging + +# 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. + +# Installer logs + +# Unit test / coverage reports +*.py,cover +cover/ + +# Translations + +# Django stuff: +db.sqlite3-journal + +# Flask stuff: + +# Scrapy stuff: + +# Sphinx documentation + +# PyBuilder +.pybuilder/ + +# Jupyter Notebook + +# IPython + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .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.pid + +# SageMath parsed files + +# Environments + +# Spyder project settings + +# Rope project settings + +# mkdocs documentation + +# mypy + +# Pyre type checker + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +### Linux template # temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden* @@ -218,3 +692,112 @@ $RECYCLE.BIN/ # .nfs files are created when an open file is removed but is still being accessed .nfs* +### SublimeText template +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json +sftp-config-alt*.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# Project +/.ruff_cache/ +/src/opentecr/_version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..f95ccf7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,15 @@ +default_language_version: + python: python3.11 +repos: + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + - repo: https://github.com/psf/black + rev: 23.1.0 + hooks: + - id: black + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.7.1 + hooks: + - id: prettier diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..b877fec --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,18 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.10" + +mkdocs: + configuration: mkdocs.yml + +python: + install: + - method: pip + path: . + extra_requirements: + - dev diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f2fa781 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +* Upcoming features and fixes + +## [0.1.0] - (2023-12-12) + +* First release diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4b4742d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 OpenTECR developers + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile deleted file mode 100644 index 9baa479..0000000 --- a/Makefile +++ /dev/null @@ -1,72 +0,0 @@ -################################################################################ -# COMMANDS # -################################################################################ - -.PHONY: qa -## Apply code quality assurance tools. -qa: - isort src/opentecr scripts/ - black src/opentecr scripts/ - -################################################################################ -# Self Documenting Commands # -################################################################################ - -.DEFAULT_GOAL := show-help - -# Inspired by -# -# sed script explained: -# /^##/: -# * save line in hold space -# * purge line -# * Loop: -# * append newline + line to hold space -# * go to next line -# * if line starts with doc comment, strip comment character off and loop -# * remove target prerequisites -# * append hold space (+ newline) to line -# * replace newline plus comments by `---` -# * print line -# Separate expressions are necessary because labels cannot be delimited by -# semicolon; see -.PHONY: show-help -show-help: - @echo "$$(tput bold)Available rules:$$(tput sgr0)" - @echo - @sed -n -e "/^## / { \ - h; \ - s/.*//; \ - :doc" \ - -e "H; \ - n; \ - s/^## //; \ - t doc" \ - -e "s/:.*//; \ - G; \ - s/\\n## /---/; \ - s/\\n/ /g; \ - p; \ - }" ${MAKEFILE_LIST} \ - | LC_ALL='C' sort --ignore-case \ - | awk -F '---' \ - -v ncol=$$(tput cols) \ - -v indent=19 \ - -v col_on="$$(tput setaf 6)" \ - -v col_off="$$(tput sgr0)" \ - '{ \ - printf "%s%*s%s ", col_on, -indent, $$1, col_off; \ - n = split($$2, words, " "); \ - line_length = ncol - indent; \ - for (i = 1; i <= n; i++) { \ - line_length -= length(words[i]) + 1; \ - if (line_length <= 0) { \ - line_length = ncol - indent - length(words[i]) - 1; \ - printf "\n%*s ", -indent, " "; \ - } \ - printf "%s ", words[i]; \ - } \ - printf "\n"; \ - }' \ - | more $(shell test $(shell uname) = Darwin && \ - echo '--no-init --raw-control-chars') diff --git a/README.md b/README.md new file mode 100644 index 0000000..0e745bd --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# OpenTECR Schema + +| | | +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Package | [![Latest PyPI Version](https://img.shields.io/pypi/v/opentecr-schema.svg)](https://pypi.org/project/opentecr-schema/) [![Supported Python Versions](https://img.shields.io/pypi/pyversions/opentecr-schema.svg)](https://pypi.org/project/opentecr-schema/) [![Documentation](https://readthedocs.org/projects/opentecr-schema/badge/?version=latest)](https://opentecr-schema.readthedocs.io/en/latest/?badge=latest) | +| Meta | [![MIT](https://img.shields.io/pypi/l/opentecr-schema.svg)](LICENSE) [![Code of Conduct](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](.github/CODE_OF_CONDUCT.md) [![Code Style Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) | +| Automation | [![GitHub Workflow](https://github.com/rgiessman/opentecr-schema/workflows/CI-CD/badge.svg)](https://github.com/rgiessman/opentecr-schema/workflows/CI-CD) [![Code Coverage](https://codecov.io/gh/rgiessman/opentecr-schema/branch/master/graph/badge.svg)](https://codecov.io/gh/rgiessman/opentecr-schema) | + +contains the code for the schema of the openTECR database + +## Post Template-Instantiation Steps + +1. Start working with git. + + ```shell + git init + ``` + +2. Install the git pre-commit hooks using the `pre-commit` tool. + + ```shell + pip install pre-commit + pre-commit install + ``` + +3. Commit all the files. + + ```shell + git add . + git commit -m "chore: initialize project cookiecutter" + ``` + +4. Create a repository on [GitHub](https://github.com) if you haven't done + so yet. +5. Browse through the architecture decision records (`docs/adr`) if you want + to understand details of the package design. +6. Remove this section from the readme and describe what your package is all + about. +7. When you're ready to make a release, perform the following steps. + + 1. On [GitHub](https://github.com) set the secure environment + variables `PYPI_USERNAME` and `PYPI_PASSWORD` to `__token__` and a respective PyPI API token. + 2. Tag your latest commit with the desired version and let GitHub handle + the release. + + ```shell + git tag 0.1.0 + git push origin 0.1.0 + ``` + +## Copyright + +* Copyright © 2023 OpenTECR developers. +* Free software distributed under the [MIT License](../LICENSE). + diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b143302 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,9 @@ +# OpenTECR Schema + +contains the code for the schema of the openTECR database + +## Copyright + +* Copyright © 2023 OpenTECR developers. +* Free software distributed under the [MIT License](../LICENSE). + diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..3ee6ff8 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,68 @@ +site_name: OpenTECR Schema +site_description: contains the code for the schema of the openTECR database +site_author: OpenTECR developers +site_url: https://opentecr-schema.readthedocs.io/ + +markdown_extensions: + - admonition + - toc: + permalink: true + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.superfences: + - pymdownx.emoji: + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg + +theme: + features: + - content.code.annotate + - navigation.indexes + - navigation.instant + - navigation.tracking + - navigation.top + name: material + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: green + accent: light green + toggle: + icon: material/weather-night + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: green + accent: light green + toggle: + icon: material/weather-sunny + name: Switch to light mode + +plugins: + - search + - mkdocstrings: + handlers: + python: + paths: [src] + options: + show_submodules: true + show_category_heading: true + show_if_no_docstring: true + show_signature_annotations: true + default_handler: python + - awesome-pages + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/rgiessman + name: OpenTECR developers + +copyright: Copyright © 2023 OpenTECR developers + +repo_url: https://github.com/rgiessman/opentecr-schema +repo_name: rgiessman/opentecr-schema + +watch: + - src/opentecr diff --git a/output/openTECR.json b/output/openTECR.json index 5f95f23..ee1ea53 100644 --- a/output/openTECR.json +++ b/output/openTECR.json @@ -1,191 +1,273 @@ { - "title": "openTECR Data Model", - "definitions": { - "LiteratureReference": { - "title": "LiteratureReference", - "description": "Define a customized base model.", - "type": "object", + "$defs": { + "Annotation": { + "description": "A class representing an annotation.", "properties": { - "pubmedId": { - "title": "PubMed Identifier", - "type": "string" - }, - "doi": { - "title": "Digital Object Identifier (DOI)", - "type": "string" - }, - "url": { - "title": "Universal Resource Locator (URL)", - "type": "string" - }, - "title": { - "title": "Title", + "namespace": { + "description": "The MIRIAM compliant prefix of the identifier namespace. MUST be registered at https://identifiers.org/.", + "title": "Namespace", "type": "string" }, - "authors": { - "title": "Authors", + "identifier": { + "description": "The actual identifier within the namespace.", + "title": "Identifier", "type": "string" - }, - "year": { - "title": "Year", - "type": "integer" } }, "required": [ - "title", - "authors", - "year" - ] + "namespace", + "identifier" + ], + "title": "Annotation", + "type": "object" }, "Contributor": { - "title": "Contributor", - "description": "Define a customized base model.", - "type": "object", + "description": "Data about a person who contributes to OpenTECR.", "properties": { "orcid": { - "title": "ORCID", "description": "The ORCID is a nonproprietary alphanumeric code to uniquely identify authors and contributors of scholarly communication.", + "title": "ORCID", "type": "string" }, "name": { - "title": "Name", "description": "The contributor's full name.", + "title": "Name", "type": "string" } }, "required": [ "orcid", "name" - ] + ], + "title": "Contributor", + "type": "object" }, - "Annotation": { - "title": "Annotation", - "description": "Define a customized base model.", - "type": "object", + "LiteratureReference": { + "description": "Information about a literature reference.", "properties": { - "namespace": { - "title": "Namespace", - "description": "The MIRIAM compliant prefix of the identifier namespace. MUST be registered at https://identifiers.org/.", + "pubmedId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "PubMed Identifier" + }, + "doi": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Digital Object Identifier (DOI)" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Universal Resource Locator (URL)" + }, + "title": { + "title": "Title", "type": "string" }, - "identifier": { - "title": "Identifier", - "description": "The actual identifier within the namespace.", + "authors": { + "title": "Authors", "type": "string" + }, + "year": { + "title": "Year", + "type": "integer" } }, "required": [ - "namespace", - "identifier" - ] + "title", + "authors", + "year" + ], + "title": "LiteratureReference", + "type": "object" }, "RepresentativeReaction": { - "title": "RepresentativeReaction", - "type": "object", + "description": "Information about a reaction.", "properties": { "rinchi": { - "title": "RInChI", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, "description": "Reaction IUPAC international chemical identifier.", - "type": "string" + "title": "RInChI" }, "rinchiKey": { - "title": "RInChIKey", + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, "description": "Hashed version of the reaction IUPAC international chemical identifier.", - "type": "string" + "title": "RInChIKey" }, "names": { - "title": "Common Names", - "description": "Common names or synonyms for this reaction mostly to further human understanding.", "default": [], - "type": "array", + "description": "Common names or synonyms for this reaction mostly to further human understanding.", "items": { "type": "string" - } + }, + "title": "Common Names", + "type": "array" }, "annotation": { - "title": "Annotation", - "description": "Cross-references for the reaction.", "default": [], - "type": "array", + "description": "Cross-references for the reaction.", "items": { - "$ref": "#/definitions/Annotation" - } + "$ref": "#/$defs/Annotation" + }, + "title": "Annotation", + "type": "array" }, "stoichiometry": { - "title": "Stoichiometry", - "description": "A dictionary of the stoichiometric coefficients and metabolitesthat define this reaction. Includes both reactants (negative coefficients) and products (positive coefficients).", - "type": "object", "additionalProperties": { "type": "number" - } + }, + "description": "A dictionary of the stoichiometric coefficients and metabolitesthat define this reaction. Includes both reactants (negative coefficients) and products (positive coefficients).", + "title": "Stoichiometry", + "type": "object" } }, "required": [ "stoichiometry" - ] + ], + "title": "RepresentativeReaction", + "type": "object" + } + }, + "description": "Define a curated measurement of thermodynamic information and context.", + "properties": { + "reference": { + "allOf": [ + { + "$ref": "#/$defs/LiteratureReference" + } + ], + "description": "The official source from which these measurements were curated. Ideally identified by a DOI.", + "title": "Literature Reference" }, - "CuratedMeasurement": { - "title": "CuratedMeasurement", - "description": "Define a curated measurement of thermodynamic information and context.", - "type": "object", - "properties": { - "reference": { - "title": "Literature Reference", - "description": "The official source from which these measurements were curated. Ideally identified by a DOI.", - "allOf": [ - { - "$ref": "#/definitions/LiteratureReference" - } - ] - }, - "curator": { - "title": "Contributor", - "description": "The person who contributed these measurements.", - "allOf": [ - { - "$ref": "#/definitions/Contributor" - } - ] - }, - "representative_reaction": { - "title": "Representative Reaction", - "description": "The representative biochemical reaction to which the measurements apply.", - "allOf": [ - { - "$ref": "#/definitions/RepresentativeReaction" - } - ] + "curator": { + "anyOf": [ + { + "$ref": "#/$defs/Contributor" }, - "equilibriumConstant": { - "title": "Equilibrium Constant", - "description": "The equilibrium constant ($K$) of this reaction.", + { + "type": "null" + } + ], + "default": null, + "description": "The person who contributed these measurements.", + "title": "Contributor" + }, + "representative_reaction": { + "allOf": [ + { + "$ref": "#/$defs/RepresentativeReaction" + } + ], + "description": "The representative biochemical reaction to which the measurements apply.", + "title": "Representative Reaction" + }, + "equilibriumConstant": { + "anyOf": [ + { "type": "number" }, - "hydrogenPotential": { - "title": "Potential of Hydrogen", - "description": "The potential of hydrogen (pH) at which the measurements were performed.", + { + "type": "null" + } + ], + "default": null, + "description": "The equilibrium constant ($K$) of this reaction.", + "title": "Equilibrium Constant" + }, + "hydrogenPotential": { + "anyOf": [ + { "type": "number" }, - "magnesiumPotential": { - "title": "Potential of Magnesium", - "description": "The potential of magnesium (pMg) at which the measurements were performed.", + { + "type": "null" + } + ], + "default": null, + "description": "The potential of hydrogen (pH) at which the measurements were performed.", + "title": "Potential of Hydrogen" + }, + "magnesiumPotential": { + "anyOf": [ + { "type": "number" }, - "temperature": { - "title": "Temperature", - "description": "The temperature in Kelvin (K) at which the measurements were performed.", + { + "type": "null" + } + ], + "default": null, + "description": "The potential of magnesium (pMg) at which the measurements were performed.", + "title": "Potential of Magnesium" + }, + "temperature": { + "anyOf": [ + { "type": "number" }, - "ionicStrength": { - "title": "Ionic Strength", - "description": "The molar ionic strength ($I$) of the solution in which the reaction occurred.", + { + "type": "null" + } + ], + "default": null, + "description": "The temperature in Kelvin (K) at which the measurements were performed.", + "title": "Temperature" + }, + "ionicStrength": { + "anyOf": [ + { "type": "number" + }, + { + "type": "null" } - }, - "required": [ - "reference", - "representative_reaction" - ] + ], + "default": null, + "description": "The molar ionic strength ($I$) of the solution in which the reaction occurred.", + "title": "Ionic Strength" } - } + }, + "required": [ + "reference", + "representative_reaction" + ], + "title": "CuratedMeasurement", + "type": "object" } \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 3d8c854..edb1cd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,133 @@ +################################################################################ +# Build Configuration +################################################################################ + [build-system] -build-backend = 'setuptools.build_meta' -requires = [ - 'setuptools>=40.6.0', - 'versioneer-518', - 'wheel' +build-backend = "hatchling.build" +requires = ["hatchling", "hatch-vcs"] + +################################################################################ +# Project Configuration +################################################################################ + +[project] +name = "opentecr-schema" +description = "contains the code for the schema of the openTECR database" +authors = [ + { name = "OpenTECR developers", email = "opentecr@googlegroups.com" } +] +maintainers = [ + { name = "OpenTECR developers", email = "opentecr@googlegroups.com" } ] +license = "MIT" +readme = {"file" = "README.md", "content-type" = "text/markdown"} +requires-python = ">=3.11" +# Please consult https://pypi.org/classifiers/ for a full list. +classifiers = [ + "Development Status :: 2 - Pre-Alpha", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: Bio-Informatics" +] +# TODO: add keywords +keywords = [] +dynamic = ["version"] +dependencies = [ + "depinfo ~=2.2", + "pydantic >= 2.0.0", +] + +[project.urls] +Homepage = "https://github.com/rgiessman/opentecr-schema" +Documentation = "https://opentecr-schema.readthedocs.io" +"Source Code" = "https://github.com/rgiessman/opentecr-schema" +"Bug Tracker" = "https://github.com/rgiessman/opentecr-schema/issues" +Download = "https://pypi.org/project/opentecr-schema/#files" + +[project.optional-dependencies] +dev = [ + "mkdocs-material ~=9.1", + "mkdocstrings[python] ~=0.20", + "mkdocs-awesome-pages-plugin ~=2.8", + "pre-commit", + "tox ~=4.0", +] + +################################################################################ +# Tool Configuration +################################################################################ + +[tool.hatch.build] +only-packages = true + +[tool.hatch.build.targets.wheel] +packages = ["src/opentecr"] + +[tool.hatch.build.hooks.vcs] +version-file = "src/opentecr/_version.py" + +[tool.hatch.version] +source = "vcs" [tool.black] -python-version = ['py38'] +line-length = 88 +python-version = ["py311"] +extend-exclude = ''' +( + _version.py +) +''' + +[tool.isort] +profile = "black" +lines_after_imports = 2 +lines_before_imports = 2 +known_first_party = "opentecr" +skip = ["__init__.py"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +markers = ["raises"] + +[tool.coverage.paths] +source = [ + "src/opentecr", + "*/site-packages/opentecr" +] + +[tool.coverage.run] +branch = true +parallel = true + +[tool.coverage.report] +exclude_lines = ["pragma: no cover"] +precision = 2 + +[tool.ruff] +line-length = 88 +select = ["ALL"] +ignore = [ + "TD", # ignore flake8-todo rules + "FIX", # ignore flake8-fixme rules + "D203", # we want no blank line before class docstrings + "D213", # multiline docstrings should start just after the quotemarks + #"FA100", # pydantic/__future__ problem: see https://github.com/astral-sh/ruff/issues/5434 +] + +[tool.ruff.extend-per-file-ignores] +"test_*.py" = ["S101"] + +[tool.mypy] +plugins = [ + "pydantic.mypy" +] + +[tool.pydantic-mypy] +init_forbid_extra = true +init_typed = true +warn_required_dynamic_aliases = true diff --git a/requirements.in b/requirements.in deleted file mode 100644 index 70c994b..0000000 --- a/requirements.in +++ /dev/null @@ -1,2 +0,0 @@ -pydantic ~=1.8 -sqlalchemy ~=1.4 diff --git a/scripts/json_schema.py b/scripts/json_schema.py index f91953d..3012fde 100644 --- a/scripts/json_schema.py +++ b/scripts/json_schema.py @@ -1,22 +1,23 @@ +"""Write the CuratedMeasurement schema to a json file.""" + import json +import logging import sys from pathlib import Path -from pydantic.schema import schema - +from opentecr.data import CuratedMeasurement project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root / "src")) -from opentecr.data import CuratedMeasurement - -def main(): +def main() -> None: + """Run the script.""" + logger = logging.getLogger() + logger.setLevel(logging.INFO) result_path = project_root / "output" / "openTECR.json" - result = json.dumps( - schema([CuratedMeasurement], title="openTECR Data Model"), indent=2 - ) - print(result) + result = json.dumps(CuratedMeasurement.model_json_schema(), indent=2) + logging.info(result) with result_path.open("w") as handle: handle.write(result) diff --git a/scripts/sql_schema.py b/scripts/sql_schema.py deleted file mode 100644 index 5695e78..0000000 --- a/scripts/sql_schema.py +++ /dev/null @@ -1,19 +0,0 @@ -import sys -from pathlib import Path - -from sqlalchemy import create_engine - - -project_root = Path(__file__).parent.parent -sys.path.insert(0, str(project_root / "src")) - -from opentecr.orm import Base - - -def main(): - db_path = project_root / "output" / "openTECR.sqlite" - Base.metadata.create_all(create_engine(f"sqlite:///{db_path}")) - - -if __name__ == "__main__": - main() diff --git a/src/opentecr/__init__.py b/src/opentecr/__init__.py index e69de29..c785f74 100644 --- a/src/opentecr/__init__.py +++ b/src/opentecr/__init__.py @@ -0,0 +1,25 @@ +# MIT License +# +# Copyright (c) 2023 OpenTECR developers +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +"""Provide top level symbols.""" diff --git a/src/opentecr/data/__init__.py b/src/opentecr/data/__init__.py index 395b282..fe62927 100644 --- a/src/opentecr/data/__init__.py +++ b/src/opentecr/data/__init__.py @@ -1,6 +1,42 @@ +# MIT License +# +# Copyright (c) 2023 OpenTECR developers +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +"""Provide top level symbols.""" + + from .annotation import Annotation from .contributor import Contributor +from .curated_measurement import CuratedMeasurement from .literature_reference import LiteratureReference from .metabolite import Metabolite from .representative_reaction import RepresentativeReaction -from .curated_measurement import CuratedMeasurement + +__all__ = [ + "Annotation", + "Contributor", + "LiteratureReference", + "Metabolite", + "RepresentativeReaction", + "CuratedMeasurement", +] diff --git a/src/opentecr/data/annotation.py b/src/opentecr/data/annotation.py index 41031c0..9bf3c0e 100644 --- a/src/opentecr/data/annotation.py +++ b/src/opentecr/data/annotation.py @@ -1,9 +1,12 @@ +"""Provides dataclass Annotation.""" + from pydantic import Field from .base import Base class Annotation(Base): + """A class representing an annotation.""" namespace: str = Field( ..., @@ -11,6 +14,7 @@ class Annotation(Base): "registered at https://identifiers.org/.", ) identifier: str = Field( - ..., description="The actual identifier within the namespace." + ..., + description="The actual identifier within the namespace.", ) # FIXME: add a qualifier field? diff --git a/src/opentecr/data/base.py b/src/opentecr/data/base.py index 43f518b..7c7fdc1 100644 --- a/src/opentecr/data/base.py +++ b/src/opentecr/data/base.py @@ -1,3 +1,5 @@ +"""Provides class Base specifying configuration that is used in all models.""" + from pydantic import BaseModel diff --git a/src/opentecr/data/contributor.py b/src/opentecr/data/contributor.py index 5a4281e..daadd83 100644 --- a/src/opentecr/data/contributor.py +++ b/src/opentecr/data/contributor.py @@ -1,9 +1,13 @@ +"""Provides data class Contributor.""" + + from pydantic import Field from .base import Base class Contributor(Base): + """Data about a person who contributes to OpenTECR.""" orcid: str = Field( ..., diff --git a/src/opentecr/data/curated_measurement.py b/src/opentecr/data/curated_measurement.py index 317f052..1c276e7 100644 --- a/src/opentecr/data/curated_measurement.py +++ b/src/opentecr/data/curated_measurement.py @@ -1,4 +1,5 @@ -from typing import Optional +"""Provides dataclass CuratedMeasurement.""" + from pydantic import Field @@ -17,7 +18,7 @@ class CuratedMeasurement(Base): description="The official source from which these measurements were curated. " "Ideally identified by a DOI.", ) - curator: Optional[Contributor] = Field( + curator: Contributor | None = Field( None, title="Contributor", description="The person who contributed these measurements.", @@ -31,33 +32,33 @@ class CuratedMeasurement(Base): # FIXME: add fields for units? (also explicitly dimensionless) # FIXME: free energy? # FIXME: apparent equilibrium constant? - equilibrium_constant: Optional[float] = Field( + equilibrium_constant: float | None = Field( None, alias="equilibriumConstant", title="Equilibrium Constant", description="The equilibrium constant ($K$) of this reaction.", ) - hydrogen_potential: Optional[float] = Field( + hydrogen_potential: float | None = Field( None, alias="hydrogenPotential", title="Potential of Hydrogen", description="The potential of hydrogen (pH) at which the measurements were " "performed.", ) - magnesium_potential: Optional[float] = Field( + magnesium_potential: float | None = Field( None, alias="magnesiumPotential", title="Potential of Magnesium", description="The potential of magnesium (pMg) at which the measurements were " "performed.", ) - temperature: Optional[float] = Field( + temperature: float | None = Field( None, title="Temperature", description="The temperature in Kelvin (K) at which the measurements were " "performed.", ) - ionic_strength: Optional[float] = Field( + ionic_strength: float | None = Field( None, alias="ionicStrength", title="Ionic Strength", diff --git a/src/opentecr/data/literature_reference.py b/src/opentecr/data/literature_reference.py index 3ac0139..24f6abc 100644 --- a/src/opentecr/data/literature_reference.py +++ b/src/opentecr/data/literature_reference.py @@ -1,4 +1,4 @@ -from typing import Optional +"""Provides dataclass LiteratureReference.""" from pydantic import Field @@ -6,10 +6,11 @@ class LiteratureReference(Base): + """Information about a literature reference.""" - pubmed_id: Optional[str] = Field(None, alias="pubmedId", title="PubMed Identifier") - doi: Optional[str] = Field(None, title="Digital Object Identifier (DOI)") - url: Optional[str] = Field(None, title="Universal Resource Locator (URL)") + pubmed_id: str | None = Field(None, alias="pubmedId", title="PubMed Identifier") + doi: str | None = Field(None, title="Digital Object Identifier (DOI)") + url: str | None = Field(None, title="Universal Resource Locator (URL)") title: str = Field(..., title="Title") authors: str = Field(..., title="Authors") year: int = Field(..., title="Year") diff --git a/src/opentecr/data/metabolite.py b/src/opentecr/data/metabolite.py index 72c9816..57a7d1a 100644 --- a/src/opentecr/data/metabolite.py +++ b/src/opentecr/data/metabolite.py @@ -1,4 +1,5 @@ -from typing import List, Optional +"""Provides dataclass Metabolite.""" + from pydantic import Field @@ -7,8 +8,7 @@ class Metabolite(Base): - """ - Define a metabolite ORM model. + """Define a metabolite ORM model. Attributes ---------- @@ -24,38 +24,39 @@ class Metabolite(Base): """ - inchi: Optional[str] = Field( + inchi: str | None = Field( None, title="InChI", description="IUPAC international chemical identifier.", ) - inchi_key: Optional[str] = Field( + inchi_key: str | None = Field( None, alias="inchiKey", title="InChIKey", description="IUPAC international chemical identifier hashed key.", ) - smiles: Optional[str] = Field( + smiles: str | None = Field( None, title="SMILES", description="Simplified molecular-input line-entry system (SMILES).", ) - molecular_formula: Optional[str] = Field( + molecular_formula: str | None = Field( None, alias="molecularFormula", title="Molecular Formula", description="The number of atoms in the metabolite.", ) # FIXME: how do we define charge exactly? valence electrons + protons? - charge: Optional[float] = Field(None) + charge: float | None = Field(None) # FIXME: molecular weight, atomic mass, or monoisotopic mass? - mass: Optional[float] = Field(None) - names: List[str] = Field( - (), + mass: float | None = Field(None) + names: list[str] = Field( + [], title="Common Names", description="Common names or synonyms for this metabolite mostly to further " "human understanding.", ) - annotation: List[Annotation] = Field( - (), description="Cross-references for the metabolite." + annotation: list[Annotation] = Field( + [], + description="Cross-references for the metabolite.", ) diff --git a/src/opentecr/data/representative_reaction.py b/src/opentecr/data/representative_reaction.py index 45f771e..043d284 100644 --- a/src/opentecr/data/representative_reaction.py +++ b/src/opentecr/data/representative_reaction.py @@ -1,6 +1,7 @@ -from typing import Dict, List, Optional +"""Provides dataclass RepresentativeReaction.""" -from pydantic import Field + +from pydantic import Field, computed_field from .annotation import Annotation from .base import Base @@ -8,30 +9,31 @@ class RepresentativeReaction(Base): - """""" + """Information about a reaction.""" - rinchi: Optional[str] = Field( + rinchi: str | None = Field( None, title="RInChI", description="Reaction IUPAC international chemical identifier.", ) - rinchi_key: Optional[str] = Field( + rinchi_key: str | None = Field( None, alias="rinchiKey", title="RInChIKey", description="Hashed version of the reaction IUPAC international chemical " "identifier.", ) - names: List[str] = Field( - (), + names: list[str] = Field( + [], title="Common Names", description="Common names or synonyms for this reaction mostly to further " "human understanding.", ) - annotation: List[Annotation] = Field( - (), description="Cross-references for the reaction." + annotation: list[Annotation] = Field( + [], + description="Cross-references for the reaction.", ) - stoichiometry: Dict[Metabolite, float] = Field( + stoichiometry: dict[Metabolite, float] = Field( ..., description="A dictionary of the stoichiometric coefficients and metabolites" "that define this reaction. Includes both reactants (negative coefficients) " @@ -39,9 +41,11 @@ class RepresentativeReaction(Base): ) @property - def reactants(self) -> List[Metabolite]: - return [met for met, coef in self.stoichiometry if coef < 0] + def reactants(self: "RepresentativeReaction") -> list[Metabolite]: + """Add the reactants attribute.""" + return [met for met, coef in self.stoichiometry.items() if coef < 0] @property - def products(self) -> List[Metabolite]: - return [met for met, coef in self.stoichiometry if coef > 0] + def products(self: "RepresentativeReaction") -> list[Metabolite]: + """Add the products attribute.""" + return [met for met, coef in self.stoichiometry.items() if coef > 0] diff --git a/src/opentecr/py.typed b/src/opentecr/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/.keep b/tests/integration/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/test_json_schema.py b/tests/integration/test_json_schema.py new file mode 100644 index 0000000..6399639 --- /dev/null +++ b/tests/integration/test_json_schema.py @@ -0,0 +1,17 @@ +"""Test json schema generation.""" + +import json +from pathlib import Path + +from opentecr.data import CuratedMeasurement + +PROJECT_ROOT = Path(__file__).parent.parent.parent + + +def test_json_schema() -> None: + """Check that json schema generation works as expected.""" + result_path = PROJECT_ROOT / "output" / "openTECR.json" + with result_path.open("r") as f: + expected = json.load(f) + actual = CuratedMeasurement.model_json_schema() + assert expected == actual diff --git a/tests/system/.keep b/tests/system/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/.keep b/tests/unit/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tox.ini b/tox.ini index f26eaa4..b83f4c4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,74 @@ -[isort] -skip = - __init__.py -profile = black -lines_after_imports = 2 -known_first_party = opentecr +[tox] +envlist = darglint, ruff, safety, mypy, docs, install, py3{11,12} +isolated_build = true + +[gh-actions] +python = + 3.11: install, safety, py310 + 3.12: flake8, install, safety, mpypy, docs, py312 + +[testenv] +# Define base for all test environments. +parallel_show_output = true + +[testenv:darglint] +# Until darglint rules are integrated with ruff. +skip_install = True +deps= + darglint +commands= + darglint {toxinidir}/src/opentecr {toxinidir}/tests + +[testenv:ruff] +skip_install = True +deps= + ruff +commands= + ruff check {toxinidir}/src/opentecr {toxinidir}/tests + +[testenv:safety] +deps= + safety +commands= + safety check --full-report + +[testenv:mypy] +skip_install = True +deps= + mypy + pydantic +commands= + mypy {toxinidir}/src/opentecr + +[testenv:docs] +extras = dev +commands= + mkdocs build --strict + +[testenv:install] +skip_install = True +deps= + hatch + twine +commands= + pip check {toxinidir} + hatch build + twine check {toxinidir}/dist/* + +[testenv:py3{11,12}] +deps = + pytest + pytest-cov + pytest-raises + pytest-xdist +commands = + pytest --cov=opentecr --cov-report=term {posargs} + +################################################################################ +# Testing tools configuration # +################################################################################ + +[darglint] +strictness = long +# TODO: Can also choose 'numpy' +docstring_style = google