diff --git a/.github/workflows/main.yaml b/.github/workflows/pytest.yaml similarity index 64% rename from .github/workflows/main.yaml rename to .github/workflows/pytest.yaml index 086b86f..df62bc7 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/pytest.yaml @@ -1,13 +1,12 @@ # Built from: # https://docs.github.com/en/actions/guides/building-and-testing-python -name: black +name: Test and check code style on: [push, pull_request] jobs: - black: - + test: runs-on: ubuntu-latest strategy: @@ -17,7 +16,7 @@ jobs: steps: #---------------------------------------------- - # check-out repo and set-up python + # check-out repo and set-up python #---------------------------------------------- - name: Check out repository uses: actions/checkout@v2 @@ -28,41 +27,39 @@ jobs: python-version: ${{ matrix.python-version }} #---------------------------------------------- - # install & configure poetry + # cache poetry installation #---------------------------------------------- - - name: Install Poetry - uses: snok/install-poetry@v1.1.1 + - name: Cache Poetry files + id: cache-poetry + uses: actions/cache@v2 with: - virtualenvs-create: true - virtualenvs-in-project: true + path: ~/.local + key: ${{ runner.os }}-python-${{ matrix.python-version }}-poetry-${{ hashFiles('**/poetry.lock') }} #---------------------------------------------- - # load cached venv if cache exists + # install & configure poetry #---------------------------------------------- - - name: Load cached venv - id: cached-poetry-dependencies - uses: actions/cache@v2 - with: - path: .venv - key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} + - name: Install Poetry + uses: snok/install-poetry@v1 #---------------------------------------------- - # install dependencies if cache does not exist + # install dependencies #---------------------------------------------- - name: Install dependencies - if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - run: poetry install --no-interaction --no-root + run: poetry install --no-interaction #---------------------------------------------- - # install your root project, if required + # run tests #---------------------------------------------- - - name: Install library - run: poetry install --no-interaction + - name: Run tests + env: + SHEET2LINKML_GOOGLE_SERVICE_ACCT: ${{ secrets.Sheet2LinkMLGoogleServiceAcct }} + run: poetry run pytest -svvv #---------------------------------------------- - # check formatting + # check formatting #---------------------------------------------- - - name: Code formatting with black + - name: Code style with black run: | # run black in check mode # if files are not formatted correctly, the build will not go through diff --git a/CHANGELOG.md b/CHANGELOG.md index 3958ae5..2cd7916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Releases](https://github.com/cancerDHC/sheet2linkml/releases) ## [Unreleased] +* Bugs or Features yet to be resolved. + +## [v1.3.0] +* Feature: added a dummy Google Sheet, the expected schema, and a test to compare them. +* Feature: cleaned up GitHub Action for style checks and added PyTest. +* Feature: added support for storing Google API credentials in an environmental variable, and added it to + GitHub Actions. +* Feature: added check for Google Sheet ID. +* Feature: updated @cached_property with @property @lru_cache to support Python 3.7. +* Bug: updated CCDH Terminology API endpoint for enumerations. +* Bug: fixed code to correctly handle 404 responses from the TCCM Terminology Service. +* Bug: replaced unnecessary print() in GSheetModel with logging.error(). ## [v1.2.0] - 2021-11-29 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6afb96b..182d6d1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -68,7 +68,12 @@ git checkout -b release-1.2 develop # Switched to a new branch "release-1.2" # minor bug fixes and preparing metadata for a release # e.g., version number, build dates # commit changes +``` + +Update the version number of the release in [CHANGELOG.md](CHANGELOG.md) and document some of the new changes that will be +part of the release. +``` # merge changes into "main" git checkout main # switched to branch "main" git merge --no-ff release-1.2 # merge into "main" with summary of changes @@ -85,7 +90,8 @@ Once the code has been merged into the `main` branch on this repo, there are two to ensure a release is complete. - You should create a GitHub tag, with the appropriate version number. -- You should push the package to PyPI. +- You should push the package to PyPI. Before pushing the package to PyPI make sure you have updated the version number in the +[pyproject.toml](pyproject.toml) file so that it matches the version number of the release you described in the CHANGELOG. ### Release to Test PyPI _(optional)_ The purpose of this section is to verify that the package looks and works as intended, by viewing it on Test PyPI and diff --git a/README.md b/README.md index d70cd81..9c55383 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,16 @@ source .venv/bin/activate python -m pip install sheet2linkml ``` +## Authorization + +sheet2linkml uses the [`pygsheets` library](https://github.com/nithinmurali/pygsheets) in order to access sheets in +Google Drive. To authorize it to access your Google Sheets, you will need to create and download Google Drive client credentials. First, +[enable the Google Drive API](https://developers.google.com/drive/api/v3/enable-drive-api). After the API is enabled, +[create and download the client credentials](https://www.iperiusbackup.net/en/how-to-enable-google-drive-api-and-get-client-credentials/) +from the [Google API Console](https://console.developers.google.com/). Save the file as `google_api_credentials.json` in +the root directory of this project. [Detailed instructions and screenshots](https://pygsheets.readthedocs.io/en/stable/authorization.html) +are also available from the [`pygsheets` documentation](https://pygsheets.readthedocs.io/). + ## Command Line Client Usage Identify the Google Sheet that you want to convert to LinkML. Note that sheet2linkml is not currently a general-purpose Google Sheet to LinkML converter. It will only work with Google Sheets that have been written in a particular, currently undefined format. @@ -42,10 +52,10 @@ Identify the Google Sheet that you want to convert to LinkML. Note that sheet2li Contact your CCDH colleagues to obtain the correct sheet ID and assert it either in a `.env` file or in the shell, like this: ```shell -export CDM_GOOGLE_SHEET_ID=WbM2Jr869ofmdcSmhX_1E0aLWvnK2-gr47Mo_tzuQKWy +export CDM_GOOGLE_SHEET_ID=1oWS7cao-fgz2MKWtyr8h2dEL9unX__0bJrWKv6mQmM4 ``` -A `google_api_credentials.json` file is also required in the root of this repo. Documentation is forthcoming. +A `google_api_credentials.json` file is also required in the root of this repo as detailed in the Authorization section above. And the user is responsible for defining - `~/path/to/crdch_model.yaml` diff --git a/poetry.lock b/poetry.lock index 9d0e33d..85389f8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,6 +6,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + [[package]] name = "attrs" version = "21.2.0" @@ -22,9 +30,9 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (> [[package]] name = "black" -version = "21.11b1" +version = "21.12b0" description = "The uncompromising code formatter." -category = "main" +category = "dev" optional = false python-versions = ">=3.6.2" @@ -33,7 +41,6 @@ click = ">=7.1.2" mypy-extensions = ">=0.4.3" pathspec = ">=0.9.0,<1" platformdirs = ">=2" -regex = ">=2021.4.4" tomli = ">=0.2.6,<2.0.0" typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} typing-extensions = [ @@ -66,7 +73,7 @@ python-versions = "*" [[package]] name = "charset-normalizer" -version = "2.0.8" +version = "2.0.9" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false @@ -119,7 +126,7 @@ python-versions = ">=3.6" [[package]] name = "google-api-core" -version = "2.2.2" +version = "2.3.0" description = "Google API client core library" category = "main" optional = false @@ -138,7 +145,7 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2)"] [[package]] name = "google-api-python-client" -version = "2.31.0" +version = "2.33.0" description = "Google API Client Library for Python" category = "main" optional = false @@ -200,7 +207,7 @@ tool = ["click (>=6.0.0)"] [[package]] name = "googleapis-common-protos" -version = "1.53.0" +version = "1.54.0" description = "Common protobufs used in Google APIs" category = "main" optional = false @@ -271,6 +278,14 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "isodate" version = "0.6.0" @@ -336,7 +351,7 @@ format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "linkml-runtime" -version = "1.1.7" +version = "1.1.9" description = "LinkML Runtime Environment" category = "main" optional = false @@ -376,7 +391,7 @@ source = ["Cython (>=0.29.7)"] name = "mypy-extensions" version = "0.4.3" description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "main" +category = "dev" optional = false python-versions = "*" @@ -393,11 +408,22 @@ rsa = ["cryptography (>=3.0.0,<4)"] signals = ["blinker (>=1.4.0)"] signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"] +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + [[package]] name = "pathspec" version = "0.9.0" description = "Utility library for gitignore style pattern matching of file paths." -category = "main" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" @@ -405,7 +431,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" name = "platformdirs" version = "2.4.0" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" +category = "dev" optional = false python-versions = ">=3.6" @@ -413,6 +439,21 @@ python-versions = ">=3.6" docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "prefixcommons" version = "0.1.9" @@ -436,6 +477,14 @@ category = "main" optional = false python-versions = ">=3.5" +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "pyasn1" version = "0.4.8" @@ -520,6 +569,28 @@ category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "pytest" +version = "6.2.5" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + [[package]] name = "python-dotenv" version = "0.15.0" @@ -582,14 +653,6 @@ pyldmod = "*" rdflib = ">=4.2.0" testfixtures = "*" -[[package]] -name = "regex" -version = "2021.11.10" -description = "Alternative regular expression module, to replace re." -category = "main" -optional = false -python-versions = "*" - [[package]] name = "requests" version = "2.26.0" @@ -666,19 +729,27 @@ build = ["setuptools-git", "wheel", "twine"] docs = ["sphinx", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"] test = ["pytest (>=3.6)", "pytest-cov", "pytest-django", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"] +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + [[package]] name = "tomli" version = "1.2.2" description = "A lil' TOML parser" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" [[package]] name = "typed-ast" -version = "1.5.0" +version = "1.5.1" description = "a fork of Python 2 and 3 ast modules with type comment support" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" @@ -734,19 +805,23 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "bbd1f751e1d250b71b02e615755ac97c2d72a8beecebf7834eba93567b0b7c44" +content-hash = "8a00e373957ee1850c86fb5dc806ece485e90a52e96bad0edf2057b9d122af87" [metadata.files] antlr4-python3-runtime = [ {file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"}, ] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] black = [ - {file = "black-21.11b1-py3-none-any.whl", hash = "sha256:802c6c30b637b28645b7fde282ed2569c0cd777dbe493a41b6a03c1d903f99ac"}, - {file = "black-21.11b1.tar.gz", hash = "sha256:a042adbb18b3262faad5aff4e834ff186bb893f95ba3a8013f09de1e5569def2"}, + {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, + {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, ] cachetools = [ {file = "cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"}, @@ -757,8 +832,8 @@ certifi = [ {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, ] charset-normalizer = [ - {file = "charset-normalizer-2.0.8.tar.gz", hash = "sha256:735e240d9a8506778cd7a453d97e817e536bb1fc29f4f6961ce297b9c7a917b0"}, - {file = "charset_normalizer-2.0.8-py3-none-any.whl", hash = "sha256:83fcdeb225499d6344c8f7f34684c2981270beacc32ede2e669e94f7fa544405"}, + {file = "charset-normalizer-2.0.9.tar.gz", hash = "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"}, + {file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"}, ] click = [ {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, @@ -777,12 +852,12 @@ frozendict = [ {file = "frozendict-2.1.1.tar.gz", hash = "sha256:655b879217dd445a2023e16154cc231febef802b5c812d5c2e822280ad69e1dc"}, ] google-api-core = [ - {file = "google-api-core-2.2.2.tar.gz", hash = "sha256:97349cc18c2bb2415f64f1353a80273a289a61294ce3eb2f7ce682d251bdd997"}, - {file = "google_api_core-2.2.2-py2.py3-none-any.whl", hash = "sha256:e7853735d4f51f4212d6bf9750620d76fc0106c0f271be0c3f43b73501c7ddf9"}, + {file = "google-api-core-2.3.0.tar.gz", hash = "sha256:538cd927bfe0f7c63bc1eb5fcadeeeb52f90bb29117dea87ae8cae48953d4c60"}, + {file = "google_api_core-2.3.0-py2.py3-none-any.whl", hash = "sha256:65fe1d9d8dde7a5ddc43d5cc134560c2a76b1a26c9f70c3c24f13b7910e89e10"}, ] google-api-python-client = [ - {file = "google-api-python-client-2.31.0.tar.gz", hash = "sha256:3f64a5eee6fa03ab51806dccf48e342fda83fea60c290518c6a2dfdbf2de9efd"}, - {file = "google_api_python_client-2.31.0-py2.py3-none-any.whl", hash = "sha256:d9f1539db48abfea3d7d65e4cfba258a5a95c901e99fb49c7cfe4de99921a757"}, + {file = "google-api-python-client-2.33.0.tar.gz", hash = "sha256:38e98611794632a12479fafbabe0b5027e8fcfc412e8375f1b23db0bc0209181"}, + {file = "google_api_python_client-2.33.0-py2.py3-none-any.whl", hash = "sha256:1322d026110bc62eb29a4a25a15895dac51486b30a29b5943bc456318677280b"}, ] google-auth = [ {file = "google-auth-2.3.3.tar.gz", hash = "sha256:d83570a664c10b97a1dc6f8df87e5fdfff012f48f62be131e449c20dfc32630e"}, @@ -797,8 +872,8 @@ google-auth-oauthlib = [ {file = "google_auth_oauthlib-0.4.6-py2.py3-none-any.whl", hash = "sha256:3f2a6e802eebbb6fb736a370fbf3b055edcb6b52878bf2f26330b5e041316c73"}, ] googleapis-common-protos = [ - {file = "googleapis-common-protos-1.53.0.tar.gz", hash = "sha256:a88ee8903aa0a81f6c3cec2d5cf62d3c8aa67c06439b0496b49048fb1854ebf4"}, - {file = "googleapis_common_protos-1.53.0-py2.py3-none-any.whl", hash = "sha256:f6d561ab8fb16b30020b940e2dd01cd80082f4762fa9f3ee670f4419b4b8dbd0"}, + {file = "googleapis-common-protos-1.54.0.tar.gz", hash = "sha256:a4031d6ec6c2b1b6dc3e0be7e10a1bd72fb0b18b07ef9be7b51f2c1004ce2437"}, + {file = "googleapis_common_protos-1.54.0-py2.py3-none-any.whl", hash = "sha256:e54345a2add15dc5e1a7891c27731ff347b4c33765d79b5ed7026a6c0c7cbcae"}, ] hbreader = [ {file = "hbreader-0.9.1-py3-none-any.whl", hash = "sha256:9a6e76c9d1afc1b977374a5dc430a1ebb0ea0488205546d4678d6e31cc5f6801"}, @@ -820,6 +895,10 @@ importlib-resources = [ {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"}, ] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] isodate = [ {file = "isodate-0.6.0-py2.py3-none-any.whl", hash = "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81"}, {file = "isodate-0.6.0.tar.gz", hash = "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8"}, @@ -841,8 +920,8 @@ jsonschema = [ {file = "jsonschema-4.2.1.tar.gz", hash = "sha256:390713469ae64b8a58698bb3cbc3859abe6925b565a973f87323ef21b09a27a8"}, ] linkml-runtime = [ - {file = "linkml_runtime-1.1.7-py3-none-any.whl", hash = "sha256:67f16cec969456489d025a707456774c163415010fbf7f6568aa783bfc015784"}, - {file = "linkml_runtime-1.1.7.tar.gz", hash = "sha256:d8f4a6ed685b31f1bb8f242a40e890ccd39d049b93b35777a9b3aa1c22c39b36"}, + {file = "linkml_runtime-1.1.9-py3-none-any.whl", hash = "sha256:855056f3381e805a728f348b0db55cb5ee7a0c1f247336afe1b9830dc3a95502"}, + {file = "linkml_runtime-1.1.9.tar.gz", hash = "sha256:9b4717c79e8d43f3d03ea26145205449728b62d284b3ad44fcb4a1fc208a64f1"}, ] lxml = [ {file = "lxml-4.6.4-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bbf2dc330bd44bfc0254ab37677ec60f7c7ecea55ad8ba1b8b2ea7bf20c265f5"}, @@ -914,6 +993,10 @@ oauthlib = [ {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"}, {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"}, ] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] pathspec = [ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, @@ -922,6 +1005,10 @@ platformdirs = [ {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, ] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] prefixcommons = [ {file = "prefixcommons-0.1.9-py3-none-any.whl", hash = "sha256:2ff99e9f2c27f41e6beb831742ca88ddae129b2de6dcc81b0b27f0de69cb2e8a"}, {file = "prefixcommons-0.1.9-py3.8.egg", hash = "sha256:f820dc69f0eba1f6fd80bdb2e78420e4970f20dfb4c307fed77c607198ca69f2"}, @@ -953,6 +1040,10 @@ protobuf = [ {file = "protobuf-3.19.1-py2.py3-none-any.whl", hash = "sha256:e813b1c9006b6399308e917ac5d298f345d95bb31f46f02b60cd92970a9afa17"}, {file = "protobuf-3.19.1.tar.gz", hash = "sha256:62a8e4baa9cb9e064eb62d1002eca820857ab2138440cb4b3ea4243830f94ca7"}, ] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] pyasn1 = [ {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, @@ -1021,6 +1112,10 @@ pyrsistent = [ {file = "pyrsistent-0.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea"}, {file = "pyrsistent-0.18.0.tar.gz", hash = "sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b"}, ] +pytest = [ + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, +] python-dotenv = [ {file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"}, {file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"}, @@ -1068,82 +1163,6 @@ rdflib-pyldmod-compat = [ {file = "rdflib-pyldmod-compat-0.1.2.tar.gz", hash = "sha256:bfba0064d8b2584afdbef5d54bac3ac1dd4c13b64aaeba7a43812c2f35ca13c6"}, {file = "rdflib_pyldmod_compat-0.1.2-py3-none-any.whl", hash = "sha256:68aa05d4f4d8f46241e0c37e631b4747f18a356d8accc5bae204350d2f314196"}, ] -regex = [ - {file = "regex-2021.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf"}, - {file = "regex-2021.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4"}, - {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9ed0b1e5e0759d6b7f8e2f143894b2a7f3edd313f38cf44e1e15d360e11749b"}, - {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:473e67837f786404570eae33c3b64a4b9635ae9f00145250851a1292f484c063"}, - {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2fee3ed82a011184807d2127f1733b4f6b2ff6ec7151d83ef3477f3b96a13d03"}, - {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d5fd67df77bab0d3f4ea1d7afca9ef15c2ee35dfb348c7b57ffb9782a6e4db6e"}, - {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5d408a642a5484b9b4d11dea15a489ea0928c7e410c7525cd892f4d04f2f617b"}, - {file = "regex-2021.11.10-cp310-cp310-win32.whl", hash = "sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a"}, - {file = "regex-2021.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12"}, - {file = "regex-2021.11.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e"}, - {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:74cbeac0451f27d4f50e6e8a8f3a52ca074b5e2da9f7b505c4201a57a8ed6286"}, - {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:3598893bde43091ee5ca0a6ad20f08a0435e93a69255eeb5f81b85e81e329264"}, - {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:50a7ddf3d131dc5633dccdb51417e2d1910d25cbcf842115a3a5893509140a3a"}, - {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:61600a7ca4bcf78a96a68a27c2ae9389763b5b94b63943d5158f2a377e09d29a"}, - {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:563d5f9354e15e048465061509403f68424fef37d5add3064038c2511c8f5e00"}, - {file = "regex-2021.11.10-cp36-cp36m-win32.whl", hash = "sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4"}, - {file = "regex-2021.11.10-cp36-cp36m-win_amd64.whl", hash = "sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e"}, - {file = "regex-2021.11.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f"}, - {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:42b50fa6666b0d50c30a990527127334d6b96dd969011e843e726a64011485da"}, - {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6e1d2cc79e8dae442b3fa4a26c5794428b98f81389af90623ffcc650ce9f6732"}, - {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:0416f7399e918c4b0e074a0f66e5191077ee2ca32a0f99d4c187a62beb47aa05"}, - {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:ce298e3d0c65bd03fa65ffcc6db0e2b578e8f626d468db64fdf8457731052942"}, - {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dc07f021ee80510f3cd3af2cad5b6a3b3a10b057521d9e6aaeb621730d320c5a"}, - {file = "regex-2021.11.10-cp37-cp37m-win32.whl", hash = "sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec"}, - {file = "regex-2021.11.10-cp37-cp37m-win_amd64.whl", hash = "sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4"}, - {file = "regex-2021.11.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83"}, - {file = "regex-2021.11.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94"}, - {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f5be7805e53dafe94d295399cfbe5227f39995a997f4fd8539bf3cbdc8f47ca8"}, - {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a955b747d620a50408b7fdf948e04359d6e762ff8a85f5775d907ceced715129"}, - {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:139a23d1f5d30db2cc6c7fd9c6d6497872a672db22c4ae1910be22d4f4b2068a"}, - {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ca49e1ab99593438b204e00f3970e7a5f70d045267051dfa6b5f4304fcfa1dbf"}, - {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:96fc32c16ea6d60d3ca7f63397bff5c75c5a562f7db6dec7d412f7c4d2e78ec0"}, - {file = "regex-2021.11.10-cp38-cp38-win32.whl", hash = "sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc"}, - {file = "regex-2021.11.10-cp38-cp38-win_amd64.whl", hash = "sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d"}, - {file = "regex-2021.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b"}, - {file = "regex-2021.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef"}, - {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cd410a1cbb2d297c67d8521759ab2ee3f1d66206d2e4328502a487589a2cb21b"}, - {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e6096b0688e6e14af6a1b10eaad86b4ff17935c49aa774eac7c95a57a4e8c296"}, - {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:529801a0d58809b60b3531ee804d3e3be4b412c94b5d267daa3de7fadef00f49"}, - {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f594b96fe2e0821d026365f72ac7b4f0b487487fb3d4aaf10dd9d97d88a9737"}, - {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2409b5c9cef7054dde93a9803156b411b677affc84fca69e908b1cb2c540025d"}, - {file = "regex-2021.11.10-cp39-cp39-win32.whl", hash = "sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a"}, - {file = "regex-2021.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29"}, - {file = "regex-2021.11.10.tar.gz", hash = "sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6"}, -] requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, @@ -1169,30 +1188,34 @@ testfixtures = [ {file = "testfixtures-6.18.3-py2.py3-none-any.whl", hash = "sha256:6ddb7f56a123e1a9339f130a200359092bd0a6455e31838d6c477e8729bb7763"}, {file = "testfixtures-6.18.3.tar.gz", hash = "sha256:2600100ae96ffd082334b378e355550fef8b4a529a6fa4c34f47130905c7426d"}, ] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] tomli = [ {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"}, {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"}, ] typed-ast = [ - {file = "typed_ast-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b310a207ee9fde3f46ba327989e6cba4195bc0c8c70a158456e7b10233e6bed"}, - {file = "typed_ast-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52ca2b2b524d770bed7a393371a38e91943f9160a190141e0df911586066ecda"}, - {file = "typed_ast-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:14fed8820114a389a2b7e91624db5f85f3f6682fda09fe0268a59aabd28fe5f5"}, - {file = "typed_ast-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:65c81abbabda7d760df7304d843cc9dbe7ef5d485504ca59a46ae2d1731d2428"}, - {file = "typed_ast-1.5.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:37ba2ab65a0028b1a4f2b61a8fe77f12d242731977d274a03d68ebb751271508"}, - {file = "typed_ast-1.5.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:49af5b8f6f03ed1eb89ee06c1d7c2e7c8e743d720c3746a5857609a1abc94c94"}, - {file = "typed_ast-1.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:e4374a76e61399a173137e7984a1d7e356038cf844f24fd8aea46c8029a2f712"}, - {file = "typed_ast-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ea517c2bb11c5e4ba7a83a91482a2837041181d57d3ed0749a6c382a2b6b7086"}, - {file = "typed_ast-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:51040bf45aacefa44fa67fb9ebcd1f2bec73182b99a532c2394eea7dabd18e24"}, - {file = "typed_ast-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:806e0c7346b9b4af8c62d9a29053f484599921a4448c37fbbcbbf15c25138570"}, - {file = "typed_ast-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a67fd5914603e2165e075f1b12f5a8356bfb9557e8bfb74511108cfbab0f51ed"}, - {file = "typed_ast-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:224afecb8b39739f5c9562794a7c98325cb9d972712e1a98b6989a4720219541"}, - {file = "typed_ast-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:155b74b078be842d2eb630dd30a280025eca0a5383c7d45853c27afee65f278f"}, - {file = "typed_ast-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:361b9e5d27bd8e3ccb6ea6ad6c4f3c0be322a1a0f8177db6d56264fa0ae40410"}, - {file = "typed_ast-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:618912cbc7e17b4aeba86ffe071698c6e2d292acbd6d1d5ec1ee724b8c4ae450"}, - {file = "typed_ast-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7e6731044f748340ef68dcadb5172a4b1f40847a2983fe3983b2a66445fbc8e6"}, - {file = "typed_ast-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e8a9b9c87801cecaad3b4c2b8876387115d1a14caa602c1618cedbb0cb2a14e6"}, - {file = "typed_ast-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:ec184dfb5d3d11e82841dbb973e7092b75f306b625fad7b2e665b64c5d60ab3f"}, - {file = "typed_ast-1.5.0.tar.gz", hash = "sha256:ff4ad88271aa7a55f19b6a161ed44e088c393846d954729549e3cde8257747bb"}, + {file = "typed_ast-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d8314c92414ce7481eee7ad42b353943679cf6f30237b5ecbf7d835519e1212"}, + {file = "typed_ast-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b53ae5de5500529c76225d18eeb060efbcec90ad5e030713fe8dab0fb4531631"}, + {file = "typed_ast-1.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:24058827d8f5d633f97223f5148a7d22628099a3d2efe06654ce872f46f07cdb"}, + {file = "typed_ast-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a6d495c1ef572519a7bac9534dbf6d94c40e5b6a608ef41136133377bba4aa08"}, + {file = "typed_ast-1.5.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:de4ecae89c7d8b56169473e08f6bfd2df7f95015591f43126e4ea7865928677e"}, + {file = "typed_ast-1.5.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:256115a5bc7ea9e665c6314ed6671ee2c08ca380f9d5f130bd4d2c1f5848d695"}, + {file = "typed_ast-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:7c42707ab981b6cf4b73490c16e9d17fcd5227039720ca14abe415d39a173a30"}, + {file = "typed_ast-1.5.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:71dcda943a471d826ea930dd449ac7e76db7be778fcd722deb63642bab32ea3f"}, + {file = "typed_ast-1.5.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4f30a2bcd8e68adbb791ce1567fdb897357506f7ea6716f6bbdd3053ac4d9471"}, + {file = "typed_ast-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ca9e8300d8ba0b66d140820cf463438c8e7b4cdc6fd710c059bfcfb1531d03fb"}, + {file = "typed_ast-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9caaf2b440efb39ecbc45e2fabde809cbe56272719131a6318fd9bf08b58e2cb"}, + {file = "typed_ast-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c9bcad65d66d594bffab8575f39420fe0ee96f66e23c4d927ebb4e24354ec1af"}, + {file = "typed_ast-1.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:591bc04e507595887160ed7aa8d6785867fb86c5793911be79ccede61ae96f4d"}, + {file = "typed_ast-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:a80d84f535642420dd17e16ae25bb46c7f4c16ee231105e7f3eb43976a89670a"}, + {file = "typed_ast-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:38cf5c642fa808300bae1281460d4f9b7617cf864d4e383054a5ef336e344d32"}, + {file = "typed_ast-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b6ab14c56bc9c7e3c30228a0a0b54b915b1579613f6e463ba6f4eb1382e7fd4"}, + {file = "typed_ast-1.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2b8d7007f6280e36fa42652df47087ac7b0a7d7f09f9468f07792ba646aac2d"}, + {file = "typed_ast-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:b6d17f37f6edd879141e64a5db17b67488cfeffeedad8c5cec0392305e9bc775"}, + {file = "typed_ast-1.5.1.tar.gz", hash = "sha256:484137cab8ecf47e137260daa20bafbba5f4e3ec7fda1c1e69ab299b75fa81c5"}, ] typing-extensions = [ {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, diff --git a/pyproject.toml b/pyproject.toml index ca2ee7c..ce1ee2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "sheet2linkml" -version = "1.2.0" +version = "1.3.0" description = "Google Sheets to LinkML generator for the CRDC-H model" authors = [ "Gaurav Vaidya " @@ -38,4 +38,7 @@ pygsheets = "^2.0.4" PyYAML = "^5.4.1" setuptools = "^52.0.0" python-dotenv = "^0.15.0" + +[tool.poetry.dev-dependencies] +pytest = "^6.2.5" black = "^21.9b0" diff --git a/sheet2linkml/cli.py b/sheet2linkml/cli.py index d2a145e..b7da525 100644 --- a/sheet2linkml/cli.py +++ b/sheet2linkml/cli.py @@ -18,9 +18,17 @@ from linkml_runtime.dumpers import yaml_dumper from dotenv import load_dotenv +import pygsheets @click.command() +@click.option( + "--google-sheet-id", + "-g", + type=str, + default=os.getenv("CDM_GOOGLE_SHEET_ID"), + help="The Google Sheet ID that should be converted into a LinkML sheet.", +) @click.option( "--output", "-o", @@ -49,24 +57,61 @@ default=True, help="Controls whether we use the CCDH Terminology Service to add enumerated values for attributes.", ) -def main(output, filter_entity, logging_config, write_mappings, include_terminologies): +def main( + google_sheet_id, + output, + filter_entity, + logging_config, + write_mappings, + include_terminologies, +): # Display INFO log entry and up. - logging.config.fileConfig(logging_config) + if os.path.exists(logging_config): + logging.config.fileConfig(logging_config) + else: + logging.basicConfig(level=logging.INFO) # Load environemental variables from `.env` if one is present. load_dotenv() # Read in Google API credentials. - google_api_credentials = os.getenv( - "GOOGLE_API_CREDENTIALS", "google_api_credentials.json" - ) - google_sheet_id = os.getenv("CDM_GOOGLE_SHEET_ID") + if "SHEET2LINKML_GOOGLE_SERVICE_ACCT" in os.environ: + # If an environment variable SHEET2LINKML_GOOGLE_SERVICE_ACCT is set, use it as a + # Service Account (https://pygsheets.readthedocs.io/en/stable/authorization.html#service-account) + logging.info( + "Using Google Sheet API credentials from service account environment variable." + ) + pygclient = pygsheets.authorize( + service_account_env_var="SHEET2LINKML_GOOGLE_SERVICE_ACCT", + scopes=GSheetModel.SCOPES, + ) + else: + # Otherwise, look for the path to Google API Credentials in google_api_credentials.json. + google_api_credentials = os.getenv( + "GOOGLE_API_CREDENTIALS", "google_api_credentials.json" + ) + if not os.path.exists(google_api_credentials): + logging.error( + f"Google API Credential file '{google_api_credentials}' not found: please see " + + "https://github.com/cancerDHC/sheet2linkml#authorization for information on creating this file." + ) + sys.exit(1) + logging.info("Using Google Sheet API credentials from credentials file.") + pygclient = pygsheets.authorize( + client_secret=google_api_credentials, scopes=GSheetModel.SCOPES + ) # Arbitrarily set a CRDC-H root URI. crdch_root = "https://example.org/crdch" + if not google_sheet_id: + logging.error( + "A Google Sheet ID is required; please set environmental variable 'CDM_GOOGLE_SHEET_ID' to a Google Sheet ID." + ) + sys.exit(1) + # Load the Google Sheet model and add the development version number. - model = GSheetModel(google_api_credentials, google_sheet_id) + model = GSheetModel(pygclient, google_sheet_id) if include_terminologies: model.use_terminology_service(TCCMService("https://terminology.ccdh.io")) logging.info(f"Google Sheet loaded: {model}") @@ -100,7 +145,7 @@ def main(output, filter_entity, logging_config, write_mappings, include_terminol ) for entity in model.entities(): logging.error(f" - {entity.name}") - exit(1) + sys.exit(1) mappings = list() @@ -121,6 +166,8 @@ def main(output, filter_entity, logging_config, write_mappings, include_terminol if write_mappings: Mappings.write_to_file(model.mappings, filename=write_mappings, model=model) + sys.exit(0) + if __name__ == "__main__": main() diff --git a/sheet2linkml/source/gsheetmodel/entity.py b/sheet2linkml/source/gsheetmodel/entity.py index 3f32c17..902c502 100644 --- a/sheet2linkml/source/gsheetmodel/entity.py +++ b/sheet2linkml/source/gsheetmodel/entity.py @@ -3,7 +3,7 @@ import urllib.parse from typing import List, Dict -from functools import cached_property +from functools import lru_cache from sheet2linkml.terminologies.service import TerminologyService from sheet2linkml.model import ModelElement @@ -67,7 +67,7 @@ def attribute_rows(self) -> List[Dict[str, str]]: if row.get(EntityWorksheet.COL_ATTRIBUTE_NAME) is not None ] - @cached_property + @property def attributes(self): """ Returns a list of attributes in this entity. We construct this by wrapping the @@ -157,7 +157,7 @@ def to_markdown(self) -> str: return f"[{self.name} in sheet {self.worksheet.title}]({self.worksheet.url})" - @cached_property + @property def mappings(self) -> Mappings: """ Returns the list of mappings for this entity. @@ -309,7 +309,7 @@ def counts(self) -> (int, int): return min_count, max_count - @cached_property + @property def mappings(self) -> Mappings: """ Returns the list of mappings for this attribute. @@ -368,7 +368,9 @@ def as_linkml_enum(self) -> EnumDefinition: return None # Look up enumerations on the Terminology Service. - enum_info = self.terminology_service.get_enum_values_for_field(self.full_name) + enum_info = self.terminology_service.get_enum_values_for_field( + "CRDC-H", self.entity.name, self.name + ) permissible_values = {} for pv in enum_info.get("permissible_values", []): text = pv.get("text") diff --git a/sheet2linkml/source/gsheetmodel/enum.py b/sheet2linkml/source/gsheetmodel/enum.py index 7995635..a83056a 100644 --- a/sheet2linkml/source/gsheetmodel/enum.py +++ b/sheet2linkml/source/gsheetmodel/enum.py @@ -2,7 +2,7 @@ import re from typing import List, Dict -from functools import cached_property +from functools import lru_cache from linkml_runtime.linkml_model.meta import ( SchemaDefinition, @@ -151,7 +151,7 @@ def to_markdown(self) -> str: return f"[{self.name} in sheet {self.worksheet.title}]({self.worksheet.url})" - @cached_property + @property def mappings(self) -> Mappings: """ Returns the list of mappings for this enum. @@ -264,7 +264,7 @@ def __str__(self): return f'{self.__class__.__name__} named "{self.name}" containing {len(self.row)} properties' - @cached_property + @property def mappings(self) -> Mappings: """ Returns the list of mappings for this enum value. diff --git a/sheet2linkml/source/gsheetmodel/gsheetmodel.py b/sheet2linkml/source/gsheetmodel/gsheetmodel.py index 791329c..eca952e 100644 --- a/sheet2linkml/source/gsheetmodel/gsheetmodel.py +++ b/sheet2linkml/source/gsheetmodel/gsheetmodel.py @@ -2,7 +2,7 @@ import logging from typing import List, Dict -from functools import cached_property, lru_cache +from functools import lru_cache from datetime import datetime, timezone import pygsheets @@ -34,7 +34,7 @@ class that generically represents a single, coherent model, and then make GSheet "https://www.googleapis.com/auth/drive.metadata.readonly", ] - def __init__(self, google_sheet_oath2_credentials: str, google_sheet_id: str): + def __init__(self, google_sheet_client, google_sheet_id: str): """ Create a new Google Sheet Model. This will create a model that uses the specified Google Sheet as an input. @@ -52,9 +52,7 @@ def __init__(self, google_sheet_oath2_credentials: str, google_sheet_id: str): :param google_sheet_id: The Google Sheet ID containing the model. """ - self.client = pygsheets.authorize( - client_secret=google_sheet_oath2_credentials, scopes=self.SCOPES - ) + self.client = google_sheet_client self.sheet = self.client.open_by_key(google_sheet_id) # TODO: at some point, we should read the version number from the Google Sheets document... somehow. @@ -102,8 +100,6 @@ def is_sheet_normative(worksheet: pygsheets.worksheet): return result - # Decorator that can save time when associated operation is periodically called with the same arguments. - @lru_cache def entity_worksheets(self) -> List[EntityWorksheet]: """ A list of worksheets available in this model. @@ -140,7 +136,6 @@ def entity_worksheets(self) -> List[EntityWorksheet]: for worksheet in entity_worksheets ] - @lru_cache def entities(self) -> List[Entity]: """ :return: The list of entities in this model. @@ -187,7 +182,7 @@ def enums_from_worksheets(self) -> List[Enum]: result.extend(worksheet.enums) return result - @cached_property + @property def mappings(self) -> List[Mappings.Mapping]: """Return a list of all the mappings in this LinkML document.""" mappings = [ @@ -314,7 +309,7 @@ def as_linkml(self, root_uri) -> SchemaDefinition: ) def fix_type_name(entity, dct, prop): - print(f"fix_type_name({entity}, {dct}, {prop})") + logging.debug(f"fix_type_name({entity}, {dct}, {prop})") value = dct[prop] if value is not None and value not in valid_types: logging.warning( diff --git a/sheet2linkml/terminologies/tccm/api.py b/sheet2linkml/terminologies/tccm/api.py index 26d115e..659fa87 100644 --- a/sheet2linkml/terminologies/tccm/api.py +++ b/sheet2linkml/terminologies/tccm/api.py @@ -21,8 +21,8 @@ def __init__(self, base_url: str): # Decorator that can save time when associated operation is periodically called with the same arguments. # This is unlikely to change during a run and is quite expensive (since we download it from the network), so # we memoize it. - @lru_cache - def get_enum_values_for_field(self, field_name: str): + @lru_cache(256) + def get_enum_values_for_field(self, model: str, entity: str, attribute: str): """ Returns information on the enum fields for a particular field. @@ -31,7 +31,7 @@ def get_enum_values_for_field(self, field_name: str): """ # Construct the URL we need to access the enumeration information. - url = f"{self.base_url}/enumerations/{field_name}" + url = f"{self.base_url}/enumerations/{model}/{entity}/{attribute}" logging.debug(f"Querying TCCM for attribute info: {url}") # Query the URL. @@ -40,7 +40,12 @@ def get_enum_values_for_field(self, field_name: str): response = requests.get( url, headers={"accept": "application/x-yaml"}, params={"value_only": "true"} ) - if not response.ok: + if response.status_code == 404: + logging.debug( + f"Field not found on TCCM Terminology Service ({field_name}): {response}" + ) + return {} + elif not response.ok: logging.debug(f"Error accessing TCCM Terminology Service: {response}") return {} diff --git a/tests/test_command_line.py b/tests/test_command_line.py new file mode 100644 index 0000000..7bb6085 --- /dev/null +++ b/tests/test_command_line.py @@ -0,0 +1,112 @@ +""" +test_command_line.py: Test the command line interfaces of this program. +""" +import re +import subprocess +import logging +import os + + +def execute_sheet2linkml(args) -> (int, str, str): + """ + Execute sheet2linkml as a command-line tool with the provided arguments. + + :param args: A list of arguments to send to sheet2linkml + :return: A tuple containing the error code, captured STDOUT and captured STDERR respectively + """ + + result = subprocess.run( + ["sheet2linkml"] + args, capture_output=True, encoding="utf-8" + ) + + return result.returncode, result.stdout, result.stderr + + +class TestCommandLine: + """ + Test execution of the `sheet2linkml` script at the command line. + """ + + def test_help(self): + """ + Test whether sheet2linkml accepts `--help` and provides useful information. + """ + (errno, stdout, stderr) = execute_sheet2linkml(["--help"]) + + print(stderr) + + assert errno == 0 + assert "Usage: sheet2linkml" in stdout + assert "--help" in stdout + assert "Show this message and exit." in stdout + assert "ERROR" not in stderr + + def test_execution(self, tmp_path): + """ + Test whether sheet2linkml can be used to process a particular Google Sheet into LinkML, which is located at + https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit?usp=sharing + """ + + output_path = os.path.join(tmp_path, "schema.yaml") + + print(f"Writing execution test output to '{output_path}'") + + (errno, stdout, stderr) = execute_sheet2linkml( + [ + "--google-sheet-id", + "1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I", + "-o", + output_path, + ] + ) + + print(stderr) + assert ( + 'INFO: Google Sheet loaded: GSheetModel with an underlying Google Sheet titled "Test schema for sheet2linkml" containing 8 worksheets' + in stderr + ) + assert ( + 'INFO: Generating LinkML for GSheetModel with an underlying Google Sheet titled "Test schema for sheet2linkml" containing 8 worksheets' + in stderr + ) + assert ( + 'INFO: Generating LinkML for Enum named Subject.breed from worksheet "O_CCDH Enums" containing 8 values' + in stderr + ) + + assert stdout == "" + assert errno == 0 + + assert os.path.exists(output_path) + + # Ensure that we get the same output from sheet2linkml as we expected. + def make_schema_comparable(lines): + """ + Some parts of the schema are not comparable, so we remove them before comparing them. + """ + + output = "" + for line in lines: + if line.startswith("generation_date:"): + continue + if re.match("^\\s*code_set_version: ", line): + continue + output += line + + return output + + dirname = os.path.dirname(__file__) + with open( + os.path.join( + dirname, + "testing-schema-on-gsheet/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I.yaml", + ), + "r", + ) as f: + expected_schema = make_schema_comparable(f.readlines()) + + with open(output_path, "r") as f: + obtained_schema = make_schema_comparable(f.readlines()) + + assert obtained_schema != "" + assert obtained_schema == expected_schema diff --git a/tests/testing-schema-on-gsheet/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I.yaml b/tests/testing-schema-on-gsheet/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I.yaml new file mode 100644 index 0000000..9d321b1 --- /dev/null +++ b/tests/testing-schema-on-gsheet/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I.yaml @@ -0,0 +1,648 @@ +name: CRDC-H +notes: +- Derived from [Test schema for sheet2linkml](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I) +id: https://example.org/crdch +imports: +- linkml:types +license: https://creativecommons.org/publicdomain/zero/1.0/ +prefixes: + linkml: https://w3id.org/linkml/ + crdch: https://example.org/crdch/ + NCIT: http://purl.obolibrary.org/obo/NCIT_ + GDC: http://example.org/gdc/ + PDC: http://example.org/pdc/ + ICDC: http://example.org/icdc/ + HTAN: http://example.org/htan/ +default_prefix: crdch +types: + crdch_string: + name: crdch_string + description: A sequence of Unicode characters. There are no limits on the number + of characters in the string. + notes: + - Derived from [crdch_string in sheet Primitives](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=1915311830) + typeof: string + crdch_integer: + name: crdch_integer + description: An integer number. This data type is based on the decimal type, + but the fractional component is not allowed. There are no restrictions on the + size of the integer. + notes: + - Derived from [crdch_integer in sheet Primitives](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=1915311830) + typeof: integer + crdch_decimal: + name: crdch_decimal + description: A rational number that has a decimal representation. This data type + does not restrict the size or precision of the number. + notes: + - Derived from [crdch_decimal in sheet Primitives](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=1915311830) + typeof: decimal + crdch_boolean: + name: crdch_boolean + description: "Value representing either \u201Ctrue\u201D or \u201Cfalse\u201D\ + . Permissible values (case-sensitive) = \u201Ctrue\u201D, \u201Cfalse\u201D\ + , \u201C1\u201D, \u201C0\u201D." + notes: + - Derived from [crdch_boolean in sheet Primitives](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=1915311830) + typeof: boolean + crdch_dateTime: + name: crdch_dateTime + description: A date and time string specified using a specialized concatenation + of the date and time data types, in the general format YYYY-MM-DDThh:mm:ss+zz:zz. + notes: + - Derived from [crdch_dateTime in sheet Primitives](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=1915311830) + typeof: datetime + crdch_curie: + name: crdch_curie + description: "A compact URI (CURIE), which is a bipartite identifier of the form\ + \ prefix:reference, in which the prefix is a convenient abbreviation of a URI.\ + \ It is expressed in the format \u201Cprefix:reference\u201D. When a mapping\ + \ of prefix to base URI is provided (external to this data type), a CURIE may\ + \ be mapped to a URI." + notes: + - Derived from [crdch_curie in sheet Primitives](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=1915311830) + typeof: uriorcurie + crdch_code: + name: crdch_code + description: '' + notes: + - Derived from [crdch_code in sheet Primitives](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=1915311830) + typeof: string +enums: + enum_CRDCH_Subject_species: + name: enum_CRDCH_Subject_species + description: Autogenerated Enumeration for CRDC-H Subject species + comments: + - 'Name according to TCCM: "CRDC-H.Subject.species"' + code_set: https://terminology.ccdh.io/enumerations/CRDC-H.Subject.species + code_set_version: '2021-12-07T07:25:24.718897+00:00' + enum_CRDCH_Subject_breed: + name: enum_CRDCH_Subject_breed + description: A label given to a group of animals homogeneous in appearance and + other characteristics that distinguish it from other animals of the same species. + notes: + - Derived from [Subject.breed in sheet O_CCDH Enums](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=1251633595) + permissible_values: + Akita: + text: Akita + description: '' + American Staffordshire Terrier: + text: American Staffordshire Terrier + description: '' + Australian Shepherd: + text: Australian Shepherd + description: '' + Basset Hound: + text: Basset Hound + description: '' + Beagle: + text: Beagle + description: '' + Belgian Malinois: + text: Belgian Malinois + description: '' + Bernese Mountain Dog: + text: Bernese Mountain Dog + description: '' + Black and Tan Coonhound: + text: Black and Tan Coonhound + description: '' + enum_CRDCH_Subject_sex: + name: enum_CRDCH_Subject_sex + description: Autogenerated Enumeration for CRDC-H Subject sex + comments: + - 'Name according to TCCM: "CRDC-H.Subject.sex"' + code_set: https://terminology.ccdh.io/enumerations/CRDC-H.Subject.sex + code_set_version: '2021-12-07T07:25:25.116036+00:00' + permissible_values: + unspecified: + text: unspecified + unknown: + text: unknown + female: + text: female + male: + text: male + not reported: + text: not reported + enum_CRDCH_Subject_ethnicity: + name: enum_CRDCH_Subject_ethnicity + description: Autogenerated Enumeration for CRDC-H Subject ethnicity + comments: + - 'Name according to TCCM: "CRDC-H.Subject.ethnicity"' + code_set: https://terminology.ccdh.io/enumerations/CRDC-H.Subject.ethnicity + code_set_version: '2021-12-07T07:25:25.328531+00:00' + permissible_values: + hispanic or latino: + text: hispanic or latino + not reported: + text: not reported + Unknown: + text: Unknown + description: Unknown + not allowed to collect: + text: not allowed to collect + not hispanic or latino: + text: not hispanic or latino + unknown: + text: unknown + enum_CRDCH_Subject_race: + name: enum_CRDCH_Subject_race + description: Autogenerated Enumeration for CRDC-H Subject race + comments: + - 'Name according to TCCM: "CRDC-H.Subject.race"' + code_set: https://terminology.ccdh.io/enumerations/CRDC-H.Subject.race + code_set_version: '2021-12-07T07:25:25.535967+00:00' + permissible_values: + other: + text: other + american indian or alaska native: + text: american indian or alaska native + native hawaiian or other pacific islander: + text: native hawaiian or other pacific islander + asian: + text: asian + not reported: + text: not reported + not allowed to collect: + text: not allowed to collect + white: + text: white + Unknown: + text: Unknown + description: Unknown + black or african american: + text: black or african american + unknown: + text: unknown + enum_CRDCH_Subject_vital_status: + name: enum_CRDCH_Subject_vital_status + description: Autogenerated Enumeration for CRDC-H Subject vital_status + comments: + - 'Name according to TCCM: "CRDC-H.Subject.vital_status"' + code_set: https://terminology.ccdh.io/enumerations/CRDC-H.Subject.vital_status + code_set_version: '2021-12-07T07:25:25.758942+00:00' + permissible_values: + Unknown: + text: Unknown + description: Unknown + Not Reported: + text: Not Reported + description: Not Reported + Alive: + text: Alive + description: ALIVE + Dead: + text: Dead + description: DEAD + enum_CRDCH_Subject_cause_of_death: + name: enum_CRDCH_Subject_cause_of_death + description: Autogenerated Enumeration for CRDC-H Subject cause_of_death + comments: + - 'Name according to TCCM: "CRDC-H.Subject.cause_of_death"' + code_set: https://terminology.ccdh.io/enumerations/CRDC-H.Subject.cause_of_death + code_set_version: '2021-12-07T07:25:25.973650+00:00' + permissible_values: + Spinal Muscular Atrophy: + text: Spinal Muscular Atrophy + description: Spinal Muscular Atrophy + Not Cancer Related: + text: Not Cancer Related + Not Reported: + text: Not Reported + description: Not Reported + Cardiovascular Disorder, NOS: + text: Cardiovascular Disorder, NOS + description: Cardiovascular Disorder, NOS + Renal Disorder, NOS: + text: Renal Disorder, NOS + description: Renal Disorder, NOS + Surgical Complications: + text: Surgical Complications + description: Surgical Procedure Complication + Cancer Related: + text: Cancer Related + End-stage Renal Disease: + text: End-stage Renal Disease + description: Chronic Kidney Disease, Stage 5 + Infection: + text: Infection + description: Infection + Toxicity: + text: Toxicity + description: Toxicity + Unknown: + text: Unknown + description: Unknown +classes: + Subject: + name: Subject + description: Demographics and other administrative information about an individual + or animal receiving care or other health-related services. + notes: + - Derived from [Subject in sheet Subject](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=79247765) + is_a: Entity + attributes: + id: + name: id + description: The 'logical' identifier of the entity within the system of record. The + simple value of this attribute stands for an identifier of this data object + within the system, it can be used as a reference from other objects within + the same system (i.e. primary and foreign keys), and it should be unique + per type of object. The same data object copied to a different system will + likely have a different "id" in the new system since "id" values are system + specific and do not represent persistent business identifiers. Business + identifiers are assigned outside the information system and are captured + in the "identifier" field. The "id" field is more likely to be a serially + or randomly generated value that is assigned to the data object as it is + created in a system. + notes: + - 'Cardinality: 1..1' + comments: + - 'FHIR naming conventions are followed here, where ''id'' is used for logical + identifiers, and ''identifier'' for business identifiers. The identified + entity may have a different ''id'' in a different system, and have many + external business ''identifiers'' associated with it. If an "id" value can + also be considered to be a business identifier, and should be transmissible + to other systems, it should be duplicated in the "identifier" attribute + along with the additional metadata needed to give context to the value of + "id". An entity in this model that does not have an "id" field is considered + to be an embeddable entity that can not stand on its own. ' + examples: + - value: '' + domain: Subject + multivalued: false + range: crdch_string + required: true + identifier: + name: identifier + exact_mappings: + - ICDC:Case.patient_id + description: A 'business' identifier or accession number for the entity, typically + as provided by an external system or authority, that are globally unique + and persist across implementing systems. Also, since these identifiers are + created outside the information system through a specific business process, + the Identifier type has additional attributes to capture this additional + metadata so the actual identifier values are qualified by the context that + created those values. This additional context allows "identifier" instances + to be transmitted as business data across systems while still being able + to trace them back to the system of origin. + notes: + - 'Cardinality: 0..m' + comments: + - FHIR naming conventions are followed here, where 'id' is used for logical + identifiers which are unique only within a given system, and 'identifier' + for business identifiers, which are used to connect entities or share content + across systems. The complex 'Identifier' data type is used to capture + the identifier itself, along with metadata about its source. + examples: + - value: '' + domain: Subject + multivalued: true + inlined_as_list: true + range: Entity + required: false + species: + name: species + description: The scientific binomial name for the species of the subject (e.g. + Homo sapiens, Mus musculus, etc.) + notes: + - 'Cardinality: 0..1' + examples: + - value: Homo sapiens + - value: Canis familiaris + - value: Mus musculus + values_from: crdch:enum_CRDCH_Subject_species + domain: Subject + multivalued: false + range: CodeableConcept + required: false + breed: + name: breed + exact_mappings: + - ICDC:Demographic.breed + description: A label given to a group of animals homogeneous in appearance + and other characteristics that distinguish it from other animals of the + same species. + notes: + - 'Cardinality: 0..1' + examples: + - value: Affenpinscher + - value: Afghan Hound + - value: Airedale Terrier + - value: Akita + values_from: crdch:enum_CRDCH_Subject_breed + domain: Subject + multivalued: false + range: CodeableConcept + required: false + sex: + name: sex + exact_mappings: + - ICDC:Demographic.sex + - GDC:Demographic.gender + - PDC:Demographic.gender + - HTAN:Demographics.Gender + description: The biologic character or quality that distinguishes male and + female from one another as expressed by analysis of the person's gonadal, + morphologic (internal and external), chromosomal, and hormonal characteristics. + notes: + - 'Cardinality: 0..1' + examples: + - value: Female + - value: Intersex + - value: Male + - value: Not Reported + - value: Unknown + - value: Unspecified + values_from: crdch:enum_CRDCH_Subject_sex + domain: Subject + multivalued: false + range: CodeableConcept + required: false + ethnicity: + name: ethnicity + exact_mappings: + - GDC:Clinical.ethnicity + - GDC:Demographic.ethnicity + - PDC:Demographic.ethnicity + - HTAN:Demographics.Ethnicity + description: An individual's self-described social and cultural grouping, + specifically whether an individual describes themselves as Hispanic or Latino. + The provided values are based on the categories defined by the U.S. Office + of Management and Business and used by the U.S. Census Bureau + notes: + - 'Cardinality: 0..1' + comments: + - Applicable only when Subject.species is Homo Sapiens + examples: + - value: Hispanic or Latino + - value: Not Allowed To Collect + - value: Not Hispanic or Latino + - value: Not Reported + - value: Unknown + values_from: crdch:enum_CRDCH_Subject_ethnicity + domain: Subject + multivalued: false + range: CodeableConcept + required: false + race: + name: race + exact_mappings: + - GDC:Clinical.race + - GDC:Demographic.race + - PDC:Demographic.race + - HTAN:Demographics.Race + description: An arbitrary classification of a taxonomic group that is a division + of a species. It usually arises as a consequence of geographical isolation + within a species and is characterized by shared heredity, physical attributes + and behavior, and in the case of humans, by common history, nationality, + or geographic distribution. The provided values are based on the categories + defined by the U.S. Office of Management and Business and used by the U.S. + Census Bureau. + notes: + - 'Cardinality: 0..m' + examples: + - value: American Indian or Alaska Native + - value: Asian + - value: Black or African American + - value: Native Hawaiian or other Pacific Islander + - value: Not Allowed To Collect + - value: Not Reported + - value: Unknown + - value: White + values_from: crdch:enum_CRDCH_Subject_race + domain: Subject + multivalued: true + range: CodeableConcept + required: false + year_of_birth: + name: year_of_birth + exact_mappings: + - GDC:Demographic.year_of_birth + - PDC:Demographic.year_of_birth + - ICDC:Demographic.date_of_birth + - HTAN:Demographics.YearOfBirth + description: Numeric value to represent the calendar year in which an individual + was born. + notes: + - 'Cardinality: 0..1' + comments: + - Applicable only when Subject.species is Homo Sapiens + examples: + - value: '' + domain: Subject + multivalued: false + range: crdch_integer + required: false + vital_status: + name: vital_status + exact_mappings: + - GDC:Demographic.vital_status + - PDC:Demographic.vital_status + - HTAN:Demographics.VitalStatus + description: Coded value indicating the state or condition of being living + or deceased; also includes the case where the vital status is unknown. + notes: + - 'Cardinality: 0..1' + examples: + - value: Alive + - value: Dead + - value: Unknown + - value: Not Reported + values_from: crdch:enum_CRDCH_Subject_vital_status + domain: Subject + multivalued: false + range: CodeableConcept + required: false + age_at_death: + name: age_at_death + close_mappings: + - GDC:Demographic.days_to_death + - PDC:Demographic.days_to_death + description: The age of an individual at the time of death, expressed in days + since birth + notes: + - 'Cardinality: 0..1' + examples: + - value: '' + domain: Subject + multivalued: false + range: Entity + required: false + year_of_death: + name: year_of_death + exact_mappings: + - GDC:Demographic.year_of_death + - PDC:Demographic.year_of_death + description: Numeric value to represent the calendar year in which an individual + died. + notes: + - 'Cardinality: 0..1' + examples: + - value: '' + domain: Subject + multivalued: false + range: crdch_integer + required: false + cause_of_death: + name: cause_of_death + exact_mappings: + - GDC:Demographic.cause_of_death + - PDC:Demographic.cause_of_death + description: Coded value indicating the circumstance or condition that results + in the death of the subject. + notes: + - 'Cardinality: 0..1' + examples: + - value: Cancer Related + - value: Cardiovascular Disorder, NOS + - value: End-stage Renal Disease + - value: '' + values_from: crdch:enum_CRDCH_Subject_cause_of_death + domain: Subject + multivalued: false + range: CodeableConcept + required: false + CodeableConcept: + name: CodeableConcept + description: A representation of a concept that may be defined by or mapped to + one or more codes in code systems (terminologies, ontologies, dictionaries, + code sets, etc) - but may also be defined by the provision of text. + notes: + - Derived from [CodeableConcept in sheet CodeableConcept](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=696864465) + comments: + - 'More than one code may be used in CodeableConcept. The concept may be coded + multiple times in different code systems (or even multiple times in the same + code systems, where multiple forms are possible). Each Coding is a representation + of the concept as described above and may have slightly different granularity + due to the differences in the definitions of the underlying codes. There is + no meaning associated with the ordering of Coding within a CodeableConcept. + A typical use of CodeableConcept is to send the local code that the concept + was coded with, and also one or more translations to publicly defined code systems + such as LOINC or SNOMED CT. ' + is_a: Entity + attributes: + coding: + name: coding + description: A reference to a code defined by a terminology system + notes: + - 'Cardinality: 0..m' + examples: + - value: '' + domain: CodeableConcept + multivalued: true + inlined_as_list: true + range: Coding + required: false + text: + name: text + description: A human language representation of the concept represented by + the Coding. + notes: + - 'Cardinality: 0..1' + comments: + - 'The text attribute represents the concept as entered or chosen by the user; + often the value of text is the same as a display of one of the codings. A + concept that does not have an appropriate or known coding can be represented + using free-text in the text attribute. ' + examples: + - value: '' + domain: CodeableConcept + multivalued: false + range: crdch_string + required: false + Coding: + name: Coding + description: A structured representation of a coded/enumerated data value, that + includes additional metadata about the code and code system. + notes: + - Derived from [Coding in sheet Coding](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=964403102) + is_a: Entity + attributes: + code: + name: code + description: The symbol/value of the code, as defined by the code system. + notes: + - 'Cardinality: 1..1' + examples: + - value: '' + domain: Coding + multivalued: false + range: crdch_string + required: true + label: + name: label + description: A human-readable name for the code, as defined by the code system. + notes: + - 'Cardinality: 0..1' + examples: + - value: '' + domain: Coding + multivalued: false + range: crdch_string + required: false + system: + name: system + description: The URL of the code system where the code is defined. + notes: + - 'Cardinality: 1..1' + examples: + - value: '' + domain: Coding + multivalued: false + range: crdch_string + required: true + system_version: + name: system_version + description: The version of the code system. + notes: + - 'Cardinality: 0..1' + examples: + - value: '' + domain: Coding + multivalued: false + range: crdch_string + required: false + value_set: + name: value_set + description: The identifier of the enumeration / value set that the code is + a part of in the context of this element + notes: + - 'Cardinality: 0..1' + examples: + - value: '' + domain: Coding + multivalued: false + range: crdch_string + required: false + value_set_version: + name: value_set_version + description: The version of the enuemration / value set the code is pulled + from + notes: + - 'Cardinality: 0..1' + examples: + - value: '' + domain: Coding + multivalued: false + range: crdch_string + required: false + tag: + name: tag + description: original value | harmonized value | + notes: + - 'Cardinality: 0..m' + examples: + - value: '' + domain: Coding + multivalued: true + inlined_as_list: true + range: crdch_string + required: false + Entity: + name: Entity + description: Any resource that has its own identifier + notes: + - Derived from [Entity in sheet Entity](https://docs.google.com/spreadsheets/d/1HpsF12vl_CeIzV2TEtANeysnh7cy6YOUDdv6fMXT00I/edit#gid=1810442705) +generation_date: '2021-12-07T07:25:23.360980+00:00'