From 0b3767cb901d184d645e1c62582c77463f922e53 Mon Sep 17 00:00:00 2001 From: steull Date: Wed, 8 Feb 2023 14:34:24 +0100 Subject: [PATCH 01/63] Template files added #90 --- .github/ISSUE_TEMPLATE/issue_template_bug.md | 31 +++++++++++++++++++ .../ISSUE_TEMPLATE/issue_template_feature.md | 19 ++++++++++++ .github/pull_request_template.md | 29 +++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/issue_template_bug.md create mode 100644 .github/ISSUE_TEMPLATE/issue_template_feature.md create mode 100644 .github/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/issue_template_bug.md b/.github/ISSUE_TEMPLATE/issue_template_bug.md new file mode 100644 index 0000000..b15724e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue_template_bug.md @@ -0,0 +1,31 @@ +--- +name: Bug Issue +about: For bugs and errors +title: Your title should make sense if said after "The issue is " +labels: "bug" +assignees: '' + +--- + +## Description of the issue + +Describe the problem in as much detail as possible. +Focus on the expected and current behavior. +If necessary, create a screenshot and insert below. + +## Steps to Reproduce +1. +2. +3. + +## Ideas of solution + +Describe possible ideas for solution and evaluate advantages and disadvantages. + +## Context and Environment +* Version used: +* Operating system: +* Environment setup and (python) version: + +## Workflow checklist +- [ ] I am aware of the workflow in [CONTRIBUTING.md](https://github.com/rl-institut/super-repo/blob/develop/CONTRIBUTING.md) diff --git a/.github/ISSUE_TEMPLATE/issue_template_feature.md b/.github/ISSUE_TEMPLATE/issue_template_feature.md new file mode 100644 index 0000000..200861f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue_template_feature.md @@ -0,0 +1,19 @@ +--- +name: Feature Issue +about: For new ideas, developments and features +title: Your title should make sense if said after "The issue is " +labels: "enhancement" +assignees: '' + +--- + +## Description of the issue + +Describe the problem in as much detail as possible. + +## Ideas of solution + +Describe possible ideas for solution and evaluate advantages and disadvantages. + +## Workflow checklist +- [ ] I am aware of the workflow in [CONTRIBUTING.md](https://github.com/rl-institut/super-repo/blob/develop/CONTRIBUTING.md) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..42b42c3 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,29 @@ +## Summary of the discussion + +Describe the findings of the discussion in the issue or meeting. + +## Type of change (CHANGELOG.md) + +### Added +- Add a new class [(#)](https://github.com/rl-institut/super-repo/pull/) + +### Updated +- Update a definition [(#)](https://github.com/rl-institut/super-repo/pull/) + +### Removed +- Remove a broken link [(#)](https://github.com/rl-institut/super-repo/pull/) + + +## Workflow checklist + +### Automation +Closes # + +### PR-Assignee +- [ ] 🐙 Follow the workflow in [CONTRIBUTING.md](https://github.com/rl-institut/super-repo/blob/develop/CONTRIBUTING.md) +- [ ] 📝 Update the [CHANGELOG.md](https://github.com/rl-institut/super-repo/blob/develop/CHANGELOG.md) +- [ ] 📙 Update the documentation + +### Reviewer +- [ ] 🐙 Follow the [Reviewer Guidelines](https://github.com/rl-institut/super-repo/blob/develop/CONTRIBUTING.md#40-let-someone-else-review-your-pr) +- [ ] 🐙 Provided feedback and show sufficient appreciation for the work done From 86d8c22af72356bb15df1ae2701f76eb677d17d6 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Tue, 28 May 2024 17:26:00 +0200 Subject: [PATCH 02/63] Clean repository and switch to poetry --- .bumpversion.toml | 38 + .cookiecutterrc | 56 - .github/workflows/automated-testing.yml | 4 +- .github/workflows/linter.yml | 35 + .pre-commit-config.yaml | 35 +- .travis.yml | 56 - AUTHORS.rst | 5 +- appveyor.yml | 86 -- ci/appveyor-bootstrap.py | 111 -- ci/appveyor-download.py | 109 -- ci/appveyor-with-compiler.cmd | 23 - ci/bootstrap.py | 58 - ci/templates/.travis.yml | 59 - ci/templates/appveyor.yml | 51 - examples/data/metadata_v14.json | 247 ---- examples/data/metadata_v15.json | 294 ---- ...s_current_metadata_validation_using_omi.py | 106 -- examples/snippets/test_dialect.py | 151 -- scripts/README.md | 6 - scripts/transform_oem141_oem151.py | 360 ----- setup.py | 82 -- src/omi/cli.py | 46 +- src/omi/dialects/__init__.py | 3 - src/omi/dialects/base/__init__.py | 0 src/omi/dialects/base/compiler.py | 89 -- src/omi/dialects/base/dialect.py | 69 - src/omi/dialects/base/parser.py | 125 -- src/omi/dialects/base/register.py | 15 - src/omi/dialects/base/renderer.py | 34 - src/omi/dialects/oep/__init__.py | 3 - src/omi/dialects/oep/compiler.py | 415 ------ src/omi/dialects/oep/conversion.py | 357 ----- src/omi/dialects/oep/dialect.py | 30 - src/omi/dialects/oep/parser.py | 1300 ----------------- src/omi/dialects/oep/renderer.py | 84 -- src/omi/dialects/rdf/__init__.py | 1 - src/omi/dialects/rdf/compiler.py | 405 ----- src/omi/dialects/rdf/dialect.py | 19 - src/omi/dialects/rdf/licenses.py | 401 ----- src/omi/dialects/rdf/namespace.py | 9 - src/omi/dialects/rdf/parser.py | 346 ----- src/omi/dialects/rdf/renderer.py | 8 - src/omi/oem_structures/__init__.py | 0 src/omi/oem_structures/oem_v15.py | 391 ----- src/omi/structure.py | 368 ----- tests/data/metadata_v13.json | 57 - tests/data/metadata_v13_converted.json | 62 - tests/data/metadata_v13_minimal.json | 3 - tests/data/metadata_v14.json | 247 ---- tests/data/metadata_v14.ttl | 202 --- tests/data/metadata_v14_minimal.json | 3 - tests/data/metadata_v14_withoutresource.json | 89 -- tests/data/metadata_v15.json | 262 ---- tests/test_cli/__init__.py | 0 tests/test_cli/test_cli_translation.py | 22 - tests/test_dialects/__init__.py | 0 tests/test_dialects/base/__init__.py | 0 tests/test_dialects/base/parser.py | 62 - tests/test_dialects/internal_structures.py | 641 -------- tests/test_dialects/test_oep/__init__.py | 0 .../test_oep/deactivate-test_roundtrip.py | 59 - tests/test_dialects/test_oep/test_compiler.py | 84 -- tests/test_dialects/test_oep/test_parser.py | 67 - .../test_oep/test_regression/__init__.py | 0 .../test_regression/test_issue86_datetime.py | 105 -- .../test_oep/test_translation.py | 30 - tests/test_dialects/test_rdf/__init__.py | 0 .../test_rdf/deactivate-test_compiler.py | 26 - .../test_rdf/deactivate-test_parser.py | 13 - .../test_rdf/deactivate-test_roundtrip.py | 33 - 70 files changed, 111 insertions(+), 8446 deletions(-) create mode 100644 .bumpversion.toml delete mode 100644 .cookiecutterrc create mode 100644 .github/workflows/linter.yml delete mode 100644 .travis.yml delete mode 100644 appveyor.yml delete mode 100644 ci/appveyor-bootstrap.py delete mode 100755 ci/appveyor-download.py delete mode 100644 ci/appveyor-with-compiler.cmd delete mode 100755 ci/bootstrap.py delete mode 100644 ci/templates/.travis.yml delete mode 100644 ci/templates/appveyor.yml delete mode 100644 examples/data/metadata_v14.json delete mode 100644 examples/data/metadata_v15.json delete mode 100644 examples/snippets/oeps_current_metadata_validation_using_omi.py delete mode 100644 examples/snippets/test_dialect.py delete mode 100644 scripts/README.md delete mode 100644 scripts/transform_oem141_oem151.py delete mode 100644 setup.py delete mode 100644 src/omi/dialects/__init__.py delete mode 100644 src/omi/dialects/base/__init__.py delete mode 100644 src/omi/dialects/base/compiler.py delete mode 100644 src/omi/dialects/base/dialect.py delete mode 100644 src/omi/dialects/base/parser.py delete mode 100644 src/omi/dialects/base/register.py delete mode 100644 src/omi/dialects/base/renderer.py delete mode 100644 src/omi/dialects/oep/__init__.py delete mode 100644 src/omi/dialects/oep/compiler.py delete mode 100644 src/omi/dialects/oep/conversion.py delete mode 100644 src/omi/dialects/oep/dialect.py delete mode 100644 src/omi/dialects/oep/parser.py delete mode 100644 src/omi/dialects/oep/renderer.py delete mode 100644 src/omi/dialects/rdf/__init__.py delete mode 100644 src/omi/dialects/rdf/compiler.py delete mode 100644 src/omi/dialects/rdf/dialect.py delete mode 100644 src/omi/dialects/rdf/licenses.py delete mode 100644 src/omi/dialects/rdf/namespace.py delete mode 100644 src/omi/dialects/rdf/parser.py delete mode 100644 src/omi/dialects/rdf/renderer.py delete mode 100644 src/omi/oem_structures/__init__.py delete mode 100644 src/omi/oem_structures/oem_v15.py delete mode 100644 src/omi/structure.py delete mode 100644 tests/data/metadata_v13.json delete mode 100644 tests/data/metadata_v13_converted.json delete mode 100644 tests/data/metadata_v13_minimal.json delete mode 100644 tests/data/metadata_v14.json delete mode 100644 tests/data/metadata_v14.ttl delete mode 100644 tests/data/metadata_v14_minimal.json delete mode 100644 tests/data/metadata_v14_withoutresource.json delete mode 100644 tests/data/metadata_v15.json delete mode 100644 tests/test_cli/__init__.py delete mode 100644 tests/test_cli/test_cli_translation.py delete mode 100644 tests/test_dialects/__init__.py delete mode 100644 tests/test_dialects/base/__init__.py delete mode 100644 tests/test_dialects/base/parser.py delete mode 100644 tests/test_dialects/internal_structures.py delete mode 100644 tests/test_dialects/test_oep/__init__.py delete mode 100644 tests/test_dialects/test_oep/deactivate-test_roundtrip.py delete mode 100644 tests/test_dialects/test_oep/test_compiler.py delete mode 100644 tests/test_dialects/test_oep/test_parser.py delete mode 100644 tests/test_dialects/test_oep/test_regression/__init__.py delete mode 100644 tests/test_dialects/test_oep/test_regression/test_issue86_datetime.py delete mode 100644 tests/test_dialects/test_oep/test_translation.py delete mode 100644 tests/test_dialects/test_rdf/__init__.py delete mode 100644 tests/test_dialects/test_rdf/deactivate-test_compiler.py delete mode 100644 tests/test_dialects/test_rdf/deactivate-test_parser.py delete mode 100644 tests/test_dialects/test_rdf/deactivate-test_roundtrip.py diff --git a/.bumpversion.toml b/.bumpversion.toml new file mode 100644 index 0000000..cf3944a --- /dev/null +++ b/.bumpversion.toml @@ -0,0 +1,38 @@ +[tool.bumpversion] +current_version = "0.2.1" +parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)" +serialize = ["{major}.{minor}.{patch}"] +search = "{current_version}" +replace = "{new_version}" +regex = false +ignore_missing_version = false +ignore_missing_files = false +tag = true +sign_tags = false +tag_name = "v{new_version}" +tag_message = "v{new_version}" +allow_dirty = false +commit = true +message = "Bump version: {current_version} → {new_version}" +commit_args = "" + +[[tool.bumpversion.files]] +filename = "pyproject.toml" + +[[tool.bumpversion.files]] +filename = "CHANGELOG.md" +search = "[Unreleased]" +replace = "[Unreleased] - {now:%Y-%m-%d}" + +[[tool.bumpversion.files]] +filename = "CHANGELOG.md" +search = "Unreleased" + +[[tool.bumpversion.files]] +filename = "README.rst" + +[[tool.bumpversion.files]] +filename = "docs/conf.py + +[[tool.bumpversion.files]] +filename = "omi/__init__.py" diff --git a/.cookiecutterrc b/.cookiecutterrc deleted file mode 100644 index 4a6d06c..0000000 --- a/.cookiecutterrc +++ /dev/null @@ -1,56 +0,0 @@ -# This file exists so you can easily regenerate your project. -# -# `cookiepatcher` is a convenient shim around `cookiecutter` -# for regenerating projects (it will generate a .cookiecutterrc -# automatically for any template). To use it: -# -# pip install cookiepatcher -# cookiepatcher gh:ionelmc/cookiecutter-pylibrary project-path -# -# See: -# https://pypi.org/project/cookiepatcher -# -# Alternatively, you can run: -# -# cookiecutter --overwrite-if-exists --config-file=project-path/.cookiecutterrc gh:ionelmc/cookiecutter-pylibrary - -default_context: - - _extensions: ['jinja2_time.TimeExtension'] - _template: 'gh:ionelmc/cookiecutter-pylibrary' - appveyor: 'yes' - c_extension_function: 'longest' - c_extension_module: '_omi' - c_extension_optional: 'no' - c_extension_support: 'no' - codacy: 'no' - codeclimate: 'no' - codecov: 'yes' - command_line_interface: 'plain' - command_line_interface_bin_name: 'omi' - coveralls: 'no' - distribution_name: 'omi' - email: 'martinglauer89@gmail.com' - full_name: 'Martin Glauer' - github_username: 'OpenEnergyPlatform' - landscape: 'no' - license: 'AGPL-3.0' - linter: 'flake8' - package_name: 'omi' - project_name: 'omi' - project_short_description: 'A library to process and translate open energy metadata.' - release_date: 'today' - repo_name: 'omi' - requiresio: 'yes' - scrutinizer: 'no' - sphinx_docs: 'yes' - sphinx_doctest: 'yes' - sphinx_theme: 'sphinx-rtd-theme' - test_matrix_configurator: 'no' - test_matrix_separate_coverage: 'no' - test_runner: 'pytest' - travis: 'yes' - version: '0.0.0' - website: 'openenergy-platform.org' - year_from: '2019' - year_to: '2019' diff --git a/.github/workflows/automated-testing.yml b/.github/workflows/automated-testing.yml index 6b77a8b..742f8f6 100644 --- a/.github/workflows/automated-testing.yml +++ b/.github/workflows/automated-testing.yml @@ -5,7 +5,7 @@ on: push: branches: - master - - develop + - dev pull_request: jobs: @@ -26,4 +26,4 @@ jobs: run: pip install tox - name: Run tox # Run tox using the version of Python in `PATH` - run: tox -v \ No newline at end of file + run: tox -v diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 0000000..219fc18 --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,35 @@ +name: CI + +# Enable Buildkit and let compose use it to speed up image building +env: + DOCKER_BUILDKIT: 1 + COMPOSE_DOCKER_CLI_BUILD: 1 + +on: + pull_request: + branches: [ "master", "dev" ] + paths-ignore: [ "docs/**" ] + + push: + branches: [ "master", "dev" ] + paths-ignore: [ "docs/**" ] + +concurrency: + group: ${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + linter: + runs-on: ubuntu-latest + steps: + + - name: Checkout Code Repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.9" + + - name: Run pre-commit + uses: pre-commit/action@v3.0.1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7fc5dad..18e7de9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,35 @@ +exclude: 'docs|node_modules|vendors|migrations|.git|.tox' +default_stages: [commit] +fail_fast: true + repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-json + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-added-large-files + + - repo: https://github.com/pre-commit/mirrors-jshint + rev: v2.13.6 + hooks: + - id: jshint + - repo: https://github.com/psf/black - rev: stable + rev: 23.3.0 hooks: - id: black - language_version: python3 - - repo: https://github.com/pre-commit/mirrors-isort - rev: master + - repo: https://github.com/charliermarsh/ruff-pre-commit + # Ruff version. + rev: 'v0.0.277' + hooks: + - id: ruff + + - repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 hooks: - - id: isort - language_version: python3 + - id: flake8 + args: [ "--config=setup.cfg" ] + additional_dependencies: [ flake8-isort ] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 593c416..0000000 --- a/.travis.yml +++ /dev/null @@ -1,56 +0,0 @@ -language: python -cache: pip -env: - global: - - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - - SEGFAULT_SIGNALS=all - matrix: - - TOXENV=check - - TOXENV=docs -matrix: - include: - - python: '3.5' - env: - - TOXENV=py35,report,codecov - - python: '3.6' - env: - - TOXENV=py36,report,codecov - - python: '3.7' - dist: xenial - env: - - TOXENV=py37,report,codecov - - python: 'pypy3' - env: - - TOXENV=pypy3,report,codecov -before_install: - - python --version - - uname -a - - lsb_release -a -install: - - pip install tox - - virtualenv --version - - easy_install --version - - pip --version - - tox --version - - pip install . - - | - set -ex - if [[ $TRAVIS_PYTHON_VERSION == 'pypy3' ]]; then - (cd $HOME - wget https://downloads.python.org/pypy/pypy3.6-v7.3.1-linux64.tar.bz2 - tar xf pypy3.*.tar.bz2 - pypy3.*/bin/pypy3 -m ensurepip - pypy3.*/bin/pypy3 -m pip install -U virtualenv) - export PATH=$(echo $HOME/pypy3.*/bin):$PATH - export TOXPYTHON=$(echo $HOME/pypy3.*/bin/pypy3) - fi - set +x -script: - - tox -v -after_failure: - - more .tox/log/* | cat - - more .tox/*/log/* | cat -notifications: - email: - on_success: never - on_failure: always diff --git a/AUTHORS.rst b/AUTHORS.rst index d11250d..249f069 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -2,5 +2,6 @@ Authors ======= -* Martin Glauer - openenergy-platform.org -* Jonas Huber - openenergy-platform.org +* Hendrik Huyskens - openenergyplatform.org +* Martin Glauer - openenergyplatform.org +* Jonas Huber - openenergyplatform.org diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index dce598c..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,86 +0,0 @@ -version: '{branch}-{build}' -build: off -cache: - - '%LOCALAPPDATA%\pip\Cache' -environment: - global: - WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' - matrix: - - TOXENV: check - TOXPYTHON: C:\Python36\python.exe - PYTHON_HOME: C:\Python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - TOXENV: 'py27,report,codecov' - TOXPYTHON: C:\Python27\python.exe - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - TOXENV: 'py27,report,codecov' - TOXPYTHON: C:\Python27-x64\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\Python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - - TOXENV: 'py34,report,codecov' - TOXPYTHON: C:\Python34\python.exe - PYTHON_HOME: C:\Python34 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '32' - - TOXENV: 'py34,report,codecov' - TOXPYTHON: C:\Python34-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python34-x64 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '64' - - TOXENV: 'py35,report,codecov' - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - TOXENV: 'py35,report,codecov' - TOXPYTHON: C:\Python35-x64\python.exe - PYTHON_HOME: C:\Python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - - TOXENV: 'py36,report,codecov' - TOXPYTHON: C:\Python36\python.exe - PYTHON_HOME: C:\Python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - TOXENV: 'py36,report,codecov' - TOXPYTHON: C:\Python36-x64\python.exe - PYTHON_HOME: C:\Python36-x64 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '64' - - TOXENV: 'py37,report,codecov' - TOXPYTHON: C:\Python37\python.exe - PYTHON_HOME: C:\Python37 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '32' - - TOXENV: 'py37,report,codecov' - TOXPYTHON: C:\Python37-x64\python.exe - PYTHON_HOME: C:\Python37-x64 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '64' -init: - - ps: echo $env:TOXENV - - ps: ls C:\Python* -install: - - python -u ci\appveyor-bootstrap.py - - '%PYTHON_HOME%\Scripts\virtualenv --version' - - '%PYTHON_HOME%\Scripts\easy_install --version' - - '%PYTHON_HOME%\Scripts\pip --version' - - '%PYTHON_HOME%\Scripts\tox --version' -test_script: - - '%WITH_COMPILER% %PYTHON_HOME%\Scripts\tox' - -on_failure: - - ps: dir "env:" - - ps: get-content .tox\*\log\* -artifacts: - - path: dist\* - -### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): -# on_finish: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/appveyor-bootstrap.py b/ci/appveyor-bootstrap.py deleted file mode 100644 index dd76230..0000000 --- a/ci/appveyor-bootstrap.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -AppVeyor will at least have few Pythons around so there's no point of implementing a bootstrapper in PowerShell. - -This is a port of https://github.com/pypa/python-packaging-user-guide/blob/master/source/code/install.ps1 -with various fixes and improvements that just weren't feasible to implement in PowerShell. -""" -from __future__ import print_function -from os import environ -from os.path import exists -from subprocess import check_call - -try: - from urllib.request import urlretrieve -except ImportError: - from urllib import urlretrieve - -BASE_URL = "https://www.python.org/ftp/python/" -GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" -GET_PIP_PATH = "C:\get-pip.py" -URLS = { - ("2.7", "64"): BASE_URL + "2.7.13/python-2.7.13.amd64.msi", - ("2.7", "32"): BASE_URL + "2.7.13/python-2.7.13.msi", - ("3.4", "64"): BASE_URL + "3.4.4/python-3.4.4.amd64.msi", - ("3.4", "32"): BASE_URL + "3.4.4/python-3.4.4.msi", - ("3.5", "64"): BASE_URL + "3.5.4/python-3.5.4-amd64.exe", - ("3.5", "32"): BASE_URL + "3.5.4/python-3.5.4.exe", - ("3.6", "64"): BASE_URL + "3.6.2/python-3.6.2-amd64.exe", - ("3.6", "32"): BASE_URL + "3.6.2/python-3.6.2.exe", -} -INSTALL_CMD = { - # Commands are allowed to fail only if they are not the last command. Eg: uninstall (/x) allowed to fail. - "2.7": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], - "3.4": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], - "3.5": [["{path}", "/quiet", "TargetDir={home}"]], - "3.6": [["{path}", "/quiet", "TargetDir={home}"]], -} - - -def download_file(url, path): - print("Downloading: {} (into {})".format(url, path)) - progress = [0, 0] - - def report(count, size, total): - progress[0] = count * size - if progress[0] - progress[1] > 1000000: - progress[1] = progress[0] - print("Downloaded {:,}/{:,} ...".format(progress[1], total)) - - dest, _ = urlretrieve(url, path, reporthook=report) - return dest - - -def install_python(version, arch, home): - print("Installing Python", version, "for", arch, "bit architecture to", home) - if exists(home): - return - - path = download_python(version, arch) - print("Installing", path, "to", home) - success = False - for cmd in INSTALL_CMD[version]: - cmd = [part.format(home=home, path=path) for part in cmd] - print("Running:", " ".join(cmd)) - try: - check_call(cmd) - except Exception as exc: - print("Failed command", cmd, "with:", exc) - if exists("install.log"): - with open("install.log") as fh: - print(fh.read()) - else: - success = True - if success: - print("Installation complete!") - else: - print("Installation failed") - - -def download_python(version, arch): - for _ in range(3): - try: - return download_file(URLS[version, arch], "installer.exe") - except Exception as exc: - print("Failed to download:", exc) - print("Retrying ...") - - -def install_pip(home): - pip_path = home + "/Scripts/pip.exe" - python_path = home + "/python.exe" - if exists(pip_path): - print("pip already installed.") - else: - print("Installing pip...") - download_file(GET_PIP_URL, GET_PIP_PATH) - print("Executing:", python_path, GET_PIP_PATH) - check_call([python_path, GET_PIP_PATH]) - - -def install_packages(home, *packages): - cmd = [home + "/Scripts/pip.exe", "install"] - cmd.extend(packages) - check_call(cmd) - - -if __name__ == "__main__": - install_python(environ['PYTHON_VERSION'], environ['PYTHON_ARCH'], environ['PYTHON_HOME']) - install_pip(environ['PYTHON_HOME']) - install_packages(environ['PYTHON_HOME'], "setuptools>=18.0.1", "wheel", "tox", "virtualenv>=13.1.0") diff --git a/ci/appveyor-download.py b/ci/appveyor-download.py deleted file mode 100755 index c119bd6..0000000 --- a/ci/appveyor-download.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python -""" -Use the AppVeyor API to download Windows artifacts. - -Taken from: https://bitbucket.org/ned/coveragepy/src/tip/ci/download_appveyor.py -# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 -# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt -""" -from __future__ import unicode_literals - -import argparse -import os -import zipfile - -import requests - - -def make_auth_headers(): - """Make the authentication headers needed to use the Appveyor API.""" - path = os.path.expanduser("~/.appveyor.token") - if not os.path.exists(path): - raise RuntimeError( - "Please create a file named `.appveyor.token` in your home directory. " - "You can get the token from https://ci.appveyor.com/api-token" - ) - with open(path) as f: - token = f.read().strip() - - headers = { - 'Authorization': 'Bearer {}'.format(token), - } - return headers - - -def download_latest_artifacts(account_project, build_id): - """Download all the artifacts from the latest build.""" - if build_id is None: - url = "https://ci.appveyor.com/api/projects/{}".format(account_project) - else: - url = "https://ci.appveyor.com/api/projects/{}/build/{}".format(account_project, build_id) - build = requests.get(url, headers=make_auth_headers()).json() - jobs = build['build']['jobs'] - print(u"Build {0[build][version]}, {1} jobs: {0[build][message]}".format(build, len(jobs))) - - for job in jobs: - name = job['name'] - print(u" {0}: {1[status]}, {1[artifactsCount]} artifacts".format(name, job)) - - url = "https://ci.appveyor.com/api/buildjobs/{}/artifacts".format(job['jobId']) - response = requests.get(url, headers=make_auth_headers()) - artifacts = response.json() - - for artifact in artifacts: - is_zip = artifact['type'] == "Zip" - filename = artifact['fileName'] - print(u" {0}, {1} bytes".format(filename, artifact['size'])) - - url = "https://ci.appveyor.com/api/buildjobs/{}/artifacts/{}".format(job['jobId'], filename) - download_url(url, filename, make_auth_headers()) - - if is_zip: - unpack_zipfile(filename) - os.remove(filename) - - -def ensure_dirs(filename): - """Make sure the directories exist for `filename`.""" - dirname = os.path.dirname(filename) - if dirname and not os.path.exists(dirname): - os.makedirs(dirname) - - -def download_url(url, filename, headers): - """Download a file from `url` to `filename`.""" - ensure_dirs(filename) - response = requests.get(url, headers=headers, stream=True) - if response.status_code == 200: - with open(filename, 'wb') as f: - for chunk in response.iter_content(16 * 1024): - f.write(chunk) - else: - print(u" Error downloading {}: {}".format(url, response)) - - -def unpack_zipfile(filename): - """Unpack a zipfile, using the names in the zip.""" - with open(filename, 'rb') as fzip: - z = zipfile.ZipFile(fzip) - for name in z.namelist(): - print(u" extracting {}".format(name)) - ensure_dirs(name) - z.extract(name) - - -parser = argparse.ArgumentParser(description='Download artifacts from AppVeyor.') -parser.add_argument('--id', - metavar='PROJECT_ID', - default='MGlauer/omi', - help='Project ID in AppVeyor.') -parser.add_argument('build', - nargs='?', - metavar='BUILD_ID', - help='Build ID in AppVeyor. Eg: master-123') - -if __name__ == "__main__": - # import logging - # logging.basicConfig(level="DEBUG") - args = parser.parse_args() - download_latest_artifacts(args.id, args.build) diff --git a/ci/appveyor-with-compiler.cmd b/ci/appveyor-with-compiler.cmd deleted file mode 100644 index 289585f..0000000 --- a/ci/appveyor-with-compiler.cmd +++ /dev/null @@ -1,23 +0,0 @@ -:: Very simple setup: -:: - if WINDOWS_SDK_VERSION is set then activate the SDK. -:: - disable the WDK if it's around. - -SET COMMAND_TO_RUN=%* -SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows -SET WIN_WDK="c:\Program Files (x86)\Windows Kits\10\Include\wdf" -ECHO SDK: %WINDOWS_SDK_VERSION% ARCH: %PYTHON_ARCH% - -IF EXIST %WIN_WDK% ( - REM See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ - REN %WIN_WDK% 0wdf -) -IF "%WINDOWS_SDK_VERSION%"=="" GOTO main - -SET DISTUTILS_USE_SDK=1 -SET MSSdk=1 -"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% -CALL "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release - -:main -ECHO Executing: %COMMAND_TO_RUN% -CALL %COMMAND_TO_RUN% || EXIT 1 diff --git a/ci/bootstrap.py b/ci/bootstrap.py deleted file mode 100755 index a672e05..0000000 --- a/ci/bootstrap.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import absolute_import, print_function, unicode_literals - -import os -import sys -from os.path import abspath -from os.path import dirname -from os.path import exists -from os.path import join - - -if __name__ == "__main__": - base_path = dirname(dirname(abspath(__file__))) - print("Project path: {0}".format(base_path)) - env_path = join(base_path, ".tox", "bootstrap") - if sys.platform == "win32": - bin_path = join(env_path, "Scripts") - else: - bin_path = join(env_path, "bin") - if not exists(env_path): - import subprocess - - print("Making bootstrap env in: {0} ...".format(env_path)) - try: - subprocess.check_call(["virtualenv", env_path]) - except subprocess.CalledProcessError: - subprocess.check_call([sys.executable, "-m", "virtualenv", env_path]) - print("Installing `jinja2` into bootstrap environment...") - subprocess.check_call([join(bin_path, "pip"), "install", "jinja2"]) - python_executable = join(bin_path, "python") - if not os.path.samefile(python_executable, sys.executable): - print("Re-executing with: {0}".format(python_executable)) - os.execv(python_executable, [python_executable, __file__]) - - import jinja2 - - import subprocess - - jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), - trim_blocks=True, - lstrip_blocks=True, - keep_trailing_newline=True - ) - - tox_environments = [ - line.strip() - # WARNING: 'tox' must be installed globally or in the project'structure virtualenv - for line in subprocess.check_output(['tox', '--listenvs'], universal_newlines=True).splitlines() - ] - tox_environments = [line for line in tox_environments if line not in ['clean', 'report', 'docs', 'check']] - - for name in os.listdir(join("ci", "templates")): - with open(join(base_path, name), "w") as fh: - fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) - print("Wrote {}".format(name)) - print("DONE.") diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml deleted file mode 100644 index 6776daa..0000000 --- a/ci/templates/.travis.yml +++ /dev/null @@ -1,59 +0,0 @@ -language: python -cache: pip -env: - global: - - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - - SEGFAULT_SIGNALS=all - matrix: - - TOXENV=check - - TOXENV=docs -matrix: - include: -{%- for env in tox_environments %}{{ '' }} - - python: '{{ env.split("-")[0] if env.startswith("pypy") else "{0[2]}.{0[3]}".format(env) }}' -{% if env.startswith('py37') %} - dist: xenial -{% endif %} - env: - - TOXENV={{ env }},report,codecov -{%- endfor %}{{ '' }} -before_install: - - python --version - - uname -a - - lsb_release -a -install: - - pip install tox - - virtualenv --version - - easy_install --version - - pip --version - - tox --version - - | - set -ex - if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then - (cd $HOME - wget https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-linux64.tar.bz2 - tar xf pypy2-*.tar.bz2 - pypy2-*/bin/pypy -m ensurepip - pypy2-*/bin/pypy -m pip install -U virtualenv) - export PATH=$(echo $HOME/pypy2-*/bin):$PATH - export TOXPYTHON=$(echo $HOME/pypy2-*/bin/pypy) - fi - if [[ $TRAVIS_PYTHON_VERSION == 'pypy3' ]]; then - (cd $HOME - wget https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-linux64.tar.bz2 - tar xf pypy3-*.tar.bz2 - pypy3-*/bin/pypy3 -m ensurepip - pypy3-*/bin/pypy3 -m pip install -U virtualenv) - export PATH=$(echo $HOME/pypy3-*/bin):$PATH - export TOXPYTHON=$(echo $HOME/pypy3-*/bin/pypy3) - fi - set +x -script: - - tox -v -after_failure: - - more .tox/log/* | cat - - more .tox/*/log/* | cat -notifications: - email: - on_success: never - on_failure: always diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml deleted file mode 100644 index 0006daa..0000000 --- a/ci/templates/appveyor.yml +++ /dev/null @@ -1,51 +0,0 @@ -version: '{branch}-{build}' -build: off -cache: - - '%LOCALAPPDATA%\pip\Cache' -environment: - global: - WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' - matrix: - - TOXENV: check - TOXPYTHON: C:\Python36\python.exe - PYTHON_HOME: C:\Python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' -{% for env in tox_environments %}{{ '' }}{% if env.startswith(('py2', 'py3')) %} - - TOXENV: '{{ env }},report,codecov' - TOXPYTHON: C:\Python{{ env[2:4] }}\python.exe - PYTHON_HOME: C:\Python{{ env[2:4] }} - PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' - PYTHON_ARCH: '32' - - TOXENV: '{{ env }},report,codecov' - TOXPYTHON: C:\Python{{ env[2:4] }}-x64\python.exe - {%- if env.startswith(('py2', 'py34')) %} - - WINDOWS_SDK_VERSION: v7.{{ '1' if env.startswith('py3') else '0' }} - {%- endif %} - - PYTHON_HOME: C:\Python{{ env[2:4] }}-x64 - PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' - PYTHON_ARCH: '64' -{% endif %}{% endfor %} -init: - - ps: echo $env:TOXENV - - ps: ls C:\Python* -install: - - python -u ci\appveyor-bootstrap.py - - '%PYTHON_HOME%\Scripts\virtualenv --version' - - '%PYTHON_HOME%\Scripts\easy_install --version' - - '%PYTHON_HOME%\Scripts\pip --version' - - '%PYTHON_HOME%\Scripts\tox --version' -test_script: - - '%WITH_COMPILER% %PYTHON_HOME%\Scripts\tox' - -on_failure: - - ps: dir "env:" - - ps: get-content .tox\*\log\* -artifacts: - - path: dist\* - -### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): -# on_finish: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/examples/data/metadata_v14.json b/examples/data/metadata_v14.json deleted file mode 100644 index b32323b..0000000 --- a/examples/data/metadata_v14.json +++ /dev/null @@ -1,247 +0,0 @@ -{ - "name": "oep_metadata_table_example_v14", - "title": "Good example title", - "id": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v14", - "description": "example metadata for example data", - "language": [ - "en-GB", - "en-US", - "de-DE", - "fr-FR" - ], - "keywords": [ - "example", - "template", - "test" - ], - "publicationDate": "2018-06-12", - "context": { - "homepage": "https://reiner-lemoine-institut.de/szenariendb/", - "documentation": "https://github.com/OpenEnergyPlatform/organisation/wiki/metadata", - "sourceCode": "https://github.com/OpenEnergyPlatform/examples/tree/master/metadata", - "contact": "https://github.com/Ludee", - "grantNo": "03ET4057", - "fundingAgency": "Bundesministerium für Wirtschaft und Energie", - "fundingAgencyLogo": "https://www.innovation-beratung-foerderung.de/INNO/Redaktion/DE/Bilder/Titelbilder/titel_foerderlogo_bmwi.jpg?__blob=poster&v=2", - "publisherLogo": "https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png" - }, - "spatial": { - "extent": "europe", - "resolution": "100 m" - }, - "temporal": { - "referenceDate": "2016-01-01", - "timeseries": { - "start": "2017-01-01T00:00+01", - "end": "2017-12-31T23:00+01", - "resolution": "1 h", - "alignment": "left", - "aggregationType": "sum" - } - }, - "sources": [ - { - "title": "OpenEnergyPlatform Metadata Example", - "description": "Metadata description", - "path": "https://github.com/OpenEnergyPlatform", - "licenses": [ - { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/legalcode", - "instruction": "You are free: To Share, To Create, To Adapt", - "attribution": "© Reiner Lemoine Institut" - } - ] - }, - { - "title": "OpenStreetMap", - "description": "A collaborative project to create a free editable map of the world", - "path": "https://www.openstreetmap.org/", - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© OpenStreetMap contributors" - } - ] - } - ], - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© Reiner Lemoine Institut © OpenStreetMap contributors" - } - ], - "contributors": [ - { - "title": "Ludee", - "date": "2016-06-16", - "object": "metadata", - "comment": "Create metadata" - }, - { - "title": "Ludee", - "date": "2016-11-22", - "object": "metadata", - "comment": "Update metadata" - }, - { - "title": "Ludee", - "date": "2016-11-22", - "object": "metadata", - "comment": "Update header and license" - }, - { - "title": "Ludee", - "date": "2017-03-16", - "object": "metadata", - "comment": "Add license to source" - }, - { - "title": "Ludee", - "date": "2017-03-28", - "object": "metadata", - "comment": "Add copyright to source and license" - }, - { - "title": "Ludee", - "date": "2017-05-30", - "object": "metadata", - "comment": "Release metadata version 1.3" - }, - { - "title": "Ludee", - "date": "2017-06-26", - "object": "metadata", - "comment": "Move referenceDate into temporal and remove array" - }, - { - "title": "Ludee", - "date": "2018-07-19", - "object": "metadata", - "comment": "Start metadata version 1.4" - }, - { - "title": "Ludee", - "date": "2018-07-26", - "object": "data", - "comment": "Rename table and files" - }, - { - "title": "Ludee", - "date": "2018-10-18", - "object": "metadata", - "comment": "Add contribution object" - }, - { - "title": "christian-rli", - "date": "2018-10-18", - "object": "metadata", - "comment": "Add datapackage compatibility" - }, - { - "title": "Ludee", - "date": "2018-11-02", - "object": "metadata", - "comment": "Release metadata version 1.4" - }, - { - "title": "christian-rli", - "date": "2019-02-05", - "object": "metadata", - "comment": "Apply template structure to example" - }, - { - "title": "Ludee", - "date": "2019-03-22", - "object": "metadata", - "comment": "Hotfix foreignKeys" - }, - { - "title": "Ludee", - "date": "2019-07-09", - "object": "metadata", - "comment": "Release metadata version OEP-1.3.0" - } - ], - "resources": [ - { - "profile": "tabular-data-resource", - "name": "model_draft.oep_metadata_table_example_v14", - "path": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v14", - "format": "PostgreSQL", - "encoding": "UTF-8", - "schema": { - "fields": [ - { - "name": "id", - "description": "Unique identifier", - "type": "serial" - }, - { - "name": "year", - "description": "Reference year", - "type": "integer" - }, - { - "name": "value", - "description": "Example value", - "type": "double precision", - "unit": "MW" - }, - { - "name": "geom", - "description": "Geometry", - "type": "geometry(Point, 4326)" - } - ], - "primaryKey": [ - "id" - ], - "foreignKeys": [ - { - "fields": [ - "year" - ], - "reference": { - "resource": "schema.table", - "fields": [ - "year" - ] - } - } - ] - }, - "dialect": { - "decimalSeparator": "." - } - } - ], - "review": { - "path": "https://github.com/OpenEnergyPlatform/data-preprocessing/wiki", - "badge": "platin" - }, - "metaMetadata": { - "metadataVersion": "OEP-1.4.0", - "metadataLicense": { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/" - } - }, - "_comment": { - "metadata": "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/organisation/wiki/metadata)", - "dates": "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", - "units": "Use a space between numbers and units (100 m)", - "languages": "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", - "licenses": "License name must follow the SPDX License List (https://spdx.org/licenses/)", - "review": "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/wiki)", - "null": "If not applicable use (null)" - } -} \ No newline at end of file diff --git a/examples/data/metadata_v15.json b/examples/data/metadata_v15.json deleted file mode 100644 index 99b0952..0000000 --- a/examples/data/metadata_v15.json +++ /dev/null @@ -1,294 +0,0 @@ -{ - "name": "oep_metadata_table_example_v151", - "title": "Example title for metadata example - Version 1.5.1", - "id": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v151", - "description": "This is an metadata example for example data. There is a corresponding table on the OEP for each metadata version.", - "language": [ - "en-GB", - "en-US", - "de-DE", - "fr-FR" - ], - "subject": [ - { - "name": "energy", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000150" - }, - { - "name": "test dataset", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000408" - } - ], - "keywords": [ - "energy", - "example", - "template", - "test" - ], - "publicationDate": "2022-02-15", - "context": { - "homepage": "https://reiner-lemoine-institut.de/lod-geoss/", - "documentation": "https://openenergy-platform.org/tutorials/jupyter/OEMetadata/", - "sourceCode": "https://github.com/OpenEnergyPlatform/oemetadata/tree/master", - "contact": "Ludee@email.de", - "grantNo": "03EI1005", - "fundingAgency": "Bundesministerium für Wirtschaft und Klimaschutz", - "fundingAgencyLogo": "https://commons.wikimedia.org/wiki/File:BMWi_Logo_2021.svg#/media/File:BMWi_Logo_2021.svg", - "publisherLogo": "https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png" - }, - "spatial": { - "location": null, - "extent": "europe", - "resolution": "100 m" - }, - "temporal": { - "referenceDate": "2016-01-01", - "timeseries": [ - { - "start": "2017-01-01T00:00+01", - "end": "2017-12-31T23:00+01", - "resolution": "1 h", - "alignment": "left", - "aggregationType": "sum" - }, - { - "start": "2018-01-01T00:00+01", - "end": "2019-06-01T23:00+01", - "resolution": "15 min", - "alignment": "right", - "aggregationType": "sum" - } - ] - }, - "sources": [ - { - "title": "OpenEnergyPlatform Metadata Example", - "description": "Metadata description", - "path": "https://github.com/OpenEnergyPlatform", - "licenses": [ - { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/legalcode", - "instruction": "You are free: To Share, To Create, To Adapt", - "attribution": "© Reiner Lemoine Institut" - } - ] - }, - { - "title": "OpenStreetMap", - "description": "A collaborative project to create a free editable map of the world", - "path": "https://www.openstreetmap.org/", - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© OpenStreetMap contributors" - } - ] - } - ], - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© Reiner Lemoine Institut © OpenStreetMap contributors" - } - ], - "contributors": [ - { - "title": "Ludee", - "email": null, - "date": "2021-11-15", - "object": "metadata", - "comment": "Release metadata version OEP-1.5.0" - }, - { - "title": "Ludee", - "email": null, - "date": "2022-02-15", - "object": "metadata", - "comment": "Release metadata version OEP-1.5.1" - } - ], - "resources": [ - { - "profile": "tabular-data-resource", - "name": "model_draft.oep_metadata_table_example_v151", - "path": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v151", - "format": "PostgreSQL", - "encoding": "UTF-8", - "schema": { - "fields": [ - { - "name": "id", - "description": "Unique identifier", - "type": "serial", - "unit": null, - "isAbout": [ - { - "name": null, - "path": null - } - ], - "valueReference": [ - { - "value": null, - "name": null, - "path": null - } - ] - }, - { - "name": "name", - "description": "Example name", - "type": "text", - "unit": null, - "isAbout": [ - { - "name": "written name", - "path": "https://openenergy-platform.org/ontology/oeo/IAO_0000590" - } - ], - "valueReference": [ - { - "value": null, - "name": null, - "path": null - } - ] - }, - { - "name": "type", - "description": "Type of wind farm", - "type": "text", - "unit": null, - "isAbout": [ - { - "name": "wind farm", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000447" - } - ], - "valueReference": [ - { - "value": "onshore ", - "name": "onshore wind farm", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000311" - }, - { - "value": "offshore ", - "name": "offshore wind farm", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000308" - } - ] - }, - { - "name": "year", - "description": "Reference year", - "type": "integer", - "unit": null, - "isAbout": [ - { - "name": "year", - "path": "https://openenergy-platform.org/ontology/oeo/UO_0000036" - } - ], - "valueReference": [ - { - "value": null, - "name": null, - "path": null - } - ] - }, - { - "name": "value", - "description": "Example value", - "type": "double precision", - "unit": "MW", - "isAbout": [ - { - "name": "quantity value", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000350" - } - ], - "valueReference": [ - { - "value": null, - "name": null, - "path": null - } - ] - }, - { - "name": "geom", - "description": "Geometry", - "type": "geometry(Point, 4326)", - "unit": null, - "isAbout": [ - { - "name": "spatial region", - "path": "https://openenergy-platform.org/ontology/oeo/BFO_0000006" - } - ], - "valueReference": [ - { - "value": null, - "name": null, - "path": null - } - ] - } - ], - "primaryKey": [ - "id" - ], - "foreignKeys": [ - { - "fields": [ - "year" - ], - "reference": { - "resource": "schema.table", - "fields": [ - "year" - ] - } - } - ] - }, - "dialect": { - "delimiter": null, - "decimalSeparator": "." - } - } - ], - "@id": "https://databus.dbpedia.org/kurzum/mastr/bnetza-mastr/01.04.00", - "@context": "https://github.com/OpenEnergyPlatform/oemetadata/blob/master/metadata/latest/context.json", - "review": { - "path": "https://github.com/OpenEnergyPlatform/data-preprocessing/issues", - "badge": "Platinum" - }, - "metaMetadata": { - "metadataVersion": "OEP-1.5.1", - "metadataLicense": { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/" - } - }, - "_comment": { - "metadata": "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/oemetadata)", - "dates": "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", - "units": "Use a space between numbers and units (100 m)", - "languages": "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", - "licenses": "License name must follow the SPDX License List (https://spdx.org/licenses/)", - "review": "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/blob/master/data-review/manual/review_manual.md)", - "null": "If not applicable use: null", - "todo": "If a value is not yet available, use: todo" - } -} \ No newline at end of file diff --git a/examples/snippets/oeps_current_metadata_validation_using_omi.py b/examples/snippets/oeps_current_metadata_validation_using_omi.py deleted file mode 100644 index 7a1f1fc..0000000 --- a/examples/snippets/oeps_current_metadata_validation_using_omi.py +++ /dev/null @@ -1,106 +0,0 @@ -from omi.dialects.oep.parser import ParserException -from omi.structure import Compilable - -from omi.dialects.oep import OEP_V_1_4_Dialect, OEP_V_1_5_Dialect -from omi.dialects.oep.compiler import JSONCompiler - -import json - - -# This is a list because I copied and pasted something and normally all oemeta data versions are checked - this is not relevant for you @Jann -METADATA_PARSERS = [OEP_V_1_5_Dialect()] -METADATA_COMPILERS = [OEP_V_1_5_Dialect(), JSONCompiler()] - - -# check 1 - is metadata parseable by OMI -def try_parse_metadata(inp): - """ - - Args: - inp: string or dict or OEPMetadata - - Returns: - Tuple[OEPMetadata or None, string or None]: - The first component is the result of the parsing procedure or `None` if - the parsing failed. The second component is None, if the parsing failed, - otherwise an error message. - - Examples: - - >>> from api.actions import try_parse_metadata - >>> result, error = try_parse_metadata('{"id":"id"}') - >>> error is None - True - - """ - - if isinstance(inp, Compilable): - # already parsed - return inp, None - elif not isinstance(inp, (str, bytes)): - # in order to use the omi parsers, input needs to be str (or bytes) - try: - inp = json.dumps(inp) - except Exception: - return None, "Could not serialize json" - - last_err = None - # try all the dialects - for parser in METADATA_PARSERS: - try: - return parser.parse(inp), None - except ParserException as e: - return None, str(e) - except Exception as e: - last_err = e - return None, str(e) - # APIError(f"Metadata could not be parsed: {last_err}") - # try next dialect - - print(f"Metadata could not be parsed: {last_err}") - -# check 2 - Is metadata compilable by omi (complies with the oemetadata spec) -def try_compile_metadata(inp): - """ - - Args: - inp: OEPMetadata - result of omi.parse(dict) - - Returns: - Tuple[str or None, str or None]: - The first component is the result of the compiling procedure or `None` if - the compiling failed. The second component is None if the compiling failed, - otherwise an error message. - """ - last_err = None - # try all the dialects - for compiler in METADATA_COMPILERS: - try: - return compiler.compile(inp), None - except Exception as e: - last_err = e - # APIError(f"Metadata could not be compiled: {last_err}") - # try next dialect - - print(f"Metadata could not be compiled: {last_err}") - -# use this instead of omis validation -# this is what is happening on the oep before the metadata is saved to persistence -def check_oemetadata_is_oep_compatible(_input_file = "examples/data/metadata_v15.json"): - - with open(_input_file, "rb") as inp: - # file = inp.read() - metadata = json.load(inp) - - metadata_oep, err = try_parse_metadata(metadata) - if not err: - metadata_obj, err = try_compile_metadata(metadata_oep) - else: - raise Exception(err) - - if not err: - metadata_str = json.dumps(metadata_obj, ensure_ascii=False) - return metadata_str - else: - raise Exception(err) - diff --git a/examples/snippets/test_dialect.py b/examples/snippets/test_dialect.py deleted file mode 100644 index 3873fbb..0000000 --- a/examples/snippets/test_dialect.py +++ /dev/null @@ -1,151 +0,0 @@ -from omi.dialects.oep.dialect import OEP_V_1_3_Dialect, OEP_V_1_4_Dialect, OEP_V_1_5_Dialect -from omi.dialects.oep.parser import JSONParser -from omi.dialects.oep.parser import ParserException -from omi.structure import Compilable - -from metadata.latest.schema import OEMETADATA_LATEST_SCHEMA - -import json - - -def parse_and_compile(): - inp = '{"id":"unique_id"}' #or read from json file - dialect1_5 = OEP_V_1_5_Dialect() - parsed = dialect1_5.parse(inp) - print(parsed) - parsed.identifier = "anotehr_unique_id" - compiled = dialect1_5.compile(parsed) - print(compiled) - -def validate_oemetadata(): - parser = JSONParser() - _input_file = "0_local_test/metadata_v15.json" - - # parser = JSONParser_1_5() - # _input_file = "tests/data/metadata_v15.json" - - with open(_input_file, "rb") as inp: - # file = inp.read() - file = json.load(inp) - - # file = parser.parse_from_file(_input_file) - # parser.validate(file, [OEMETADATA_LATEST_SCHEMA]) - return parser.is_valid(file) - - -# validate_oemetadata() - -from omi.dialects.oep import OEP_V_1_4_Dialect, OEP_V_1_5_Dialect -from omi.dialects.oep.compiler import JSONCompiler - -import pathlib - -METADATA_PARSERS = [OEP_V_1_5_Dialect(), OEP_V_1_4_Dialect()] -METADATA_COMPILERS = [OEP_V_1_5_Dialect(), OEP_V_1_4_Dialect(), JSONCompiler()] - -def try_parse_metadata(inp): - """ - - Args: - inp: string or dict or OEPMetadata - - Returns: - Tuple[OEPMetadata or None, string or None]: - The first component is the result of the parsing procedure or `None` if - the parsing failed. The second component is None, if the parsing failed, - otherwise an error message. - - Examples: - - >>> from api.actions import try_parse_metadata - >>> result, error = try_parse_metadata('{"id":"id"}') - >>> error is None - True - - """ - - if isinstance(inp, Compilable): - # already parsed - return inp, None - elif not isinstance(inp, (str, bytes)): - # in order to use the omi parsers, input needs to be str (or bytes) - try: - inp = json.dumps(inp) - except Exception: - return None, "Could not serialize json" - - last_err = None - # try all the dialects - for parser in METADATA_PARSERS: - try: - return parser.parse(inp), None - except ParserException as e: - return None, str(e) - except Exception as e: - last_err = e - return None, str(e) - # APIError(f"Metadata could not be parsed: {last_err}") - # try next dialect - - print(f"Metadata could not be parsed: {last_err}") - - -def try_compile_metadata(inp): - """ - - Args: - inp: OEPMetadata - - Returns: - Tuple[str or None, str or None]: - The first component is the result of the compiling procedure or `None` if - the compiling failed. The second component is None if the compiling failed, - otherwise an error message. - """ - last_err = None - # try all the dialects - for compiler in METADATA_COMPILERS: - try: - return compiler.compile(inp), None - except Exception as e: - last_err = e - # APIError(f"Metadata could not be compiled: {last_err}") - # try next dialect - - print(f"Metadata could not be compiled: {last_err}") - - -def set_table_metadata(metadata): - metadata_oep, err = try_parse_metadata(metadata) - if not err: - metadata_obj, err = try_compile_metadata(metadata_oep) - else: - raise Exception(err) - - if not err: - metadata_str = json.dumps(metadata_obj, ensure_ascii=False) - return metadata_str - else: - raise Exception(err) - - -def save_json(data: json, save_at: pathlib.Path = "examples/data/output/", filename: str = "omi_processed_metadata.json"): - - pathlib.Path(save_at).mkdir(parents=True, exist_ok=True) - with open(f"{save_at}{filename}", "w", encoding="utf-8") as fp: - fp.write(data) - - -def run_oep_case(): - # _input_file = "tests/data/metadata_v15.json" - _input_file = "examples/data/metadata_v15.json" - - with open(_input_file, "rb") as inp: - # file = inp.read() - file = json.load(inp) - - jsn = set_table_metadata(file) - - save_json(data = jsn) - -run_oep_case() \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md deleted file mode 100644 index 45b9c3d..0000000 --- a/scripts/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Scripts - -Scripts are a collection of code snippets or even fully functional code modules. They have been usefull in the past or are still in use for various local tasks. - -# Currently available -- transform_oem141_oem151.py - A pure python based script to transform oemetadata version 1.4 JSON strings to the newer oemetadata version 1.5.1. \ No newline at end of file diff --git a/scripts/transform_oem141_oem151.py b/scripts/transform_oem141_oem151.py deleted file mode 100644 index 0eb7c77..0000000 --- a/scripts/transform_oem141_oem151.py +++ /dev/null @@ -1,360 +0,0 @@ -import os -import json -from copy import deepcopy - - -""" -This script transforms OEMetadata v1.4.1 (oem141) into OEMetadata v.1.5.1 (oem151). -It does so by backfilling the key-values from oem141 files into an empty oem151 template.json. -Default keys and values from oem151 are provided, if keys are not used or values are not filled in oem141. -The method applies for all key-values, except for the ‘foreignKeys‘ key, which is a direct copy of the list in oem141 -and the '_comment' and 'metaMetadata' key, which are not processed, as new oem151 information is provided - -Files are read from ./JSON/v141 -Files are saved to ./JSON/v151 - -""" - -# create directories or pass if exist -oem_versions = ["v141", "v151"] -for oem in oem_versions: - try: - os.makedirs(f"./JSON/{oem}") - except FileExistsError: - print(f"The directory: ./JSON/{oem} exists already") - pass - -# load oem141 file paths -json_path = "./JSON/v141" -json_files = os.listdir(json_path) -all_json_files_paths = [json_path + "/" + json_file for json_file in json_files] - -# oem151 template -oem151 = { - "name": None, - "title": None, - "id": None, - "description": None, - "language": [None], - "subject": [{"name": None, "path": None}], - "keywords": [None], - "publicationDate": None, - "context": { - "homepage": None, - "documentation": None, - "sourceCode": None, - "contact": None, - "grantNo": None, - "fundingAgency": None, - "fundingAgencyLogo": None, - "publisherLogo": None, - }, - "spatial": {"location": None, "extent": None, "resolution": None}, - "temporal": { - "referenceDate": None, - "timeseries": [ - { - "start": None, - "end": None, - "resolution": None, - "alignment": None, - "aggregationType": None, - }, - { - "start": None, - "end": None, - "resolution": None, - "alignment": None, - "aggregationType": None, - }, - ], - }, - "sources": [ - { - "title": None, - "description": None, - "path": None, - "licenses": [ - { - "name": None, - "title": None, - "path": None, - "instruction": None, - "attribution": None, - } - ], - }, - { - "title": None, - "description": None, - "path": None, - "licenses": [ - { - "name": None, - "title": None, - "path": None, - "instruction": None, - "attribution": None, - } - ], - }, - ], - "licenses": [ - { - "name": None, - "title": None, - "path": None, - "instruction": None, - "attribution": None, - } - ], - "contributors": [ - {"title": None, "email": None, "date": None, "object": None, "comment": None} - ], - "resources": [ - { - "profile": None, - "name": None, - "path": None, - "format": None, - "encoding": None, - "schema": { - "fields": [ - { - "name": None, - "description": None, - "type": None, - "unit": None, - "isAbout": [{"name": None, "path": None}], - "valueReference": [{"value": None, "name": None, "path": None}], - }, - { - "name": None, - "description": None, - "type": None, - "unit": None, - "isAbout": [{"name": None, "path": None}], - "valueReference": [{"value": None, "name": None, "path": None}], - }, - ], - "primaryKey": [None], - "foreignKeys": [ - { - "fields": [None], - "reference": {"resource": None, "fields": [None]}, - } - ], - }, - "dialect": {"delimiter": None, "decimalSeparator": "."}, - } - ], - "@id": None, - "@context": None, - "review": {"path": None, "badge": None}, - "metaMetadata": { - "metadataVersion": "OEP-1.5.0", - "metadataLicense": { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/", - }, - }, - "_comment": { - "metadata": "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/oemetadata)", - "dates": "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", - "units": "Use a space between numbers and units (100 m)", - "languages": "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", - "licenses": "License name must follow the SPDX License List (https://spdx.org/licenses/)", - "review": "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/blob/master/data-review/manual/review_manual.md)", - "null": "If not applicable use: null", - "todo": "If a value is not yet available, use: todo", - }, -} - -# create oem template with empty lists in keys: sources, temporal, licenses, contributers, resources -oem151_empty_lists = deepcopy(oem151) -keys_to_empty = ["sources", "temporal", "licenses", "contributors", "resources"] -for keys in keys_to_empty: - oem151_empty_lists[keys] = [] - -# make copy of empty oem151 template -v151_temp_backfill_clean = deepcopy(oem151_empty_lists) - -# prepare oem151 keys and dicts for later reference during backfill - -context_keys = list(oem151["context"].keys()) -spatial_keys = list(oem151["spatial"].keys()) -review_keys = list(oem151["review"].keys()) - -contributer_dict = deepcopy(oem151["contributors"][0]) - -licenses_keys = list(oem151["licenses"][0].keys()) -licenses_dict = deepcopy(oem151["licenses"][0]) - -temporal_dict = deepcopy(oem151["temporal"]) -temporal_dict["timeseries"] = [temporal_dict["timeseries"][0]] -temporal_keys = list(oem151["temporal"].keys()) -ts_keys = list(oem151["temporal"]["timeseries"][0].keys()) - -sources_dict = deepcopy(oem151["sources"][0]) -sources_licenses_keys = list(oem151["sources"][0]["licenses"][0]) -sources_licenses_dict = deepcopy(oem151["sources"][0]["licenses"][0]) - -resources_dict = deepcopy(oem151["resources"][0]) -resources_dict["schema"]["fields"] = [] -resources_schema_keys = list(oem151["resources"][0]["schema"]) -resources_dialect_dict = deepcopy(oem151["resources"][0]["dialect"]) -resources_schema_fields = deepcopy(oem151["resources"][0]["schema"]["fields"][0]) - -# backfill oem141 to oem151 for all json_files in ./JSON/v141 -for json_file in all_json_files_paths: - - # save filename for output - filename = json_file.split("/")[-1].split(".")[0] - - # open oem141 files - with open(json_file, encoding="utf-8") as json_file: - v141_file = json.load(json_file) - - # create a clean and empty 'v151_temp_backfill' for each processed oem141 JSON - v151_temp_backfill = deepcopy(v151_temp_backfill_clean) - - # start backfill - # Note: '_comment' and 'metaMetadata' are not processed, as new oem151 information is provided. - # backfill key-values on 1st level. - first_level_keys = [ - "name", - "title", - "id", - "description", - "language", - "keywords", - "publicationDate", - ] - - for key in first_level_keys: - if key in v141_file and v141_file[key] != "": - v151_temp_backfill[key] = v141_file[key] - else: - print( - f"The key:[{key}] is not present in oemetadata v141, despite being declared in the OEM v141 template " - ) - - # backfill review keys - for key in review_keys: - if key in v141_file["review"].keys() and v141_file["review"][key] != "": - v151_temp_backfill["review"][key] = v141_file["review"][key] - - # backfill context keys - for key in context_keys: - if key in v141_file["context"].keys() and v141_file["context"][key] != "": - v151_temp_backfill["context"][key] = v141_file["context"][key] - - # backfill spatial keys - for key in spatial_keys: - if key in v141_file["spatial"].keys() and v141_file["spatial"][key] != "": - v151_temp_backfill["spatial"][key] = v141_file["spatial"][key] - - # backfill contributors - for index, dict in enumerate(v141_file["contributors"]): - # add own contributer_dict for each contributer - v151_temp_backfill["contributors"].append(deepcopy(contributer_dict)) - for key, value in dict.items(): - if v141_file["contributors"][index][key] != "": - v151_temp_backfill["contributors"][index][key] = value - - # backfill licenses - for index, dict in enumerate(v141_file["licenses"]): - # add own licenses_dict for each licenses - v151_temp_backfill["licenses"].append(deepcopy(licenses_dict)) - for key, value in dict.items(): - if v141_file["licenses"][index][key] != "": - v151_temp_backfill["licenses"][index][key] = value - - # backfill temporal - # Note: in oem141 only 1 temporal_dict should exist, as the key 'temporal' is just dict and not list, as in oem151. - # add own temporal_dict for each temporal. - v151_temp_backfill["temporal"] = temporal_dict - - for key in temporal_keys: - if key == "timeseries": - for timeseries_key in ts_keys: - if v141_file["temporal"][key] is not None and timeseries_key in list( - v141_file["temporal"]["timeseries"].keys() - ): - v151_temp_backfill["temporal"][key][0][timeseries_key] = v141_file[ - "temporal" - ][key][timeseries_key] - else: - print(f"{timeseries_key} is not used and filled in oem141") - - elif key in v141_file["temporal"].keys(): - if v141_file["temporal"][key] != "": - v151_temp_backfill["temporal"][key] = v141_file["temporal"][key] - - # backfill sources - for index, dict in enumerate(v141_file["sources"]): - # add own sources_dict for each source - v151_temp_backfill["sources"].append(deepcopy(sources_dict)) - for key, value in dict.items(): - if key == "licenses" and v141_file["sources"][index]["licenses"]: - for src_lc_key in sources_licenses_keys: - if ( - src_lc_key in list(v141_file["sources"][index]["licenses"][0]) - and v141_file["sources"][index]["licenses"][0][src_lc_key] != "" - ): - v151_temp_backfill["sources"][index][key][0][ - src_lc_key - ] = value[0][src_lc_key] - - elif key == "licenses" and not v141_file["sources"][index]["licenses"]: - v151_temp_backfill["sources"][index][key][0] = sources_licenses_dict - - elif v141_file["sources"][index][key] != "": - v151_temp_backfill["sources"][index][key] = value - - # backfill resources - for index, dict in enumerate(v141_file["resources"]): - # add own resources_dict for each resource - v151_temp_backfill["resources"].append(deepcopy(resources_dict)) - for key, value in dict.items(): - if key == "schema": - if "primaryKey" in value: - v151_temp_backfill["resources"][index][key][ - "primaryKey" - ] = v141_file["resources"][index][key]["primaryKey"] - if "foreignKeys" in value: - v151_temp_backfill["resources"][index][key][ - "foreignKeys" - ] = v141_file["resources"][index][key]["foreignKeys"] - - if "fields" in value: - for index_fields, dict_fields in enumerate( - v141_file["resources"][index]["schema"]["fields"] - ): - v151_temp_backfill["resources"][index]["schema"][ - "fields" - ].append(deepcopy(resources_schema_fields)) - for key_fields, value_fields in dict_fields.items(): - v151_temp_backfill["resources"][index]["schema"]["fields"][ - index_fields - ][key_fields] = value_fields - - elif key == "dialect": - if "delimiter" in value: - v151_temp_backfill["resources"][index][key]["delimiter"] = value[ - "delimiter" - ] - elif "decimalSeparator" in value: - v151_temp_backfill["resources"][index][key][ - "decimalSeparator" - ] = value["decimalSeparator"] - - elif v141_file["resources"][index][key] != "": - v151_temp_backfill["resources"][index][key] = value - - # save backfilled oem151 template to json in ./JSON/v151 - with open( - f"./JSON/v151/{filename}.metadata_oem151.json", "w", encoding="utf-8" - ) as outfile: - json.dump(v151_temp_backfill, outfile, indent=4, ensure_ascii=False) diff --git a/setup.py b/setup.py deleted file mode 100644 index bd60dd0..0000000 --- a/setup.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function - -import io -import re -from glob import glob -from os.path import basename -from os.path import dirname -from os.path import join -from os.path import splitext - -from setuptools import find_packages -from setuptools import setup - - -def read(*names, **kwargs): - with io.open( - join(dirname(__file__), *names), encoding=kwargs.get("encoding", "utf8") - ) as fh: - return fh.read() - - -setup( - name="omi", - version="0.2.1", - license="AGPL-3.0", - description="A library to process and translate open energy metadata.", - long_description="%s\n%s" - % ( - re.compile("^.. start-badges.*^.. end-badges", re.M | re.S).sub( - "", read("README.rst") - ), - re.sub(":[a-z]+:`~?(.*?)`", r"``\1``", read("CHANGELOG.rst")), - ), - author="Martin Glauer", - author_email="martinglauer89@gmail.com", - url="https://github.com/OpenEnergyPlatform/omi", - packages=find_packages("src"), - package_dir={"": "src"}, - py_modules=[splitext(basename(path))[0] for path in glob("src/*.py")], - include_package_data=True, - zip_safe=False, - classifiers=[ - # complete classifier list: http://pypi.python.org/pypi?%3Aaction=list_classifiers - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: GNU Affero General Public License v3", - "Operating System :: Unix", - "Operating System :: POSIX", - "Operating System :: Microsoft :: Windows", - "Programming Language :: Python", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - # uncomment if you test on these interpreters: - # 'Programming Language :: Python :: Implementation :: IronPython', - # 'Programming Language :: Python :: Implementation :: Jython', - # 'Programming Language :: Python :: Implementation :: Stackless', - "Topic :: Utilities", - ], - project_urls={ - "Documentation": "https://omi.readthedocs.io/", - "Changelog": "https://omi.readthedocs.io/en/latest/changelog.html", - "Issue Tracker": "https://github.com/MGlauer/omi/issues", - }, - keywords=[ - # eg: 'keyword1', 'keyword2', 'keyword3', - ], - python_requires=">=3.5", - install_requires=["click", "rdfLib", "python-dateutil", "jsonschema", "oemetadata>=1.5.2"], - tests_require=["tox", "pytest"], - extras_require={ - "dev": ["black", "isort", "pre-commit"] - # eg: - # 'rst': ['docutils>=0.11'], - # ':python_version=="2.6"': ['argparse'], - }, - entry_points={"console_scripts": ["omi = omi.cli:main"]}, -) diff --git a/src/omi/cli.py b/src/omi/cli.py index 024d35e..0a891c0 100644 --- a/src/omi/cli.py +++ b/src/omi/cli.py @@ -16,53 +16,15 @@ """ import click -from omi.dialects import get_dialect -from omi.dialects.oep import conversion - @click.group() -def grp(): - pass - - -@grp.command("translate") -@click.option("-f", help="Dialect identifier of the input") -@click.option("-t", default="oep-v1.5", help="Dialect identifier to translate to") -@click.option("-o", default=None, help="Output file") -@click.option("-omit_nones", default=True, help="Use to either keep or omit all null / none values from the json.") -@click.argument("file_path") -def translate(f, t, o, omit_nones, file_path): - with open(file_path, "r", encoding="utf-8") as infile: - from_dialect = get_dialect(f)() - obj = from_dialect.parse(infile.read()) - to_dialect = get_dialect(t)() - to_dialect._compiler.OMIT_NONE_FIELDS = omit_nones - s = to_dialect.compile_and_render(obj) - if o: - with open(o, "w", encoding="utf-8") as outfile: - outfile.write(s) - else: - print(s) - - -@grp.command("convert") -@click.option( - "-i", - default=None, - help="Input file. Must be a JSON conforming to the oemetadata v1.4 spec.", -) -@click.option( - "-o", - default=None, - help="Output file. Will be a a JSON conforming to the oemetadata v1.5 spec.", -) -def convert(o, i): - conversion.run_conversion(o, i) - print(f"Created updated metadata file: {o}") +def grp() -> None: + """Init click group.""" cli = click.CommandCollection(sources=[grp]) -def main(): +def main() -> None: + """Start click application.""" cli() diff --git a/src/omi/dialects/__init__.py b/src/omi/dialects/__init__.py deleted file mode 100644 index 1407efc..0000000 --- a/src/omi/dialects/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import omi.dialects.oep.dialect -import omi.dialects.rdf.dialect -from omi.dialects.base.register import get_dialect diff --git a/src/omi/dialects/base/__init__.py b/src/omi/dialects/base/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/omi/dialects/base/compiler.py b/src/omi/dialects/base/compiler.py deleted file mode 100644 index f130823..0000000 --- a/src/omi/dialects/base/compiler.py +++ /dev/null @@ -1,89 +0,0 @@ -from omi import structure -from omi.oem_structures import oem_v15 - - -class Compiler: - """ - Compiles :class:`~omi.structure.Compilable` objects into the respective metadata - format. Every omi compiler should inherit from this class - """ - - def visit(self, obj, *args, **kwargs): - """ - Calls the respective compiler for :class:`~omi.structure.Compilable` objects - respective to :attr:`Compilable.__compiler_name__` - - - Parameters - ---------- - obj - Object to compile - - Returns - ------- - Metadata representation of `obj` - - """ - if isinstance(obj, list): - return [self.visit(x) for x in obj if x is not None] - if isinstance(obj, structure.Compilable): - meth = getattr(self, "visit_{name}".format(name=obj.__compiler_name__)) - return meth(obj, *args, **kwargs) - else: - return obj - - def visit_context(self, context: structure.Context, *args, **kwargs): - raise NotImplementedError - - def visit_contributor(self, contributor: structure.Contribution, *args, **kwargs): - raise NotImplementedError - - def visit_language(self, language: structure.Language, *args, **kwargs): - raise NotImplementedError - - def visit_spatial(self, spatial: structure.Spatial, *args, **kwargs): - raise NotImplementedError - - def visit_temporal(self, temporal: structure.Temporal, *args, **kwargs): - raise NotImplementedError - - def visit_timestamp_orientation( - self, ts_orientation: structure.TimestampOrientation, *args, **kwargs - ): - raise NotImplementedError - - def visit_timeseries(self, timeseries: oem_v15.Timeseries, *args, **kwargs): - raise NotImplementedError - - def visit_source(self, source: structure.Source, *args, **kwargs): - raise NotImplementedError - - def visit_license(self, lic: structure.License, *args, **kwargs): - raise NotImplementedError - - def visit_resource(self, resource: structure.Resource, *args, **kwargs): - raise NotImplementedError - - def visit_schema(self, context: structure.Schema, *args, **kwargs): - raise NotImplementedError - - def visit_dialect(self, context: structure.Dialect, *args, **kwargs): - raise NotImplementedError - - def visit_field(self, field: structure.Field, *args, **kwargs): - raise NotImplementedError - - def visit_foreign_key(self, foreign_key: structure.ForeignKey, *args, **kwargs): - raise NotImplementedError - - def visit_reference(self, reference: structure.Reference, *args, **kwargs): - raise NotImplementedError - - def visit_review(self, review: structure.Review, *args, **kwargs): - raise NotImplementedError - - def visit_meta_comment(self, comment: structure.MetaComment, *args, **kwargs): - raise NotImplementedError - - def visit_metadata(self, metadata: structure.OEPMetadata, *args, **kwargs): - raise NotImplementedError diff --git a/src/omi/dialects/base/dialect.py b/src/omi/dialects/base/dialect.py deleted file mode 100644 index 5e4fb21..0000000 --- a/src/omi/dialects/base/dialect.py +++ /dev/null @@ -1,69 +0,0 @@ -from omi.dialects.base.compiler import Compiler -from omi.dialects.base.parser import Parser -from omi.dialects.base.renderer import Renderer -from omi.structure import OEPMetadata - - -class Dialect: - _parser = Parser - _compiler = Compiler - _renderer = Renderer - - def compile(self, obj: OEPMetadata, *args, **kwargs): - """ - Compiles the passed :class:`~omi.structure.OEPMetadata`-object into the - structure fitting for this dialect - - Parameters - ---------- - obj - The :class:`~omi.structure.OEPMetadata`-object to compile - - Returns - ------- - - """ - c = self._compiler() - return c.visit(obj, *args, **kwargs) - - def parse(self, string: str, *args, **kwargs) -> OEPMetadata: - """ - Loads the passed string into an - :class:`~omi.structure.OEPMetadata`-object. - - Parameters - ---------- - string - The string to parse - - Returns - ------- - The :class:`~omi.structure.OEPMetadata`-object represented by `string` - """ - p = self._parser() - return p.parse_from_string(string, *args, **kwargs) - - def render(self, structure, *args, **kwargs) -> str: - """ - Turns the structure used by this dialect into a string - - Parameters - ---------- - structure - The structure to stringify - - Returns - ------- - A string representation of `structure` - """ - r = self._renderer() - return r.render(structure, *args, **kwargs) - - def compile_and_render(self, obj: OEPMetadata, *args, **kwargs): - """ - Combination of :func:`~omi.dialects.base.dialect.Dialect.compile` and :func:`~omi.dialects.base.dialect.Dialect.render`. - """ - c = self._compiler() - r = self._renderer() - compiled = c.visit(obj, *args, **kwargs) - return r.render(compiled, *args, **kwargs) diff --git a/src/omi/dialects/base/parser.py b/src/omi/dialects/base/parser.py deleted file mode 100644 index a3529a2..0000000 --- a/src/omi/dialects/base/parser.py +++ /dev/null @@ -1,125 +0,0 @@ -from typing import TypeVar - -from omi.structure import OEPMetadata - -T = TypeVar("T") - - -class ParserException(Exception): - pass - - -class Parser: - """ - A parser is used to transform to read a specific metadata format and - transform it into the internal metadata representation. - """ - def parse(self, structure: T, *args, **kwargs) -> OEPMetadata: - """ - Transforms the input structure into metadata as used by the - OpenEnergyPlatform - - Parameters - ---------- - inp: str - The input string that should be parsed into OEP metadata - - Returns - ------- - OEPMetadata - OEP metadata represented by `inp` - """ - raise NotImplementedError - - def load_string(self, string: str, *args, **kwargs): - """ - Load a string into the structure represented by the dialect - Parameters - ---------- - string: str - - Returns - ------- - Translates the passed string into the format used as input for this parser - """ - raise NotImplementedError - - def parse_from_string( - self, - string: str, - load_args=None, - parse_args=None, - load_kwargs=None, - parse_kwargs=None, - ) -> OEPMetadata: - """ - Parse a string into :class:`~omi.structure.OEPMetadata` - - Parameters - ---------- - string - - Returns - ------- - - """ - return self.parse( - self.load_string(string, *(load_args or []), **(load_kwargs or {})), - *(parse_args or []), - **(parse_kwargs or {}) - ) - - @staticmethod - def __unpack_file(file, **kwargs): - """ - - Parameters - ---------- - - Returns - ------- - - """ - with open(file, **kwargs, encoding='utf-8') as inp: - return inp.read() - - def parse_from_file(self, file_path, *args, **kwargs): - return self.parse_from_string(self.__unpack_file(file_path), *args, **kwargs) - - def is_valid(self, inp: str) -> bool: - """ - Verify whether `inp` is a sting representation that is parsable by this - parser - - Parameters - ---------- - inp: str - String to verify - - Returns - ------- - bool: - Indicated whether this object is parsable or not - - """ - raise NotImplementedError - - def is_file_valid(self, file: str, **kwargs): - """ - Verify whether the contents of the file under `file` is parsable by this - parser - - Parameters - ---------- - file: str - Path to the file to validate - - **kwargs: - - Returns - ------- - bool: - Returns `True` iff the file's content is parsable - - """ - return self.is_valid(self.__unpack_file(file, **kwargs)) diff --git a/src/omi/dialects/base/register.py b/src/omi/dialects/base/register.py deleted file mode 100644 index a52c4b5..0000000 --- a/src/omi/dialects/base/register.py +++ /dev/null @@ -1,15 +0,0 @@ -from omi.dialects.base.dialect import Dialect - -DIALECT_DICT = {} - - -def register(identifier: str): - def inner(dialect_class: Dialect): - DIALECT_DICT[identifier] = dialect_class - return dialect_class - - return inner - - -def get_dialect(identifier: str) -> Dialect: - return DIALECT_DICT[identifier] diff --git a/src/omi/dialects/base/renderer.py b/src/omi/dialects/base/renderer.py deleted file mode 100644 index 5fa7afd..0000000 --- a/src/omi/dialects/base/renderer.py +++ /dev/null @@ -1,34 +0,0 @@ -class Renderer: - def render(self, inp, *args, **kwargs) -> str: - """ - Transforms the given structure string into a string - - Parameters - ---------- - inp - The structure to render - - Returns - ------- - str - A string representation of `inp` - """ - raise NotImplementedError - - @staticmethod - def __unpack_file(*args, **kwargs): - """ - - Parameters - ---------- - - Returns - ------- - - """ - with open(*args, **kwargs) as inp: - return inp.read() - - def render_to_file(self, inp, file_path, *args, **kwargs): - with open(file_path, "w") as outfile: - outfile.write(self.render(inp, *args, **kwargs)) diff --git a/src/omi/dialects/oep/__init__.py b/src/omi/dialects/oep/__init__.py deleted file mode 100644 index 6c8388f..0000000 --- a/src/omi/dialects/oep/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from omi.dialects.oep.dialect import OEP_V_1_3_Dialect -from omi.dialects.oep.dialect import OEP_V_1_4_Dialect -from omi.dialects.oep.dialect import OEP_V_1_5_Dialect diff --git a/src/omi/dialects/oep/compiler.py b/src/omi/dialects/oep/compiler.py deleted file mode 100644 index 49dc431..0000000 --- a/src/omi/dialects/oep/compiler.py +++ /dev/null @@ -1,415 +0,0 @@ -import datetime -import logging - -from omi import structure -from omi.dialects.base.compiler import Compiler -from omi.oem_structures import oem_v15 - - -def compile_date_or_none(x, format=None): - if isinstance(x, (datetime.datetime, datetime.date)): - if format: - return x.strftime(format) - else: - return x.isoformat() - else: - return x - - -class JSONCompiler(Compiler): - __METADATA_VERSION = "OEP-1.4.0" - OMIT_NONE_FIELDS = False - - def _construct_dict(self, *args, omit_none=None, **kwargs): - """ - Accepts a list of arguments of shape (name: str, field: Compileable) and returns a dictionary that maps - name -> self.visit(field). If `omit_none` is true, fields that are `None` are ignored. - Parameters - ---------- - args - omit_none - kwargs - - Returns - ------- - - """ - omit_none = self.OMIT_NONE_FIELDS - d = { - field_name: self.visit(field) - for field_name, field in args - if (not omit_none) or (field is not None) - } - d.update(**kwargs) - return d - - def visit_context(self, context: structure.Context, *args, **kwargs): - result = self._construct_dict( - ("homepage", context.homepage), - ("documentation", context.documentation), - ("sourceCode", context.source_code), - ("contact", context.contact), - ("grantNo", context.grant_number), - ) - if context.funding_agency is not None: - result["fundingAgency"] = context.funding_agency.name - result["fundingAgencyLogo"] = context.funding_agency.logo - if context.publisher is not None: - result["publisherLogo"] = context.publisher.logo - logging.info( - f"The context is parsed from file with the following values: {context}" - ) - logging.info( - f"The context class is compiled to the following python dict: {result}" - ) - return result - - def visit_contribution(self, contribution: structure.Contribution, *args, **kwargs): - result = self._construct_dict( - ("title", contribution.contributor.name), - ("email", contribution.contributor.email), - ("object", contribution.object), - ("comment", contribution.comment), - ("date", compile_date_or_none(contribution.date, "%Y-%m-%d")), - ) - logging.info( - f"The contributions are parsed from file with the following values: {contribution}" - ) - logging.info( - f"The contributions class is compiled to the following python dict: {result}" - ) - return result - - def visit_language(self, language: structure.Language, *args, **kwargs): - return str(language) - - def visit_spatial(self, spatial: structure.Spatial, *args, **kwargs): - return self._construct_dict( - ("location", spatial.location), - ("extent", spatial.extent), - ("resolution", spatial.resolution), - ) - - def visit_timestamp_orientation( - self, tso: structure.TimestampOrientation, *args, **kwargs - ): - if tso == structure.TimestampOrientation.left: - return "left" - elif tso == structure.TimestampOrientation.middle: - return "middle" - elif tso == structure.TimestampOrientation.right: - return "right" - else: - raise NotImplementedError - - def visit_temporal(self, temporal: structure.Temporal, *args, **kwargs): - start = None - end = None - if temporal.ts_start is not None: - start = compile_date_or_none(temporal.ts_start) - if temporal.ts_end is not None: - end = compile_date_or_none(temporal.ts_end) - return self._construct_dict( - ( - "referenceDate", - compile_date_or_none(temporal.reference_date, "%Y-%m-%d"), - ), - timeseries=self._construct_dict( - ("start", start), - ("end", end), - ("resolution", temporal.ts_resolution), - ("alignment", temporal.ts_orientation), - ("aggregationType", temporal.aggregation), - ), - ) - - def visit_source(self, source: structure.Source, *args, **kwargs): - return self._construct_dict( - ("title", source.title), - ("description", source.description), - ("path", source.path), - ("licenses", source.licenses), - ) - - def visit_license(self, lic: structure.License, *args, **kwargs): - return self._construct_dict( - ("name", lic.identifier), - ("title", lic.name), - ("path", lic.path), - ) - - def visit_terms_of_use(self, terms_of_use: structure.TermsOfUse): - license_kwargs = ( - self.visit(terms_of_use.license) if terms_of_use.license else {} - ) - return self._construct_dict( - ("instruction", terms_of_use.instruction), - ("attribution", terms_of_use.attribution), - **license_kwargs, - ) - - def visit_resource(self, resource: structure.Resource, *args, **kwargs): - return self._construct_dict( - ("profile", resource.profile), - ("name", resource.name), - ("path", resource.path), - ("format", resource.format), - ("encoding", resource.encoding), - ("schema", resource.schema), - ("dialect", resource.dialect), - ) - - def visit_field(self, field: structure.Field, *args, **kwargs): - return self._construct_dict( - ("name", field.name), - ("description", field.description), - ("type", field.type), - ("unit", field.unit), - ) - - def visit_schema(self, schema: structure.Schema, *args, **kwargs): - return self._construct_dict( - ("primaryKey", schema.primary_key), - ("foreignKeys", schema.foreign_keys), - ("fields", schema.fields), - ) - - def visit_dialect(self, dialect: structure.Dialect, *args, **kwargs): - return self._construct_dict( - ("delimiter", dialect.delimiter), - ("decimalSeparator", dialect.decimal_separator), - ) - - def visit_foreign_key(self, foreign_key: structure.ForeignKey, *args, **kwargs): - if foreign_key.references: - source_fields, target_fields, target_resources = zip( - *map(self.visit, foreign_key.references) - ) - target_resource = target_resources[0] - - return self._construct_dict( - ("fields", source_fields), - reference=self._construct_dict( - ("resource", target_resource), - ("fields", target_fields), - ), - ) - else: - raise Exception("Missing reference in foreign key") - - def visit_reference(self, reference: structure.Reference, *args, **kwargs): - return ( - reference.source.name, - reference.target.name, - reference.target.resource.name, - ) - - def visit_review(self, review: structure.Review, *args, **kwargs): - return self._construct_dict(("path", review.path), badge=review.badge) - - def visit_meta_comment(self, comment: structure.MetaComment, *args, **kwargs): - return self._construct_dict( - ("metadata", comment.metadata_info), - ("dates", comment.dates), - ("units", comment.units), - ("languages", comment.languages), - ("licenses", comment.licenses), - ("review", comment.review), - ("null", comment.none), - ) - - def visit_metadata(self, metadata: structure.OEPMetadata, *args, **kwargs): - publication_date = None - if metadata.publication_date is not None: - publication_date = compile_date_or_none( - metadata.publication_date, "%Y-%m-%d" - ) - return self._construct_dict( - ("name", metadata.name), - ("title", metadata.title), - ("id", metadata.identifier), - ("description", metadata.description), - ("keywords", metadata.keywords), - ("publicationDate", publication_date), - ("context", metadata.context), - ("spatial", metadata.spatial), - ("temporal", metadata.temporal), - ("review", metadata.review), - ("language", metadata.languages), - ("sources", metadata.sources), - ("licenses", metadata.license), - ("contributors", metadata.contributions), - ("resources", metadata.resources), - ("_comment", metadata.comment), - metaMetadata=self._construct_dict( - ("metadataVersion", self.__METADATA_VERSION), - metadataLicense=self._construct_dict( - name="CC0-1.0", - title="Creative Commons Zero v1.0 Universal", - path="https://creativecommons.org/publicdomain/zero/1.0/", - ), - ), - ) - - -class JSONCompilerOEM15(JSONCompiler): - """ - Compiles OEMetadata version JSONCompilerOEM15.__METADATA_VERSION . - - Args: - JSONCompiler (object): Parent Class provides compiler methods for all - metadata fields that have not been changed in - the metadata structure. - """ - - __METADATA_VERSION = "OEP-1.5.2" - - def visit(self, obj, *args, **kwargs): - """ - Calls the respective compiler for :class:`~omi.structure.Compilable` objects - respective to :attr:`Compilable.__compiler_name__` - - - Parameters - ---------- - obj - Object to compile - - Returns - ------- - Metadata representation of `obj` - - """ - if isinstance(obj, list): - return [self.visit(x) for x in obj if x is not None] - if isinstance(obj, oem_v15.Compilable): - meth = getattr(self, "visit_{name}".format(name=obj.__compiler_name__)) - return meth(obj, *args, **kwargs) - else: - return obj - - def visit_subject(self, subject: oem_v15.Subject, *args, **kwargs): - return self._construct_dict(("name", subject.name), ("path", subject.path)) - - def visit_timestamp_orientation( - self, tso: oem_v15.TimestampOrientation, *args, **kwargs - ): - if tso == oem_v15.TimestampOrientation.left: - return "left" - elif tso == oem_v15.TimestampOrientation.middle: - return "middle" - elif tso == oem_v15.TimestampOrientation.right: - return "right" - else: - raise NotImplementedError - - def visit_timeseries(self, timeseries: oem_v15.Timeseries, *args, **kwargs): - start = None - end = None - if timeseries.ts_start is not None: - start = compile_date_or_none(timeseries.ts_start) - if timeseries.ts_end is not None: - end = compile_date_or_none(timeseries.ts_end) - return self._construct_dict( - ("start", start), - ("end", end), - ("resolution", timeseries.ts_resolution), - ("alignment", timeseries.ts_orientation), - ("aggregationType", timeseries.aggregation), - ) - - def visit_temporal(self, temporal: oem_v15.Temporal, *args, **kwargs): - return self._construct_dict( - ( - "referenceDate", - compile_date_or_none(temporal.reference_date, "%Y-%m-%d"), - ), - ("timeseries", temporal.timeseries_collection), - ) - - def visit_license(self, lic: oem_v15.License, *args, **kwargs): - return self._construct_dict( - ("name", lic.name), - ("title", lic.title), - ("path", lic.path), - ) - - def visit_isAbout(self, isAbout: oem_v15.IsAbout, *args, **kwargs): - return self._construct_dict(("name", isAbout.name), ("path", isAbout.path)) - - def visit_valueReference( - self, valueReference: oem_v15.ValueReference, *args, **kwargs - ): - return self._construct_dict( - ("value", valueReference.value), - ("name", valueReference.name), - ("path", valueReference.path), - ) - - def visit_field(self, field: oem_v15.Field, *args, **kwargs): - return self._construct_dict( - ("name", field.name), - ("description", field.description), - ("type", field.type), - ("isAbout", field.isAbout), - ("valueReference", field.valueReference), - ("unit", field.unit), - ) - - def visit_meta_comment(self, comment: oem_v15.MetaComment, *args, **kwargs): - return self._construct_dict( - ("metadata", comment.metadata_info), - ("dates", comment.dates), - ("units", comment.units), - ("languages", comment.languages), - ("licenses", comment.licenses), - ("review", comment.review), - ("null", comment.null), - ("todo", comment.todo), - ) - - def visit_metadata(self, metadata: oem_v15.OEPMetadata, *args, **kwargs): - publication_date = None - if metadata.publication_date is not None: - publication_date = compile_date_or_none( - metadata.publication_date, "%Y-%m-%d" - ) - return self._construct_dict( - ("name", metadata.name), - ("title", metadata.title), - ("id", metadata.identifier), - ("description", metadata.description), - ("subject", metadata.subject), - ("language", metadata.languages), - ("keywords", metadata.keywords), - ("publicationDate", publication_date), - ("context", metadata.context), - ("spatial", metadata.spatial), - ("temporal", metadata.temporal), - ("sources", metadata.sources), - ("licenses", metadata.license), - ("contributors", metadata.contributions), - ("resources", metadata.resources), - ("@id", metadata.databus_identifier), - ("@context", metadata.databus_context), - ("review", metadata.review), - metaMetadata=self._construct_dict( - ("metadataVersion", self.__METADATA_VERSION), - metadataLicense=self._construct_dict( - name="CC0-1.0", - title="Creative Commons Zero v1.0 Universal", - path="https://creativecommons.org/publicdomain/zero/1.0/", - ), - ), - _comment=self._construct_dict( - metadata="Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/oemetadata)", - dates="Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", - units="Use a space between numbers and units (100 m)", - languages="Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", - licenses="License name must follow the SPDX License List (https://spdx.org/licenses/)", - review="Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/blob/master/data-review/manual/review_manual.md)", - null="If not applicable use: null", - todo="If a value is not yet available, use: todo", - ), - **kwargs, - ) diff --git a/src/omi/dialects/oep/conversion.py b/src/omi/dialects/oep/conversion.py deleted file mode 100644 index b428307..0000000 --- a/src/omi/dialects/oep/conversion.py +++ /dev/null @@ -1,357 +0,0 @@ -""" -The conversion in OMI is used to translate metadata version, usually from low to high version. - -The Converter class is the base class that provides base funtionality to enable a specific version translateion. - -The Translation class includes all functionality to create missing fields and supply default, none or user input values for each new field. -The translated metadata will be build and can be saved to a file by using convinience methodes provides by the converter or manually saved -to file using OMI's dialect15.compile_and_render(). - -BUGS: -- oeo keys are not included -- value reference and is about got wrong name format in json -""" - -import json -import logging -import pathlib -from datetime import datetime - -from omi import structure -from omi.dialects import get_dialect -from omi.dialects.base.dialect import Dialect -from omi.oem_structures import oem_v15 - - -# NOTE: Maybe change this to abstract class and implement methods in concrete child classes -class Converter: - """ - Base Converter class for oemetadata version conversion. - Provides basic converstion functions and helpers. - All concrete conversion classes should inheret from this class. - - """ - - def __init__( - self, - dielact_id: str = "oep-v1.5", # latest version - metadata: structure.OEPMetadata = None, - ) -> None: - self.dialect_id = dielact_id - self.metadata = metadata - self.omi_version = "OMI-v0.0.8" - - def validate_str_version_format(self): - return NotImplementedError - - def sanitize_oem(self, oemetadata: dict) -> oem_v15.OEPMetadata: - """ - Remove all "" or " " values. Additionaly it is possible to specify a specific - field that will set to none. Key that got a none value will result in a json null - that will not be visible in a json document. - - Args: - oemetadata (oem_v15.OEPMetadata): _description_ - - Returns: - oem_v15.OEPMetadata: _description_ - """ - - omi_dialect = self.detect_oemetadata_dialect(oemetadata) - metadata = omi_dialect._parser().parse(oemetadata) - print(type(omi_dialect)) - - if ( - metadata["metaMetadata"]["metadataVersion"] == "oep-v1.5.1" - ): # NOTE hardcoded - oemetadata_obj: oem_v15.OEPMetadata - # sanitize .... - else: - oemetadata_obj: structure.OEPMetadata - # sanitize .... - - return oemetadata_obj - - def format_version_string(self, version_string: str = None) -> str: - if version_string is not None: - version_string = version_string.lower() - parts = version_string.split(sep="-") - self.dialect_id = parts[0] + "-v" + parts[1][:-2] - return self.dialect_id - - def detect_oemetadata_dialect(self, metadata=None) -> Dialect: - if metadata is None: - try: - version: str = self.metadata["metaMetadata"]["metadataVersion"] - self.dialect_id = self.format_version_string(version_string=version) - logging.info(f"The dectected dialect is: {self.dialect_id}") - except Exception as e: - logging.warning( - { - "exception": f"{e}", - "message": f"Could not detect the dialect based on the Oemetadata json string. The key related to meta-metadata information might not be present in the input metadata json file. Fallback to the default dialect: '{self.dialect_id}'.", - } - ) - else: - try: - version: str = metadata["metaMetadata"]["metadataVersion"] - self.dialect_id = self.format_version_string(version_string=version) - logging.info(f"The dectected dialect is: {self.dialect_id}") - except Exception as e: - logging.warning( - { - "exception": f"{e}", - "message": f"Could not detect the dialect based on the Oemetadata json string. The key related to meta-metadata information might not be present in the input metadata json file. Fallback to the default dialect: '{self.dialect_id}'.", - } - ) - - return get_dialect(identifier=self.dialect_id) - - # NOTE: Add omi version to user? - def set_contribution( - self, - metadata: oem_v15.OEPMetadata, - user: str = "OMI-v0.0.8", - user_email: str = None, - ) -> oem_v15.OEPMetadata: - to_metadata = "oep-v1.5.1" # NOTE hardcoded - contribution = oem_v15.Contribution( - contributor=oem_v15.Person(name=user, email=user_email), - date=datetime.now(), - obj="Metadata conversion", - comment="Update metadata to " - + to_metadata - + " using OMIs metadata conversion tool.", - ) - - metadata = metadata.contributions.append(contribution) - - return metadata - - def convert_oemetadata(): - pass - - -class Metadata14To15Translation(Converter): - """ - Converts/translates the oemetadata object generated based on the input oemetadata.json file to oemetadata-v1.5.1 structure. - - Args: - Converter : Base converter class - """ - - def remove_temporal( - self, metadata14: structure.OEPMetadata - ) -> structure.OEPMetadata: - metadata14.temporal = None - return metadata14 - - @staticmethod - def create_subject(subject: oem_v15.Subject, name: str = "", path: str = ""): - subject = subject() - subject.name = name - subject.path = path - return subject - - @staticmethod - def create_oeo_id(oeo_id: str = None): - return oeo_id - - @staticmethod - def create_oeo_context(oeo_context: str = None): - return oeo_context - - # NOTE: need? - @staticmethod - def create_meta_comment_null( - meta_comment_null: str = "If not applicable use: null", # hardcoded - ): - return meta_comment_null - - @staticmethod - def create_meta_comment_todo( - meta_comment_todo: str = "If a value is not yet available, use: todo", # hardcoded - ): - return meta_comment_todo - - @staticmethod - def create_is_about(is_about: oem_v15.IsAbout, name: str = "", path: str = ""): - is_about = is_about() - is_about.name = name - is_about.path = path - return is_about - - @staticmethod - def create_value_reference( - value_reference: oem_v15.ValueReference, - value: str = "", - name: str = "", - path: str = "", - ): - value_reference = value_reference() - value_reference.value = value - value_reference.name = name - value_reference.path = path - return value_reference - - def convert_timestamp_orientation(self, ts: str): - new_ts = oem_v15.TimestampOrientation.create(ts) - return new_ts - - # NOTE maybe move timeseries element from convert temporal - def convert_timeseries(self, metadata14_temporal: structure.Temporal): - timeseries = None - if metadata14_temporal.ts_start and metadata14_temporal.ts_end is not None: - timeseries = oem_v15.Timeseries( - start=metadata14_temporal.ts_start, - end=metadata14_temporal.ts_end, - resolution=metadata14_temporal.ts_resolution, - ts_orientation=self.convert_timestamp_orientation( - metadata14_temporal.ts_orientation.name - ), - aggregation=metadata14_temporal.aggregation, - ) - return timeseries - - def convert_temporal( - self, - metadata14_temporal: structure.Temporal, - ): - temporal = None - if metadata14_temporal is not None: - temporal = oem_v15.Temporal( - reference_date=metadata14_temporal.reference_date, - timeseries_collection=[self.convert_timeseries(metadata14_temporal)], - ) # NOTE: assume there will be a single timeseries as input because OEM-v1.4 did not support multiple timeseries elements - return temporal - - def convert_meta_comment( - self, metadata14_meta_comment: structure.MetaComment - ) -> oem_v15.MetaComment: - - meta_comment = None - if metadata14_meta_comment is not None: - meta_comment = oem_v15.MetaComment( - metadata_info=metadata14_meta_comment.metadata_info, - dates=metadata14_meta_comment.dates, - units=metadata14_meta_comment.units, - languages=metadata14_meta_comment.languages, - licenses=metadata14_meta_comment.licenses, - review=metadata14_meta_comment.review, - null=self.create_meta_comment_null(), - todo=self.create_meta_comment_todo(), - ) - - return meta_comment - - def convert_ressources_field(self, metadata14_ressources_field: list = None): - field: oem_v15.Field - ressources_fields = [] - if metadata14_ressources_field is not None: - for field in metadata14_ressources_field: - ressources_field = oem_v15.Field( - name=field.name, - description=field.description, - field_type=field.type, - isAbout=[self.create_is_about(oem_v15.IsAbout)], - valueReference=[ - self.create_value_reference(oem_v15.ValueReference) - ], - unit=field.unit, - resource=field.resource, - ) - ressources_fields.append(ressources_field) - else: - ressources_fields = None - - return ressources_fields - - def convert_ressource(self, metadata14_ressources: list): - ressource: oem_v15.Resource - ressources = [] - for ressource in metadata14_ressources: - single_ressource = oem_v15.Resource( - name=ressource.name, - path=ressource.path, - profile=ressource.profile, - resource_format=ressource.format, - encoding=ressource.encoding, - schema=oem_v15.Schema( - fields=self.convert_ressources_field(ressource.schema.fields), - primary_key=ressource.schema.primary_key, - foreign_keys=ressource.schema.foreign_keys, - ), - dialect=ressource.dialect, - ) - ressources.append(single_ressource) - - return ressources - - def build_metadata15(self, metadata: structure.OEPMetadata): - converted_metadata = oem_v15.OEPMetadata( - name=metadata.name, - title=metadata.title, - identifier=metadata.identifier, - description=metadata.description, - subject=[ - self.create_subject(oem_v15.Subject) - ], # NOTE add just one dummy subject object, maybe its better to keep it empty? # add value from user input?? - languages=metadata.languages, - keywords=metadata.keywords, - publication_date=metadata.publication_date, - context=metadata.context, - spatial=metadata.spatial, - temporal=self.convert_temporal( - metadata.temporal - ), # NOTE add value from user input?? - sources=metadata.sources, - terms_of_use=metadata.license, - contributions=metadata.contributions, - resources=self.convert_ressource(metadata.resources), - databus_identifier=self.create_oeo_id(), # NOTE add value from user input?? - databus_context=self.create_oeo_context(), # NOTE add value from user input?? - review=metadata.review, - comment=self.convert_meta_comment(metadata.comment), - ) - - return converted_metadata - - -def read_input_json(file_path: pathlib.Path = "tests/data/metadata_v14.json"): - with open(file_path, "r", encoding="utf-8") as f: - jsn = json.load(f) - - return jsn - - -def save_to_file(metadata: oem_v15.OEPMetadata, file_path: pathlib.Path): - - with open(file_path, "w", encoding="utf-8") as outfile: - outfile.write(metadata) - - -def run_conversion( - to_metadata: str, from_metadata: str, convert=Metadata14To15Translation -): - metadata_file = read_input_json(from_metadata) - convert = convert(metadata=metadata_file) - dialect_input = convert.detect_oemetadata_dialect() - metadata = dialect_input._parser().parse(metadata_file) - convert.set_contribution(metadata) - - converted = convert.build_metadata15(metadata) - - dialect15 = get_dialect("oep-v1.5")() - s = dialect15.compile_and_render(obj=converted) - save_to_file(s, to_metadata) - - return s - - -if __name__ == "__main__": - - # Run conversion with test data - run_conversion( - to_metadata="1_test_results/metadata/conversion_out_oem151.json", - from_metadata="tests/data/metadata_v14.json", - ) diff --git a/src/omi/dialects/oep/dialect.py b/src/omi/dialects/oep/dialect.py deleted file mode 100644 index bca1355..0000000 --- a/src/omi/dialects/oep/dialect.py +++ /dev/null @@ -1,30 +0,0 @@ -from omi.dialects.base.dialect import Dialect -from omi.dialects.base.register import register -from omi.dialects.oep.compiler import JSONCompiler -from omi.dialects.oep.compiler import JSONCompilerOEM15 -from omi.dialects.oep.parser import JSONParser_1_3 -from omi.dialects.oep.parser import JSONParser_1_4 -from omi.dialects.oep.parser import JSONParser_1_5 -from omi.dialects.oep.renderer import JSONRenderer -from omi.oem_structures import oem_v15 - - -@register("oep-v1.3") -class OEP_V_1_3_Dialect(Dialect): - _parser = JSONParser_1_3 - _compiler = None - _renderer = JSONRenderer - - -@register("oep-v1.4") -class OEP_V_1_4_Dialect(Dialect): - _parser = JSONParser_1_4 - _compiler = JSONCompiler - _renderer = JSONRenderer - - -@register("oep-v1.5") -class OEP_V_1_5_Dialect(Dialect): - _parser = JSONParser_1_5 - _compiler = JSONCompilerOEM15 - _renderer = JSONRenderer diff --git a/src/omi/dialects/oep/parser.py b/src/omi/dialects/oep/parser.py deleted file mode 100644 index e670480..0000000 --- a/src/omi/dialects/oep/parser.py +++ /dev/null @@ -1,1300 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import json -import logging -import pathlib -import re - -import dateutil -import jsonschema -from jsonschema import ValidationError -# oemetadata -from metadata.latest.schema import OEMETADATA_LATEST_SCHEMA -from metadata.v130.schema import OEMETADATA_V130_SCHEMA -from metadata.v140.schema import OEMETADATA_V140_SCHEMA -from metadata.v141.schema import OEMETADATA_V141_SCHEMA -from metadata.v150.schema import OEMETADATA_V150_SCHEMA -from metadata.v151.schema import OEMETADATA_V151_SCHEMA - -from omi import structure -from omi.dialects.base.parser import Parser -from omi.dialects.base.parser import ParserException -from omi.oem_structures import oem_v15 - -ALL_OEM_SCHEMAS = [ - OEMETADATA_LATEST_SCHEMA, - OEMETADATA_V151_SCHEMA, - OEMETADATA_V150_SCHEMA, - OEMETADATA_V141_SCHEMA, - OEMETADATA_V140_SCHEMA, - OEMETADATA_V130_SCHEMA, -] - - -def parse_date_or_none(x, fieldname=None, element=None): - if x is None: - pass - elif type(x) == int: - # e.g just a year or a unix timestamp - # NOTE: isinstance(x, int) is also True for a bool, - # which we dont want - pass - elif isinstance(x, str): - # IMPORTANT NOTE: only use dateutil.parser if date part is complete - # if you parse something like '2020' or '2020-01', it will silently - # fill in the missing month/day from the current date! - # in this case, we keep the string, if it is at least is the correct pattern - - if re.match("^[123][0-9]{3}(|-[0-9]{1,2})$", x): - # only year or year-month: keep string - pass - elif re.match("^[123][0-9]{3}-[0-9]{1,2}-[0-9]{1,2}", x): - try: - date_time = dateutil.parser.parse(x) - except Exception: - raise ParserException( - f"In fields {fieldname} {element} element is a invalid value for date: {x}" - ) - if re.match("^[123][0-9]{3}-[0-9]{1,2}-[0-9]{1,2}$", x): - # date only - x = date_time.date() - else: - x = date_time - else: - raise ParserException( - f"In fields {fieldname} {element} element is a invalid value for date: {x}" - ) - else: - raise ParserException( - f"In fields {fieldname} {element} element is a invalid type for date: {type(x)}" - ) - return x - - -def create_report_json( - error_data, # type list[dict] - save_at: pathlib.Path = "reports/", - filename: str = "report.json", -): - # if len(error_data) >= 1: - pathlib.Path(save_at).mkdir(parents=True, exist_ok=True) - with open(f"{save_at}{filename}", "w", encoding="utf-8") as fp: - json.dump(error_data, fp, indent=4, sort_keys=False) - - print( - f"Created error report containing {len(error_data)} errors at: {save_at}{filename}" - ) - - -class JSONParser(Parser): - def normalize_key_names_of_input(iput: dict): - pass - - def load_string(self, string: str, *args, **kwargs): - return json.loads(string) - - def get_json_validator(self, schema: OEMETADATA_LATEST_SCHEMA): - """ - Get the jsonschema validator that matches the schema. - Also checks if the schmea is valid. - - Args: - schema (OEMETADATA_LATEST_SCHEMA): - - Returns: - validator: jsonschema.Draft202012Validator - """ - jsonschema.Draft202012Validator.check_schema(schema) - validator = jsonschema.Draft202012Validator(schema=schema) - return validator - - def get_any_schema_valid( - self, - metadata: dict, - schemas: list = ALL_OEM_SCHEMAS, - ): - """ - Additional helper funtion - get any schema that is valid for the metadata. - Returns The first valid schema or None - - Args: - schemas (list): _description_ - metadata (dict): _description_ - - Returns: - _type_: _description_ - """ - - valid_schemas = [] - for schema in schemas: - if len(valid_schemas) <= 1: - continue - elif self.is_valid(inp=metadata, schema=schema): - valid_schemas.append(schema) - - if len(valid_schemas) >= 1: - valid_schemas = None - return valid_schemas - - def get_schema_by_metadata_version(self, metadata: dict): - oem_13 = ["1.3", "OEP-1.3"] - oem_14 = "OEP-1.4.0" - oem_141 = "OEP-1.4.1" - oem_15 = "OEP-1.5.0" - oem_151 = "OEP-1.5.1" - - schema = None - - if metadata.get("metadata_version"): - if metadata.get("metadata_version") in oem_13: - schema = OEMETADATA_V130_SCHEMA - - if metadata.get("metaMetadata"): - if metadata.get("metaMetadata")["metadataVersion"] == oem_14: - schema = OEMETADATA_V140_SCHEMA - if metadata.get("metaMetadata")["metadataVersion"] == oem_141: - schema = OEMETADATA_V141_SCHEMA - if metadata.get("metaMetadata")["metadataVersion"] == oem_15: - schema = OEMETADATA_V150_SCHEMA - if metadata.get("metaMetadata")["metadataVersion"] == oem_151: - schema = OEMETADATA_V151_SCHEMA - - # fallback to latest schema if metadata does not contian the exprected metadata version sting - if schema is None: - logging.info( - "Metadata does not contain the expected 'metaMetadata' or 'metadata_version' key. Fallback to latest schema." - ) - schema = OEMETADATA_LATEST_SCHEMA - return schema - - def validate(self, metadata: dict, schema: dict = None, save_report=True): - """ - Check whether the given dictionary adheres to the the json-schema - and oemetadata specification. If errors are found a jsonschema error - report is created in directory 'reports/'. - - Parameters - ---------- - metadata - The dictionary to validate - schema: optional - The jsonschema used for validation. - Default is None. - Returns - ------- - Nothing - """ - - report = [] - if not schema: - schema = self.get_schema_by_metadata_version(metadata=metadata) - validator = self.get_json_validator(schema) - - for error in sorted(validator.iter_errors(instance=metadata), key=str): - # https://python-jsonschema.readthedocs.io/en/stable/errors/#handling-validation-errors - error_dict = { - "oemetadata schema version": schema.get("$id"), - # "json path": error.absolute_path, - "instance path": [i for i in error.absolute_path], - "value that raised the error": error.instance, - "error message": error.message, - "schema_path": [i for i in error.schema_path], - } - report.append(error_dict) - - if save_report: - create_report_json(report) - - return report - - def is_valid(self, inp: dict, schema): - # 1 - valid JSON? - if isinstance(inp, str): - try: - jsn = json.loads(inp) - except ValueError: - return False - else: - jsn = inp - - # 2 - valid OEMETADATA - try: - validator = self.get_json_validator(schema) - validator.validate(jsn) - return True - except ValidationError: - return False - - -class JSONParser_1_3(JSONParser): - def is_valid(self, inp: dict, schema=OEMETADATA_V130_SCHEMA): - # 1 - valid JSON? - if isinstance(inp, str): - try: - jsn = json.loads(inp, encode="utf-8") - except ValueError: - return False - else: - jsn = inp - - # 2 - valid OEMETADATA - try: - validator = self.get_json_validator(schema) - validator.validate(jsn) - return True - except ValidationError: - return False - - def parse(self, json_old, *args, **kwargs): - # context section - context = None - - # filling the spatial section - if "spatial" in json_old: - old_spatial = json_old.get("spatial") - spatial = structure.Spatial( - extent=old_spatial.get("extent"), - resolution=old_spatial.get("resolution"), - ) - else: - spatial = None - - # filling the temporal section - old_temporal = json_old.get("temporal") - if old_temporal is None: - temporal = None - else: - temporal = structure.Temporal( - reference_date=parse_date_or_none(old_temporal.get("reference_date")) - ) - - # filling the source section - # For future reference: There is an important semantic difference between `source = None` and `sources = []` - # The former means that there is no information regarding sources the latter means that there are no sources. - # This is holds for all lists around here - old_sources = json_old.get("sources") - if old_sources is None: - sources = None - else: - sources = [ - structure.Source( - title=old_source.get("name"), - description=old_source.get("description"), - path=old_source.get("url"), - licenses=[ - structure.TermsOfUse(attribution=old_source.get("copyright")) - ], - ) - for old_source in old_sources - ] - - # filling the license section - old_license = json_old.get("license") - if old_license is None: - licenses = None # not []! (see sources) - else: - licenses = [ - structure.TermsOfUse( - lic=structure.License( - identifier=old_license.get("id"), - name=old_license.get("name"), - path=old_license.get("url"), - ), - instruction=old_license.get("instruction"), - attribution=old_license.get("copyright"), - ) - ] - - # filling the contributers section - old_contributors = json_old.get("contributors") - if old_contributors is None: - contributions = None - else: - contributions = [ - structure.Contribution( - contributor=structure.Person( - name=old_contributor.get("name"), - email=old_contributor.get("email"), - ), - date=parse_date_or_none(old_contributor.get("date")), - comment=old_contributor.get("comment"), - ) - for old_contributor in old_contributors - ] - - # extending with script-user information - - old_resources = json_old.get("resources") - if old_resources is None: - resources = None - else: - resources = [] - if len(old_resources) == 0: - raise ParserException( - "The field Resource field is empty! Please provide a description of your data resources e.g. a table schema." - ) - for resource in old_resources: - old_fields = resource.get("fields") - if old_fields is None: - fields = None - else: - fields = [ - structure.Field( - name=field.get("name"), - description=field.get("description"), - unit=field.get("unit"), - ) - for field in old_fields - ] - schema = structure.Schema(fields=fields) - resources.append( - structure.Resource( - name=resource.get("name"), - resource_format="PostgreSQL", - schema=schema, - ) - ) - - review = None - - comment = None - - metadata = structure.OEPMetadata( - title=json_old.get("title"), - description=json_old.get("description"), - languages=json_old.get("language"), - identifier=None, - context=context, - spatial=spatial, - temporal=temporal, - sources=sources, - terms_of_use=licenses, - contributions=contributions, - resources=resources, - review=review, - comment=comment, - ) - return metadata - - -class JSONParser_1_4(JSONParser): - def is_valid(self, inp: dict, schema=OEMETADATA_V141_SCHEMA): - # 1 - valid JSON? - if isinstance(inp, str): - try: - jsn = json.loads(inp, encode="utf-8") - except ValueError: - return False - else: - jsn = inp - - # 2 - valid OEMETADATA - try: - validator = self.get_json_validator(schema) - validator.validate(jsn) - return True - except ValidationError: - return False - - def parse_term_of_use(self, old_license: dict): - return structure.TermsOfUse( - lic=structure.License( - identifier=old_license.get("name"), - name=old_license.get("title"), - path=old_license.get("path"), - ), - instruction=old_license.get("instruction"), - attribution=old_license.get("attribution"), - ) - - def parse(self, json_old: dict, *args, **kwargs): - # context section - if "id" not in json_old: - raise ParserException( - "The metadata string does not contain an id. This field is required." - ) - - inp_context = json_old.get("context") - if inp_context is None: - context = None - else: - funding_agency = None - if "fundingAgency" in inp_context: - funding_agency = structure.Agency( - name=inp_context.get("fundingAgency"), - logo=inp_context.get("fundingAgencyLogo"), - ) - - context = structure.Context( - homepage=inp_context.get("homepage"), - documentation=inp_context.get("documentation"), - source_code=inp_context.get("sourceCode"), - contact=inp_context.get("contact"), - grant_number=inp_context.get("grantNo"), - funding_agency=funding_agency, - publisher=structure.Agency(logo=inp_context.get("publisherLogo")) - if "publisherLogo" in inp_context - else None, - ) - - # filling the spatial section - old_spatial = json_old.get("spatial") - if old_spatial is None: - spatial = None - else: - spatial = structure.Spatial( - location=old_spatial.get("location"), - extent=old_spatial.get("extent"), - resolution=old_spatial.get("resolution"), - ) - - # filling the temporal section - inp_temporal = json_old.get("temporal") - if inp_temporal is None: - temporal = None - else: - inp_timeseries = inp_temporal.get("timeseries") - timeseries = {} - if inp_timeseries is not None: - timeseries = dict( - start=parse_date_or_none(inp_timeseries.get("start")), - end=parse_date_or_none(inp_timeseries.get("end")), - resolution=inp_timeseries.get("resolution"), - ts_orientation=structure.TimestampOrientation.create( - inp_timeseries.get("alignment") - ) - if "alignment" in inp_timeseries - and inp_timeseries["alignment"] is not None - else None, - aggregation=inp_timeseries.get("aggregationType"), - ) - temporal = structure.Temporal( - reference_date=parse_date_or_none(inp_temporal.get("referenceDate")), - **timeseries, - ) - - # filling the source section - old_sources = json_old.get("sources") - if old_sources is None: - sources = None - else: - sources = [ - structure.Source( - title=old_source.get("title"), - description=old_source.get("description"), - path=old_source.get("path"), - licenses=[ - self.parse_term_of_use(l) - for l in old_source.get("licenses", []) - ], - ) - for old_source in old_sources - ] - - # filling the license section - old_licenses = json_old.get("licenses") - if old_licenses is None: - licenses = None - else: - licenses = [ - self.parse_term_of_use(old_license) for old_license in old_licenses - ] - - # filling the contributers section - old_contributors = json_old.get("contributors") - if old_contributors is None: - contributors = None - else: - contributors = [ - structure.Contribution( - contributor=structure.Person( - name=old_contributor.get("title"), - email=old_contributor.get("email"), - ), - date=parse_date_or_none( - old_contributor.get("date"), f"{cont_element}contributors.data" - ), - obj=old_contributor.get("object"), - comment=old_contributor.get("comment"), - ) - for cont_element, old_contributor in enumerate(old_contributors) - ] - - # extending with script-user information - old_resources = json_old.get("resources") - if old_resources is None: - resources = None - # Code added to raise exception when resource is empty - else: - if len(old_resources) == 0: - raise ParserException("Resource field doesn't have any child entity") - resources = [] - for resource in old_resources: - old_schema = resource.get("schema") - if old_schema is None: - schema = None - else: - old_fields = old_schema.get("fields") - if old_fields is None: - fields = None - else: - fields = [ - structure.Field( - name=field.get("name"), - description=field.get("description"), - field_type=field.get("type"), - unit=field.get("unit"), - ) - for field in old_fields - ] - field_dict = {field.name: field for field in fields or []} - old_foreign_keys = old_schema.get("foreignKeys", []) - foreign_keys = [] - for fk in old_foreign_keys: - old_reference = fk.get("reference") - if old_reference is None: - raise ParserException("Foreign key without reference:", fk) - source_fields = [ - field_dict[field_name] - for field_name in fk.get("fields", []) - ] - old_referenced_fields = old_reference.get("fields") - if old_referenced_fields is None: - referenced_fields = None - else: - referenced_fields = [ - structure.Field(name=fk_field) - for fk_field in old_referenced_fields - ] - referenced_resource = structure.Resource( - name=old_reference.get("resource"), - schema=structure.Schema(fields=referenced_fields), - ) - for rf in referenced_fields: - rf.resource = referenced_resource - references = [ - structure.Reference(s, t) - for s, t in zip(source_fields, referenced_fields) - ] - foreign_keys.append(structure.ForeignKey(references=references)) - schema = structure.Schema( - fields=fields, - primary_key=resource["schema"].get("primaryKey"), - foreign_keys=foreign_keys, - ) - old_dialect = resource.get("dialect") - if old_dialect is None: - dialect = None - else: - dialect = structure.Dialect( - delimiter=resource["dialect"].get("delimiter"), - decimal_separator=resource["dialect"].get("decimalSeparator"), - ) - resources.append( - structure.Resource( - profile=resource.get("profile"), - name=resource.get("name"), - path=resource.get("path"), - resource_format=resource.get("format"), - encoding=resource.get("encoding"), - schema=schema, - dialect=dialect, - ) - ) - - inp_review = json_old.get("review") - if inp_review is None: - review = None - else: - review = structure.Review( - path=inp_review.get("path"), badge=inp_review.get("badge") - ) - - inp_comment = json_old.get("_comment") - if inp_comment is None: - comment = None - else: - comment = structure.MetaComment( - metadata_info=inp_comment.get("metadata"), - dates=inp_comment.get("dates"), - units=inp_comment.get("units"), - languages=inp_comment.get("languages"), - licenses=inp_comment.get("licenses"), - review=inp_comment.get("review"), - none=inp_comment.get("null"), - ) - - metadata = structure.OEPMetadata( - name=json_old.get("name"), - title=json_old.get("title"), - identifier=json_old["id"], - description=json_old.get("description"), - languages=json_old.get("language"), - keywords=json_old.get("keywords"), - publication_date=parse_date_or_none(json_old.get("publicationDate")), - context=context, - spatial=spatial, - temporal=temporal, - sources=sources, - terms_of_use=licenses, - contributions=contributors, - resources=resources, - review=review, - comment=comment, - ) - return metadata - - def assert_1_3_metastring(self, json_string: str): - """Checks string conformity to OEP Metadata Standard Version 1.3 - - Parameters - ---------- - json_string: str - The JSON string to be checked. - - Returns - ------- - bool - True if valid, Raises Exception otherwise. - """ - - keys = [ - "title", - "description", - "language", - "spatial", - "temporal", - "sources", - "license", - "contributions", - "resources", - "metadata_version", - ] - subkeys_spatial = ["location", "extent", "resolution"] - subkeys_temporal = ["reference_date", "start", "end", "resolution"] - subkeys_license = ["id", "name", "version", "url", "instruction", "copyright"] - object_subkeys = { - "spatial": subkeys_spatial, - "temporal": subkeys_temporal, - "license": subkeys_license, - } - subkeys_sources = [ - "name", - "description", - "url", - "license", - "copyright", - ] # in list of objects - subkeys_contributors = [ - "name", - "email", - "date", - "comment", - ] # in list of objects - subkeys_resources = ["name", "format", "fields"] # in list of objects - list_subkeys = { - "sources": subkeys_sources, - "contributions": subkeys_contributors, - "resources": subkeys_resources, - } - subkeys_resources_fields = ["name", "description", "unit"] # in list of objects - - json_dict = json.loads(json_string) - try: - # check if all top level keys are present - for i in keys: - if not i in json_dict.keys(): - raise Exception( - 'The String did not contain the key "{0}"'.format(i) - ) - # check for all keys in second level objects - for key in object_subkeys: - for subkey in object_subkeys[key]: - if not subkey in json_dict[key]: - raise Exception( - 'The "{0}" object did not contain a "{1}" key'.format( - key, subkey - ) - ) - # check for all objects in lists if they contain all required keys - for key in list_subkeys: - for list_element in json_dict[key]: - for subkey in list_subkeys[key]: - if not subkey in list_element: - raise Exception( - 'An object in "{0}" is missing a "{1}" key'.format( - key, subkey - ) - ) - except Exception as error: - print( - "The input String does not conform to metadatastring version 1.3 standard" - ) - print(error) - - # TODO make function check all subkeys as well - def has_rogue_keys(self, json_string): - """Checks all keys if they are part of the metadata specification. Gives warnings if not. - - Parameters - ---------- - json_string: str - The JSON string to be checked. - - Returns - ------- - """ - - json_dict = json.loads(json_string) - allowed_keys = [ - "title", - "description", - "language", - "spatial", - "temporal", - "sources", - "license", - "contributions", - "resources", - "metadata_version", - ] - for j in json_dict.keys(): - if not j in allowed_keys: - print('Warning: "{0}" is not among the allowed keys'.format(j)) - - def get_table_name(self, metadata_file): - """Provides the tablename information from the metadata_file - - Parameters - ---------- - metadata_file: str - The metadata script from where the tablename is extracted from. - - Returns - ------- - tablename: str - returns the tablename. - """ - - raise NotImplementedError - - -# TODO: Update parser below - - -class JSONParser_1_5(JSONParser): - def is_valid(self, inp: dict, schema=OEMETADATA_LATEST_SCHEMA): - # 1 - valid JSON? - if isinstance(inp, str): - try: - jsn = json.loads(inp, encode="utf-8") - except ValueError: - return False - else: - jsn = inp - - # 2 - valid OEMETADATA - try: - validator = self.get_json_validator(schema) - validator.validate(jsn) - return True - except ValidationError: - return False - - def parse_from_string( - self, - string: str, - load_args=None, - parse_args=None, - load_kwargs=None, - parse_kwargs=None, - ) -> oem_v15.OEPMetadata: - """ - Parse a string into :class:`~omi.oem_structures.oem_v15.OEPMetadata` - - Parameters - ---------- - string - - Returns - ------- - - """ - return self.parse( - self.load_string(string, *(load_args or []), **(load_kwargs or {})), - *(parse_args or []), - **(parse_kwargs or {}), - ) - - def get_any_value_not_none( - self, - element: dict, - keys, - get_return_default=None, # keys: list[str] - reove as not support by py3.8 - ): - """ - Get the value for a key in a dict - but try multiple key names, in - case they have changed in earlier oemetadata versions. - - Args: - element (dict): dict element of the input metadata - keys (list[str]): list of key name options - get_return_default (_type_, optional): A default return value if key is not present. Defaults to None. - - Returns: - any: By default it is the value at the key or None - but can be any as the value is not strict. - """ - - for key_name in keys: - _element = element.get(key_name, get_return_default) - if _element is None: - continue - - return _element - - def parse_term_of_use(self, old_license: dict): - return oem_v15.TermsOfUse( - lic=oem_v15.License( - name=old_license.get("name"), - title=old_license.get("title"), - path=old_license.get("path"), - ), - instruction=old_license.get("instruction"), - attribution=old_license.get("attribution"), - ) - - def ensure_json_keys_lowercase(json_old: dict): - element = json_old # element must be part of json_old not hole json_old - if isinstance(element, dict): - pass - - if isinstance(element, list): - pass - - def parse(self, json_old: dict, *args, **kwargs): - """_summary_ - - Args: - json_old (dict): _description_ - - Raises: - ParserException: _description_ - ParserException: _description_ - ParserException: _description_ - - Returns: - _type_: _description_ - """ - - if "id" not in json_old: - raise ParserException("metadata string does not contain an id") - - # filling the subject section - old_subjects = json_old.get("subject") - if old_subjects is None: - subject = None - else: - subject = [ - oem_v15.Subject( - name=old_subject.get("name"), path=old_subject.get("path") - ) - for old_subject in old_subjects - ] - - # context section - inp_context: dict = json_old.get("context") - if inp_context is None: - context = None - else: - funding_agency = None - if "fundingAgency" in inp_context: - funding_agency = oem_v15.Agency( - name=inp_context.get("fundingAgency"), - logo=inp_context.get("fundingAgencyLogo"), - ) - - context = oem_v15.Context( - homepage=inp_context.get("homepage"), - documentation=inp_context.get("documentation"), - source_code=inp_context.get("sourceCode"), - contact=inp_context.get("contact"), - grant_number=inp_context.get("grantNo"), - funding_agency=funding_agency, - publisher=oem_v15.Agency(logo=inp_context.get("publisherLogo")) - if "publisherLogo" in inp_context - else None, - ) - - # filling the spatial section - old_spatial: dict = json_old.get("spatial") - if old_spatial is None: - spatial = None - else: - spatial = oem_v15.Spatial( - location=old_spatial.get("location"), - extent=old_spatial.get("extent"), - resolution=old_spatial.get("resolution"), - ) - - # filling the temporal section - inp_temporal: dict = json_old.get("temporal") - if inp_temporal is None: - temporal = None - else: - inp_timeseries: dict = inp_temporal.get("timeseries", []) - if inp_timeseries is None: - timeseries = None - else: - timeseries = [ - oem_v15.Timeseries( - start=parse_date_or_none(ts.get("start")), - end=parse_date_or_none(ts.get("end")), - resolution=ts.get("resolution"), - ts_orientation=oem_v15.TimestampOrientation.create( - ts.get("alignment") - ) - if "alignment" in ts and ts["alignment"] is not None - else None, - aggregation=ts.get("aggregationType"), - ) - for ts in inp_timeseries - ] - temporal = oem_v15.Temporal( - reference_date=parse_date_or_none(inp_temporal.get("referenceDate")), - # TODO: does ** kwargs work on list? - timeseries_collection=timeseries, - ) - - def parse_sources_lincese_including_former_key_names(element: dict): - licenses_new = "licenses" - licenses_old = "license" - - if isinstance(element.get(licenses_new), list): - return [self.parse_term_of_use(l) for l in element.get(licenses_new)] - - if isinstance(element.get(licenses_old), str): - name = element.get(licenses_old) - - # avoide empty structures like [{}] - if name is None: - _result = [] - else: - from_13_license = oem_v15.License(name=name) - _result = [oem_v15.TermsOfUse(lic=from_13_license)] - - return _result - - def parse_source_including_former_key_names(key: dict): - # sources key name options - including key names pre oem v1.4 - key_name_options = { - "title_equal": ["title", "name"], - "path_equal": ["path", "url"], - "licenses_equal": ["licenses", "license"], - } - - source = oem_v15.Source( - title=self.get_any_value_not_none( - element=key, keys=key_name_options.get("title_equal") - ), - description=key.get("description"), - path=self.get_any_value_not_none( - element=key, keys=key_name_options.get("path_equal") - ), - licenses=parse_sources_lincese_including_former_key_names(element=key), - ) - - return source - - # filling the source section - # expected to be a list but can also be a dict in old versions - old_sources: list = json_old.get("sources") - if old_sources is None: - sources = None - else: - sources = [ - parse_source_including_former_key_names(key=old_source) - for old_source in old_sources - ] - - def parse_old_licenses_including_former_key_names(element: dict): - """ - Parse license from imput data - also handle key name variations from - early oemetadata versions. - """ - key_name_options = { - "licenses_equal": ["licenses", "license"], - } - - return self.get_any_value_not_none( - element, key_name_options.get("licenses_equal") - ) - - def parse_license_including_former_structure(licenses_element): - """ - The lincences key was got a structural differnece in former oemetada versions. - In Version 1.3 the key was called lincense and was a singe object/dict, in the - current version this key is calles licences and is a list of objects/dicts. - Also the key names in the dicht are deviating. - """ - if isinstance(licenses_element, list): - _result = [ - self.parse_term_of_use(old_license) for old_license in old_licenses - ] - - if isinstance(licenses_element, dict): - _mapping_former_keys = { - "name": licenses_element.get("id"), - "title": licenses_element.get("name"), - "path": licenses_element.get("url"), - "instruction": licenses_element.get("instruction"), - "attribution": licenses_element.get("copyright"), - } - - _result = [self.parse_term_of_use(old_license=_mapping_former_keys)] - - return _result - - # filling the license section - old_licenses = parse_old_licenses_including_former_key_names(element=json_old) - if old_licenses is None: - licenses = None - else: - licenses = parse_license_including_former_structure( - licenses_element=old_licenses - ) - - # filling the contributers section - old_contributors = json_old.get("contributors") - if old_contributors is None: - contributors = None - else: - contributors = [ - oem_v15.Contribution( - contributor=oem_v15.Person( - name=self.get_any_value_not_none( - element=old_contributor, keys=["title", "name"] - ), - email=old_contributor.get("email"), - ), - date=parse_date_or_none( - old_contributor.get("date"), "contributors.data", cont_element - ), - obj=old_contributor.get("object"), - comment=old_contributor.get("comment"), - ) - for cont_element, old_contributor in enumerate(old_contributors) - ] - - # extending with script-user information - old_resources = json_old.get("resources") - if old_resources is None: - resources = None - # Code added to raise exception when resource is empty - else: - if len(old_resources) == 0: - raise ParserException( - "The resources field is empty! Please provide a description of you data resources e.g. a table schema. See https://github.com/OpenEnergyPlatform/oemetadata/blob/master/metadata/latest/metadata_key_description.md#resource-keys." - ) - resources = [] - for resource in old_resources: - old_schema = resource.get("schema") - if old_schema is None: - schema = None - else: - # filling the fields section - old_fields = old_schema.get("fields") - if old_fields is None: - fields = None - logging.info(f"Parse fields from: {old_fields}") - else: - fields = [] - - for field in old_fields: - # filling the is about section - old_is_abouts = field.get("isAbout") - if old_is_abouts is None: - isAbout = None - else: - isAbout = [ - oem_v15.IsAbout( - name=old_is_about.get("name"), - path=old_is_about.get("path"), - ) - for old_is_about in old_is_abouts - ] - - # filling the value reference section - old_value_references = field.get("valueReference") - if old_value_references is None: - valueReference = None - else: - valueReference = [ - oem_v15.ValueReference( - value=old_value_reference.get("value"), - name=old_value_reference.get("name"), - path=old_value_reference.get("path"), - ) - for old_value_reference in old_value_references - ] - - fields.append( - oem_v15.Field( - name=field.get("name"), - description=field.get("description"), - field_type=field.get("type"), - isAbout=isAbout, - valueReference=valueReference, - unit=field.get("unit"), - ) - ) - - field_dict = {field.name: field for field in fields or []} - old_foreign_keys = old_schema.get("foreignKeys", []) - foreign_keys = [] - for fk in old_foreign_keys: - old_reference = fk.get("reference") - if old_reference is None: - raise ParserException( - "The Foreign key you provided is missing reference information:", - fk, - ) - source_fields = [ - field_dict[field_name] - for field_name in fk.get("fields", []) - ] - old_referenced_fields = old_reference.get("fields") - if old_referenced_fields is None: - referenced_fields = None - else: - referenced_fields = [ - oem_v15.Field(name=fk_field) - for fk_field in old_referenced_fields - ] - referenced_resource = oem_v15.Resource( - name=old_reference.get("resource"), - schema=oem_v15.Schema(fields=referenced_fields), - ) - for rf in referenced_fields: - rf.resource = referenced_resource - references = [ - oem_v15.Reference(s, t) - for s, t in zip(source_fields, referenced_fields) - ] - foreign_keys.append(oem_v15.ForeignKey(references=references)) - schema = oem_v15.Schema( - fields=fields, - primary_key=resource["schema"].get("primaryKey"), - foreign_keys=foreign_keys, - ) - - old_dialect = resource.get("dialect") - if old_dialect is None: - dialect = None - else: - dialect = oem_v15.Dialect( - delimiter=resource["dialect"].get("delimiter"), - decimal_separator=resource["dialect"].get("decimalSeparator"), - ) - resources.append( - oem_v15.Resource( - profile=resource.get("profile"), - name=resource.get("name"), - path=resource.get("path"), - resource_format=resource.get("format"), - encoding=resource.get("encoding"), - schema=schema, - dialect=dialect, - ) - ) - - inp_review = json_old.get("review") - if inp_review is None: - review = None - else: - review = oem_v15.Review( - path=inp_review.get("path"), badge=inp_review.get("badge") - ) - - inp_comment = json_old.get("_comment") - if inp_comment is None: - comment = None - else: - comment = oem_v15.MetaComment( - metadata_info=inp_comment.get("metadata"), - dates=inp_comment.get("dates"), - units=inp_comment.get("units"), - languages=inp_comment.get("languages"), - licenses=inp_comment.get("licenses"), - review=inp_comment.get("review"), - null=inp_comment.get("null"), - todo=inp_comment.get("todo"), - ) - - metadata = oem_v15.OEPMetadata( - name=json_old.get("name"), - title=json_old.get("title"), - identifier=json_old["id"], - description=json_old.get("description"), - subject=subject, - languages=json_old.get("language"), - keywords=json_old.get("keywords"), - publication_date=parse_date_or_none(json_old.get("publicationDate")), - context=context, - spatial=spatial, - temporal=temporal, - sources=sources, - terms_of_use=licenses, - contributions=contributors, - resources=resources, - databus_identifier=json_old.get("@id"), - databus_context=json_old.get("@context"), - review=review, - comment=comment, - ) - return metadata - - # TODO make function check all subkeys as well - def has_rogue_keys(self, json_string): - """Checks all keys if they are part of the metadata specification. Gives warnings if not. - - Parameters - ---------- - json_string: str - The JSON string to be checked. - - Returns - ------- - """ - - json_dict = json.loads(json_string) - allowed_keys = [ - "title", - "description", - "language", - "spatial", - "temporal", - "sources", - "license", - "contributions", - "resources", - "metadata_version", - ] - for j in json_dict.keys(): - if not j in allowed_keys: - print('Warning: "{0}" is not among the allowed keys'.format(j)) - - def get_table_name(self, metadata_file): - """Provides the tablename information from the metadata_file - - Parameters - ---------- - metadata_file: str - The metadata script from where the tablename is extracted from. - - Returns - ------- - tablename: str - returns the tablename. - """ - - raise NotImplementedError diff --git a/src/omi/dialects/oep/renderer.py b/src/omi/dialects/oep/renderer.py deleted file mode 100644 index 5ace540..0000000 --- a/src/omi/dialects/oep/renderer.py +++ /dev/null @@ -1,84 +0,0 @@ -import json -from collections import OrderedDict - -from omi.dialects.base.renderer import Renderer - - -class JSONRenderer(json.JSONEncoder, Renderer): - """ - This enconder sets up a structured oder of the json string when transforming - it from a python OrderedDict - """ - - def __init__(self, *args, **kwargs): - super(JSONRenderer, self).__init__(*args, **kwargs) - self.current_indent = 0 - self.current_indent_str = "" - - def encode(self, o): - # Special Processing for lists - if isinstance(o, (list, tuple)): - primitives_only = True - for item in o: - if isinstance(item, (list, tuple, OrderedDict)): - primitives_only = False - break - output = [] - if primitives_only: - for item in o: - output.append(json.dumps(item, ensure_ascii=False)) - return "[ " + ", ".join(output) + " ]" - else: - self.current_indent += 2 - self.current_indent_str = "".join( - [" " for x in range(self.current_indent)] - ) - liste = [] - for item in o: - output = [] - # This is performed if in the list is a OrderedDict - if isinstance(item, OrderedDict): - for key, value in item.items(): - output.append(json.dumps(key) + ": " + self.encode(value)) - - liste.append( - "\n" - + 2 * self.current_indent_str - + "{" - + (",\n" + 2 * self.current_indent_str).join(output) - + "}" - ) - - else: - raise AssertionError( - "Only OrderedDicts in lists are properly structured. Please redefine it in the encode function." - ) - output.append(self.current_indent_str + self.encode(item)) - return "[\n" + ",".join(output) + "]" - - self.current_indent -= 2 - self.current_indent_str = "".join( - [" " for x in range(self.current_indent)] - ) - - return "[" + ",".join(liste) + "]" - - elif isinstance(o, OrderedDict): - output = [] - self.current_indent += 4 - self.current_indent_str = "".join([" " for x in range(self.current_indent)]) - for key, value in o.items(): - output.append( - self.current_indent_str - + json.dumps(key) - + ": " - + self.encode(value) - ) - self.current_indent -= 4 - self.current_indent_str = "".join([" " for x in range(self.current_indent)]) - return "{\n" + ",\n".join(output) + "}" - else: - return json.dumps(o, ensure_ascii=False) - - def render(self, inp, *args, **kwargs): - return self.encode(inp) diff --git a/src/omi/dialects/rdf/__init__.py b/src/omi/dialects/rdf/__init__.py deleted file mode 100644 index f72e61b..0000000 --- a/src/omi/dialects/rdf/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from omi.dialects.rdf.dialect import OEP_V_1_4_RDF_Dialect diff --git a/src/omi/dialects/rdf/compiler.py b/src/omi/dialects/rdf/compiler.py deleted file mode 100644 index e63829a..0000000 --- a/src/omi/dialects/rdf/compiler.py +++ /dev/null @@ -1,405 +0,0 @@ -from typing import Dict -from typing import Tuple - -from rdflib import BNode -from rdflib import Graph -from rdflib import Literal -from rdflib import URIRef -from rdflib.graph import Node -from rdflib.namespace import DCTERMS -from rdflib.namespace import FOAF -from rdflib.namespace import RDF -from rdflib.namespace import XSD - -from omi import structure -from omi.dialects.base.compiler import Compiler -from omi.dialects.rdf.licenses import LICENSE_DICT -from omi.dialects.rdf.namespace import ADMS -from omi.dialects.rdf.namespace import DCAT -from omi.dialects.rdf.namespace import DCATDE -from omi.dialects.rdf.namespace import OEO -from omi.dialects.rdf.namespace import SCHEMA -from omi.dialects.rdf.namespace import SKOS -from omi.dialects.rdf.namespace import SPDX - -LANG_DICT = { - "eng": "http://publications.europa.eu/resource/authority/language/ENG", - "en-GB": "http://publications.europa.eu/resource/authority/language/ENG", - "ger": "http://publications.europa.eu/resource/authority/language/GER", - "en-US": "", - "de-DE": "http://publications.europa.eu/resource/authority/language/GER", - "fr-FR": "", -} - - -class RDFCompiler(Compiler): - def __init__(self, graph: Graph = None): - if graph is None: - self.graph = Graph() - self.graph.namespace_manager.bind("foaf", FOAF) - self.graph.namespace_manager.bind("dct", DCTERMS) - self.graph.namespace_manager.bind("dcat", DCAT) - self.graph.namespace_manager.bind("dcatde", DCATDE) - self.graph.namespace_manager.bind("oeo", OEO) - self.graph.namespace_manager.bind("schema", SCHEMA) - self.graph.namespace_manager.bind("skos", SKOS) - self.graph.namespace_manager.bind("adms", ADMS) - self.graph.namespace_manager.bind("spdx", SPDX) - else: - self.graph = graph - - def visit_agency(self, agency: structure.Agency, *args, **kwargs): - node = BNode() - self._add_literal_or_None(node, DCTERMS.title, agency.name) - self._add_literal_or_None(node, OEO.has_logo, agency.logo) - return node - - def visit_context(self, context: structure.Context, *args, **kwargs): - - return ( - Literal(context.homepage), - Literal(context.contact), - Literal(context.documentation), - Literal(context.source_code), - Literal(context.grant_number), - self.visit(context.funding_agency), - self.visit(context.publisher), - ) - - def visit_person(self, person: structure.Person, *args, **kwargs): - - node = BNode() - self.graph.add((node, RDF.type, FOAF.Person)) - self._add_literal_or_None(node, FOAF.name, person.name) - self._add_literal_or_None(node, FOAF.mbox, person.email) - return node - - def visit_contribution(self, contribution: structure.Contribution, *args, **kwargs): - c = BNode() - self.graph.add( - ( - c, - DCTERMS.contributor, - self.visit(contribution.contributor, *args, **kwargs), - ) - ) - self._add_literal_or_None( - c, OEO.date, contribution.date.strftime("%Y-%m-%d"), datatype=XSD.date - ) - self._add_literal_or_None(c, OEO.comment, contribution.comment) - self._add_literal_or_None(c, OEO.object, contribution.object) - return c - - def visit_language(self, language: structure.Language, *args, **kwargs): - return URIRef(LANG_DICT[language]) - - def visit_spatial(self, spatial: structure.Spatial, *args, **kwargs): - node = BNode() - self._add_literal_or_None(node, SKOS.prefLabel, spatial.extent) - self._add_literal_or_None(node, OEO.has_spatial_resolution, spatial.resolution) - self._add_literal_or_None(node, OEO.location, spatial.location) - return node - - def visit_temporal(self, temporal: structure.Temporal, *args, **kwargs): - node = BNode() - self.graph.add((node, RDF.type, DCTERMS.PeriodOfTime)) - self._add_literal_or_None(node, SCHEMA.startDate, temporal.ts_start) - self._add_literal_or_None(node, SCHEMA.endDate, temporal.ts_end) - self._add_literal_or_None(node, OEO.has_time_resolution, temporal.ts_resolution) - self._add_literal_or_None(node, OEO.referenceDate, temporal.reference_date) - self.graph.add( - (node, OEO.has_timestamp_alignment, self.visit(temporal.ts_orientation)) - ) - self._add_literal_or_None(node, OEO.uses_aggregation, temporal.aggregation) - return node - - def visit_timestamp_orientation( - self, ts_orientation: structure.TimestampOrientation, *args, **kwargs - ): - if ts_orientation == structure.TimestampOrientation.left: - return OEO.left_orientation - elif ts_orientation == structure.TimestampOrientation.middle: - return OEO.middle_orientation - elif ts_orientation == structure.TimestampOrientation.right: - return OEO.right_orientation - else: - raise Exception("Unknown timestamp orientation") - - def visit_source(self, source: structure.Source, *args, **kwargs): - node = BNode() - self._add_literal_or_None(node, DCTERMS.title, source.title) - self._add_literal_or_None(node, DCTERMS.description, source.description) - self._add_literal_or_None(node, FOAF.page, source.path) - for l in source.licenses: - li = self.visit(l, **kwargs) - self.graph.add((node, OEO.has_terms_of_use, li)) - return node - - def visit_terms_of_use(self, tou: structure.TermsOfUse, *args, **kwargs): - node = BNode() - self._add_literal_or_None( - node, DCATDE.licenseAttributionByText, tou.attribution - ) - self._add_literal_or_None(node, OEO.has_instruction, tou.instruction) - self.graph.add((node, DCAT.license, self.visit(tou.license, **kwargs))) - return node - - def visit_license(self, lic: structure.License, *args, license_dict=None, **kwargs): - if False: # lic.name in LICENSE_DICT: - li = URIRef(LICENSE_DICT[lic.identifier]) - else: - if license_dict is not None and lic.identifier in license_dict: - li = license_dict[lic.identifier] - else: - li = BNode() - self.graph.add((li, RDF.type, DCAT.LicenseDocument)) - if lic.other_references: - for ref in lic.other_references: - self._add_literal_or_None(li, SPDX.seeAlso, ref) - self._add_literal_or_None(li, FOAF.page, lic.path) - self._add_literal_or_None(li, SPDX.licenseId, lic.identifier) - if lic.text: - self._add_literal_or_None(li, SPDX.licenseText, lic.text) - self._add_literal_or_None(li, SPDX.name, lic.name) - if license_dict is not None and lic.identifier is not None: - license_dict[lic.identifier] = li - return li - - def visit_resource( - self, - resource: structure.Resource, - *args, - resource_dict: Dict[str, Tuple[Node, Dict[str, Node]]] = None, - **kwargs - ): - s = BNode() - self.graph.add((s, RDF.type, DCAT.Distribution)) - self._add_literal_or_None(s, DCTERMS.title, resource.name) - self._add_literal_or_None(s, DCAT.accessURL, resource.path) - self._add_literal_or_None(s, OEO.has_format, resource.format) # dct:format ? - self._add_literal_or_None(s, OEO.profile, resource.profile) - self._add_literal_or_None(s, OEO.encoding, resource.encoding) - if resource.dialect is not None: - self.graph.add((s, OEO.has_dialect, self.visit(resource.dialect))) - fields, pks, fks = self.visit(resource.schema, s, resource_dict=resource_dict) - for f in fields: - self.graph.add((s, OEO.field, f)) - for pk in pks: - self.graph.add((s, OEO.primaryKey, pk)) - for fk in fks: - self.graph.add((s, OEO.has_foreignKey, fk)) - return s - - def visit_schema( - self, - schema: structure.Schema, - *args, - resource_dict: Dict[str, Tuple[Node, Dict[str, Node]]] = None, - **kwargs - ): - resource_node = args[0] - field_dict = dict() - fields = [] - resource_dict = resource_dict or dict() - for field in schema.fields: - fnode = self.visit(field, *args, **kwargs) - fields.append(fnode) - field_dict[field.name] = fnode - - pks = [field_dict[pk] for pk in schema.primary_key or []] - - resource_name = schema.fields[0].resource.name - - if resource_name in resource_dict: - resource_dict[resource_name][1].update(field_dict) - else: - resource_dict[resource_name] = (resource_node, field_dict) - - fks = [ - self.visit(fk, *args, resource_dict=resource_dict, **kwargs) - for fk in schema.foreign_keys or [] - ] - - return fields, pks, fks - - def visit_dialect(self, dialect: structure.Dialect, *args, **kwargs): - node = BNode() - self._add_literal_or_None(node, OEO.delimiter, dialect.delimiter) - self._add_literal_or_None(node, OEO.decimalSeparator, dialect.decimal_separator) - return node - - def visit_field(self, field: structure.Field, *args, **kwargs): - field_uri = BNode() - self.graph.add((field_uri, RDF.type, OEO.DatabaseField)) - self._add_literal_or_None(field_uri, DCTERMS.title, field.name) - self._add_literal_or_None(field_uri, DCTERMS.description, field.description) - self._add_literal_or_None(field_uri, OEO.type, field.type) - self._add_literal_or_None(field_uri, OEO.unit, field.unit) - return field_uri - - def visit_foreign_key( - self, foreign_key: structure.ForeignKey, *args, resource_dict=None, **kwargs - ): - fk_node = BNode() - for r in foreign_key.references: - self.graph.add( - ( - fk_node, - OEO.has_reference, - self.visit(r, *args, resource_dict=resource_dict, **kwargs), - ) - ) - return fk_node - - def visit_reference( - self, - reference: structure.Reference, - *args, - resource_dict: Dict[str, Tuple[Node, Dict[str, Node]]] = None, - **kwargs - ): - r_node = BNode() - self.graph.add( - (r_node, OEO.has_source, self._get_field(reference.source, resource_dict)) - ) - target = self._get_or_create_field( - reference.target, resource_dict, *args, **kwargs - ) - self.graph.add((r_node, OEO.has_target, target)) - return r_node - - def visit_review(self, review: structure.Review, *args, **kwargs): - node = BNode() - self.graph.add((node, FOAF.page, URIRef(review.path))) - self._add_literal_or_None(node, OEO.has_badge, review.badge) - return node - - def visit_meta_comment(self, comment: structure.MetaComment, *args, **kwargs): - com = BNode() - self._add_literal_or_None(com, OEO.metadata_info, comment.metadata_info) - self._add_literal_or_None(com, OEO.dates_info, comment.dates) - self._add_literal_or_None(com, OEO.units_info, comment.units) - self._add_literal_or_None(com, OEO.languages_info, comment.languages) - self._add_literal_or_None(com, OEO.licenses_info, comment.licenses) - self._add_literal_or_None(com, OEO.review_info, comment.review) - self._add_literal_or_None(com, OEO.none_info, comment.none) - return com - - def visit_metadata(self, metadata: structure.OEPMetadata, *args, **kwargs): - datasetURI = URIRef(metadata.identifier) - - license_dict = kwargs.get("license_dict", {}) - - self.graph.add((datasetURI, RDF.type, DCAT.Dataset)) - self._add_literal_or_None(datasetURI, ADMS.Identifier, metadata.name) - self._add_literal_or_None(datasetURI, DCTERMS.title, metadata.title) - self._add_literal_or_None(datasetURI, DCTERMS.description, metadata.description) - - for lang in metadata.languages: - self._add_literal_or_None(datasetURI, DCTERMS.language, self.visit(lang)) - - for k in metadata.keywords: - self._add_literal_or_None(datasetURI, DCAT.keyword, k) - - self.graph.add( - ( - datasetURI, - OEO.publicationDate, - Literal( - metadata.publication_date.strftime("%Y-%m-%d"), datatype=XSD.date - ), - ) - ) - - homepage, contact, documentation, source_code, grant_number, funding_agency, publisher = self.visit( - metadata.context - ) - - self.graph.add((datasetURI, FOAF.homepage, homepage)) - self.graph.add((datasetURI, DCAT.contactpoint, contact)) - self.graph.add((datasetURI, OEO.documentation, documentation)) - self.graph.add((datasetURI, OEO.sourceCode, source_code)) - self.graph.add((datasetURI, OEO.grantNo, grant_number)) - - if funding_agency: - self.graph.add((datasetURI, OEO.has_funding_agency, funding_agency)) - - if publisher: - self.graph.add((datasetURI, OEO.has_publisher, publisher)) - - self.graph.add((datasetURI, DCTERMS.spatial, self.visit(metadata.spatial))) - self.graph.add((datasetURI, DCTERMS.temporal, self.visit(metadata.temporal))) - for s in metadata.sources: - self.graph.add( - (datasetURI, DCTERMS.source, self.visit(s, license_dict=license_dict)) - ) - for l in metadata.license: - self.graph.add( - ( - datasetURI, - OEO.has_terms_of_use, - self.visit(l, license_dict=license_dict), - ) - ) - - for c in metadata.contributions: - self.graph.add((datasetURI, OEO.has_contribution, self.visit(c))) - for r in metadata.resources: - self.graph.add((datasetURI, OEO.has_resource, self.visit(r))) - - self.graph.add((datasetURI, OEO.has_review, self.visit(metadata.review))) - - self.graph.add( - ( - datasetURI, - OEO.metadataLicense, - URIRef("https://creativecommons.org/publicdomain/zero/1.0/legalcode"), - ) - ) - - self.graph.add((datasetURI, OEO.comment, self.visit(metadata.comment))) - - return datasetURI - - def _get_or_create_field( - self, - field: structure.Field, - resource_dict: Dict[str, Tuple[Node, Dict[str, Node]]], - *args, - **kwargs - ): - try: - if field.resource.name not in resource_dict: - self.visit_resource(field.resource, resource_dict=resource_dict) - return self._get_field(field, resource_dict) - except FieldNotFoundError: - f = self.visit_field(field, *args, **kwargs) - res_node, field_dict = resource_dict[field.resource.name] - self.graph.add((res_node, OEO.field, f)) - field_dict[field.name] = f - return f - - def _get_field( - self, - field: structure.Field, - resource_dict: Dict[str, Tuple[Node, Dict[str, Node]]], - ): - res = resource_dict.get(field.resource.name) - if res is None: - raise ResourceNotFoundError(field.resource.name) - node = res[1].get(field.name) - if node is None: - raise FieldNotFoundError(field.name) - return node - - def _add_literal_or_None(self, subject: Node, predicate, obj: str, **kwargs): - if obj is not None: - self.graph.add((subject, predicate, Literal(obj, **kwargs))) - - -class FieldNotFoundError(Exception): - pass - - -class ResourceNotFoundError(Exception): - pass diff --git a/src/omi/dialects/rdf/dialect.py b/src/omi/dialects/rdf/dialect.py deleted file mode 100644 index 68b00e1..0000000 --- a/src/omi/dialects/rdf/dialect.py +++ /dev/null @@ -1,19 +0,0 @@ -from omi.dialects.base.dialect import Dialect -from omi.dialects.base.register import register -from omi.dialects.rdf.compiler import RDFCompiler -from omi.dialects.rdf.parser import RDFParser -from omi.dialects.rdf.renderer import GraphRenderer -from omi.structure import OEPMetadata - - -@register("oep-rdf-v1.4") -class OEP_V_1_4_RDF_Dialect(Dialect): - _parser = RDFParser - _compiler = RDFCompiler - _renderer = GraphRenderer - - def compile_and_render(self, obj: OEPMetadata, *args, **kwargs): - c = self._compiler() - r = self._renderer() - c.visit(obj, *args, **kwargs) - return r.render(c.graph, *args, **kwargs).decode("utf-8") diff --git a/src/omi/dialects/rdf/licenses.py b/src/omi/dialects/rdf/licenses.py deleted file mode 100644 index 6c08219..0000000 --- a/src/omi/dialects/rdf/licenses.py +++ /dev/null @@ -1,401 +0,0 @@ -import itertools - -SPDX_LICENSES = { - "0BSD": "https://spdx.org/licenses/0BSD.html", - "AAL": "https://spdx.org/licenses/AAL.html", - "ADSL": "https://spdx.org/licenses/ADSL.html", - "AFL-1.1": "https://spdx.org/licenses/AFL-1.1.html", - "AFL-1.2": "https://spdx.org/licenses/AFL-1.2.html", - "AFL-2.0": "https://spdx.org/licenses/AFL-2.0.html", - "AFL-2.1": "https://spdx.org/licenses/AFL-2.1.html", - "AFL-3.0": "https://spdx.org/licenses/AFL-3.0.html", - "AGPL-1.0": "https://spdx.org/licenses/AGPL-1.0.html", - "AGPL-1.0-only": "https://spdx.org/licenses/AGPL-1.0-only.html", - "AGPL-1.0-or-later": "https://spdx.org/licenses/AGPL-1.0-or-later.html", - "AGPL-3.0": "https://spdx.org/licenses/AGPL-3.0.html", - "AGPL-3.0-only": "https://spdx.org/licenses/AGPL-3.0-only.html", - "AGPL-3.0-or-later": "https://spdx.org/licenses/AGPL-3.0-or-later.html", - "AMDPLPA": "https://spdx.org/licenses/AMDPLPA.html", - "AML": "https://spdx.org/licenses/AML.html", - "AMPAS": "https://spdx.org/licenses/AMPAS.html", - "ANTLR-PD": "https://spdx.org/licenses/ANTLR-PD.html", - "APAFML": "https://spdx.org/licenses/APAFML.html", - "APL-1.0": "https://spdx.org/licenses/APL-1.0.html", - "APSL-1.0": "https://spdx.org/licenses/APSL-1.0.html", - "APSL-1.1": "https://spdx.org/licenses/APSL-1.1.html", - "APSL-1.2": "https://spdx.org/licenses/APSL-1.2.html", - "APSL-2.0": "https://spdx.org/licenses/APSL-2.0.html", - "Abstyles": "https://spdx.org/licenses/Abstyles.html", - "Adobe-2006": "https://spdx.org/licenses/Adobe-2006.html", - "Adobe-Glyph": "https://spdx.org/licenses/Adobe-Glyph.html", - "Afmparse": "https://spdx.org/licenses/Afmparse.html", - "Aladdin": "https://spdx.org/licenses/Aladdin.html", - "Apache-1.0": "https://spdx.org/licenses/Apache-1.0.html", - "Apache-1.1": "https://spdx.org/licenses/Apache-1.1.html", - "Apache-2.0": "https://spdx.org/licenses/Apache-2.0.html", - "Artistic-1.0": "https://spdx.org/licenses/Artistic-1.0.html", - "Artistic-1.0-Perl": "https://spdx.org/licenses/Artistic-1.0-Perl.html", - "Artistic-1.0-cl8": "https://spdx.org/licenses/Artistic-1.0-cl8.html", - "Artistic-2.0": "https://spdx.org/licenses/Artistic-2.0.html", - "BSD-1-Clause": "https://spdx.org/licenses/BSD-1-Clause.html", - "BSD-2-Clause": "https://spdx.org/licenses/BSD-2-Clause.html", - "BSD-2-Clause-FreeBSD": "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.html", - "BSD-2-Clause-NetBSD": "https://spdx.org/licenses/BSD-2-Clause-NetBSD.html", - "BSD-2-Clause-Patent": "https://spdx.org/licenses/BSD-2-Clause-Patent.html", - "BSD-3-Clause": "https://spdx.org/licenses/BSD-3-Clause.html", - "BSD-3-Clause-Attribution": "https://spdx.org/licenses/BSD-3-Clause-Attribution.html", - "BSD-3-Clause-Clear": "https://spdx.org/licenses/BSD-3-Clause-Clear.html", - "BSD-3-Clause-LBNL": "https://spdx.org/licenses/BSD-3-Clause-LBNL.html", - "BSD-3-Clause-No-Nuclear-License": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.html", - "BSD-3-Clause-No-Nuclear-License-2014": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.html", - "BSD-3-Clause-No-Nuclear-Warranty": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.html", - "BSD-4-Clause": "https://spdx.org/licenses/BSD-4-Clause.html", - "BSD-4-Clause-UC": "https://spdx.org/licenses/BSD-4-Clause-UC.html", - "BSD-Protection": "https://spdx.org/licenses/BSD-Protection.html", - "BSD-Source-Code": "https://spdx.org/licenses/BSD-Source-Code.html", - "BSL-1.0": "https://spdx.org/licenses/BSL-1.0.html", - "Bahyph": "https://spdx.org/licenses/Bahyph.html", - "Barr": "https://spdx.org/licenses/Barr.html", - "Beerware": "https://spdx.org/licenses/Beerware.html", - "BitTorrent-1.0": "https://spdx.org/licenses/BitTorrent-1.0.html", - "BitTorrent-1.1": "https://spdx.org/licenses/BitTorrent-1.1.html", - "BlueOak-1.0.0": "https://spdx.org/licenses/BlueOak-1.0.0.html", - "Borceux": "https://spdx.org/licenses/Borceux.html", - "CATOSL-1.1": "https://spdx.org/licenses/CATOSL-1.1.html", - "CC-BY-1.0": "https://spdx.org/licenses/CC-BY-1.0.html", - "CC-BY-2.0": "https://spdx.org/licenses/CC-BY-2.0.html", - "CC-BY-2.5": "https://spdx.org/licenses/CC-BY-2.5.html", - "CC-BY-3.0": "https://spdx.org/licenses/CC-BY-3.0.html", - "CC-BY-4.0": "https://spdx.org/licenses/CC-BY-4.0.html", - "CC-BY-NC-1.0": "https://spdx.org/licenses/CC-BY-NC-1.0.html", - "CC-BY-NC-2.0": "https://spdx.org/licenses/CC-BY-NC-2.0.html", - "CC-BY-NC-2.5": "https://spdx.org/licenses/CC-BY-NC-2.5.html", - "CC-BY-NC-3.0": "https://spdx.org/licenses/CC-BY-NC-3.0.html", - "CC-BY-NC-4.0": "https://spdx.org/licenses/CC-BY-NC-4.0.html", - "CC-BY-NC-ND-1.0": "https://spdx.org/licenses/CC-BY-NC-ND-1.0.html", - "CC-BY-NC-ND-2.0": "https://spdx.org/licenses/CC-BY-NC-ND-2.0.html", - "CC-BY-NC-ND-2.5": "https://spdx.org/licenses/CC-BY-NC-ND-2.5.html", - "CC-BY-NC-ND-3.0": "https://spdx.org/licenses/CC-BY-NC-ND-3.0.html", - "CC-BY-NC-ND-4.0": "https://spdx.org/licenses/CC-BY-NC-ND-4.0.html", - "CC-BY-NC-SA-1.0": "https://spdx.org/licenses/CC-BY-NC-SA-1.0.html", - "CC-BY-NC-SA-2.0": "https://spdx.org/licenses/CC-BY-NC-SA-2.0.html", - "CC-BY-NC-SA-2.5": "https://spdx.org/licenses/CC-BY-NC-SA-2.5.html", - "CC-BY-NC-SA-3.0": "https://spdx.org/licenses/CC-BY-NC-SA-3.0.html", - "CC-BY-NC-SA-4.0": "https://spdx.org/licenses/CC-BY-NC-SA-4.0.html", - "CC-BY-ND-1.0": "https://spdx.org/licenses/CC-BY-ND-1.0.html", - "CC-BY-ND-2.0": "https://spdx.org/licenses/CC-BY-ND-2.0.html", - "CC-BY-ND-2.5": "https://spdx.org/licenses/CC-BY-ND-2.5.html", - "CC-BY-ND-3.0": "https://spdx.org/licenses/CC-BY-ND-3.0.html", - "CC-BY-ND-4.0": "https://spdx.org/licenses/CC-BY-ND-4.0.html", - "CC-BY-SA-1.0": "https://spdx.org/licenses/CC-BY-SA-1.0.html", - "CC-BY-SA-2.0": "https://spdx.org/licenses/CC-BY-SA-2.0.html", - "CC-BY-SA-2.5": "https://spdx.org/licenses/CC-BY-SA-2.5.html", - "CC-BY-SA-3.0": "https://spdx.org/licenses/CC-BY-SA-3.0.html", - "CC-BY-SA-4.0": "https://spdx.org/licenses/CC-BY-SA-4.0.html", - "CC0-1.0": "https://spdx.org/licenses/CC0-1.0.html", - "CDDL-1.0": "https://spdx.org/licenses/CDDL-1.0.html", - "CDDL-1.1": "https://spdx.org/licenses/CDDL-1.1.html", - "CDLA-Permissive-1.0": "https://spdx.org/licenses/CDLA-Permissive-1.0.html", - "CDLA-Sharing-1.0": "https://spdx.org/licenses/CDLA-Sharing-1.0.html", - "CECILL-1.0": "https://spdx.org/licenses/CECILL-1.0.html", - "CECILL-1.1": "https://spdx.org/licenses/CECILL-1.1.html", - "CECILL-2.0": "https://spdx.org/licenses/CECILL-2.0.html", - "CECILL-2.1": "https://spdx.org/licenses/CECILL-2.1.html", - "CECILL-B": "https://spdx.org/licenses/CECILL-B.html", - "CECILL-C": "https://spdx.org/licenses/CECILL-C.html", - "CERN-OHL-1.1": "https://spdx.org/licenses/CERN-OHL-1.1.html", - "CERN-OHL-1.2": "https://spdx.org/licenses/CERN-OHL-1.2.html", - "CNRI-Jython": "https://spdx.org/licenses/CNRI-Jython.html", - "CNRI-Python": "https://spdx.org/licenses/CNRI-Python.html", - "CNRI-Python-GPL-Compatible": "https://spdx.org/licenses/CNRI-Python-GPL-Compatible.html", - "CPAL-1.0": "https://spdx.org/licenses/CPAL-1.0.html", - "CPL-1.0": "https://spdx.org/licenses/CPL-1.0.html", - "CPOL-1.02": "https://spdx.org/licenses/CPOL-1.02.html", - "CUA-OPL-1.0": "https://spdx.org/licenses/CUA-OPL-1.0.html", - "Caldera": "https://spdx.org/licenses/Caldera.html", - "ClArtistic": "https://spdx.org/licenses/ClArtistic.html", - "Condor-1.1": "https://spdx.org/licenses/Condor-1.1.html", - "Crossword": "https://spdx.org/licenses/Crossword.html", - "CrystalStacker": "https://spdx.org/licenses/CrystalStacker.html", - "Cube": "https://spdx.org/licenses/Cube.html", - "D-FSL-1.0": "https://spdx.org/licenses/D-FSL-1.0.html", - "DOC": "https://spdx.org/licenses/DOC.html", - "DSDP": "https://spdx.org/licenses/DSDP.html", - "Dotseqn": "https://spdx.org/licenses/Dotseqn.html", - "ECL-1.0": "https://spdx.org/licenses/ECL-1.0.html", - "ECL-2.0": "https://spdx.org/licenses/ECL-2.0.html", - "EFL-1.0": "https://spdx.org/licenses/EFL-1.0.html", - "EFL-2.0": "https://spdx.org/licenses/EFL-2.0.html", - "EPL-1.0": "https://spdx.org/licenses/EPL-1.0.html", - "EPL-2.0": "https://spdx.org/licenses/EPL-2.0.html", - "EUDatagrid": "https://spdx.org/licenses/EUDatagrid.html", - "EUPL-1.0": "https://spdx.org/licenses/EUPL-1.0.html", - "EUPL-1.1": "https://spdx.org/licenses/EUPL-1.1.html", - "EUPL-1.2": "https://spdx.org/licenses/EUPL-1.2.html", - "Entessa": "https://spdx.org/licenses/Entessa.html", - "ErlPL-1.1": "https://spdx.org/licenses/ErlPL-1.1.html", - "Eurosym": "https://spdx.org/licenses/Eurosym.html", - "FSFAP": "https://spdx.org/licenses/FSFAP.html", - "FSFUL": "https://spdx.org/licenses/FSFUL.html", - "FSFULLR": "https://spdx.org/licenses/FSFULLR.html", - "FTL": "https://spdx.org/licenses/FTL.html", - "Fair": "https://spdx.org/licenses/Fair.html", - "Frameworx-1.0": "https://spdx.org/licenses/Frameworx-1.0.html", - "FreeImage": "https://spdx.org/licenses/FreeImage.html", - "GFDL-1.1": "https://spdx.org/licenses/GFDL-1.1.html", - "GFDL-1.1-only": "https://spdx.org/licenses/GFDL-1.1-only.html", - "GFDL-1.1-or-later": "https://spdx.org/licenses/GFDL-1.1-or-later.html", - "GFDL-1.2": "https://spdx.org/licenses/GFDL-1.2.html", - "GFDL-1.2-only": "https://spdx.org/licenses/GFDL-1.2-only.html", - "GFDL-1.2-or-later": "https://spdx.org/licenses/GFDL-1.2-or-later.html", - "GFDL-1.3": "https://spdx.org/licenses/GFDL-1.3.html", - "GFDL-1.3-only": "https://spdx.org/licenses/GFDL-1.3-only.html", - "GFDL-1.3-or-later": "https://spdx.org/licenses/GFDL-1.3-or-later.html", - "GL2PS": "https://spdx.org/licenses/GL2PS.html", - "GPL-1.0": "https://spdx.org/licenses/GPL-1.0.html", - "GPL-1.0+": "https://spdx.org/licenses/GPL-1.0+.html", - "GPL-1.0-only": "https://spdx.org/licenses/GPL-1.0-only.html", - "GPL-1.0-or-later": "https://spdx.org/licenses/GPL-1.0-or-later.html", - "GPL-2.0": "https://spdx.org/licenses/GPL-2.0.html", - "GPL-2.0+": "https://spdx.org/licenses/GPL-2.0+.html", - "GPL-2.0-only": "https://spdx.org/licenses/GPL-2.0-only.html", - "GPL-2.0-or-later": "https://spdx.org/licenses/GPL-2.0-or-later.html", - "GPL-2.0-with-GCC-exception": "https://spdx.org/licenses/GPL-2.0-with-GCC-exception.html", - "GPL-2.0-with-autoconf-exception": "https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.html", - "GPL-2.0-with-bison-exception": "https://spdx.org/licenses/GPL-2.0-with-bison-exception.html", - "GPL-2.0-with-classpath-exception": "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.html", - "GPL-2.0-with-font-exception": "https://spdx.org/licenses/GPL-2.0-with-font-exception.html", - "GPL-3.0": "https://spdx.org/licenses/GPL-3.0.html", - "GPL-3.0+": "https://spdx.org/licenses/GPL-3.0+.html", - "GPL-3.0-only": "https://spdx.org/licenses/GPL-3.0-only.html", - "GPL-3.0-or-later": "https://spdx.org/licenses/GPL-3.0-or-later.html", - "GPL-3.0-with-GCC-exception": "https://spdx.org/licenses/GPL-3.0-with-GCC-exception.html", - "GPL-3.0-with-autoconf-exception": "https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.html", - "Giftware": "https://spdx.org/licenses/Giftware.html", - "Glide": "https://spdx.org/licenses/Glide.html", - "Glulxe": "https://spdx.org/licenses/Glulxe.html", - "HPND": "https://spdx.org/licenses/HPND.html", - "HPND-sell-variant": "https://spdx.org/licenses/HPND-sell-variant.html", - "HaskellReport": "https://spdx.org/licenses/HaskellReport.html", - "IBM-pibs": "https://spdx.org/licenses/IBM-pibs.html", - "ICU": "https://spdx.org/licenses/ICU.html", - "IJG": "https://spdx.org/licenses/IJG.html", - "IPA": "https://spdx.org/licenses/IPA.html", - "IPL-1.0": "https://spdx.org/licenses/IPL-1.0.html", - "ISC": "https://spdx.org/licenses/ISC.html", - "ImageMagick": "https://spdx.org/licenses/ImageMagick.html", - "Imlib2": "https://spdx.org/licenses/Imlib2.html", - "Info-ZIP": "https://spdx.org/licenses/Info-ZIP.html", - "Intel": "https://spdx.org/licenses/Intel.html", - "Intel-ACPI": "https://spdx.org/licenses/Intel-ACPI.html", - "Interbase-1.0": "https://spdx.org/licenses/Interbase-1.0.html", - "JPNIC": "https://spdx.org/licenses/JPNIC.html", - "JSON": "https://spdx.org/licenses/JSON.html", - "JasPer-2.0": "https://spdx.org/licenses/JasPer-2.0.html", - "LAL-1.2": "https://spdx.org/licenses/LAL-1.2.html", - "LAL-1.3": "https://spdx.org/licenses/LAL-1.3.html", - "LGPL-2.0": "https://spdx.org/licenses/LGPL-2.0.html", - "LGPL-2.0+": "https://spdx.org/licenses/LGPL-2.0+.html", - "LGPL-2.0-only": "https://spdx.org/licenses/LGPL-2.0-only.html", - "LGPL-2.0-or-later": "https://spdx.org/licenses/LGPL-2.0-or-later.html", - "LGPL-2.1": "https://spdx.org/licenses/LGPL-2.1.html", - "LGPL-2.1+": "https://spdx.org/licenses/LGPL-2.1+.html", - "LGPL-2.1-only": "https://spdx.org/licenses/LGPL-2.1-only.html", - "LGPL-2.1-or-later": "https://spdx.org/licenses/LGPL-2.1-or-later.html", - "LGPL-3.0": "https://spdx.org/licenses/LGPL-3.0.html", - "LGPL-3.0+": "https://spdx.org/licenses/LGPL-3.0+.html", - "LGPL-3.0-only": "https://spdx.org/licenses/LGPL-3.0-only.html", - "LGPL-3.0-or-later": "https://spdx.org/licenses/LGPL-3.0-or-later.html", - "LGPLLR": "https://spdx.org/licenses/LGPLLR.html", - "LPL-1.0": "https://spdx.org/licenses/LPL-1.0.html", - "LPL-1.02": "https://spdx.org/licenses/LPL-1.02.html", - "LPPL-1.0": "https://spdx.org/licenses/LPPL-1.0.html", - "LPPL-1.1": "https://spdx.org/licenses/LPPL-1.1.html", - "LPPL-1.2": "https://spdx.org/licenses/LPPL-1.2.html", - "LPPL-1.3a": "https://spdx.org/licenses/LPPL-1.3a.html", - "LPPL-1.3c": "https://spdx.org/licenses/LPPL-1.3c.html", - "Latex2e": "https://spdx.org/licenses/Latex2e.html", - "Leptonica": "https://spdx.org/licenses/Leptonica.html", - "LiLiQ-P-1.1": "https://spdx.org/licenses/LiLiQ-P-1.1.html", - "LiLiQ-R-1.1": "https://spdx.org/licenses/LiLiQ-R-1.1.html", - "LiLiQ-Rplus-1.1": "https://spdx.org/licenses/LiLiQ-Rplus-1.1.html", - "Libpng": "https://spdx.org/licenses/Libpng.html", - "Linux-OpenIB": "https://spdx.org/licenses/Linux-OpenIB.html", - "MIT": "https://spdx.org/licenses/MIT.html", - "MIT-0": "https://spdx.org/licenses/MIT-0.html", - "MIT-CMU": "https://spdx.org/licenses/MIT-CMU.html", - "MIT-advertising": "https://spdx.org/licenses/MIT-advertising.html", - "MIT-enna": "https://spdx.org/licenses/MIT-enna.html", - "MIT-feh": "https://spdx.org/licenses/MIT-feh.html", - "MITNFA": "https://spdx.org/licenses/MITNFA.html", - "MPL-1.0": "https://spdx.org/licenses/MPL-1.0.html", - "MPL-1.1": "https://spdx.org/licenses/MPL-1.1.html", - "MPL-2.0": "https://spdx.org/licenses/MPL-2.0.html", - "MPL-2.0-no-copyleft-exception": "https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.html", - "MS-PL": "https://spdx.org/licenses/MS-PL.html", - "MS-RL": "https://spdx.org/licenses/MS-RL.html", - "MTLL": "https://spdx.org/licenses/MTLL.html", - "MakeIndex": "https://spdx.org/licenses/MakeIndex.html", - "MirOS": "https://spdx.org/licenses/MirOS.html", - "Motosoto": "https://spdx.org/licenses/Motosoto.html", - "Multics": "https://spdx.org/licenses/Multics.html", - "Mup": "https://spdx.org/licenses/Mup.html", - "NASA-1.3": "https://spdx.org/licenses/NASA-1.3.html", - "NBPL-1.0": "https://spdx.org/licenses/NBPL-1.0.html", - "NCSA": "https://spdx.org/licenses/NCSA.html", - "NGPL": "https://spdx.org/licenses/NGPL.html", - "NLOD-1.0": "https://spdx.org/licenses/NLOD-1.0.html", - "NLPL": "https://spdx.org/licenses/NLPL.html", - "NOSL": "https://spdx.org/licenses/NOSL.html", - "NPL-1.0": "https://spdx.org/licenses/NPL-1.0.html", - "NPL-1.1": "https://spdx.org/licenses/NPL-1.1.html", - "NPOSL-3.0": "https://spdx.org/licenses/NPOSL-3.0.html", - "NRL": "https://spdx.org/licenses/NRL.html", - "NTP": "https://spdx.org/licenses/NTP.html", - "Naumen": "https://spdx.org/licenses/Naumen.html", - "Net-SNMP": "https://spdx.org/licenses/Net-SNMP.html", - "NetCDF": "https://spdx.org/licenses/NetCDF.html", - "Newsletr": "https://spdx.org/licenses/Newsletr.html", - "Nokia": "https://spdx.org/licenses/Nokia.html", - "Noweb": "https://spdx.org/licenses/Noweb.html", - "Nunit": "https://spdx.org/licenses/Nunit.html", - "OCCT-PL": "https://spdx.org/licenses/OCCT-PL.html", - "OCLC-2.0": "https://spdx.org/licenses/OCLC-2.0.html", - "ODC-By-1.0": "https://spdx.org/licenses/ODC-By-1.0.html", - "ODbL-1.0": "https://spdx.org/licenses/ODbL-1.0.html", - "OFL-1.0": "https://spdx.org/licenses/OFL-1.0.html", - "OFL-1.1": "https://spdx.org/licenses/OFL-1.1.html", - "OGL-UK-1.0": "https://spdx.org/licenses/OGL-UK-1.0.html", - "OGL-UK-2.0": "https://spdx.org/licenses/OGL-UK-2.0.html", - "OGL-UK-3.0": "https://spdx.org/licenses/OGL-UK-3.0.html", - "OGTSL": "https://spdx.org/licenses/OGTSL.html", - "OLDAP-1.1": "https://spdx.org/licenses/OLDAP-1.1.html", - "OLDAP-1.2": "https://spdx.org/licenses/OLDAP-1.2.html", - "OLDAP-1.3": "https://spdx.org/licenses/OLDAP-1.3.html", - "OLDAP-1.4": "https://spdx.org/licenses/OLDAP-1.4.html", - "OLDAP-2.0": "https://spdx.org/licenses/OLDAP-2.0.html", - "OLDAP-2.0.1": "https://spdx.org/licenses/OLDAP-2.0.1.html", - "OLDAP-2.1": "https://spdx.org/licenses/OLDAP-2.1.html", - "OLDAP-2.2": "https://spdx.org/licenses/OLDAP-2.2.html", - "OLDAP-2.2.1": "https://spdx.org/licenses/OLDAP-2.2.1.html", - "OLDAP-2.2.2": "https://spdx.org/licenses/OLDAP-2.2.2.html", - "OLDAP-2.3": "https://spdx.org/licenses/OLDAP-2.3.html", - "OLDAP-2.4": "https://spdx.org/licenses/OLDAP-2.4.html", - "OLDAP-2.5": "https://spdx.org/licenses/OLDAP-2.5.html", - "OLDAP-2.6": "https://spdx.org/licenses/OLDAP-2.6.html", - "OLDAP-2.7": "https://spdx.org/licenses/OLDAP-2.7.html", - "OLDAP-2.8": "https://spdx.org/licenses/OLDAP-2.8.html", - "OML": "https://spdx.org/licenses/OML.html", - "OPL-1.0": "https://spdx.org/licenses/OPL-1.0.html", - "OSET-PL-2.1": "https://spdx.org/licenses/OSET-PL-2.1.html", - "OSL-1.0": "https://spdx.org/licenses/OSL-1.0.html", - "OSL-1.1": "https://spdx.org/licenses/OSL-1.1.html", - "OSL-2.0": "https://spdx.org/licenses/OSL-2.0.html", - "OSL-2.1": "https://spdx.org/licenses/OSL-2.1.html", - "OSL-3.0": "https://spdx.org/licenses/OSL-3.0.html", - "OpenSSL": "https://spdx.org/licenses/OpenSSL.html", - "PDDL-1.0": "https://spdx.org/licenses/PDDL-1.0.html", - "PHP-3.0": "https://spdx.org/licenses/PHP-3.0.html", - "PHP-3.01": "https://spdx.org/licenses/PHP-3.01.html", - "Plexus": "https://spdx.org/licenses/Plexus.html", - "PostgreSQL": "https://spdx.org/licenses/PostgreSQL.html", - "Python-2.0": "https://spdx.org/licenses/Python-2.0.html", - "QPL-1.0": "https://spdx.org/licenses/QPL-1.0.html", - "Qhull": "https://spdx.org/licenses/Qhull.html", - "RHeCos-1.1": "https://spdx.org/licenses/RHeCos-1.1.html", - "RPL-1.1": "https://spdx.org/licenses/RPL-1.1.html", - "RPL-1.5": "https://spdx.org/licenses/RPL-1.5.html", - "RPSL-1.0": "https://spdx.org/licenses/RPSL-1.0.html", - "RSA-MD": "https://spdx.org/licenses/RSA-MD.html", - "RSCPL": "https://spdx.org/licenses/RSCPL.html", - "Rdisc": "https://spdx.org/licenses/Rdisc.html", - "Ruby": "https://spdx.org/licenses/Ruby.html", - "SAX-PD": "https://spdx.org/licenses/SAX-PD.html", - "SCEA": "https://spdx.org/licenses/SCEA.html", - "SGI-B-1.0": "https://spdx.org/licenses/SGI-B-1.0.html", - "SGI-B-1.1": "https://spdx.org/licenses/SGI-B-1.1.html", - "SGI-B-2.0": "https://spdx.org/licenses/SGI-B-2.0.html", - "SISSL": "https://spdx.org/licenses/SISSL.html", - "SISSL-1.2": "https://spdx.org/licenses/SISSL-1.2.html", - "SMLNJ": "https://spdx.org/licenses/SMLNJ.html", - "SMPPL": "https://spdx.org/licenses/SMPPL.html", - "SNIA": "https://spdx.org/licenses/SNIA.html", - "SPL-1.0": "https://spdx.org/licenses/SPL-1.0.html", - "SWL": "https://spdx.org/licenses/SWL.html", - "Saxpath": "https://spdx.org/licenses/Saxpath.html", - "Sendmail": "https://spdx.org/licenses/Sendmail.html", - "Sendmail-8.23": "https://spdx.org/licenses/Sendmail-8.23.html", - "SimPL-2.0": "https://spdx.org/licenses/SimPL-2.0.html", - "Sleepycat": "https://spdx.org/licenses/Sleepycat.html", - "Spencer-86": "https://spdx.org/licenses/Spencer-86.html", - "Spencer-94": "https://spdx.org/licenses/Spencer-94.html", - "Spencer-99": "https://spdx.org/licenses/Spencer-99.html", - "StandardML-NJ": "https://spdx.org/licenses/StandardML-NJ.html", - "SugarCRM-1.1.3": "https://spdx.org/licenses/SugarCRM-1.1.3.html", - "TAPR-OHL-1.0": "https://spdx.org/licenses/TAPR-OHL-1.0.html", - "TCL": "https://spdx.org/licenses/TCL.html", - "TCP-wrappers": "https://spdx.org/licenses/TCP-wrappers.html", - "TMate": "https://spdx.org/licenses/TMate.html", - "TORQUE-1.1": "https://spdx.org/licenses/TORQUE-1.1.html", - "TOSL": "https://spdx.org/licenses/TOSL.html", - "TU-Berlin-1.0": "https://spdx.org/licenses/TU-Berlin-1.0.html", - "TU-Berlin-2.0": "https://spdx.org/licenses/TU-Berlin-2.0.html", - "UPL-1.0": "https://spdx.org/licenses/UPL-1.0.html", - "Unicode-DFS-2015": "https://spdx.org/licenses/Unicode-DFS-2015.html", - "Unicode-DFS-2016": "https://spdx.org/licenses/Unicode-DFS-2016.html", - "Unicode-TOU": "https://spdx.org/licenses/Unicode-TOU.html", - "Unlicense": "https://spdx.org/licenses/Unlicense.html", - "VOSTROM": "https://spdx.org/licenses/VOSTROM.html", - "VSL-1.0": "https://spdx.org/licenses/VSL-1.0.html", - "Vim": "https://spdx.org/licenses/Vim.html", - "W3C": "https://spdx.org/licenses/W3C.html", - "W3C-19980720": "https://spdx.org/licenses/W3C-19980720.html", - "W3C-20150513": "https://spdx.org/licenses/W3C-20150513.html", - "WTFPL": "https://spdx.org/licenses/WTFPL.html", - "Watcom-1.0": "https://spdx.org/licenses/Watcom-1.0.html", - "Wsuipa": "https://spdx.org/licenses/Wsuipa.html", - "X11": "https://spdx.org/licenses/X11.html", - "XFree86-1.1": "https://spdx.org/licenses/XFree86-1.1.html", - "XSkat": "https://spdx.org/licenses/XSkat.html", - "Xerox": "https://spdx.org/licenses/Xerox.html", - "Xnet": "https://spdx.org/licenses/Xnet.html", - "YPL-1.0": "https://spdx.org/licenses/YPL-1.0.html", - "YPL-1.1": "https://spdx.org/licenses/YPL-1.1.html", - "ZPL-1.1": "https://spdx.org/licenses/ZPL-1.1.html", - "ZPL-2.0": "https://spdx.org/licenses/ZPL-2.0.html", - "ZPL-2.1": "https://spdx.org/licenses/ZPL-2.1.html", - "Zed": "https://spdx.org/licenses/Zed.html", - "Zend-2.0": "https://spdx.org/licenses/Zend-2.0.html", - "Zimbra-1.3": "https://spdx.org/licenses/Zimbra-1.3.html", - "Zimbra-1.4": "https://spdx.org/licenses/Zimbra-1.4.html", - "Zlib": "https://spdx.org/licenses/Zlib.html", - "bzip2-1.0.5": "https://spdx.org/licenses/bzip2-1.0.5.html", - "bzip2-1.0.6": "https://spdx.org/licenses/bzip2-1.0.6.html", - "copyleft-next-0.3.0": "https://spdx.org/licenses/copyleft-next-0.3.0.html", - "copyleft-next-0.3.1": "https://spdx.org/licenses/copyleft-next-0.3.1.html", - "curl": "https://spdx.org/licenses/curl.html", - "diffmark": "https://spdx.org/licenses/diffmark.html", - "dvipdfm": "https://spdx.org/licenses/dvipdfm.html", - "eCos-2.0": "https://spdx.org/licenses/eCos-2.0.html", - "eGenix": "https://spdx.org/licenses/eGenix.html", - "gSOAP-1.3b": "https://spdx.org/licenses/gSOAP-1.3b.html", - "gnuplot": "https://spdx.org/licenses/gnuplot.html", - "iMatix": "https://spdx.org/licenses/iMatix.html", - "libpng-2.0": "https://spdx.org/licenses/libpng-2.0.html", - "libtiff": "https://spdx.org/licenses/libtiff.html", - "mpich2": "https://spdx.org/licenses/mpich2.html", - "psfrag": "https://spdx.org/licenses/psfrag.html", - "psutils": "https://spdx.org/licenses/psutils.html", - "wxWindows": "https://spdx.org/licenses/wxWindows.html", - "xinetd": "https://spdx.org/licenses/xinetd.html", - "xpp": "https://spdx.org/licenses/xpp.html", - "zlib-acknowledgement": "https://spdx.org/licenses/zlib-acknowledgement.html", -} - -ALL_LICENSE_DICTS = [SPDX_LICENSES] - -LICENSE_DICT = dict( - itertools.chain.from_iterable(dct.items() for dct in ALL_LICENSE_DICTS) -) - -__all__ = [LICENSE_DICT] diff --git a/src/omi/dialects/rdf/namespace.py b/src/omi/dialects/rdf/namespace.py deleted file mode 100644 index a1989ad..0000000 --- a/src/omi/dialects/rdf/namespace.py +++ /dev/null @@ -1,9 +0,0 @@ -from rdflib import Namespace - -DCAT = Namespace("http://www.w3.org/ns/dcat#") -OEO = Namespace("http://openenergy-platform.org/ontology/v0.0.1/oeo/") -DCATDE = Namespace("http://dcat-ap.de/def/dcatde/") -SCHEMA = Namespace("http://schema.org/") -SKOS = Namespace("http://www.w3.org/2004/02/skos/core#") -SPDX = Namespace("http://spdx.org/rdf/terms#") -ADMS = Namespace("http://www.w3.org/ns/adms#") diff --git a/src/omi/dialects/rdf/parser.py b/src/omi/dialects/rdf/parser.py deleted file mode 100644 index 4cf0ab8..0000000 --- a/src/omi/dialects/rdf/parser.py +++ /dev/null @@ -1,346 +0,0 @@ -from typing import Dict -from typing import Iterable -from typing import Tuple - -from dateutil.parser import parse as parse_date -from rdflib import Graph -from rdflib.graph import Node -from rdflib.graph import URIRef -from rdflib.namespace import DCTERMS -from rdflib.namespace import FOAF -from rdflib.namespace import RDF -from rdflib.namespace import RDFS - -from omi import structure as struc -from omi.dialects.base.parser import Parser -from omi.dialects.rdf.namespace import ADMS -from omi.dialects.rdf.namespace import DCAT -from omi.dialects.rdf.namespace import DCATDE -from omi.dialects.rdf.namespace import OEO -from omi.dialects.rdf.namespace import SCHEMA -from omi.dialects.rdf.namespace import SKOS -from omi.dialects.rdf.namespace import SPDX - - -def _only(gen): - r = _one_or_none(gen) - if r is None: - raise Exception("No matching elements") - return r - - -def _one_str_or_none(gen): - r = _one_or_none(gen) - if r is None: - return None - return str(r) - - -def _one_or_none(gen): - l = list(gen) - if not l: - return None - if len(l) > 1: - raise Exception("Found more than one match:" + str(l)) - return l[0] - - -class RDFParser(Parser): - def load_string(self, string: str, *args, **kwargs): - g = Graph() - g.parse(data=string, format="ttl") - return g - - def parse(self, graph, *args, **kwargs): - for dataset in {s for s, _, _ in graph}.difference({o for _, _, o in graph}): - if dataset in graph.subjects(RDF.type, DCAT.Dataset): - return self.parse_metadata(graph, dataset) - - def parse_date(self, node: Node): - return parse_date(node) - - def parse_context(self, graph: Graph, parent: Node) -> struc.Context: - fa = _only(graph.objects(parent, OEO.has_funding_agency)) - kwargs = {} - if fa is not None: - kwargs["funding_agency"] = struc.Agency( - name=_one_str_or_none(graph.objects(fa, DCTERMS.title)), - logo=_one_str_or_none(graph.objects(fa, OEO.has_logo)), - ) - pa = _only(graph.objects(parent, OEO.has_publisher)) - if pa is not None: - kwargs["publisher"] = struc.Agency( - name=_one_str_or_none(graph.objects(pa, DCTERMS.title)), - logo=_one_str_or_none(graph.objects(pa, OEO.has_logo)), - ) - return struc.Context( - contact=_one_str_or_none(graph.objects(parent, DCAT.contactpoint)), - documentation=_one_str_or_none(graph.objects(parent, OEO.documentation)), - grant_number=_one_str_or_none(graph.objects(parent, OEO.grantNo)), - homepage=_one_str_or_none(graph.objects(parent, FOAF.homepage)), - source_code=_one_str_or_none(graph.objects(parent, OEO.sourceCode)), - **kwargs - ) - - def parse_contributor(self, graph: Graph, parent: Node) -> struc.Contribution: - return struc.Contribution( - contributor=self.parse_person( - graph, _only(graph.objects(parent, DCTERMS.contributor)) - ), - date=self.parse_date(_only(graph.objects(parent, OEO.date))), - obj=_one_str_or_none(graph.objects(parent, OEO.object)), - comment=_one_str_or_none(graph.objects(parent, OEO.comment)), - ) - - def parse_person(self, graph, parent: Node): - return struc.Person( - name=_one_str_or_none(graph.objects(parent, FOAF.name)), - email=_one_str_or_none(graph.objects(parent, FOAF.mbox)), - ) - - def parse_spatial(self, graph: Graph, parent: Node) -> struc.Spatial: - return struc.Spatial( - extent=_one_str_or_none(graph.objects(parent, SKOS.prefLabel)), - location=_one_str_or_none(graph.objects(parent, OEO.location)), - resolution=_one_str_or_none( - graph.objects(parent, OEO.has_spatial_resolution) - ), - ) - - def parse_temporal(self, graph: Graph, parent: Node) -> struc.Temporal: - - orientation = self.parse_timestamp_orientation( - _only(graph.objects(parent, OEO.has_timestamp_alignment)) - ) - - return struc.Temporal( - start=self.parse_date(_only(graph.objects(parent, SCHEMA.startDate))), - end=self.parse_date(_only(graph.objects(parent, SCHEMA.endDate))), - ts_orientation=orientation, - reference_date=self.parse_date( - _only(graph.objects(parent, OEO.referenceDate)) - ), - resolution=_one_str_or_none(graph.objects(parent, OEO.has_time_resolution)), - aggregation=_one_str_or_none(graph.objects(parent, OEO.uses_aggregation)), - ) - - def parse_timestamp_orientation(self, node): - if node == OEO.left_orientation: - return struc.TimestampOrientation.left - elif node == OEO.middle_orientation: - return struc.TimestampOrientation.middle - elif node == OEO.right_orientation: - return struc.TimestampOrientation.right - else: - raise Exception("Unknown timestamp orientation: {}".format(node)) - - def parse_source(self, graph: Graph, parent: Node) -> struc.Source: - return struc.Source( - title=_one_str_or_none(graph.objects(parent, DCTERMS.title)), - description=_one_str_or_none(graph.objects(parent, DCTERMS.description)), - path=_one_str_or_none(graph.objects(parent, FOAF.page)), - licenses=[ - self.parse_terms_of_use(graph, tos) - for tos in graph.objects(parent, OEO.has_terms_of_use) - ], - ) - - def parse_terms_of_use(self, graph: Graph, parent: Node) -> struc.TermsOfUse: - if isinstance(parent, URIRef): - return None - else: - - return struc.TermsOfUse( - lic=self.parse_license( - graph, _only(graph.objects(parent, DCAT.license)) - ), - attribution=str( - _only(graph.objects(parent, DCATDE.licenseAttributionByText)) - ), - instruction=_one_str_or_none( - graph.objects(parent, OEO.has_instruction) - ), - ) - - def parse_license(self, graph, parent: Node): - kw = dict() - for c in graph.objects(parent, RDFS.comment): - kw["comment"] = _one_str_or_none(c) - refs = list(graph.objects(parent, RDFS.seeAlso)) - return struc.License( - name=_one_str_or_none(graph.objects(parent, SPDX.name)), - identifier=_one_str_or_none(graph.objects(parent, SPDX.licenseId)), - path=_one_str_or_none(graph.objects(parent, FOAF.page)), - other_references=refs if refs else None, - text=_one_str_or_none(graph.objects(parent, SPDX.licenseText)), - **kw - ) - - def parse_resource( - self, - graph: Graph, - parent: Node, - resources: Dict[str, Tuple[struc.Resource, Dict[str, struc.Field]]] = None, - ) -> struc.Resource: - rname = _one_str_or_none(graph.objects(parent, DCTERMS.title)) - resources = resources or dict() - if resources and rname in resources: - return resources[rname][0] - else: - dialect_node = _one_or_none(graph.objects(parent, OEO.has_dialect)) - dialect = self.parse_dialect(graph, dialect_node) if dialect_node else None - r = struc.Resource( - dialect=dialect, - encoding=_one_str_or_none(graph.objects(parent, OEO.encoding)), - name=rname, - path=_one_str_or_none(graph.objects(parent, DCAT.accessURL)), - profile=_one_str_or_none(graph.objects(parent, OEO.profile)), - resource_format=_one_str_or_none(graph.objects(parent, OEO.has_format)), - schema=None, - ) - r_fields = {} - resources[rname] = r, r_fields - schema = self.parse_schema(graph, parent, resources=resources) - r.schema = schema - for f in schema.fields: - if f.name not in r_fields: - r_fields[f.name] = f - return r - - def parse_schema( - self, - graph: Graph, - parent: Node, - resources: Dict[str, Tuple[Node, Iterable[Node]]] = None, - ) -> struc.Schema: - return struc.Schema( - fields=[ - self.parse_field(graph, f) for f in graph.objects(parent, OEO.field) - ], - primary_key=[ - self.parse_field(graph, f).name - for f in graph.objects(parent, OEO.primaryKey) - ], - foreign_keys=[ - self.parse_foreign_key(graph, f, resources=resources) - for f in graph.objects(parent, OEO.has_foreignKey) - ], - ) - - def parse_dialect(self, graph: Graph, parent: Node) -> struc.Dialect: - delim = _one_or_none(graph.objects(parent, DCTERMS.delimiter)) - if delim is not None: - delim = str(delim) - return struc.Dialect( - decimal_separator=_one_str_or_none( - graph.objects(parent, OEO.decimalSeparator) - ), - delimiter=delim, - ) - - def parse_field(self, graph: Graph, parent: Node) -> struc.Field: - return struc.Field( - name=_one_str_or_none(graph.objects(parent, DCTERMS.title)), - unit=_one_str_or_none(graph.objects(parent, OEO.unit)), - field_type=_one_str_or_none(graph.objects(parent, OEO.type)), - description=_one_str_or_none(graph.objects(parent, DCTERMS.description)), - ) - - def parse_foreign_key( - self, - graph: Graph, - parent: Node, - resources: Dict[str, Tuple[Node, Iterable[Node]]] = None, - ) -> struc.ForeignKey: - return struc.ForeignKey( - references=[ - self.parse_reference(graph, r) - for r in graph.objects(parent, OEO.has_reference) - ] - ) - - def parse_reference( - self, - graph: Graph, - parent: Node, - resources: Dict[str, Tuple[Node, Iterable[Node]]] = None, - ) -> struc.Reference: - target_node = _only(graph.objects(parent, OEO.has_target)) - target_field = self.parse_field(graph, target_node) - target_resource = self.parse_resource( - graph, _only(graph.subjects(OEO.field, target_node)) - ) - target_field.resource = target_resource - return struc.Reference( - source=self.parse_field( - graph, _only(graph.objects(parent, OEO.has_source)) - ), - target=target_field, - ) - - def parse_review(self, graph: Graph, parent: Node) -> struc.Review: - return struc.Review( - badge=_one_str_or_none(graph.objects(parent, OEO.has_badge)), - path=_one_str_or_none(graph.objects(parent, FOAF.page)), - ) - - def parse_meta_comment(self, graph: Graph, parent: Node) -> struc.MetaComment: - return struc.MetaComment( - dates=_one_str_or_none(graph.objects(parent, OEO.dates_info)), - languages=_one_str_or_none(graph.objects(parent, OEO.languages_info)), - licenses=_one_str_or_none(graph.objects(parent, OEO.licenses_info)), - metadata_info=_one_str_or_none(graph.objects(parent, OEO.metadata_info)), - none=_one_str_or_none(graph.objects(parent, OEO.none_info)), - review=_one_str_or_none(graph.objects(parent, OEO.review_info)), - units=_one_str_or_none(graph.objects(parent, OEO.units_info)), - ) - - def parse_metadata(self, graph: Graph, parent: Node) -> struc.OEPMetadata: - context = self.parse_context(graph, parent) - contributors = [ - self.parse_contributor(graph, c) - for c in graph.objects(parent, OEO.has_contribution) - ] - language = [str(l) for l in graph.objects(parent, DCTERMS.language)] - spatial = self.parse_spatial( - graph, _only(graph.objects(parent, DCTERMS.spatial)) - ) - - temporal = self.parse_temporal( - graph, _only(graph.objects(parent, DCTERMS.temporal)) - ) - comment = self.parse_meta_comment( - graph, _only(graph.objects(parent, OEO.comment)) - ) - resources = [ - self.parse_resource(graph, r) - for r in graph.objects(parent, OEO.has_resource) - ] - terms_of_use = [ - self.parse_terms_of_use(graph, l) - for l in graph.objects(parent, OEO.has_terms_of_use) - ] - sources = [ - self.parse_source(graph, s) for s in graph.objects(parent, DCTERMS.source) - ] - review = self.parse_review(graph, _only(graph.objects(parent, OEO.has_review))) - return struc.OEPMetadata( - comment=comment, - context=context, - contributions=contributors, - description=_one_str_or_none(graph.objects(parent, DCTERMS.description)), - identifier=str(parent), - keywords=list(map(str, graph.objects(parent, DCAT.keyword))), - languages=language, - name=_one_str_or_none(graph.objects(parent, ADMS.Identifier)), - terms_of_use=terms_of_use, - publication_date=self.parse_date( - _only(graph.objects(parent, OEO.publicationDate)) - ), - resources=resources, - review=review, - sources=sources, - spatial=spatial, - temporal=temporal, - title=_one_str_or_none(graph.objects(parent, DCTERMS.title)), - ) diff --git a/src/omi/dialects/rdf/renderer.py b/src/omi/dialects/rdf/renderer.py deleted file mode 100644 index a4a767b..0000000 --- a/src/omi/dialects/rdf/renderer.py +++ /dev/null @@ -1,8 +0,0 @@ -from rdflib.graph import Graph - -from omi.dialects.base.renderer import Renderer - - -class GraphRenderer(Renderer): - def render(self, inp: Graph, *args, **kwargs): - return inp.serialize(format="ttl") diff --git a/src/omi/oem_structures/__init__.py b/src/omi/oem_structures/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/omi/oem_structures/oem_v15.py b/src/omi/oem_structures/oem_v15.py deleted file mode 100644 index 866aa48..0000000 --- a/src/omi/oem_structures/oem_v15.py +++ /dev/null @@ -1,391 +0,0 @@ -""" -This module extends the internal data structure -with the latest changes of the OEMetadata standard. - -An OEMeadata version specific dialect can import it. - -This bypasses the limitation of OMI to rely on a single -internal representation of the data structure, since -OEMetadata is not a static structure and OMI must be -be able to validate multiple OEMetadata versions. -""" -# TODO: maybe change all inheritance to omi.structure to -# avoide alot of redundancy - -from datetime import datetime -from enum import Enum -from typing import Iterable - -from omi.structure import Compilable - - -class Language(Compilable): - __compiler_name__ = "language" - - -class Subject(Compilable): - __compiler_name__ = "subject" - - def __init__(self, name: str = None, path: str = None): - self.name = name - self.path = path - - -class Spatial(Compilable): - __compiler_name__ = "spatial" - - def __init__( - self, location: str = None, extent: str = None, resolution: str = None - ): - self.location = location - self.extent = extent - self.resolution = resolution - - -class TimestampOrientation(Compilable, Enum): - __compiler_name__ = "timestamp_orientation" - left = 0 - middle = 1 - right = 2 - - @staticmethod - def create(value: str) -> "TimestampOrientation": - if value == "left": - return TimestampOrientation.left - elif value == "middle": - return TimestampOrientation.middle - elif value == "right": - return TimestampOrientation.right - else: - raise Exception("Unknown timestamp orientation:", value) - - -class Timeseries(Compilable): - __compiler_name__ = "timeseries" - - def __init__( - self, - start: datetime = None, - end: datetime = None, - resolution: str = None, - ts_orientation: TimestampOrientation = None, - aggregation: str = None, - ): - self.ts_start = start - self.ts_end = end - self.ts_resolution = resolution - self.ts_orientation = ts_orientation - self.aggregation = aggregation - - -class Temporal(Compilable): - __compiler_name__ = "temporal" - - def __init__( - self, - reference_date: datetime = None, - timeseries_collection: Iterable[Timeseries] = None, - ): - self.reference_date = reference_date - self.timeseries_collection = timeseries_collection - - -class License(Compilable): - __compiler_name__ = "license" - - def __init__( - self, - name: str = None, - title: str = None, - path: str = None, - # instruction: str = None, - # attribution: str = None, - # other_references: Iterable[str] = None, - ): - self.name = name - self.title = title - self.path = path - # self.instruction = instruction - # self.attribution = attribution - # self.other_references = other_references - - @staticmethod - def instance_name_from_id(identifier: str): - return "L_" + identifier.replace("-", "_").replace(".", "_").replace( - "+", "_plus" - ) - - -class TermsOfUse(Compilable): - __compiler_name__ = "terms_of_use" - - def __init__( - self, instruction: str = None, attribution: str = None, lic: License = None - ): - self.license = lic - self.instruction = instruction - self.attribution = attribution - - - -class Source(Compilable): - __compiler_name__ = "source" - - def __init__( - self, - title: str = None, - description: str = None, - path: str = None, - licenses: Iterable[TermsOfUse] = None, - ): - self.title = title - self.description = description - self.path = path - self.licenses = licenses - - -class Person(Compilable): - __compiler_name__ = "person" - - def __init__(self, name: str = None, email: str = None): - self.name = name - self.email = email - - -class Contribution(Compilable): - __compiler_name__ = "contribution" - - def __init__( - self, - contributor: Person = None, - date: datetime = None, - obj: str = None, - comment: str = None, - ): - self.contributor = contributor - self.date = date - self.object = obj - self.comment = comment - - -class IsAbout(Compilable): - __compiler_name__ = "isAbout" - - def __init__(self, name: str = None, path: str = None): - self.name = name - self.path = path - - -class ValueReference(Compilable): - __compiler_name__ = "valueReference" - - def __init__(self, value: str = None, name: str = None, path: str = None): - self.value = value - self.name = name - self.path = path - - -class Field(Compilable): - __compiler_name__ = "field" - - def __init__( - self, - name: str = None, - description: str = None, - field_type: str = None, - isAbout: Iterable[IsAbout] = None, - valueReference: Iterable[ValueReference] = None, - unit: str = None, - resource: "Resource" = None, - ): - self.name = name - self.description = description - self.type = field_type - self.isAbout = isAbout - self.valueReference = valueReference - self.unit = unit - self.resource = resource - - def __repr__(self): - return "{}({})".format( - self.__class__.__name__, - ",".join( - "{}={}".format(key, val) - for key, val in self.__dict__.items() - if key != "resource" - ), - ) - - -class Agency(Compilable): - __compiler_name__ = "agency" - - def __init__(self, name: str = None, logo: str = None): - self.name = name - self.logo = logo - - -class Context(Compilable): - __compiler_name__ = "context" - - def __init__( - self, - homepage: str = None, - documentation: str = None, - source_code: str = None, - contact: str = None, - grant_number: str = None, - funding_agency: Agency = None, - publisher: Agency = None, - ): - self.homepage = homepage - self.documentation = documentation - self.source_code = source_code - self.contact = contact - self.grant_number = grant_number - self.funding_agency = funding_agency - self.publisher = publisher - - -class Reference(Compilable): - __compiler_name__ = "reference" - - def __init__(self, source: Field = None, target: Field = None): - self.source = source - self.target = target - - -class ForeignKey(Compilable): - __compiler_name__ = "foreign_key" - - def __init__(self, references: Iterable[Reference] = None): - self.references = references - - -class Schema(Compilable): - __compiler_name__ = "schema" - - def __init__( - self, - fields: Iterable[Field] = None, - primary_key: Iterable[str] = None, - foreign_keys: Iterable[ForeignKey] = None, - ): - self.fields = fields - self.primary_key = primary_key - self.foreign_keys = foreign_keys - - -class Dialect(Compilable): - __compiler_name__ = "dialect" - - def __init__(self, delimiter: str = None, decimal_separator: str = None): - self.delimiter = delimiter - self.decimal_separator = decimal_separator - - -class Resource(Compilable): - __compiler_name__ = "resource" - - def __init__( - self, - name: str = None, - path: str = None, - profile: str = None, - resource_format: str = None, - encoding: str = None, - schema: Schema = None, - dialect: Dialect = None, - ): - self.name = name - self.path = path - self.profile = profile - self.format = resource_format - self.encoding = encoding - self.schema = schema - if schema is not None: - for field in schema.fields: - field.resource = self - self.dialect = dialect - - -class MetaComment(Compilable): - __compiler_name__ = "meta_comment" - - def __init__( - self, - metadata_info: str = None, - dates: str = None, - units: str = None, - languages: str = None, - licenses: str = None, - review: str = None, - null: str = None, - todo: str = None, - ): - self.metadata_info = metadata_info - self.dates = dates - self.units = units - self.languages = languages - self.licenses = licenses - self.review = review - self.null = null - self.todo = todo - - -class Review(Compilable): - __compiler_name__ = "review" - - def __init__(self, path: str = None, badge: str = None): - self.path = path - self.badge = badge - - -class OEPMetadata(Compilable): - __compiler_name__ = "metadata" - __required__ = ["id"] - - def __init__( - self, - name: str = None, - title: str = None, - identifier: str = None, - description: str = None, - languages: Iterable[Language] = None, - subject: Iterable[Subject] = None, - keywords: Iterable[str] = None, - publication_date: datetime = None, - context: Context = None, - spatial: Spatial = None, - temporal: Temporal = None, - sources: Iterable[Source] = None, - terms_of_use: Iterable[TermsOfUse] = None, - contributions: Iterable[Contribution] = None, - resources: Iterable[Resource] = None, - databus_identifier: str = None, - databus_context: str = None, - review: Review = None, - comment: MetaComment = None, - ): - self.name = name - self.title = title - self.identifier = identifier - self.description = description - self.subject = subject - self.languages = languages - self.keywords = keywords - self.publication_date = publication_date - self.context = context - self.spatial = spatial - self.temporal = temporal - self.sources = sources - self.license = terms_of_use - self.contributions = contributions - self.resources = resources - self.databus_identifier = databus_identifier - self.databus_context = databus_context - self.review = review - self.comment = comment - - def has_keywords(self): - return self.keywords is not None diff --git a/src/omi/structure.py b/src/omi/structure.py deleted file mode 100644 index 2ce0b80..0000000 --- a/src/omi/structure.py +++ /dev/null @@ -1,368 +0,0 @@ -from datetime import datetime -from enum import Enum -from typing import Iterable - - -class Compilable: - """ - An abstract class for all metadata components. - """ - - __compiler_name__ = None - """Used to identify the appropriate compiler function for this structure""" - - __required__ = None - __optional__ = None - - def __repr__(self): - return "{}({})".format( - self.__class__.__name__, - ",".join("{}={}".format(key, val) for key, val in self.__dict__.items()), - ) - - def __lt__(self, other): - for key in sorted(self.__dict__): - s = getattr(self, key) - o = getattr(other, key) - if s is None: - return True - elif s < o: - return True - elif s > o: - return False - return False - - def get_missing_fields(self): - for key in sorted(self.__dict__): - if key in self.__required__: - if s is None: - yield key - v = getattr(self, key) - if isinstance(v, Compilable): - for x in v.get_missing_fields(): - yield key + "." + x - - -class Language(Compilable): - __compiler_name__ = "language" - - -class Spatial(Compilable): - __compiler_name__ = "spatial" - - def __init__( - self, location: str = None, extent: str = None, resolution: str = None - ): - self.location = location - self.extent = extent - self.resolution = resolution - - -class TimestampOrientation(Compilable, Enum): - __compiler_name__ = "timestamp_orientation" - left = 0 - middle = 1 - right = 2 - - @staticmethod - def create(value: str) -> "TimestampOrientation": - if value == "left": - return TimestampOrientation.left - elif value == "middle": - return TimestampOrientation.middle - elif value == "right": - return TimestampOrientation.right - else: - raise Exception("Unknown timestamp orientation:", value) - - -class Temporal(Compilable): - __compiler_name__ = "temporal" - - def __init__( - self, - reference_date: datetime = None, - start: datetime = None, - end: datetime = None, - resolution: str = None, - ts_orientation: TimestampOrientation = None, - aggregation: str = None, - ): # TODO: This should not be a string... maybe - # we should use datetime instead? - self.reference_date = reference_date - self.ts_start = start - self.ts_end = end - self.ts_resolution = resolution - self.ts_orientation = ts_orientation - self.aggregation = aggregation - - -class License(Compilable): - __compiler_name__ = "license" - - def __init__( - self, - name: str = None, - identifier: str = None, - text: str = None, - path: str = None, - other_references: Iterable[str] = None, - comment: str = None, - ): - self.name = name - self.path = path - self.identifier = identifier - self.other_references = other_references - self.text = text - self.comment = comment - - @staticmethod - def instance_name_from_id(identifier: str): - return "L_" + identifier.replace("-", "_").replace(".", "_").replace( - "+", "_plus" - ) - - -class TermsOfUse(Compilable): - __compiler_name__ = "terms_of_use" - - def __init__( - self, instruction: str = None, attribution: str = None, lic: License = None - ): - self.instruction = instruction - self.attribution = attribution - self.license = lic - - -class Source(Compilable): - __compiler_name__ = "source" - - def __init__( - self, - title: str = None, - description: str = None, - path: str = None, - licenses: Iterable[TermsOfUse] = None, - ): - self.title = title - self.description = description - self.path = path - self.licenses = licenses - - -class Person(Compilable): - __compiler_name__ = "person" - - def __init__(self, name: str = None, email: str = None): - self.name = name - self.email = email - - -class Contribution(Compilable): - __compiler_name__ = "contribution" - - def __init__( - self, - contributor: Person = None, - date: datetime = None, - obj: str = None, - comment: str = None, - ): - self.contributor = contributor - self.date = date - self.object = obj - self.comment = comment - - -class Field(Compilable): - __compiler_name__ = "field" - - def __init__( - self, - name: str = None, - description: str = None, - field_type: str = None, - unit: str = None, - resource: "Resource" = None, - ): - self.name = name - self.description = description - self.type = field_type - self.unit = unit - self.resource = resource - - def __repr__(self): - return "{}({})".format( - self.__class__.__name__, - ",".join( - "{}={}".format(key, val) - for key, val in self.__dict__.items() - if key != "resource" - ), - ) - - -class Agency(Compilable): - __compiler_name__ = "agency" - - def __init__(self, name: str = None, logo: str = None): - self.name = name - self.logo = logo - - -class Context(Compilable): - __compiler_name__ = "context" - - def __init__( - self, - homepage: str = None, - documentation: str = None, - source_code: str = None, - contact: str = None, - grant_number: str = None, - funding_agency: Agency = None, - publisher: Agency = None, - ): - self.homepage = homepage - self.documentation = documentation - self.source_code = source_code - self.contact = contact - self.grant_number = grant_number - self.funding_agency = funding_agency - self.publisher = publisher - - -class Reference(Compilable): - __compiler_name__ = "reference" - - def __init__(self, source: Field = None, target: Field = None): - self.source = source - self.target = target - - -class ForeignKey(Compilable): - __compiler_name__ = "foreign_key" - - def __init__(self, references: Iterable[Reference] = None): - self.references = references - - -class Schema(Compilable): - __compiler_name__ = "schema" - - def __init__( - self, - fields: Iterable[Field] = None, - primary_key: Iterable[str] = None, - foreign_keys: Iterable[ForeignKey] = None, - ): - self.fields = fields - self.primary_key = primary_key - self.foreign_keys = foreign_keys - - -class Dialect(Compilable): - __compiler_name__ = "dialect" - - def __init__(self, delimiter: str = None, decimal_separator: str = None): - self.delimiter = delimiter - self.decimal_separator = decimal_separator - - -class Resource(Compilable): - __compiler_name__ = "resource" - - def __init__( - self, - name: str = None, - path: str = None, - profile: str = None, - resource_format: str = None, - encoding: str = None, - schema: Schema = None, - dialect: Dialect = None, - ): - self.name = name - self.path = path - self.profile = profile - self.format = resource_format - self.encoding = encoding - self.schema = schema - if schema is not None: - for field in schema.fields: - field.resource = self - self.dialect = dialect - - -class MetaComment(Compilable): - __compiler_name__ = "meta_comment" - - def __init__( - self, - metadata_info: str = None, - dates: str = None, - units: str = None, - languages: str = None, - licenses: str = None, - review: str = None, - none: str = None, - ): - self.metadata_info = metadata_info - self.dates = dates - self.units = units - self.languages = languages - self.licenses = licenses - self.review = review - self.none = none - - -class Review(Compilable): - __compiler_name__ = "review" - - def __init__(self, path: str = None, badge: str = None): - self.path = path - self.badge = badge - - -class OEPMetadata(Compilable): - __compiler_name__ = "metadata" - __required__ = ["id"] - - def __init__( - self, - name: str = None, - title: str = None, - identifier: str = None, - description: str = None, - languages: Iterable[Language] = None, - keywords: Iterable[str] = None, - publication_date: datetime = None, - context: Context = None, - spatial: Spatial = None, - temporal: Temporal = None, - sources: Iterable[Source] = None, - terms_of_use: Iterable[TermsOfUse] = None, - contributions: Iterable[Contribution] = None, - resources: Iterable[Resource] = None, - review: Review = None, - comment: MetaComment = None, - ): - self.name = name - self.title = title - self.identifier = identifier - self.description = description - self.languages = languages - self.keywords = keywords - self.publication_date = publication_date - self.context = context - self.spatial = spatial - self.temporal = temporal - self.sources = sources - self.license = terms_of_use - self.contributions = contributions - self.resources = resources - self.review = review - self.comment = comment - - def has_keywords(self): - return self.keywords is not None \ No newline at end of file diff --git a/tests/data/metadata_v13.json b/tests/data/metadata_v13.json deleted file mode 100644 index 6702e83..0000000 --- a/tests/data/metadata_v13.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "title": "Conceived Example Table Meant for Creating an Illustrative Metadata String thereof", - "description": "An imaginary table that provides many features, offering a suitable source for metadata template entries", - "language": [ "eng" ], - "spatial": { - "location": "", - "extent": "Berlin", - "resolution": "1 m"}, - "temporal": { - "reference_date": "2018-11-13", - "start": "", - "end": "", - "resolution": ""}, - "sources": [ - {"name": "Technical review and evaluation of Issue", - "description": "Study financed by Organisation describes Issue. The study is authored by Jon Doe and Erika Mustermann", - "url": "https://doi.org/1.1/j.d.2000.01.001", - "license": "", - "copyright": "Publisher"}, - {"name": "Metastudy on Issue", - "description": "Study financed by State Actor evaluates Issue in regions. The study is authored by Jane Doe and Otto Normal", - "url": "https://doi.org/2.2/j.d.2022.02.022", - "license": "", - "copyright": "Publisher2"}], - "license": { - "id": "ODbL-1.0", - "name": "Open Data Commons Open Database License 1.0", - "version": "1", - "url": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "copyright": "Institute"}, - "contributors": [ - {"name": "Person McHuman", - "email": "person.mchuman@good-institute.net", - "date": "2011-1-11", - "comment": "Prepared the dataset"}, - {"name": "Indivia Mensch", - "email": "indivia.mensch@gute-organisation.org", - "date": "2012-2-12", - "comment": "Fixed Metadata String and date format "}], - "resources": [ - {"name": "example.datatable", - "format": "PostgreSQL", - "fields": [ - {"name": "id", - "description": "unambiguous unique numer", - "unit": "none"}, - {"name": "component_id", - "description": "Identifying numer of component. May repeat due to several occurences of the same component.", - "unit": "none"}, - {"name": "measurement", - "description": "Measured by Instrument", - "unit": "kWh"}, - {"name": "reference", - "description": "Bibtex String that references the information source.", - "unit": "none"}]}], - "metadata_version": "1.3"} diff --git a/tests/data/metadata_v13_converted.json b/tests/data/metadata_v13_converted.json deleted file mode 100644 index a281c1d..0000000 --- a/tests/data/metadata_v13_converted.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "title": "Conceived Example Table Meant for Creating an Illustrative Metadata String thereof", - "description": "An imaginary table that provides many features, offering a suitable source for metadata template entries", - "language": [ "eng" ], - "spatial": { - "extent": "Berlin", - "resolution": "1 m"}, - "temporal": { - "referenceDate": "2018-11-13", - "timeseries": {} -}, - "sources": [ - {"title": "Technical review and evaluation of Issue", - "description": "Study financed by Organisation describes Issue. The study is authored by Jon Doe and Erika Mustermann", - "path": "https://doi.org/1.1/j.d.2000.01.001", - "licenses": [{"attribution": "Publisher"}] - }, - {"title": "Metastudy on Issue", - "description": "Study financed by State Actor evaluates Issue in regions. The study is authored by Jane Doe and Otto Normal", - "path": "https://doi.org/2.2/j.d.2022.02.022", - "licenses": [{"attribution": "Publisher2" - }] - }], - "licenses": [ - {"instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "Institute", - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/"}], - "contributors": [ - {"title": "Person McHuman", - "email": "person.mchuman@good-institute.net", - "date": "2011-01-11", - "comment": "Prepared the dataset"}, - {"title": "Indivia Mensch", - "email": "indivia.mensch@gute-organisation.org", - "date": "2012-02-12", - "comment": "Fixed Metadata String and date format "}], - "resources": [ - { - "name": "example.datatable", - "format": "PostgreSQL", - "schema": { - "fields": [ - {"name": "id", - "description": "unambiguous unique numer", - "unit": "none"}, - {"name": "component_id", - "description": "Identifying numer of component. May repeat due to several occurences of the same component.", - "unit": "none"}, - {"name": "measurement", - "description": "Measured by Instrument", - "unit": "kWh"}, - {"name": "reference", - "description": "Bibtex String that references the information source.", - "unit": "none"}]}}], - "metaMetadata": { - "metadataVersion": "OEP-1.4.0", - "metadataLicense": { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/"}}} diff --git a/tests/data/metadata_v13_minimal.json b/tests/data/metadata_v13_minimal.json deleted file mode 100644 index e677875..0000000 --- a/tests/data/metadata_v13_minimal.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "id": "id" -} diff --git a/tests/data/metadata_v14.json b/tests/data/metadata_v14.json deleted file mode 100644 index ca773ce..0000000 --- a/tests/data/metadata_v14.json +++ /dev/null @@ -1,247 +0,0 @@ -{ - "name": "oep_metadata_table_example_v14", - "title": "Good example title", - "id": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v14", - "description": "example metadata for example data", - "language": [ - "en-GB", - "en-US", - "de-DE", - "fr-FR" - ], - "keywords": [ - "example", - "template", - "test" - ], - "publicationDate": "2018-06-12", - "context": { - "homepage": "https://reiner-lemoine-institut.de/szenariendb/", - "documentation": "https://github.com/OpenEnergyPlatform/organisation/wiki/metadata", - "sourceCode": "https://github.com/OpenEnergyPlatform/examples/tree/master/metadata", - "contact": "https://github.com/Ludee", - "grantNo": "03ET4057", - "fundingAgency": "Bundesministerium für Wirtschaft und Energie", - "fundingAgencyLogo": "https://www.innovation-beratung-foerderung.de/INNO/Redaktion/DE/Bilder/Titelbilder/titel_foerderlogo_bmwi.jpg?__blob=poster&v=2", - "publisherLogo": "https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png" - }, - "spatial": { - "extent": "europe", - "resolution": "100 m" - }, - "temporal": { - "referenceDate": "2016-01-01", - "timeseries": { - "start": "2017-01-01T00:00:00+01:00", - "end": "2017-12-31T23:00:00+01:00", - "resolution": "1 h", - "alignment": "left", - "aggregationType": "sum" - } - }, - "sources": [ - { - "title": "OpenEnergyPlatform Metadata Example", - "description": "Metadata description", - "path": "https://github.com/OpenEnergyPlatform", - "licenses": [ - { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/legalcode", - "instruction": "You are free: To Share, To Create, To Adapt", - "attribution": "© Reiner Lemoine Institut" - } - ] - }, - { - "title": "OpenStreetMap", - "description": "A collaborative project to create a free editable map of the world", - "path": "https://www.openstreetmap.org/", - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© OpenStreetMap contributors" - } - ] - } - ], - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© Reiner Lemoine Institut © OpenStreetMap contributors" - } - ], - "contributors": [ - { - "title": "Ludee", - "date": "2016-06-16", - "object": "metadata", - "comment": "Create metadata" - }, - { - "title": "Ludee", - "date": "2016-11-22", - "object": "metadata", - "comment": "Update metadata" - }, - { - "title": "Ludee", - "date": "2016-11-22", - "object": "metadata", - "comment": "Update header and license" - }, - { - "title": "Ludee", - "date": "2017-03-16", - "object": "metadata", - "comment": "Add license to source" - }, - { - "title": "Ludee", - "date": "2017-03-28", - "object": "metadata", - "comment": "Add copyright to source and license" - }, - { - "title": "Ludee", - "date": "2017-05-30", - "object": "metadata", - "comment": "Release metadata version 1.3" - }, - { - "title": "Ludee", - "date": "2017-06-26", - "object": "metadata", - "comment": "Move referenceDate into temporal and remove array" - }, - { - "title": "Ludee", - "date": "2018-07-19", - "object": "metadata", - "comment": "Start metadata version 1.4" - }, - { - "title": "Ludee", - "date": "2018-07-26", - "object": "data", - "comment": "Rename table and files" - }, - { - "title": "Ludee", - "date": "2018-10-18", - "object": "metadata", - "comment": "Add contribution object" - }, - { - "title": "christian-rli", - "date": "2018-10-18", - "object": "metadata", - "comment": "Add datapackage compatibility" - }, - { - "title": "Ludee", - "date": "2018-11-02", - "object": "metadata", - "comment": "Release metadata version 1.4" - }, - { - "title": "christian-rli", - "date": "2019-02-05", - "object": "metadata", - "comment": "Apply template structure to example" - }, - { - "title": "Ludee", - "date": "2019-03-22", - "object": "metadata", - "comment": "Hotfix foreignKeys" - }, - { - "title": "Ludee", - "date": "2019-07-09", - "object": "metadata", - "comment": "Release metadata version OEP-1.3.0" - } - ], - "resources": [ - { - "profile": "tabular-data-resource", - "name": "model_draft.oep_metadata_table_example_v14", - "path": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v14", - "format": "PostgreSQL", - "encoding": "UTF-8", - "schema": { - "fields": [ - { - "name": "id", - "description": "Unique identifier", - "type": "serial" - }, - { - "name": "year", - "description": "Reference year", - "type": "integer" - }, - { - "name": "value", - "description": "Example value", - "type": "double precision", - "unit": "MW" - }, - { - "name": "geom", - "description": "Geometry", - "type": "geometry(Point, 4326)" - } - ], - "primaryKey": [ - "id" - ], - "foreignKeys": [ - { - "fields": [ - "year" - ], - "reference": { - "resource": "schema.table", - "fields": [ - "year" - ] - } - } - ] - }, - "dialect": { - "decimalSeparator": "." - } - } - ], - "review": { - "path": "https://github.com/OpenEnergyPlatform/data-preprocessing/wiki", - "badge": "platin" - }, - "metaMetadata": { - "metadataVersion": "OEP-1.4.0", - "metadataLicense": { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/" - } - }, - "_comment": { - "metadata": "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/organisation/wiki/metadata)", - "dates": "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", - "units": "Use a space between numbers and units (100 m)", - "languages": "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", - "licenses": "License name must follow the SPDX License List (https://spdx.org/licenses/)", - "review": "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/wiki)", - "null": "If not applicable use (null)" - } -} \ No newline at end of file diff --git a/tests/data/metadata_v14.ttl b/tests/data/metadata_v14.ttl deleted file mode 100644 index fb2e153..0000000 --- a/tests/data/metadata_v14.ttl +++ /dev/null @@ -1,202 +0,0 @@ -@prefix adms: . -@prefix dcat: . -@prefix dcatde: . -@prefix dct: . -@prefix foaf: . -@prefix oeo: . -@prefix rdf: . -@prefix rdfs: . -@prefix schema: . -@prefix skos: . -@prefix spdx: . -@prefix xml: . -@prefix xsd: . - - a dcat:Dataset ; - oeo:comment [ oeo:dates_info "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)" ; - oeo:languages_info "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)" ; - oeo:licenses_info "License name must follow the SPDX License List (https://spdx.org/licenses/)" ; - oeo:metadata_info "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/organisation/wiki/metadata)" ; - oeo:none_info "If not applicable use (null)" ; - oeo:review_info "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/wiki)" ; - oeo:units_info "Use a space between numbers and units (100 m)" ] ; - oeo:documentation "https://github.com/OpenEnergyPlatform/organisation/wiki/metadata" ; - oeo:grantNo "03ET4057" ; - oeo:has_contribution [ oeo:comment "Add datapackage compatibility" ; - oeo:date "2018-10-18"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "christian-rli" ] ], - [ oeo:comment "Update header and license" ; - oeo:date "2016-11-22"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Update metadata" ; - oeo:date "2016-11-22"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Start metadata version 1.4" ; - oeo:date "2018-07-19"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Rename table and files" ; - oeo:date "2018-07-26"^^xsd:date ; - oeo:object "data" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Move referenceDate into temporal and remove array" ; - oeo:date "2017-06-26"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Add contribution object" ; - oeo:date "2018-10-18"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Create metadata" ; - oeo:date "2016-06-16"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Hotfix foreignKeys" ; - oeo:date "2019-03-22"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Add license to source" ; - oeo:date "2017-03-16"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Release metadata version 1.4" ; - oeo:date "2018-11-02"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Apply template structure to example" ; - oeo:date "2019-02-05"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "christian-rli" ] ], - [ oeo:comment "Add copyright to source and license" ; - oeo:date "2017-03-28"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ], - [ oeo:comment "Release metadata version 1.3" ; - oeo:date "2017-05-30"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ] , - [ oeo:comment "Release metadata version OEP-1.3.0" ; - oeo:date "2019-07-09"^^xsd:date ; - oeo:object "metadata" ; - dct:contributor [ a foaf:Person ; - foaf:name "Ludee" ] ]; - oeo:has_resource [ a dcat:Distribution ; - oeo:encoding "UTF-8" ; - oeo:field _:N6d1fcbe69a154f9283bd5a0aadc815a5, - [ a oeo:DatabaseField ; - oeo:type "double precision" ; - oeo:unit "MW" ; - dct:description "Example value" ; - dct:title "value" ], - [ a oeo:DatabaseField ; - oeo:type "geometry(Point, 4326)" ; - dct:description "Geometry" ; - dct:title "geom" ], - _:Nfe3d1d343c024a7a9a22bd5bced82242 ; - oeo:has_dialect [ oeo:decimalSeparator "." ] ; - oeo:has_foreignKey [ oeo:has_reference [ oeo:has_source _:N6d1fcbe69a154f9283bd5a0aadc815a5 ; - oeo:has_target _:Nace2ec79248d4e8baca5a082a3cdb26a ] ] ; - oeo:has_format "PostgreSQL" ; - oeo:primaryKey _:Nfe3d1d343c024a7a9a22bd5bced82242 ; - oeo:profile "tabular-data-resource" ; - dct:title "model_draft.oep_metadata_table_example_v14" ; - dcat:accessURL "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v14" ] ; - oeo:has_review [ oeo:has_badge "platin" ; - foaf:page ] ; - oeo:has_terms_of_use [ dcatde:licenseAttributionByText "© Reiner Lemoine Institut © OpenStreetMap contributors" ; - oeo:has_instruction "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!" ; - dcat:license _:odbl ] ; - oeo:metadataLicense ; - oeo:publicationDate "2018-06-12"^^xsd:date ; - oeo:sourceCode "https://github.com/OpenEnergyPlatform/examples/tree/master/metadata" ; - dct:description "example metadata for example data" ; - dct:language "de-DE", - "en-GB", - "en-US", - "fr-FR" ; - dct:source [ - dct:description "Metadata description" ; - oeo:has_terms_of_use [ - dcat:license _:cc0; - oeo:has_instruction "You are free: To Share, To Create, To Adapt" ; - dcatde:licenseAttributionByText "© Reiner Lemoine Institut" ; - ]; - dct:title "OpenEnergyPlatform Metadata Example" ; - foaf:page "https://github.com/OpenEnergyPlatform" - ],[ - dct:description "A collaborative project to create a free editable map of the world" ; - dct:title "OpenStreetMap" ; - oeo:has_terms_of_use [ - dcat:license _:odbl; - oeo:has_instruction "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!" ; - dcatde:licenseAttributionByText "© OpenStreetMap contributors" ; - ]; - foaf:page "https://www.openstreetmap.org/" - ] ; - dct:spatial [ oeo:has_spatial_resolution "100 m" ; - skos:prefLabel "europe" ] ; - dct:temporal [ a dct:PeriodOfTime ; - oeo:has_timestamp_alignment oeo:left_orientation ; - oeo:has_time_resolution "1 h" ; - oeo:referenceDate "2016-01-01T00:00:00"^^xsd:dateTime ; - schema:endDate "2017-12-31T23:00:00+01:00"^^xsd:dateTime ; - schema:startDate "2017-01-01T00:00:00+01:00"^^xsd:dateTime ; - oeo:uses_aggregation "sum"] ; - dct:title "Good example title" ; - adms:Identifier "oep_metadata_table_example_v14" ; - dcat:contactpoint "https://github.com/Ludee" ; - dcat:keyword "example", - "template", - "test" ; - oeo:has_funding_agency [ - dct:title "Bundesministerium für Wirtschaft und Energie"; - oeo:has_logo "https://www.innovation-beratung-foerderung.de/INNO/Redaktion/DE/Bilder/Titelbilder/titel_foerderlogo_bmwi.jpg?__blob=poster&v=2" - ]; - oeo:has_publisher [ - oeo:has_logo "https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png" - ]; - foaf:homepage "https://reiner-lemoine-institut.de/szenariendb/" . - -[] a dcat:Distribution ; - oeo:field _:Nace2ec79248d4e8baca5a082a3cdb26a ; - dct:title "schema.table" . - -_:N6d1fcbe69a154f9283bd5a0aadc815a5 a oeo:DatabaseField ; - oeo:type "integer" ; - dct:description "Reference year" ; - dct:title "year" . - -_:Nace2ec79248d4e8baca5a082a3cdb26a a oeo:DatabaseField ; - dct:title "year" . - -_:Nfe3d1d343c024a7a9a22bd5bced82242 a oeo:DatabaseField ; - oeo:type "serial" ; - dct:description "Unique identifier" ; - dct:title "id" . - -_:odbl a dcat:LicenseDocument ; - spdx:licenseId "ODbL-1.0" ; - spdx:name "Open Data Commons Open Database License 1.0" ; - foaf:page "https://opendatacommons.org/licenses/odbl/1.0/" . - -_:cc0 a dcat:LicenseDocument ; - spdx:licenseId "CC0-1.0" ; - spdx:name "Creative Commons Zero v1.0 Universal" ; - foaf:page "https://creativecommons.org/publicdomain/zero/1.0/legalcode" . diff --git a/tests/data/metadata_v14_minimal.json b/tests/data/metadata_v14_minimal.json deleted file mode 100644 index e677875..0000000 --- a/tests/data/metadata_v14_minimal.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "id": "id" -} diff --git a/tests/data/metadata_v14_withoutresource.json b/tests/data/metadata_v14_withoutresource.json deleted file mode 100644 index 13a8bfd..0000000 --- a/tests/data/metadata_v14_withoutresource.json +++ /dev/null @@ -1,89 +0,0 @@ -{"name": "oep_metadata_table_example_v14", -"title": "Good example title", -"id": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v14", -"description": "example metadata for example data", -"language": [ "en-GB", "en-US", "de-DE", "fr-FR" ], -"keywords": [ "example", "template", "test" ], -"publicationDate": "2018-06-12", -"context": - {"homepage": "https://reiner-lemoine-institut.de/szenariendb/", - "documentation": "https://github.com/OpenEnergyPlatform/organisation/wiki/metadata", - "sourceCode": "https://github.com/OpenEnergyPlatform/examples/tree/master/metadata", - "contact": "https://github.com/Ludee", - "grantNo": "03ET4057", - "fundingAgency": "Bundesministerium für Wirtschaft und Energie", - "fundingAgencyLogo": "https://www.innovation-beratung-foerderung.de/INNO/Redaktion/DE/Bilder/Titelbilder/titel_foerderlogo_bmwi.jpg?__blob=poster&v=2", - "publisherLogo": "https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png"}, -"spatial": - { - "extent": "europe", - "resolution": "100 m"}, -"temporal": - {"referenceDate": "2016-01-01", - "timeseries": - {"start": "2017-01-01T00:00+01", - "end": "2017-12-31T23:00+01", - "resolution": "1 h", - "alignment": "left", - "aggregationType": "sum"} }, -"sources": [ - {"title": "OpenEnergyPlatform Metadata Example", - "description": "Metadata description", - "path": "https://github.com/OpenEnergyPlatform", - "licenses": [ - {"name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/legalcode", - "instruction": "You are free: To Share, To Create, To Adapt", - "attribution": "© Reiner Lemoine Institut"} ] }, - {"title": "OpenStreetMap", - "description": "A collaborative project to create a free editable map of the world", - "path": "https://www.openstreetmap.org/", - "licenses": [ - {"name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© OpenStreetMap contributors"} ] } ], -"licenses": [ - {"name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© Reiner Lemoine Institut © OpenStreetMap contributors"}], -"contributors": [ - {"title": "Ludee", "date": "2016-06-16", "object": "metadata", "comment": "Create metadata"}, - {"title": "Ludee", "date": "2016-11-22", "object": "metadata", "comment": "Update metadata"}, - {"title": "Ludee", "date": "2016-11-22", "object": "metadata", "comment": "Update header and license"}, - {"title": "Ludee", "date": "2017-03-16", "object": "metadata", "comment": "Add license to source"}, - {"title": "Ludee", "date": "2017-03-28", "object": "metadata", "comment": "Add copyright to source and license"}, - {"title": "Ludee", "date": "2017-05-30", "object": "metadata", "comment": "Release metadata version 1.3"}, - {"title": "Ludee", "date": "2017-06-26", "object": "metadata", "comment": "Move referenceDate into temporal and remove array"}, - {"title": "Ludee", "date": "2018-07-19", "object": "metadata", "comment": "Start metadata version 1.4"}, - {"title": "Ludee", "date": "2018-07-26", "object": "data", "comment": "Rename table and files"}, - {"title": "Ludee", "date": "2018-10-18", "object": "metadata", "comment": "Add contribution object"}, - {"title": "christian-rli", "date": "2018-10-18", "object": "metadata", "comment": "Add datapackage compatibility"}, - {"title": "Ludee", "date": "2018-11-02", "object": "metadata", "comment": "Release metadata version 1.4"}, - {"title": "christian-rli", "date": "2019-02-05", "object": "metadata", "comment": "Apply template structure to example"}, - {"title": "Ludee", "date": "2019-03-22", "object": "metadata", "comment": "Hotfix foreignKeys"}, - {"title": "Ludee", "date": "2019-07-09", "object": "metadata", "comment": "Release metadata version OEP-1.3.0"} ], -"resources": [ - ], -"review": { - "path": "https://github.com/OpenEnergyPlatform/data-preprocessing/wiki", - "badge": "platin"}, -"metaMetadata": - {"metadataVersion": "OEP-1.4.0", - "metadataLicense": - {"name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/"} }, -"_comment": - {"metadata": "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/organisation/wiki/metadata)", - "dates": "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", - "units": "Use a space between numbers and units (100 m)", - "languages": "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", - "licenses": "License name must follow the SPDX License List (https://spdx.org/licenses/)", - "review": "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/wiki)", - "null": "If not applicable use (null)"} } - diff --git a/tests/data/metadata_v15.json b/tests/data/metadata_v15.json deleted file mode 100644 index 68d455d..0000000 --- a/tests/data/metadata_v15.json +++ /dev/null @@ -1,262 +0,0 @@ -{ - "name": "oep_metadata_table_example_v151", - "title": "Example title for metadata example - Version 1.5.1", - "id": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v151", - "description": "This is an metadata example for example data. There is a corresponding table on the OEP for each metadata version.", - "language": [ - "en-GB", - "en-US", - "de-DE", - "fr-FR" - ], - "subject": [ - { - "name": "energy", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000150" - }, - { - "name": "test dataset", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000408" - } - ], - "keywords": [ - "energy", - "example", - "template", - "test" - ], - "publicationDate": "2022-02-15", - "context": { - "homepage": "https://reiner-lemoine-institut.de/lod-geoss/", - "documentation": "https://openenergy-platform.org/tutorials/jupyter/OEMetadata/", - "sourceCode": "https://github.com/OpenEnergyPlatform/oemetadata/tree/master", - "contact": "https://github.com/Ludee", - "grantNo": "03EI1005", - "fundingAgency": "Bundesministerium für Wirtschaft und Klimaschutz", - "fundingAgencyLogo": "https://commons.wikimedia.org/wiki/File:BMWi_Logo_2021.svg#/media/File:BMWi_Logo_2021.svg", - "publisherLogo": "https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png" - }, - "spatial": { - "extent": "europe", - "resolution": "100 m" - }, - "temporal": { - "referenceDate": "2016-01-01", - "timeseries": [ - { - "start": "2017-01-01T00:00:00+01:00", - "end": "2017-12-31T23:00:00+01:00", - "resolution": "1 h", - "alignment": "left", - "aggregationType": "sum" - }, - { - "start": "2018-01-01T00:00:00+01:00", - "end": "2019-06-01T23:00:00+01:00", - "resolution": "15 min", - "alignment": "right", - "aggregationType": "sum" - } - ] - }, - "sources": [ - { - "title": "OpenEnergyPlatform Metadata Example", - "description": "Metadata description", - "path": "https://github.com/OpenEnergyPlatform", - "licenses": [ - { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/legalcode", - "instruction": "You are free: To Share, To Create, To Adapt", - "attribution": "© Reiner Lemoine Institut" - } - ] - }, - { - "title": "OpenStreetMap", - "description": "A collaborative project to create a free editable map of the world", - "path": "https://www.openstreetmap.org/", - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© OpenStreetMap contributors" - } - ] - } - ], - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© Reiner Lemoine Institut © OpenStreetMap contributors" - } - ], - "contributors": [ - { - "title": "Ludee", - "date": "2021-11-15", - "object": "metadata", - "comment": "Release metadata version OEP-1.5.0" - }, - { - "title": "Ludee", - "date": "2022-02-15", - "object": "metadata", - "comment": "Release metadata version OEP-1.5.1" - } - ], - "resources": [ - { - "profile": "tabular-data-resource", - "name": "model_draft.oep_metadata_table_example_v151", - "path": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v151", - "format": "PostgreSQL", - "encoding": "UTF-8", - "schema": { - "fields": [ - { - "name": "id", - "description": "Unique identifier", - "type": "serial", - "isAbout": [ - {} - ], - "valueReference": [ - {} - ] - }, - { - "name": "name", - "description": "Example name", - "type": "text", - "isAbout": [ - { - "name": "written name", - "path": "https://openenergy-platform.org/ontology/oeo/IAO_0000590" - } - ], - "valueReference": [ - {} - ] - }, - { - "name": "type", - "description": "Type of wind farm", - "type": "text", - "isAbout": [ - { - "name": "wind farm", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000447" - } - ], - "valueReference": [ - { - "value": "onshore", - "name": "onshore wind farm", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000311" - }, - { - "value": "offshore", - "name": "offshore wind farm", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000308" - } - ] - }, - { - "name": "year", - "description": "Reference year", - "type": "integer", - "isAbout": [ - { - "name": "year", - "path": "https://openenergy-platform.org/ontology/oeo/UO_0000036" - } - ], - "valueReference": [ - {} - ] - }, - { - "name": "value", - "description": "Example value", - "type": "double precision", - "unit": "MW", - "isAbout": [ - { - "name": "quantity value", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000350" - } - ], - "valueReference": [ - {} - ] - }, - { - "name": "geom", - "description": "Geometry", - "type": "geometry(Point, 4326)", - "isAbout": [ - { - "name": "spatial region", - "path": "https://openenergy-platform.org/ontology/oeo/BFO_0000006" - } - ], - "valueReference": [ - {} - ] - } - ], - "primaryKey": [ - "id" - ], - "foreignKeys": [ - { - "fields": [ - "year" - ], - "reference": { - "resource": "schema.table", - "fields": [ - "year" - ] - } - } - ] - }, - "dialect": { - "decimalSeparator": "." - } - } - ], - "@id": "https://databus.dbpedia.org/kurzum/mastr/bnetza-mastr/01.04.00", - "@context": "https://github.com/OpenEnergyPlatform/oemetadata/blob/master/metadata/latest/context.json", - "review": { - "path": "https://github.com/OpenEnergyPlatform/data-preprocessing/issues", - "badge": "Platinum" - }, - "metaMetadata": { - "metadataVersion": "OEP-1.5.2", - "metadataLicense": { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/" - } - }, - "_comment": { - "metadata": "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/oemetadata)", - "dates": "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", - "units": "Use a space between numbers and units (100 m)", - "languages": "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", - "licenses": "License name must follow the SPDX License List (https://spdx.org/licenses/)", - "review": "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/blob/master/data-review/manual/review_manual.md)", - "null": "If not applicable use: null", - "todo": "If a value is not yet available, use: todo" - } -} \ No newline at end of file diff --git a/tests/test_cli/__init__.py b/tests/test_cli/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_cli/test_cli_translation.py b/tests/test_cli/test_cli_translation.py deleted file mode 100644 index 6ec50ae..0000000 --- a/tests/test_cli/test_cli_translation.py +++ /dev/null @@ -1,22 +0,0 @@ -import click -from click.testing import CliRunner -from omi.cli import translate -import json - - -def test_cli_translation(): - @click.command() - @click.argument("name") - def hello(name): - click.echo("Hello %s!" % name) - - runner = CliRunner() - result = runner.invoke( - translate, - ["-f", "oep-v1.3", "-t", "oep-v1.4", "-omit_nones", "True", "tests/data/metadata_v13.json"], - ) - with open("tests/data/metadata_v13_converted.json") as expected_file: - expected = json.loads(expected_file.read()) - print(result) - assert result.exit_code == 0 - assert json.loads(result.output) == expected diff --git a/tests/test_dialects/__init__.py b/tests/test_dialects/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_dialects/base/__init__.py b/tests/test_dialects/base/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_dialects/base/parser.py b/tests/test_dialects/base/parser.py deleted file mode 100644 index d1c50a8..0000000 --- a/tests/test_dialects/base/parser.py +++ /dev/null @@ -1,62 +0,0 @@ -import datetime -import unittest - -from omi.structure import Compilable -from omi.structure import Field -from omi.oem_structures import oem_v15 - - -def _test_generic_parsing(parser, inp, expected, **kwargs): - if parser: - result = parser.parse_from_file(inp) - assert_compileable_equal(expected, result, **kwargs) - - -def assert_compileable_equal(expected, got, nulls=None, exclude=None): - exclude = exclude or [] - nulls = nulls or [None] - assert isinstance(expected, (Compilable, dict)), type(expected) - assert isinstance(got, (Compilable, dict)), (type(got), expected) - for key in set(expected.__dict__.keys()).union(set(got.__dict__.keys())): - if ( - key not in exclude - and isinstance(getattr(expected, key), Field) - or isinstance(getattr(expected, key), oem_v15.Field) - ): - l = getattr(expected, key) - r = getattr(got, key) - if isinstance(l, Compilable): - new_exclude = [] - if isinstance(l, Field): - new_exclude = ["resource"] - elif isinstance(l, oem_v15.Field): - new_exclude = ["resource"] - assert_compileable_equal(l, r, nulls=nulls, exclude=new_exclude) - elif isinstance(l, list) and isinstance(r, list): - assert len(l) == len( - r - ), "Lists do not match (Expected: {}; Got: {})".format(l, r) - for l0, r0 in zip(sorted(l), sorted(r)): - if isinstance(l0, Compilable): - if isinstance(l0, Field): - new_exclude = ["resource"] - elif isinstance(l, oem_v15.Field): - new_exclude = ["resource"] - else: - new_exclude = [] - assert_compileable_equal( - l0, r0, nulls=nulls, exclude=new_exclude - ) - else: - assert l0 == r0, "Expected: {}; Got: {}".format(l0, r0) - else: - if not (l is None and r in nulls): - assert ( - l == r - ), "Keys {} do not match (Expected:{}:{}; Got: {}:{})".format( - key, type(l), l, type(r), r - ) - - -def parse_date(s): - return datetime.datetime.strptime(s, "%Y-%M-%d") diff --git a/tests/test_dialects/internal_structures.py b/tests/test_dialects/internal_structures.py deleted file mode 100644 index 97d9f81..0000000 --- a/tests/test_dialects/internal_structures.py +++ /dev/null @@ -1,641 +0,0 @@ -import datetime - -from omi import structure as s -from omi.oem_structures import oem_v15 - -_source_year = s.Field( - name="year", description="Reference year", field_type="integer", unit=None -) - -_target_year = s.Field(name="year", description=None, field_type=None, unit=None) - -_target_resource = s.Resource( - name="schema.table", - schema=s.Schema(fields=[_target_year], foreign_keys=None, primary_key=None), - dialect=None, - encoding=None, - path=None, - profile=None, - resource_format=None, -) - -_target_year.resource = _target_resource - - -cc010 = s.License( - name="Creative Commons Zero v1.0 Universal", - identifier="CC0-1.0", - path="https://creativecommons.org/publicdomain/zero/1.0/legalcode", - other_references=None, - text=None, -) - -odbl10 = s.License( - identifier="ODbL-1.0", - name="Open Data Commons Open Database License 1.0", - path="https://opendatacommons.org/licenses/odbl/1.0/", - other_references=None, - text=None, -) - -odbl10_13 = s.License( - name=None, identifier="ODbL-1.0", path=None, other_references=None, text=None -) - -metadata_v_1_3 = s.OEPMetadata( - name=None, - title="Conceived Example Table Meant for Creating an Illustrative Metadata String thereof", - identifier=None, - description="An imaginary table that provides many features, offering a suitable source for metadata template entries", - languages=["eng"], - keywords=None, - publication_date=None, - context=None, - spatial=s.Spatial(location=None, extent="Berlin", resolution="1 m"), - temporal=s.Temporal( - reference_date=datetime.datetime(2018, 11, 13), - start=None, - end=None, - resolution=None, - ts_orientation=None, - ), - sources=[ - s.Source( - title="Technical review and evaluation of Issue", - description="Study financed by Organisation describes Issue. The study is authored by Jon Doe and Erika Mustermann", - path="https://doi.org/1.1/j.d.2000.01.001", - licenses=[s.TermsOfUse(attribution="Publisher")], - ), - s.Source( - title="Metastudy on Issue", - description="Study financed by State Actor evaluates Issue in regions. The study is authored by Jane Doe and Otto Normal", - path="https://doi.org/2.2/j.d.2022.02.022", - licenses=[s.TermsOfUse(attribution="Publisher2")], - ), - ], - terms_of_use=[ - s.TermsOfUse( - lic=odbl10, - instruction="You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - attribution="Institute", - ) - ], - contributions=[ - s.Contribution( - contributor=s.Person( - name="Person McHuman", email="person.mchuman@good-institute.net" - ), - date=datetime.datetime(2011, 1, 11, 0, 0, 0), - obj=None, - comment="Prepared the dataset", - ), - s.Contribution( - contributor=s.Person( - name="Indivia Mensch", email="indivia.mensch@gute-organisation.org" - ), - date=datetime.datetime(2012, 2, 12, 0, 0, 0), - obj=None, - comment="Fixed Metadata String and date format ", - ), - ], - resources=[ - s.Resource( - name="example.datatable", - resource_format="PostgreSQL", - schema=s.Schema( - fields=[ - s.Field( - name="id", - description="unambiguous unique numer", - unit=None, - field_type=None, - ), - s.Field( - name="component_id", - description="Identifying numer of component. May repeat due to several occurences of the same component.", - unit=None, - field_type=None, - ), - s.Field( - name="measurement", - description="Measured by Instrument", - unit="kWh", - field_type=None, - ), - s.Field( - name="reference", - description="Bibtex String that references the information source.", - unit=None, - field_type=None, - ), - ], - primary_key=None, - foreign_keys=None, - ), - path=None, - profile=None, - encoding=None, - dialect=None, - ) - ], - review=None, - comment=None, -) - -metadata_v_1_4 = s.OEPMetadata( - name="oep_metadata_table_example_v14", - title="Good example title", - identifier="http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v14", - description="example metadata for example data", - languages=["en-GB", "en-US", "de-DE", "fr-FR"], - keywords=["example", "template", "test"], - publication_date=datetime.datetime(2018, 6, 12, 0, 0), - context=s.Context( - homepage="https://reiner-lemoine-institut.de/szenariendb/", - documentation="https://github.com/OpenEnergyPlatform/organisation/wiki/metadata", - source_code="https://github.com/OpenEnergyPlatform/examples/tree/master/metadata", - contact="https://github.com/Ludee", - grant_number="03ET4057", - funding_agency=s.Agency( - name="Bundesministerium für Wirtschaft und Energie", - logo="https://www.innovation-beratung-foerderung.de/INNO/Redaktion/DE/Bilder/Titelbilder/titel_foerderlogo_bmwi.jpg?__blob=poster&v=2", - ), - publisher=s.Agency( - logo="https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png" - ), - ), - spatial=s.Spatial(location=None, extent="europe", resolution="100 m"), - temporal=s.Temporal( - reference_date=datetime.datetime(2016, 1, 1, 0, 0), - start=datetime.datetime( - 2017, 1, 1, 0, 0, tzinfo=datetime.timezone(datetime.timedelta(0, 3600)) - ), - end=datetime.datetime( - 2017, 12, 31, 23, 0, tzinfo=datetime.timezone(datetime.timedelta(0, 3600)) - ), - resolution="1 h", - ts_orientation=s.TimestampOrientation.left, - aggregation="sum", - ), - sources=[ - s.Source( - title="OpenEnergyPlatform Metadata Example", - description="Metadata description", - path="https://github.com/OpenEnergyPlatform", - licenses=[ - s.TermsOfUse( - lic=cc010, - instruction="You are free: To Share, To Create, To Adapt", - attribution="© Reiner Lemoine Institut", - ) - ], - ), - s.Source( - title="OpenStreetMap", - description="A collaborative project to create a free editable map of the world", - path="https://www.openstreetmap.org/", - licenses=[ - s.TermsOfUse( - lic=odbl10, - instruction="You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - attribution="© OpenStreetMap contributors", - ) - ], - ), - ], - terms_of_use=[ - s.TermsOfUse( - lic=odbl10, - instruction="You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - attribution="© Reiner Lemoine Institut © OpenStreetMap contributors", - ) - ], - contributions=[ - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2016, 6, 16), - obj="metadata", - comment="Create metadata", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2016, 11, 22), - obj="metadata", - comment="Update metadata", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2016, 11, 22), - obj="metadata", - comment="Update header and license", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2017, 3, 16), - obj="metadata", - comment="Add license to source", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2017, 3, 28), - obj="metadata", - comment="Add copyright to source and license", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2017, 5, 30), - obj="metadata", - comment="Release metadata version 1.3", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2017, 6, 26), - obj="metadata", - comment="Move referenceDate into temporal and remove array", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2018, 7, 19), - obj="metadata", - comment="Start metadata version 1.4", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2018, 7, 26), - obj="data", - comment="Rename table and files", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2018, 10, 18), - obj="metadata", - comment="Add contribution object", - ), - s.Contribution( - contributor=s.Person(name="christian-rli", email=None), - date=datetime.datetime(2018, 10, 18), - obj="metadata", - comment="Add datapackage compatibility", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2018, 11, 2), - obj="metadata", - comment="Release metadata version 1.4", - ), - s.Contribution( - contributor=s.Person(name="christian-rli", email=None), - date=datetime.datetime(2019, 2, 5), - obj="metadata", - comment="Apply template structure to example", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2019, 3, 22), - obj="metadata", - comment="Hotfix foreignKeys", - ), - s.Contribution( - contributor=s.Person(name="Ludee", email=None), - date=datetime.datetime(2019, 7, 9), - obj="metadata", - comment="Release metadata version OEP-1.3.0", - ), - ], - resources=[ - s.Resource( - name="model_draft.oep_metadata_table_example_v14", - path="http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v14", - profile="tabular-data-resource", - resource_format="PostgreSQL", - encoding="UTF-8", - schema=s.Schema( - fields=[ - s.Field( - name="id", - description="Unique identifier", - field_type="serial", - unit=None, - ), - _source_year, - s.Field( - name="value", - description="Example value", - field_type="double precision", - unit="MW", - ), - s.Field( - name="geom", - description="Geometry", - field_type="geometry(Point, 4326)", - unit=None, - ), - ], - primary_key=["id"], - foreign_keys=[ - s.ForeignKey( - references=[ - s.Reference(source=_source_year, target=_target_year) - ] - ) - ], - ), - dialect=s.Dialect(delimiter=None, decimal_separator="."), - ) - ], - review=s.Review( - path="https://github.com/OpenEnergyPlatform/data-preprocessing/wiki", - badge="platin", - ), - comment=s.MetaComment( - metadata_info="Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/organisation/wiki/metadata)", - dates="Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", - units="Use a space between numbers and units (100 m)", - languages="Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", - licenses="License name must follow the SPDX License List (https://spdx.org/licenses/)", - review="Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/wiki)", - none="If not applicable use (null)", - ), -) - -############################################### oem v151 ######################################################### -cc010_v15 = oem_v15.License( - name="CC0-1.0", - title="Creative Commons Zero v1.0 Universal", - path="https://creativecommons.org/publicdomain/zero/1.0/legalcode", -) - -odbl10_v15 = oem_v15.License( - name="ODbL-1.0", - title="Open Data Commons Open Database License 1.0", - path="https://opendatacommons.org/licenses/odbl/1.0/", -) - - -metadata_v_1_5 = oem_v15.OEPMetadata( - name="oep_metadata_table_example_v151", - title="Example title for metadata example - Version 1.5.1", - identifier="http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v151", - description="This is an metadata example for example data. There is a corresponding table on the OEP for each metadata version.", - languages=["en-GB", "en-US", "de-DE", "fr-FR"], - subject=[ - oem_v15.Subject( - name="energy", - path="https://openenergy-platform.org/ontology/oeo/OEO_00000150", - ), - oem_v15.Subject( - name="test dataset", - path="https://openenergy-platform.org/ontology/oeo/OEO_00000408", - ), - ], - keywords=["energy", "example", "template", "test"], - publication_date=datetime.datetime(2022, 2, 15, 0, 0), - context=oem_v15.Context( - homepage="https://reiner-lemoine-institut.de/lod-geoss/", - documentation="https://openenergy-platform.org/tutorials/jupyter/OEMetadata/", - source_code="https://github.com/OpenEnergyPlatform/oemetadata/tree/master", - contact="https://github.com/Ludee", - grant_number="03EI1005", - funding_agency=oem_v15.Agency( - name="Bundesministerium für Wirtschaft und Klimaschutz", - logo="https://commons.wikimedia.org/wiki/File:BMWi_Logo_2021.svg#/media/File:BMWi_Logo_2021.svg", - ), - publisher=oem_v15.Agency( - logo="https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png" - ), - ), - spatial=oem_v15.Spatial(location=None, extent="europe", resolution="100 m"), - temporal=oem_v15.Temporal( - reference_date=datetime.datetime(2016, 1, 1, 0, 0), - timeseries_collection=[ - oem_v15.Timeseries( - start=datetime.datetime( - 2017, - 1, - 1, - 0, - 0, - tzinfo=datetime.timezone(datetime.timedelta(0, 3600)), - ), - end=datetime.datetime( - 2017, - 12, - 31, - 23, - 0, - tzinfo=datetime.timezone(datetime.timedelta(0, 3600)), - ), - resolution="1 h", - ts_orientation=oem_v15.TimestampOrientation.left, - aggregation="sum", - ), - oem_v15.Timeseries( - start=datetime.datetime( - 2018, - 1, - 1, - 0, - 0, - tzinfo=datetime.timezone(datetime.timedelta(0, 3600)), - ), - end=datetime.datetime( - 2019, - 6, - 1, - 23, - 0, - tzinfo=datetime.timezone(datetime.timedelta(0, 3600)), - ), - resolution="15 min", - ts_orientation=oem_v15.TimestampOrientation.right, - aggregation="sum", - ), - ], - ), - sources=[ - oem_v15.Source( - title="OpenEnergyPlatform Metadata Example", - description="Metadata description", - path="https://github.com/OpenEnergyPlatform", - licenses=[ - oem_v15.TermsOfUse( - lic=cc010_v15, - instruction="You are free: To Share, To Create, To Adapt", - attribution="© Reiner Lemoine Institut", - ) - ], - ), - oem_v15.Source( - title="OpenStreetMap", - description="A collaborative project to create a free editable map of the world", - path="https://www.openstreetmap.org/", - licenses=[ - oem_v15.TermsOfUse( - lic=odbl10_v15, - instruction="You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - attribution="© OpenStreetMap contributors", - ) - ], - ), - ], - terms_of_use=[ - oem_v15.TermsOfUse( - lic=odbl10_v15, - instruction="You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - attribution="© Reiner Lemoine Institut © OpenStreetMap contributors", - ) - ], - contributions=[ - oem_v15.Contribution( - contributor=oem_v15.Person(name="Ludee", email=None), - date=datetime.datetime(2021, 11, 15), - obj="metadata", - comment="Release metadata version OEP-1.5.0", - ), - oem_v15.Contribution( - contributor=oem_v15.Person(name="Ludee", email=None), - date=datetime.datetime(2022, 2, 15), - obj="metadata", - comment="Release metadata version OEP-1.5.1", - ), - ], - resources=[ - oem_v15.Resource( - name="model_draft.oep_metadata_table_example_v151", - path="http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v151", - profile="tabular-data-resource", - resource_format="PostgreSQL", - encoding="UTF-8", - schema=oem_v15.Schema( - fields=[ - oem_v15.Field( - name="id", - description="Unique identifier", - field_type="serial", - unit=None, - isAbout=[oem_v15.IsAbout(name=None, path=None)], - valueReference=[ - oem_v15.ValueReference(value=None, name=None, path=None) - ], - ), - oem_v15.Field( - name="name", - description="Example name", - field_type="text", - unit=None, - isAbout=[ - oem_v15.IsAbout( - name="written name", - path="https://openenergy-platform.org/ontology/oeo/IAO_0000590", - ) - ], - valueReference=[ - oem_v15.ValueReference(value=None, name=None, path=None) - ], - ), - oem_v15.Field( - name="type", - description="Type of wind farm", - field_type="text", - unit=None, - isAbout=[ - oem_v15.IsAbout( - name="wind farm", - path="https://openenergy-platform.org/ontology/oeo/OEO_00000447", - ) - ], - valueReference=[ - oem_v15.ValueReference( - value="onshore", - name="onshore wind farm", - path="https://openenergy-platform.org/ontology/oeo/OEO_00000311", - ), - oem_v15.ValueReference( - value="offshore", - name="offshore wind farm", - path="https://openenergy-platform.org/ontology/oeo/OEO_00000308", - ), - ], - ), - oem_v15.Field( - name="year", - description="Reference year", - field_type="integer", - unit=None, - isAbout=[ - oem_v15.IsAbout( - name="year", - path="https://openenergy-platform.org/ontology/oeo/UO_0000036", - ) - ], - valueReference=[ - oem_v15.ValueReference(value=None, name=None, path=None) - ], - ), - oem_v15.Field( - name="value", - description="Example value", - field_type="double precision", - unit="MW", - isAbout=[ - oem_v15.IsAbout( - name="quantity value", - path="https://openenergy-platform.org/ontology/oeo/OEO_00000350", - ) - ], - valueReference=[ - oem_v15.ValueReference(value=None, name=None, path=None) - ], - ), - oem_v15.Field( - name="geom", - description="Geometry", - field_type="geometry(Point, 4326)", - unit=None, - isAbout=[ - oem_v15.IsAbout( - name="spatial region", - path="https://openenergy-platform.org/ontology/oeo/BFO_0000006", - ) - ], - valueReference=[ - oem_v15.ValueReference(value=None, name=None, path=None) - ], - ), - ], - primary_key=["id"], - foreign_keys=[ - oem_v15.ForeignKey( - references=[ - oem_v15.Reference(source=_source_year, target=_target_year) - ] - ) - ], - ), - dialect=oem_v15.Dialect(delimiter=None, decimal_separator="."), - ) - ], - databus_identifier="https://databus.dbpedia.org/kurzum/mastr/bnetza-mastr/01.04.00", - databus_context="https://github.com/OpenEnergyPlatform/oemetadata/blob/master/metadata/latest/context.json", - review=oem_v15.Review( - path="https://github.com/OpenEnergyPlatform/data-preprocessing/issues", - badge="Platinum", - ), - comment=oem_v15.MetaComment( - metadata_info="Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/oemetadata)", - dates="Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", - units="Use a space between numbers and units (100 m)", - languages="Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", - licenses="License name must follow the SPDX License List (https://spdx.org/licenses/)", - review="Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/blob/master/data-review/manual/review_manual.md)", - null="If not applicable use: null", - todo="If a value is not yet available, use: todo", - ), -) - - -metadata_v_1_3_minimal = s.OEPMetadata() - -metadata_v_1_4_minimal = s.OEPMetadata(identifier="id") - -metadata_v_1_5_minimal = oem_v15.OEPMetadata(identifier="id") diff --git a/tests/test_dialects/test_oep/__init__.py b/tests/test_dialects/test_oep/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_dialects/test_oep/deactivate-test_roundtrip.py b/tests/test_dialects/test_oep/deactivate-test_roundtrip.py deleted file mode 100644 index 3e1189d..0000000 --- a/tests/test_dialects/test_oep/deactivate-test_roundtrip.py +++ /dev/null @@ -1,59 +0,0 @@ -import json -from itertools import combinations, product - -from omi.dialects.oep.compiler import JSONCompiler -from omi.dialects.oep.parser import JSONParser_1_4 -from omi.dialects.rdf.compiler import RDFCompiler -from omi.dialects.rdf.parser import RDFParser - -from .test_compiler import assert_equal - - -def test_roundtrip(): - with open("tests/data/metadata_v14.json", "r") as _input_file: - input_string = _input_file.read() - assert_roundtrip(json.loads(input_string), input_string) - - -def assert_roundtrip(expected_json, input_string): - json_compiler = JSONCompiler() - json_parser = JSONParser_1_4() - rdf_compiler = RDFCompiler() - rdf_p = RDFParser() - # Step 1: Parse JSON to internal structure - internal_metadata = json_parser.parse_from_string(input_string) - # Step 2: Translate to rdf - _ = rdf_compiler.visit(internal_metadata) - # Step 3: Parse rdf string - internal_metadata2 = rdf_p.parse(rdf_compiler.graph) - # Step 4: Translate to JSON - result_json = json_compiler.visit(internal_metadata2) - # Final step: Compare - assert_equal(expected_json, result_json, disregard_ordering=True) - - -def __mask(jsn, keep): - if isinstance(jsn, dict): - return [dict(zip(keys, items)) - for keys in combinations(jsn.keys(),2) - for items in product(*(__mask(jsn[field], keep) - if field not in keep else [jsn[field]] - for field in keys)) - if keys and all(k not in jsn.keys() or k in keys for k in keep) ] - if isinstance(jsn, list): - if jsn: - return [[possibility] + jsn[1:] for possibility in __mask(jsn[0], keep)] - else: - return [[]] - else: - return [jsn] - - -def test_roundtrip_with_missing_fields(): - json_compiler = JSONCompiler() - json_parser = JSONParser_1_4() - with open("tests/data/metadata_v14.json", "r") as _input_file: - input_string = _input_file.read() - for expected_json in __mask(json.loads(input_string), keep=["id", "metaMetadata"]): - assert_equal(json_compiler.visit(json_parser.parse(expected_json)), expected_json) - diff --git a/tests/test_dialects/test_oep/test_compiler.py b/tests/test_dialects/test_oep/test_compiler.py deleted file mode 100644 index 13eaaa4..0000000 --- a/tests/test_dialects/test_oep/test_compiler.py +++ /dev/null @@ -1,84 +0,0 @@ -import json - -from omi.dialects.oep.compiler import JSONCompiler, JSONCompilerOEM15 - -from ..internal_structures import metadata_v_1_4, metadata_v_1_5 - - -def test_compiler_v1_5(): - compiler = JSONCompilerOEM15() - # omit none values as expected result also does not include None´s - compiler.OMIT_NONE_FIELDS = True - with open("tests/data/metadata_v15.json", "r", encoding="utf-8") as _input_file: - expected_result = json.load(_input_file) - result = compiler.visit(metadata_v_1_5) - assert_equal(expected_result, result) - - -def test_compiler_v1_4(): - compiler = JSONCompiler() - # omit none values as expected result also does not include None´s - compiler.OMIT_NONE_FIELDS = True - with open("tests/data/metadata_v14.json", "r", encoding="utf-8") as _input_file: - expected_result = json.load(_input_file) - result = compiler.visit(metadata_v_1_4) - assert_equal(expected_result, result) - - -def assert_equal(first, second, nulls=frozenset(["none"]), **kwargs): - if isinstance(first, dict): - assert_dict_equal(first, second, **kwargs) - elif isinstance(first, list): - assert_list_equal(first, second, **kwargs) - else: - if first in nulls and second is None: - return - assert first == second - - -def assert_dict_equal(first, second, **kwargs): - assert_list_equal(list(first.keys()), list(second.keys()), disregard_ordering=True) - for key in first.keys(): - if key not in second: - raise Exception('Key "{}" missing in {}'.format(key, second)) - assert_equal(first[key], second[key], **kwargs) - - -def assert_list_equal(first, second, disregard_ordering=False, **kwargs): - assert len(first) == len(second), "Length mismatch ({}!={}) for {} and {}".format( - len(first), len(second), first, second - ) - - if disregard_ordering: - remaining = list(second) - for expected in first: - to_remove = None - for i, result in enumerate(remaining): - try: - assert_equal( - expected, - result, - disregard_ordering=disregard_ordering, - **kwargs - ) - except: - pass - else: - to_remove = i - if to_remove is not None: - del remaining[to_remove] - else: - raise AssertionError( - "Element not found: {} in {}".format(expected, second) - ) - if remaining: - raise AssertionError( - "Elements not found: {} in {}".format(remaining, first) - ) - else: - for i, (l, r) in enumerate(zip(first, second)): - try: - assert_equal(l, r, **kwargs) - except: - print("Mismatch in element {}: {}!={}".format(i, l, r)) - raise diff --git a/tests/test_dialects/test_oep/test_parser.py b/tests/test_dialects/test_oep/test_parser.py deleted file mode 100644 index 443b995..0000000 --- a/tests/test_dialects/test_oep/test_parser.py +++ /dev/null @@ -1,67 +0,0 @@ -from email import parser -import unittest -from omi.dialects.oep.parser import JSONParser_1_3 -from omi.dialects.oep.parser import JSONParser_1_4 -from omi.dialects.oep.parser import JSONParser_1_5 - -from ..base.parser import _test_generic_parsing -from ..internal_structures import metadata_v_1_3 -from ..internal_structures import metadata_v_1_3_minimal -from ..internal_structures import metadata_v_1_4 -from ..internal_structures import metadata_v_1_4_minimal -from ..internal_structures import metadata_v_1_5 -from ..internal_structures import metadata_v_1_5_minimal -from omi.dialects.base.parser import ParserException - -from metadata.latest.schema import OEMETADATA_LATEST_SCHEMA - -import json - - -class ParserTest(unittest.TestCase): - def test_parser_v1_3(self): - parser = JSONParser_1_3() - _input_file = "tests/data/metadata_v13.json" - expected_result = metadata_v_1_3 - _test_generic_parsing(parser, _input_file, expected_result, nulls=["none", ""]) - - def test_parser_v1_4(self): - parser = JSONParser_1_4() - _input_file = "tests/data/metadata_v14.json" - expected_result = metadata_v_1_4 - _test_generic_parsing(parser, _input_file, expected_result) - - def test_parser_v1_4_Resource(self): - parser = JSONParser_1_4() - _input_file = "tests/data/metadata_v14_withoutresource.json" - expected_result = metadata_v_1_4 - with self.assertRaises(ParserException): - _test_generic_parsing(parser, _input_file, expected_result) - - def test_parser_v1_3_minimal(self): - parser = JSONParser_1_3() - _input_file = "tests/data/metadata_v13_minimal.json" - expected_result = metadata_v_1_3_minimal - _test_generic_parsing(parser, _input_file, expected_result) - - def test_parser_v1_4_minimal(self): - parser = JSONParser_1_4() - _input_file = "tests/data/metadata_v14_minimal.json" - expected_result = metadata_v_1_4_minimal - _test_generic_parsing(parser, _input_file, expected_result) - - def test_parser_v1_5(self): - parser = JSONParser_1_5() - _input_file = "tests/data/metadata_v15.json" - expected_result = metadata_v_1_5 - _test_generic_parsing(parser, _input_file, expected_result) - - def test_parser_v1_5_is_valid(self): - parser = JSONParser_1_5() - _input_file = "tests/data/metadata_v15.json" - - with open(_input_file, "r", encoding="utf-8") as f: - jsn = json.load(f) - - # file = parser.parse_from_file(_input_file) - parser.validate(jsn) diff --git a/tests/test_dialects/test_oep/test_regression/__init__.py b/tests/test_dialects/test_oep/test_regression/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_dialects/test_oep/test_regression/test_issue86_datetime.py b/tests/test_dialects/test_oep/test_regression/test_issue86_datetime.py deleted file mode 100644 index f701b2e..0000000 --- a/tests/test_dialects/test_oep/test_regression/test_issue86_datetime.py +++ /dev/null @@ -1,105 +0,0 @@ -import json -from unittest import SkipTest -from unittest import TestCase - -from omi.dialects.oep import OEP_V_1_3_Dialect -from omi.dialects.oep import OEP_V_1_4_Dialect -from omi.dialects.oep import OEP_V_1_5_Dialect -from omi.dialects.oep.compiler import compile_date_or_none -from omi.dialects.oep.parser import ParserException -from omi.dialects.oep.parser import parse_date_or_none - -# in the metadata, for some values we return the date,not the full datetime - - -class TestIssue86Datetime(TestCase): - - BAD_VALUES = [True, {}, "", "not a date", "200", "2020-30-40", "2020-01-01 WTF"] - OK_VALUES = { - None: None, - 2020: 2020, - "2020": "2020", - "2020-12": "2020-12", - "2020-12-02": "2020-12-02", - "2020-12-2": "2020-12-02", - "2020-10-01T10:12:13": "2020-10-01T10:12:13", - "2020-10-01 10:12": "2020-10-01T10:12:00", - "2020-10-01T10:12:13+0200": "2020-10-01T10:12:13+02:00", - } - - def roundtrip_value(self, value): - value = parse_date_or_none(value) - value = compile_date_or_none(value) - return value - - def test_datetime_roundtrip(self): - for bad_value in self.BAD_VALUES: - self.assertRaises(ParserException, self.roundtrip_value, bad_value) - for ok_value, exp_value in self.OK_VALUES.items(): - self.assertEqual(self.roundtrip_value(ok_value), exp_value) - - -class TestIssue86Metadata(TestIssue86Datetime): - """test roundtrip in OEP_V_1_5_Dialect""" - - dialect = None - OK_VALUES = { - None: (None, None), - 2020: (2020, 2020), - "2020": ("2020", "2020"), - "2020-12": ("2020-12", "2020-12"), - "2020-12-02": ("2020-12-02", "2020-12-02"), - "2020-12-2": ("2020-12-02", "2020-12-02"), - "2020-10-01T10:12:13": ("2020-10-01T10:12:13", "2020-10-01"), - "2020-10-01 10:12": ("2020-10-01T10:12:00", "2020-10-01"), - "2020-10-01T10:12:13+0200": ("2020-10-01T10:12:13+02:00", "2020-10-01"), - } - - def test_datetime_roundtrip(self): - # only actually run tests in subclasses - if self.dialect: - return super().test_datetime_roundtrip() - - def roundtrip_value(self, value): - metadata_in = {"id": "test"} - self.set_date_datetime_values(metadata_in, value) - metadata_str = json.dumps(metadata_in) - metadata_obj = self.dialect.parse(metadata_str) - metadata_out = self.dialect.compile(metadata_obj) - return self.get_date_datetime_values(metadata_out) - - def set_date_datetime_values(self, metadata, value): - raise NotImplementedError() - - def get_date_datetime_values(self, metadata): - raise NotImplementedError() - - -class TestIssue86Datetime_V_1_5(TestIssue86Metadata): - """test roundtrip in OEP_V_1_5_Dialect""" - - dialect = OEP_V_1_5_Dialect() - - def set_date_datetime_values(self, metadata, value): - metadata["publicationDate"] = value - metadata["temporal"] = {"timeseries": [{"start": value}]} - - def get_date_datetime_values(self, metadata): - v_datetime = metadata["temporal"]["timeseries"][0].get("start") - v_date = metadata.get("publicationDate") - return (v_datetime, v_date) - - -class TestIssue86Datetime_V_1_4(TestIssue86Metadata): - """test roundtrip in OEP_V_1_4_Dialect""" - - dialect = OEP_V_1_4_Dialect() - - def set_date_datetime_values(self, metadata, value): - metadata["publicationDate"] = value - metadata["temporal"] = {"timeseries": {"start": value}} - - def get_date_datetime_values(self, metadata): - v_datetime = metadata["temporal"]["timeseries"].get("start") - v_date = metadata.get("publicationDate") - return (v_datetime, v_date) diff --git a/tests/test_dialects/test_oep/test_translation.py b/tests/test_dialects/test_oep/test_translation.py deleted file mode 100644 index 1448ebd..0000000 --- a/tests/test_dialects/test_oep/test_translation.py +++ /dev/null @@ -1,30 +0,0 @@ -import json -from collections import OrderedDict - -from omi.dialects.oep.compiler import JSONCompiler -from omi.dialects.oep.parser import JSONParser_1_3 - -from .test_compiler import assert_equal - - -def test_translation_1_3_to_1_4(): - parser = JSONParser_1_3() - compiler = JSONCompiler() - # omit none values as expected result also does not include None´s - compiler.OMIT_NONE_FIELDS = True - with open("tests/data/metadata_v13_minimal.json", "r") as _input_file: - input_string = _input_file.read() - # Step 1: Parse JSON to internal structure - internal_metadata = parser.parse_from_string(input_string) - # Step 2: Translate to version 1_4 - result_json = compiler.visit(internal_metadata) - - expected_json = OrderedDict(json.loads('''{ - "metaMetadata": { - "metadataVersion": "OEP-1.4.0", - "metadataLicense": { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/"}}}''')) - - assert_equal(expected_json, result_json) diff --git a/tests/test_dialects/test_rdf/__init__.py b/tests/test_dialects/test_rdf/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_dialects/test_rdf/deactivate-test_compiler.py b/tests/test_dialects/test_rdf/deactivate-test_compiler.py deleted file mode 100644 index fd262f5..0000000 --- a/tests/test_dialects/test_rdf/deactivate-test_compiler.py +++ /dev/null @@ -1,26 +0,0 @@ -from rdflib import BNode -from rdflib import Graph -from rdflib.compare import _squashed_graphs_triples -from rdflib.compare import graph_diff -from rdflib.compare import isomorphic -from rdflib.compare import similar -from rdflib.compare import to_isomorphic - -from omi.dialects.rdf.compiler import RDFCompiler - -from ..internal_structures import metadata_v_1_4 -from omi.dialects.rdf.namespace import OEO -from rdflib.namespace import DCTERMS - - -def test_compiler_v1_4(): - compiler = RDFCompiler() - with open("tests/data/metadata_v14.ttl", "r", encoding="utf-8") as _input_file: - expected_graph = Graph() - expected_graph.parse(data=_input_file.read(), format="ttl") - _ = compiler.visit(metadata_v_1_4) - expected = to_isomorphic(expected_graph) - got = to_isomorphic(compiler.graph) - for (t1, t2) in _squashed_graphs_triples(expected, got): - assert t1 == t2 - assert isomorphic(expected, got) diff --git a/tests/test_dialects/test_rdf/deactivate-test_parser.py b/tests/test_dialects/test_rdf/deactivate-test_parser.py deleted file mode 100644 index 67cc489..0000000 --- a/tests/test_dialects/test_rdf/deactivate-test_parser.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- - -from omi.dialects.rdf.parser import RDFParser - -from ..base.parser import _test_generic_parsing -from ..internal_structures import metadata_v_1_4 - - -def test_parser_rdf(): - parser = RDFParser() - _input_file = "tests/data/metadata_v14.ttl" - expected_result = metadata_v_1_4 - _test_generic_parsing(parser, _input_file, expected_result) diff --git a/tests/test_dialects/test_rdf/deactivate-test_roundtrip.py b/tests/test_dialects/test_rdf/deactivate-test_roundtrip.py deleted file mode 100644 index 699e625..0000000 --- a/tests/test_dialects/test_rdf/deactivate-test_roundtrip.py +++ /dev/null @@ -1,33 +0,0 @@ -from rdflib import Graph -from rdflib.compare import _squashed_graphs_triples -from rdflib.compare import isomorphic - -from omi.dialects.oep.compiler import JSONCompiler -from omi.dialects.oep.parser import JSONParser_1_4 -from omi.dialects.oep.renderer import JSONRenderer -from omi.dialects.rdf.compiler import RDFCompiler -from omi.dialects.rdf.parser import RDFParser - - -def test_roundtrip(): - json_compiler = JSONCompiler() - json_parser = JSONParser_1_4() - json_renderer = JSONRenderer() - rdf_compiler = RDFCompiler() - rdf_p = RDFParser() - with open("tests/data/metadata_v14.ttl", "r") as _input_file: - input_string = _input_file.read() - expected_graph = Graph() - expected_graph.parse(data=input_string, format="ttl") - # Step 1: Parse Turtle to internal structure - internal_metadata = rdf_p.parse_from_string(input_string) - # Step 2: Translate to json string - json_metadata = json_renderer.render(json_compiler.visit(internal_metadata)) - # Step 3: Parse json string - internal_metadata2 = json_parser.parse_from_string(json_metadata) - # Step 4: Translate to Turtle - _ = rdf_compiler.visit(internal_metadata2) - # Final step: Compare - for (t1, t2) in _squashed_graphs_triples(expected_graph, rdf_compiler.graph): - assert t1 == t2 - assert isomorphic(expected_graph, rdf_compiler.graph) From cb7ed2879de51d3d05ef84e2250437d3bdc1fcbd Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Tue, 28 May 2024 17:26:18 +0200 Subject: [PATCH 03/63] Add poetry setup --- poetry.lock | 640 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 78 ++++++ 2 files changed, 718 insertions(+) create mode 100644 poetry.lock create mode 100644 pyproject.toml diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..75485f9 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,640 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.14.0" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "identify" +version = "2.5.36" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isodate" +version = "0.6.1" +description = "An ISO 8601 date/time/duration parser and formatter" +optional = false +python-versions = "*" +files = [ + {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, + {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "jsonschema" +version = "4.22.0" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.22.0-py3-none-any.whl", hash = "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802"}, + {file = "jsonschema-4.22.0.tar.gz", hash = "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "oemetadata" +version = "1.6.0" +description = "Open Energy Platform (OEP) - metadata schemas, examples and templates package" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oemetadata-1.6.0-py3-none-any.whl", hash = "sha256:61103a7333ed2a3482a4228fd13d74b0f08f5240e7d48a70c26e8a1901061ece"}, + {file = "oemetadata-1.6.0.tar.gz", hash = "sha256:8f57f10cded2cdeb20c9316477176d68808ff4ca74659cdd8a93cca0695f89da"}, +] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "3.7.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"}, + {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "pyparsing" +version = "3.1.2" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyproject-api" +version = "1.6.1" +description = "API to interact with the python pyproject.toml based projects" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"}, + {file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"}, +] + +[package.dependencies] +packaging = ">=23.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"] + +[[package]] +name = "pytest" +version = "8.2.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "rdflib" +version = "7.0.0" +description = "RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information." +optional = false +python-versions = ">=3.8.1,<4.0.0" +files = [ + {file = "rdflib-7.0.0-py3-none-any.whl", hash = "sha256:0438920912a642c866a513de6fe8a0001bd86ef975057d6962c79ce4771687cd"}, + {file = "rdflib-7.0.0.tar.gz", hash = "sha256:9995eb8569428059b8c1affd26b25eac510d64f5043d9ce8c84e0d0036e995ae"}, +] + +[package.dependencies] +isodate = ">=0.6.0,<0.7.0" +pyparsing = ">=2.1.0,<4" + +[package.extras] +berkeleydb = ["berkeleydb (>=18.1.0,<19.0.0)"] +html = ["html5lib (>=1.0,<2.0)"] +lxml = ["lxml (>=4.3.0,<5.0.0)"] +networkx = ["networkx (>=2.0.0,<3.0.0)"] + +[[package]] +name = "referencing" +version = "0.35.1" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "rpds-py" +version = "0.18.1" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53"}, + {file = "rpds_py-0.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1"}, + {file = "rpds_py-0.18.1-cp310-none-win32.whl", hash = "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333"}, + {file = "rpds_py-0.18.1-cp310-none-win_amd64.whl", hash = "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88"}, + {file = "rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb"}, + {file = "rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a"}, + {file = "rpds_py-0.18.1-cp312-none-win32.whl", hash = "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6"}, + {file = "rpds_py-0.18.1-cp312-none-win_amd64.whl", hash = "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc"}, + {file = "rpds_py-0.18.1-cp38-none-win32.whl", hash = "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9"}, + {file = "rpds_py-0.18.1-cp38-none-win_amd64.whl", hash = "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26"}, + {file = "rpds_py-0.18.1-cp39-none-win32.whl", hash = "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360"}, + {file = "rpds_py-0.18.1-cp39-none-win_amd64.whl", hash = "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e"}, + {file = "rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f"}, +] + +[[package]] +name = "setuptools" +version = "70.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tox" +version = "4.15.0" +description = "tox is a generic virtualenv management and test command line tool" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tox-4.15.0-py3-none-any.whl", hash = "sha256:300055f335d855b2ab1b12c5802de7f62a36d4fd53f30bd2835f6a201dda46ea"}, + {file = "tox-4.15.0.tar.gz", hash = "sha256:7a0beeef166fbe566f54f795b4906c31b428eddafc0102ac00d20998dd1933f6"}, +] + +[package.dependencies] +cachetools = ">=5.3.2" +chardet = ">=5.2" +colorama = ">=0.4.6" +filelock = ">=3.13.1" +packaging = ">=23.2" +platformdirs = ">=4.1" +pluggy = ">=1.3" +pyproject-api = ">=1.6.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +virtualenv = ">=20.25" + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"] +testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=8.0.2)", "distlib (>=0.3.8)", "flaky (>=3.7)", "hatch-vcs (>=0.4)", "hatchling (>=1.21)", "psutil (>=5.9.7)", "pytest (>=7.4.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-xdist (>=3.5)", "re-assert (>=1.1)", "time-machine (>=2.13)", "wheel (>=0.42)"] + +[[package]] +name = "virtualenv" +version = "20.26.2" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.9,<3.13" +content-hash = "a1f0e5e9b44407ce3b4fcb34ea8cf921990898cd54967fd3b460d67b521935bb" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..088cb43 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,78 @@ +[tool.poetry] +name = "omi" +version = "0.2.1" +description = "A library to process and translate open energy metadata." +authors = [ + "Hendrik Huyskens ", + "Jonas Huber ", + "Martin Glauer " +] +readme = "README.rst" +license = "AGPL-3.0" + +[tool.poetry.dependencies] +python = ">=3.9,<3.13" +click = "^8.1.7" +rdfLib = "^7.0.0" +python-dateutil = "^2.9.0" +jsonschema = "^4.22.0" +oemetadata = ">=1.5.2" + +[tool.poetry.group.dev.dependencies] +tox = "^4.15.0" +pytest = "^8.2.1" +pre-commit = "^3.7.1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.black] +line-length = 120 + +[tool.isort] +profile = "black" + +[tool.ruff] +line-length = 120 +select = ["ALL"] +exclude = [ + "manage.py", + "digiplan/utils/ogr_layer_mapping.py", + "config/wsgi.py", + "digiplan/contrib/*", + "merge_local_dotenvs_in_dotenv.py", + "digiplan/utils/context_processors.py" +] +ignore = [ + "I001", # Import block is un-sorted or un-formatted (done by isort) + "D203", # 1 blank line required before class docstring + "D212", # Multi-line docstring summary should start at the first line pydocstyle + "ANN101", # Missing type annotation for `self` in method + "ANN102", # Missing type annotation for `cls` in classmethod + "ANN003", # Missing type annotation for `**kwargs` + "EM102", # Exception must not use an f-string literal, assign to variable first + "TRY003", # Avoid specifying long messages outside the exception class + "S101", # Use of `assert` detected + "UP007", # Use `X | Y` for type annotations + "B905", # `zip()` without an explicit `strict=` parameter + "FIX001", # Line contains FIXME + "FIX002", # Line contains TODO + "RET504", # Unnecessary variable assignment before `return` statement + "G004", # Logging statement uses f-string + "PD011", # Use `.to_numpy()` instead of `.values` (does not work out of the box) + "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` + "UP038", # (non-pep604-isinstance) +] +fix = true +show-fixes = true +unfixable = ["UP007", "I001"] + +[tool.ruff.per-file-ignores] +"tests/*" = [ + "PLR2004", # Magic value used in comparison + "ANN201", # Missing return type annotation for public function +] +"*/__init__.py" = [ + "D104", # Missing docstring in public package +] From f50970f6418d0420bca9608856c4ecaf4d35223a Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Tue, 28 May 2024 17:27:05 +0200 Subject: [PATCH 04/63] Remove legacy bump config --- .bumpversion.cfg | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .bumpversion.cfg diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 526fcd0..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,20 +0,0 @@ -[bumpversion] -current_version = 0.2.1 -commit = True -tag = True - -[bumpversion:file:setup.py] -search = version="{current_version}" -replace = version="{new_version}" - -[bumpversion:file:README.rst] -search = v{current_version}. -replace = v{new_version}. - -[bumpversion:file:docs/conf.py] -search = version = release = "{current_version}" -replace = version = release = "{new_version}" - -[bumpversion:file:src/omi/__init__.py] -search = __version__ = "{current_version}" -replace = __version__ = "{new_version}" From fdf0ddbe2bb18e73e85348110b2ea6d87b948567 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Tue, 28 May 2024 17:27:45 +0200 Subject: [PATCH 05/63] Fix bump toml --- .bumpversion.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index cf3944a..978825e 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -32,7 +32,7 @@ search = "Unreleased" filename = "README.rst" [[tool.bumpversion.files]] -filename = "docs/conf.py +filename = "docs/conf.py" [[tool.bumpversion.files]] filename = "omi/__init__.py" From 5f6976d4dcf162f184248f194fc4483c5d5a6760 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Tue, 28 May 2024 17:29:15 +0200 Subject: [PATCH 06/63] Change CHANGELOG.rst header for current version --- CHANGELOG.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 07ca0ae..c8ec3a7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,9 +2,9 @@ Changelog ========= -current (2024-XX-XX) +current -------------------- -* +* 0.2.1 (2024-01-26) -------------------- @@ -30,7 +30,7 @@ current (2024-XX-XX) 0.0.9 (2022-10-31) -------------------- -* Fix bug that is raised if the input oemetadata does not contain the key _comment (PR#74) +* Fix bug that is raised if the input oemetadata does not contain the key _comment (PR#74) 0.0.8 (2022-10-20) -------------------- From 2235a53118ee09ac5ee7402549f75e63b5e3e716 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Wed, 29 May 2024 11:00:28 +0200 Subject: [PATCH 07/63] Init validation module --- .gitignore | 3 +- src/omi/validation.py | 76 ++++++++++++++++++++++++++++++++++++++++ tests/__init__.py | 0 tests/test_validation.py | 20 +++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/omi/validation.py create mode 100644 tests/__init__.py create mode 100644 tests/test_validation.py diff --git a/.gitignore b/.gitignore index 16a8eda..28e9c6c 100644 --- a/.gitignore +++ b/.gitignore @@ -80,4 +80,5 @@ docs/_build # manual testing scripts -/local_test \ No newline at end of file +/local_test +/.venv/ diff --git a/src/omi/validation.py b/src/omi/validation.py new file mode 100644 index 0000000..765140c --- /dev/null +++ b/src/omi/validation.py @@ -0,0 +1,76 @@ +"""Validation module for OMI.""" + +from __future__ import annotations + +import json +import pathlib +from dataclasses import dataclass + +from metadata import v152, v160 + +# Order matters! First entry equals latest version of metadata format +METADATA_FORMATS = {"OEP": ["OEP-1.6.0", "OEP-1.5.2"], "INSPIRE": []} +METADATA_VERSIONS = {version: md_format for md_format, versions in METADATA_FORMATS.items() for version in versions} + + +@dataclass +class MetadataSchema: + """Metadata schema class, holding JSON schema and (optional) template and example for given schema.""" + + schema: dict + template: dict | None = None + example: dict | None = None + + +def get_metadata_schema(metadata_version: str) -> MetadataSchema: + """ + Return metadata schema for given metadata version. + + Metadata versions are defined in METADATA_FORMATS. + Fetching metadata schema depends on metadata format. + + Parameters + ---------- + metadata_version: str + Metadata version + + Raises + ------ + ValueError + if metadata version is not in METADATA_FORMATS + + Returns + ------- + MetadataSchema + Metadata schema holding (at least) JSON schema for given metadata version. + """ + if metadata_version not in METADATA_VERSIONS: + raise ValueError(f"Metadata format for metadata version {metadata_version} could not be found.") + metadata_format = METADATA_VERSIONS[metadata_version] + + metadata_schema_functions = {"OEP": get_metadata_schema_for_oep} + return metadata_schema_functions[metadata_format](metadata_version) + + +def get_metadata_schema_for_oep(metadata_version: str) -> MetadataSchema: + """ + Return OEP metadata schema for given metadata version. + + Parameters + ---------- + metadata_version: str + Metadata version + + Returns + ------- + MetadataSchema + Metadata schema for given metadata version including template and example. + """ + metadata_modules = {"OEP-1.5.2": v152, "OEP-1.6.0": v160} + metadata_module = metadata_modules[metadata_version] + module_path = pathlib.Path(metadata_module.__file__).parent + schema = {} + for item in ("schema", "template", "example"): + with (module_path / f"{item}.json").open("r") as f: + schema[item] = json.loads(f.read()) + return MetadataSchema(**schema) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_validation.py b/tests/test_validation.py new file mode 100644 index 0000000..6f7301f --- /dev/null +++ b/tests/test_validation.py @@ -0,0 +1,20 @@ +"""Tests for validation module of OMI.""" + +import pytest + +from omi import validation + + +def test_metadata_schema_for_oep_version(): + """Test schema, template and example for OEP metadata.""" + version = "OEP-1.5.2" + schema = validation.get_metadata_schema(version) + assert schema.schema["description"] == "Open Energy Plaftorm (OEP) metadata schema v1.5.2" + assert schema.template["name"] is None + assert schema.example["name"] == "oep_metadata_table_example_v152" + + +def test_metadata_schema_not_found(): + """Test failing schema for invalid metadata version.""" + with pytest.raises(ValueError, match="Metadata format for metadata version OEP-1.5.0 could not be found."): + validation.get_metadata_schema("OEP-1.5.0") From dab10d70cb79a6e058f388c8a75667578ef2bedc Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Wed, 29 May 2024 12:22:52 +0200 Subject: [PATCH 08/63] Implement validation of OEP metadata and add tests --- src/omi/validation.py | 53 ++- .../invalid_oep_metadata_example.json | 413 ++++++++++++++++++ .../unsupported_oep_metadata_example.json | 399 +++++++++++++++++ tests/test_validation.py | 41 +- 4 files changed, 902 insertions(+), 4 deletions(-) create mode 100644 tests/test_data/invalid_oep_metadata_example.json create mode 100644 tests/test_data/unsupported_oep_metadata_example.json diff --git a/src/omi/validation.py b/src/omi/validation.py index 765140c..1a51b47 100644 --- a/src/omi/validation.py +++ b/src/omi/validation.py @@ -6,6 +6,7 @@ import pathlib from dataclasses import dataclass +import jsonschema from metadata import v152, v160 # Order matters! First entry equals latest version of metadata format @@ -13,6 +14,10 @@ METADATA_VERSIONS = {version: md_format for md_format, versions in METADATA_FORMATS.items() for version in versions} +class ValidationError(Exception): + """Exception raised when a validation fails.""" + + @dataclass class MetadataSchema: """Metadata schema class, holding JSON schema and (optional) template and example for given schema.""" @@ -22,6 +27,48 @@ class MetadataSchema: example: dict | None = None +def validate_metadata(metadata: dict) -> None: + """ + Validate metadata against related metadata schema. + + Parameters + ---------- + metadata: dict + Metadata + + Returns + ------- + None + if metadata schema is valid. Otherwise it raises an exception. + """ + metadata_version = __extract_metadata_version(metadata) + metadata_schema = get_metadata_schema(metadata_version) + jsonschema.validate(metadata, metadata_schema.schema) + + +def __extract_metadata_version(metadata: dict) -> str: + """ + Extract metadata version from metadata. + + Parameters + ---------- + metadata: dict + Metadata + + Returns + ------- + str + Metadata version as string + """ + # For OEP metadata + try: + return metadata["metaMetadata"]["metadataVersion"] + except KeyError: + pass + msg = "Could not extract metadata version from metadata." + raise ValidationError(msg) + + def get_metadata_schema(metadata_version: str) -> MetadataSchema: """ Return metadata schema for given metadata version. @@ -45,14 +92,14 @@ def get_metadata_schema(metadata_version: str) -> MetadataSchema: Metadata schema holding (at least) JSON schema for given metadata version. """ if metadata_version not in METADATA_VERSIONS: - raise ValueError(f"Metadata format for metadata version {metadata_version} could not be found.") + raise ValidationError(f"Metadata format for metadata version {metadata_version} could not be found.") metadata_format = METADATA_VERSIONS[metadata_version] - metadata_schema_functions = {"OEP": get_metadata_schema_for_oep} + metadata_schema_functions = {"OEP": __get_metadata_schema_for_oep} return metadata_schema_functions[metadata_format](metadata_version) -def get_metadata_schema_for_oep(metadata_version: str) -> MetadataSchema: +def __get_metadata_schema_for_oep(metadata_version: str) -> MetadataSchema: """ Return OEP metadata schema for given metadata version. diff --git a/tests/test_data/invalid_oep_metadata_example.json b/tests/test_data/invalid_oep_metadata_example.json new file mode 100644 index 0000000..b307d98 --- /dev/null +++ b/tests/test_data/invalid_oep_metadata_example.json @@ -0,0 +1,413 @@ +{ + "unsupported_keyword": "invalid", + "title": "Example title for metadata example - Version 1.6.0", + "id": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v160", + "description": "This is an metadata example for example data. There is a corresponding table on the OEP for each metadata version.", + "language": [ + "en-GB", + "en-US", + "de-DE", + "fr-FR" + ], + "subject": [ + { + "name": "energy", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000150" + }, + { + "name": "test dataset", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000408" + } + ], + "keywords": [ + "energy", + "example", + "template", + "test" + ], + "publicationDate": "2022-02-15", + "context": { + "homepage": "https://reiner-lemoine-institut.de/lod-geoss/", + "documentation": "https://openenergy-platform.org/tutorials/jupyter/OEMetadata/", + "sourceCode": "https://github.com/OpenEnergyPlatform/oemetadata/tree/master", + "contact": "https://github.com/Ludee", + "grantNo": "03EI1005", + "fundingAgency": "Bundesministerium für Wirtschaft und Klimaschutz", + "fundingAgencyLogo": "https://commons.wikimedia.org/wiki/File:BMWi_Logo_2021.svg#/media/File:BMWi_Logo_2021.svg", + "publisherLogo": "https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png" + }, + "spatial": { + "location": null, + "extent": "europe", + "resolution": "100 m" + }, + "temporal": { + "referenceDate": "2016-01-01", + "timeseries": [ + { + "start": "2017-01-01T00:00+01", + "end": "2017-12-31T23:00+01", + "resolution": "1 h", + "alignment": "left", + "aggregationType": "sum" + }, + { + "start": "2018-01-01T00:00+01", + "end": "2019-06-01T23:00+01", + "resolution": "15 min", + "alignment": "right", + "aggregationType": "sum" + } + ] + }, + "sources": [ + { + "title": "OpenEnergyPlatform Metadata Example", + "description": "Metadata description", + "path": "https://github.com/OpenEnergyPlatform", + "licenses": [ + { + "name": "CC0-1.0", + "title": "Creative Commons Zero v1.0 Universal", + "path": "https://creativecommons.org/publicdomain/zero/1.0/legalcode", + "instruction": "You are free: To Share, To Create, To Adapt", + "attribution": "© Reiner Lemoine Institut" + } + ] + }, + { + "title": "OpenStreetMap", + "description": "A collaborative project to create a free editable map of the world", + "path": "https://www.openstreetmap.org/", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0", + "path": "https://opendatacommons.org/licenses/odbl/1.0/index.html", + "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", + "attribution": "© OpenStreetMap contributors" + } + ] + } + ], + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0", + "path": "https://opendatacommons.org/licenses/odbl/1.0/", + "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", + "attribution": "© Reiner Lemoine Institut © OpenStreetMap contributors" + } + ], + "contributors": [ + { + "title": "Ludee", + "email": null, + "date": "2016-06-16", + "object": "metadata", + "comment": "Create metadata" + }, + { + "title": "Ludee", + "email": null, + "date": "2016-11-22", + "object": "metadata", + "comment": "Update metadata" + }, + { + "title": "Ludee", + "email": null, + "date": "2016-11-22", + "object": "metadata", + "comment": "Update header and license" + }, + { + "title": "Ludee", + "email": null, + "date": "2017-03-16", + "object": "metadata", + "comment": "Add license to source" + }, + { + "title": "Ludee", + "email": null, + "date": "2017-03-28", + "object": "metadata", + "comment": "Add copyright to source and license" + }, + { + "title": "Ludee", + "email": null, + "date": "2017-05-30", + "object": "metadata", + "comment": "Release metadata version 1.3" + }, + { + "title": "Ludee", + "email": null, + "date": "2017-06-26", + "object": "metadata", + "comment": "Move referenceDate into temporal and remove array" + }, + { + "title": "Ludee", + "email": null, + "date": "2018-07-19", + "object": "metadata", + "comment": "Start metadata version 1.4" + }, + { + "title": "Ludee", + "email": null, + "date": "2018-07-26", + "object": "data", + "comment": "Rename table and files" + }, + { + "title": "Ludee", + "email": null, + "date": "2018-10-18", + "object": "metadata", + "comment": "Add contribution object" + }, + { + "title": "christian-rli", + "email": null, + "date": "2018-10-18", + "object": "metadata", + "comment": "Add datapackage compatibility" + }, + { + "title": "Ludee", + "email": null, + "date": "2018-11-02", + "object": "metadata", + "comment": "Release metadata version 1.4" + }, + { + "title": "christian-rli", + "email": null, + "date": "2019-02-05", + "object": "metadata", + "comment": "Apply template structure to example" + }, + { + "title": "Ludee", + "email": null, + "date": "2019-03-22", + "object": "metadata", + "comment": "Hotfix foreignKeys" + }, + { + "title": "Ludee", + "email": null, + "date": "2019-07-09", + "object": "metadata", + "comment": "Release metadata version OEP-1.3.0" + }, + { + "title": "Ludee", + "email": null, + "date": "2021-11-15", + "object": "metadata", + "comment": "Release metadata version OEP-1.5.0" + }, + { + "title": "Ludee", + "email": null, + "date": "2022-02-15", + "object": "metadata", + "comment": "Release metadata version OEP-1.5.1" + }, + { + "title": "jh-RLI", + "email": null, + "date": "2022-11-18", + "object": "metadata", + "comment": "Release metadata version OEP-1.5.2" + }, + { + "title": "christian-rli", + "email": null, + "date": "2023-05-30", + "object": "metadata", + "comment": "Release metadata version OEP-1.6.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "model_draft.oep_metadata_table_example_v160", + "path": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v160", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial", + "unit": null, + "isAbout": [ + { + "name": null, + "path": null + } + ], + "valueReference": [ + { + "value": null, + "name": null, + "path": null + } + ] + }, + { + "name": "name", + "description": "Example name", + "type": "text", + "unit": null, + "isAbout": [ + { + "name": "written name", + "path": "https://openenergy-platform.org/ontology/oeo/IAO_0000590" + } + ], + "valueReference": [ + { + "value": null, + "name": null, + "path": null + } + ] + }, + { + "name": "type", + "description": "Type of wind farm", + "type": "text", + "unit": null, + "isAbout": [ + { + "name": "wind farm", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000447" + } + ], + "valueReference": [ + { + "value": "onshore ", + "name": "onshore wind farm", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000311" + }, + { + "value": "offshore ", + "name": "offshore wind farm", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000308" + } + ] + }, + { + "name": "year", + "description": "Reference year", + "type": "integer", + "unit": null, + "isAbout": [ + { + "name": "year", + "path": "https://openenergy-platform.org/ontology/oeo/UO_0000036" + } + ], + "valueReference": [ + { + "value": null, + "name": null, + "path": null + } + ] + }, + { + "name": "value", + "description": "Example value", + "type": "double precision", + "unit": "MW", + "isAbout": [ + { + "name": "quantity value", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000350" + } + ], + "valueReference": [ + { + "value": null, + "name": null, + "path": null + } + ] + }, + { + "name": "geom", + "description": "Geometry", + "type": "geometry(Point, 4326)", + "unit": null, + "isAbout": [ + { + "name": "spatial region", + "path": "https://openenergy-platform.org/ontology/oeo/BFO_0000006" + } + ], + "valueReference": [ + { + "value": null, + "name": null, + "path": null + } + ] + } + ], + "primaryKey": [ + "id" + ], + "foreignKeys": [ + { + "fields": [ + "year" + ], + "reference": { + "resource": "schema.table", + "fields": [ + "year" + ] + } + } + ] + }, + "dialect": { + "delimiter": null, + "decimalSeparator": "." + } + } + ], + "@id": "https://databus.dbpedia.org/kurzum/mastr/bnetza-mastr/01.04.00", + "@context": "https://raw.githubusercontent.com/OpenEnergyPlatform/oemetadata/develop/metadata/latest/context.json", + "review": { + "path": "https://github.com/OpenEnergyPlatform/data-preprocessing/issues", + "badge": "Platinum" + }, + "metaMetadata": { + "metadataVersion": "OEP-1.6.0", + "metadataLicense": { + "name": "CC0-1.0", + "title": "Creative Commons Zero v1.0 Universal", + "path": "https://creativecommons.org/publicdomain/zero/1.0/" + } + }, + "_comment": { + "metadata": "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/oemetadata)", + "dates": "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", + "units": "Use a space between numbers and units (100 m)", + "languages": "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", + "licenses": "License name must follow the SPDX License List (https://spdx.org/licenses/)", + "review": "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/blob/master/data-review/manual/review_manual.md)", + "null": "If not applicable use: null", + "todo": "If a value is not yet available, use: todo" + } +} diff --git a/tests/test_data/unsupported_oep_metadata_example.json b/tests/test_data/unsupported_oep_metadata_example.json new file mode 100644 index 0000000..e1731a1 --- /dev/null +++ b/tests/test_data/unsupported_oep_metadata_example.json @@ -0,0 +1,399 @@ +{ + "name": "oep_metadata_table_example_v151", + "title": "Example title for metadata example - Version 1.5.1", + "id": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v151", + "description": "This is an metadata example for example data. There is a corresponding table on the OEP for each metadata version.", + "language": [ + "en-GB", + "en-US", + "de-DE", + "fr-FR" + ], + "subject": [ + { + "name": "energy", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000150" + }, + { + "name": "test dataset", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000408" + } + ], + "keywords": [ + "energy", + "example", + "template", + "test" + ], + "publicationDate": "2022-02-15", + "context": { + "homepage": "https://reiner-lemoine-institut.de/lod-geoss/", + "documentation": "https://openenergy-platform.org/tutorials/jupyter/OEMetadata/", + "sourceCode": "https://github.com/OpenEnergyPlatform/oemetadata/tree/master", + "contact": "https://github.com/Ludee", + "grantNo": "03EI1005", + "fundingAgency": "Bundesministerium für Wirtschaft und Klimaschutz", + "fundingAgencyLogo": "https://commons.wikimedia.org/wiki/File:BMWi_Logo_2021.svg#/media/File:BMWi_Logo_2021.svg", + "publisherLogo": "https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png" + }, + "spatial": { + "location": null, + "extent": "europe", + "resolution": "100 m" + }, + "temporal": { + "referenceDate": "2016-01-01", + "timeseries": [ + { + "start": "2017-01-01T00:00+01", + "end": "2017-12-31T23:00+01", + "resolution": "1 h", + "alignment": "left", + "aggregationType": "sum" + }, + { + "start": "2018-01-01T00:00+01", + "end": "2019-06-01T23:00+01", + "resolution": "15 min", + "alignment": "right", + "aggregationType": "sum" + } + ] + }, + "sources": [ + { + "title": "OpenEnergyPlatform Metadata Example", + "description": "Metadata description", + "path": "https://github.com/OpenEnergyPlatform", + "licenses": [ + { + "name": "CC0-1.0", + "title": "Creative Commons Zero v1.0 Universal", + "path": "https://creativecommons.org/publicdomain/zero/1.0/legalcode", + "instruction": "You are free: To Share, To Create, To Adapt", + "attribution": "© Reiner Lemoine Institut" + } + ] + }, + { + "title": "OpenStreetMap", + "description": "A collaborative project to create a free editable map of the world", + "path": "https://www.openstreetmap.org/", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0", + "path": "https://opendatacommons.org/licenses/odbl/1.0/index.html", + "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", + "attribution": "© OpenStreetMap contributors" + } + ] + } + ], + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0", + "path": "https://opendatacommons.org/licenses/odbl/1.0/", + "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", + "attribution": "© Reiner Lemoine Institut © OpenStreetMap contributors" + } + ], + "contributors": [ + { + "title": "Ludee", + "email": null, + "date": "2016-06-16", + "object": "metadata", + "comment": "Create metadata" + }, + { + "title": "Ludee", + "email": null, + "date": "2016-11-22", + "object": "metadata", + "comment": "Update metadata" + }, + { + "title": "Ludee", + "email": null, + "date": "2016-11-22", + "object": "metadata", + "comment": "Update header and license" + }, + { + "title": "Ludee", + "email": null, + "date": "2017-03-16", + "object": "metadata", + "comment": "Add license to source" + }, + { + "title": "Ludee", + "email": null, + "date": "2017-03-28", + "object": "metadata", + "comment": "Add copyright to source and license" + }, + { + "title": "Ludee", + "email": null, + "date": "2017-05-30", + "object": "metadata", + "comment": "Release metadata version 1.3" + }, + { + "title": "Ludee", + "email": null, + "date": "2017-06-26", + "object": "metadata", + "comment": "Move referenceDate into temporal and remove array" + }, + { + "title": "Ludee", + "email": null, + "date": "2018-07-19", + "object": "metadata", + "comment": "Start metadata version 1.4" + }, + { + "title": "Ludee", + "email": null, + "date": "2018-07-26", + "object": "data", + "comment": "Rename table and files" + }, + { + "title": "Ludee", + "email": null, + "date": "2018-10-18", + "object": "metadata", + "comment": "Add contribution object" + }, + { + "title": "christian-rli", + "email": null, + "date": "2018-10-18", + "object": "metadata", + "comment": "Add datapackage compatibility" + }, + { + "title": "Ludee", + "email": null, + "date": "2018-11-02", + "object": "metadata", + "comment": "Release metadata version 1.4" + }, + { + "title": "christian-rli", + "email": null, + "date": "2019-02-05", + "object": "metadata", + "comment": "Apply template structure to example" + }, + { + "title": "Ludee", + "email": null, + "date": "2019-03-22", + "object": "metadata", + "comment": "Hotfix foreignKeys" + }, + { + "title": "Ludee", + "email": null, + "date": "2019-07-09", + "object": "metadata", + "comment": "Release metadata version OEP-1.3.0" + }, + { + "title": "Ludee", + "email": null, + "date": "2021-11-15", + "object": "metadata", + "comment": "Release metadata version OEP-1.5.0" + }, + { + "title": "Ludee", + "email": null, + "date": "2022-02-15", + "object": "metadata", + "comment": "Release metadata version OEP-1.5.1" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "model_draft.oep_metadata_table_example_v151", + "path": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v151", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial", + "unit": null, + "isAbout": [ + { + "name": null, + "path": null + } + ], + "valueReference": [ + { + "value": null, + "name": null, + "path": null + } + ] + }, + { + "name": "name", + "description": "Example name", + "type": "text", + "unit": null, + "isAbout": [ + { + "name": "written name", + "path": "https://openenergy-platform.org/ontology/oeo/IAO_0000590" + } + ], + "valueReference": [ + { + "value": null, + "name": null, + "path": null + } + ] + }, + { + "name": "type", + "description": "Type of wind farm", + "type": "text", + "unit": null, + "isAbout": [ + { + "name": "wind farm", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000447" + } + ], + "valueReference": [ + { + "value": "onshore ", + "name": "onshore wind farm", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000311" + }, + { + "value": "offshore ", + "name": "offshore wind farm", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000308" + } + ] + }, + { + "name": "year", + "description": "Reference year", + "type": "integer", + "unit": null, + "isAbout": [ + { + "name": "year", + "path": "https://openenergy-platform.org/ontology/oeo/UO_0000036" + } + ], + "valueReference": [ + { + "value": null, + "name": null, + "path": null + } + ] + }, + { + "name": "value", + "description": "Example value", + "type": "double precision", + "unit": "MW", + "isAbout": [ + { + "name": "quantity value", + "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000350" + } + ], + "valueReference": [ + { + "value": null, + "name": null, + "path": null + } + ] + }, + { + "name": "geom", + "description": "Geometry", + "type": "geometry(Point, 4326)", + "unit": null, + "isAbout": [ + { + "name": "spatial region", + "path": "https://openenergy-platform.org/ontology/oeo/BFO_0000006" + } + ], + "valueReference": [ + { + "value": null, + "name": null, + "path": null + } + ] + } + ], + "primaryKey": [ + "id" + ], + "foreignKeys": [ + { + "fields": [ + "year" + ], + "reference": { + "resource": "schema.table", + "fields": [ + "year" + ] + } + } + ] + }, + "dialect": { + "delimiter": null, + "decimalSeparator": "." + } + } + ], + "@id": "https://databus.dbpedia.org/kurzum/mastr/bnetza-mastr/01.04.00", + "@context": "https://raw.githubusercontent.com/OpenEnergyPlatform/oemetadata/develop/metadata/latest/context.json", + "review": { + "path": "https://github.com/OpenEnergyPlatform/data-preprocessing/issues", + "badge": "Platinum" + }, + "metaMetadata": { + "metadataVersion": "OEP-1.5.1", + "metadataLicense": { + "name": "CC0-1.0", + "title": "Creative Commons Zero v1.0 Universal", + "path": "https://creativecommons.org/publicdomain/zero/1.0/" + } + }, + "_comment": { + "metadata": "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/oemetadata)", + "dates": "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", + "units": "Use a space between numbers and units (100 m)", + "languages": "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", + "licenses": "License name must follow the SPDX License List (https://spdx.org/licenses/)", + "review": "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/blob/master/data-review/manual/review_manual.md)", + "null": "If not applicable use: null", + "todo": "If a value is not yet available, use: todo" + } +} diff --git a/tests/test_validation.py b/tests/test_validation.py index 6f7301f..97ad9c0 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -1,9 +1,45 @@ """Tests for validation module of OMI.""" +import json +import pathlib + import pytest +from jsonschema.exceptions import ValidationError from omi import validation +UNSUPPORTED_OEP_METADATA_EXAMPLE_FILE = ( + pathlib.Path(__file__).parent / "test_data" / "unsupported_oep_metadata_example.json" +) +INVALID_OEP_METADATA_EXAMPLE_FILE = pathlib.Path(__file__).parent / "test_data" / "invalid_oep_metadata_example.json" + + +def test_validation_of_oep_metadata(): + """Test successful validation of OEP metadata.""" + versions = ("OEP-1.5.2", "OEP-1.6.0") + for version in versions: + metadata_schema = validation.get_metadata_schema(version) + validation.validate_metadata(metadata_schema.example) + + +def test_invalid_oep_metadata_version(): + """Test if validation error is raised for invalid OEP metadata.""" + with INVALID_OEP_METADATA_EXAMPLE_FILE.open("r") as f: + invalid_oep_metadata = json.load(f) + with pytest.raises(ValidationError): + validation.validate_metadata(invalid_oep_metadata) + + +def test_unsupported_oep_metadata_version(): + """Test if validation error is raised for unsupported OEP metadata version.""" + with UNSUPPORTED_OEP_METADATA_EXAMPLE_FILE.open("r") as f: + unsupported_oep_metadata = json.load(f) + with pytest.raises( + validation.ValidationError, + match="Metadata format for metadata version OEP-1.5.1 could not be found.", + ): + validation.validate_metadata(unsupported_oep_metadata) + def test_metadata_schema_for_oep_version(): """Test schema, template and example for OEP metadata.""" @@ -16,5 +52,8 @@ def test_metadata_schema_for_oep_version(): def test_metadata_schema_not_found(): """Test failing schema for invalid metadata version.""" - with pytest.raises(ValueError, match="Metadata format for metadata version OEP-1.5.0 could not be found."): + with pytest.raises( + validation.ValidationError, + match="Metadata format for metadata version OEP-1.5.0 could not be found.", + ): validation.get_metadata_schema("OEP-1.5.0") From 7bd10983478aa8eb8220886347ae177d57349ba1 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Wed, 29 May 2024 13:22:40 +0200 Subject: [PATCH 09/63] Add conversion from OEP v1.5.2 to v1.6.0 --- src/omi/base.py | 30 +++++++++++++++++++++++++ src/omi/conversion.py | 48 ++++++++++++++++++++++++++++++++++++++++ src/omi/validation.py | 27 +++------------------- tests/test_conversion.py | 11 +++++++++ 4 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 src/omi/base.py create mode 100644 src/omi/conversion.py create mode 100644 tests/test_conversion.py diff --git a/src/omi/base.py b/src/omi/base.py new file mode 100644 index 0000000..9c28319 --- /dev/null +++ b/src/omi/base.py @@ -0,0 +1,30 @@ +"""Base functionality for OMI.""" + +from __future__ import annotations + + +class MetadataError(Exception): + """Raised when a metadata error is encountered.""" + + +def extract_metadata_version(metadata: dict) -> str: + """ + Extract metadata version from metadata. + + Parameters + ---------- + metadata: dict + Metadata + + Returns + ------- + str + Metadata version as string + """ + # For OEP metadata + try: + return metadata["metaMetadata"]["metadataVersion"] + except KeyError: + pass + msg = "Could not extract metadata version from metadata." + raise MetadataError(msg) diff --git a/src/omi/conversion.py b/src/omi/conversion.py new file mode 100644 index 0000000..5a44fa4 --- /dev/null +++ b/src/omi/conversion.py @@ -0,0 +1,48 @@ +"""Conversion module for OMI to update metadata to different versions.""" + +from omi.base import extract_metadata_version + +METADATA_CONVERSION_CHAIN = {"OEP": ["OEP-1.5.2", "OEP-1.6.0"], "INSPIRE": []} + + +def convert_metadata(metadata: dict, target_version: str) -> dict: + """ + Convert metadata to target version. + + Parameters + ---------- + metadata: dict + Metadata dictionary + target_version: str + Target version to convert + + Returns + ------- + dict + Updated metadata + """ + metadata_version = extract_metadata_version(metadata) + metadata_conversion_functions = { + ("OEP-1.5.2", "OEP-1.6.0"): __convert_oep_152_to_160, + } + metadata_conversion_function = metadata_conversion_functions[(metadata_version, target_version)] + return metadata_conversion_function(metadata) + + +def __convert_oep_152_to_160(metadata: dict) -> dict: + """ + Convert metadata with version "OEP-1.5.2" to "OEP-1.6.0". + + Parameters + ---------- + metadata: dict + Metadata + + Returns + ------- + dict + Updated metadata + """ + # No changes in metadata fields + metadata["metaMetadata"]["metadataVersion"] = "OEP-1.6.0" + return metadata diff --git a/src/omi/validation.py b/src/omi/validation.py index 1a51b47..75e7d65 100644 --- a/src/omi/validation.py +++ b/src/omi/validation.py @@ -9,6 +9,8 @@ import jsonschema from metadata import v152, v160 +from omi.base import extract_metadata_version + # Order matters! First entry equals latest version of metadata format METADATA_FORMATS = {"OEP": ["OEP-1.6.0", "OEP-1.5.2"], "INSPIRE": []} METADATA_VERSIONS = {version: md_format for md_format, versions in METADATA_FORMATS.items() for version in versions} @@ -41,34 +43,11 @@ def validate_metadata(metadata: dict) -> None: None if metadata schema is valid. Otherwise it raises an exception. """ - metadata_version = __extract_metadata_version(metadata) + metadata_version = extract_metadata_version(metadata) metadata_schema = get_metadata_schema(metadata_version) jsonschema.validate(metadata, metadata_schema.schema) -def __extract_metadata_version(metadata: dict) -> str: - """ - Extract metadata version from metadata. - - Parameters - ---------- - metadata: dict - Metadata - - Returns - ------- - str - Metadata version as string - """ - # For OEP metadata - try: - return metadata["metaMetadata"]["metadataVersion"] - except KeyError: - pass - msg = "Could not extract metadata version from metadata." - raise ValidationError(msg) - - def get_metadata_schema(metadata_version: str) -> MetadataSchema: """ Return metadata schema for given metadata version. diff --git a/tests/test_conversion.py b/tests/test_conversion.py new file mode 100644 index 0000000..4fe612e --- /dev/null +++ b/tests/test_conversion.py @@ -0,0 +1,11 @@ +"""Tests for OMIs conversion module.""" + +from omi import base, conversion, validation + + +def test_conversion_from_oep_152_to_160(): + """Test conversion from OEP v1.5.2 -> v1.6.0.""" + metadata_schema_152 = validation.get_metadata_schema("OEP-1.5.2").example + converted_metadata_152 = conversion.convert_metadata(metadata_schema_152, "OEP-1.6.0") + assert base.extract_metadata_version(converted_metadata_152) == "OEP-1.6.0" + validation.validate_metadata(converted_metadata_152) From 19f5826f699c913cb3c8d9c0ce7f8d9fa34802c0 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Thu, 30 May 2024 09:32:47 +0200 Subject: [PATCH 10/63] Read conversion chain recursively --- src/omi/conversion.py | 63 ++++++++++++++++++++++++++++++++++++---- tests/test_conversion.py | 35 ++++++++++++++++++++++ 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/omi/conversion.py b/src/omi/conversion.py index 5a44fa4..dc33076 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -1,8 +1,12 @@ """Conversion module for OMI to update metadata to different versions.""" +from copy import deepcopy + from omi.base import extract_metadata_version -METADATA_CONVERSION_CHAIN = {"OEP": ["OEP-1.5.2", "OEP-1.6.0"], "INSPIRE": []} + +class ConversionError(Exception): + """Raised when a conversion fails.""" def convert_metadata(metadata: dict, target_version: str) -> dict: @@ -22,11 +26,53 @@ def convert_metadata(metadata: dict, target_version: str) -> dict: Updated metadata """ metadata_version = extract_metadata_version(metadata) - metadata_conversion_functions = { - ("OEP-1.5.2", "OEP-1.6.0"): __convert_oep_152_to_160, - } - metadata_conversion_function = metadata_conversion_functions[(metadata_version, target_version)] - return metadata_conversion_function(metadata) + conversion_chain = __get_conversion_chain(metadata_version, target_version) + converted_metadata = deepcopy(metadata) + for next_version in conversion_chain[1:]: + current_version = extract_metadata_version(converted_metadata) + converted_metadata = METADATA_CONVERSIONS[(current_version, next_version)](converted_metadata) + return converted_metadata + + +def __get_conversion_chain(source_version: str, target_version: str) -> list[str]: + """ + Try to find conversion chain from source version to target version. + + Parameters + ---------- + source_version: str + Starting version + target_version: str + Version goal + + Raises + ------ + ConversionError + if no conversion chain is found + + Returns + ------- + list[str] + List of conversion chain from source version to target version + """ + + def get_chain(current_version: str) -> list[str]: + for source, target in METADATA_CONVERSIONS: + if source != current_version: + continue + if target == target_version: + # Solution found! Return last conversion tuple + return [current_version, target_version] + child_chain = get_chain(target) + if child_chain is None: + continue + return [current_version, *child_chain] + return None + + conversion_chain = get_chain(source_version) + if conversion_chain: + return conversion_chain + raise ConversionError(f"No conversion chain found from {source_version} to {target_version}.") def __convert_oep_152_to_160(metadata: dict) -> dict: @@ -46,3 +92,8 @@ def __convert_oep_152_to_160(metadata: dict) -> dict: # No changes in metadata fields metadata["metaMetadata"]["metadataVersion"] = "OEP-1.6.0" return metadata + + +METADATA_CONVERSIONS = { + ("OEP-1.5.2", "OEP-1.6.0"): __convert_oep_152_to_160, +} diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 4fe612e..e23e49c 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -9,3 +9,38 @@ def test_conversion_from_oep_152_to_160(): converted_metadata_152 = conversion.convert_metadata(metadata_schema_152, "OEP-1.6.0") assert base.extract_metadata_version(converted_metadata_152) == "OEP-1.6.0" validation.validate_metadata(converted_metadata_152) + + +def test_conversion_chain(): + """Test conversion chain with conversion tree structure.""" + + def a_b_conversion(md: dict) -> dict: + md["metaMetadata"]["metadataVersion"] = "b" + md["value"] = md["value"] * 2 + return md + + def a_c_conversion(md: dict) -> dict: + md["metaMetadata"]["metadataVersion"] = "c" + md["value"] = md["value"] * 3 + return md + + def c_d_conversion(md: dict) -> dict: + md["metaMetadata"]["metadataVersion"] = "d" + md["value"] = md["value"] * 4 + return md + + def c_e_conversion(md: dict) -> dict: + md["metaMetadata"]["metadataVersion"] = "e" + md["value"] = md["value"] * 5 + return md + + conversion.METADATA_CONVERSIONS[("a", "b")] = a_b_conversion + conversion.METADATA_CONVERSIONS[("a", "c")] = a_c_conversion + conversion.METADATA_CONVERSIONS[("c", "d")] = c_d_conversion + conversion.METADATA_CONVERSIONS[("c", "e")] = c_e_conversion + + # Create dummy metadata in OEP format with version "a" + metadata = {"name": "a", "value": 10, "metaMetadata": {"metadataVersion": "a"}} + converted_metadata = conversion.convert_metadata(metadata, "e") + assert base.extract_metadata_version(converted_metadata) == "e" + assert converted_metadata["value"] == 10 * 3 * 5 From 64926b7466fcc516000c55f2b40a51835031310b Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Thu, 30 May 2024 09:33:32 +0200 Subject: [PATCH 11/63] Minor change --- src/omi/conversion.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/omi/conversion.py b/src/omi/conversion.py index dc33076..379dd5d 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -1,4 +1,5 @@ """Conversion module for OMI to update metadata to different versions.""" +from __future__ import annotations from copy import deepcopy @@ -56,7 +57,7 @@ def __get_conversion_chain(source_version: str, target_version: str) -> list[str List of conversion chain from source version to target version """ - def get_chain(current_version: str) -> list[str]: + def get_chain(current_version: str) -> list[str] | None: for source, target in METADATA_CONVERSIONS: if source != current_version: continue From c254481de721fd2a7158de966f895f634ea266e5 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Thu, 30 May 2024 09:35:35 +0200 Subject: [PATCH 12/63] Minor change --- src/omi/validation.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/omi/validation.py b/src/omi/validation.py index 75e7d65..54d4f10 100644 --- a/src/omi/validation.py +++ b/src/omi/validation.py @@ -74,8 +74,7 @@ def get_metadata_schema(metadata_version: str) -> MetadataSchema: raise ValidationError(f"Metadata format for metadata version {metadata_version} could not be found.") metadata_format = METADATA_VERSIONS[metadata_version] - metadata_schema_functions = {"OEP": __get_metadata_schema_for_oep} - return metadata_schema_functions[metadata_format](metadata_version) + return METADATA_SCHEMAS[metadata_format](metadata_version) def __get_metadata_schema_for_oep(metadata_version: str) -> MetadataSchema: @@ -100,3 +99,6 @@ def __get_metadata_schema_for_oep(metadata_version: str) -> MetadataSchema: with (module_path / f"{item}.json").open("r") as f: schema[item] = json.loads(f.read()) return MetadataSchema(**schema) + + +METADATA_SCHEMAS = {"OEP": __get_metadata_schema_for_oep} From f2d1891c5cb4934e5bc6b0297c153a18ddf5bf32 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Thu, 30 May 2024 09:40:32 +0200 Subject: [PATCH 13/63] Add test for failing conversion --- tests/test_conversion.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_conversion.py b/tests/test_conversion.py index e23e49c..9230c32 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -1,4 +1,5 @@ """Tests for OMIs conversion module.""" +import pytest from omi import base, conversion, validation @@ -44,3 +45,10 @@ def c_e_conversion(md: dict) -> dict: converted_metadata = conversion.convert_metadata(metadata, "e") assert base.extract_metadata_version(converted_metadata) == "e" assert converted_metadata["value"] == 10 * 3 * 5 + + +def test_invalid_conversion(): + """Test if conversion error is raised for invalid conversion chain.""" + metadata = {"metaMetadata": {"metadataVersion": "OEP-1.5.2"}} + with pytest.raises(conversion.ConversionError, match="No conversion chain found from OEP-1.5.2 to OEP-1.5.0."): + conversion.convert_metadata(metadata, "OEP-1.5.0") From b672ace6910f5c11f34342442b792976682d8021 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Thu, 30 May 2024 13:58:39 +0200 Subject: [PATCH 14/63] Implement infer metadata --- poetry.lock | 674 ++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + src/omi/base.py | 103 ++++++ src/omi/inspection.py | 77 +++++ src/omi/validation.py | 78 +---- tests/test_conversion.py | 3 +- tests/test_data/data.csv | 3 + tests/test_inspection.py | 36 +++ tests/test_validation.py | 12 +- 9 files changed, 903 insertions(+), 84 deletions(-) create mode 100644 src/omi/inspection.py create mode 100644 tests/test_data/data.csv create mode 100644 tests/test_inspection.py diff --git a/poetry.lock b/poetry.lock index 75485f9..97d92cb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,16 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + [[package]] name = "attrs" version = "23.2.0" @@ -30,6 +41,17 @@ files = [ {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, ] +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + [[package]] name = "cfgv" version = "3.4.0" @@ -52,6 +74,105 @@ files = [ {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, ] +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + [[package]] name = "click" version = "8.1.7" @@ -118,6 +239,76 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1 testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] +[[package]] +name = "frictionless" +version = "5.17.0" +description = "Data management framework for Python that provides functionality to describe, extract, validate, and transform tabular data" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frictionless-5.17.0-py3-none-any.whl", hash = "sha256:c7ceaf69f9430449971bd47d330f741d3d5d9513716308a2b05c56c25bdeb22d"}, + {file = "frictionless-5.17.0.tar.gz", hash = "sha256:bee4a9f076fab15e462772d09d3a496b50d73ec5ecb927d97b9307eeaefc6d3c"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +chardet = ">=3.0" +humanize = ">=4.2" +isodate = ">=0.6" +jinja2 = ">=3.0" +jsonschema = ">=4.20" +marko = ">=1.0" +petl = ">=1.6" +pydantic = ">=2.0" +python-dateutil = ">=2.8" +python-slugify = ">=1.2" +pyyaml = ">=5.3" +requests = ">=2.10" +rfc3986 = ">=1.4" +simpleeval = ">=0.9.11" +stringcase = ">=1.2" +tabulate = ">=0.8.10" +typer = ">=0.12" +typing-extensions = ">=4.3" +validators = ">=0.18" + +[package.extras] +aws = ["boto3 (>=1.9)"] +bigquery = ["google-api-python-client (>=1.12.1)"] +ckan = ["frictionless-ckan-mapper (>=1.0)"] +datasette = ["datasette (>=0.64.2)"] +dev = ["hatch", "httpx", "ipython", "livemark", "moto", "neovim", "oauth2client", "pyright (==1.1.317)", "pytest", "pytest-cov", "pytest-dotenv", "pytest-lazy-fixtures", "pytest-mock", "pytest-only", "pytest-timeout", "pytest-vcr", "requests-mock", "ruff", "yattag"] +duckdb = ["duckdb (>=0.8)", "duckdb-engine (>=0.7)", "sqlalchemy (>=1.4)"] +excel = ["openpyxl (>=3.0)", "tableschema-to-template (>=0.0)", "xlrd (>=1.2)", "xlwt (>=1.2)"] +github = ["pygithub (>=1.50)"] +gsheets = ["pygsheets (>=2.0)"] +html = ["pyquery (>=1.4)"] +json = ["ijson (>=3.0)", "jsonlines (>=1.2)"] +mysql = ["pymysql (>=1.0)", "sqlalchemy (>=1.4)"] +ods = ["ezodf (>=0.3)", "lxml (>=4.0)"] +pandas = ["pandas (>=1.0)", "pyarrow (>=14.0)"] +parquet = ["fastparquet (>=0.8)"] +postgresql = ["psycopg (>=3.0)", "psycopg2 (>=2.9)", "sqlalchemy (>=1.4)"] +spss = ["savreaderwriter (>=3.0)"] +sql = ["sqlalchemy (>=1.4)"] +visidata = ["visidata (>=2.10)"] +wkt = ["tatsu (>=5.8.3)"] +zenodo = ["pyzenodo3 (>=1.0)"] + +[[package]] +name = "humanize" +version = "4.9.0" +description = "Python humanize utilities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "humanize-4.9.0-py3-none-any.whl", hash = "sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16"}, + {file = "humanize-4.9.0.tar.gz", hash = "sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa"}, +] + +[package.extras] +tests = ["freezegun", "pytest", "pytest-cov"] + [[package]] name = "identify" version = "2.5.36" @@ -132,6 +323,17 @@ files = [ [package.extras] license = ["ukkonen"] +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + [[package]] name = "iniconfig" version = "2.0.0" @@ -157,6 +359,23 @@ files = [ [package.dependencies] six = "*" +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + [[package]] name = "jsonschema" version = "4.22.0" @@ -192,6 +411,126 @@ files = [ [package.dependencies] referencing = ">=0.31.0" +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "marko" +version = "2.0.3" +description = "A markdown parser with high extensibility." +optional = false +python-versions = ">=3.7" +files = [ + {file = "marko-2.0.3-py3-none-any.whl", hash = "sha256:7fca1c4ab1dbc09b4b3be83c22caafd7d97c99439cb4143d025727cb3df1f4d0"}, + {file = "marko-2.0.3.tar.gz", hash = "sha256:3b323dcd7dd48181871718ac09b3825bc8f74493cec378f2bacaaceec47577d4"}, +] + +[package.extras] +codehilite = ["pygments"] +repr = ["objprint"] +toc = ["python-slugify"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "nodeenv" version = "1.8.0" @@ -228,6 +567,32 @@ files = [ {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] +[[package]] +name = "petl" +version = "1.7.15" +description = "A Python package for extracting, transforming and loading tables of data." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "petl-1.7.15.tar.gz", hash = "sha256:8e31438380ad51552539865ad3b1ab655de1b531bd03980c871ec2cff4a8c414"}, +] + +[package.extras] +avro = ["fastavro (>=0.24.0)"] +bcolz = ["bcolz (>=1.2.1)"] +db = ["SQLAlchemy (>=1.3.6,<2.0)"] +hdf5 = ["cython (>=0.29.13)", "numexpr (>=2.6.9)", "numpy (>=1.16.4)", "tables (>=3.5.2)"] +http = ["aiohttp (>=3.6.2)", "requests"] +interval = ["intervaltree (>=3.0.2)"] +numpy = ["numpy (>=1.16.4)"] +pandas = ["pandas (>=0.24.2)"] +remote = ["fsspec (>=0.7.4)"] +smb = ["smbprotocol (>=1.0.1)"] +whoosh = ["whoosh"] +xls = ["xlrd (>=2.0.1)", "xlwt (>=1.3.0)"] +xlsx = ["openpyxl (>=2.6.2)"] +xpath = ["lxml (>=4.4.0)"] + [[package]] name = "platformdirs" version = "4.2.2" @@ -277,6 +642,130 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "pydantic" +version = "2.7.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.7.2-py3-none-any.whl", hash = "sha256:834ab954175f94e6e68258537dc49402c4a5e9d0409b9f1b86b7e934a8372de7"}, + {file = "pydantic-2.7.2.tar.gz", hash = "sha256:71b2945998f9c9b7919a45bde9a50397b289937d215ae141c1d0903ba7149fd7"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.18.3" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.3" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:744697428fcdec6be5670460b578161d1ffe34743a5c15656be7ea82b008197c"}, + {file = "pydantic_core-2.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b40c05ced1ba4218b14986fe6f283d22e1ae2ff4c8e28881a70fb81fbfcda7"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a9a75622357076efb6b311983ff190fbfb3c12fc3a853122b34d3d358126c"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2e253af04ceaebde8eb201eb3f3e3e7e390f2d275a88300d6a1959d710539e2"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:855ec66589c68aa367d989da5c4755bb74ee92ccad4fdb6af942c3612c067e34"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3e42bb54e7e9d72c13ce112e02eb1b3b55681ee948d748842171201a03a98a"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6ac9ffccc9d2e69d9fba841441d4259cb668ac180e51b30d3632cd7abca2b9b"}, + {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c56eca1686539fa0c9bda992e7bd6a37583f20083c37590413381acfc5f192d6"}, + {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:17954d784bf8abfc0ec2a633108207ebc4fa2df1a0e4c0c3ccbaa9bb01d2c426"}, + {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:98ed737567d8f2ecd54f7c8d4f8572ca7c7921ede93a2e52939416170d357812"}, + {file = "pydantic_core-2.18.3-cp310-none-win32.whl", hash = "sha256:9f9e04afebd3ed8c15d67a564ed0a34b54e52136c6d40d14c5547b238390e779"}, + {file = "pydantic_core-2.18.3-cp310-none-win_amd64.whl", hash = "sha256:45e4ffbae34f7ae30d0047697e724e534a7ec0a82ef9994b7913a412c21462a0"}, + {file = "pydantic_core-2.18.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9ebe8231726c49518b16b237b9fe0d7d361dd221302af511a83d4ada01183ab"}, + {file = "pydantic_core-2.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b8e20e15d18bf7dbb453be78a2d858f946f5cdf06c5072453dace00ab652e2b2"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0d9ff283cd3459fa0bf9b0256a2b6f01ac1ff9ffb034e24457b9035f75587cb"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f7ef5f0ebb77ba24c9970da18b771711edc5feaf00c10b18461e0f5f5949231"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73038d66614d2e5cde30435b5afdced2b473b4c77d4ca3a8624dd3e41a9c19be"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6afd5c867a74c4d314c557b5ea9520183fadfbd1df4c2d6e09fd0d990ce412cd"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd7df92f28d351bb9f12470f4c533cf03d1b52ec5a6e5c58c65b183055a60106"}, + {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:80aea0ffeb1049336043d07799eace1c9602519fb3192916ff525b0287b2b1e4"}, + {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaee40f25bba38132e655ffa3d1998a6d576ba7cf81deff8bfa189fb43fd2bbe"}, + {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9128089da8f4fe73f7a91973895ebf2502539d627891a14034e45fb9e707e26d"}, + {file = "pydantic_core-2.18.3-cp311-none-win32.whl", hash = "sha256:fec02527e1e03257aa25b1a4dcbe697b40a22f1229f5d026503e8b7ff6d2eda7"}, + {file = "pydantic_core-2.18.3-cp311-none-win_amd64.whl", hash = "sha256:58ff8631dbab6c7c982e6425da8347108449321f61fe427c52ddfadd66642af7"}, + {file = "pydantic_core-2.18.3-cp311-none-win_arm64.whl", hash = "sha256:3fc1c7f67f34c6c2ef9c213e0f2a351797cda98249d9ca56a70ce4ebcaba45f4"}, + {file = "pydantic_core-2.18.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f0928cde2ae416a2d1ebe6dee324709c6f73e93494d8c7aea92df99aab1fc40f"}, + {file = "pydantic_core-2.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bee9bb305a562f8b9271855afb6ce00223f545de3d68560b3c1649c7c5295e9"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e862823be114387257dacbfa7d78547165a85d7add33b446ca4f4fae92c7ff5c"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a36f78674cbddc165abab0df961b5f96b14461d05feec5e1f78da58808b97e7"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba905d184f62e7ddbb7a5a751d8a5c805463511c7b08d1aca4a3e8c11f2e5048"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fdd362f6a586e681ff86550b2379e532fee63c52def1c666887956748eaa326"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b214b7ee3bd3b865e963dbed0f8bc5375f49449d70e8d407b567af3222aae4"}, + {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691018785779766127f531674fa82bb368df5b36b461622b12e176c18e119022"}, + {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:60e4c625e6f7155d7d0dcac151edf5858102bc61bf959d04469ca6ee4e8381bd"}, + {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4e651e47d981c1b701dcc74ab8fec5a60a5b004650416b4abbef13db23bc7be"}, + {file = "pydantic_core-2.18.3-cp312-none-win32.whl", hash = "sha256:ffecbb5edb7f5ffae13599aec33b735e9e4c7676ca1633c60f2c606beb17efc5"}, + {file = "pydantic_core-2.18.3-cp312-none-win_amd64.whl", hash = "sha256:2c8333f6e934733483c7eddffdb094c143b9463d2af7e6bd85ebcb2d4a1b82c6"}, + {file = "pydantic_core-2.18.3-cp312-none-win_arm64.whl", hash = "sha256:7a20dded653e516a4655f4c98e97ccafb13753987434fe7cf044aa25f5b7d417"}, + {file = "pydantic_core-2.18.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:eecf63195be644b0396f972c82598cd15693550f0ff236dcf7ab92e2eb6d3522"}, + {file = "pydantic_core-2.18.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c44efdd3b6125419c28821590d7ec891c9cb0dff33a7a78d9d5c8b6f66b9702"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e59fca51ffbdd1638b3856779342ed69bcecb8484c1d4b8bdb237d0eb5a45e2"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70cf099197d6b98953468461d753563b28e73cf1eade2ffe069675d2657ed1d5"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63081a49dddc6124754b32a3774331467bfc3d2bd5ff8f10df36a95602560361"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:370059b7883485c9edb9655355ff46d912f4b03b009d929220d9294c7fd9fd60"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a64faeedfd8254f05f5cf6fc755023a7e1606af3959cfc1a9285744cc711044"}, + {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19d2e725de0f90d8671f89e420d36c3dd97639b98145e42fcc0e1f6d492a46dc"}, + {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:67bc078025d70ec5aefe6200ef094576c9d86bd36982df1301c758a9fff7d7f4"}, + {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:adf952c3f4100e203cbaf8e0c907c835d3e28f9041474e52b651761dc248a3c0"}, + {file = "pydantic_core-2.18.3-cp38-none-win32.whl", hash = "sha256:9a46795b1f3beb167eaee91736d5d17ac3a994bf2215a996aed825a45f897558"}, + {file = "pydantic_core-2.18.3-cp38-none-win_amd64.whl", hash = "sha256:200ad4e3133cb99ed82342a101a5abf3d924722e71cd581cc113fe828f727fbc"}, + {file = "pydantic_core-2.18.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:304378b7bf92206036c8ddd83a2ba7b7d1a5b425acafff637172a3aa72ad7083"}, + {file = "pydantic_core-2.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c826870b277143e701c9ccf34ebc33ddb4d072612683a044e7cce2d52f6c3fef"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e201935d282707394f3668380e41ccf25b5794d1b131cdd96b07f615a33ca4b1"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5560dda746c44b48bf82b3d191d74fe8efc5686a9ef18e69bdabccbbb9ad9442"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b32c2a1f8032570842257e4c19288eba9a2bba4712af542327de9a1204faff8"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:929c24e9dea3990bc8bcd27c5f2d3916c0c86f5511d2caa69e0d5290115344a9"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a8376fef60790152564b0eab376b3e23dd6e54f29d84aad46f7b264ecca943"}, + {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dccf3ef1400390ddd1fb55bf0632209d39140552d068ee5ac45553b556780e06"}, + {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41dbdcb0c7252b58fa931fec47937edb422c9cb22528f41cb8963665c372caf6"}, + {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:666e45cf071669fde468886654742fa10b0e74cd0fa0430a46ba6056b24fb0af"}, + {file = "pydantic_core-2.18.3-cp39-none-win32.whl", hash = "sha256:f9c08cabff68704a1b4667d33f534d544b8a07b8e5d039c37067fceb18789e78"}, + {file = "pydantic_core-2.18.3-cp39-none-win_amd64.whl", hash = "sha256:4afa5f5973e8572b5c0dcb4e2d4fda7890e7cd63329bd5cc3263a25c92ef0026"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:77319771a026f7c7d29c6ebc623de889e9563b7087911b46fd06c044a12aa5e9"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:df11fa992e9f576473038510d66dd305bcd51d7dd508c163a8c8fe148454e059"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d531076bdfb65af593326ffd567e6ab3da145020dafb9187a1d131064a55f97c"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33ce258e4e6e6038f2b9e8b8a631d17d017567db43483314993b3ca345dcbbb"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1f9cd7f5635b719939019be9bda47ecb56e165e51dd26c9a217a433e3d0d59a9"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cd4a032bb65cc132cae1fe3e52877daecc2097965cd3914e44fbd12b00dae7c5"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f2718430098bcdf60402136c845e4126a189959d103900ebabb6774a5d9fdb"}, + {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0037a92cf0c580ed14e10953cdd26528e8796307bb8bb312dc65f71547df04d"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b95a0972fac2b1ff3c94629fc9081b16371dad870959f1408cc33b2f78ad347a"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a62e437d687cc148381bdd5f51e3e81f5b20a735c55f690c5be94e05da2b0d5c"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b367a73a414bbb08507da102dc2cde0fa7afe57d09b3240ce82a16d608a7679c"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ecce4b2360aa3f008da3327d652e74a0e743908eac306198b47e1c58b03dd2b"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd4435b8d83f0c9561a2a9585b1de78f1abb17cb0cef5f39bf6a4b47d19bafe3"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:616221a6d473c5b9aa83fa8982745441f6a4a62a66436be9445c65f241b86c94"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7e6382ce89a92bc1d0c0c5edd51e931432202b9080dc921d8d003e616402efd1"}, + {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff58f379345603d940e461eae474b6bbb6dab66ed9a851ecd3cb3709bf4dcf6a"}, + {file = "pydantic_core-2.18.3.tar.gz", hash = "sha256:432e999088d85c8f36b9a3f769a8e2b57aabd817bbb729a90d1fe7f18f6f1f39"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pyparsing" version = "3.1.2" @@ -346,6 +835,23 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-slugify" +version = "8.0.4" +description = "A Python slugify application that also handles Unicode" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"}, + {file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"}, +] + +[package.dependencies] +text-unidecode = ">=1.3" + +[package.extras] +unidecode = ["Unidecode (>=1.1.1)"] + [[package]] name = "pyyaml" version = "6.0.1" @@ -442,6 +948,59 @@ files = [ attrs = ">=22.2.0" rpds-py = ">=0.7.0" +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rfc3986" +version = "2.0.0" +description = "Validating URI References per RFC 3986" +optional = false +python-versions = ">=3.7" +files = [ + {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, + {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, +] + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "rpds-py" version = "0.18.1" @@ -565,6 +1124,28 @@ files = [ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "simpleeval" +version = "0.9.13" +description = "A simple, safe single expression evaluator library." +optional = false +python-versions = "*" +files = [ + {file = "simpleeval-0.9.13-py2.py3-none-any.whl", hash = "sha256:22a2701a5006e4188d125d34accf2405c2c37c93f6b346f2484b6422415ae54a"}, + {file = "simpleeval-0.9.13.tar.gz", hash = "sha256:4a30f9cc01825fe4c719c785e3762623e350c4840d5e6855c2a8496baaa65fac"}, +] + [[package]] name = "six" version = "1.16.0" @@ -576,6 +1157,41 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "stringcase" +version = "1.2.0" +description = "String case converter." +optional = false +python-versions = "*" +files = [ + {file = "stringcase-1.2.0.tar.gz", hash = "sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008"}, +] + +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "text-unidecode" +version = "1.3" +description = "The most basic Text::Unidecode port" +optional = false +python-versions = "*" +files = [ + {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, + {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, +] + [[package]] name = "tomli" version = "2.0.1" @@ -614,6 +1230,62 @@ virtualenv = ">=20.25" docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"] testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=8.0.2)", "distlib (>=0.3.8)", "flaky (>=3.7)", "hatch-vcs (>=0.4)", "hatchling (>=1.21)", "psutil (>=5.9.7)", "pytest (>=7.4.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-xdist (>=3.5)", "re-assert (>=1.1)", "time-machine (>=2.13)", "wheel (>=0.42)"] +[[package]] +name = "typer" +version = "0.12.3" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + +[[package]] +name = "typing-extensions" +version = "4.12.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, + {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "validators" +version = "0.28.3" +description = "Python Data Validation for Humans™" +optional = false +python-versions = ">=3.8" +files = [ + {file = "validators-0.28.3-py3-none-any.whl", hash = "sha256:53cafa854f13850156259d9cc479b864ee901f6a96e6b109e6fc33f98f37d99f"}, + {file = "validators-0.28.3.tar.gz", hash = "sha256:c6c79840bcde9ba77b19f6218f7738188115e27830cbaff43264bc4ed24c429d"}, +] + [[package]] name = "virtualenv" version = "20.26.2" @@ -637,4 +1309,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "a1f0e5e9b44407ce3b4fcb34ea8cf921990898cd54967fd3b460d67b521935bb" +content-hash = "d48feff977ab413b36949f3de503698612ccbba878b732bffb2a02a0341e1883" diff --git a/pyproject.toml b/pyproject.toml index 088cb43..69d3c87 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ rdfLib = "^7.0.0" python-dateutil = "^2.9.0" jsonschema = "^4.22.0" oemetadata = ">=1.5.2" +frictionless = "^5.17.0" [tool.poetry.group.dev.dependencies] tox = "^4.15.0" diff --git a/src/omi/base.py b/src/omi/base.py index 9c28319..f4f7ce6 100644 --- a/src/omi/base.py +++ b/src/omi/base.py @@ -2,11 +2,30 @@ from __future__ import annotations +import json +import pathlib +from dataclasses import dataclass + +from metadata import v152, v160 + +# Order matters! First entry equals latest version of metadata format +METADATA_FORMATS = {"OEP": ["OEP-1.6.0", "OEP-1.5.2"], "INSPIRE": []} +METADATA_VERSIONS = {version: md_format for md_format, versions in METADATA_FORMATS.items() for version in versions} + class MetadataError(Exception): """Raised when a metadata error is encountered.""" +@dataclass +class MetadataSpecification: + """Metadata schema class, holding JSON schema and (optional) template and example for given schema.""" + + schema: dict + template: dict | None = None + example: dict | None = None + + def extract_metadata_version(metadata: dict) -> str: """ Extract metadata version from metadata. @@ -28,3 +47,87 @@ def extract_metadata_version(metadata: dict) -> str: pass msg = "Could not extract metadata version from metadata." raise MetadataError(msg) + + +def get_latest_metadata_version(metadata_format: str) -> str: + """ + Return the latest metadata version of a given metadata format. + + Parameters + ---------- + metadata_format: str + Metadata format to check for latest version + + Raises + ------ + MetadataError + if metadata format is unknown or has no latest version + + Returns + ------- + str + Latest version of metadata format + """ + if metadata_format not in METADATA_FORMATS: + raise MetadataError( + f"Unknown metadata format: {metadata_format}. Possible candidates are: {','.join(METADATA_FORMATS)}.", + ) + if len(METADATA_FORMATS[metadata_format]) == 0: + raise MetadataError(f"No latest metadata version found for format {metadata_format}.") + return METADATA_FORMATS[metadata_format][0] + + +def get_metadata_specification(metadata_version: str) -> MetadataSpecification: + """ + Return metadata specification for given metadata version. + + Metadata versions are defined in METADATA_FORMATS. + Fetching metadata specification depends on metadata format. + + Parameters + ---------- + metadata_version: str + Metadata version + + Raises + ------ + ValueError + if metadata version is not in METADATA_FORMATS + + Returns + ------- + MetadataSpecification + Metadata specification holding (at least) JSON schema for given metadata version. + """ + if metadata_version not in METADATA_VERSIONS: + raise MetadataError(f"Metadata format for metadata version {metadata_version} could not be found.") + metadata_format = METADATA_VERSIONS[metadata_version] + + return METADATA_SPECIFICATIONS[metadata_format](metadata_version) + + +def __get_metadata_specs_for_oep(metadata_version: str) -> MetadataSpecification: + """ + Return OEP metadata schema for given metadata version. + + Parameters + ---------- + metadata_version: str + Metadata version + + Returns + ------- + MetadataSpecification + Metadata schema for given metadata version including template and example. + """ + metadata_modules = {"OEP-1.5.2": v152, "OEP-1.6.0": v160} + metadata_module = metadata_modules[metadata_version] + module_path = pathlib.Path(metadata_module.__file__).parent + specs = {} + for item in ("schema", "template", "example"): + with (module_path / f"{item}.json").open("r") as f: + specs[item] = json.loads(f.read()) + return MetadataSpecification(**specs) + + +METADATA_SPECIFICATIONS = {"OEP": __get_metadata_specs_for_oep} diff --git a/src/omi/inspection.py b/src/omi/inspection.py new file mode 100644 index 0000000..b6f946a --- /dev/null +++ b/src/omi/inspection.py @@ -0,0 +1,77 @@ +"""Module to inspect data and create metadata from it.""" + +from typing import Any + +from frictionless import Detector, Dialect, Resource +from frictionless.formats import CsvControl + +from omi import base + + +class InspectionError(Exception): + """Raised when an error occurs during inspection.""" + + +def infer_metadata(data: Any, metadata_format: str) -> dict: # noqa: ANN401 + """ + Guess metadata from data in given metadata format. + + Parameters + ---------- + data: Any + Data read from CSV file or other source frictionless may understand + metadata_format: str + Metadata format the inferred metadata should follow + + Returns + ------- + dict + OEMetadata guessed from data, containing name from CSV + """ + latest_metadata_version = base.get_latest_metadata_version(metadata_format) + template_metadata = base.get_metadata_specification(latest_metadata_version).template + if template_metadata is None: + raise InspectionError(f"No metadata template for metadata format {metadata_format} found.") + + fields = __guess_fields_from_data(data) + inferred_metadata = METADATA_TEMPLATE_ENGINE[metadata_format](template_metadata, fields) + return inferred_metadata + + +def __guess_fields_from_data(data: Any) -> list[dict[str, str]]: # noqa: ANN401 + """ + Field names and types of data columns are detected by Frictionless. + + Parameters + ---------- + data: Any + Data read from CSV file or other source frictionless may understand + + Returns + ------- + list[dict[str, str]] + List of fields holding name and type as strings + """ + csv_control = CsvControl(delimiter=";") + dialect = Dialect(controls=[csv_control]) + detector = Detector(field_float_numbers=True) + resource = Resource( + source=data, + name="test", + profile="tabular-data-resource", + format="csv", + dialect=dialect, + detector=detector, + ) + # Must be run, before schema can be inspected + resource.read_rows() + fields = resource.schema.to_dict()["fields"] + return fields + + +def __apply_fields_to_oep_metadata_template(metadata: dict, fields: list[dict[str, Any]]) -> dict: + metadata["resources"][0]["schema"]["fields"] = fields + return metadata + + +METADATA_TEMPLATE_ENGINE = {"OEP": __apply_fields_to_oep_metadata_template} diff --git a/src/omi/validation.py b/src/omi/validation.py index 54d4f10..cc2388e 100644 --- a/src/omi/validation.py +++ b/src/omi/validation.py @@ -2,33 +2,15 @@ from __future__ import annotations -import json -import pathlib -from dataclasses import dataclass - import jsonschema -from metadata import v152, v160 - -from omi.base import extract_metadata_version -# Order matters! First entry equals latest version of metadata format -METADATA_FORMATS = {"OEP": ["OEP-1.6.0", "OEP-1.5.2"], "INSPIRE": []} -METADATA_VERSIONS = {version: md_format for md_format, versions in METADATA_FORMATS.items() for version in versions} +from omi.base import extract_metadata_version, get_metadata_specification class ValidationError(Exception): """Exception raised when a validation fails.""" -@dataclass -class MetadataSchema: - """Metadata schema class, holding JSON schema and (optional) template and example for given schema.""" - - schema: dict - template: dict | None = None - example: dict | None = None - - def validate_metadata(metadata: dict) -> None: """ Validate metadata against related metadata schema. @@ -44,61 +26,5 @@ def validate_metadata(metadata: dict) -> None: if metadata schema is valid. Otherwise it raises an exception. """ metadata_version = extract_metadata_version(metadata) - metadata_schema = get_metadata_schema(metadata_version) + metadata_schema = get_metadata_specification(metadata_version) jsonschema.validate(metadata, metadata_schema.schema) - - -def get_metadata_schema(metadata_version: str) -> MetadataSchema: - """ - Return metadata schema for given metadata version. - - Metadata versions are defined in METADATA_FORMATS. - Fetching metadata schema depends on metadata format. - - Parameters - ---------- - metadata_version: str - Metadata version - - Raises - ------ - ValueError - if metadata version is not in METADATA_FORMATS - - Returns - ------- - MetadataSchema - Metadata schema holding (at least) JSON schema for given metadata version. - """ - if metadata_version not in METADATA_VERSIONS: - raise ValidationError(f"Metadata format for metadata version {metadata_version} could not be found.") - metadata_format = METADATA_VERSIONS[metadata_version] - - return METADATA_SCHEMAS[metadata_format](metadata_version) - - -def __get_metadata_schema_for_oep(metadata_version: str) -> MetadataSchema: - """ - Return OEP metadata schema for given metadata version. - - Parameters - ---------- - metadata_version: str - Metadata version - - Returns - ------- - MetadataSchema - Metadata schema for given metadata version including template and example. - """ - metadata_modules = {"OEP-1.5.2": v152, "OEP-1.6.0": v160} - metadata_module = metadata_modules[metadata_version] - module_path = pathlib.Path(metadata_module.__file__).parent - schema = {} - for item in ("schema", "template", "example"): - with (module_path / f"{item}.json").open("r") as f: - schema[item] = json.loads(f.read()) - return MetadataSchema(**schema) - - -METADATA_SCHEMAS = {"OEP": __get_metadata_schema_for_oep} diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 9230c32..1fb3892 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -1,12 +1,13 @@ """Tests for OMIs conversion module.""" import pytest +import omi.base from omi import base, conversion, validation def test_conversion_from_oep_152_to_160(): """Test conversion from OEP v1.5.2 -> v1.6.0.""" - metadata_schema_152 = validation.get_metadata_schema("OEP-1.5.2").example + metadata_schema_152 = omi.base.get_metadata_specification("OEP-1.5.2").example converted_metadata_152 = conversion.convert_metadata(metadata_schema_152, "OEP-1.6.0") assert base.extract_metadata_version(converted_metadata_152) == "OEP-1.6.0" validation.validate_metadata(converted_metadata_152) diff --git a/tests/test_data/data.csv b/tests/test_data/data.csv new file mode 100644 index 0000000..2f683a8 --- /dev/null +++ b/tests/test_data/data.csv @@ -0,0 +1,3 @@ +string;integer;number;array string;array integer;array float;object;date;bool +gas;55;0.5;["helo", "none"];[1, 2, 3];[1.5, 1.3, 1.8];{"a": 300, "b": "hello"};2024-01-01;true +wind;18;1.8;["tut", "tut", "tut"];[3, 6, 3];[4.55, 44.7, 23.999];{"c": 2, "d": 4};2024-02-02;false diff --git a/tests/test_inspection.py b/tests/test_inspection.py new file mode 100644 index 0000000..7236c16 --- /dev/null +++ b/tests/test_inspection.py @@ -0,0 +1,36 @@ +"""Tests for `inspection` module of OMI.""" + +import pathlib + +from omi import inspection + +CSV_DATA_FILE = pathlib.Path(__file__).parent / "test_data" / "data.csv" + + +def test_inspection(): + """Test inspection of test data file and check resulting metadata.""" + with CSV_DATA_FILE.open("r") as f: + metadata = inspection.infer_metadata(f, "OEP") + + assert len(metadata["resources"]) == 1 + assert len(metadata["resources"][0]["schema"]["fields"]) == 9 + + assert metadata["resources"][0]["schema"]["fields"][0]["name"] == "string" + assert metadata["resources"][0]["schema"]["fields"][1]["name"] == "integer" + assert metadata["resources"][0]["schema"]["fields"][2]["name"] == "number" + assert metadata["resources"][0]["schema"]["fields"][3]["name"] == "array string" + assert metadata["resources"][0]["schema"]["fields"][4]["name"] == "array integer" + assert metadata["resources"][0]["schema"]["fields"][5]["name"] == "array float" + assert metadata["resources"][0]["schema"]["fields"][6]["name"] == "object" + assert metadata["resources"][0]["schema"]["fields"][7]["name"] == "date" + assert metadata["resources"][0]["schema"]["fields"][8]["name"] == "bool" + + assert metadata["resources"][0]["schema"]["fields"][0]["type"] == "string" + assert metadata["resources"][0]["schema"]["fields"][1]["type"] == "integer" + assert metadata["resources"][0]["schema"]["fields"][2]["type"] == "number" + assert metadata["resources"][0]["schema"]["fields"][3]["type"] == "array" + assert metadata["resources"][0]["schema"]["fields"][4]["type"] == "array" + assert metadata["resources"][0]["schema"]["fields"][5]["type"] == "array" + assert metadata["resources"][0]["schema"]["fields"][6]["type"] == "object" + assert metadata["resources"][0]["schema"]["fields"][7]["type"] == "date" + assert metadata["resources"][0]["schema"]["fields"][8]["type"] == "boolean" diff --git a/tests/test_validation.py b/tests/test_validation.py index 97ad9c0..bd08026 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -6,7 +6,7 @@ import pytest from jsonschema.exceptions import ValidationError -from omi import validation +from omi import base, validation UNSUPPORTED_OEP_METADATA_EXAMPLE_FILE = ( pathlib.Path(__file__).parent / "test_data" / "unsupported_oep_metadata_example.json" @@ -18,7 +18,7 @@ def test_validation_of_oep_metadata(): """Test successful validation of OEP metadata.""" versions = ("OEP-1.5.2", "OEP-1.6.0") for version in versions: - metadata_schema = validation.get_metadata_schema(version) + metadata_schema = base.get_metadata_specification(version) validation.validate_metadata(metadata_schema.example) @@ -35,7 +35,7 @@ def test_unsupported_oep_metadata_version(): with UNSUPPORTED_OEP_METADATA_EXAMPLE_FILE.open("r") as f: unsupported_oep_metadata = json.load(f) with pytest.raises( - validation.ValidationError, + base.MetadataError, match="Metadata format for metadata version OEP-1.5.1 could not be found.", ): validation.validate_metadata(unsupported_oep_metadata) @@ -44,7 +44,7 @@ def test_unsupported_oep_metadata_version(): def test_metadata_schema_for_oep_version(): """Test schema, template and example for OEP metadata.""" version = "OEP-1.5.2" - schema = validation.get_metadata_schema(version) + schema = base.get_metadata_specification(version) assert schema.schema["description"] == "Open Energy Plaftorm (OEP) metadata schema v1.5.2" assert schema.template["name"] is None assert schema.example["name"] == "oep_metadata_table_example_v152" @@ -53,7 +53,7 @@ def test_metadata_schema_for_oep_version(): def test_metadata_schema_not_found(): """Test failing schema for invalid metadata version.""" with pytest.raises( - validation.ValidationError, + base.MetadataError, match="Metadata format for metadata version OEP-1.5.0 could not be found.", ): - validation.get_metadata_schema("OEP-1.5.0") + base.get_metadata_specification("OEP-1.5.0") From ab20488715e607f9c127f648d4b91a0d0186f103 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Thu, 30 May 2024 14:57:18 +0200 Subject: [PATCH 15/63] Implement conversion of frictionless types into OEP types --- src/omi/inspection.py | 70 ++++++++++++++++++++++++++++++++++++---- tests/test_inspection.py | 8 ++--- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/omi/inspection.py b/src/omi/inspection.py index b6f946a..f7b4dd5 100644 --- a/src/omi/inspection.py +++ b/src/omi/inspection.py @@ -1,5 +1,6 @@ """Module to inspect data and create metadata from it.""" +from collections.abc import Callable from typing import Any from frictionless import Detector, Dialect, Resource @@ -16,6 +17,8 @@ def infer_metadata(data: Any, metadata_format: str) -> dict: # noqa: ANN401 """ Guess metadata from data in given metadata format. + Note: It expects semicolon-delimited data. + Parameters ---------- data: Any @@ -33,12 +36,12 @@ def infer_metadata(data: Any, metadata_format: str) -> dict: # noqa: ANN401 if template_metadata is None: raise InspectionError(f"No metadata template for metadata format {metadata_format} found.") - fields = __guess_fields_from_data(data) - inferred_metadata = METADATA_TEMPLATE_ENGINE[metadata_format](template_metadata, fields) + fields, resource = __guess_fields_from_data(data) + inferred_metadata = METADATA_TEMPLATE_ENGINE[metadata_format](template_metadata, fields, resource) return inferred_metadata -def __guess_fields_from_data(data: Any) -> list[dict[str, str]]: # noqa: ANN401 +def __guess_fields_from_data(data: Any) -> tuple[list[dict[str, str]], Resource]: # noqa: ANN401 """ Field names and types of data columns are detected by Frictionless. @@ -51,6 +54,8 @@ def __guess_fields_from_data(data: Any) -> list[dict[str, str]]: # noqa: ANN401 ------- list[dict[str, str]] List of fields holding name and type as strings + Resource + Extracted resource """ csv_control = CsvControl(delimiter=";") dialect = Dialect(controls=[csv_control]) @@ -64,14 +69,65 @@ def __guess_fields_from_data(data: Any) -> list[dict[str, str]]: # noqa: ANN401 detector=detector, ) # Must be run, before schema can be inspected - resource.read_rows() + resource.infer() fields = resource.schema.to_dict()["fields"] - return fields + return fields, resource + + +def __apply_fields_to_oep_metadata_template(metadata: dict, fields: list[dict[str, str]], resource: Resource) -> dict: + """ + Apply fields to metadata template for OEP metadata. + + Parameters + ---------- + metadata: dict + Metadata template + fields: list[dict[str, str]] + List of fields holding name and type as strings + resource: Resource + Extracted frictionless resource holding data + Returns + ------- + dict + OEP metadata template holding guessed fields + """ + type_mapping = {str(str): "string", str(int): "integer", str(float): "float"} + + def convert_field(field: dict[str, str]) -> dict[str, str]: + """ + Convert frictionless field types to OEP types. + + This only includes conversion of number to float and detection of subtypes in arrays + (currently, only string, integer and float are detected as subtypes). + + Parameters + ---------- + field: dict[str, str] + Frictionless field description + + Returns + ------- + dict[str, str] + Field description with OEP supported types + """ + if field["type"] == "number": + return {"name": field["name"], "type": "float"} + if field["type"] == "array": + for row in rows: + if len(row[field["name"]]) == 0: + continue + item_type = str(type(row[field["name"]][0])) + return {"name": field["name"], "type": f"array {type_mapping[item_type]}"} + # All arrays are empty - so no further subtype can be detected + return {"name": field["name"], "type": "array"} + return field + + rows = resource.read_rows() + fields = [convert_field(field) for field in fields] -def __apply_fields_to_oep_metadata_template(metadata: dict, fields: list[dict[str, Any]]) -> dict: metadata["resources"][0]["schema"]["fields"] = fields return metadata -METADATA_TEMPLATE_ENGINE = {"OEP": __apply_fields_to_oep_metadata_template} +METADATA_TEMPLATE_ENGINE: dict[str, Callable] = {"OEP": __apply_fields_to_oep_metadata_template} diff --git a/tests/test_inspection.py b/tests/test_inspection.py index 7236c16..bd4a7c3 100644 --- a/tests/test_inspection.py +++ b/tests/test_inspection.py @@ -27,10 +27,10 @@ def test_inspection(): assert metadata["resources"][0]["schema"]["fields"][0]["type"] == "string" assert metadata["resources"][0]["schema"]["fields"][1]["type"] == "integer" - assert metadata["resources"][0]["schema"]["fields"][2]["type"] == "number" - assert metadata["resources"][0]["schema"]["fields"][3]["type"] == "array" - assert metadata["resources"][0]["schema"]["fields"][4]["type"] == "array" - assert metadata["resources"][0]["schema"]["fields"][5]["type"] == "array" + assert metadata["resources"][0]["schema"]["fields"][2]["type"] == "float" + assert metadata["resources"][0]["schema"]["fields"][3]["type"] == "array string" + assert metadata["resources"][0]["schema"]["fields"][4]["type"] == "array integer" + assert metadata["resources"][0]["schema"]["fields"][5]["type"] == "array float" assert metadata["resources"][0]["schema"]["fields"][6]["type"] == "object" assert metadata["resources"][0]["schema"]["fields"][7]["type"] == "date" assert metadata["resources"][0]["schema"]["fields"][8]["type"] == "boolean" From 2ede089d121bd5382155b093c69198bcada5f8a4 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Tue, 4 Jun 2024 15:57:36 +0200 Subject: [PATCH 16/63] Add license validation --- src/omi/data/README.md | 1 + src/omi/data/licenses.json | 8275 ++++++++++++++++++++++++++++++++++++ src/omi/license.py | 108 + 3 files changed, 8384 insertions(+) create mode 100644 src/omi/data/README.md create mode 100644 src/omi/data/licenses.json create mode 100644 src/omi/license.py diff --git a/src/omi/data/README.md b/src/omi/data/README.md new file mode 100644 index 0000000..81a437a --- /dev/null +++ b/src/omi/data/README.md @@ -0,0 +1 @@ +License information was downloaded form . diff --git a/src/omi/data/licenses.json b/src/omi/data/licenses.json new file mode 100644 index 0000000..eef5f67 --- /dev/null +++ b/src/omi/data/licenses.json @@ -0,0 +1,8275 @@ +{ + "licenseListVersion": "3.24.0", + "licenses": [ + { + "reference": "https://spdx.org/licenses/0BSD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/0BSD.json", + "referenceNumber": 537, + "name": "BSD Zero Clause License", + "licenseId": "0BSD", + "seeAlso": [ + "http://landley.net/toybox/license.html", + "https://opensource.org/licenses/0BSD" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/3D-Slicer-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/3D-Slicer-1.0.json", + "referenceNumber": 200, + "name": "3D Slicer License v1.0", + "licenseId": "3D-Slicer-1.0", + "seeAlso": [ + "https://slicer.org/LICENSE", + "https://github.com/Slicer/Slicer/blob/main/License.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/AAL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AAL.json", + "referenceNumber": 406, + "name": "Attribution Assurance License", + "licenseId": "AAL", + "seeAlso": [ + "https://opensource.org/licenses/attribution" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Abstyles.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Abstyles.json", + "referenceNumber": 526, + "name": "Abstyles License", + "licenseId": "Abstyles", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Abstyles" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/AdaCore-doc.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AdaCore-doc.json", + "referenceNumber": 382, + "name": "AdaCore Doc License", + "licenseId": "AdaCore-doc", + "seeAlso": [ + "https://github.com/AdaCore/xmlada/blob/master/docs/index.rst", + "https://github.com/AdaCore/gnatcoll-core/blob/master/docs/index.rst", + "https://github.com/AdaCore/gnatcoll-db/blob/master/docs/index.rst" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Adobe-2006.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Adobe-2006.json", + "referenceNumber": 558, + "name": "Adobe Systems Incorporated Source Code License Agreement", + "licenseId": "Adobe-2006", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/AdobeLicense" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Adobe-Display-PostScript.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Adobe-Display-PostScript.json", + "referenceNumber": 431, + "name": "Adobe Display PostScript License", + "licenseId": "Adobe-Display-PostScript", + "seeAlso": [ + "https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type\u003dheads#L752" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Adobe-Glyph.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Adobe-Glyph.json", + "referenceNumber": 297, + "name": "Adobe Glyph List License", + "licenseId": "Adobe-Glyph", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MIT#AdobeGlyph" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Adobe-Utopia.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Adobe-Utopia.json", + "referenceNumber": 532, + "name": "Adobe Utopia Font License", + "licenseId": "Adobe-Utopia", + "seeAlso": [ + "https://gitlab.freedesktop.org/xorg/font/adobe-utopia-100dpi/-/blob/master/COPYING?ref_type\u003dheads" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/ADSL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ADSL.json", + "referenceNumber": 463, + "name": "Amazon Digital Services License", + "licenseId": "ADSL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/AmazonDigitalServicesLicense" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/AFL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AFL-1.1.json", + "referenceNumber": 601, + "name": "Academic Free License v1.1", + "licenseId": "AFL-1.1", + "seeAlso": [ + "http://opensource.linux-mirror.org/licenses/afl-1.1.txt", + "http://wayback.archive.org/web/20021004124254/http://www.opensource.org/licenses/academic.php" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/AFL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AFL-1.2.json", + "referenceNumber": 72, + "name": "Academic Free License v1.2", + "licenseId": "AFL-1.2", + "seeAlso": [ + "http://opensource.linux-mirror.org/licenses/afl-1.2.txt", + "http://wayback.archive.org/web/20021204204652/http://www.opensource.org/licenses/academic.php" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/AFL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AFL-2.0.json", + "referenceNumber": 187, + "name": "Academic Free License v2.0", + "licenseId": "AFL-2.0", + "seeAlso": [ + "http://wayback.archive.org/web/20060924134533/http://www.opensource.org/licenses/afl-2.0.txt" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/AFL-2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AFL-2.1.json", + "referenceNumber": 383, + "name": "Academic Free License v2.1", + "licenseId": "AFL-2.1", + "seeAlso": [ + "http://opensource.linux-mirror.org/licenses/afl-2.1.txt" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/AFL-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AFL-3.0.json", + "referenceNumber": 369, + "name": "Academic Free License v3.0", + "licenseId": "AFL-3.0", + "seeAlso": [ + "http://www.rosenlaw.com/AFL3.0.htm", + "https://opensource.org/licenses/afl-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Afmparse.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Afmparse.json", + "referenceNumber": 345, + "name": "Afmparse License", + "licenseId": "Afmparse", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Afmparse" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/AGPL-1.0.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/AGPL-1.0.json", + "referenceNumber": 221, + "name": "Affero General Public License v1.0", + "licenseId": "AGPL-1.0", + "seeAlso": [ + "http://www.affero.org/oagpl.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/AGPL-1.0-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AGPL-1.0-only.json", + "referenceNumber": 334, + "name": "Affero General Public License v1.0 only", + "licenseId": "AGPL-1.0-only", + "seeAlso": [ + "http://www.affero.org/oagpl.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/AGPL-1.0-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AGPL-1.0-or-later.json", + "referenceNumber": 527, + "name": "Affero General Public License v1.0 or later", + "licenseId": "AGPL-1.0-or-later", + "seeAlso": [ + "http://www.affero.org/oagpl.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/AGPL-3.0.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/AGPL-3.0.json", + "referenceNumber": 394, + "name": "GNU Affero General Public License v3.0", + "licenseId": "AGPL-3.0", + "seeAlso": [ + "https://www.gnu.org/licenses/agpl.txt", + "https://opensource.org/licenses/AGPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/AGPL-3.0-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AGPL-3.0-only.json", + "referenceNumber": 123, + "name": "GNU Affero General Public License v3.0 only", + "licenseId": "AGPL-3.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/agpl.txt", + "https://opensource.org/licenses/AGPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/AGPL-3.0-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AGPL-3.0-or-later.json", + "referenceNumber": 105, + "name": "GNU Affero General Public License v3.0 or later", + "licenseId": "AGPL-3.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/agpl.txt", + "https://opensource.org/licenses/AGPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Aladdin.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Aladdin.json", + "referenceNumber": 168, + "name": "Aladdin Free Public License", + "licenseId": "Aladdin", + "seeAlso": [ + "http://pages.cs.wisc.edu/~ghost/doc/AFPL/6.01/Public.htm" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/AMD-newlib.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AMD-newlib.json", + "referenceNumber": 222, + "name": "AMD newlib License", + "licenseId": "AMD-newlib", + "seeAlso": [ + "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/sys/a29khif/_close.S;h\u003d04f52ae00de1dafbd9055ad8d73c5c697a3aae7f;hb\u003dHEAD" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/AMDPLPA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AMDPLPA.json", + "referenceNumber": 149, + "name": "AMD\u0027s plpa_map.c License", + "licenseId": "AMDPLPA", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/AMD_plpa_map_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/AML.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AML.json", + "referenceNumber": 13, + "name": "Apple MIT License", + "licenseId": "AML", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Apple_MIT_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/AML-glslang.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AML-glslang.json", + "referenceNumber": 1, + "name": "AML glslang variant License", + "licenseId": "AML-glslang", + "seeAlso": [ + "https://github.com/KhronosGroup/glslang/blob/main/LICENSE.txt#L949", + "https://docs.omniverse.nvidia.com/install-guide/latest/common/licenses.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/AMPAS.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/AMPAS.json", + "referenceNumber": 420, + "name": "Academy of Motion Picture Arts and Sciences BSD", + "licenseId": "AMPAS", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/BSD#AMPASBSD" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/ANTLR-PD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ANTLR-PD.json", + "referenceNumber": 576, + "name": "ANTLR Software Rights Notice", + "licenseId": "ANTLR-PD", + "seeAlso": [ + "http://www.antlr2.org/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/ANTLR-PD-fallback.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ANTLR-PD-fallback.json", + "referenceNumber": 194, + "name": "ANTLR Software Rights Notice with license fallback", + "licenseId": "ANTLR-PD-fallback", + "seeAlso": [ + "http://www.antlr2.org/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/any-OSI.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/any-OSI.json", + "referenceNumber": 121, + "name": "Any OSI License", + "licenseId": "any-OSI", + "seeAlso": [ + "https://metacpan.org/pod/Exporter::Tidy#LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Apache-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Apache-1.0.json", + "referenceNumber": 616, + "name": "Apache License 1.0", + "licenseId": "Apache-1.0", + "seeAlso": [ + "http://www.apache.org/licenses/LICENSE-1.0" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Apache-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Apache-1.1.json", + "referenceNumber": 313, + "name": "Apache License 1.1", + "licenseId": "Apache-1.1", + "seeAlso": [ + "http://apache.org/licenses/LICENSE-1.1", + "https://opensource.org/licenses/Apache-1.1" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Apache-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Apache-2.0.json", + "referenceNumber": 564, + "name": "Apache License 2.0", + "licenseId": "Apache-2.0", + "seeAlso": [ + "https://www.apache.org/licenses/LICENSE-2.0", + "https://opensource.org/licenses/Apache-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/APAFML.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/APAFML.json", + "referenceNumber": 136, + "name": "Adobe Postscript AFM License", + "licenseId": "APAFML", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/AdobePostscriptAFM" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/APL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/APL-1.0.json", + "referenceNumber": 515, + "name": "Adaptive Public License 1.0", + "licenseId": "APL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/APL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/App-s2p.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/App-s2p.json", + "referenceNumber": 470, + "name": "App::s2p License", + "licenseId": "App-s2p", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/App-s2p" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/APSL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/APSL-1.0.json", + "referenceNumber": 39, + "name": "Apple Public Source License 1.0", + "licenseId": "APSL-1.0", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Apple_Public_Source_License_1.0" + ], + "isOsiApproved": true, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/APSL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/APSL-1.1.json", + "referenceNumber": 582, + "name": "Apple Public Source License 1.1", + "licenseId": "APSL-1.1", + "seeAlso": [ + "http://www.opensource.apple.com/source/IOSerialFamily/IOSerialFamily-7/APPLE_LICENSE" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/APSL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/APSL-1.2.json", + "referenceNumber": 628, + "name": "Apple Public Source License 1.2", + "licenseId": "APSL-1.2", + "seeAlso": [ + "http://www.samurajdata.se/opensource/mirror/licenses/apsl.php" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/APSL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/APSL-2.0.json", + "referenceNumber": 144, + "name": "Apple Public Source License 2.0", + "licenseId": "APSL-2.0", + "seeAlso": [ + "http://www.opensource.apple.com/license/apsl/" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Arphic-1999.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Arphic-1999.json", + "referenceNumber": 131, + "name": "Arphic Public License", + "licenseId": "Arphic-1999", + "seeAlso": [ + "http://ftp.gnu.org/gnu/non-gnu/chinese-fonts-truetype/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Artistic-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Artistic-1.0.json", + "referenceNumber": 388, + "name": "Artistic License 1.0", + "licenseId": "Artistic-1.0", + "seeAlso": [ + "https://opensource.org/licenses/Artistic-1.0" + ], + "isOsiApproved": true, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/Artistic-1.0-cl8.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Artistic-1.0-cl8.json", + "referenceNumber": 321, + "name": "Artistic License 1.0 w/clause 8", + "licenseId": "Artistic-1.0-cl8", + "seeAlso": [ + "https://opensource.org/licenses/Artistic-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Artistic-1.0-Perl.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Artistic-1.0-Perl.json", + "referenceNumber": 652, + "name": "Artistic License 1.0 (Perl)", + "licenseId": "Artistic-1.0-Perl", + "seeAlso": [ + "http://dev.perl.org/licenses/artistic.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Artistic-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Artistic-2.0.json", + "referenceNumber": 355, + "name": "Artistic License 2.0", + "licenseId": "Artistic-2.0", + "seeAlso": [ + "http://www.perlfoundation.org/artistic_license_2_0", + "https://www.perlfoundation.org/artistic-license-20.html", + "https://opensource.org/licenses/artistic-license-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/ASWF-Digital-Assets-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ASWF-Digital-Assets-1.0.json", + "referenceNumber": 330, + "name": "ASWF Digital Assets License version 1.0", + "licenseId": "ASWF-Digital-Assets-1.0", + "seeAlso": [ + "https://github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.0.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/ASWF-Digital-Assets-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ASWF-Digital-Assets-1.1.json", + "referenceNumber": 447, + "name": "ASWF Digital Assets License 1.1", + "licenseId": "ASWF-Digital-Assets-1.1", + "seeAlso": [ + "https://github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.1.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Baekmuk.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Baekmuk.json", + "referenceNumber": 436, + "name": "Baekmuk License", + "licenseId": "Baekmuk", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing:Baekmuk?rd\u003dLicensing/Baekmuk" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Bahyph.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Bahyph.json", + "referenceNumber": 494, + "name": "Bahyph License", + "licenseId": "Bahyph", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Bahyph" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Barr.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Barr.json", + "referenceNumber": 48, + "name": "Barr License", + "licenseId": "Barr", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Barr" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/bcrypt-Solar-Designer.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/bcrypt-Solar-Designer.json", + "referenceNumber": 27, + "name": "bcrypt Solar Designer License", + "licenseId": "bcrypt-Solar-Designer", + "seeAlso": [ + "https://github.com/bcrypt-ruby/bcrypt-ruby/blob/master/ext/mri/crypt_blowfish.c" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Beerware.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Beerware.json", + "referenceNumber": 143, + "name": "Beerware License", + "licenseId": "Beerware", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Beerware", + "https://people.freebsd.org/~phk/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Bitstream-Charter.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Bitstream-Charter.json", + "referenceNumber": 560, + "name": "Bitstream Charter Font License", + "licenseId": "Bitstream-Charter", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Charter#License_Text", + "https://raw.githubusercontent.com/blackhole89/notekit/master/data/fonts/Charter%20license.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Bitstream-Vera.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Bitstream-Vera.json", + "referenceNumber": 581, + "name": "Bitstream Vera Font License", + "licenseId": "Bitstream-Vera", + "seeAlso": [ + "https://web.archive.org/web/20080207013128/http://www.gnome.org/fonts/", + "https://docubrain.com/sites/default/files/licenses/bitstream-vera.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BitTorrent-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BitTorrent-1.0.json", + "referenceNumber": 373, + "name": "BitTorrent Open Source License v1.0", + "licenseId": "BitTorrent-1.0", + "seeAlso": [ + "http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1\u003d1.1\u0026r2\u003d1.1.1.1\u0026diff_format\u003ds" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BitTorrent-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BitTorrent-1.1.json", + "referenceNumber": 288, + "name": "BitTorrent Open Source License v1.1", + "licenseId": "BitTorrent-1.1", + "seeAlso": [ + "http://directory.fsf.org/wiki/License:BitTorrentOSL1.1" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/blessing.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/blessing.json", + "referenceNumber": 469, + "name": "SQLite Blessing", + "licenseId": "blessing", + "seeAlso": [ + "https://www.sqlite.org/src/artifact/e33a4df7e32d742a?ln\u003d4-9", + "https://sqlite.org/src/artifact/df5091916dbb40e6" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BlueOak-1.0.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BlueOak-1.0.0.json", + "referenceNumber": 60, + "name": "Blue Oak Model License 1.0.0", + "licenseId": "BlueOak-1.0.0", + "seeAlso": [ + "https://blueoakcouncil.org/license/1.0.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Boehm-GC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Boehm-GC.json", + "referenceNumber": 322, + "name": "Boehm-Demers-Weiser GC License", + "licenseId": "Boehm-GC", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing:MIT#Another_Minimal_variant_(found_in_libatomic_ops)", + "https://github.com/uim/libgcroots/blob/master/COPYING", + "https://github.com/ivmai/libatomic_ops/blob/master/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Borceux.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Borceux.json", + "referenceNumber": 552, + "name": "Borceux license", + "licenseId": "Borceux", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Borceux" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Brian-Gladman-2-Clause.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Brian-Gladman-2-Clause.json", + "referenceNumber": 457, + "name": "Brian Gladman 2-Clause License", + "licenseId": "Brian-Gladman-2-Clause", + "seeAlso": [ + "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L140-L156", + "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Brian-Gladman-3-Clause.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Brian-Gladman-3-Clause.json", + "referenceNumber": 409, + "name": "Brian Gladman 3-Clause License", + "licenseId": "Brian-Gladman-3-Clause", + "seeAlso": [ + "https://github.com/SWI-Prolog/packages-clib/blob/master/sha1/brg_endian.h" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-1-Clause.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-1-Clause.json", + "referenceNumber": 567, + "name": "BSD 1-Clause License", + "licenseId": "BSD-1-Clause", + "seeAlso": [ + "https://svnweb.freebsd.org/base/head/include/ifaddrs.h?revision\u003d326823" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/BSD-2-Clause.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause.json", + "referenceNumber": 264, + "name": "BSD 2-Clause \"Simplified\" License", + "licenseId": "BSD-2-Clause", + "seeAlso": [ + "https://opensource.org/licenses/BSD-2-Clause" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/BSD-2-Clause-Darwin.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-Darwin.json", + "referenceNumber": 231, + "name": "BSD 2-Clause - Ian Darwin variant", + "licenseId": "BSD-2-Clause-Darwin", + "seeAlso": [ + "https://github.com/file/file/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-2-Clause-first-lines.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-first-lines.json", + "referenceNumber": 245, + "name": "BSD 2-Clause - first lines requirement", + "licenseId": "BSD-2-Clause-first-lines", + "seeAlso": [ + "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L664-L690", + "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.json", + "referenceNumber": 192, + "name": "BSD 2-Clause FreeBSD License", + "licenseId": "BSD-2-Clause-FreeBSD", + "seeAlso": [ + "http://www.freebsd.org/copyright/freebsd-license.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/BSD-2-Clause-NetBSD.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-NetBSD.json", + "referenceNumber": 449, + "name": "BSD 2-Clause NetBSD License", + "licenseId": "BSD-2-Clause-NetBSD", + "seeAlso": [ + "http://www.netbsd.org/about/redistribution.html#default" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/BSD-2-Clause-Patent.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-Patent.json", + "referenceNumber": 612, + "name": "BSD-2-Clause Plus Patent License", + "licenseId": "BSD-2-Clause-Patent", + "seeAlso": [ + "https://opensource.org/licenses/BSDplusPatent" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/BSD-2-Clause-Views.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-Views.json", + "referenceNumber": 657, + "name": "BSD 2-Clause with views sentence", + "licenseId": "BSD-2-Clause-Views", + "seeAlso": [ + "http://www.freebsd.org/copyright/freebsd-license.html", + "https://people.freebsd.org/~ivoras/wine/patch-wine-nvidia.sh", + "https://github.com/protegeproject/protege/blob/master/license.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause.json", + "referenceNumber": 216, + "name": "BSD 3-Clause \"New\" or \"Revised\" License", + "licenseId": "BSD-3-Clause", + "seeAlso": [ + "https://opensource.org/licenses/BSD-3-Clause", + "https://www.eclipse.org/org/documents/edl-v10.php" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-acpica.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-acpica.json", + "referenceNumber": 408, + "name": "BSD 3-Clause acpica variant", + "licenseId": "BSD-3-Clause-acpica", + "seeAlso": [ + "https://github.com/acpica/acpica/blob/master/source/common/acfileio.c#L119" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-Attribution.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-Attribution.json", + "referenceNumber": 14, + "name": "BSD with attribution", + "licenseId": "BSD-3-Clause-Attribution", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/BSD_with_Attribution" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-Clear.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-Clear.json", + "referenceNumber": 347, + "name": "BSD 3-Clause Clear License", + "licenseId": "BSD-3-Clause-Clear", + "seeAlso": [ + "http://labs.metacarta.com/license-explanation.html#license" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-flex.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-flex.json", + "referenceNumber": 211, + "name": "BSD 3-Clause Flex variant", + "licenseId": "BSD-3-Clause-flex", + "seeAlso": [ + "https://github.com/westes/flex/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-HP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-HP.json", + "referenceNumber": 210, + "name": "Hewlett-Packard BSD variant license", + "licenseId": "BSD-3-Clause-HP", + "seeAlso": [ + "https://github.com/zdohnal/hplip/blob/master/COPYING#L939" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-LBNL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-LBNL.json", + "referenceNumber": 597, + "name": "Lawrence Berkeley National Labs BSD variant license", + "licenseId": "BSD-3-Clause-LBNL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/LBNLBSD" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-Modification.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-Modification.json", + "referenceNumber": 364, + "name": "BSD 3-Clause Modification", + "licenseId": "BSD-3-Clause-Modification", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing:BSD#Modification_Variant" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-No-Military-License.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-No-Military-License.json", + "referenceNumber": 30, + "name": "BSD 3-Clause No Military License", + "licenseId": "BSD-3-Clause-No-Military-License", + "seeAlso": [ + "https://gitlab.syncad.com/hive/dhive/-/blob/master/LICENSE", + "https://github.com/greymass/swift-eosio/blob/master/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.json", + "referenceNumber": 21, + "name": "BSD 3-Clause No Nuclear License", + "licenseId": "BSD-3-Clause-No-Nuclear-License", + "seeAlso": [ + "http://download.oracle.com/otn-pub/java/licenses/bsd.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.json", + "referenceNumber": 543, + "name": "BSD 3-Clause No Nuclear License 2014", + "licenseId": "BSD-3-Clause-No-Nuclear-License-2014", + "seeAlso": [ + "https://java.net/projects/javaeetutorial/pages/BerkeleyLicense" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.json", + "referenceNumber": 402, + "name": "BSD 3-Clause No Nuclear Warranty", + "licenseId": "BSD-3-Clause-No-Nuclear-Warranty", + "seeAlso": [ + "https://jogamp.org/git/?p\u003dgluegen.git;a\u003dblob_plain;f\u003dLICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-Open-MPI.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-Open-MPI.json", + "referenceNumber": 376, + "name": "BSD 3-Clause Open MPI variant", + "licenseId": "BSD-3-Clause-Open-MPI", + "seeAlso": [ + "https://www.open-mpi.org/community/license.php", + "http://www.netlib.org/lapack/LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-3-Clause-Sun.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-Sun.json", + "referenceNumber": 554, + "name": "BSD 3-Clause Sun Microsystems", + "licenseId": "BSD-3-Clause-Sun", + "seeAlso": [ + "https://github.com/xmlark/msv/blob/b9316e2f2270bc1606952ea4939ec87fbba157f3/xsdlib/src/main/java/com/sun/msv/datatype/regexp/InternalImpl.java" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-4-Clause.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-4-Clause.json", + "referenceNumber": 650, + "name": "BSD 4-Clause \"Original\" or \"Old\" License", + "licenseId": "BSD-4-Clause", + "seeAlso": [ + "http://directory.fsf.org/wiki/License:BSD_4Clause" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/BSD-4-Clause-Shortened.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-4-Clause-Shortened.json", + "referenceNumber": 252, + "name": "BSD 4 Clause Shortened", + "licenseId": "BSD-4-Clause-Shortened", + "seeAlso": [ + "https://metadata.ftp-master.debian.org/changelogs//main/a/arpwatch/arpwatch_2.1a15-7_copyright" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-4-Clause-UC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-4-Clause-UC.json", + "referenceNumber": 117, + "name": "BSD-4-Clause (University of California-Specific)", + "licenseId": "BSD-4-Clause-UC", + "seeAlso": [ + "http://www.freebsd.org/copyright/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-4.3RENO.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-4.3RENO.json", + "referenceNumber": 298, + "name": "BSD 4.3 RENO License", + "licenseId": "BSD-4.3RENO", + "seeAlso": [ + "https://sourceware.org/git/?p\u003dbinutils-gdb.git;a\u003dblob;f\u003dlibiberty/strcasecmp.c;h\u003d131d81c2ce7881fa48c363dc5bf5fb302c61ce0b;hb\u003dHEAD", + "https://git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT#L55-63" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-4.3TAHOE.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-4.3TAHOE.json", + "referenceNumber": 0, + "name": "BSD 4.3 TAHOE License", + "licenseId": "BSD-4.3TAHOE", + "seeAlso": [ + "https://github.com/389ds/389-ds-base/blob/main/ldap/include/sysexits-compat.h#L15", + "https://git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id\u003da74c6b4ee49397cf330b333da1042bffa60ed14f#n1788" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-Advertising-Acknowledgement.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-Advertising-Acknowledgement.json", + "referenceNumber": 423, + "name": "BSD Advertising Acknowledgement License", + "licenseId": "BSD-Advertising-Acknowledgement", + "seeAlso": [ + "https://github.com/python-excel/xlrd/blob/master/LICENSE#L33" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-Attribution-HPND-disclaimer.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-Attribution-HPND-disclaimer.json", + "referenceNumber": 171, + "name": "BSD with Attribution and HPND disclaimer", + "licenseId": "BSD-Attribution-HPND-disclaimer", + "seeAlso": [ + "https://github.com/cyrusimap/cyrus-sasl/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-Inferno-Nettverk.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-Inferno-Nettverk.json", + "referenceNumber": 401, + "name": "BSD-Inferno-Nettverk", + "licenseId": "BSD-Inferno-Nettverk", + "seeAlso": [ + "https://www.inet.no/dante/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-Protection.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-Protection.json", + "referenceNumber": 403, + "name": "BSD Protection License", + "licenseId": "BSD-Protection", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/BSD_Protection_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-Source-beginning-file.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-Source-beginning-file.json", + "referenceNumber": 97, + "name": "BSD Source Code Attribution - beginning of file variant", + "licenseId": "BSD-Source-beginning-file", + "seeAlso": [ + "https://github.com/lattera/freebsd/blob/master/sys/cam/cam.c#L4" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-Source-Code.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-Source-Code.json", + "referenceNumber": 22, + "name": "BSD Source Code Attribution", + "licenseId": "BSD-Source-Code", + "seeAlso": [ + "https://github.com/robbiehanson/CocoaHTTPServer/blob/master/LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-Systemics.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-Systemics.json", + "referenceNumber": 178, + "name": "Systemics BSD variant license", + "licenseId": "BSD-Systemics", + "seeAlso": [ + "https://metacpan.org/release/DPARIS/Crypt-DES-2.07/source/COPYRIGHT" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSD-Systemics-W3Works.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSD-Systemics-W3Works.json", + "referenceNumber": 350, + "name": "Systemics W3Works BSD variant license", + "licenseId": "BSD-Systemics-W3Works", + "seeAlso": [ + "https://metacpan.org/release/DPARIS/Crypt-Blowfish-2.14/source/COPYRIGHT#L7" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/BSL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BSL-1.0.json", + "referenceNumber": 514, + "name": "Boost Software License 1.0", + "licenseId": "BSL-1.0", + "seeAlso": [ + "http://www.boost.org/LICENSE_1_0.txt", + "https://opensource.org/licenses/BSL-1.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/BUSL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/BUSL-1.1.json", + "referenceNumber": 549, + "name": "Business Source License 1.1", + "licenseId": "BUSL-1.1", + "seeAlso": [ + "https://mariadb.com/bsl11/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/bzip2-1.0.5.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/bzip2-1.0.5.json", + "referenceNumber": 419, + "name": "bzip2 and libbzip2 License v1.0.5", + "licenseId": "bzip2-1.0.5", + "seeAlso": [ + "https://sourceware.org/bzip2/1.0.5/bzip2-manual-1.0.5.html", + "http://bzip.org/1.0.5/bzip2-manual-1.0.5.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/bzip2-1.0.6.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/bzip2-1.0.6.json", + "referenceNumber": 396, + "name": "bzip2 and libbzip2 License v1.0.6", + "licenseId": "bzip2-1.0.6", + "seeAlso": [ + "https://sourceware.org/git/?p\u003dbzip2.git;a\u003dblob;f\u003dLICENSE;hb\u003dbzip2-1.0.6", + "http://bzip.org/1.0.5/bzip2-manual-1.0.5.html", + "https://sourceware.org/cgit/valgrind/tree/mpi/libmpiwrap.c" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/C-UDA-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/C-UDA-1.0.json", + "referenceNumber": 432, + "name": "Computational Use of Data Agreement v1.0", + "licenseId": "C-UDA-1.0", + "seeAlso": [ + "https://github.com/microsoft/Computational-Use-of-Data-Agreement/blob/master/C-UDA-1.0.md", + "https://cdla.dev/computational-use-of-data-agreement-v1-0/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CAL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CAL-1.0.json", + "referenceNumber": 653, + "name": "Cryptographic Autonomy License 1.0", + "licenseId": "CAL-1.0", + "seeAlso": [ + "http://cryptographicautonomylicense.com/license-text.html", + "https://opensource.org/licenses/CAL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/CAL-1.0-Combined-Work-Exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CAL-1.0-Combined-Work-Exception.json", + "referenceNumber": 217, + "name": "Cryptographic Autonomy License 1.0 (Combined Work Exception)", + "licenseId": "CAL-1.0-Combined-Work-Exception", + "seeAlso": [ + "http://cryptographicautonomylicense.com/license-text.html", + "https://opensource.org/licenses/CAL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Caldera.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Caldera.json", + "referenceNumber": 622, + "name": "Caldera License", + "licenseId": "Caldera", + "seeAlso": [ + "http://www.lemis.com/grog/UNIX/ancient-source-all.pdf" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Caldera-no-preamble.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Caldera-no-preamble.json", + "referenceNumber": 585, + "name": "Caldera License (without preamble)", + "licenseId": "Caldera-no-preamble", + "seeAlso": [ + "https://github.com/apache/apr/blob/trunk/LICENSE#L298C6-L298C29" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Catharon.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Catharon.json", + "referenceNumber": 45, + "name": "Catharon License", + "licenseId": "Catharon", + "seeAlso": [ + "https://github.com/scummvm/scummvm/blob/v2.8.0/LICENSES/CatharonLicense.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CATOSL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CATOSL-1.1.json", + "referenceNumber": 193, + "name": "Computer Associates Trusted Open Source License 1.1", + "licenseId": "CATOSL-1.1", + "seeAlso": [ + "https://opensource.org/licenses/CATOSL-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/CC-BY-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-1.0.json", + "referenceNumber": 37, + "name": "Creative Commons Attribution 1.0 Generic", + "licenseId": "CC-BY-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-2.0.json", + "referenceNumber": 241, + "name": "Creative Commons Attribution 2.0 Generic", + "licenseId": "CC-BY-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by/2.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-2.5.json", + "referenceNumber": 129, + "name": "Creative Commons Attribution 2.5 Generic", + "licenseId": "CC-BY-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by/2.5/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-2.5-AU.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-2.5-AU.json", + "referenceNumber": 583, + "name": "Creative Commons Attribution 2.5 Australia", + "licenseId": "CC-BY-2.5-AU", + "seeAlso": [ + "https://creativecommons.org/licenses/by/2.5/au/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0.json", + "referenceNumber": 302, + "name": "Creative Commons Attribution 3.0 Unported", + "licenseId": "CC-BY-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by/3.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-3.0-AT.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-AT.json", + "referenceNumber": 324, + "name": "Creative Commons Attribution 3.0 Austria", + "licenseId": "CC-BY-3.0-AT", + "seeAlso": [ + "https://creativecommons.org/licenses/by/3.0/at/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-3.0-AU.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-AU.json", + "referenceNumber": 342, + "name": "Creative Commons Attribution 3.0 Australia", + "licenseId": "CC-BY-3.0-AU", + "seeAlso": [ + "https://creativecommons.org/licenses/by/3.0/au/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-3.0-DE.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-DE.json", + "referenceNumber": 239, + "name": "Creative Commons Attribution 3.0 Germany", + "licenseId": "CC-BY-3.0-DE", + "seeAlso": [ + "https://creativecommons.org/licenses/by/3.0/de/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-3.0-IGO.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-IGO.json", + "referenceNumber": 19, + "name": "Creative Commons Attribution 3.0 IGO", + "licenseId": "CC-BY-3.0-IGO", + "seeAlso": [ + "https://creativecommons.org/licenses/by/3.0/igo/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-3.0-NL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-NL.json", + "referenceNumber": 501, + "name": "Creative Commons Attribution 3.0 Netherlands", + "licenseId": "CC-BY-3.0-NL", + "seeAlso": [ + "https://creativecommons.org/licenses/by/3.0/nl/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-3.0-US.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-US.json", + "referenceNumber": 569, + "name": "Creative Commons Attribution 3.0 United States", + "licenseId": "CC-BY-3.0-US", + "seeAlso": [ + "https://creativecommons.org/licenses/by/3.0/us/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-4.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-4.0.json", + "referenceNumber": 265, + "name": "Creative Commons Attribution 4.0 International", + "licenseId": "CC-BY-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by/4.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-1.0.json", + "referenceNumber": 167, + "name": "Creative Commons Attribution Non Commercial 1.0 Generic", + "licenseId": "CC-BY-NC-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/1.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-2.0.json", + "referenceNumber": 92, + "name": "Creative Commons Attribution Non Commercial 2.0 Generic", + "licenseId": "CC-BY-NC-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/2.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-2.5.json", + "referenceNumber": 253, + "name": "Creative Commons Attribution Non Commercial 2.5 Generic", + "licenseId": "CC-BY-NC-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/2.5/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-3.0.json", + "referenceNumber": 199, + "name": "Creative Commons Attribution Non Commercial 3.0 Unported", + "licenseId": "CC-BY-NC-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/3.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-3.0-DE.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-3.0-DE.json", + "referenceNumber": 429, + "name": "Creative Commons Attribution Non Commercial 3.0 Germany", + "licenseId": "CC-BY-NC-3.0-DE", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/3.0/de/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-4.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-4.0.json", + "referenceNumber": 188, + "name": "Creative Commons Attribution Non Commercial 4.0 International", + "licenseId": "CC-BY-NC-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc/4.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-ND-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-1.0.json", + "referenceNumber": 365, + "name": "Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic", + "licenseId": "CC-BY-NC-ND-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd-nc/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-ND-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-2.0.json", + "referenceNumber": 416, + "name": "Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic", + "licenseId": "CC-BY-NC-ND-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-nd/2.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-ND-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-2.5.json", + "referenceNumber": 58, + "name": "Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic", + "licenseId": "CC-BY-NC-ND-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-nd/2.5/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-ND-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-3.0.json", + "referenceNumber": 213, + "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported", + "licenseId": "CC-BY-NC-ND-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-nd/3.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-ND-3.0-DE.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-3.0-DE.json", + "referenceNumber": 84, + "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0 Germany", + "licenseId": "CC-BY-NC-ND-3.0-DE", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-nd/3.0/de/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-ND-3.0-IGO.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-3.0-IGO.json", + "referenceNumber": 587, + "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0 IGO", + "licenseId": "CC-BY-NC-ND-3.0-IGO", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-nd/3.0/igo/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-ND-4.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-4.0.json", + "referenceNumber": 296, + "name": "Creative Commons Attribution Non Commercial No Derivatives 4.0 International", + "licenseId": "CC-BY-NC-ND-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-SA-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-1.0.json", + "referenceNumber": 170, + "name": "Creative Commons Attribution Non Commercial Share Alike 1.0 Generic", + "licenseId": "CC-BY-NC-SA-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-SA-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-2.0.json", + "referenceNumber": 484, + "name": "Creative Commons Attribution Non Commercial Share Alike 2.0 Generic", + "licenseId": "CC-BY-NC-SA-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/2.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-DE.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-DE.json", + "referenceNumber": 184, + "name": "Creative Commons Attribution Non Commercial Share Alike 2.0 Germany", + "licenseId": "CC-BY-NC-SA-2.0-DE", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/2.0/de/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-FR.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-FR.json", + "referenceNumber": 116, + "name": "Creative Commons Attribution-NonCommercial-ShareAlike 2.0 France", + "licenseId": "CC-BY-NC-SA-2.0-FR", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/2.0/fr/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-UK.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-UK.json", + "referenceNumber": 415, + "name": "Creative Commons Attribution Non Commercial Share Alike 2.0 England and Wales", + "licenseId": "CC-BY-NC-SA-2.0-UK", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/2.0/uk/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-SA-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-2.5.json", + "referenceNumber": 106, + "name": "Creative Commons Attribution Non Commercial Share Alike 2.5 Generic", + "licenseId": "CC-BY-NC-SA-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/2.5/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-SA-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-3.0.json", + "referenceNumber": 323, + "name": "Creative Commons Attribution Non Commercial Share Alike 3.0 Unported", + "licenseId": "CC-BY-NC-SA-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-SA-3.0-DE.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-3.0-DE.json", + "referenceNumber": 150, + "name": "Creative Commons Attribution Non Commercial Share Alike 3.0 Germany", + "licenseId": "CC-BY-NC-SA-3.0-DE", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/3.0/de/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-SA-3.0-IGO.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-3.0-IGO.json", + "referenceNumber": 295, + "name": "Creative Commons Attribution Non Commercial Share Alike 3.0 IGO", + "licenseId": "CC-BY-NC-SA-3.0-IGO", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/3.0/igo/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-NC-SA-4.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-4.0.json", + "referenceNumber": 351, + "name": "Creative Commons Attribution Non Commercial Share Alike 4.0 International", + "licenseId": "CC-BY-NC-SA-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-ND-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-1.0.json", + "referenceNumber": 56, + "name": "Creative Commons Attribution No Derivatives 1.0 Generic", + "licenseId": "CC-BY-ND-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/1.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-ND-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-2.0.json", + "referenceNumber": 640, + "name": "Creative Commons Attribution No Derivatives 2.0 Generic", + "licenseId": "CC-BY-ND-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/2.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-ND-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-2.5.json", + "referenceNumber": 276, + "name": "Creative Commons Attribution No Derivatives 2.5 Generic", + "licenseId": "CC-BY-ND-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/2.5/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-ND-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-3.0.json", + "referenceNumber": 173, + "name": "Creative Commons Attribution No Derivatives 3.0 Unported", + "licenseId": "CC-BY-ND-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/3.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-ND-3.0-DE.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-3.0-DE.json", + "referenceNumber": 525, + "name": "Creative Commons Attribution No Derivatives 3.0 Germany", + "licenseId": "CC-BY-ND-3.0-DE", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/3.0/de/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-ND-4.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-4.0.json", + "referenceNumber": 328, + "name": "Creative Commons Attribution No Derivatives 4.0 International", + "licenseId": "CC-BY-ND-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-nd/4.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-SA-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-1.0.json", + "referenceNumber": 453, + "name": "Creative Commons Attribution Share Alike 1.0 Generic", + "licenseId": "CC-BY-SA-1.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/1.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-SA-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-2.0.json", + "referenceNumber": 174, + "name": "Creative Commons Attribution Share Alike 2.0 Generic", + "licenseId": "CC-BY-SA-2.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/2.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-SA-2.0-UK.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-2.0-UK.json", + "referenceNumber": 387, + "name": "Creative Commons Attribution Share Alike 2.0 England and Wales", + "licenseId": "CC-BY-SA-2.0-UK", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/2.0/uk/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-SA-2.1-JP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-2.1-JP.json", + "referenceNumber": 471, + "name": "Creative Commons Attribution Share Alike 2.1 Japan", + "licenseId": "CC-BY-SA-2.1-JP", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/2.1/jp/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-SA-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-2.5.json", + "referenceNumber": 461, + "name": "Creative Commons Attribution Share Alike 2.5 Generic", + "licenseId": "CC-BY-SA-2.5", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/2.5/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-SA-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-3.0.json", + "referenceNumber": 621, + "name": "Creative Commons Attribution Share Alike 3.0 Unported", + "licenseId": "CC-BY-SA-3.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/3.0/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-SA-3.0-AT.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-3.0-AT.json", + "referenceNumber": 31, + "name": "Creative Commons Attribution Share Alike 3.0 Austria", + "licenseId": "CC-BY-SA-3.0-AT", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/3.0/at/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-SA-3.0-DE.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-3.0-DE.json", + "referenceNumber": 325, + "name": "Creative Commons Attribution Share Alike 3.0 Germany", + "licenseId": "CC-BY-SA-3.0-DE", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/3.0/de/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-SA-3.0-IGO.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-3.0-IGO.json", + "referenceNumber": 20, + "name": "Creative Commons Attribution-ShareAlike 3.0 IGO", + "licenseId": "CC-BY-SA-3.0-IGO", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/3.0/igo/legalcode" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC-BY-SA-4.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-4.0.json", + "referenceNumber": 155, + "name": "Creative Commons Attribution Share Alike 4.0 International", + "licenseId": "CC-BY-SA-4.0", + "seeAlso": [ + "https://creativecommons.org/licenses/by-sa/4.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/CC-PDDC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC-PDDC.json", + "referenceNumber": 349, + "name": "Creative Commons Public Domain Dedication and Certification", + "licenseId": "CC-PDDC", + "seeAlso": [ + "https://creativecommons.org/licenses/publicdomain/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CC0-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CC0-1.0.json", + "referenceNumber": 70, + "name": "Creative Commons Zero v1.0 Universal", + "licenseId": "CC0-1.0", + "seeAlso": [ + "https://creativecommons.org/publicdomain/zero/1.0/legalcode" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/CDDL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CDDL-1.0.json", + "referenceNumber": 282, + "name": "Common Development and Distribution License 1.0", + "licenseId": "CDDL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/cddl1" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/CDDL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CDDL-1.1.json", + "referenceNumber": 130, + "name": "Common Development and Distribution License 1.1", + "licenseId": "CDDL-1.1", + "seeAlso": [ + "http://glassfish.java.net/public/CDDL+GPL_1_1.html", + "https://javaee.github.io/glassfish/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CDL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CDL-1.0.json", + "referenceNumber": 592, + "name": "Common Documentation License 1.0", + "licenseId": "CDL-1.0", + "seeAlso": [ + "http://www.opensource.apple.com/cdl/", + "https://fedoraproject.org/wiki/Licensing/Common_Documentation_License", + "https://www.gnu.org/licenses/license-list.html#ACDL" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CDLA-Permissive-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CDLA-Permissive-1.0.json", + "referenceNumber": 551, + "name": "Community Data License Agreement Permissive 1.0", + "licenseId": "CDLA-Permissive-1.0", + "seeAlso": [ + "https://cdla.io/permissive-1-0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CDLA-Permissive-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CDLA-Permissive-2.0.json", + "referenceNumber": 319, + "name": "Community Data License Agreement Permissive 2.0", + "licenseId": "CDLA-Permissive-2.0", + "seeAlso": [ + "https://cdla.dev/permissive-2-0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CDLA-Sharing-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CDLA-Sharing-1.0.json", + "referenceNumber": 445, + "name": "Community Data License Agreement Sharing 1.0", + "licenseId": "CDLA-Sharing-1.0", + "seeAlso": [ + "https://cdla.io/sharing-1-0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CECILL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CECILL-1.0.json", + "referenceNumber": 219, + "name": "CeCILL Free Software License Agreement v1.0", + "licenseId": "CECILL-1.0", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL_V1-fr.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CECILL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CECILL-1.1.json", + "referenceNumber": 38, + "name": "CeCILL Free Software License Agreement v1.1", + "licenseId": "CECILL-1.1", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL_V1.1-US.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CECILL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CECILL-2.0.json", + "referenceNumber": 73, + "name": "CeCILL Free Software License Agreement v2.0", + "licenseId": "CECILL-2.0", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL_V2-en.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/CECILL-2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CECILL-2.1.json", + "referenceNumber": 393, + "name": "CeCILL Free Software License Agreement v2.1", + "licenseId": "CECILL-2.1", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/CECILL-B.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CECILL-B.json", + "referenceNumber": 354, + "name": "CeCILL-B Free Software License Agreement", + "licenseId": "CECILL-B", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/CECILL-C.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CECILL-C.json", + "referenceNumber": 271, + "name": "CeCILL-C Free Software License Agreement", + "licenseId": "CECILL-C", + "seeAlso": [ + "http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/CERN-OHL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CERN-OHL-1.1.json", + "referenceNumber": 32, + "name": "CERN Open Hardware Licence v1.1", + "licenseId": "CERN-OHL-1.1", + "seeAlso": [ + "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CERN-OHL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CERN-OHL-1.2.json", + "referenceNumber": 95, + "name": "CERN Open Hardware Licence v1.2", + "licenseId": "CERN-OHL-1.2", + "seeAlso": [ + "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CERN-OHL-P-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CERN-OHL-P-2.0.json", + "referenceNumber": 198, + "name": "CERN Open Hardware Licence Version 2 - Permissive", + "licenseId": "CERN-OHL-P-2.0", + "seeAlso": [ + "https://www.ohwr.org/project/cernohl/wikis/Documents/CERN-OHL-version-2" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/CERN-OHL-S-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CERN-OHL-S-2.0.json", + "referenceNumber": 370, + "name": "CERN Open Hardware Licence Version 2 - Strongly Reciprocal", + "licenseId": "CERN-OHL-S-2.0", + "seeAlso": [ + "https://www.ohwr.org/project/cernohl/wikis/Documents/CERN-OHL-version-2" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/CERN-OHL-W-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CERN-OHL-W-2.0.json", + "referenceNumber": 82, + "name": "CERN Open Hardware Licence Version 2 - Weakly Reciprocal", + "licenseId": "CERN-OHL-W-2.0", + "seeAlso": [ + "https://www.ohwr.org/project/cernohl/wikis/Documents/CERN-OHL-version-2" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/CFITSIO.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CFITSIO.json", + "referenceNumber": 96, + "name": "CFITSIO License", + "licenseId": "CFITSIO", + "seeAlso": [ + "https://heasarc.gsfc.nasa.gov/docs/software/fitsio/c/f_user/node9.html", + "https://heasarc.gsfc.nasa.gov/docs/software/ftools/fv/doc/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/check-cvs.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/check-cvs.json", + "referenceNumber": 521, + "name": "check-cvs License", + "licenseId": "check-cvs", + "seeAlso": [ + "http://cvs.savannah.gnu.org/viewvc/cvs/ccvs/contrib/check_cvs.in?revision\u003d1.1.4.3\u0026view\u003dmarkup\u0026pathrev\u003dcvs1-11-23#l2" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/checkmk.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/checkmk.json", + "referenceNumber": 272, + "name": "Checkmk License", + "licenseId": "checkmk", + "seeAlso": [ + "https://github.com/libcheck/check/blob/master/checkmk/checkmk.in" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/ClArtistic.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ClArtistic.json", + "referenceNumber": 66, + "name": "Clarified Artistic License", + "licenseId": "ClArtistic", + "seeAlso": [ + "http://gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/", + "http://www.ncftp.com/ncftp/doc/LICENSE.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Clips.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Clips.json", + "referenceNumber": 450, + "name": "Clips License", + "licenseId": "Clips", + "seeAlso": [ + "https://github.com/DrItanium/maya/blob/master/LICENSE.CLIPS" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CMU-Mach.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CMU-Mach.json", + "referenceNumber": 410, + "name": "CMU Mach License", + "licenseId": "CMU-Mach", + "seeAlso": [ + "https://www.cs.cmu.edu/~410/licenses.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CMU-Mach-nodoc.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CMU-Mach-nodoc.json", + "referenceNumber": 111, + "name": "CMU Mach - no notices-in-documentation variant", + "licenseId": "CMU-Mach-nodoc", + "seeAlso": [ + "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L718-L728", + "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CNRI-Jython.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CNRI-Jython.json", + "referenceNumber": 509, + "name": "CNRI Jython License", + "licenseId": "CNRI-Jython", + "seeAlso": [ + "http://www.jython.org/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CNRI-Python.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CNRI-Python.json", + "referenceNumber": 611, + "name": "CNRI Python License", + "licenseId": "CNRI-Python", + "seeAlso": [ + "https://opensource.org/licenses/CNRI-Python" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/CNRI-Python-GPL-Compatible.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CNRI-Python-GPL-Compatible.json", + "referenceNumber": 504, + "name": "CNRI Python Open Source GPL Compatible License Agreement", + "licenseId": "CNRI-Python-GPL-Compatible", + "seeAlso": [ + "http://www.python.org/download/releases/1.6.1/download_win/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/COIL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/COIL-1.0.json", + "referenceNumber": 286, + "name": "Copyfree Open Innovation License", + "licenseId": "COIL-1.0", + "seeAlso": [ + "https://coil.apotheon.org/plaintext/01.0.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Community-Spec-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Community-Spec-1.0.json", + "referenceNumber": 631, + "name": "Community Specification License 1.0", + "licenseId": "Community-Spec-1.0", + "seeAlso": [ + "https://github.com/CommunitySpecification/1.0/blob/master/1._Community_Specification_License-v1.md" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Condor-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Condor-1.1.json", + "referenceNumber": 251, + "name": "Condor Public License v1.1", + "licenseId": "Condor-1.1", + "seeAlso": [ + "http://research.cs.wisc.edu/condor/license.html#condor", + "http://web.archive.org/web/20111123062036/http://research.cs.wisc.edu/condor/license.html#condor" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/copyleft-next-0.3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/copyleft-next-0.3.0.json", + "referenceNumber": 421, + "name": "copyleft-next 0.3.0", + "licenseId": "copyleft-next-0.3.0", + "seeAlso": [ + "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/copyleft-next-0.3.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/copyleft-next-0.3.1.json", + "referenceNumber": 119, + "name": "copyleft-next 0.3.1", + "licenseId": "copyleft-next-0.3.1", + "seeAlso": [ + "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.1" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Cornell-Lossless-JPEG.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Cornell-Lossless-JPEG.json", + "referenceNumber": 632, + "name": "Cornell Lossless JPEG License", + "licenseId": "Cornell-Lossless-JPEG", + "seeAlso": [ + "https://android.googlesource.com/platform/external/dng_sdk/+/refs/heads/master/source/dng_lossless_jpeg.cpp#16", + "https://www.mssl.ucl.ac.uk/~mcrw/src/20050920/proto.h", + "https://gitlab.freedesktop.org/libopenraw/libopenraw/blob/master/lib/ljpegdecompressor.cpp#L32" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CPAL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CPAL-1.0.json", + "referenceNumber": 315, + "name": "Common Public Attribution License 1.0", + "licenseId": "CPAL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/CPAL-1.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/CPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CPL-1.0.json", + "referenceNumber": 135, + "name": "Common Public License 1.0", + "licenseId": "CPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/CPL-1.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/CPOL-1.02.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CPOL-1.02.json", + "referenceNumber": 479, + "name": "Code Project Open License 1.02", + "licenseId": "CPOL-1.02", + "seeAlso": [ + "http://www.codeproject.com/info/cpol10.aspx" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/Cronyx.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Cronyx.json", + "referenceNumber": 377, + "name": "Cronyx License", + "licenseId": "Cronyx", + "seeAlso": [ + "https://gitlab.freedesktop.org/xorg/font/alias/-/blob/master/COPYING", + "https://gitlab.freedesktop.org/xorg/font/cronyx-cyrillic/-/blob/master/COPYING", + "https://gitlab.freedesktop.org/xorg/font/misc-cyrillic/-/blob/master/COPYING", + "https://gitlab.freedesktop.org/xorg/font/screen-cyrillic/-/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Crossword.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Crossword.json", + "referenceNumber": 340, + "name": "Crossword License", + "licenseId": "Crossword", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Crossword" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CrystalStacker.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CrystalStacker.json", + "referenceNumber": 593, + "name": "CrystalStacker License", + "licenseId": "CrystalStacker", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing:CrystalStacker?rd\u003dLicensing/CrystalStacker" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/CUA-OPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/CUA-OPL-1.0.json", + "referenceNumber": 553, + "name": "CUA Office Public License v1.0", + "licenseId": "CUA-OPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/CUA-OPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Cube.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Cube.json", + "referenceNumber": 404, + "name": "Cube License", + "licenseId": "Cube", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Cube" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/curl.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/curl.json", + "referenceNumber": 604, + "name": "curl License", + "licenseId": "curl", + "seeAlso": [ + "https://github.com/bagder/curl/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/cve-tou.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/cve-tou.json", + "referenceNumber": 306, + "name": "Common Vulnerability Enumeration ToU License", + "licenseId": "cve-tou", + "seeAlso": [ + "https://www.cve.org/Legal/TermsOfUse" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/D-FSL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/D-FSL-1.0.json", + "referenceNumber": 154, + "name": "Deutsche Freie Software Lizenz", + "licenseId": "D-FSL-1.0", + "seeAlso": [ + "http://www.dipp.nrw.de/d-fsl/lizenzen/", + "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/de/D-FSL-1_0_de.txt", + "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/en/D-FSL-1_0_en.txt", + "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl", + "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/deutsche-freie-software-lizenz", + "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/german-free-software-license", + "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_de.txt/at_download/file", + "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_en.txt/at_download/file" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/DEC-3-Clause.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/DEC-3-Clause.json", + "referenceNumber": 15, + "name": "DEC 3-Clause License", + "licenseId": "DEC-3-Clause", + "seeAlso": [ + "https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type\u003dheads#L239" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/diffmark.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/diffmark.json", + "referenceNumber": 292, + "name": "diffmark license", + "licenseId": "diffmark", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/diffmark" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/DL-DE-BY-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/DL-DE-BY-2.0.json", + "referenceNumber": 225, + "name": "Data licence Germany – attribution – version 2.0", + "licenseId": "DL-DE-BY-2.0", + "seeAlso": [ + "https://www.govdata.de/dl-de/by-2-0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/DL-DE-ZERO-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/DL-DE-ZERO-2.0.json", + "referenceNumber": 341, + "name": "Data licence Germany – zero – version 2.0", + "licenseId": "DL-DE-ZERO-2.0", + "seeAlso": [ + "https://www.govdata.de/dl-de/zero-2-0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/DOC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/DOC.json", + "referenceNumber": 397, + "name": "DOC License", + "licenseId": "DOC", + "seeAlso": [ + "http://www.cs.wustl.edu/~schmidt/ACE-copying.html", + "https://www.dre.vanderbilt.edu/~schmidt/ACE-copying.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Dotseqn.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Dotseqn.json", + "referenceNumber": 132, + "name": "Dotseqn License", + "licenseId": "Dotseqn", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Dotseqn" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/DRL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/DRL-1.0.json", + "referenceNumber": 16, + "name": "Detection Rule License 1.0", + "licenseId": "DRL-1.0", + "seeAlso": [ + "https://github.com/Neo23x0/sigma/blob/master/LICENSE.Detection.Rules.md" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/DRL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/DRL-1.1.json", + "referenceNumber": 278, + "name": "Detection Rule License 1.1", + "licenseId": "DRL-1.1", + "seeAlso": [ + "https://github.com/SigmaHQ/Detection-Rule-License/blob/6ec7fbde6101d101b5b5d1fcb8f9b69fbc76c04a/LICENSE.Detection.Rules.md" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/DSDP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/DSDP.json", + "referenceNumber": 485, + "name": "DSDP License", + "licenseId": "DSDP", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/DSDP" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/dtoa.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/dtoa.json", + "referenceNumber": 358, + "name": "David M. Gay dtoa License", + "licenseId": "dtoa", + "seeAlso": [ + "https://github.com/SWI-Prolog/swipl-devel/blob/master/src/os/dtoa.c", + "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/stdlib/mprec.h;hb\u003dHEAD" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/dvipdfm.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/dvipdfm.json", + "referenceNumber": 100, + "name": "dvipdfm License", + "licenseId": "dvipdfm", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/dvipdfm" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/ECL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ECL-1.0.json", + "referenceNumber": 124, + "name": "Educational Community License v1.0", + "licenseId": "ECL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/ECL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/ECL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ECL-2.0.json", + "referenceNumber": 361, + "name": "Educational Community License v2.0", + "licenseId": "ECL-2.0", + "seeAlso": [ + "https://opensource.org/licenses/ECL-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/eCos-2.0.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/eCos-2.0.json", + "referenceNumber": 372, + "name": "eCos license version 2.0", + "licenseId": "eCos-2.0", + "seeAlso": [ + "https://www.gnu.org/licenses/ecos-license.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/EFL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/EFL-1.0.json", + "referenceNumber": 335, + "name": "Eiffel Forum License v1.0", + "licenseId": "EFL-1.0", + "seeAlso": [ + "http://www.eiffel-nice.org/license/forum.txt", + "https://opensource.org/licenses/EFL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/EFL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/EFL-2.0.json", + "referenceNumber": 88, + "name": "Eiffel Forum License v2.0", + "licenseId": "EFL-2.0", + "seeAlso": [ + "http://www.eiffel-nice.org/license/eiffel-forum-license-2.html", + "https://opensource.org/licenses/EFL-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/eGenix.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/eGenix.json", + "referenceNumber": 261, + "name": "eGenix.com Public License 1.1.0", + "licenseId": "eGenix", + "seeAlso": [ + "http://www.egenix.com/products/eGenix.com-Public-License-1.1.0.pdf", + "https://fedoraproject.org/wiki/Licensing/eGenix.com_Public_License_1.1.0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Elastic-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Elastic-2.0.json", + "referenceNumber": 147, + "name": "Elastic License 2.0", + "licenseId": "Elastic-2.0", + "seeAlso": [ + "https://www.elastic.co/licensing/elastic-license", + "https://github.com/elastic/elasticsearch/blob/master/licenses/ELASTIC-LICENSE-2.0.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Entessa.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Entessa.json", + "referenceNumber": 546, + "name": "Entessa Public License v1.0", + "licenseId": "Entessa", + "seeAlso": [ + "https://opensource.org/licenses/Entessa" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/EPICS.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/EPICS.json", + "referenceNumber": 120, + "name": "EPICS Open License", + "licenseId": "EPICS", + "seeAlso": [ + "https://epics.anl.gov/license/open.php" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/EPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/EPL-1.0.json", + "referenceNumber": 500, + "name": "Eclipse Public License 1.0", + "licenseId": "EPL-1.0", + "seeAlso": [ + "http://www.eclipse.org/legal/epl-v10.html", + "https://opensource.org/licenses/EPL-1.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/EPL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/EPL-2.0.json", + "referenceNumber": 407, + "name": "Eclipse Public License 2.0", + "licenseId": "EPL-2.0", + "seeAlso": [ + "https://www.eclipse.org/legal/epl-2.0", + "https://www.opensource.org/licenses/EPL-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/ErlPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ErlPL-1.1.json", + "referenceNumber": 466, + "name": "Erlang Public License v1.1", + "licenseId": "ErlPL-1.1", + "seeAlso": [ + "http://www.erlang.org/EPLICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/etalab-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/etalab-2.0.json", + "referenceNumber": 636, + "name": "Etalab Open License 2.0", + "licenseId": "etalab-2.0", + "seeAlso": [ + "https://github.com/DISIC/politique-de-contribution-open-source/blob/master/LICENSE.pdf", + "https://raw.githubusercontent.com/DISIC/politique-de-contribution-open-source/master/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/EUDatagrid.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/EUDatagrid.json", + "referenceNumber": 228, + "name": "EU DataGrid Software License", + "licenseId": "EUDatagrid", + "seeAlso": [ + "http://eu-datagrid.web.cern.ch/eu-datagrid/license.html", + "https://opensource.org/licenses/EUDatagrid" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/EUPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/EUPL-1.0.json", + "referenceNumber": 227, + "name": "European Union Public License 1.0", + "licenseId": "EUPL-1.0", + "seeAlso": [ + "http://ec.europa.eu/idabc/en/document/7330.html", + "http://ec.europa.eu/idabc/servlets/Doc027f.pdf?id\u003d31096" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/EUPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/EUPL-1.1.json", + "referenceNumber": 266, + "name": "European Union Public License 1.1", + "licenseId": "EUPL-1.1", + "seeAlso": [ + "https://joinup.ec.europa.eu/software/page/eupl/licence-eupl", + "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl1.1.-licence-en_0.pdf", + "https://opensource.org/licenses/EUPL-1.1" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/EUPL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/EUPL-1.2.json", + "referenceNumber": 559, + "name": "European Union Public License 1.2", + "licenseId": "EUPL-1.2", + "seeAlso": [ + "https://joinup.ec.europa.eu/page/eupl-text-11-12", + "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf", + "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/2020-03/EUPL-1.2%20EN.txt", + "https://joinup.ec.europa.eu/sites/default/files/inline-files/EUPL%20v1_2%20EN(1).txt", + "http://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri\u003dCELEX:32017D0863", + "https://opensource.org/licenses/EUPL-1.2" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Eurosym.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Eurosym.json", + "referenceNumber": 63, + "name": "Eurosym License", + "licenseId": "Eurosym", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Eurosym" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Fair.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Fair.json", + "referenceNumber": 570, + "name": "Fair License", + "licenseId": "Fair", + "seeAlso": [ + "https://web.archive.org/web/20150926120323/http://fairlicense.org/", + "https://opensource.org/licenses/Fair" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/FBM.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/FBM.json", + "referenceNumber": 175, + "name": "Fuzzy Bitmap License", + "licenseId": "FBM", + "seeAlso": [ + "https://github.com/SWI-Prolog/packages-xpce/blob/161a40cd82004f731ba48024f9d30af388a7edf5/src/img/gifwrite.c#L21-L26" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/FDK-AAC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/FDK-AAC.json", + "referenceNumber": 49, + "name": "Fraunhofer FDK AAC Codec Library", + "licenseId": "FDK-AAC", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/FDK-AAC", + "https://directory.fsf.org/wiki/License:Fdk" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Ferguson-Twofish.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Ferguson-Twofish.json", + "referenceNumber": 617, + "name": "Ferguson Twofish License", + "licenseId": "Ferguson-Twofish", + "seeAlso": [ + "https://github.com/wernerd/ZRTPCPP/blob/6b3cd8e6783642292bad0c21e3e5e5ce45ff3e03/cryptcommon/twofish.c#L113C3-L127" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Frameworx-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Frameworx-1.0.json", + "referenceNumber": 259, + "name": "Frameworx Open License 1.0", + "licenseId": "Frameworx-1.0", + "seeAlso": [ + "https://opensource.org/licenses/Frameworx-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/FreeBSD-DOC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/FreeBSD-DOC.json", + "referenceNumber": 333, + "name": "FreeBSD Documentation License", + "licenseId": "FreeBSD-DOC", + "seeAlso": [ + "https://www.freebsd.org/copyright/freebsd-doc-license/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/FreeImage.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/FreeImage.json", + "referenceNumber": 181, + "name": "FreeImage Public License v1.0", + "licenseId": "FreeImage", + "seeAlso": [ + "http://freeimage.sourceforge.net/freeimage-license.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/FSFAP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/FSFAP.json", + "referenceNumber": 36, + "name": "FSF All Permissive License", + "licenseId": "FSFAP", + "seeAlso": [ + "https://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/FSFAP-no-warranty-disclaimer.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/FSFAP-no-warranty-disclaimer.json", + "referenceNumber": 536, + "name": "FSF All Permissive License (without Warranty)", + "licenseId": "FSFAP-no-warranty-disclaimer", + "seeAlso": [ + "https://git.savannah.gnu.org/cgit/wget.git/tree/util/trunc.c?h\u003dv1.21.3\u0026id\u003d40747a11e44ced5a8ac628a41f879ced3e2ebce9#n6" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/FSFUL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/FSFUL.json", + "referenceNumber": 454, + "name": "FSF Unlimited License", + "licenseId": "FSFUL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/FSFULLR.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/FSFULLR.json", + "referenceNumber": 422, + "name": "FSF Unlimited License (with License Retention)", + "licenseId": "FSFULLR", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License#License_Retention_Variant" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/FSFULLRWD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/FSFULLRWD.json", + "referenceNumber": 197, + "name": "FSF Unlimited License (With License Retention and Warranty Disclaimer)", + "licenseId": "FSFULLRWD", + "seeAlso": [ + "https://lists.gnu.org/archive/html/autoconf/2012-04/msg00061.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/FTL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/FTL.json", + "referenceNumber": 438, + "name": "Freetype Project License", + "licenseId": "FTL", + "seeAlso": [ + "http://freetype.fis.uniroma2.it/FTL.TXT", + "http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT", + "http://gitlab.freedesktop.org/freetype/freetype/-/raw/master/docs/FTL.TXT" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Furuseth.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Furuseth.json", + "referenceNumber": 380, + "name": "Furuseth License", + "licenseId": "Furuseth", + "seeAlso": [ + "https://git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT?ref_type\u003dheads#L39-51" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/fwlw.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/fwlw.json", + "referenceNumber": 529, + "name": "fwlw License", + "licenseId": "fwlw", + "seeAlso": [ + "https://mirrors.nic.cz/tex-archive/macros/latex/contrib/fwlw/README" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GCR-docs.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GCR-docs.json", + "referenceNumber": 115, + "name": "Gnome GCR Documentation License", + "licenseId": "GCR-docs", + "seeAlso": [ + "https://github.com/GNOME/gcr/blob/master/docs/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GD.json", + "referenceNumber": 291, + "name": "GD License", + "licenseId": "GD", + "seeAlso": [ + "https://libgd.github.io/manuals/2.3.0/files/license-txt.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.1.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.1.json", + "referenceNumber": 589, + "name": "GNU Free Documentation License v1.1", + "licenseId": "GFDL-1.1", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.1-invariants-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-invariants-only.json", + "referenceNumber": 307, + "name": "GNU Free Documentation License v1.1 only - invariants", + "licenseId": "GFDL-1.1-invariants-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.1-invariants-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-invariants-or-later.json", + "referenceNumber": 98, + "name": "GNU Free Documentation License v1.1 or later - invariants", + "licenseId": "GFDL-1.1-invariants-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.1-no-invariants-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-no-invariants-only.json", + "referenceNumber": 47, + "name": "GNU Free Documentation License v1.1 only - no invariants", + "licenseId": "GFDL-1.1-no-invariants-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.1-no-invariants-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-no-invariants-or-later.json", + "referenceNumber": 273, + "name": "GNU Free Documentation License v1.1 or later - no invariants", + "licenseId": "GFDL-1.1-no-invariants-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.1-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-only.json", + "referenceNumber": 626, + "name": "GNU Free Documentation License v1.1 only", + "licenseId": "GFDL-1.1-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.1-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-or-later.json", + "referenceNumber": 644, + "name": "GNU Free Documentation License v1.1 or later", + "licenseId": "GFDL-1.1-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.2.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.2.json", + "referenceNumber": 520, + "name": "GNU Free Documentation License v1.2", + "licenseId": "GFDL-1.2", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.2-invariants-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-invariants-only.json", + "referenceNumber": 495, + "name": "GNU Free Documentation License v1.2 only - invariants", + "licenseId": "GFDL-1.2-invariants-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.2-invariants-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-invariants-or-later.json", + "referenceNumber": 6, + "name": "GNU Free Documentation License v1.2 or later - invariants", + "licenseId": "GFDL-1.2-invariants-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.2-no-invariants-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-no-invariants-only.json", + "referenceNumber": 77, + "name": "GNU Free Documentation License v1.2 only - no invariants", + "licenseId": "GFDL-1.2-no-invariants-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.2-no-invariants-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-no-invariants-or-later.json", + "referenceNumber": 279, + "name": "GNU Free Documentation License v1.2 or later - no invariants", + "licenseId": "GFDL-1.2-no-invariants-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.2-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-only.json", + "referenceNumber": 648, + "name": "GNU Free Documentation License v1.2 only", + "licenseId": "GFDL-1.2-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.2-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-or-later.json", + "referenceNumber": 318, + "name": "GNU Free Documentation License v1.2 or later", + "licenseId": "GFDL-1.2-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.3.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.3.json", + "referenceNumber": 287, + "name": "GNU Free Documentation License v1.3", + "licenseId": "GFDL-1.3", + "seeAlso": [ + "https://www.gnu.org/licenses/fdl-1.3.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.3-invariants-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-invariants-only.json", + "referenceNumber": 289, + "name": "GNU Free Documentation License v1.3 only - invariants", + "licenseId": "GFDL-1.3-invariants-only", + "seeAlso": [ + "https://www.gnu.org/licenses/fdl-1.3.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.3-invariants-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-invariants-or-later.json", + "referenceNumber": 497, + "name": "GNU Free Documentation License v1.3 or later - invariants", + "licenseId": "GFDL-1.3-invariants-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/fdl-1.3.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.3-no-invariants-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-no-invariants-only.json", + "referenceNumber": 254, + "name": "GNU Free Documentation License v1.3 only - no invariants", + "licenseId": "GFDL-1.3-no-invariants-only", + "seeAlso": [ + "https://www.gnu.org/licenses/fdl-1.3.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.3-no-invariants-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-no-invariants-or-later.json", + "referenceNumber": 207, + "name": "GNU Free Documentation License v1.3 or later - no invariants", + "licenseId": "GFDL-1.3-no-invariants-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/fdl-1.3.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.3-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-only.json", + "referenceNumber": 635, + "name": "GNU Free Documentation License v1.3 only", + "licenseId": "GFDL-1.3-only", + "seeAlso": [ + "https://www.gnu.org/licenses/fdl-1.3.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GFDL-1.3-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-or-later.json", + "referenceNumber": 448, + "name": "GNU Free Documentation License v1.3 or later", + "licenseId": "GFDL-1.3-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/fdl-1.3.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Giftware.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Giftware.json", + "referenceNumber": 172, + "name": "Giftware License", + "licenseId": "Giftware", + "seeAlso": [ + "http://liballeg.org/license.html#allegro-4-the-giftware-license" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GL2PS.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GL2PS.json", + "referenceNumber": 434, + "name": "GL2PS License", + "licenseId": "GL2PS", + "seeAlso": [ + "http://www.geuz.org/gl2ps/COPYING.GL2PS" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Glide.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Glide.json", + "referenceNumber": 189, + "name": "3dfx Glide License", + "licenseId": "Glide", + "seeAlso": [ + "http://www.users.on.net/~triforce/glidexp/COPYING.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Glulxe.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Glulxe.json", + "referenceNumber": 85, + "name": "Glulxe License", + "licenseId": "Glulxe", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Glulxe" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GLWTPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GLWTPL.json", + "referenceNumber": 190, + "name": "Good Luck With That Public License", + "licenseId": "GLWTPL", + "seeAlso": [ + "https://github.com/me-shaon/GLWTPL/commit/da5f6bc734095efbacb442c0b31e33a65b9d6e85" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/gnuplot.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/gnuplot.json", + "referenceNumber": 110, + "name": "gnuplot License", + "licenseId": "gnuplot", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Gnuplot" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GPL-1.0.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-1.0.json", + "referenceNumber": 630, + "name": "GNU General Public License v1.0 only", + "licenseId": "GPL-1.0", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GPL-1.0+.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-1.0+.json", + "referenceNumber": 26, + "name": "GNU General Public License v1.0 or later", + "licenseId": "GPL-1.0+", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GPL-1.0-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GPL-1.0-only.json", + "referenceNumber": 12, + "name": "GNU General Public License v1.0 only", + "licenseId": "GPL-1.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GPL-1.0-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GPL-1.0-or-later.json", + "referenceNumber": 642, + "name": "GNU General Public License v1.0 or later", + "licenseId": "GPL-1.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GPL-2.0.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-2.0.json", + "referenceNumber": 524, + "name": "GNU General Public License v2.0 only", + "licenseId": "GPL-2.0", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + "https://opensource.org/licenses/GPL-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GPL-2.0+.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-2.0+.json", + "referenceNumber": 25, + "name": "GNU General Public License v2.0 or later", + "licenseId": "GPL-2.0+", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + "https://opensource.org/licenses/GPL-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GPL-2.0-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GPL-2.0-only.json", + "referenceNumber": 618, + "name": "GNU General Public License v2.0 only", + "licenseId": "GPL-2.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + "https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt", + "https://opensource.org/licenses/GPL-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GPL-2.0-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GPL-2.0-or-later.json", + "referenceNumber": 164, + "name": "GNU General Public License v2.0 or later", + "licenseId": "GPL-2.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + "https://opensource.org/licenses/GPL-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.json", + "referenceNumber": 146, + "name": "GNU General Public License v2.0 w/Autoconf exception", + "licenseId": "GPL-2.0-with-autoconf-exception", + "seeAlso": [ + "http://ac-archive.sourceforge.net/doc/copyright.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GPL-2.0-with-bison-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-2.0-with-bison-exception.json", + "referenceNumber": 374, + "name": "GNU General Public License v2.0 w/Bison exception", + "licenseId": "GPL-2.0-with-bison-exception", + "seeAlso": [ + "http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.json", + "referenceNumber": 331, + "name": "GNU General Public License v2.0 w/Classpath exception", + "licenseId": "GPL-2.0-with-classpath-exception", + "seeAlso": [ + "https://www.gnu.org/software/classpath/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GPL-2.0-with-font-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-2.0-with-font-exception.json", + "referenceNumber": 542, + "name": "GNU General Public License v2.0 w/Font exception", + "licenseId": "GPL-2.0-with-font-exception", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-faq.html#FontException" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GPL-2.0-with-GCC-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-2.0-with-GCC-exception.json", + "referenceNumber": 68, + "name": "GNU General Public License v2.0 w/GCC Runtime Library exception", + "licenseId": "GPL-2.0-with-GCC-exception", + "seeAlso": [ + "https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GPL-3.0.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-3.0.json", + "referenceNumber": 442, + "name": "GNU General Public License v3.0 only", + "licenseId": "GPL-3.0", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-3.0-standalone.html", + "https://opensource.org/licenses/GPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GPL-3.0+.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-3.0+.json", + "referenceNumber": 270, + "name": "GNU General Public License v3.0 or later", + "licenseId": "GPL-3.0+", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-3.0-standalone.html", + "https://opensource.org/licenses/GPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GPL-3.0-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GPL-3.0-only.json", + "referenceNumber": 133, + "name": "GNU General Public License v3.0 only", + "licenseId": "GPL-3.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-3.0-standalone.html", + "https://opensource.org/licenses/GPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GPL-3.0-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/GPL-3.0-or-later.json", + "referenceNumber": 390, + "name": "GNU General Public License v3.0 or later", + "licenseId": "GPL-3.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-3.0-standalone.html", + "https://opensource.org/licenses/GPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.json", + "referenceNumber": 458, + "name": "GNU General Public License v3.0 w/Autoconf exception", + "licenseId": "GPL-3.0-with-autoconf-exception", + "seeAlso": [ + "https://www.gnu.org/licenses/autoconf-exception-3.0.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/GPL-3.0-with-GCC-exception.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/GPL-3.0-with-GCC-exception.json", + "referenceNumber": 356, + "name": "GNU General Public License v3.0 w/GCC Runtime Library exception", + "licenseId": "GPL-3.0-with-GCC-exception", + "seeAlso": [ + "https://www.gnu.org/licenses/gcc-exception-3.1.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Graphics-Gems.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Graphics-Gems.json", + "referenceNumber": 574, + "name": "Graphics Gems License", + "licenseId": "Graphics-Gems", + "seeAlso": [ + "https://github.com/erich666/GraphicsGems/blob/master/LICENSE.md" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/gSOAP-1.3b.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/gSOAP-1.3b.json", + "referenceNumber": 655, + "name": "gSOAP Public License v1.3b", + "licenseId": "gSOAP-1.3b", + "seeAlso": [ + "http://www.cs.fsu.edu/~engelen/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/gtkbook.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/gtkbook.json", + "referenceNumber": 237, + "name": "gtkbook License", + "licenseId": "gtkbook", + "seeAlso": [ + "https://github.com/slogan621/gtkbook", + "https://github.com/oetiker/rrdtool-1.x/blob/master/src/plbasename.c#L8-L11" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Gutmann.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Gutmann.json", + "referenceNumber": 441, + "name": "Gutmann License", + "licenseId": "Gutmann", + "seeAlso": [ + "https://www.cs.auckland.ac.nz/~pgut001/dumpasn1.c" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HaskellReport.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HaskellReport.json", + "referenceNumber": 625, + "name": "Haskell Language Report License", + "licenseId": "HaskellReport", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Haskell_Language_Report_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/hdparm.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/hdparm.json", + "referenceNumber": 81, + "name": "hdparm License", + "licenseId": "hdparm", + "seeAlso": [ + "https://github.com/Distrotech/hdparm/blob/4517550db29a91420fb2b020349523b1b4512df2/LICENSE.TXT" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Hippocratic-2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Hippocratic-2.1.json", + "referenceNumber": 299, + "name": "Hippocratic License 2.1", + "licenseId": "Hippocratic-2.1", + "seeAlso": [ + "https://firstdonoharm.dev/version/2/1/license.html", + "https://github.com/EthicalSource/hippocratic-license/blob/58c0e646d64ff6fbee275bfe2b9492f914e3ab2a/LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HP-1986.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HP-1986.json", + "referenceNumber": 277, + "name": "Hewlett-Packard 1986 License", + "licenseId": "HP-1986", + "seeAlso": [ + "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/machine/hppa/memchr.S;h\u003d1cca3e5e8867aa4bffef1f75a5c1bba25c0c441e;hb\u003dHEAD#l2" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HP-1989.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HP-1989.json", + "referenceNumber": 639, + "name": "Hewlett-Packard 1989 License", + "licenseId": "HP-1989", + "seeAlso": [ + "https://github.com/bleargh45/Data-UUID/blob/master/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND.json", + "referenceNumber": 281, + "name": "Historical Permission Notice and Disclaimer", + "licenseId": "HPND", + "seeAlso": [ + "https://opensource.org/licenses/HPND", + "http://lists.opensource.org/pipermail/license-discuss_lists.opensource.org/2002-November/006304.html" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/HPND-DEC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-DEC.json", + "referenceNumber": 577, + "name": "Historical Permission Notice and Disclaimer - DEC variant", + "licenseId": "HPND-DEC", + "seeAlso": [ + "https://gitlab.freedesktop.org/xorg/app/xkbcomp/-/blob/master/COPYING?ref_type\u003dheads#L69" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-doc.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-doc.json", + "referenceNumber": 391, + "name": "Historical Permission Notice and Disclaimer - documentation variant", + "licenseId": "HPND-doc", + "seeAlso": [ + "https://gitlab.freedesktop.org/xorg/lib/libxext/-/blob/master/COPYING?ref_type\u003dheads#L185-197", + "https://gitlab.freedesktop.org/xorg/lib/libxtst/-/blob/master/COPYING?ref_type\u003dheads#L70-77" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-doc-sell.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-doc-sell.json", + "referenceNumber": 163, + "name": "Historical Permission Notice and Disclaimer - documentation sell variant", + "licenseId": "HPND-doc-sell", + "seeAlso": [ + "https://gitlab.freedesktop.org/xorg/lib/libxtst/-/blob/master/COPYING?ref_type\u003dheads#L108-117", + "https://gitlab.freedesktop.org/xorg/lib/libxext/-/blob/master/COPYING?ref_type\u003dheads#L153-162" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-export-US.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-export-US.json", + "referenceNumber": 214, + "name": "HPND with US Government export control warning", + "licenseId": "HPND-export-US", + "seeAlso": [ + "https://www.kermitproject.org/ck90.html#source" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-export-US-acknowledgement.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-export-US-acknowledgement.json", + "referenceNumber": 610, + "name": "HPND with US Government export control warning and acknowledgment", + "licenseId": "HPND-export-US-acknowledgement", + "seeAlso": [ + "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L831-L852", + "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-export-US-modify.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-export-US-modify.json", + "referenceNumber": 498, + "name": "HPND with US Government export control warning and modification rqmt", + "licenseId": "HPND-export-US-modify", + "seeAlso": [ + "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L1157-L1182", + "https://github.com/pythongssapi/k5test/blob/v0.10.3/K5TEST-LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-export2-US.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-export2-US.json", + "referenceNumber": 33, + "name": "HPND with US Government export control and 2 disclaimers", + "licenseId": "HPND-export2-US", + "seeAlso": [ + "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L111-L133", + "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-Fenneberg-Livingston.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-Fenneberg-Livingston.json", + "referenceNumber": 145, + "name": "Historical Permission Notice and Disclaimer - Fenneberg-Livingston variant", + "licenseId": "HPND-Fenneberg-Livingston", + "seeAlso": [ + "https://github.com/FreeRADIUS/freeradius-client/blob/master/COPYRIGHT#L32", + "https://github.com/radcli/radcli/blob/master/COPYRIGHT#L34" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-INRIA-IMAG.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-INRIA-IMAG.json", + "referenceNumber": 614, + "name": "Historical Permission Notice and Disclaimer - INRIA-IMAG variant", + "licenseId": "HPND-INRIA-IMAG", + "seeAlso": [ + "https://github.com/ppp-project/ppp/blob/master/pppd/ipv6cp.c#L75-L83" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-Intel.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-Intel.json", + "referenceNumber": 195, + "name": "Historical Permission Notice and Disclaimer - Intel variant", + "licenseId": "HPND-Intel", + "seeAlso": [ + "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/machine/i960/memcpy.S;hb\u003dHEAD" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-Kevlin-Henney.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-Kevlin-Henney.json", + "referenceNumber": 428, + "name": "Historical Permission Notice and Disclaimer - Kevlin Henney variant", + "licenseId": "HPND-Kevlin-Henney", + "seeAlso": [ + "https://github.com/mruby/mruby/blob/83d12f8d52522cdb7c8cc46fad34821359f453e6/mrbgems/mruby-dir/src/Win/dirent.c#L127-L140" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-Markus-Kuhn.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-Markus-Kuhn.json", + "referenceNumber": 8, + "name": "Historical Permission Notice and Disclaimer - Markus Kuhn variant", + "licenseId": "HPND-Markus-Kuhn", + "seeAlso": [ + "https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c", + "https://sourceware.org/git/?p\u003dbinutils-gdb.git;a\u003dblob;f\u003dreadline/readline/support/wcwidth.c;h\u003d0f5ec995796f4813abbcf4972aec0378ab74722a;hb\u003dHEAD#l55" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-merchantability-variant.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-merchantability-variant.json", + "referenceNumber": 540, + "name": "Historical Permission Notice and Disclaimer - merchantability variant", + "licenseId": "HPND-merchantability-variant", + "seeAlso": [ + "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/misc/fini.c;hb\u003dHEAD" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-MIT-disclaimer.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-MIT-disclaimer.json", + "referenceNumber": 185, + "name": "Historical Permission Notice and Disclaimer with MIT disclaimer", + "licenseId": "HPND-MIT-disclaimer", + "seeAlso": [ + "https://metacpan.org/release/NLNETLABS/Net-DNS-SEC-1.22/source/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-Pbmplus.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-Pbmplus.json", + "referenceNumber": 603, + "name": "Historical Permission Notice and Disclaimer - Pbmplus variant", + "licenseId": "HPND-Pbmplus", + "seeAlso": [ + "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/netpbm.c#l8" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-sell-MIT-disclaimer-xserver.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-sell-MIT-disclaimer-xserver.json", + "referenceNumber": 125, + "name": "Historical Permission Notice and Disclaimer - sell xserver variant with MIT disclaimer", + "licenseId": "HPND-sell-MIT-disclaimer-xserver", + "seeAlso": [ + "https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type\u003dheads#L1781" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-sell-regexpr.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-sell-regexpr.json", + "referenceNumber": 633, + "name": "Historical Permission Notice and Disclaimer - sell regexpr variant", + "licenseId": "HPND-sell-regexpr", + "seeAlso": [ + "https://gitlab.com/bacula-org/bacula/-/blob/Branch-11.0/bacula/LICENSE-FOSS?ref_type\u003dheads#L245" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-sell-variant.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-sell-variant.json", + "referenceNumber": 344, + "name": "Historical Permission Notice and Disclaimer - sell variant", + "licenseId": "HPND-sell-variant", + "seeAlso": [ + "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h\u003dv4.19" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-sell-variant-MIT-disclaimer.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-sell-variant-MIT-disclaimer.json", + "referenceNumber": 160, + "name": "HPND sell variant with MIT disclaimer", + "licenseId": "HPND-sell-variant-MIT-disclaimer", + "seeAlso": [ + "https://github.com/sigmavirus24/x11-ssh-askpass/blob/master/README" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-sell-variant-MIT-disclaimer-rev.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-sell-variant-MIT-disclaimer-rev.json", + "referenceNumber": 609, + "name": "HPND sell variant with MIT disclaimer - reverse", + "licenseId": "HPND-sell-variant-MIT-disclaimer-rev", + "seeAlso": [ + "https://github.com/sigmavirus24/x11-ssh-askpass/blob/master/dynlist.c" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-UC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-UC.json", + "referenceNumber": 386, + "name": "Historical Permission Notice and Disclaimer - University of California variant", + "licenseId": "HPND-UC", + "seeAlso": [ + "https://core.tcl-lang.org/tk/file?name\u003dcompat/unistd.h" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HPND-UC-export-US.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-UC-export-US.json", + "referenceNumber": 118, + "name": "Historical Permission Notice and Disclaimer - University of California, US export warning", + "licenseId": "HPND-UC-export-US", + "seeAlso": [ + "https://github.com/RTimothyEdwards/magic/blob/master/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/HTMLTIDY.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HTMLTIDY.json", + "referenceNumber": 134, + "name": "HTML Tidy License", + "licenseId": "HTMLTIDY", + "seeAlso": [ + "https://github.com/htacg/tidy-html5/blob/next/README/LICENSE.md" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/IBM-pibs.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/IBM-pibs.json", + "referenceNumber": 102, + "name": "IBM PowerPC Initialization and Boot Software", + "licenseId": "IBM-pibs", + "seeAlso": [ + "http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003darch/powerpc/cpu/ppc4xx/miiphy.c;h\u003d297155fdafa064b955e53e9832de93bfb0cfb85b;hb\u003d9fab4bf4cc077c21e43941866f3f2c196f28670d" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/ICU.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ICU.json", + "referenceNumber": 67, + "name": "ICU License", + "licenseId": "ICU", + "seeAlso": [ + "http://source.icu-project.org/repos/icu/icu/trunk/license.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/IEC-Code-Components-EULA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/IEC-Code-Components-EULA.json", + "referenceNumber": 359, + "name": "IEC Code Components End-user licence agreement", + "licenseId": "IEC-Code-Components-EULA", + "seeAlso": [ + "https://www.iec.ch/webstore/custserv/pdf/CC-EULA.pdf", + "https://www.iec.ch/CCv1", + "https://www.iec.ch/copyright" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/IJG.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/IJG.json", + "referenceNumber": 641, + "name": "Independent JPEG Group License", + "licenseId": "IJG", + "seeAlso": [ + "http://dev.w3.org/cvsweb/Amaya/libjpeg/Attic/README?rev\u003d1.2" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/IJG-short.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/IJG-short.json", + "referenceNumber": 615, + "name": "Independent JPEG Group License - short", + "licenseId": "IJG-short", + "seeAlso": [ + "https://sourceforge.net/p/xmedcon/code/ci/master/tree/libs/ljpg/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/ImageMagick.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ImageMagick.json", + "referenceNumber": 478, + "name": "ImageMagick License", + "licenseId": "ImageMagick", + "seeAlso": [ + "http://www.imagemagick.org/script/license.php" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/iMatix.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/iMatix.json", + "referenceNumber": 327, + "name": "iMatix Standard Function Library Agreement", + "licenseId": "iMatix", + "seeAlso": [ + "http://legacy.imatix.com/html/sfl/sfl4.htm#license" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Imlib2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Imlib2.json", + "referenceNumber": 169, + "name": "Imlib2 License", + "licenseId": "Imlib2", + "seeAlso": [ + "http://trac.enlightenment.org/e/browser/trunk/imlib2/COPYING", + "https://git.enlightenment.org/legacy/imlib2.git/tree/COPYING" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Info-ZIP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Info-ZIP.json", + "referenceNumber": 269, + "name": "Info-ZIP License", + "licenseId": "Info-ZIP", + "seeAlso": [ + "http://www.info-zip.org/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Inner-Net-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Inner-Net-2.0.json", + "referenceNumber": 572, + "name": "Inner Net License v2.0", + "licenseId": "Inner-Net-2.0", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Inner_Net_License", + "https://sourceware.org/git/?p\u003dglibc.git;a\u003dblob;f\u003dLICENSES;h\u003d530893b1dc9ea00755603c68fb36bd4fc38a7be8;hb\u003dHEAD#l207" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Intel.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Intel.json", + "referenceNumber": 623, + "name": "Intel Open Source License", + "licenseId": "Intel", + "seeAlso": [ + "https://opensource.org/licenses/Intel" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Intel-ACPI.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Intel-ACPI.json", + "referenceNumber": 571, + "name": "Intel ACPI Software License Agreement", + "licenseId": "Intel-ACPI", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Intel_ACPI_Software_License_Agreement" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Interbase-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Interbase-1.0.json", + "referenceNumber": 654, + "name": "Interbase Public License v1.0", + "licenseId": "Interbase-1.0", + "seeAlso": [ + "https://web.archive.org/web/20060319014854/http://info.borland.com/devsupport/interbase/opensource/IPL.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/IPA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/IPA.json", + "referenceNumber": 94, + "name": "IPA Font License", + "licenseId": "IPA", + "seeAlso": [ + "https://opensource.org/licenses/IPA" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/IPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/IPL-1.0.json", + "referenceNumber": 332, + "name": "IBM Public License v1.0", + "licenseId": "IPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/IPL-1.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/ISC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ISC.json", + "referenceNumber": 488, + "name": "ISC License", + "licenseId": "ISC", + "seeAlso": [ + "https://www.isc.org/licenses/", + "https://www.isc.org/downloads/software-support-policy/isc-license/", + "https://opensource.org/licenses/ISC" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/ISC-Veillard.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ISC-Veillard.json", + "referenceNumber": 513, + "name": "ISC Veillard variant", + "licenseId": "ISC-Veillard", + "seeAlso": [ + "https://raw.githubusercontent.com/GNOME/libxml2/4c2e7c651f6c2f0d1a74f350cbda95f7df3e7017/hash.c", + "https://github.com/GNOME/libxml2/blob/master/dict.c", + "https://sourceforge.net/p/ctrio/git/ci/master/tree/README" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Jam.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Jam.json", + "referenceNumber": 108, + "name": "Jam License", + "licenseId": "Jam", + "seeAlso": [ + "https://www.boost.org/doc/libs/1_35_0/doc/html/jam.html", + "https://web.archive.org/web/20160330173339/https://swarm.workshop.perforce.com/files/guest/perforce_software/jam/src/README" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/JasPer-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/JasPer-2.0.json", + "referenceNumber": 487, + "name": "JasPer License", + "licenseId": "JasPer-2.0", + "seeAlso": [ + "http://www.ece.uvic.ca/~mdadams/jasper/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/JPL-image.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/JPL-image.json", + "referenceNumber": 363, + "name": "JPL Image Use Policy", + "licenseId": "JPL-image", + "seeAlso": [ + "https://www.jpl.nasa.gov/jpl-image-use-policy" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/JPNIC.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/JPNIC.json", + "referenceNumber": 83, + "name": "Japan Network Information Center License", + "licenseId": "JPNIC", + "seeAlso": [ + "https://gitlab.isc.org/isc-projects/bind9/blob/master/COPYRIGHT#L366" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/JSON.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/JSON.json", + "referenceNumber": 65, + "name": "JSON License", + "licenseId": "JSON", + "seeAlso": [ + "http://www.json.org/license.html" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/Kastrup.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Kastrup.json", + "referenceNumber": 226, + "name": "Kastrup License", + "licenseId": "Kastrup", + "seeAlso": [ + "https://ctan.math.utah.edu/ctan/tex-archive/macros/generic/kastrup/binhex.dtx" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Kazlib.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Kazlib.json", + "referenceNumber": 232, + "name": "Kazlib License", + "licenseId": "Kazlib", + "seeAlso": [ + "http://git.savannah.gnu.org/cgit/kazlib.git/tree/except.c?id\u003d0062df360c2d17d57f6af19b0e444c51feb99036" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Knuth-CTAN.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Knuth-CTAN.json", + "referenceNumber": 290, + "name": "Knuth CTAN License", + "licenseId": "Knuth-CTAN", + "seeAlso": [ + "https://ctan.org/license/knuth" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LAL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LAL-1.2.json", + "referenceNumber": 165, + "name": "Licence Art Libre 1.2", + "licenseId": "LAL-1.2", + "seeAlso": [ + "http://artlibre.org/licence/lal/licence-art-libre-12/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LAL-1.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LAL-1.3.json", + "referenceNumber": 600, + "name": "Licence Art Libre 1.3", + "licenseId": "LAL-1.3", + "seeAlso": [ + "https://artlibre.org/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Latex2e.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Latex2e.json", + "referenceNumber": 439, + "name": "Latex2e License", + "licenseId": "Latex2e", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Latex2e" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Latex2e-translated-notice.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Latex2e-translated-notice.json", + "referenceNumber": 620, + "name": "Latex2e with translated notice permission", + "licenseId": "Latex2e-translated-notice", + "seeAlso": [ + "https://git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id\u003da74c6b4ee49397cf330b333da1042bffa60ed14f#n74" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Leptonica.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Leptonica.json", + "referenceNumber": 103, + "name": "Leptonica License", + "licenseId": "Leptonica", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Leptonica" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LGPL-2.0.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/LGPL-2.0.json", + "referenceNumber": 353, + "name": "GNU Library General Public License v2 only", + "licenseId": "LGPL-2.0", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-2.0+.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/LGPL-2.0+.json", + "referenceNumber": 62, + "name": "GNU Library General Public License v2 or later", + "licenseId": "LGPL-2.0+", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-2.0-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LGPL-2.0-only.json", + "referenceNumber": 519, + "name": "GNU Library General Public License v2 only", + "licenseId": "LGPL-2.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-2.0-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LGPL-2.0-or-later.json", + "referenceNumber": 366, + "name": "GNU Library General Public License v2 or later", + "licenseId": "LGPL-2.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-2.1.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/LGPL-2.1.json", + "referenceNumber": 656, + "name": "GNU Lesser General Public License v2.1 only", + "licenseId": "LGPL-2.1", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", + "https://opensource.org/licenses/LGPL-2.1" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-2.1+.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/LGPL-2.1+.json", + "referenceNumber": 64, + "name": "GNU Lesser General Public License v2.1 or later", + "licenseId": "LGPL-2.1+", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", + "https://opensource.org/licenses/LGPL-2.1" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-2.1-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LGPL-2.1-only.json", + "referenceNumber": 177, + "name": "GNU Lesser General Public License v2.1 only", + "licenseId": "LGPL-2.1-only", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", + "https://opensource.org/licenses/LGPL-2.1" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-2.1-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LGPL-2.1-or-later.json", + "referenceNumber": 24, + "name": "GNU Lesser General Public License v2.1 or later", + "licenseId": "LGPL-2.1-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", + "https://opensource.org/licenses/LGPL-2.1" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-3.0.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/LGPL-3.0.json", + "referenceNumber": 578, + "name": "GNU Lesser General Public License v3.0 only", + "licenseId": "LGPL-3.0", + "seeAlso": [ + "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", + "https://www.gnu.org/licenses/lgpl+gpl-3.0.txt", + "https://opensource.org/licenses/LGPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-3.0+.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/LGPL-3.0+.json", + "referenceNumber": 233, + "name": "GNU Lesser General Public License v3.0 or later", + "licenseId": "LGPL-3.0+", + "seeAlso": [ + "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", + "https://www.gnu.org/licenses/lgpl+gpl-3.0.txt", + "https://opensource.org/licenses/LGPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-3.0-only.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LGPL-3.0-only.json", + "referenceNumber": 3, + "name": "GNU Lesser General Public License v3.0 only", + "licenseId": "LGPL-3.0-only", + "seeAlso": [ + "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", + "https://www.gnu.org/licenses/lgpl+gpl-3.0.txt", + "https://opensource.org/licenses/LGPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LGPL-3.0-or-later.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LGPL-3.0-or-later.json", + "referenceNumber": 262, + "name": "GNU Lesser General Public License v3.0 or later", + "licenseId": "LGPL-3.0-or-later", + "seeAlso": [ + "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", + "https://www.gnu.org/licenses/lgpl+gpl-3.0.txt", + "https://opensource.org/licenses/LGPL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LGPLLR.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LGPLLR.json", + "referenceNumber": 477, + "name": "Lesser General Public License For Linguistic Resources", + "licenseId": "LGPLLR", + "seeAlso": [ + "http://www-igm.univ-mlv.fr/~unitex/lgpllr.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Libpng.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Libpng.json", + "referenceNumber": 186, + "name": "libpng License", + "licenseId": "Libpng", + "seeAlso": [ + "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/libpng-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/libpng-2.0.json", + "referenceNumber": 257, + "name": "PNG Reference Library version 2", + "licenseId": "libpng-2.0", + "seeAlso": [ + "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/libselinux-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/libselinux-1.0.json", + "referenceNumber": 556, + "name": "libselinux public domain notice", + "licenseId": "libselinux-1.0", + "seeAlso": [ + "https://github.com/SELinuxProject/selinux/blob/master/libselinux/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/libtiff.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/libtiff.json", + "referenceNumber": 392, + "name": "libtiff License", + "licenseId": "libtiff", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/libtiff" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/libutil-David-Nugent.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/libutil-David-Nugent.json", + "referenceNumber": 400, + "name": "libutil David Nugent License", + "licenseId": "libutil-David-Nugent", + "seeAlso": [ + "http://web.mit.edu/freebsd/head/lib/libutil/login_ok.3", + "https://cgit.freedesktop.org/libbsd/tree/man/setproctitle.3bsd" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LiLiQ-P-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LiLiQ-P-1.1.json", + "referenceNumber": 43, + "name": "Licence Libre du Québec – Permissive version 1.1", + "licenseId": "LiLiQ-P-1.1", + "seeAlso": [ + "https://forge.gouv.qc.ca/licence/fr/liliq-v1-1/", + "http://opensource.org/licenses/LiLiQ-P-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/LiLiQ-R-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LiLiQ-R-1.1.json", + "referenceNumber": 74, + "name": "Licence Libre du Québec – Réciprocité version 1.1", + "licenseId": "LiLiQ-R-1.1", + "seeAlso": [ + "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-liliq-r-v1-1/", + "http://opensource.org/licenses/LiLiQ-R-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/LiLiQ-Rplus-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LiLiQ-Rplus-1.1.json", + "referenceNumber": 40, + "name": "Licence Libre du Québec – Réciprocité forte version 1.1", + "licenseId": "LiLiQ-Rplus-1.1", + "seeAlso": [ + "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-forte-liliq-r-v1-1/", + "http://opensource.org/licenses/LiLiQ-Rplus-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Linux-man-pages-1-para.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Linux-man-pages-1-para.json", + "referenceNumber": 339, + "name": "Linux man-pages - 1 paragraph", + "licenseId": "Linux-man-pages-1-para", + "seeAlso": [ + "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/getcpu.2#n4" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Linux-man-pages-copyleft.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Linux-man-pages-copyleft.json", + "referenceNumber": 590, + "name": "Linux man-pages Copyleft", + "licenseId": "Linux-man-pages-copyleft", + "seeAlso": [ + "https://www.kernel.org/doc/man-pages/licenses.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Linux-man-pages-copyleft-2-para.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Linux-man-pages-copyleft-2-para.json", + "referenceNumber": 86, + "name": "Linux man-pages Copyleft - 2 paragraphs", + "licenseId": "Linux-man-pages-copyleft-2-para", + "seeAlso": [ + "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/move_pages.2#n5", + "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/migrate_pages.2#n8" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Linux-man-pages-copyleft-var.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Linux-man-pages-copyleft-var.json", + "referenceNumber": 337, + "name": "Linux man-pages Copyleft Variant", + "licenseId": "Linux-man-pages-copyleft-var", + "seeAlso": [ + "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/set_mempolicy.2#n5" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Linux-OpenIB.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Linux-OpenIB.json", + "referenceNumber": 613, + "name": "Linux Kernel Variant of OpenIB.org license", + "licenseId": "Linux-OpenIB", + "seeAlso": [ + "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LOOP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LOOP.json", + "referenceNumber": 607, + "name": "Common Lisp LOOP License", + "licenseId": "LOOP", + "seeAlso": [ + "https://gitlab.com/embeddable-common-lisp/ecl/-/blob/develop/src/lsp/loop.lsp", + "http://git.savannah.gnu.org/cgit/gcl.git/tree/gcl/lsp/gcl_loop.lsp?h\u003dVersion_2_6_13pre", + "https://sourceforge.net/p/sbcl/sbcl/ci/master/tree/src/code/loop.lisp", + "https://github.com/cl-adams/adams/blob/master/LICENSE.md", + "https://github.com/blakemcbride/eclipse-lisp/blob/master/lisp/loop.lisp", + "https://gitlab.common-lisp.net/cmucl/cmucl/-/blob/master/src/code/loop.lisp" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LPD-document.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LPD-document.json", + "referenceNumber": 522, + "name": "LPD Documentation License", + "licenseId": "LPD-document", + "seeAlso": [ + "https://github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md", + "https://www.ietf.org/rfc/rfc1952.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LPL-1.0.json", + "referenceNumber": 196, + "name": "Lucent Public License Version 1.0", + "licenseId": "LPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/LPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/LPL-1.02.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LPL-1.02.json", + "referenceNumber": 69, + "name": "Lucent Public License v1.02", + "licenseId": "LPL-1.02", + "seeAlso": [ + "http://plan9.bell-labs.com/plan9/license.html", + "https://opensource.org/licenses/LPL-1.02" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LPPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LPPL-1.0.json", + "referenceNumber": 215, + "name": "LaTeX Project Public License v1.0", + "licenseId": "LPPL-1.0", + "seeAlso": [ + "http://www.latex-project.org/lppl/lppl-1-0.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LPPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LPPL-1.1.json", + "referenceNumber": 114, + "name": "LaTeX Project Public License v1.1", + "licenseId": "LPPL-1.1", + "seeAlso": [ + "http://www.latex-project.org/lppl/lppl-1-1.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LPPL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LPPL-1.2.json", + "referenceNumber": 435, + "name": "LaTeX Project Public License v1.2", + "licenseId": "LPPL-1.2", + "seeAlso": [ + "http://www.latex-project.org/lppl/lppl-1-2.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LPPL-1.3a.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LPPL-1.3a.json", + "referenceNumber": 18, + "name": "LaTeX Project Public License v1.3a", + "licenseId": "LPPL-1.3a", + "seeAlso": [ + "http://www.latex-project.org/lppl/lppl-1-3a.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/LPPL-1.3c.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LPPL-1.3c.json", + "referenceNumber": 240, + "name": "LaTeX Project Public License v1.3c", + "licenseId": "LPPL-1.3c", + "seeAlso": [ + "http://www.latex-project.org/lppl/lppl-1-3c.txt", + "https://opensource.org/licenses/LPPL-1.3c" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/lsof.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/lsof.json", + "referenceNumber": 605, + "name": "lsof License", + "licenseId": "lsof", + "seeAlso": [ + "https://github.com/lsof-org/lsof/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Lucida-Bitmap-Fonts.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Lucida-Bitmap-Fonts.json", + "referenceNumber": 399, + "name": "Lucida Bitmap Fonts License", + "licenseId": "Lucida-Bitmap-Fonts", + "seeAlso": [ + "https://gitlab.freedesktop.org/xorg/font/bh-100dpi/-/blob/master/COPYING?ref_type\u003dheads" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LZMA-SDK-9.11-to-9.20.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LZMA-SDK-9.11-to-9.20.json", + "referenceNumber": 430, + "name": "LZMA SDK License (versions 9.11 to 9.20)", + "licenseId": "LZMA-SDK-9.11-to-9.20", + "seeAlso": [ + "https://www.7-zip.org/sdk.html", + "https://sourceforge.net/projects/sevenzip/files/LZMA%20SDK/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/LZMA-SDK-9.22.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/LZMA-SDK-9.22.json", + "referenceNumber": 244, + "name": "LZMA SDK License (versions 9.22 and beyond)", + "licenseId": "LZMA-SDK-9.22", + "seeAlso": [ + "https://www.7-zip.org/sdk.html", + "https://sourceforge.net/projects/sevenzip/files/LZMA%20SDK/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Mackerras-3-Clause.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Mackerras-3-Clause.json", + "referenceNumber": 59, + "name": "Mackerras 3-Clause License", + "licenseId": "Mackerras-3-Clause", + "seeAlso": [ + "https://github.com/ppp-project/ppp/blob/master/pppd/chap_ms.c#L6-L28" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Mackerras-3-Clause-acknowledgment.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Mackerras-3-Clause-acknowledgment.json", + "referenceNumber": 598, + "name": "Mackerras 3-Clause - acknowledgment variant", + "licenseId": "Mackerras-3-Clause-acknowledgment", + "seeAlso": [ + "https://github.com/ppp-project/ppp/blob/master/pppd/auth.c#L6-L28" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/magaz.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/magaz.json", + "referenceNumber": 516, + "name": "magaz License", + "licenseId": "magaz", + "seeAlso": [ + "https://mirrors.nic.cz/tex-archive/macros/latex/contrib/magaz/magaz.tex" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/mailprio.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/mailprio.json", + "referenceNumber": 179, + "name": "mailprio License", + "licenseId": "mailprio", + "seeAlso": [ + "https://fossies.org/linux/sendmail/contrib/mailprio" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MakeIndex.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MakeIndex.json", + "referenceNumber": 206, + "name": "MakeIndex License", + "licenseId": "MakeIndex", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MakeIndex" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Martin-Birgmeier.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Martin-Birgmeier.json", + "referenceNumber": 535, + "name": "Martin Birgmeier License", + "licenseId": "Martin-Birgmeier", + "seeAlso": [ + "https://github.com/Perl/perl5/blob/blead/util.c#L6136" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/McPhee-slideshow.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/McPhee-slideshow.json", + "referenceNumber": 492, + "name": "McPhee Slideshow License", + "licenseId": "McPhee-slideshow", + "seeAlso": [ + "https://mirror.las.iastate.edu/tex-archive/graphics/metapost/contrib/macros/slideshow/slideshow.mp" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/metamail.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/metamail.json", + "referenceNumber": 437, + "name": "metamail License", + "licenseId": "metamail", + "seeAlso": [ + "https://github.com/Dual-Life/mime-base64/blob/master/Base64.xs#L12" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Minpack.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Minpack.json", + "referenceNumber": 512, + "name": "Minpack License", + "licenseId": "Minpack", + "seeAlso": [ + "http://www.netlib.org/minpack/disclaimer", + "https://gitlab.com/libeigen/eigen/-/blob/master/COPYING.MINPACK" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MirOS.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MirOS.json", + "referenceNumber": 183, + "name": "The MirOS Licence", + "licenseId": "MirOS", + "seeAlso": [ + "https://opensource.org/licenses/MirOS" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/MIT.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT.json", + "referenceNumber": 608, + "name": "MIT License", + "licenseId": "MIT", + "seeAlso": [ + "https://opensource.org/license/mit/" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/MIT-0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-0.json", + "referenceNumber": 395, + "name": "MIT No Attribution", + "licenseId": "MIT-0", + "seeAlso": [ + "https://github.com/aws/mit-0", + "https://romanrm.net/mit-zero", + "https://github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/MIT-advertising.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-advertising.json", + "referenceNumber": 293, + "name": "Enlightenment License (e16)", + "licenseId": "MIT-advertising", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MIT_With_Advertising" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MIT-CMU.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-CMU.json", + "referenceNumber": 575, + "name": "CMU License", + "licenseId": "MIT-CMU", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing:MIT?rd\u003dLicensing/MIT#CMU_Style", + "https://github.com/python-pillow/Pillow/blob/fffb426092c8db24a5f4b6df243a8a3c01fb63cd/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MIT-enna.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-enna.json", + "referenceNumber": 638, + "name": "enna License", + "licenseId": "MIT-enna", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MIT#enna" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MIT-feh.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-feh.json", + "referenceNumber": 53, + "name": "feh License", + "licenseId": "MIT-feh", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MIT#feh" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MIT-Festival.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-Festival.json", + "referenceNumber": 317, + "name": "MIT Festival Variant", + "licenseId": "MIT-Festival", + "seeAlso": [ + "https://github.com/festvox/flite/blob/master/COPYING", + "https://github.com/festvox/speech_tools/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MIT-Khronos-old.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-Khronos-old.json", + "referenceNumber": 249, + "name": "MIT Khronos - old variant", + "licenseId": "MIT-Khronos-old", + "seeAlso": [ + "https://github.com/KhronosGroup/SPIRV-Cross/blob/main/LICENSES/LicenseRef-KhronosFreeUse.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MIT-Modern-Variant.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-Modern-Variant.json", + "referenceNumber": 424, + "name": "MIT License Modern Variant", + "licenseId": "MIT-Modern-Variant", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing:MIT#Modern_Variants", + "https://ptolemy.berkeley.edu/copyright.htm", + "https://pirlwww.lpl.arizona.edu/resources/guide/software/PerlTk/Tixlic.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/MIT-open-group.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-open-group.json", + "referenceNumber": 283, + "name": "MIT Open Group variant", + "licenseId": "MIT-open-group", + "seeAlso": [ + "https://gitlab.freedesktop.org/xorg/app/iceauth/-/blob/master/COPYING", + "https://gitlab.freedesktop.org/xorg/app/xvinfo/-/blob/master/COPYING", + "https://gitlab.freedesktop.org/xorg/app/xsetroot/-/blob/master/COPYING", + "https://gitlab.freedesktop.org/xorg/app/xauth/-/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MIT-testregex.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-testregex.json", + "referenceNumber": 427, + "name": "MIT testregex Variant", + "licenseId": "MIT-testregex", + "seeAlso": [ + "https://github.com/dotnet/runtime/blob/55e1ac7c07df62c4108d4acedf78f77574470ce5/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/AttRegexTests.cs#L12-L28" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MIT-Wu.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MIT-Wu.json", + "referenceNumber": 459, + "name": "MIT Tom Wu Variant", + "licenseId": "MIT-Wu", + "seeAlso": [ + "https://github.com/chromium/octane/blob/master/crypto.js" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MITNFA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MITNFA.json", + "referenceNumber": 157, + "name": "MIT +no-false-attribs license", + "licenseId": "MITNFA", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MITNFA" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MMIXware.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MMIXware.json", + "referenceNumber": 474, + "name": "MMIXware License", + "licenseId": "MMIXware", + "seeAlso": [ + "https://gitlab.lrz.de/mmix/mmixware/-/blob/master/boilerplate.w" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Motosoto.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Motosoto.json", + "referenceNumber": 627, + "name": "Motosoto License", + "licenseId": "Motosoto", + "seeAlso": [ + "https://opensource.org/licenses/Motosoto" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/MPEG-SSG.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MPEG-SSG.json", + "referenceNumber": 417, + "name": "MPEG Software Simulation", + "licenseId": "MPEG-SSG", + "seeAlso": [ + "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/ppm/ppmtompeg/jrevdct.c#l1189" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/mpi-permissive.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/mpi-permissive.json", + "referenceNumber": 80, + "name": "mpi Permissive License", + "licenseId": "mpi-permissive", + "seeAlso": [ + "https://sources.debian.org/src/openmpi/4.1.0-10/ompi/debuggers/msgq_interface.h/?hl\u003d19#L19" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/mpich2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/mpich2.json", + "referenceNumber": 482, + "name": "mpich2 License", + "licenseId": "mpich2", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/MIT" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MPL-1.0.json", + "referenceNumber": 28, + "name": "Mozilla Public License 1.0", + "licenseId": "MPL-1.0", + "seeAlso": [ + "http://www.mozilla.org/MPL/MPL-1.0.html", + "https://opensource.org/licenses/MPL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/MPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MPL-1.1.json", + "referenceNumber": 619, + "name": "Mozilla Public License 1.1", + "licenseId": "MPL-1.1", + "seeAlso": [ + "http://www.mozilla.org/MPL/MPL-1.1.html", + "https://opensource.org/licenses/MPL-1.1" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/MPL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MPL-2.0.json", + "referenceNumber": 263, + "name": "Mozilla Public License 2.0", + "licenseId": "MPL-2.0", + "seeAlso": [ + "https://www.mozilla.org/MPL/2.0/", + "https://opensource.org/licenses/MPL-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.json", + "referenceNumber": 455, + "name": "Mozilla Public License 2.0 (no copyleft exception)", + "licenseId": "MPL-2.0-no-copyleft-exception", + "seeAlso": [ + "https://www.mozilla.org/MPL/2.0/", + "https://opensource.org/licenses/MPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/mplus.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/mplus.json", + "referenceNumber": 541, + "name": "mplus Font License", + "licenseId": "mplus", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing:Mplus?rd\u003dLicensing/mplus" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MS-LPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MS-LPL.json", + "referenceNumber": 528, + "name": "Microsoft Limited Public License", + "licenseId": "MS-LPL", + "seeAlso": [ + "https://www.openhub.net/licenses/mslpl", + "https://github.com/gabegundy/atlserver/blob/master/License.txt", + "https://en.wikipedia.org/wiki/Shared_Source_Initiative#Microsoft_Limited_Public_License_(Ms-LPL)" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MS-PL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MS-PL.json", + "referenceNumber": 499, + "name": "Microsoft Public License", + "licenseId": "MS-PL", + "seeAlso": [ + "http://www.microsoft.com/opensource/licenses.mspx", + "https://opensource.org/licenses/MS-PL" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/MS-RL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MS-RL.json", + "referenceNumber": 343, + "name": "Microsoft Reciprocal License", + "licenseId": "MS-RL", + "seeAlso": [ + "http://www.microsoft.com/opensource/licenses.mspx", + "https://opensource.org/licenses/MS-RL" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/MTLL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MTLL.json", + "referenceNumber": 137, + "name": "Matrix Template Library License", + "licenseId": "MTLL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Matrix_Template_Library_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MulanPSL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MulanPSL-1.0.json", + "referenceNumber": 107, + "name": "Mulan Permissive Software License, Version 1", + "licenseId": "MulanPSL-1.0", + "seeAlso": [ + "https://license.coscl.org.cn/MulanPSL/", + "https://github.com/yuwenlong/longphp/blob/25dfb70cc2a466dc4bb55ba30901cbce08d164b5/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/MulanPSL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/MulanPSL-2.0.json", + "referenceNumber": 490, + "name": "Mulan Permissive Software License, Version 2", + "licenseId": "MulanPSL-2.0", + "seeAlso": [ + "https://license.coscl.org.cn/MulanPSL2" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Multics.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Multics.json", + "referenceNumber": 573, + "name": "Multics License", + "licenseId": "Multics", + "seeAlso": [ + "https://opensource.org/licenses/Multics" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Mup.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Mup.json", + "referenceNumber": 440, + "name": "Mup License", + "licenseId": "Mup", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Mup" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NAIST-2003.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NAIST-2003.json", + "referenceNumber": 104, + "name": "Nara Institute of Science and Technology License (2003)", + "licenseId": "NAIST-2003", + "seeAlso": [ + "https://enterprise.dejacode.com/licenses/public/naist-2003/#license-text", + "https://github.com/nodejs/node/blob/4a19cc8947b1bba2b2d27816ec3d0edf9b28e503/LICENSE#L343" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NASA-1.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NASA-1.3.json", + "referenceNumber": 127, + "name": "NASA Open Source Agreement 1.3", + "licenseId": "NASA-1.3", + "seeAlso": [ + "http://ti.arc.nasa.gov/opensource/nosa/", + "https://opensource.org/licenses/NASA-1.3" + ], + "isOsiApproved": true, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/Naumen.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Naumen.json", + "referenceNumber": 128, + "name": "Naumen Public License", + "licenseId": "Naumen", + "seeAlso": [ + "https://opensource.org/licenses/Naumen" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/NBPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NBPL-1.0.json", + "referenceNumber": 41, + "name": "Net Boolean Public License v1", + "licenseId": "NBPL-1.0", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d37b4b3f6cc4bf34e1d3dec61e69914b9819d8894" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NCBI-PD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NCBI-PD.json", + "referenceNumber": 362, + "name": "NCBI Public Domain Notice", + "licenseId": "NCBI-PD", + "seeAlso": [ + "https://github.com/ncbi/sra-tools/blob/e8e5b6af4edc460156ad9ce5902d0779cffbf685/LICENSE", + "https://github.com/ncbi/datasets/blob/0ea4cd16b61e5b799d9cc55aecfa016d6c9bd2bf/LICENSE.md", + "https://github.com/ncbi/gprobe/blob/de64d30fee8b4c4013094d7d3139ea89b5dd1ace/LICENSE", + "https://github.com/ncbi/egapx/blob/08930b9dec0c69b2d1a05e5153c7b95ef0a3eb0f/LICENSE", + "https://github.com/ncbi/datasets/blob/master/LICENSE.md" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NCGL-UK-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NCGL-UK-2.0.json", + "referenceNumber": 320, + "name": "Non-Commercial Government Licence", + "licenseId": "NCGL-UK-2.0", + "seeAlso": [ + "http://www.nationalarchives.gov.uk/doc/non-commercial-government-licence/version/2/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NCL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NCL.json", + "referenceNumber": 153, + "name": "NCL Source Code License", + "licenseId": "NCL", + "seeAlso": [ + "https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/src/modules/module-filter-chain/pffft.c?ref_type\u003dheads#L1-52" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NCSA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NCSA.json", + "referenceNumber": 557, + "name": "University of Illinois/NCSA Open Source License", + "licenseId": "NCSA", + "seeAlso": [ + "http://otm.illinois.edu/uiuc_openSource", + "https://opensource.org/licenses/NCSA" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Net-SNMP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Net-SNMP.json", + "referenceNumber": 234, + "name": "Net-SNMP License", + "licenseId": "Net-SNMP", + "seeAlso": [ + "http://net-snmp.sourceforge.net/about/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NetCDF.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NetCDF.json", + "referenceNumber": 503, + "name": "NetCDF license", + "licenseId": "NetCDF", + "seeAlso": [ + "http://www.unidata.ucar.edu/software/netcdf/copyright.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Newsletr.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Newsletr.json", + "referenceNumber": 412, + "name": "Newsletr License", + "licenseId": "Newsletr", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Newsletr" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NGPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NGPL.json", + "referenceNumber": 275, + "name": "Nethack General Public License", + "licenseId": "NGPL", + "seeAlso": [ + "https://opensource.org/licenses/NGPL" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/NICTA-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NICTA-1.0.json", + "referenceNumber": 311, + "name": "NICTA Public Software License, Version 1.0", + "licenseId": "NICTA-1.0", + "seeAlso": [ + "https://opensource.apple.com/source/mDNSResponder/mDNSResponder-320.10/mDNSPosix/nss_ReadMe.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NIST-PD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NIST-PD.json", + "referenceNumber": 309, + "name": "NIST Public Domain Notice", + "licenseId": "NIST-PD", + "seeAlso": [ + "https://github.com/tcheneau/simpleRPL/blob/e645e69e38dd4e3ccfeceb2db8cba05b7c2e0cd3/LICENSE.txt", + "https://github.com/tcheneau/Routing/blob/f09f46fcfe636107f22f2c98348188a65a135d98/README.md" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NIST-PD-fallback.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NIST-PD-fallback.json", + "referenceNumber": 34, + "name": "NIST Public Domain Notice with license fallback", + "licenseId": "NIST-PD-fallback", + "seeAlso": [ + "https://github.com/usnistgov/jsip/blob/59700e6926cbe96c5cdae897d9a7d2656b42abe3/LICENSE", + "https://github.com/usnistgov/fipy/blob/86aaa5c2ba2c6f1be19593c5986071cf6568cc34/LICENSE.rst" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NIST-Software.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NIST-Software.json", + "referenceNumber": 76, + "name": "NIST Software License", + "licenseId": "NIST-Software", + "seeAlso": [ + "https://github.com/open-quantum-safe/liboqs/blob/40b01fdbb270f8614fde30e65d30e9da18c02393/src/common/rand/rand_nist.c#L1-L15" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NLOD-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NLOD-1.0.json", + "referenceNumber": 565, + "name": "Norwegian Licence for Open Government Data (NLOD) 1.0", + "licenseId": "NLOD-1.0", + "seeAlso": [ + "http://data.norge.no/nlod/en/1.0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NLOD-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NLOD-2.0.json", + "referenceNumber": 483, + "name": "Norwegian Licence for Open Government Data (NLOD) 2.0", + "licenseId": "NLOD-2.0", + "seeAlso": [ + "http://data.norge.no/nlod/en/2.0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NLPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NLPL.json", + "referenceNumber": 71, + "name": "No Limit Public License", + "licenseId": "NLPL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/NLPL" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Nokia.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Nokia.json", + "referenceNumber": 44, + "name": "Nokia Open Source License", + "licenseId": "Nokia", + "seeAlso": [ + "https://opensource.org/licenses/nokia" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/NOSL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NOSL.json", + "referenceNumber": 126, + "name": "Netizen Open Source License", + "licenseId": "NOSL", + "seeAlso": [ + "http://bits.netizen.com.au/licenses/NOSL/nosl.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Noweb.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Noweb.json", + "referenceNumber": 534, + "name": "Noweb License", + "licenseId": "Noweb", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Noweb" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NPL-1.0.json", + "referenceNumber": 346, + "name": "Netscape Public License v1.0", + "licenseId": "NPL-1.0", + "seeAlso": [ + "http://www.mozilla.org/MPL/NPL/1.0/" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/NPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NPL-1.1.json", + "referenceNumber": 418, + "name": "Netscape Public License v1.1", + "licenseId": "NPL-1.1", + "seeAlso": [ + "http://www.mozilla.org/MPL/NPL/1.1/" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/NPOSL-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NPOSL-3.0.json", + "referenceNumber": 579, + "name": "Non-Profit Open Software License 3.0", + "licenseId": "NPOSL-3.0", + "seeAlso": [ + "https://opensource.org/licenses/NOSL3.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/NRL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NRL.json", + "referenceNumber": 230, + "name": "NRL License", + "licenseId": "NRL", + "seeAlso": [ + "http://web.mit.edu/network/isakmp/nrllicense.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/NTP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NTP.json", + "referenceNumber": 547, + "name": "NTP License", + "licenseId": "NTP", + "seeAlso": [ + "https://opensource.org/licenses/NTP" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/NTP-0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/NTP-0.json", + "referenceNumber": 460, + "name": "NTP No Attribution", + "licenseId": "NTP-0", + "seeAlso": [ + "https://github.com/tytso/e2fsprogs/blob/master/lib/et/et_name.c" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Nunit.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/Nunit.json", + "referenceNumber": 634, + "name": "Nunit License", + "licenseId": "Nunit", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Nunit" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/O-UDA-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/O-UDA-1.0.json", + "referenceNumber": 191, + "name": "Open Use of Data Agreement v1.0", + "licenseId": "O-UDA-1.0", + "seeAlso": [ + "https://github.com/microsoft/Open-Use-of-Data-Agreement/blob/v1.0/O-UDA-1.0.md", + "https://cdla.dev/open-use-of-data-agreement-v1-0/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OAR.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OAR.json", + "referenceNumber": 4, + "name": "OAR License", + "licenseId": "OAR", + "seeAlso": [ + "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/string/strsignal.c;hb\u003dHEAD#l35" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OCCT-PL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OCCT-PL.json", + "referenceNumber": 596, + "name": "Open CASCADE Technology Public License", + "licenseId": "OCCT-PL", + "seeAlso": [ + "http://www.opencascade.com/content/occt-public-license" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OCLC-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OCLC-2.0.json", + "referenceNumber": 308, + "name": "OCLC Research Public License 2.0", + "licenseId": "OCLC-2.0", + "seeAlso": [ + "http://www.oclc.org/research/activities/software/license/v2final.htm", + "https://opensource.org/licenses/OCLC-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/ODbL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ODbL-1.0.json", + "referenceNumber": 243, + "name": "Open Data Commons Open Database License v1.0", + "licenseId": "ODbL-1.0", + "seeAlso": [ + "http://www.opendatacommons.org/licenses/odbl/1.0/", + "https://opendatacommons.org/licenses/odbl/1-0/" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/ODC-By-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ODC-By-1.0.json", + "referenceNumber": 7, + "name": "Open Data Commons Attribution License v1.0", + "licenseId": "ODC-By-1.0", + "seeAlso": [ + "https://opendatacommons.org/licenses/by/1.0/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OFFIS.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OFFIS.json", + "referenceNumber": 238, + "name": "OFFIS License", + "licenseId": "OFFIS", + "seeAlso": [ + "https://sourceforge.net/p/xmedcon/code/ci/master/tree/libs/dicom/README" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OFL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OFL-1.0.json", + "referenceNumber": 475, + "name": "SIL Open Font License 1.0", + "licenseId": "OFL-1.0", + "seeAlso": [ + "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL10_web" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/OFL-1.0-no-RFN.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OFL-1.0-no-RFN.json", + "referenceNumber": 23, + "name": "SIL Open Font License 1.0 with no Reserved Font Name", + "licenseId": "OFL-1.0-no-RFN", + "seeAlso": [ + "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL10_web" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OFL-1.0-RFN.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OFL-1.0-RFN.json", + "referenceNumber": 11, + "name": "SIL Open Font License 1.0 with Reserved Font Name", + "licenseId": "OFL-1.0-RFN", + "seeAlso": [ + "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL10_web" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OFL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OFL-1.1.json", + "referenceNumber": 248, + "name": "SIL Open Font License 1.1", + "licenseId": "OFL-1.1", + "seeAlso": [ + "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web", + "https://opensource.org/licenses/OFL-1.1" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/OFL-1.1-no-RFN.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OFL-1.1-no-RFN.json", + "referenceNumber": 550, + "name": "SIL Open Font License 1.1 with no Reserved Font Name", + "licenseId": "OFL-1.1-no-RFN", + "seeAlso": [ + "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web", + "https://opensource.org/licenses/OFL-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/OFL-1.1-RFN.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OFL-1.1-RFN.json", + "referenceNumber": 507, + "name": "SIL Open Font License 1.1 with Reserved Font Name", + "licenseId": "OFL-1.1-RFN", + "seeAlso": [ + "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web", + "https://opensource.org/licenses/OFL-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/OGC-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OGC-1.0.json", + "referenceNumber": 166, + "name": "OGC Software License, Version 1.0", + "licenseId": "OGC-1.0", + "seeAlso": [ + "https://www.ogc.org/ogc/software/1.0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OGDL-Taiwan-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OGDL-Taiwan-1.0.json", + "referenceNumber": 468, + "name": "Taiwan Open Government Data License, version 1.0", + "licenseId": "OGDL-Taiwan-1.0", + "seeAlso": [ + "https://data.gov.tw/license" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OGL-Canada-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OGL-Canada-2.0.json", + "referenceNumber": 464, + "name": "Open Government Licence - Canada", + "licenseId": "OGL-Canada-2.0", + "seeAlso": [ + "https://open.canada.ca/en/open-government-licence-canada" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OGL-UK-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OGL-UK-1.0.json", + "referenceNumber": 489, + "name": "Open Government Licence v1.0", + "licenseId": "OGL-UK-1.0", + "seeAlso": [ + "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/1/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OGL-UK-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OGL-UK-2.0.json", + "referenceNumber": 467, + "name": "Open Government Licence v2.0", + "licenseId": "OGL-UK-2.0", + "seeAlso": [ + "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/2/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OGL-UK-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OGL-UK-3.0.json", + "referenceNumber": 151, + "name": "Open Government Licence v3.0", + "licenseId": "OGL-UK-3.0", + "seeAlso": [ + "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OGTSL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OGTSL.json", + "referenceNumber": 367, + "name": "Open Group Test Suite License", + "licenseId": "OGTSL", + "seeAlso": [ + "http://www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt", + "https://opensource.org/licenses/OGTSL" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/OLDAP-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-1.1.json", + "referenceNumber": 180, + "name": "Open LDAP Public License v1.1", + "licenseId": "OLDAP-1.1", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d806557a5ad59804ef3a44d5abfbe91d706b0791f" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-1.2.json", + "referenceNumber": 229, + "name": "Open LDAP Public License v1.2", + "licenseId": "OLDAP-1.2", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d42b0383c50c299977b5893ee695cf4e486fb0dc7" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-1.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-1.3.json", + "referenceNumber": 224, + "name": "Open LDAP Public License v1.3", + "licenseId": "OLDAP-1.3", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003de5f8117f0ce088d0bd7a8e18ddf37eaa40eb09b1" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-1.4.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-1.4.json", + "referenceNumber": 255, + "name": "Open LDAP Public License v1.4", + "licenseId": "OLDAP-1.4", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dc9f95c2f3f2ffb5e0ae55fe7388af75547660941" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.0.json", + "referenceNumber": 208, + "name": "Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)", + "licenseId": "OLDAP-2.0", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcbf50f4e1185a21abd4c0a54d3f4341fe28f36ea" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.0.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.0.1.json", + "referenceNumber": 79, + "name": "Open LDAP Public License v2.0.1", + "licenseId": "OLDAP-2.0.1", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db6d68acd14e51ca3aab4428bf26522aa74873f0e" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.1.json", + "referenceNumber": 360, + "name": "Open LDAP Public License v2.1", + "licenseId": "OLDAP-2.1", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db0d176738e96a0d3b9f85cb51e140a86f21be715" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.2.json", + "referenceNumber": 316, + "name": "Open LDAP Public License v2.2", + "licenseId": "OLDAP-2.2", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d470b0c18ec67621c85881b2733057fecf4a1acc3" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.2.1.json", + "referenceNumber": 426, + "name": "Open LDAP Public License v2.2.1", + "licenseId": "OLDAP-2.2.1", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d4bc786f34b50aa301be6f5600f58a980070f481e" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.2.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.2.2.json", + "referenceNumber": 384, + "name": "Open LDAP Public License 2.2.2", + "licenseId": "OLDAP-2.2.2", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003ddf2cc1e21eb7c160695f5b7cffd6296c151ba188" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.3.json", + "referenceNumber": 381, + "name": "Open LDAP Public License v2.3", + "licenseId": "OLDAP-2.3", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dd32cf54a32d581ab475d23c810b0a7fbaf8d63c3" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.4.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.4.json", + "referenceNumber": 93, + "name": "Open LDAP Public License v2.4", + "licenseId": "OLDAP-2.4", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcd1284c4a91a8a380d904eee68d1583f989ed386" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.5.json", + "referenceNumber": 651, + "name": "Open LDAP Public License v2.5", + "licenseId": "OLDAP-2.5", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d6852b9d90022e8593c98205413380536b1b5a7cf" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.6.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.6.json", + "referenceNumber": 568, + "name": "Open LDAP Public License v2.6", + "licenseId": "OLDAP-2.6", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d1cae062821881f41b73012ba816434897abf4205" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.7.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.7.json", + "referenceNumber": 220, + "name": "Open LDAP Public License v2.7", + "licenseId": "OLDAP-2.7", + "seeAlso": [ + "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d47c2415c1df81556eeb39be6cad458ef87c534a2" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/OLDAP-2.8.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLDAP-2.8.json", + "referenceNumber": 5, + "name": "Open LDAP Public License v2.8", + "licenseId": "OLDAP-2.8", + "seeAlso": [ + "http://www.openldap.org/software/release/license.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/OLFL-1.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OLFL-1.3.json", + "referenceNumber": 142, + "name": "Open Logistics Foundation License Version 1.3", + "licenseId": "OLFL-1.3", + "seeAlso": [ + "https://openlogisticsfoundation.org/licenses/", + "https://opensource.org/license/olfl-1-3/" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/OML.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OML.json", + "referenceNumber": 375, + "name": "Open Market License", + "licenseId": "OML", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Open_Market_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OpenPBS-2.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OpenPBS-2.3.json", + "referenceNumber": 314, + "name": "OpenPBS v2.3 Software License", + "licenseId": "OpenPBS-2.3", + "seeAlso": [ + "https://github.com/adaptivecomputing/torque/blob/master/PBS_License.txt", + "https://www.mcs.anl.gov/research/projects/openpbs/PBS_License.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OpenSSL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OpenSSL.json", + "referenceNumber": 303, + "name": "OpenSSL License", + "licenseId": "OpenSSL", + "seeAlso": [ + "http://www.openssl.org/source/license.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/OpenSSL-standalone.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OpenSSL-standalone.json", + "referenceNumber": 602, + "name": "OpenSSL License - standalone", + "licenseId": "OpenSSL-standalone", + "seeAlso": [ + "https://library.netapp.com/ecm/ecm_download_file/ECMP1196395", + "https://hstechdocs.helpsystems.com/manuals/globalscape/archive/cuteftp6/open_ssl_license_agreement.htm" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OpenVision.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OpenVision.json", + "referenceNumber": 588, + "name": "OpenVision License", + "licenseId": "OpenVision", + "seeAlso": [ + "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L66-L98", + "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html", + "https://fedoraproject.org/wiki/Licensing:MIT#OpenVision_Variant" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OPL-1.0.json", + "referenceNumber": 91, + "name": "Open Public License v1.0", + "licenseId": "OPL-1.0", + "seeAlso": [ + "http://old.koalateam.com/jackaroo/OPL_1_0.TXT", + "https://fedoraproject.org/wiki/Licensing/Open_Public_License" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/OPL-UK-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OPL-UK-3.0.json", + "referenceNumber": 480, + "name": "United Kingdom Open Parliament Licence v3.0", + "licenseId": "OPL-UK-3.0", + "seeAlso": [ + "https://www.parliament.uk/site-information/copyright-parliament/open-parliament-licence/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OPUBL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OPUBL-1.0.json", + "referenceNumber": 329, + "name": "Open Publication License v1.0", + "licenseId": "OPUBL-1.0", + "seeAlso": [ + "http://opencontent.org/openpub/", + "https://www.debian.org/opl", + "https://www.ctan.org/license/opl" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/OSET-PL-2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OSET-PL-2.1.json", + "referenceNumber": 517, + "name": "OSET Public License version 2.1", + "licenseId": "OSET-PL-2.1", + "seeAlso": [ + "http://www.osetfoundation.org/public-license", + "https://opensource.org/licenses/OPL-2.1" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/OSL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OSL-1.0.json", + "referenceNumber": 162, + "name": "Open Software License 1.0", + "licenseId": "OSL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/OSL-1.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/OSL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OSL-1.1.json", + "referenceNumber": 586, + "name": "Open Software License 1.1", + "licenseId": "OSL-1.1", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/OSL1.1" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/OSL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OSL-2.0.json", + "referenceNumber": 531, + "name": "Open Software License 2.0", + "licenseId": "OSL-2.0", + "seeAlso": [ + "http://web.archive.org/web/20041020171434/http://www.rosenlaw.com/osl2.0.html" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/OSL-2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OSL-2.1.json", + "referenceNumber": 138, + "name": "Open Software License 2.1", + "licenseId": "OSL-2.1", + "seeAlso": [ + "http://web.archive.org/web/20050212003940/http://www.rosenlaw.com/osl21.htm", + "https://opensource.org/licenses/OSL-2.1" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/OSL-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/OSL-3.0.json", + "referenceNumber": 300, + "name": "Open Software License 3.0", + "licenseId": "OSL-3.0", + "seeAlso": [ + "https://web.archive.org/web/20120101081418/http://rosenlaw.com:80/OSL3.0.htm", + "https://opensource.org/licenses/OSL-3.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/PADL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/PADL.json", + "referenceNumber": 113, + "name": "PADL License", + "licenseId": "PADL", + "seeAlso": [ + "https://git.openldap.org/openldap/openldap/-/blob/master/libraries/libldap/os-local.c?ref_type\u003dheads#L19-23" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Parity-6.0.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Parity-6.0.0.json", + "referenceNumber": 246, + "name": "The Parity Public License 6.0.0", + "licenseId": "Parity-6.0.0", + "seeAlso": [ + "https://paritylicense.com/versions/6.0.0.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Parity-7.0.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Parity-7.0.0.json", + "referenceNumber": 212, + "name": "The Parity Public License 7.0.0", + "licenseId": "Parity-7.0.0", + "seeAlso": [ + "https://paritylicense.com/versions/7.0.0.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/PDDL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/PDDL-1.0.json", + "referenceNumber": 493, + "name": "Open Data Commons Public Domain Dedication \u0026 License 1.0", + "licenseId": "PDDL-1.0", + "seeAlso": [ + "http://opendatacommons.org/licenses/pddl/1.0/", + "https://opendatacommons.org/licenses/pddl/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/PHP-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/PHP-3.0.json", + "referenceNumber": 584, + "name": "PHP License v3.0", + "licenseId": "PHP-3.0", + "seeAlso": [ + "http://www.php.net/license/3_0.txt", + "https://opensource.org/licenses/PHP-3.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/PHP-3.01.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/PHP-3.01.json", + "referenceNumber": 538, + "name": "PHP License v3.01", + "licenseId": "PHP-3.01", + "seeAlso": [ + "http://www.php.net/license/3_01.txt" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Pixar.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Pixar.json", + "referenceNumber": 204, + "name": "Pixar License", + "licenseId": "Pixar", + "seeAlso": [ + "https://github.com/PixarAnimationStudios/OpenSubdiv/raw/v3_5_0/LICENSE.txt", + "https://graphics.pixar.com/opensubdiv/docs/license.html", + "https://github.com/PixarAnimationStudios/OpenSubdiv/blob/v3_5_0/opensubdiv/version.cpp#L2-L22" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/pkgconf.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/pkgconf.json", + "referenceNumber": 389, + "name": "pkgconf License", + "licenseId": "pkgconf", + "seeAlso": [ + "https://github.com/pkgconf/pkgconf/blob/master/cli/main.c#L8" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Plexus.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Plexus.json", + "referenceNumber": 141, + "name": "Plexus Classworlds License", + "licenseId": "Plexus", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Plexus_Classworlds_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/pnmstitch.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/pnmstitch.json", + "referenceNumber": 158, + "name": "pnmstitch License", + "licenseId": "pnmstitch", + "seeAlso": [ + "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/editor/pnmstitch.c#l2" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/PolyForm-Noncommercial-1.0.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/PolyForm-Noncommercial-1.0.0.json", + "referenceNumber": 54, + "name": "PolyForm Noncommercial License 1.0.0", + "licenseId": "PolyForm-Noncommercial-1.0.0", + "seeAlso": [ + "https://polyformproject.org/licenses/noncommercial/1.0.0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/PolyForm-Small-Business-1.0.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/PolyForm-Small-Business-1.0.0.json", + "referenceNumber": 594, + "name": "PolyForm Small Business License 1.0.0", + "licenseId": "PolyForm-Small-Business-1.0.0", + "seeAlso": [ + "https://polyformproject.org/licenses/small-business/1.0.0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/PostgreSQL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/PostgreSQL.json", + "referenceNumber": 643, + "name": "PostgreSQL License", + "licenseId": "PostgreSQL", + "seeAlso": [ + "http://www.postgresql.org/about/licence", + "https://opensource.org/licenses/PostgreSQL" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/PPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/PPL.json", + "referenceNumber": 580, + "name": "Peer Production License", + "licenseId": "PPL", + "seeAlso": [ + "https://wiki.p2pfoundation.net/Peer_Production_License", + "http://www.networkcultures.org/_uploads/%233notebook_telekommunist.pdf" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/PSF-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/PSF-2.0.json", + "referenceNumber": 55, + "name": "Python Software Foundation License 2.0", + "licenseId": "PSF-2.0", + "seeAlso": [ + "https://opensource.org/licenses/Python-2.0" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/psfrag.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/psfrag.json", + "referenceNumber": 555, + "name": "psfrag License", + "licenseId": "psfrag", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/psfrag" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/psutils.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/psutils.json", + "referenceNumber": 260, + "name": "psutils License", + "licenseId": "psutils", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/psutils" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Python-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Python-2.0.json", + "referenceNumber": 285, + "name": "Python License 2.0", + "licenseId": "Python-2.0", + "seeAlso": [ + "https://opensource.org/licenses/Python-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Python-2.0.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Python-2.0.1.json", + "referenceNumber": 201, + "name": "Python License 2.0.1", + "licenseId": "Python-2.0.1", + "seeAlso": [ + "https://www.python.org/download/releases/2.0.1/license/", + "https://docs.python.org/3/license.html", + "https://github.com/python/cpython/blob/main/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/python-ldap.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/python-ldap.json", + "referenceNumber": 446, + "name": "Python ldap License", + "licenseId": "python-ldap", + "seeAlso": [ + "https://github.com/python-ldap/python-ldap/blob/main/LICENCE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Qhull.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Qhull.json", + "referenceNumber": 326, + "name": "Qhull License", + "licenseId": "Qhull", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Qhull" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/QPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/QPL-1.0.json", + "referenceNumber": 413, + "name": "Q Public License 1.0", + "licenseId": "QPL-1.0", + "seeAlso": [ + "http://doc.qt.nokia.com/3.3/license.html", + "https://opensource.org/licenses/QPL-1.0", + "https://doc.qt.io/archives/3.3/license.html" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/QPL-1.0-INRIA-2004.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/QPL-1.0-INRIA-2004.json", + "referenceNumber": 486, + "name": "Q Public License 1.0 - INRIA 2004 variant", + "licenseId": "QPL-1.0-INRIA-2004", + "seeAlso": [ + "https://github.com/maranget/hevea/blob/master/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/radvd.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/radvd.json", + "referenceNumber": 433, + "name": "radvd License", + "licenseId": "radvd", + "seeAlso": [ + "https://github.com/radvd-project/radvd/blob/master/COPYRIGHT" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Rdisc.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Rdisc.json", + "referenceNumber": 50, + "name": "Rdisc License", + "licenseId": "Rdisc", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Rdisc_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/RHeCos-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/RHeCos-1.1.json", + "referenceNumber": 99, + "name": "Red Hat eCos Public License v1.1", + "licenseId": "RHeCos-1.1", + "seeAlso": [ + "http://ecos.sourceware.org/old-license.html" + ], + "isOsiApproved": false, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/RPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/RPL-1.1.json", + "referenceNumber": 205, + "name": "Reciprocal Public License 1.1", + "licenseId": "RPL-1.1", + "seeAlso": [ + "https://opensource.org/licenses/RPL-1.1" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/RPL-1.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/RPL-1.5.json", + "referenceNumber": 52, + "name": "Reciprocal Public License 1.5", + "licenseId": "RPL-1.5", + "seeAlso": [ + "https://opensource.org/licenses/RPL-1.5" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/RPSL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/RPSL-1.0.json", + "referenceNumber": 637, + "name": "RealNetworks Public Source License v1.0", + "licenseId": "RPSL-1.0", + "seeAlso": [ + "https://helixcommunity.org/content/rpsl", + "https://opensource.org/licenses/RPSL-1.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/RSA-MD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/RSA-MD.json", + "referenceNumber": 496, + "name": "RSA Message-Digest License", + "licenseId": "RSA-MD", + "seeAlso": [ + "http://www.faqs.org/rfcs/rfc1321.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/RSCPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/RSCPL.json", + "referenceNumber": 235, + "name": "Ricoh Source Code Public License", + "licenseId": "RSCPL", + "seeAlso": [ + "http://wayback.archive.org/web/20060715140826/http://www.risource.org/RPL/RPL-1.0A.shtml", + "https://opensource.org/licenses/RSCPL" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Ruby.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Ruby.json", + "referenceNumber": 223, + "name": "Ruby License", + "licenseId": "Ruby", + "seeAlso": [ + "https://www.ruby-lang.org/en/about/license.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/SAX-PD.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SAX-PD.json", + "referenceNumber": 301, + "name": "Sax Public Domain Notice", + "licenseId": "SAX-PD", + "seeAlso": [ + "http://www.saxproject.org/copying.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SAX-PD-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SAX-PD-2.0.json", + "referenceNumber": 561, + "name": "Sax Public Domain Notice 2.0", + "licenseId": "SAX-PD-2.0", + "seeAlso": [ + "http://www.saxproject.org/copying.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Saxpath.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Saxpath.json", + "referenceNumber": 109, + "name": "Saxpath License", + "licenseId": "Saxpath", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Saxpath_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SCEA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SCEA.json", + "referenceNumber": 35, + "name": "SCEA Shared Source License", + "licenseId": "SCEA", + "seeAlso": [ + "http://research.scea.com/scea_shared_source_license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SchemeReport.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SchemeReport.json", + "referenceNumber": 425, + "name": "Scheme Language Report License", + "licenseId": "SchemeReport", + "seeAlso": [], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Sendmail.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Sendmail.json", + "referenceNumber": 274, + "name": "Sendmail License", + "licenseId": "Sendmail", + "seeAlso": [ + "http://www.sendmail.com/pdfs/open_source/sendmail_license.pdf", + "https://web.archive.org/web/20160322142305/https://www.sendmail.com/pdfs/open_source/sendmail_license.pdf" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Sendmail-8.23.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Sendmail-8.23.json", + "referenceNumber": 247, + "name": "Sendmail License 8.23", + "licenseId": "Sendmail-8.23", + "seeAlso": [ + "https://www.proofpoint.com/sites/default/files/sendmail-license.pdf", + "https://web.archive.org/web/20181003101040/https://www.proofpoint.com/sites/default/files/sendmail-license.pdf" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SGI-B-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SGI-B-1.0.json", + "referenceNumber": 476, + "name": "SGI Free Software License B v1.0", + "licenseId": "SGI-B-1.0", + "seeAlso": [ + "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.1.0.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SGI-B-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SGI-B-1.1.json", + "referenceNumber": 456, + "name": "SGI Free Software License B v1.1", + "licenseId": "SGI-B-1.1", + "seeAlso": [ + "http://oss.sgi.com/projects/FreeB/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SGI-B-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SGI-B-2.0.json", + "referenceNumber": 405, + "name": "SGI Free Software License B v2.0", + "licenseId": "SGI-B-2.0", + "seeAlso": [ + "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/SGI-OpenGL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SGI-OpenGL.json", + "referenceNumber": 629, + "name": "SGI OpenGL License", + "licenseId": "SGI-OpenGL", + "seeAlso": [ + "https://gitlab.freedesktop.org/mesa/glw/-/blob/master/README?ref_type\u003dheads" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SGP4.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SGP4.json", + "referenceNumber": 336, + "name": "SGP4 Permission Notice", + "licenseId": "SGP4", + "seeAlso": [ + "https://celestrak.org/publications/AIAA/2006-6753/faq.php" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SHL-0.5.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SHL-0.5.json", + "referenceNumber": 338, + "name": "Solderpad Hardware License v0.5", + "licenseId": "SHL-0.5", + "seeAlso": [ + "https://solderpad.org/licenses/SHL-0.5/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SHL-0.51.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SHL-0.51.json", + "referenceNumber": 29, + "name": "Solderpad Hardware License, Version 0.51", + "licenseId": "SHL-0.51", + "seeAlso": [ + "https://solderpad.org/licenses/SHL-0.51/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SimPL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SimPL-2.0.json", + "referenceNumber": 444, + "name": "Simple Public License 2.0", + "licenseId": "SimPL-2.0", + "seeAlso": [ + "https://opensource.org/licenses/SimPL-2.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/SISSL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SISSL.json", + "referenceNumber": 268, + "name": "Sun Industry Standards Source License v1.1", + "licenseId": "SISSL", + "seeAlso": [ + "http://www.openoffice.org/licenses/sissl_license.html", + "https://opensource.org/licenses/SISSL" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/SISSL-1.2.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SISSL-1.2.json", + "referenceNumber": 502, + "name": "Sun Industry Standards Source License v1.2", + "licenseId": "SISSL-1.2", + "seeAlso": [ + "http://gridscheduler.sourceforge.net/Gridengine_SISSL_license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SL.json", + "referenceNumber": 645, + "name": "SL License", + "licenseId": "SL", + "seeAlso": [ + "https://github.com/mtoyoda/sl/blob/master/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Sleepycat.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Sleepycat.json", + "referenceNumber": 182, + "name": "Sleepycat License", + "licenseId": "Sleepycat", + "seeAlso": [ + "https://opensource.org/licenses/Sleepycat" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/SMLNJ.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SMLNJ.json", + "referenceNumber": 148, + "name": "Standard ML of New Jersey License", + "licenseId": "SMLNJ", + "seeAlso": [ + "https://www.smlnj.org/license.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/SMPPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SMPPL.json", + "referenceNumber": 250, + "name": "Secure Messaging Protocol Public License", + "licenseId": "SMPPL", + "seeAlso": [ + "https://github.com/dcblake/SMP/blob/master/Documentation/License.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SNIA.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SNIA.json", + "referenceNumber": 518, + "name": "SNIA Public License 1.1", + "licenseId": "SNIA", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/SNIA_Public_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/snprintf.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/snprintf.json", + "referenceNumber": 161, + "name": "snprintf License", + "licenseId": "snprintf", + "seeAlso": [ + "https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/bsd-snprintf.c#L2" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/softSurfer.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/softSurfer.json", + "referenceNumber": 523, + "name": "softSurfer License", + "licenseId": "softSurfer", + "seeAlso": [ + "https://github.com/mm2/Little-CMS/blob/master/src/cmssm.c#L207", + "https://fedoraproject.org/wiki/Licensing/softSurfer" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Soundex.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Soundex.json", + "referenceNumber": 368, + "name": "Soundex License", + "licenseId": "Soundex", + "seeAlso": [ + "https://metacpan.org/release/RJBS/Text-Soundex-3.05/source/Soundex.pm#L3-11" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Spencer-86.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Spencer-86.json", + "referenceNumber": 472, + "name": "Spencer License 86", + "licenseId": "Spencer-86", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Spencer-94.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Spencer-94.json", + "referenceNumber": 378, + "name": "Spencer License 94", + "licenseId": "Spencer-94", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License", + "https://metacpan.org/release/KNOK/File-MMagic-1.30/source/COPYING#L28" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Spencer-99.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Spencer-99.json", + "referenceNumber": 139, + "name": "Spencer License 99", + "licenseId": "Spencer-99", + "seeAlso": [ + "http://www.opensource.apple.com/source/tcl/tcl-5/tcl/generic/regfronts.c" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SPL-1.0.json", + "referenceNumber": 280, + "name": "Sun Public License v1.0", + "licenseId": "SPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/SPL-1.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/ssh-keyscan.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ssh-keyscan.json", + "referenceNumber": 294, + "name": "ssh-keyscan License", + "licenseId": "ssh-keyscan", + "seeAlso": [ + "https://github.com/openssh/openssh-portable/blob/master/LICENCE#L82" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SSH-OpenSSH.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SSH-OpenSSH.json", + "referenceNumber": 506, + "name": "SSH OpenSSH license", + "licenseId": "SSH-OpenSSH", + "seeAlso": [ + "https://github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/LICENCE#L10" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SSH-short.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SSH-short.json", + "referenceNumber": 563, + "name": "SSH short notice", + "licenseId": "SSH-short", + "seeAlso": [ + "https://github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/pathnames.h", + "http://web.mit.edu/kolya/.f/root/athena.mit.edu/sipb.mit.edu/project/openssh/OldFiles/src/openssh-2.9.9p2/ssh-add.1", + "https://joinup.ec.europa.eu/svn/lesoll/trunk/italc/lib/src/dsa_key.cpp" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SSLeay-standalone.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SSLeay-standalone.json", + "referenceNumber": 591, + "name": "SSLeay License - standalone", + "licenseId": "SSLeay-standalone", + "seeAlso": [ + "https://www.tq-group.com/filedownloads/files/software-license-conditions/OriginalSSLeay/OriginalSSLeay.pdf" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SSPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SSPL-1.0.json", + "referenceNumber": 17, + "name": "Server Side Public License, v 1", + "licenseId": "SSPL-1.0", + "seeAlso": [ + "https://www.mongodb.com/licensing/server-side-public-license" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/StandardML-NJ.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/StandardML-NJ.json", + "referenceNumber": 658, + "name": "Standard ML of New Jersey License", + "licenseId": "StandardML-NJ", + "seeAlso": [ + "https://www.smlnj.org/license.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/SugarCRM-1.1.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SugarCRM-1.1.3.json", + "referenceNumber": 42, + "name": "SugarCRM Public License v1.1.3", + "licenseId": "SugarCRM-1.1.3", + "seeAlso": [ + "http://www.sugarcrm.com/crm/SPL" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Sun-PPP.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Sun-PPP.json", + "referenceNumber": 385, + "name": "Sun PPP License", + "licenseId": "Sun-PPP", + "seeAlso": [ + "https://github.com/ppp-project/ppp/blob/master/pppd/eap.c#L7-L16" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Sun-PPP-2000.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Sun-PPP-2000.json", + "referenceNumber": 310, + "name": "Sun PPP License (2000)", + "licenseId": "Sun-PPP-2000", + "seeAlso": [ + "https://github.com/ppp-project/ppp/blob/master/modules/ppp_ahdlc.c#L7-L19" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SunPro.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SunPro.json", + "referenceNumber": 57, + "name": "SunPro License", + "licenseId": "SunPro", + "seeAlso": [ + "https://github.com/freebsd/freebsd-src/blob/main/lib/msun/src/e_acosh.c", + "https://github.com/freebsd/freebsd-src/blob/main/lib/msun/src/e_lgammal.c" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/SWL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/SWL.json", + "referenceNumber": 649, + "name": "Scheme Widget Library (SWL) Software License Agreement", + "licenseId": "SWL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/SWL" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/swrule.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/swrule.json", + "referenceNumber": 90, + "name": "swrule License", + "licenseId": "swrule", + "seeAlso": [ + "https://ctan.math.utah.edu/ctan/tex-archive/macros/generic/misc/swrule.sty" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Symlinks.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Symlinks.json", + "referenceNumber": 414, + "name": "Symlinks License", + "licenseId": "Symlinks", + "seeAlso": [ + "https://www.mail-archive.com/debian-bugs-rc@lists.debian.org/msg11494.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TAPR-OHL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TAPR-OHL-1.0.json", + "referenceNumber": 242, + "name": "TAPR Open Hardware License v1.0", + "licenseId": "TAPR-OHL-1.0", + "seeAlso": [ + "https://www.tapr.org/OHL" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TCL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TCL.json", + "referenceNumber": 2, + "name": "TCL/TK License", + "licenseId": "TCL", + "seeAlso": [ + "http://www.tcl.tk/software/tcltk/license.html", + "https://fedoraproject.org/wiki/Licensing/TCL" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TCP-wrappers.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TCP-wrappers.json", + "referenceNumber": 9, + "name": "TCP Wrappers License", + "licenseId": "TCP-wrappers", + "seeAlso": [ + "http://rc.quest.com/topics/openssh/license.php#tcpwrappers" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TermReadKey.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TermReadKey.json", + "referenceNumber": 256, + "name": "TermReadKey License", + "licenseId": "TermReadKey", + "seeAlso": [ + "https://github.com/jonathanstowe/TermReadKey/blob/master/README#L9-L10" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TGPPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TGPPL-1.0.json", + "referenceNumber": 101, + "name": "Transitive Grace Period Public Licence 1.0", + "licenseId": "TGPPL-1.0", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/TGPPL", + "https://tahoe-lafs.org/trac/tahoe-lafs/browser/trunk/COPYING.TGPPL.rst" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/threeparttable.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/threeparttable.json", + "referenceNumber": 398, + "name": "threeparttable License", + "licenseId": "threeparttable", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Threeparttable" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TMate.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TMate.json", + "referenceNumber": 539, + "name": "TMate Open Source License", + "licenseId": "TMate", + "seeAlso": [ + "http://svnkit.com/license.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TORQUE-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TORQUE-1.1.json", + "referenceNumber": 61, + "name": "TORQUE v2.5+ Software License v1.1", + "licenseId": "TORQUE-1.1", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/TORQUEv1.1" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TOSL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TOSL.json", + "referenceNumber": 267, + "name": "Trusster Open Source License", + "licenseId": "TOSL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/TOSL" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TPDL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TPDL.json", + "referenceNumber": 75, + "name": "Time::ParseDate License", + "licenseId": "TPDL", + "seeAlso": [ + "https://metacpan.org/pod/Time::ParseDate#LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TPL-1.0.json", + "referenceNumber": 508, + "name": "THOR Public License 1.0", + "licenseId": "TPL-1.0", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing:ThorPublicLicense" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TTWL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TTWL.json", + "referenceNumber": 87, + "name": "Text-Tabs+Wrap License", + "licenseId": "TTWL", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/TTWL", + "https://github.com/ap/Text-Tabs/blob/master/lib.modern/Text/Tabs.pm#L148" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TTYP0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TTYP0.json", + "referenceNumber": 451, + "name": "TTYP0 License", + "licenseId": "TTYP0", + "seeAlso": [ + "https://people.mpi-inf.mpg.de/~uwe/misc/uw-ttyp0/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TU-Berlin-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TU-Berlin-1.0.json", + "referenceNumber": 159, + "name": "Technische Universitaet Berlin License 1.0", + "licenseId": "TU-Berlin-1.0", + "seeAlso": [ + "https://github.com/swh/ladspa/blob/7bf6f3799fdba70fda297c2d8fd9f526803d9680/gsm/COPYRIGHT" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/TU-Berlin-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/TU-Berlin-2.0.json", + "referenceNumber": 624, + "name": "Technische Universitaet Berlin License 2.0", + "licenseId": "TU-Berlin-2.0", + "seeAlso": [ + "https://github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/UCAR.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/UCAR.json", + "referenceNumber": 78, + "name": "UCAR License", + "licenseId": "UCAR", + "seeAlso": [ + "https://github.com/Unidata/UDUNITS-2/blob/master/COPYRIGHT" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/UCL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/UCL-1.0.json", + "referenceNumber": 646, + "name": "Upstream Compatibility License v1.0", + "licenseId": "UCL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/UCL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/ulem.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ulem.json", + "referenceNumber": 566, + "name": "ulem License", + "licenseId": "ulem", + "seeAlso": [ + "https://mirrors.ctan.org/macros/latex/contrib/ulem/README" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/UMich-Merit.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/UMich-Merit.json", + "referenceNumber": 505, + "name": "Michigan/Merit Networks License", + "licenseId": "UMich-Merit", + "seeAlso": [ + "https://github.com/radcli/radcli/blob/master/COPYRIGHT#L64" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Unicode-3.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Unicode-3.0.json", + "referenceNumber": 46, + "name": "Unicode License v3", + "licenseId": "Unicode-3.0", + "seeAlso": [ + "https://www.unicode.org/license.txt" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Unicode-DFS-2015.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Unicode-DFS-2015.json", + "referenceNumber": 647, + "name": "Unicode License Agreement - Data Files and Software (2015)", + "licenseId": "Unicode-DFS-2015", + "seeAlso": [ + "https://web.archive.org/web/20151224134844/http://unicode.org/copyright.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Unicode-DFS-2016.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Unicode-DFS-2016.json", + "referenceNumber": 152, + "name": "Unicode License Agreement - Data Files and Software (2016)", + "licenseId": "Unicode-DFS-2016", + "seeAlso": [ + "https://www.unicode.org/license.txt", + "http://web.archive.org/web/20160823201924/http://www.unicode.org/copyright.html#License", + "http://www.unicode.org/copyright.html" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/Unicode-TOU.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Unicode-TOU.json", + "referenceNumber": 606, + "name": "Unicode Terms of Use", + "licenseId": "Unicode-TOU", + "seeAlso": [ + "http://web.archive.org/web/20140704074106/http://www.unicode.org/copyright.html", + "http://www.unicode.org/copyright.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/UnixCrypt.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/UnixCrypt.json", + "referenceNumber": 462, + "name": "UnixCrypt License", + "licenseId": "UnixCrypt", + "seeAlso": [ + "https://foss.heptapod.net/python-libs/passlib/-/blob/branch/stable/LICENSE#L70", + "https://opensource.apple.com/source/JBoss/JBoss-737/jboss-all/jetty/src/main/org/mortbay/util/UnixCrypt.java.auto.html", + "https://archive.eclipse.org/jetty/8.0.1.v20110908/xref/org/eclipse/jetty/http/security/UnixCrypt.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Unlicense.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Unlicense.json", + "referenceNumber": 411, + "name": "The Unlicense", + "licenseId": "Unlicense", + "seeAlso": [ + "https://unlicense.org/" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/UPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/UPL-1.0.json", + "referenceNumber": 511, + "name": "Universal Permissive License v1.0", + "licenseId": "UPL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/UPL" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/URT-RLE.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/URT-RLE.json", + "referenceNumber": 443, + "name": "Utah Raster Toolkit Run Length Encoded License", + "licenseId": "URT-RLE", + "seeAlso": [ + "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/pnmtorle.c", + "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/rletopnm.c" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Vim.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Vim.json", + "referenceNumber": 371, + "name": "Vim License", + "licenseId": "Vim", + "seeAlso": [ + "http://vimdoc.sourceforge.net/htmldoc/uganda.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/VOSTROM.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/VOSTROM.json", + "referenceNumber": 122, + "name": "VOSTROM Public License for Open Source", + "licenseId": "VOSTROM", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/VOSTROM" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/VSL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/VSL-1.0.json", + "referenceNumber": 510, + "name": "Vovida Software License v1.0", + "licenseId": "VSL-1.0", + "seeAlso": [ + "https://opensource.org/licenses/VSL-1.0" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/W3C.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/W3C.json", + "referenceNumber": 284, + "name": "W3C Software Notice and License (2002-12-31)", + "licenseId": "W3C", + "seeAlso": [ + "http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231.html", + "https://opensource.org/licenses/W3C" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/W3C-19980720.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/W3C-19980720.json", + "referenceNumber": 156, + "name": "W3C Software Notice and License (1998-07-20)", + "licenseId": "W3C-19980720", + "seeAlso": [ + "http://www.w3.org/Consortium/Legal/copyright-software-19980720.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/W3C-20150513.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/W3C-20150513.json", + "referenceNumber": 452, + "name": "W3C Software Notice and Document License (2015-05-13)", + "licenseId": "W3C-20150513", + "seeAlso": [ + "https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document", + "https://www.w3.org/copyright/software-license-2015/", + "https://www.w3.org/copyright/software-license-2023/" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/w3m.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/w3m.json", + "referenceNumber": 202, + "name": "w3m License", + "licenseId": "w3m", + "seeAlso": [ + "https://github.com/tats/w3m/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Watcom-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Watcom-1.0.json", + "referenceNumber": 533, + "name": "Sybase Open Watcom Public License 1.0", + "licenseId": "Watcom-1.0", + "seeAlso": [ + "https://opensource.org/licenses/Watcom-1.0" + ], + "isOsiApproved": true, + "isFsfLibre": false + }, + { + "reference": "https://spdx.org/licenses/Widget-Workshop.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Widget-Workshop.json", + "referenceNumber": 548, + "name": "Widget Workshop License", + "licenseId": "Widget-Workshop", + "seeAlso": [ + "https://github.com/novnc/noVNC/blob/master/core/crypto/des.js#L24" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Wsuipa.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Wsuipa.json", + "referenceNumber": 305, + "name": "Wsuipa License", + "licenseId": "Wsuipa", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Wsuipa" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/WTFPL.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/WTFPL.json", + "referenceNumber": 176, + "name": "Do What The F*ck You Want To Public License", + "licenseId": "WTFPL", + "seeAlso": [ + "http://www.wtfpl.net/about/", + "http://sam.zoy.org/wtfpl/COPYING" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/wxWindows.html", + "isDeprecatedLicenseId": true, + "detailsUrl": "https://spdx.org/licenses/wxWindows.json", + "referenceNumber": 258, + "name": "wxWindows Library License", + "licenseId": "wxWindows", + "seeAlso": [ + "https://opensource.org/licenses/WXwindows" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/X11.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/X11.json", + "referenceNumber": 203, + "name": "X11 License", + "licenseId": "X11", + "seeAlso": [ + "http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/X11-distribute-modifications-variant.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/X11-distribute-modifications-variant.json", + "referenceNumber": 112, + "name": "X11 License Distribution Modification Variant", + "licenseId": "X11-distribute-modifications-variant", + "seeAlso": [ + "https://github.com/mirror/ncurses/blob/master/COPYING" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Xdebug-1.03.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Xdebug-1.03.json", + "referenceNumber": 10, + "name": "Xdebug License v 1.03", + "licenseId": "Xdebug-1.03", + "seeAlso": [ + "https://github.com/xdebug/xdebug/blob/master/LICENSE" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Xerox.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Xerox.json", + "referenceNumber": 595, + "name": "Xerox License", + "licenseId": "Xerox", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Xerox" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Xfig.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Xfig.json", + "referenceNumber": 89, + "name": "Xfig License", + "licenseId": "Xfig", + "seeAlso": [ + "https://github.com/Distrotech/transfig/blob/master/transfig/transfig.c", + "https://fedoraproject.org/wiki/Licensing:MIT#Xfig_Variant", + "https://sourceforge.net/p/mcj/xfig/ci/master/tree/src/Makefile.am" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/XFree86-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/XFree86-1.1.json", + "referenceNumber": 562, + "name": "XFree86 License 1.1", + "licenseId": "XFree86-1.1", + "seeAlso": [ + "http://www.xfree86.org/current/LICENSE4.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/xinetd.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/xinetd.json", + "referenceNumber": 465, + "name": "xinetd License", + "licenseId": "xinetd", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Xinetd_License" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/xkeyboard-config-Zinoviev.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/xkeyboard-config-Zinoviev.json", + "referenceNumber": 140, + "name": "xkeyboard-config Zinoviev License", + "licenseId": "xkeyboard-config-Zinoviev", + "seeAlso": [ + "https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/blob/master/COPYING?ref_type\u003dheads#L178" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/xlock.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/xlock.json", + "referenceNumber": 357, + "name": "xlock License", + "licenseId": "xlock", + "seeAlso": [ + "https://fossies.org/linux/tiff/contrib/ras/ras2tif.c" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Xnet.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Xnet.json", + "referenceNumber": 236, + "name": "X.Net License", + "licenseId": "Xnet", + "seeAlso": [ + "https://opensource.org/licenses/Xnet" + ], + "isOsiApproved": true + }, + { + "reference": "https://spdx.org/licenses/xpp.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/xpp.json", + "referenceNumber": 312, + "name": "XPP License", + "licenseId": "xpp", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/xpp" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/XSkat.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/XSkat.json", + "referenceNumber": 544, + "name": "XSkat License", + "licenseId": "XSkat", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/XSkat_License" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/xzoom.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/xzoom.json", + "referenceNumber": 530, + "name": "xzoom License", + "licenseId": "xzoom", + "seeAlso": [ + "https://metadata.ftp-master.debian.org/changelogs//main/x/xzoom/xzoom_0.3-27_copyright" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/YPL-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/YPL-1.0.json", + "referenceNumber": 491, + "name": "Yahoo! Public License v1.0", + "licenseId": "YPL-1.0", + "seeAlso": [ + "http://www.zimbra.com/license/yahoo_public_license_1.0.html" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/YPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/YPL-1.1.json", + "referenceNumber": 473, + "name": "Yahoo! Public License v1.1", + "licenseId": "YPL-1.1", + "seeAlso": [ + "http://www.zimbra.com/license/yahoo_public_license_1.1.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Zed.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Zed.json", + "referenceNumber": 599, + "name": "Zed License", + "licenseId": "Zed", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/Zed" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Zeeff.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Zeeff.json", + "referenceNumber": 218, + "name": "Zeeff License", + "licenseId": "Zeeff", + "seeAlso": [ + "ftp://ftp.tin.org/pub/news/utils/newsx/newsx-1.6.tar.gz" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Zend-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Zend-2.0.json", + "referenceNumber": 481, + "name": "Zend License v2.0", + "licenseId": "Zend-2.0", + "seeAlso": [ + "https://web.archive.org/web/20130517195954/http://www.zend.com/license/2_00.txt" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Zimbra-1.3.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Zimbra-1.3.json", + "referenceNumber": 379, + "name": "Zimbra Public License v1.3", + "licenseId": "Zimbra-1.3", + "seeAlso": [ + "http://web.archive.org/web/20100302225219/http://www.zimbra.com/license/zimbra-public-license-1-3.html" + ], + "isOsiApproved": false, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/Zimbra-1.4.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Zimbra-1.4.json", + "referenceNumber": 304, + "name": "Zimbra Public License v1.4", + "licenseId": "Zimbra-1.4", + "seeAlso": [ + "http://www.zimbra.com/legal/zimbra-public-license-1-4" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/Zlib.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Zlib.json", + "referenceNumber": 209, + "name": "zlib License", + "licenseId": "Zlib", + "seeAlso": [ + "http://www.zlib.net/zlib_license.html", + "https://opensource.org/licenses/Zlib" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/zlib-acknowledgement.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/zlib-acknowledgement.json", + "referenceNumber": 348, + "name": "zlib/libpng License with Acknowledgement", + "licenseId": "zlib-acknowledgement", + "seeAlso": [ + "https://fedoraproject.org/wiki/Licensing/ZlibWithAcknowledgement" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/ZPL-1.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ZPL-1.1.json", + "referenceNumber": 545, + "name": "Zope Public License 1.1", + "licenseId": "ZPL-1.1", + "seeAlso": [ + "http://old.zope.org/Resources/License/ZPL-1.1" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/ZPL-2.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ZPL-2.0.json", + "referenceNumber": 51, + "name": "Zope Public License 2.0", + "licenseId": "ZPL-2.0", + "seeAlso": [ + "http://old.zope.org/Resources/License/ZPL-2.0", + "https://opensource.org/licenses/ZPL-2.0" + ], + "isOsiApproved": true, + "isFsfLibre": true + }, + { + "reference": "https://spdx.org/licenses/ZPL-2.1.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/ZPL-2.1.json", + "referenceNumber": 352, + "name": "Zope Public License 2.1", + "licenseId": "ZPL-2.1", + "seeAlso": [ + "http://old.zope.org/Resources/ZPL/" + ], + "isOsiApproved": true, + "isFsfLibre": true + } + ], + "releaseDate": "2024-05-22" +} diff --git a/src/omi/license.py b/src/omi/license.py new file mode 100644 index 0000000..6e4f4e7 --- /dev/null +++ b/src/omi/license.py @@ -0,0 +1,108 @@ +"""Module to check licenses.""" + +import json +import re +from pathlib import Path + +LICENCES_FILE = Path(__file__).parent / "data" / "licenses.json" + + +class LicenseError(Exception): + """Exception raised when a license is invalid.""" + + +def normalize_license_name(name: str) -> str: + """ + Normalize license name. + + Replace whitespaces with hyphens and convert to uppercase + + Parameters + ---------- + name: str + License name + + Returns + ------- + str + Normalized license name + """ + return re.sub(r"\s", "-", name).upper() + + +def read_licenses() -> set[str]: + """ + Read license IDs from SPDX licenses. + + Returns + ------- + set[str] + Set of license IDs + """ + with LICENCES_FILE.open("r", encoding="utf-8") as file: + licenses = json.load(file) + # Create a set of unique license ID values + return {license_info.get("licenseId").upper() for license_info in licenses["licenses"]} + + +def validate_license(license_id: str) -> bool: + """ + Validate single license ID. + + Parameters + ---------- + license_id: str + License ID + + Returns + ------- + bool + True if valid, False otherwise + """ + normalized_license = normalize_license_name(license_id) + if normalized_license not in LICENSES: + return False + return True + + +def validate_oemetadata_licenses(metadata: dict) -> None: + """ + Validate licenses in OEMetadata. + + Parameters + ---------- + metadata: dict + OEMetadata dictionary + + Raises + ------ + LicenseError + Raised if license is invalid or no license is found + + Returns + ------- + None + if licences are valid, otherwise LicenseError is raised + """ + if metadata is None: + msg = "Metadata is empty." + raise LicenseError(msg) + + licenses = metadata.get("licenses", []) + + if not licenses: + msg = "No license information available in the metadata." + raise LicenseError(msg) + + for i, license_ in enumerate(licenses): + if not license_.get("name"): + raise LicenseError(f"The license name is missing in {i}. license ({license_})") + + if not validate_license(license_["name"]): + raise LicenseError( + f"The (normalized) license name '{license_['name']}' was not found in the SPDX licenses list. " + "(See https://github.com/spdx/license-list-data/blob/main/json/licenses.json).", + ) + + +LICENSES = read_licenses() From 38f6bceade1e484ed219524c8343e1dba99f9009 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Wed, 5 Jun 2024 08:56:59 +0200 Subject: [PATCH 17/63] Minor change --- tests/test_data/data.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_data/data.csv b/tests/test_data/data.csv index 2f683a8..2818778 100644 --- a/tests/test_data/data.csv +++ b/tests/test_data/data.csv @@ -1,3 +1,3 @@ string;integer;number;array string;array integer;array float;object;date;bool -gas;55;0.5;["helo", "none"];[1, 2, 3];[1.5, 1.3, 1.8];{"a": 300, "b": "hello"};2024-01-01;true -wind;18;1.8;["tut", "tut", "tut"];[3, 6, 3];[4.55, 44.7, 23.999];{"c": 2, "d": 4};2024-02-02;false +gas;55;0.5;["helo", "none"];[1, 2, 3];[1.5, 1.3, 1.8];{"a": 300, "b": "hello"};2024-01-01;True +wind;18;1.8;["tut", "tut", "tut"];[3, 6, 3];[4.55, 44.7, 23.999];{"c": 2, "d": 4};2024-02-02;False From 04da51a38d561b0803f040bb5ace642bffc862b8 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Wed, 5 Jun 2024 14:47:12 +0200 Subject: [PATCH 18/63] Implement data validation --- poetry.lock | 142 +++++++- pyproject.toml | 1 + src/omi/base.py | 28 ++ src/omi/settings.py | 3 + src/omi/validation.py | 305 +++++++++++++++++- tests/test_base.py | 28 ++ tests/test_data/{ => inspection}/data.csv | 0 tests/test_data/validation/data.csv | 3 + .../hackathon_lignite_hh_invalid.csv | 3 + .../validation/hackathon_lignite_hh_valid.csv | 3 + .../validation/metadata_for_data_csv.json | 215 ++++++++++++ tests/test_data_validation.py | 78 +++++ tests/test_inspection.py | 2 +- ...idation.py => test_metadata_validation.py} | 0 14 files changed, 808 insertions(+), 3 deletions(-) create mode 100644 src/omi/settings.py create mode 100644 tests/test_base.py rename tests/test_data/{ => inspection}/data.csv (100%) create mode 100644 tests/test_data/validation/data.csv create mode 100644 tests/test_data/validation/hackathon_lignite_hh_invalid.csv create mode 100644 tests/test_data/validation/hackathon_lignite_hh_valid.csv create mode 100644 tests/test_data/validation/metadata_for_data_csv.json create mode 100644 tests/test_data_validation.py rename tests/{test_validation.py => test_metadata_validation.py} (100%) diff --git a/poetry.lock b/poetry.lock index 97d92cb..4a7c4ed 100644 --- a/poetry.lock +++ b/poetry.lock @@ -545,6 +545,51 @@ files = [ [package.dependencies] setuptools = "*" +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + [[package]] name = "oemetadata" version = "1.6.0" @@ -567,6 +612,79 @@ files = [ {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] +[[package]] +name = "pandas" +version = "2.2.2" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, + {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, + {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, + {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, + {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, + {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + [[package]] name = "petl" version = "1.7.15" @@ -852,6 +970,17 @@ text-unidecode = ">=1.3" [package.extras] unidecode = ["Unidecode (>=1.1.1)"] +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + [[package]] name = "pyyaml" version = "6.0.1" @@ -1258,6 +1387,17 @@ files = [ {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, ] +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + [[package]] name = "urllib3" version = "2.2.1" @@ -1309,4 +1449,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "d48feff977ab413b36949f3de503698612ccbba878b732bffb2a02a0341e1883" +content-hash = "0ee4a82f7293c59ce766ec9079900e1d13eb3ade6ae15a36fa50c7772dbe4917" diff --git a/pyproject.toml b/pyproject.toml index 69d3c87..02e670d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ python-dateutil = "^2.9.0" jsonschema = "^4.22.0" oemetadata = ">=1.5.2" frictionless = "^5.17.0" +pandas = "^2.2.2" [tool.poetry.group.dev.dependencies] tox = "^4.15.0" diff --git a/src/omi/base.py b/src/omi/base.py index f4f7ce6..7871645 100644 --- a/src/omi/base.py +++ b/src/omi/base.py @@ -6,8 +6,11 @@ import pathlib from dataclasses import dataclass +import requests from metadata import v152, v160 +from .settings import OEP_URL + # Order matters! First entry equals latest version of metadata format METADATA_FORMATS = {"OEP": ["OEP-1.6.0", "OEP-1.5.2"], "INSPIRE": []} METADATA_VERSIONS = {version: md_format for md_format, versions in METADATA_FORMATS.items() for version in versions} @@ -26,6 +29,31 @@ class MetadataSpecification: example: dict | None = None +def get_metadata_from_oep_table(oep_table: str, oep_schema: str = "model_draft") -> dict: + """ + Get metadata from OEP table. + + Parameters + ---------- + oep_table: str + OEP table name + oep_schema: str + OEP schema name + + Returns + ------- + dict + Metadata in OEMetadata format + """ + response = requests.get(f"{OEP_URL}/api/v0/schema/{oep_schema}/tables/{oep_table}/meta/", timeout=90) + if response.status_code != requests.codes.ok: + raise MetadataError(f"Could not retrieve metadata from OEP table '{oep_schema}.{oep_table}'.") + metadata = response.json() + if not metadata: + raise MetadataError(f"Metadata from '{oep_schema}.{oep_table}' is empty.") + return metadata + + def extract_metadata_version(metadata: dict) -> str: """ Extract metadata version from metadata. diff --git a/src/omi/settings.py b/src/omi/settings.py new file mode 100644 index 0000000..0b1d46d --- /dev/null +++ b/src/omi/settings.py @@ -0,0 +1,3 @@ +"""Settings module for OMI.""" + +OEP_URL = "https://openenergyplatform.org" diff --git a/src/omi/validation.py b/src/omi/validation.py index cc2388e..df7c74f 100644 --- a/src/omi/validation.py +++ b/src/omi/validation.py @@ -3,8 +3,44 @@ from __future__ import annotations import jsonschema +import pandas as pd +import requests +from frictionless import Field, Report, Resource, Schema +from frictionless.fields import ( + ArrayField, + BooleanField, + DateField, + DatetimeField, + IntegerField, + NumberField, + ObjectField, + StringField, +) -from omi.base import extract_metadata_version, get_metadata_specification +from omi import license +from omi.base import ( + MetadataError, + extract_metadata_version, + get_metadata_from_oep_table, + get_metadata_specification, +) +from omi.settings import OEP_URL + +FRICTIONLESS_FIELD_MAPPING = { + "string": StringField, + "text": StringField, + "integer": IntegerField, + "bigint": IntegerField, + "float": NumberField, + "float array": ArrayField, + "double precision": NumberField, + "boolean": BooleanField, + "date": DateField, + "datetime": DatetimeField, + "array": ArrayField, + "object": ObjectField, + "json": ObjectField, +} class ValidationError(Exception): @@ -28,3 +64,270 @@ def validate_metadata(metadata: dict) -> None: metadata_version = extract_metadata_version(metadata) metadata_schema = get_metadata_specification(metadata_version) jsonschema.validate(metadata, metadata_schema.schema) + license.validate_oemetadata_licenses(metadata) + + +def validate_data( + data: pd.DataFrame, + *, + metadata: dict | None = None, + oep_table: str | None = None, + oep_schema: str | None = None, + return_report: bool = False, +) -> None | Report: + """ + Validate data against given metadata or table definition on OEP including metadata on OEP (if set). + + Parameters + ---------- + data: pd.DataFrame + Data to validate + metadata: dict | None + Metadata in OEMetadata format. If given, data is validated against metadata schema. + oep_table: str | None + Table name on OEP. If given, data is validated against OEP table. + oep_schema: str | None + Schema name on OEP. If given, data is validated against OEP table. + return_report: bool + If set to True, instead of raising an error if data is invalid an error report is returned + + Raises + ------ + ValidationError + if data is not in pandas.DataFrame format + if field is missing in table or metadata definition + if data does not fit to OEP table definition and/or metadata schema (if `return_report` is set to True, + instead of raising an error a report is returned) + + Returns + ------- + None + if everything is valid. Otherwise, it raises an exception. + """ + if not isinstance(data, pd.DataFrame): + msg = "Data must be given as pandas.DataFrame." + raise ValidationError(msg) + if not metadata and (not oep_table or not oep_schema): + msg = "You must either set metadata or OEP table to validate data against." + raise ValidationError(msg) + if metadata and (oep_table or oep_schema): + msg = ( + "Cannot validate data against both metadata and OEP table. " + "Please set only metadata or OEP table name and schema." + ) + raise ValidationError( + msg, + ) + + if metadata: + return validate_data_against_metadata(data, metadata, return_report=return_report) + return validate_data_against_oep_table(data, oep_table, oep_schema, return_report=return_report) + + +def validate_data_against_oep_table( + data: pd.DataFrame, + oep_table: str, + oep_schema: str, + *, + return_report: bool = True, +) -> None | Report: + """ + Validate data against given metadata. + + Parameters + ---------- + data: pandas.DataFrame + Data to validate + oep_table: str + OEP table name + oep_schema: str + OEP schema name + return_report: bool + If set to True, report is returned instead of raising an error. + + Returns + ------- + Report + Frictionless report if `return_report` is set to True, otherwise None is returned. + """ + if not oep_table or not oep_schema: + msg = "You must set OEP table AND schema." + raise ValidationError(msg) + + # First validate data against table definition + table_fields = __get_fields_from_oep_table(oep_table, oep_schema) + report = __validate_data_against_schema(data, table_fields) + if not report.valid: + if return_report: + return report + raise ValidationError(f"Data validation failed against OEP table definition. Reason: {report.tasks[0].errors}") + + # Second, validate data against metadata from table + try: + metadata = get_metadata_from_oep_table(oep_table, oep_schema) + except MetadataError: + return None + metadata_fields = __get_fields_from_metadata(metadata) + report = __validate_data_against_schema(data, metadata_fields) + if not report.valid: + if return_report: + return report + raise ValidationError( + f"Data validation failed against metadata from OEP table. Reason: {report.tasks[0].errors}", + ) + return None + + +def validate_data_against_metadata( + data: pd.DataFrame, + metadata: dict, + *, + return_report: bool = True, +) -> None | Report: + """ + Validate data against given metadata. + + Parameters + ---------- + data: pandas.DataFrame + Data to validate + metadata: dict + Metadata in OEMetadata format. + return_report: bool + If set to True, report is returned instead of raising an error. + + Returns + ------- + Report + Frictionless report if `return_report` is set to True, otherwise None is returned. + """ + metadata_fields = __get_fields_from_metadata(metadata) + report = __validate_data_against_schema(data, metadata_fields) + if not report.valid: + if return_report: + return report + raise ValidationError(f"Data validation failed. Reason: {report.tasks[0].errors}") + return None + + +def __validate_data_against_schema(data: pd.DataFrame, fields: dict[str, str]) -> Report: + """ + Validate data against related schema definition and return frictionless report. + + Parameters + ---------- + data: pandas.DataFrame + Date to validate + fields: dict[str, str] + Dictionary of fields and their types to validate data with + + Returns + ------- + Report + Frictionless report of validated data + """ + ordered_fields = {} + for field in data.columns: + if field not in fields: + raise ValidationError(f"Could not find field '{field}' in schema.") + ordered_fields[field] = fields[field] + frictionless_fields = __map_fields_to_frictionless_fields(ordered_fields) + schema = Schema(fields=frictionless_fields) + resource = Resource( + data=data, + profile="tabular-data-resource", + schema=schema, + ) + report = resource.validate() + return report + + +def __map_fields_to_frictionless_fields(fields: dict[str, str]) -> list[Field]: + """ + Map fields to Frictionless fields. + + Parameters + ---------- + fields: dict[str, str] + Dictionary of fields holding name and related type as string + + Raises + ------ + ValidationError + if field cannot be mapped to Frictionless field + + Returns + ------- + list[Field] + List of frictionless Fields + """ + frictionless_fields = [] + for field_name, field_type in fields.items(): + if field_type.endswith("[]"): + # This indicates an array field + frictionless_fields.append(ArrayField(name=field_name)) + continue + if field_type not in FRICTIONLESS_FIELD_MAPPING: + raise ValidationError( + f"Field '{field_name} with type '{field_type}' cannot be mapped to Frictionless fields", + ) + f_field = FRICTIONLESS_FIELD_MAPPING[field_type] + frictionless_fields.append(f_field(name=field_name)) + return frictionless_fields + + +def __get_fields_from_oep_table(oep_table: str, oep_schema: str) -> dict[str, str]: + """ + Get table fields and related types from OEP table. + + Parameters + ---------- + oep_table: str + Table name on OEP + oep_schema: str + Schema name on OEP + + Returns + ------- + dict[str, str] + Dictionary of table fields and related types. + """ + response = requests.get(f"{OEP_URL}/api/v0/schema/{oep_schema}/tables/{oep_table}/columns", timeout=90) + if response.status_code != requests.codes.ok: + raise ValidationError(f"Could not find table '{oep_table}' in schema '{oep_schema}' on OEP.") + table_fields = response.json() + return {name: field["data_type"] for name, field in table_fields.items()} + + +def __get_fields_from_metadata(metadata: dict) -> dict[str, str]: + """ + Get fields and related types from metadata. + + Parameters + ---------- + metadata: dict + Metadata in OEMetadata format. + + Raises + ------ + ValidationError + if fields cannot be extracted from metadata. + + Returns + ------- + dict[str, str] + Dictionary of fields and related types. + """ + if "resources" not in metadata: + msg = "No resources found in metadata." + raise ValidationError(msg) + if len(metadata["resources"]) > 1: + msg = "More than one rsource found in metadata. Can only validate single resource." + raise ValidationError(msg) + if "schema" not in metadata["resources"][0]: + msg = "No schema found in metadata resource." + raise ValidationError(msg) + if "fields" not in metadata["resources"][0]["schema"]: + msg = "No fields found in resource schema." + raise ValidationError(msg) + return {field["name"]: field["type"] for field in metadata["resources"][0]["schema"]["fields"]} diff --git a/tests/test_base.py b/tests/test_base.py new file mode 100644 index 0000000..7fbd7f0 --- /dev/null +++ b/tests/test_base.py @@ -0,0 +1,28 @@ +"""Tests for OMIs `base` package.""" +import pytest + +from omi import base, validation + + +def test_metadata_from_oep(): + """Test metadata from OEP.""" + metadata = base.get_metadata_from_oep_table("ind_steel_pellet_1") + validation.validate_metadata(metadata) + + +def test_metadata_from_oep_non_existing_table(): + """Test error for non existing table.""" + with pytest.raises( + base.MetadataError, + match="Could not retrieve metadata from OEP table 'model_draft.non_existing_table'.", + ): + base.get_metadata_from_oep_table("non_existing_table") + + +def test_metadata_from_oep_empty(): + """Test error for empty metadata.""" + with pytest.raises( + base.MetadataError, + match="Metadata from 'model_draft.bnetza_eeg_anlagenstammdaten_wind_classification' is empty.", + ): + base.get_metadata_from_oep_table("bnetza_eeg_anlagenstammdaten_wind_classification") diff --git a/tests/test_data/data.csv b/tests/test_data/inspection/data.csv similarity index 100% rename from tests/test_data/data.csv rename to tests/test_data/inspection/data.csv diff --git a/tests/test_data/validation/data.csv b/tests/test_data/validation/data.csv new file mode 100644 index 0000000..b0ed699 --- /dev/null +++ b/tests/test_data/validation/data.csv @@ -0,0 +1,3 @@ +id;region;year;cost_var_e;bandwidth_type;source;method;comment;version +0;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 +0;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 diff --git a/tests/test_data/validation/hackathon_lignite_hh_invalid.csv b/tests/test_data/validation/hackathon_lignite_hh_invalid.csv new file mode 100644 index 0000000..bc93869 --- /dev/null +++ b/tests/test_data/validation/hackathon_lignite_hh_invalid.csv @@ -0,0 +1,3 @@ +id;region;year;natural_domestic_limit;bandwidth_type;source;method;comment;version +0;DE;2024;100.0;should_be_json;{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 +0;DE;2024;100.0;should_be_json;{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 diff --git a/tests/test_data/validation/hackathon_lignite_hh_valid.csv b/tests/test_data/validation/hackathon_lignite_hh_valid.csv new file mode 100644 index 0000000..a6ef6ec --- /dev/null +++ b/tests/test_data/validation/hackathon_lignite_hh_valid.csv @@ -0,0 +1,3 @@ +id;region;year;natural_domestic_limit;bandwidth_type;source;method;comment;version +0;["DE"];2024;100.0;{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 +0;["DE"];2024;100.0;{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 diff --git a/tests/test_data/validation/metadata_for_data_csv.json b/tests/test_data/validation/metadata_for_data_csv.json new file mode 100644 index 0000000..fcbc28e --- /dev/null +++ b/tests/test_data/validation/metadata_for_data_csv.json @@ -0,0 +1,215 @@ +{ + "name":"x2x_import_lng", + "title":"sedos_x2x_import_lng", + "id":null, + "description":"Techno-economic parameter for import of liquified natural gas (LNG).", + "subject":[ + { + "name":"import", + "path":"http://openenergy-platform.org/ontology/oeo/OEO_00020201" + }, + { + "name":"liquified natural gas", + "path":"http://openenergy-platform.org/ontology/oeo/oeo-physical/OEO_00010237" + } + ], + "language":[ + "en-GB" + ], + "keywords":[ + "SEDOS", + "AP5", + "X2X", + "oedatamodel-parameter", + "input_data" + ], + "publicationDate":"2024-04-08", + "context":{ + "homepage":"https://sedos-project.github.io/.github/", + "documentation":"https://sedos-project.github.io/.github/", + "sourceCode":"https://github.com/sedos-project", + "contact":"g.mueller@fz-juelich.de", + "grantNo":"03EI1040D", + "fundingAgency":"Bundesministerium für Wirtschaft und Klimaschutz (BMWK)", + "fundingAgencyLogo":"https://en.wikipedia.org/wiki/Federal_Ministry_for_Economic_Affairs_and_Climate_Action#/media/File:BMWi_Logo_2021.svg" + }, + "spatial":{ + "location":"germany", + "extent":null, + "resolution":null + }, + "temporal":{ + "referenceDate":null, + "timeseries":[ + { + "start":null, + "end":null, + "resolution":null, + "alignment":null, + "aggregationType":null + } + ] + }, + "sources":[ + { + "title":"TYNDP 2022. Scenario Building Guidelines.", + "description":null, + "path":"https://2022.entsos-tyndp-scenarios.eu/wp-content/uploads/2022/04/TYNDP_2022_Scenario_Building_Guidelines_Version_April_2022.pdf", + "licenses":[ + { + "instruction":null, + "attribution":"copyright. ENTSOG, ENTSO-E, 2022.", + "name":null, + "title":null, + "path":null + } + ] + }, + { + "title":"Economics of Gas Transportation by Pipeline and LNG", + "description":null, + "path":"https://link.springer.com/chapter/10.1007/978-3-030-86884-0_2", + "licenses":[ + { + "instruction":"You are free to share and adapt. You must give appropriate credit.", + "attribution":"copyright. Molnar, 2022.", + "name":"CC BY 4.0 DEED", + "title":"Attribution 4.0 International", + "path":"https://creativecommons.org/licenses/by/4.0/" + } + ] + } + ], + "licenses":[ + { + "instruction":"You are free: To Share, To Create, To Adapt", + "attribution":null, + "name":"CC0-1.0", + "title":"Creative Commons Zero v1.0 Universal", + "path":"https://creativecommons.org/publicdomain/zero/1.0/legalcode" + } + ], + "contributors":[ + { + "title":"Gian Müller, FZJ", + "email":"g.mueller@fz-juelich.de", + "object":"data and metadata", + "comment":"Data for import of liquified natural gas (LNG).", + "date":"2024-04-08" + } + ], + "resources":[ + { + "profile":"tabular-data-resource", + "name":"model_draft.x2x_import_lng", + "path":null, + "format":"PostgreSQL", + "encoding":"UTF-8", + "schema":{ + "primaryKey":[ + "id" + ], + "foreignKeys":[ ], + "fields":[ + { + "name":"id", + "description":"Unique identifier", + "type":"bigint", + "isAbout":[ ], + "valueReference":[ ], + "unit":null + }, + { + "name":"region", + "description":"Country or region", + "type":"text", + "isAbout":[ ], + "valueReference":[ ], + "unit":null + }, + { + "name":"year", + "description":"Year", + "type":"integer", + "isAbout":[ ], + "valueReference":[ ], + "unit":"a" + }, + { + "name":"cost_var_e", + "description":"Variable costs in cost per unit of a certain good.", + "type":"float array", + "isAbout":[ ], + "valueReference":[ ], + "unit":"€/MWh" + }, + { + "name":"bandwidth_type", + "description":"Bandwidth Type", + "type":"json", + "isAbout":[ ], + "valueReference":[ ], + "unit":null + }, + { + "name":"version", + "description":"Version", + "type":"text", + "isAbout":[ ], + "valueReference":[ ], + "unit":null + }, + { + "name":"method", + "description":"Method", + "type":"json", + "isAbout":[ ], + "valueReference":[ ], + "unit":null + }, + { + "name":"source", + "description":"Source", + "type":"json", + "isAbout":[ ], + "valueReference":[ ], + "unit":null + }, + { + "name":"comment", + "description":"Comment", + "type":"text", + "isAbout":[ ], + "valueReference":[ ], + "unit":null + } + ] + }, + "dialect":{ + "delimiter":";", + "decimalSeparator":"." + } + } + ], + "@id":null, + "@context":"https://raw.githubusercontent.com/OpenEnergyPlatform/oemetadata/develop/metadata/latest/context.json", + "review":null, + "metaMetadata":{ + "metadataVersion":"OEP-1.5.2", + "metadataLicense":{ + "name":"CC0-1.0", + "title":"Creative Commons Zero v1.0 Universal", + "path":"https://creativecommons.org/publicdomain/zero/1.0/" + } + }, + "_comment":{ + "metadata":"Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/oemetadata)", + "dates":"Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", + "units":"Use a space between numbers and units (100 m)", + "languages":"Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", + "licenses":"License name must follow the SPDX License List (https://spdx.org/licenses/)", + "review":"Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/blob/master/data-review/manual/review_manual.md)", + "null":"If not applicable use: null", + "todo":"If a value is not yet available, use: todo" + } +} diff --git a/tests/test_data_validation.py b/tests/test_data_validation.py new file mode 100644 index 0000000..16e3f1c --- /dev/null +++ b/tests/test_data_validation.py @@ -0,0 +1,78 @@ +"""Tests for validating data via OMI.""" +import json +import pathlib + +import pandas as pd +import pytest +from frictionless import Report + +from omi import validation + + +def test_data_validation_against_oep(): + """Test data validation with example file against OEP table.""" + valid_data_file = pathlib.Path(__file__).parent / "test_data" / "validation" / "hackathon_lignite_hh_valid.csv" + valid_data = pd.read_csv(valid_data_file, delimiter=";") + validation.validate_data(valid_data, oep_table="hackathon_com_lignite_hh", oep_schema="model_draft") + + +def test_data_validation_against_metadata(): + """Test data validation with example file against metadata from file.""" + valid_data_file = pathlib.Path(__file__).parent / "test_data" / "validation" / "data.csv" + valid_data = pd.read_csv(valid_data_file, delimiter=";") + metadata_file = pathlib.Path(__file__).parent / "test_data" / "validation" / "metadata_for_data_csv.json" + with metadata_file.open("r") as f: + metadata = json.load(f) + validation.validate_data(valid_data, metadata=metadata) + + +def test_data_validation_report(): + """Test data validation with example file.""" + valid_data_file = pathlib.Path(__file__).parent / "test_data" / "validation" / "hackathon_lignite_hh_valid.csv" + valid_data = pd.read_csv(valid_data_file, delimiter=";") + report = validation.validate_data( + valid_data, + oep_table="hackathon_com_lignite_hh", + oep_schema="model_draft", + return_report=True, + ) + assert report is None + + +def test_data_validation_invalid(): + """Test invalid data validation with example file.""" + invalid_data_file = pathlib.Path(__file__).parent / "test_data" / "validation" / "hackathon_lignite_hh_invalid.csv" + invalid_data = pd.read_csv(invalid_data_file, delimiter=";") + with pytest.raises(validation.ValidationError, match="Data validation failed."): + validation.validate_data(invalid_data, oep_table="hackathon_com_lignite_hh", oep_schema="model_draft") + + +def test_data_validation_invalid_report(): + """Test invalid data validation with example file.""" + invalid_data_file = pathlib.Path(__file__).parent / "test_data" / "validation" / "hackathon_lignite_hh_invalid.csv" + invalid_data = pd.read_csv(invalid_data_file, delimiter=";") + report = validation.validate_data( + invalid_data, + oep_table="hackathon_com_lignite_hh", + oep_schema="model_draft", + return_report=True, + ) + assert isinstance(report, Report) + assert not report.valid + + +def test_invalid_arguments_to_validation_function(): + """Test different invalid function calls to validation function.""" + with pytest.raises(validation.ValidationError, match="Data must be given as pandas.DataFrame."): + validation.validate_data({}) + with pytest.raises( + validation.ValidationError, + match="You must either set metadata or OEP table to validate data against.", + ): + validation.validate_data(pd.DataFrame()) + with pytest.raises(validation.ValidationError, match="Cannot validate data against both metadata and OEP table."): + validation.validate_data(pd.DataFrame(), metadata={"a": "a"}, oep_table="a", oep_schema="a") + with pytest.raises(validation.ValidationError, match="Cannot validate data against both metadata and OEP table."): + validation.validate_data(pd.DataFrame(), metadata={"a": "a"}, oep_table="a") + with pytest.raises(validation.ValidationError, match="Cannot validate data against both metadata and OEP table."): + validation.validate_data(pd.DataFrame(), metadata={"a": "a"}, oep_schema="a") diff --git a/tests/test_inspection.py b/tests/test_inspection.py index bd4a7c3..8cf504b 100644 --- a/tests/test_inspection.py +++ b/tests/test_inspection.py @@ -4,7 +4,7 @@ from omi import inspection -CSV_DATA_FILE = pathlib.Path(__file__).parent / "test_data" / "data.csv" +CSV_DATA_FILE = pathlib.Path(__file__).parent / "test_data" / "inspection" / "data.csv" def test_inspection(): diff --git a/tests/test_validation.py b/tests/test_metadata_validation.py similarity index 100% rename from tests/test_validation.py rename to tests/test_metadata_validation.py From 2949c06fb9bf182d5b3e5a6ba309a449bbf60ada Mon Sep 17 00:00:00 2001 From: christian-rli Date: Thu, 6 Jun 2024 18:01:27 +0200 Subject: [PATCH 19/63] Add examples of types of faulty metadata #103 --- tests/test_data/additional_key.json | 41 ++++++++++++++++ tests/test_data/duplicate_key.json | 41 ++++++++++++++++ tests/test_data/missing_fields.json | 33 +++++++++++++ tests/test_data/wrong.json | 40 ++++++++++++++++ tests/test_data/wrong_datatype.json | 41 ++++++++++++++++ tests/test_data/wrong_json_syntax.json | 40 ++++++++++++++++ tests/test_data/wrong_key.json | 40 ++++++++++++++++ tests/test_data/wrong_nesting.json | 40 ++++++++++++++++ tests/test_data/wrong_structure.json | 46 ++++++++++++++++++ tests/test_data/wrong_version.json | 47 +++++++++++++++++++ .../test_data/wrongly_placed_null_value.json | 36 ++++++++++++++ 11 files changed, 445 insertions(+) create mode 100644 tests/test_data/additional_key.json create mode 100644 tests/test_data/duplicate_key.json create mode 100644 tests/test_data/missing_fields.json create mode 100644 tests/test_data/wrong.json create mode 100644 tests/test_data/wrong_datatype.json create mode 100644 tests/test_data/wrong_json_syntax.json create mode 100644 tests/test_data/wrong_key.json create mode 100644 tests/test_data/wrong_nesting.json create mode 100644 tests/test_data/wrong_structure.json create mode 100644 tests/test_data/wrong_version.json create mode 100644 tests/test_data/wrongly_placed_null_value.json diff --git a/tests/test_data/additional_key.json b/tests/test_data/additional_key.json new file mode 100644 index 0000000..ac304f2 --- /dev/null +++ b/tests/test_data/additional_key.json @@ -0,0 +1,41 @@ +{ + "title": "title", + "id": "http://such.tld/name", + "description": "These Metadata add the key 'universe'.", + "universe": "milky way", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial" + }, + { + "name": "name", + "description": "Example name", + "type": "text" + } + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.6.0" + } +} diff --git a/tests/test_data/duplicate_key.json b/tests/test_data/duplicate_key.json new file mode 100644 index 0000000..32ae540 --- /dev/null +++ b/tests/test_data/duplicate_key.json @@ -0,0 +1,41 @@ +{ + "title": "title", + "id": "http://such.tld/name", + "description": "These Metadata have two descrptions.", + "description": "These Metadata have two descrptions and they don't even match.", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial" + }, + { + "name": "name", + "description": "Example name", + "type": "text" + } + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.6.0" + } +} diff --git a/tests/test_data/missing_fields.json b/tests/test_data/missing_fields.json new file mode 100644 index 0000000..dbf583b --- /dev/null +++ b/tests/test_data/missing_fields.json @@ -0,0 +1,33 @@ +{ + "title": "title", + "description": "These Metadata are missing a license and an id.", + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial" + }, + { + "name": "name", + "description": "Example name", + "type": "text" + } + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.6.0" + } +} diff --git a/tests/test_data/wrong.json b/tests/test_data/wrong.json new file mode 100644 index 0000000..bc5d9c8 --- /dev/null +++ b/tests/test_data/wrong.json @@ -0,0 +1,40 @@ +{ + "title": "title", + "id": "http://such.tld/name", + "description": "These Metadata use a wrong date format in publication date.", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial" + }, + { + "name": "name", + "description": "Example name", + "type": "text" + } + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.6.0" + } +} diff --git a/tests/test_data/wrong_datatype.json b/tests/test_data/wrong_datatype.json new file mode 100644 index 0000000..58db1a6 --- /dev/null +++ b/tests/test_data/wrong_datatype.json @@ -0,0 +1,41 @@ +{ + "title": "title", + "id": "http://such.tld/name", + "description": "These Metadata use a wrong date format in publication date.", + "publicationDate": "First of June 2024", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial" + }, + { + "name": "name", + "description": "Example name", + "type": "text" + } + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.6.0" + } +} diff --git a/tests/test_data/wrong_json_syntax.json b/tests/test_data/wrong_json_syntax.json new file mode 100644 index 0000000..4402adc --- /dev/null +++ b/tests/test_data/wrong_json_syntax.json @@ -0,0 +1,40 @@ +{ + "title": "title", + "id": "http://such.tld/name", + "description": "These Metadata have extra symbols, braking the structure: line 28 has double colons, extra quotes a semicolon and an extra curly bracket.", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial" + }, + { + "name": "name", + "description":: ""Example'' name";} + "type": "text" + } + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.6.0" + } +} diff --git a/tests/test_data/wrong_key.json b/tests/test_data/wrong_key.json new file mode 100644 index 0000000..5322e2c --- /dev/null +++ b/tests/test_data/wrong_key.json @@ -0,0 +1,40 @@ +{ + "titel": "titel", + "id": "http://such.tld/name", + "description": "These Metadata misspell 'title' as 'titel'.", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial" + }, + { + "name": "name", + "description": "Example name", + "type": "text" + } + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.6.0" + } +} diff --git a/tests/test_data/wrong_nesting.json b/tests/test_data/wrong_nesting.json new file mode 100644 index 0000000..c25f9b0 --- /dev/null +++ b/tests/test_data/wrong_nesting.json @@ -0,0 +1,40 @@ +{ + "title": "title", + "id": "http://such.tld/name", + "description": "These Metadata wrongly put the name of the first field object in a list.", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + ["name": "id"], + "description": "Unique identifier", + "type": "serial" + }, + { + "name": "name", + "description": "Example name", + "type": "text" + } + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.6.0" + } +} diff --git a/tests/test_data/wrong_structure.json b/tests/test_data/wrong_structure.json new file mode 100644 index 0000000..a3165f9 --- /dev/null +++ b/tests/test_data/wrong_structure.json @@ -0,0 +1,46 @@ +{ + "title": "title", + "id": "http://such.tld/name", + "description": "These metadata use a list in spatial, when it should be a dictionary.", + "publicationDate": "First of June 2024", + "spatial": [ + "location": null, + "extent": "europe", + "resolution": "100 m" + ], + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial" + }, + { + "name": "name", + "description": "Example name", + "type": "text" + } + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.6.0" + } +} diff --git a/tests/test_data/wrong_version.json b/tests/test_data/wrong_version.json new file mode 100644 index 0000000..3461ba5 --- /dev/null +++ b/tests/test_data/wrong_version.json @@ -0,0 +1,47 @@ +{ + "title": "title", + "id": "http://such.tld/name", + "description": "These metadata say they're v1.5.0 but 'isAbout' is only defined in version 1.5.2 or later.", + "publicationDate": "First of June 2024", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial" + }, + { + "name": "name", + "description": "Example name", + "type": "text", + "isAbout": [ + { + "name": "written name", + "path": "https://openenergy-platform.org/ontology/oeo/IAO_0000590" + } + ] + } + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.5.0" + } +} diff --git a/tests/test_data/wrongly_placed_null_value.json b/tests/test_data/wrongly_placed_null_value.json new file mode 100644 index 0000000..9b6a81d --- /dev/null +++ b/tests/test_data/wrongly_placed_null_value.json @@ -0,0 +1,36 @@ +{ + "title": "title", + "id": "http://such.tld/name", + "description": "These Metadata use a null value instead of a second object in fields.", + "licenses": [ + { + "name": "ODbL-1.0", + "title": "Open Data Commons Open Database License 1.0" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "very.name", + "path": "http://such.tld/name", + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "serial" + }, + null + ], + "primaryKey": [ + "id" + ] + } + } + ], + "metaMetadata": { + "metadataVersion": "OEP-1.6.0" + } +} From 37691f0931a32d288010434992a0502a5640104a Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Fri, 7 Jun 2024 09:32:54 +0200 Subject: [PATCH 20/63] Rename metadata version function --- src/omi/base.py | 2 +- src/omi/conversion.py | 6 +++--- src/omi/validation.py | 4 ++-- tests/test_conversion.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/omi/base.py b/src/omi/base.py index 7871645..6469c9b 100644 --- a/src/omi/base.py +++ b/src/omi/base.py @@ -54,7 +54,7 @@ def get_metadata_from_oep_table(oep_table: str, oep_schema: str = "model_draft") return metadata -def extract_metadata_version(metadata: dict) -> str: +def get_metadata_version(metadata: dict) -> str: """ Extract metadata version from metadata. diff --git a/src/omi/conversion.py b/src/omi/conversion.py index 379dd5d..8fa1270 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -3,7 +3,7 @@ from copy import deepcopy -from omi.base import extract_metadata_version +from omi.base import get_metadata_version class ConversionError(Exception): @@ -26,11 +26,11 @@ def convert_metadata(metadata: dict, target_version: str) -> dict: dict Updated metadata """ - metadata_version = extract_metadata_version(metadata) + metadata_version = get_metadata_version(metadata) conversion_chain = __get_conversion_chain(metadata_version, target_version) converted_metadata = deepcopy(metadata) for next_version in conversion_chain[1:]: - current_version = extract_metadata_version(converted_metadata) + current_version = get_metadata_version(converted_metadata) converted_metadata = METADATA_CONVERSIONS[(current_version, next_version)](converted_metadata) return converted_metadata diff --git a/src/omi/validation.py b/src/omi/validation.py index df7c74f..7454ed1 100644 --- a/src/omi/validation.py +++ b/src/omi/validation.py @@ -20,9 +20,9 @@ from omi import license from omi.base import ( MetadataError, - extract_metadata_version, get_metadata_from_oep_table, get_metadata_specification, + get_metadata_version, ) from omi.settings import OEP_URL @@ -61,7 +61,7 @@ def validate_metadata(metadata: dict) -> None: None if metadata schema is valid. Otherwise it raises an exception. """ - metadata_version = extract_metadata_version(metadata) + metadata_version = get_metadata_version(metadata) metadata_schema = get_metadata_specification(metadata_version) jsonschema.validate(metadata, metadata_schema.schema) license.validate_oemetadata_licenses(metadata) diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 1fb3892..6c3e1e4 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -9,7 +9,7 @@ def test_conversion_from_oep_152_to_160(): """Test conversion from OEP v1.5.2 -> v1.6.0.""" metadata_schema_152 = omi.base.get_metadata_specification("OEP-1.5.2").example converted_metadata_152 = conversion.convert_metadata(metadata_schema_152, "OEP-1.6.0") - assert base.extract_metadata_version(converted_metadata_152) == "OEP-1.6.0" + assert base.get_metadata_version(converted_metadata_152) == "OEP-1.6.0" validation.validate_metadata(converted_metadata_152) @@ -44,7 +44,7 @@ def c_e_conversion(md: dict) -> dict: # Create dummy metadata in OEP format with version "a" metadata = {"name": "a", "value": 10, "metaMetadata": {"metadataVersion": "a"}} converted_metadata = conversion.convert_metadata(metadata, "e") - assert base.extract_metadata_version(converted_metadata) == "e" + assert base.get_metadata_version(converted_metadata) == "e" assert converted_metadata["value"] == 10 * 3 * 5 From bc5022215cb02717aa15575d53d47e7f041aea37 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 10 Jun 2024 15:31:09 +0200 Subject: [PATCH 21/63] Add comparison between OEP table and metadata --- src/omi/validation.py | 85 ++++++ .../invalid_oep_metadata_example.json | 0 .../validation/metadata_oep_validation.json | 244 ++++++++++++++++++ .../unsupported_oep_metadata_example.json | 0 tests/test_metadata_validation.py | 71 ++++- 5 files changed, 396 insertions(+), 4 deletions(-) rename tests/test_data/{ => validation}/invalid_oep_metadata_example.json (100%) create mode 100644 tests/test_data/validation/metadata_oep_validation.json rename tests/test_data/{ => validation}/unsupported_oep_metadata_example.json (100%) diff --git a/src/omi/validation.py b/src/omi/validation.py index 7454ed1..d9d71aa 100644 --- a/src/omi/validation.py +++ b/src/omi/validation.py @@ -2,6 +2,8 @@ from __future__ import annotations +import warnings + import jsonschema import pandas as pd import requests @@ -210,6 +212,89 @@ def validate_data_against_metadata( return None +def validate_oep_table_against_metadata( # noqa: C901 + oep_table: str, + oep_schema: str, + metadata: dict | None = None, +) -> None: + """ + Validate OEP table against given metadata. + + Parameters + ---------- + oep_table: str + OEP table name + oep_schema: str + OEP schema name + metadata: dict | None + Metadata in OEMetadata format. If no metadata is given, metadata defined for OEP table on OEP is used. + + Raises + ------ + ValidationError + if OEP table schema does not fit metadata schema. + + Returns + ------- + None + if everything is valid. Otherwise, it raises an exception. + """ + if metadata is None: + metadata = get_metadata_from_oep_table(oep_table, oep_schema) + + # First check if metadata is even valid: + validate_metadata(metadata) + + errors = [] + + # Check resource name: + if "resources" not in metadata: + warnings.warn("No resource section defined in metadata. Cannot compare schema definitions.", stacklevel=1) + if len(metadata["resources"]) > 1: + warnings.warn( + "Found more than one resource in metadata. Can only compare single resource with oep table definition.", + stacklevel=1, + ) + if "name" not in metadata["resources"][0]: + raise ValidationError( + f"Metadata resource has no name. It should be one of '{oep_table}', or '{oep_schema}.{oep_table}'.", + ) + if ( + metadata["resources"][0]["name"] != oep_table + and metadata["resources"][0]["name"] != f"{oep_schema}.{oep_table}" + ): + raise ValidationError( + f"Name '{metadata['resources'][0]['name']}' of metadata resource does not fit to oep table name. " + f"It should be one of '{oep_table}', or '{oep_schema}.{oep_table}'.", + ) + + # Compare fields and related types: + oep_table_fields = __get_fields_from_oep_table(oep_table, oep_schema) + metadata_fields = __get_fields_from_metadata(metadata) + # Map fields to same field type format (using frictionless format as comparison format) + mapped_oep_table_fields = { + field.name: field.type for field in __map_fields_to_frictionless_fields(oep_table_fields) + } + mapped_metadata_fields = {field.name: field.type for field in __map_fields_to_frictionless_fields(metadata_fields)} + for field_name, field_type in mapped_oep_table_fields.items(): + if field_name not in metadata_fields: + errors.append( + f"Field '{field_name}' from OEP table '{oep_schema}.{oep_table}' is missing in metadata schema.", + ) + continue + if field_type != mapped_metadata_fields[field_name]: + errors.append( + f"Field type '{oep_table_fields[field_name]}' from OEP table field '{field_name}' " + f"differs from type '{metadata_fields[field_name]}' defined in metadata schema.", + ) + for field_name in metadata_fields: + if field_name not in oep_table_fields: + errors.append(f"Field '{field_name}' not defined in OEP table '{oep_schema}.{oep_table}'.") # noqa: PERF401 + if not errors: + return + raise ValidationError(errors) + + def __validate_data_against_schema(data: pd.DataFrame, fields: dict[str, str]) -> Report: """ Validate data against related schema definition and return frictionless report. diff --git a/tests/test_data/invalid_oep_metadata_example.json b/tests/test_data/validation/invalid_oep_metadata_example.json similarity index 100% rename from tests/test_data/invalid_oep_metadata_example.json rename to tests/test_data/validation/invalid_oep_metadata_example.json diff --git a/tests/test_data/validation/metadata_oep_validation.json b/tests/test_data/validation/metadata_oep_validation.json new file mode 100644 index 0000000..5eb1c8e --- /dev/null +++ b/tests/test_data/validation/metadata_oep_validation.json @@ -0,0 +1,244 @@ +{ + "name": "x2x_p2gas_soec_1", + "title": "sedos_x2x_p2gas_soec_1", + "id": null, + "description": "Techno-economic parameter for SOEC hydrogen electrolyzers.", + "subject": [ + { + "name": "water electrolyser", + "path": "http://openenergy-platform.org/ontology/oeo/OEO_00010021" + } + ], + "language": [ + "en-GB" + ], + "keywords": [ + "SEDOS", + "AP5", + "X2X", + "oedatamodel-parameter", + "input_data" + ], + "publicationDate": "2024-04-08", + "context": { + "homepage": "https://sedos-project.github.io/.github/", + "documentation": "https://sedos-project.github.io/.github/", + "sourceCode": "https://github.com/sedos-project", + "contact": "g.mueller@fz-juelich.de", + "grantNo": "03EI1040D", + "fundingAgency": "Bundesministerium für Wirtschaft und Klimaschutz (BMWK)", + "fundingAgencyLogo": "https://en.wikipedia.org/wiki/Federal_Ministry_for_Economic_Affairs_and_Climate_Action#/media/File:BMWi_Logo_2021.svg" + }, + "spatial": { + "location": "global", + "extent": null, + "resolution": null + }, + "temporal": { + "referenceDate": null, + "timeseries": [ + { + "start": null, + "end": null, + "resolution": null, + "alignment": null, + "aggregationType": null + } + ] + }, + "sources": [ + { + "title": "Technology Data for Renewable Fuels", + "description": null, + "path": "https://ens.dk/sites/ens.dk/files/Analyser/technology_data_for_renewable_fuels.pdf", + "licenses": [ + { + "instruction": "You are free to: Share, Adapt. You must give appropriate credit.", + "attribution": "© Danish Energy Agency 2017.", + "name": "CC-BY-4.0 DEED", + "title": null, + "path": "https://creativecommons.org/licenses/by/4.0/" + } + ] + } + ], + "licenses": [ + { + "instruction": "You are free: To Share, To Create, To Adapt", + "attribution": null, + "name": "CC0-1.0", + "title": "Creative Commons Zero v1.0 Universal", + "path": "https://creativecommons.org/publicdomain/zero/1.0/legalcode" + } + ], + "contributors": [ + { + "title": "Gian Müller, FZJ", + "email": "g.mueller@fz-juelich.de", + "object": "data and metadata", + "comment": "Data for SOEC electrolyser process.", + "date": "2024-04-08" + } + ], + "resources": [ + { + "profile": "tabular-data-resource", + "name": "model_draft.x2x_p2gas_soec_1", + "path": null, + "format": "PostgreSQL", + "encoding": "UTF-8", + "schema": { + "primaryKey": [ + "id" + ], + "foreignKeys": [], + "fields": [ + { + "name": "id", + "description": "Unique identifier", + "type": "bigint", + "isAbout": [], + "valueReference": [], + "unit": null + }, + { + "name": "region", + "description": "Country or region", + "type": "text", + "isAbout": [], + "valueReference": [], + "unit": null + }, + { + "name": "year", + "description": "Year", + "type": "integer", + "isAbout": [], + "valueReference": [], + "unit": "a" + }, + { + "name": "conversion_factor_sec_elec", + "description": "Commodity conversion factor of electricity", + "type": "float array", + "isAbout": [], + "valueReference": [], + "unit": "MWh/MWh" + }, + { + "name": "conversion_factor_sec_heat_high", + "description": "Commodity conversion factor of high temperature heat", + "type": "float array", + "isAbout": [], + "valueReference": [], + "unit": "MWh/MWh" + }, + { + "name": "conversion_factor_sec_hydrogen_orig", + "description": "Commodity conversion factor of hydrogen", + "type": "float array", + "isAbout": [], + "valueReference": [], + "unit": "MWh/MWh" + }, + { + "name": "cost_inv_p", + "description": "Investment cost per capacity for SOEC electrolysis", + "type": "float array", + "isAbout": [], + "valueReference": [], + "unit": "EUR/MW" + }, + { + "name": "cost_fix_p", + "description": "Fixed operation and maintenance costs for SOEC electrolysis.", + "type": "float array", + "isAbout": [], + "valueReference": [], + "unit": "EUR/MW*a" + }, + { + "name": "lifetime", + "description": "Lifetime in operation.", + "type": "float array", + "isAbout": [], + "valueReference": [], + "unit": "a" + }, + { + "name": "wacc", + "description": "Percentage of costs for capital after taxes. Used to calculate annuity factor for investment costs.", + "type": "text", + "isAbout": [], + "valueReference": [], + "unit": "%" + }, + { + "name": "bandwidth_type", + "description": "Bandwidth Type", + "type": "json", + "isAbout": [], + "valueReference": [], + "unit": null + }, + { + "name": "version", + "description": "Version", + "type": "text", + "isAbout": [], + "valueReference": [], + "unit": null + }, + { + "name": "method", + "description": "Method", + "type": "json", + "isAbout": [], + "valueReference": [], + "unit": null + }, + { + "name": "source", + "description": "Source", + "type": "json", + "isAbout": [], + "valueReference": [], + "unit": null + }, + { + "name": "comment", + "description": "Comment", + "type": "text", + "isAbout": [], + "valueReference": [], + "unit": null + } + ] + }, + "dialect": { + "delimiter": ";", + "decimalSeparator": "." + } + } + ], + "@id": null, + "@context": "https://raw.githubusercontent.com/OpenEnergyPlatform/oemetadata/develop/metadata/latest/context.json", + "metaMetadata": { + "metadataVersion": "OEP-1.5.2", + "metadataLicense": { + "name": "CC0-1.0", + "title": "Creative Commons Zero v1.0 Universal", + "path": "https://creativecommons.org/publicdomain/zero/1.0/" + } + }, + "_comment": { + "metadata": "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/oemetadata)", + "dates": "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", + "units": "Use a space between numbers and units (100 m)", + "languages": "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", + "licenses": "License name must follow the SPDX License List (https://spdx.org/licenses/)", + "review": "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/blob/master/data-review/manual/review_manual.md)", + "null": "If not applicable use: null", + "todo": "If a value is not yet available, use: todo" + } +} diff --git a/tests/test_data/unsupported_oep_metadata_example.json b/tests/test_data/validation/unsupported_oep_metadata_example.json similarity index 100% rename from tests/test_data/unsupported_oep_metadata_example.json rename to tests/test_data/validation/unsupported_oep_metadata_example.json diff --git a/tests/test_metadata_validation.py b/tests/test_metadata_validation.py index bd08026..6a9dae9 100644 --- a/tests/test_metadata_validation.py +++ b/tests/test_metadata_validation.py @@ -3,15 +3,16 @@ import json import pathlib +import jsonschema import pytest from jsonschema.exceptions import ValidationError from omi import base, validation -UNSUPPORTED_OEP_METADATA_EXAMPLE_FILE = ( - pathlib.Path(__file__).parent / "test_data" / "unsupported_oep_metadata_example.json" -) -INVALID_OEP_METADATA_EXAMPLE_FILE = pathlib.Path(__file__).parent / "test_data" / "invalid_oep_metadata_example.json" +TEST_VALIDATION_DATA_PATH = pathlib.Path(__file__).parent / "test_data" / "validation" + +UNSUPPORTED_OEP_METADATA_EXAMPLE_FILE = TEST_VALIDATION_DATA_PATH / "unsupported_oep_metadata_example.json" +INVALID_OEP_METADATA_EXAMPLE_FILE = TEST_VALIDATION_DATA_PATH / "invalid_oep_metadata_example.json" def test_validation_of_oep_metadata(): @@ -57,3 +58,65 @@ def test_metadata_schema_not_found(): match="Metadata format for metadata version OEP-1.5.0 could not be found.", ): base.get_metadata_specification("OEP-1.5.0") + + +def test_metadata_against_oep_table(): + """Test OEP table definition against OEP metadata.""" + table = "x2x_p2gas_soec_1" + with (TEST_VALIDATION_DATA_PATH / "metadata_oep_validation.json").open("r") as f: + metadata = json.load(f) + validation.validate_oep_table_against_metadata(oep_table=table, oep_schema="model_draft", metadata=metadata) + + +def test_metadata_against_oep_table_using_metadata_from_oep(): + """Test OEP table definition against OEP metadata, where metadata is taken from OEP.""" + table = "x2x_p2gas_soec_1" + with pytest.raises(jsonschema.exceptions.ValidationError, match="None is not of type 'object'"): + validation.validate_oep_table_against_metadata(oep_table=table, oep_schema="model_draft") + + +def test_metadata_against_oep_table_invalid_name(): + """Test different naming convention violation for OEP metadata.""" + table = "x2x_p2gas_soec_1" + with (TEST_VALIDATION_DATA_PATH / "metadata_oep_validation.json").open("r") as f: + metadata = json.load(f) + metadata["resources"][0]["name"] = "invalid" + with pytest.raises( + validation.ValidationError, + match=f"Name 'invalid' of metadata resource does not fit to oep table name. " + f"It should be one of '{table}', or 'model_draft.{table}'.", + ): + validation.validate_oep_table_against_metadata(oep_table=table, oep_schema="model_draft", metadata=metadata) + del metadata["resources"][0]["name"] + with pytest.raises(validation.ValidationError, match="Metadata resource has no name."): + validation.validate_oep_table_against_metadata(oep_table=table, oep_schema="model_draft", metadata=metadata) + + +def test_metadata_against_oep_table_with_missing_fields(): + """Test error raised for missing fields.""" + table = "x2x_p2gas_soec_1" + with (TEST_VALIDATION_DATA_PATH / "metadata_oep_validation.json").open("r") as f: + metadata = json.load(f) + metadata["resources"][0]["schema"]["fields"].append({"name": "added_field", "type": "string"}) + with pytest.raises(validation.ValidationError, match="Field 'added_field' not defined in OEP table"): + validation.validate_oep_table_against_metadata(oep_table=table, oep_schema="model_draft", metadata=metadata) + metadata["resources"][0]["schema"]["fields"].pop() + metadata["resources"][0]["schema"]["fields"].pop() + with pytest.raises( + validation.ValidationError, + match=f"Field 'comment' from OEP table 'model_draft.{table}' is missing in metadata schema.", + ): + validation.validate_oep_table_against_metadata(oep_table=table, oep_schema="model_draft", metadata=metadata) + + +def test_metadata_against_oep_table_with_incompatible_field_types(): + """Test error raised for incompatible field types.""" + table = "x2x_p2gas_soec_1" + with (TEST_VALIDATION_DATA_PATH / "metadata_oep_validation.json").open("r") as f: + metadata = json.load(f) + metadata["resources"][0]["schema"]["fields"][0]["type"] = "string" + with pytest.raises( + validation.ValidationError, + match="Field type 'bigint' from OEP table field 'id' differs from type 'string' defined in metadata schema.", + ): + validation.validate_oep_table_against_metadata(oep_table=table, oep_schema="model_draft", metadata=metadata) From d51f2cc5d667944bdef67364d1cc3ada89c9b0d0 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 17 Jun 2024 16:03:40 +0200 Subject: [PATCH 22/63] Add validation for multiple invalid metadata files --- .pre-commit-config.yaml | 1 + src/omi/validation.py | 74 +++- .../invalid_metadata}/additional_key.json | 0 .../invalid_metadata}/duplicate_key.json | 0 .../invalid_metadata}/missing_fields.json | 0 .../invalid_metadata}/wrong_datatype.json | 0 .../invalid_metadata}/wrong_json_syntax.json | 0 .../invalid_metadata}/wrong_key.json | 0 .../invalid_metadata}/wrong_nesting.json | 0 .../invalid_metadata}/wrong_structure.json | 0 .../invalid_metadata}/wrong_version.json | 0 .../wrongly_placed_null_value.json | 0 .../invalid_oep_metadata_example.json | 413 ------------------ tests/test_data/wrong.json | 40 -- tests/test_metadata_validation.py | 59 ++- 15 files changed, 114 insertions(+), 473 deletions(-) rename tests/test_data/{ => validation/invalid_metadata}/additional_key.json (100%) rename tests/test_data/{ => validation/invalid_metadata}/duplicate_key.json (100%) rename tests/test_data/{ => validation/invalid_metadata}/missing_fields.json (100%) rename tests/test_data/{ => validation/invalid_metadata}/wrong_datatype.json (100%) rename tests/test_data/{ => validation/invalid_metadata}/wrong_json_syntax.json (100%) rename tests/test_data/{ => validation/invalid_metadata}/wrong_key.json (100%) rename tests/test_data/{ => validation/invalid_metadata}/wrong_nesting.json (100%) rename tests/test_data/{ => validation/invalid_metadata}/wrong_structure.json (100%) rename tests/test_data/{ => validation/invalid_metadata}/wrong_version.json (100%) rename tests/test_data/{ => validation/invalid_metadata}/wrongly_placed_null_value.json (100%) delete mode 100644 tests/test_data/validation/invalid_oep_metadata_example.json delete mode 100644 tests/test_data/wrong.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 18e7de9..15e34db 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,6 +7,7 @@ repos: rev: v4.4.0 hooks: - id: check-json + exclude: tests/test_data/validation/invalid_metadata/* - id: end-of-file-fixer - id: trailing-whitespace - id: check-added-large-files diff --git a/src/omi/validation.py b/src/omi/validation.py index d9d71aa..579d3f1 100644 --- a/src/omi/validation.py +++ b/src/omi/validation.py @@ -2,6 +2,7 @@ from __future__ import annotations +import json import warnings import jsonschema @@ -49,30 +50,35 @@ class ValidationError(Exception): """Exception raised when a validation fails.""" -def validate_metadata(metadata: dict) -> None: +def validate_metadata(metadata: dict | str) -> None: """ Validate metadata against related metadata schema. Parameters ---------- - metadata: dict - Metadata + metadata: dict | str + Metadata as dict or as JSON string Returns ------- None if metadata schema is valid. Otherwise it raises an exception. """ + if isinstance(metadata, str): + metadata = parse_metadata(metadata) metadata_version = get_metadata_version(metadata) metadata_schema = get_metadata_specification(metadata_version) - jsonschema.validate(metadata, metadata_schema.schema) + try: + jsonschema.validate(metadata, metadata_schema.schema) + except jsonschema.exceptions.ValidationError as ve: + raise ValidationError(f"Error validating metadata against related metadata schema: {ve.message}") from ve license.validate_oemetadata_licenses(metadata) def validate_data( data: pd.DataFrame, *, - metadata: dict | None = None, + metadata: dict | str | None = None, oep_table: str | None = None, oep_schema: str | None = None, return_report: bool = False, @@ -84,7 +90,7 @@ def validate_data( ---------- data: pd.DataFrame Data to validate - metadata: dict | None + metadata: dict | str | None Metadata in OEMetadata format. If given, data is validated against metadata schema. oep_table: str | None Table name on OEP. If given, data is validated against OEP table. @@ -122,6 +128,8 @@ def validate_data( ) if metadata: + if isinstance(metadata, str): + metadata = parse_metadata(metadata) return validate_data_against_metadata(data, metadata, return_report=return_report) return validate_data_against_oep_table(data, oep_table, oep_schema, return_report=return_report) @@ -182,7 +190,7 @@ def validate_data_against_oep_table( def validate_data_against_metadata( data: pd.DataFrame, - metadata: dict, + metadata: dict | str, *, return_report: bool = True, ) -> None | Report: @@ -193,7 +201,7 @@ def validate_data_against_metadata( ---------- data: pandas.DataFrame Data to validate - metadata: dict + metadata: dict | str Metadata in OEMetadata format. return_report: bool If set to True, report is returned instead of raising an error. @@ -203,6 +211,8 @@ def validate_data_against_metadata( Report Frictionless report if `return_report` is set to True, otherwise None is returned. """ + if isinstance(metadata, str): + metadata = parse_metadata(metadata) metadata_fields = __get_fields_from_metadata(metadata) report = __validate_data_against_schema(data, metadata_fields) if not report.valid: @@ -215,7 +225,7 @@ def validate_data_against_metadata( def validate_oep_table_against_metadata( # noqa: C901 oep_table: str, oep_schema: str, - metadata: dict | None = None, + metadata: dict | str | None = None, ) -> None: """ Validate OEP table against given metadata. @@ -226,7 +236,7 @@ def validate_oep_table_against_metadata( # noqa: C901 OEP table name oep_schema: str OEP schema name - metadata: dict | None + metadata: dict | str | None Metadata in OEMetadata format. If no metadata is given, metadata defined for OEP table on OEP is used. Raises @@ -239,6 +249,8 @@ def validate_oep_table_against_metadata( # noqa: C901 None if everything is valid. Otherwise, it raises an exception. """ + if isinstance(metadata, str): + metadata = parse_metadata(metadata) if metadata is None: metadata = get_metadata_from_oep_table(oep_table, oep_schema) @@ -295,6 +307,48 @@ def validate_oep_table_against_metadata( # noqa: C901 raise ValidationError(errors) +def parse_metadata(metadata_string: str) -> dict: + """ + Parse metadata string into a dictionary. + + Parameters + ---------- + metadata_string: str + Metadata given as JSOn string + + Returns + ------- + dict + Metadata as dictionary + """ + + def dict_raise_on_duplicates(ordered_pairs: dict) -> dict: + """ + Reject duplicate keys. + + From https://stackoverflow.com/a/14902564/5804947 + """ + d = {} + for k, v in ordered_pairs: + if k in d: + raise ValidationError(f"Duplicate keys in metadata: '{k}'") + d[k] = v + return d + + try: + json.loads(metadata_string, object_pairs_hook=dict_raise_on_duplicates) + except json.JSONDecodeError as jde: + start = max(0, jde.pos - 10) + end = min(len(metadata_string), jde.pos + 10) + context = metadata_string[start:end] + error_message = ( + f"Failed to decode JSON: " + f"{jde.msg} at line {jde.lineno}, column {jde.colno}, position {jde.pos}. " + f"Context around this position: '{context}'" + ) + raise ValidationError(error_message) from jde + + def __validate_data_against_schema(data: pd.DataFrame, fields: dict[str, str]) -> Report: """ Validate data against related schema definition and return frictionless report. diff --git a/tests/test_data/additional_key.json b/tests/test_data/validation/invalid_metadata/additional_key.json similarity index 100% rename from tests/test_data/additional_key.json rename to tests/test_data/validation/invalid_metadata/additional_key.json diff --git a/tests/test_data/duplicate_key.json b/tests/test_data/validation/invalid_metadata/duplicate_key.json similarity index 100% rename from tests/test_data/duplicate_key.json rename to tests/test_data/validation/invalid_metadata/duplicate_key.json diff --git a/tests/test_data/missing_fields.json b/tests/test_data/validation/invalid_metadata/missing_fields.json similarity index 100% rename from tests/test_data/missing_fields.json rename to tests/test_data/validation/invalid_metadata/missing_fields.json diff --git a/tests/test_data/wrong_datatype.json b/tests/test_data/validation/invalid_metadata/wrong_datatype.json similarity index 100% rename from tests/test_data/wrong_datatype.json rename to tests/test_data/validation/invalid_metadata/wrong_datatype.json diff --git a/tests/test_data/wrong_json_syntax.json b/tests/test_data/validation/invalid_metadata/wrong_json_syntax.json similarity index 100% rename from tests/test_data/wrong_json_syntax.json rename to tests/test_data/validation/invalid_metadata/wrong_json_syntax.json diff --git a/tests/test_data/wrong_key.json b/tests/test_data/validation/invalid_metadata/wrong_key.json similarity index 100% rename from tests/test_data/wrong_key.json rename to tests/test_data/validation/invalid_metadata/wrong_key.json diff --git a/tests/test_data/wrong_nesting.json b/tests/test_data/validation/invalid_metadata/wrong_nesting.json similarity index 100% rename from tests/test_data/wrong_nesting.json rename to tests/test_data/validation/invalid_metadata/wrong_nesting.json diff --git a/tests/test_data/wrong_structure.json b/tests/test_data/validation/invalid_metadata/wrong_structure.json similarity index 100% rename from tests/test_data/wrong_structure.json rename to tests/test_data/validation/invalid_metadata/wrong_structure.json diff --git a/tests/test_data/wrong_version.json b/tests/test_data/validation/invalid_metadata/wrong_version.json similarity index 100% rename from tests/test_data/wrong_version.json rename to tests/test_data/validation/invalid_metadata/wrong_version.json diff --git a/tests/test_data/wrongly_placed_null_value.json b/tests/test_data/validation/invalid_metadata/wrongly_placed_null_value.json similarity index 100% rename from tests/test_data/wrongly_placed_null_value.json rename to tests/test_data/validation/invalid_metadata/wrongly_placed_null_value.json diff --git a/tests/test_data/validation/invalid_oep_metadata_example.json b/tests/test_data/validation/invalid_oep_metadata_example.json deleted file mode 100644 index b307d98..0000000 --- a/tests/test_data/validation/invalid_oep_metadata_example.json +++ /dev/null @@ -1,413 +0,0 @@ -{ - "unsupported_keyword": "invalid", - "title": "Example title for metadata example - Version 1.6.0", - "id": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v160", - "description": "This is an metadata example for example data. There is a corresponding table on the OEP for each metadata version.", - "language": [ - "en-GB", - "en-US", - "de-DE", - "fr-FR" - ], - "subject": [ - { - "name": "energy", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000150" - }, - { - "name": "test dataset", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000408" - } - ], - "keywords": [ - "energy", - "example", - "template", - "test" - ], - "publicationDate": "2022-02-15", - "context": { - "homepage": "https://reiner-lemoine-institut.de/lod-geoss/", - "documentation": "https://openenergy-platform.org/tutorials/jupyter/OEMetadata/", - "sourceCode": "https://github.com/OpenEnergyPlatform/oemetadata/tree/master", - "contact": "https://github.com/Ludee", - "grantNo": "03EI1005", - "fundingAgency": "Bundesministerium für Wirtschaft und Klimaschutz", - "fundingAgencyLogo": "https://commons.wikimedia.org/wiki/File:BMWi_Logo_2021.svg#/media/File:BMWi_Logo_2021.svg", - "publisherLogo": "https://reiner-lemoine-institut.de//wp-content/uploads/2015/09/rlilogo.png" - }, - "spatial": { - "location": null, - "extent": "europe", - "resolution": "100 m" - }, - "temporal": { - "referenceDate": "2016-01-01", - "timeseries": [ - { - "start": "2017-01-01T00:00+01", - "end": "2017-12-31T23:00+01", - "resolution": "1 h", - "alignment": "left", - "aggregationType": "sum" - }, - { - "start": "2018-01-01T00:00+01", - "end": "2019-06-01T23:00+01", - "resolution": "15 min", - "alignment": "right", - "aggregationType": "sum" - } - ] - }, - "sources": [ - { - "title": "OpenEnergyPlatform Metadata Example", - "description": "Metadata description", - "path": "https://github.com/OpenEnergyPlatform", - "licenses": [ - { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/legalcode", - "instruction": "You are free: To Share, To Create, To Adapt", - "attribution": "© Reiner Lemoine Institut" - } - ] - }, - { - "title": "OpenStreetMap", - "description": "A collaborative project to create a free editable map of the world", - "path": "https://www.openstreetmap.org/", - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/index.html", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© OpenStreetMap contributors" - } - ] - } - ], - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0", - "path": "https://opendatacommons.org/licenses/odbl/1.0/", - "instruction": "You are free: To Share, To Create, To Adapt; As long as you: Attribute, Share-Alike, Keep open!", - "attribution": "© Reiner Lemoine Institut © OpenStreetMap contributors" - } - ], - "contributors": [ - { - "title": "Ludee", - "email": null, - "date": "2016-06-16", - "object": "metadata", - "comment": "Create metadata" - }, - { - "title": "Ludee", - "email": null, - "date": "2016-11-22", - "object": "metadata", - "comment": "Update metadata" - }, - { - "title": "Ludee", - "email": null, - "date": "2016-11-22", - "object": "metadata", - "comment": "Update header and license" - }, - { - "title": "Ludee", - "email": null, - "date": "2017-03-16", - "object": "metadata", - "comment": "Add license to source" - }, - { - "title": "Ludee", - "email": null, - "date": "2017-03-28", - "object": "metadata", - "comment": "Add copyright to source and license" - }, - { - "title": "Ludee", - "email": null, - "date": "2017-05-30", - "object": "metadata", - "comment": "Release metadata version 1.3" - }, - { - "title": "Ludee", - "email": null, - "date": "2017-06-26", - "object": "metadata", - "comment": "Move referenceDate into temporal and remove array" - }, - { - "title": "Ludee", - "email": null, - "date": "2018-07-19", - "object": "metadata", - "comment": "Start metadata version 1.4" - }, - { - "title": "Ludee", - "email": null, - "date": "2018-07-26", - "object": "data", - "comment": "Rename table and files" - }, - { - "title": "Ludee", - "email": null, - "date": "2018-10-18", - "object": "metadata", - "comment": "Add contribution object" - }, - { - "title": "christian-rli", - "email": null, - "date": "2018-10-18", - "object": "metadata", - "comment": "Add datapackage compatibility" - }, - { - "title": "Ludee", - "email": null, - "date": "2018-11-02", - "object": "metadata", - "comment": "Release metadata version 1.4" - }, - { - "title": "christian-rli", - "email": null, - "date": "2019-02-05", - "object": "metadata", - "comment": "Apply template structure to example" - }, - { - "title": "Ludee", - "email": null, - "date": "2019-03-22", - "object": "metadata", - "comment": "Hotfix foreignKeys" - }, - { - "title": "Ludee", - "email": null, - "date": "2019-07-09", - "object": "metadata", - "comment": "Release metadata version OEP-1.3.0" - }, - { - "title": "Ludee", - "email": null, - "date": "2021-11-15", - "object": "metadata", - "comment": "Release metadata version OEP-1.5.0" - }, - { - "title": "Ludee", - "email": null, - "date": "2022-02-15", - "object": "metadata", - "comment": "Release metadata version OEP-1.5.1" - }, - { - "title": "jh-RLI", - "email": null, - "date": "2022-11-18", - "object": "metadata", - "comment": "Release metadata version OEP-1.5.2" - }, - { - "title": "christian-rli", - "email": null, - "date": "2023-05-30", - "object": "metadata", - "comment": "Release metadata version OEP-1.6.0" - } - ], - "resources": [ - { - "profile": "tabular-data-resource", - "name": "model_draft.oep_metadata_table_example_v160", - "path": "http://openenergyplatform.org/dataedit/view/model_draft/oep_metadata_table_example_v160", - "format": "PostgreSQL", - "encoding": "UTF-8", - "schema": { - "fields": [ - { - "name": "id", - "description": "Unique identifier", - "type": "serial", - "unit": null, - "isAbout": [ - { - "name": null, - "path": null - } - ], - "valueReference": [ - { - "value": null, - "name": null, - "path": null - } - ] - }, - { - "name": "name", - "description": "Example name", - "type": "text", - "unit": null, - "isAbout": [ - { - "name": "written name", - "path": "https://openenergy-platform.org/ontology/oeo/IAO_0000590" - } - ], - "valueReference": [ - { - "value": null, - "name": null, - "path": null - } - ] - }, - { - "name": "type", - "description": "Type of wind farm", - "type": "text", - "unit": null, - "isAbout": [ - { - "name": "wind farm", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000447" - } - ], - "valueReference": [ - { - "value": "onshore ", - "name": "onshore wind farm", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000311" - }, - { - "value": "offshore ", - "name": "offshore wind farm", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000308" - } - ] - }, - { - "name": "year", - "description": "Reference year", - "type": "integer", - "unit": null, - "isAbout": [ - { - "name": "year", - "path": "https://openenergy-platform.org/ontology/oeo/UO_0000036" - } - ], - "valueReference": [ - { - "value": null, - "name": null, - "path": null - } - ] - }, - { - "name": "value", - "description": "Example value", - "type": "double precision", - "unit": "MW", - "isAbout": [ - { - "name": "quantity value", - "path": "https://openenergy-platform.org/ontology/oeo/OEO_00000350" - } - ], - "valueReference": [ - { - "value": null, - "name": null, - "path": null - } - ] - }, - { - "name": "geom", - "description": "Geometry", - "type": "geometry(Point, 4326)", - "unit": null, - "isAbout": [ - { - "name": "spatial region", - "path": "https://openenergy-platform.org/ontology/oeo/BFO_0000006" - } - ], - "valueReference": [ - { - "value": null, - "name": null, - "path": null - } - ] - } - ], - "primaryKey": [ - "id" - ], - "foreignKeys": [ - { - "fields": [ - "year" - ], - "reference": { - "resource": "schema.table", - "fields": [ - "year" - ] - } - } - ] - }, - "dialect": { - "delimiter": null, - "decimalSeparator": "." - } - } - ], - "@id": "https://databus.dbpedia.org/kurzum/mastr/bnetza-mastr/01.04.00", - "@context": "https://raw.githubusercontent.com/OpenEnergyPlatform/oemetadata/develop/metadata/latest/context.json", - "review": { - "path": "https://github.com/OpenEnergyPlatform/data-preprocessing/issues", - "badge": "Platinum" - }, - "metaMetadata": { - "metadataVersion": "OEP-1.6.0", - "metadataLicense": { - "name": "CC0-1.0", - "title": "Creative Commons Zero v1.0 Universal", - "path": "https://creativecommons.org/publicdomain/zero/1.0/" - } - }, - "_comment": { - "metadata": "Metadata documentation and explanation (https://github.com/OpenEnergyPlatform/oemetadata)", - "dates": "Dates and time must follow the ISO8601 including time zone (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss±hh)", - "units": "Use a space between numbers and units (100 m)", - "languages": "Languages must follow the IETF (BCP47) format (en-GB, en-US, de-DE)", - "licenses": "License name must follow the SPDX License List (https://spdx.org/licenses/)", - "review": "Following the OEP Data Review (https://github.com/OpenEnergyPlatform/data-preprocessing/blob/master/data-review/manual/review_manual.md)", - "null": "If not applicable use: null", - "todo": "If a value is not yet available, use: todo" - } -} diff --git a/tests/test_data/wrong.json b/tests/test_data/wrong.json deleted file mode 100644 index bc5d9c8..0000000 --- a/tests/test_data/wrong.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "title": "title", - "id": "http://such.tld/name", - "description": "These Metadata use a wrong date format in publication date.", - "licenses": [ - { - "name": "ODbL-1.0", - "title": "Open Data Commons Open Database License 1.0" - } - ], - "resources": [ - { - "profile": "tabular-data-resource", - "name": "very.name", - "path": "http://such.tld/name", - "format": "PostgreSQL", - "encoding": "UTF-8", - "schema": { - "fields": [ - { - "name": "id", - "description": "Unique identifier", - "type": "serial" - }, - { - "name": "name", - "description": "Example name", - "type": "text" - } - ], - "primaryKey": [ - "id" - ] - } - } - ], - "metaMetadata": { - "metadataVersion": "OEP-1.6.0" - } -} diff --git a/tests/test_metadata_validation.py b/tests/test_metadata_validation.py index 6a9dae9..c4b8db5 100644 --- a/tests/test_metadata_validation.py +++ b/tests/test_metadata_validation.py @@ -2,17 +2,16 @@ import json import pathlib +import re -import jsonschema import pytest -from jsonschema.exceptions import ValidationError -from omi import base, validation +from omi import base, license, validation TEST_VALIDATION_DATA_PATH = pathlib.Path(__file__).parent / "test_data" / "validation" +INVALID_METADAT_PATH = TEST_VALIDATION_DATA_PATH / "invalid_metadata" UNSUPPORTED_OEP_METADATA_EXAMPLE_FILE = TEST_VALIDATION_DATA_PATH / "unsupported_oep_metadata_example.json" -INVALID_OEP_METADATA_EXAMPLE_FILE = TEST_VALIDATION_DATA_PATH / "invalid_oep_metadata_example.json" def test_validation_of_oep_metadata(): @@ -23,12 +22,52 @@ def test_validation_of_oep_metadata(): validation.validate_metadata(metadata_schema.example) -def test_invalid_oep_metadata_version(): - """Test if validation error is raised for invalid OEP metadata.""" - with INVALID_OEP_METADATA_EXAMPLE_FILE.open("r") as f: +def test_invalid_oep_metadata(): + """Test if validation error is raised for different invalid OEP metadata.""" + with (INVALID_METADAT_PATH / "additional_key.json").open("r") as f: invalid_oep_metadata = json.load(f) - with pytest.raises(ValidationError): - validation.validate_metadata(invalid_oep_metadata) + # `re.escape` must be used, otherwise quotes around 'universe' are not detected correctly + with pytest.raises( + validation.ValidationError, + match=re.escape("Additional properties are not allowed ('universe' was unexpected)"), + ): + validation.validate_metadata(invalid_oep_metadata) + + with (INVALID_METADAT_PATH / "missing_fields.json").open("r") as f: + invalid_oep_metadata = json.load(f) + with pytest.raises(license.LicenseError, match="No license information available in the metadata."): + validation.validate_metadata(invalid_oep_metadata) + + with (INVALID_METADAT_PATH / "wrongly_placed_null_value.json").open("r") as f: + invalid_oep_metadata = json.load(f) + with pytest.raises(validation.ValidationError, match="None is not of type 'object'"): + validation.validate_metadata(invalid_oep_metadata) + + +def test_invalid_oep_metadata_caused_by_invalid_json(): + """Test if validation error is raised for invalid OEP metadata due to invalid JSOn syntax.""" + with (INVALID_METADAT_PATH / "duplicate_key.json").open("r") as f: + invalid_metadata_string = f.read() + with pytest.raises(validation.ValidationError, match="Duplicate keys in metadata: 'description'"): + validation.validate_metadata(invalid_metadata_string) + + with (INVALID_METADAT_PATH / "wrong_json_syntax.json").open("r") as f: + invalid_metadata_string = f.read() + with pytest.raises(validation.ValidationError, match="Failed to decode JSON: Expecting value"): + validation.validate_metadata(invalid_metadata_string) + + with (INVALID_METADAT_PATH / "wrong_nesting.json").open("r") as f: + invalid_metadata_string = f.read() + with pytest.raises( + validation.ValidationError, + match="Failed to decode JSON: Expecting property name enclosed in double quotes", + ): + validation.validate_metadata(invalid_metadata_string) + + with (INVALID_METADAT_PATH / "wrong_structure.json").open("r") as f: + invalid_metadata_string = f.read() + with pytest.raises(validation.ValidationError, match="Failed to decode JSON: Expecting ',' delimiter"): + validation.validate_metadata(invalid_metadata_string) def test_unsupported_oep_metadata_version(): @@ -71,7 +110,7 @@ def test_metadata_against_oep_table(): def test_metadata_against_oep_table_using_metadata_from_oep(): """Test OEP table definition against OEP metadata, where metadata is taken from OEP.""" table = "x2x_p2gas_soec_1" - with pytest.raises(jsonschema.exceptions.ValidationError, match="None is not of type 'object'"): + with pytest.raises(validation.ValidationError, match="None is not of type 'object'"): validation.validate_oep_table_against_metadata(oep_table=table, oep_schema="model_draft") From 9b82d3c860c33c565603b756da31e32f2c6f7daa Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 17 Jun 2024 16:21:44 +0200 Subject: [PATCH 23/63] Remove outdated docs --- docs/dialect.rst | 38 ------------------------- docs/index.rst | 3 -- docs/internal_structure.rst | 8 ------ docs/oep_metadata.rst | 55 ------------------------------------- docs/usage.rst | 2 -- 5 files changed, 106 deletions(-) delete mode 100644 docs/dialect.rst delete mode 100644 docs/internal_structure.rst delete mode 100644 docs/oep_metadata.rst diff --git a/docs/dialect.rst b/docs/dialect.rst deleted file mode 100644 index 7bdff16..0000000 --- a/docs/dialect.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. _dialect: - -======== -Dialects -======== - -This section discusses the concepts of :class:`~omi.dialects.base.dialect.Parser`, :class:`~omi.dialects.base.dialect.Compiler` and -:class:`~omi.dialects.base.dialect.Dialect` - -The OMI tool handles all metadata in an internal data structure that covers the -relevant information needed to describe data. Different metadata formats -(e.g. the OEP metadata format) can be **parsed** into this structure or -**compiled** from it. - -Therefore, OMI uses the notion of **Parser** and **Compiler**. A -:class:`~omi.dialects.base.dialect.Dialect` combines the functionalities of -:class:`~omi.dialects.base.dialect.Parser`, -:class:`~omi.dialects.base.dialect.Compiler` and adds some convenience methods -to it. Each dialect has an id that can be used to call it via the :ref:`command line interface` - -.. _available_dialects: - -Available dialects are: - - `oep-v1.3` - - `oep-v1.4` - - `oep-rdf-v1.4` - - `oep-v1.5` - -.. autoclass:: omi.dialects.base.dialect.Parser - :members: - -.. autoclass:: omi.dialects.base.dialect.Compiler - :members: - -.. autoclass:: omi.dialects.base.dialect.Dialect - :members: - - diff --git a/docs/index.rst b/docs/index.rst index 982cc97..40f35b5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,9 +12,6 @@ Contents contributing authors changelog - internal_structure - dialect - oep_metadata Indices and tables ================== diff --git a/docs/internal_structure.rst b/docs/internal_structure.rst deleted file mode 100644 index 87849ec..0000000 --- a/docs/internal_structure.rst +++ /dev/null @@ -1,8 +0,0 @@ -============ -OMI Metadata -============ - -.. autoclass:: omi.structure.Compilable - :members: - - .. autoattribute:: omi.structure.Compilable.__compiler_name__ diff --git a/docs/oep_metadata.rst b/docs/oep_metadata.rst deleted file mode 100644 index 05dd42e..0000000 --- a/docs/oep_metadata.rst +++ /dev/null @@ -1,55 +0,0 @@ -====================== -Translations in Python -====================== - -.. testsetup:: * - - from omi.dialects.oep.dialect import OEP_V_1_3_Dialect, OEP_V_1_4_Dialect, OEP_V_1_5_Dialect - -In order to perform the translation from one dialect to another, you need to -parse your input using the respective input dialect. As a minimal example, let's -say you have a metadata string in the outdated version 1.3 and aim to update it -to the more modern version 1.4. - -Step 1: Parse it -**************** - -Your first step is to parse the given string using :class:`omi.dialects.oep.dialect.OEP_V_1_3_Dialect`. -For starters, we use the most basic metadata string: The empty dictionary - -.. doctest:: - - >>> inp = '{}' - >>> dialect1_3 = OEP_V_1_3_Dialect() - >>> parsed = dialect1_3.parse(inp) - >>> parsed - OEPMetadata(name=None,title=None,identifier=None,description=None,languages=None,keywords=None,publication_date=None,context=None,spatial=None,temporal=None,sources=None,license=None,contributions=None,resources=None,review=None,comment=None) - -The input has been parsed into the internal structure i.e. an :class:`OEPMetadata`-object. - -Step 2: Change it -***************** - -If needed you can feel free to manipulate this string according to your use case. -Don't forget to document your changes under contributions (ToDo) ;) - -In this example, we will add an identifier, as is required by OEP-Metadata v1.4 - -.. doctest:: - - >>> parsed.identifier = "unique_id" - >>> parsed - OEPMetadata(name=None,title=None,identifier=unique_id,description=None,languages=None,keywords=None,publication_date=None,context=None,spatial=None,temporal=None,sources=None,license=None,contributions=None,resources=None,review=None,comment=None) - -Step 3: Compile it -****************** - -Now that we have an :class:`OEPMetadata`-object we are happy with, we want to translate it to the -new metadata format by using the respective dialect - -.. doctest:: - - >>> dialect1_4 = OEP_V_1_4_Dialect() - >>> dialect1_4._compiler.OMIT_NONE_FIELDS = True - >>> dialect1_4.compile(parsed) - {'id': 'unique_id', 'metaMetadata': {'metadataVersion': 'OEP-1.4.0', 'metadataLicense': {'name': 'CC0-1.0', 'title': 'Creative Commons Zero v1.0 Universal', 'path': 'https://creativecommons.org/publicdomain/zero/1.0/'}}} diff --git a/docs/usage.rst b/docs/usage.rst index 753f1c3..36242ec 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -8,8 +8,6 @@ Usage :prog: omi :show-nested: -A list of available dialects can be found in the :ref:`dialect segment` - To use omi in a project:: import omi From 779d97760d7d40073186971547ea0dbf2ecb1c26 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 17 Jun 2024 16:22:00 +0200 Subject: [PATCH 24/63] Remove MANIFEST.in (not needed for poetry) --- MANIFEST.in | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index cbba3f3..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,21 +0,0 @@ -graft docs -graft src -graft ci -graft tests -graft examples -graft scripts - -include .bumpversion.cfg -include .coveragerc -include .cookiecutterrc -include .editorconfig - -include AUTHORS.rst -include CHANGELOG.rst -include CONTRIBUTING.rst -include LICENSE -include README.rst - -include tox.ini .pre-commit-config.yaml .travis.yml appveyor.yml - -global-exclude *.py[cod] __pycache__ *.so *.dylib From cb5520091fc1f2d77785a636fe57d278732f87a6 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 17 Jun 2024 16:22:32 +0200 Subject: [PATCH 25/63] Adapt tox.ini to poetry --- tox.ini | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tox.ini b/tox.ini index 1bda854..c2957ca 100644 --- a/tox.ini +++ b/tox.ini @@ -36,16 +36,12 @@ commands = [testenv:check] deps = - docutils - check-manifest + poetry flake8 - readme-renderer - pygments isort skip_install = true commands = - python setup.py check --strict --metadata --restructuredtext - check-manifest {toxinidir} + poetry check --lock isort --verbose --check-only --diff src [testenv:spell] From dc1d37ae84faf3690d953f89011624e7b5f46cf0 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 17 Jun 2024 16:35:51 +0200 Subject: [PATCH 26/63] Make linter happy --- README.rst | 24 ++++++++++++------------ src/omi/__main__.py | 1 - 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index 8cd7834..be21dd1 100644 --- a/README.rst +++ b/README.rst @@ -82,9 +82,9 @@ Usage ===== **Parse, Compile, Render, Convert and Validate** -Omi can read(parse), compile, Render(json compilant), convert(convert metadata from v1.4 to v1.5 structure) and validate - a json -file or object that is compliant with the oemetadata spec. This is usefull to do various operations that help to integrate with - as -well as in interact with the oemetadata. Some parts of this tool might still be volatile but the code quality is conventionsly improved +Omi can read(parse), compile, Render(json compilant), convert(convert metadata from v1.4 to v1.5 structure) and validate - a json +file or object that is compliant with the oemetadata spec. This is usefull to do various operations that help to integrate with - as +well as in interact with the oemetadata. Some parts of this tool might still be volatile but the code quality is conventionsly improved as this module is a core component of the oeplatfroms metadata integration system. Check if omi is able to read a oemetadata file (for version 1.4 and 1.5) @@ -96,8 +96,8 @@ CLI - oemetadata version 1.4:: omi translate -f oep-v1.4 -t oep-v1.4 examples/data/metadata_v14.json -omi is able to read a JSON file and parse it into one of the internal Python structures (depending on the oemetadata version). -The OEPMetadata Python object can then be compiled and converted back to JSON. You can manipulate a successfully parsed +omi is able to read a JSON file and parse it into one of the internal Python structures (depending on the oemetadata version). +The OEPMetadata Python object can then be compiled and converted back to JSON. You can manipulate a successfully parsed OEPMetadata object. Module usage:: @@ -120,7 +120,7 @@ by using a CLI command. CLI - oemetadata conversion from v1.4 to v1.5:: - omi convert -i {input/path} -o {output/path} + omi convert -i {input/path} -o {output/path} **Validation** @@ -142,21 +142,21 @@ Module usage:: parser = JSONParser() parser.validate(metadata) - - # check if your metadata is valid for the given schmea + + # check if your metadata is valid for the given schmea schema = ... get a schema or import form oemetadata module parser.is_valid(metadata, schema) **Additional Fields - not related to the OEMetadata specification** Sometimes it is necessary to store additional key-value pairs along with the keys included in the OEMetadata specification. -OMI's compiler methods are capable of handling additional arguments or key-value arguments, but this must be -be explicitly specified. +OMI's compiler methods are capable of handling additional arguments or key-value arguments, but this must be +be explicitly specified. -To add additional key-value pairs, you must: +To add additional key-value pairs, you must: NOTE: If you save the renderer return value in a json file and try to parse the file, the extra field is not included. - You must read the json file using Python and then add the extra field back oemetadata object as shown below. + You must read the json file using Python and then add the extra field back oemetadata object as shown below. 1 Parse the oemetadata from json file / variable into omis internal structure:: diff --git a/src/omi/__main__.py b/src/omi/__main__.py index 8343d09..de20537 100644 --- a/src/omi/__main__.py +++ b/src/omi/__main__.py @@ -1,7 +1,6 @@ """ Entrypoint module, in case you use `python -momi`. - Why does this file exist, and why __main__? For more info, read: - https://www.python.org/dev/peps/pep-0338/ From 9ba770a28f3ca0425d4d6fdf670561bebaf17305 Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Mon, 17 Jun 2024 17:01:02 +0200 Subject: [PATCH 27/63] Add test for invalid data --- src/omi/validation.py | 7 +++- tests/test_data/validation/data.csv | 2 +- .../hackathon_lignite_hh_invalid.csv | 2 +- .../validation/hackathon_lignite_hh_valid.csv | 2 +- .../invalid_data/duplicate_primary_keys.csv | 3 ++ .../validation/invalid_data/extra_column.csv | 3 ++ .../invalid_data/invalid_datatype.csv | 3 ++ .../invalid_data/missing_column.csv | 3 ++ tests/test_data_validation.py | 33 +++++++++++++++++++ 9 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 tests/test_data/validation/invalid_data/duplicate_primary_keys.csv create mode 100644 tests/test_data/validation/invalid_data/extra_column.csv create mode 100644 tests/test_data/validation/invalid_data/invalid_datatype.csv create mode 100644 tests/test_data/validation/invalid_data/missing_column.csv diff --git a/src/omi/validation.py b/src/omi/validation.py index 579d3f1..0801645 100644 --- a/src/omi/validation.py +++ b/src/omi/validation.py @@ -365,13 +365,18 @@ def __validate_data_against_schema(data: pd.DataFrame, fields: dict[str, str]) - Report Frictionless report of validated data """ + # Check if all fields oin metadata are represented in data + for field in fields: + if field not in data.columns: + raise ValidationError(f"Could not find column '{field}' in data.") + ordered_fields = {} for field in data.columns: if field not in fields: raise ValidationError(f"Could not find field '{field}' in schema.") ordered_fields[field] = fields[field] frictionless_fields = __map_fields_to_frictionless_fields(ordered_fields) - schema = Schema(fields=frictionless_fields) + schema = Schema(fields=frictionless_fields, primary_key=["id"]) resource = Resource( data=data, profile="tabular-data-resource", diff --git a/tests/test_data/validation/data.csv b/tests/test_data/validation/data.csv index b0ed699..ea631f2 100644 --- a/tests/test_data/validation/data.csv +++ b/tests/test_data/validation/data.csv @@ -1,3 +1,3 @@ id;region;year;cost_var_e;bandwidth_type;source;method;comment;version 0;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 -0;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 +1;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 diff --git a/tests/test_data/validation/hackathon_lignite_hh_invalid.csv b/tests/test_data/validation/hackathon_lignite_hh_invalid.csv index bc93869..d613ca8 100644 --- a/tests/test_data/validation/hackathon_lignite_hh_invalid.csv +++ b/tests/test_data/validation/hackathon_lignite_hh_invalid.csv @@ -1,3 +1,3 @@ id;region;year;natural_domestic_limit;bandwidth_type;source;method;comment;version 0;DE;2024;100.0;should_be_json;{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 -0;DE;2024;100.0;should_be_json;{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 +1;DE;2024;100.0;should_be_json;{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 diff --git a/tests/test_data/validation/hackathon_lignite_hh_valid.csv b/tests/test_data/validation/hackathon_lignite_hh_valid.csv index a6ef6ec..10915c2 100644 --- a/tests/test_data/validation/hackathon_lignite_hh_valid.csv +++ b/tests/test_data/validation/hackathon_lignite_hh_valid.csv @@ -1,3 +1,3 @@ id;region;year;natural_domestic_limit;bandwidth_type;source;method;comment;version 0;["DE"];2024;100.0;{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 -0;["DE"];2024;100.0;{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 +1;["DE"];2024;100.0;{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 diff --git a/tests/test_data/validation/invalid_data/duplicate_primary_keys.csv b/tests/test_data/validation/invalid_data/duplicate_primary_keys.csv new file mode 100644 index 0000000..5b25773 --- /dev/null +++ b/tests/test_data/validation/invalid_data/duplicate_primary_keys.csv @@ -0,0 +1,3 @@ +id;region;year;cost_var_e;bandwidth_type;source;method;comment;version +1;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 +1;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 diff --git a/tests/test_data/validation/invalid_data/extra_column.csv b/tests/test_data/validation/invalid_data/extra_column.csv new file mode 100644 index 0000000..fda814d --- /dev/null +++ b/tests/test_data/validation/invalid_data/extra_column.csv @@ -0,0 +1,3 @@ +id;region;year;cost_var_e;bandwidth_type;source;method;comment;version;added_column +0;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1;1 +0;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1;2 diff --git a/tests/test_data/validation/invalid_data/invalid_datatype.csv b/tests/test_data/validation/invalid_data/invalid_datatype.csv new file mode 100644 index 0000000..0a89385 --- /dev/null +++ b/tests/test_data/validation/invalid_data/invalid_datatype.csv @@ -0,0 +1,3 @@ +id;region;year;cost_var_e;bandwidth_type;source;method;comment;version +0;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};1;{"natural_domestic_limit": "exact"};{"test": "test"};v1 +0;["DE"];2024;100.0;{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"natural_domestic_limit": "exact"};{"test": "test"};v1 diff --git a/tests/test_data/validation/invalid_data/missing_column.csv b/tests/test_data/validation/invalid_data/missing_column.csv new file mode 100644 index 0000000..95895b8 --- /dev/null +++ b/tests/test_data/validation/invalid_data/missing_column.csv @@ -0,0 +1,3 @@ +id;region;year;cost_var_e;bandwidth_type;source;comment;version +0;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"test": "test"};v1 +0;["DE"];2024;[100.0];{"natural_domestic_limit": "point"};{"natural_domestic_limit": "file"};{"test": "test"};v1 diff --git a/tests/test_data_validation.py b/tests/test_data_validation.py index 16e3f1c..512fb53 100644 --- a/tests/test_data_validation.py +++ b/tests/test_data_validation.py @@ -61,6 +61,39 @@ def test_data_validation_invalid_report(): assert not report.valid +def test_invalid_data(): + """Test invalid data validation with example files.""" + metadata_file = pathlib.Path(__file__).parent / "test_data" / "validation" / "metadata_for_data_csv.json" + with metadata_file.open("r") as f: + metadata = json.load(f) + + invalid_data_file = ( + pathlib.Path(__file__).parent / "test_data" / "validation" / "invalid_data" / "missing_column.csv" + ) + invalid_data = pd.read_csv(invalid_data_file, delimiter=";") + with pytest.raises(validation.ValidationError, match="Could not find column 'method' in data."): + validation.validate_data(invalid_data, metadata=metadata) + + invalid_data_file = pathlib.Path(__file__).parent / "test_data" / "validation" / "invalid_data" / "extra_column.csv" + invalid_data = pd.read_csv(invalid_data_file, delimiter=";") + with pytest.raises(validation.ValidationError, match="Could not find field 'added_column' in schema."): + validation.validate_data(invalid_data, metadata=metadata) + + invalid_data_file = ( + pathlib.Path(__file__).parent / "test_data" / "validation" / "invalid_data" / "invalid_datatype.csv" + ) + invalid_data = pd.read_csv(invalid_data_file, delimiter=";") + with pytest.raises(validation.ValidationError, match="type-error"): + validation.validate_data(invalid_data, metadata=metadata) + + invalid_data_file = ( + pathlib.Path(__file__).parent / "test_data" / "validation" / "invalid_data" / "duplicate_primary_keys.csv" + ) + invalid_data = pd.read_csv(invalid_data_file, delimiter=";") + with pytest.raises(validation.ValidationError, match="primary-key"): + validation.validate_data(invalid_data, metadata=metadata) + + def test_invalid_arguments_to_validation_function(): """Test different invalid function calls to validation function.""" with pytest.raises(validation.ValidationError, match="Data must be given as pandas.DataFrame."): From cd83c3470e21997c9f8d8b1a6afcecf64e405a8f Mon Sep 17 00:00:00 2001 From: Hendrik Huyskens Date: Tue, 18 Jun 2024 14:24:52 +0200 Subject: [PATCH 28/63] Add warnings for optional fields in metadata validation --- src/omi/validation.py | 33 +++++++++++++++++++ .../validation/metadata_for_data_csv.json | 1 - 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/omi/validation.py b/src/omi/validation.py index 0801645..6abf2e9 100644 --- a/src/omi/validation.py +++ b/src/omi/validation.py @@ -73,6 +73,7 @@ def validate_metadata(metadata: dict | str) -> None: except jsonschema.exceptions.ValidationError as ve: raise ValidationError(f"Error validating metadata against related metadata schema: {ve.message}") from ve license.validate_oemetadata_licenses(metadata) + __validate_optional_fields_in_metadata(metadata, metadata_schema.schema) def validate_data( @@ -386,6 +387,38 @@ def __validate_data_against_schema(data: pd.DataFrame, fields: dict[str, str]) - return report +def __validate_optional_fields_in_metadata(metadata: dict, schema: dict) -> None: + """ + Validate optional fields in metadata dictionary based on schema. Raise warnings if optional fields are missing. + + Parameters + ---------- + metadata: dict + Metadata as dictionary to check optional fields + schema: dict + JSONSchema for checking optional fields + + Returns + ------- + None + """ + + def check_properties(sub_meta: dict, sub_schema: dict, current_path: str) -> None: + """Check optional fields in metadata dictionary iteratively.""" + if "properties" not in sub_schema: + return + for field in sub_schema["properties"]: + if ("required" not in sub_schema or field not in sub_schema["required"]) and field not in sub_meta: + if current_path == "": + current_path = "top level" + warnings.warn(f"Optional field '{field}' not found in metadata at {current_path}.", stacklevel=2) + if field in sub_meta: + new_path = field if current_path == "" else f"{current_path}.{field}" + check_properties(sub_meta[field], sub_schema["properties"][field], new_path) + + check_properties(metadata, schema, "") + + def __map_fields_to_frictionless_fields(fields: dict[str, str]) -> list[Field]: """ Map fields to Frictionless fields. diff --git a/tests/test_data/validation/metadata_for_data_csv.json b/tests/test_data/validation/metadata_for_data_csv.json index fcbc28e..5ae241d 100644 --- a/tests/test_data/validation/metadata_for_data_csv.json +++ b/tests/test_data/validation/metadata_for_data_csv.json @@ -193,7 +193,6 @@ ], "@id":null, "@context":"https://raw.githubusercontent.com/OpenEnergyPlatform/oemetadata/develop/metadata/latest/context.json", - "review":null, "metaMetadata":{ "metadataVersion":"OEP-1.5.2", "metadataLicense":{ From 6d45717c671c7a89cca4d950414c36014b792b67 Mon Sep 17 00:00:00 2001 From: jh-RLI Date: Thu, 24 Oct 2024 11:43:56 +0200 Subject: [PATCH 29/63] fix missing whitespace --- src/omi/cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/omi/cli.py b/src/omi/cli.py index 0a891c0..6b4d0aa 100644 --- a/src/omi/cli.py +++ b/src/omi/cli.py @@ -6,7 +6,7 @@ You might be tempted to import things from __main__ later, but that will cause problems: the code will get executed twice: - - When you run `python -momi` python will execute + - When you run `python -m omi` python will execute ``__main__.py`` as a script. That means there won't be any ``omi.__main__`` in ``sys.modules``. - When you import __main__ it will get executed again (as a module) because @@ -14,6 +14,7 @@ Also see (1) from http://click.pocoo.org/5/setuptools/#setuptools-integration """ + import click From 3637854457ae0455e43cfe5a5b2c944218dc2f8a Mon Sep 17 00:00:00 2001 From: jh-RLI Date: Thu, 24 Oct 2024 11:56:09 +0200 Subject: [PATCH 30/63] update changelog --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c8ec3a7..48bbcd8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,7 +4,7 @@ Changelog current -------------------- -* +* Fully rewrite OMI and implement the json schema spec only, remove python class based parsing (#104)[https://github.com/OpenEnergyPlatform/omi/pull/104] 0.2.1 (2024-01-26) -------------------- From 69b937015f702c784aeaed36a4e656674e6eb181 Mon Sep 17 00:00:00 2001 From: jh-RLI Date: Thu, 24 Oct 2024 12:45:26 +0200 Subject: [PATCH 31/63] add local scrip directory to gitignore (used for local manual testing) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 28e9c6c..e05857e 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,5 @@ docs/_build # manual testing scripts /local_test +/script /.venv/ From c2aeb5b2d2998252860693e2a2733cfd4ea91607 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:49:00 +0000 Subject: [PATCH 32/63] Bump urllib3 from 2.2.1 to 2.2.2 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.1 to 2.2.2. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.2.1...2.2.2) --- updated-dependencies: - dependency-name: urllib3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4a7c4ed..3fa2f48 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1400,13 +1400,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] From 9cf86576f279f698e8bfd7129dc4b89897f1e7d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:49:07 +0000 Subject: [PATCH 33/63] Bump certifi from 2024.2.2 to 2024.7.4 Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.2.2 to 2024.7.4. - [Commits](https://github.com/certifi/python-certifi/compare/2024.02.02...2024.07.04) --- updated-dependencies: - dependency-name: certifi dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4a7c4ed..037c2f8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -43,13 +43,13 @@ files = [ [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] From 5ce621bce8479816e6820da516e062f0be5de921 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Tue, 29 Oct 2024 14:56:42 +0100 Subject: [PATCH 34/63] rework readme (draft) #55 --- README.rst | 70 ++++++++++++++---------------------------------------- 1 file changed, 18 insertions(+), 52 deletions(-) diff --git a/README.rst b/README.rst index be21dd1..e89bbfe 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,10 @@ Open Energy Family - Open Metadata Integration OMI ================================================== +A library to process and translate and work with the open energy metadata. + +* Free software: AGPL-3.0 + Overview ======== @@ -22,17 +26,9 @@ Overview :target: https://readthedocs.org/projects/omi :alt: Documentation Status -.. |travis| image:: https://travis-ci.org/OpenEnergyPlatform/omi.svg?branch=master - :alt: Travis-CI Build Status - :target: https://travis-ci.org/OpenEnergyPlatform/omi - -.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/OpenEnergyPlatform/omi?branch=master&svg=true - :alt: AppVeyor Build Status - :target: https://ci.appveyor.com/project/OpenEnergyPlatform/omi - -.. |requires| image:: https://requires.io/github/OpenEnergyPlatform/omi/requirements.svg?branch=master - :alt: Requirements Status - :target: https://requires.io/github/OpenEnergyPlatform/omi/requirements/?branch=master +.. |Automated Test| image:: https://github.com/OpenEnergyPlatform/omi/actions/workflows/automated-testing.yml/badge.svg + :target: https://github.com/OpenEnergyPlatform/omi/actions/workflows/automated-testing.yml + :alt: Test status .. |codecov| image:: https://codecov.io/github/OpenEnergyPlatform/omi/coverage.svg?branch=master :alt: Coverage Status @@ -61,10 +57,6 @@ Overview .. end-badges -A library to process and translate open energy metadata. - -* Free software: AGPL-3.0 - Installation ============ @@ -75,42 +67,20 @@ Installation Documentation ============= - +Documentation for OMI versions up to 0.2: https://omi.readthedocs.io/ +Documentation for reworked OMI versions starting from 1.0 you can find in the README document. Later on we migrate the documentation to mkdocs. + Usage ===== -**Parse, Compile, Render, Convert and Validate** -Omi can read(parse), compile, Render(json compilant), convert(convert metadata from v1.4 to v1.5 structure) and validate - a json -file or object that is compliant with the oemetadata spec. This is usefull to do various operations that help to integrate with - as -well as in interact with the oemetadata. Some parts of this tool might still be volatile but the code quality is conventionsly improved -as this module is a core component of the oeplatfroms metadata integration system. - -Check if omi is able to read a oemetadata file (for version 1.4 and 1.5) -CLI - oemetadata version 1.5:: - - omi translate -f oep-v1.5 examples/data/metadata_v15.json - -CLI - oemetadata version 1.4:: - - omi translate -f oep-v1.4 -t oep-v1.4 examples/data/metadata_v14.json - -omi is able to read a JSON file and parse it into one of the internal Python structures (depending on the oemetadata version). -The OEPMetadata Python object can then be compiled and converted back to JSON. You can manipulate a successfully parsed -OEPMetadata object. - -Module usage:: - - from omi.dialects.oep.dialect import OEP_V_1_3_Dialect, OEP_V_1_4_Dialect, OEP_V_1_5_Dialect - inp = '{"id":"unique_id"}' #or read from json file - dialect1_5 = OEP_V_1_5_Dialect() - parsed = dialect1_5.parse(input) - print(parsed) - parsed.identifier = "another_unique_id" - compiled = dialect1_5.compile(parsed) - print(compiled) +You can use omi as python module and import its functionality into your codebase or use the cli capabilities. OMI provides tooling for validation +of oemetdata JSON documents using JSON-Schema. It also include helpers to generate the tabular data resource definition to seep up the metadata +creation and helps to select a open license by checking the license identifier against the SPDX license list. +As the oemetadata is updated from time to time we provides conversion functionality to convert metadata documents that use an earlier version +of the oemetadata-specification to help users stick with the latest enhancements the latest oemetadata version offers. **Conversion** @@ -135,17 +105,13 @@ Module usage:: # You can import the JSONParser directly like this: import json - from omi.dialects.oep.parser import JSONParser + from omi import validation with open("tests/data/metadata_v15.json", "r", encoding="utf-8") as f: metadata = json.load(f) - parser = JSONParser() - parser.validate(metadata) - - # check if your metadata is valid for the given schmea - schema = ... get a schema or import form oemetadata module - parser.is_valid(metadata, schema) + result = validation(metadata) + # TBD **Additional Fields - not related to the OEMetadata specification** From 849e0e4623d7b23893ee947dba91f70a0f7b43fb Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 09:09:43 +0100 Subject: [PATCH 35/63] add OEMetaData v20 #102 --- src/omi/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/omi/base.py b/src/omi/base.py index 6469c9b..f6d6a4b 100644 --- a/src/omi/base.py +++ b/src/omi/base.py @@ -7,12 +7,12 @@ from dataclasses import dataclass import requests -from metadata import v152, v160 +from metadata import v20, v152, v160 from .settings import OEP_URL # Order matters! First entry equals latest version of metadata format -METADATA_FORMATS = {"OEP": ["OEP-1.6.0", "OEP-1.5.2"], "INSPIRE": []} +METADATA_FORMATS = {"OEP": ["OEMetadata-2.0.0", "OEP-1.6.0", "OEP-1.5.2"], "INSPIRE": []} METADATA_VERSIONS = {version: md_format for md_format, versions in METADATA_FORMATS.items() for version in versions} @@ -148,7 +148,7 @@ def __get_metadata_specs_for_oep(metadata_version: str) -> MetadataSpecification MetadataSpecification Metadata schema for given metadata version including template and example. """ - metadata_modules = {"OEP-1.5.2": v152, "OEP-1.6.0": v160} + metadata_modules = {"OEP-1.5.2": v152, "OEP-1.6.0": v160, "OEMetadata-2.0.0": v20} metadata_module = metadata_modules[metadata_version] module_path = pathlib.Path(metadata_module.__file__).parent specs = {} From 176b67772c9ca266c00f945404425ead18238eb8 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 09:21:36 +0100 Subject: [PATCH 36/63] add additional imports #102 --- src/omi/conversion.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/omi/conversion.py b/src/omi/conversion.py index 8fa1270..b6f0702 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -1,4 +1,5 @@ """Conversion module for OMI to update metadata to different versions.""" + from __future__ import annotations from copy import deepcopy From 3e5fba4c09ea0ec50f6030ca69ad6b674eb1e041 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 11:18:28 +0100 Subject: [PATCH 37/63] add draft functionality to convert metadata to oemetadata fom v160 to v2 #102 --- src/omi/conversion.py | 127 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/src/omi/conversion.py b/src/omi/conversion.py index b6f0702..6a1923b 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -4,7 +4,7 @@ from copy import deepcopy -from omi.base import get_metadata_version +from omi.base import get_metadata_specification, get_metadata_version class ConversionError(Exception): @@ -96,6 +96,131 @@ def __convert_oep_152_to_160(metadata: dict) -> dict: return metadata +def __convert_oep_160_to_200(metadata: dict) -> dict: # noqa: C901, PLR0915 + """ + Convert metadata with version "OEP-1.6.0" to "OEMetadata-2.0.0" using the v2.0 template. + + Parameters + ---------- + metadata: dict + Metadata dictionary in v1.6 format + + Returns + ------- + dict + Updated metadata dictionary in v2.0 format + """ + metadata_v2 = get_metadata_specification("OEMetadata-2.0.0") + # Deep copy template to avoid mutating the original template + metadata_v2 = deepcopy(metadata_v2.template) + + # Map v1.6 fields to v2.0 fields + metadata_v2["name"] = None + metadata_v2["title"] = None + metadata_v2["id"] = None + + # Populate resources + for i, resource in enumerate(metadata.get("resources", [])): + if i >= len(metadata_v2["resources"]): + metadata_v2["resources"].append(deepcopy(metadata_v2["resources"][0])) + + resource_v2 = metadata_v2["resources"][i] + resource_v2["@id"] = metadata.get("@id") + resource_v2["@context"] = metadata.get("@context") + resource_v2["name"] = resource.get("name").split(".")[1] + resource_v2["topics"] = [resource.get("name", "").split(".")[0]] + resource_v2["title"] = metadata.get("title") + resource_v2["path"] = metadata.get("id") + resource_v2["description"] = metadata.get("description") + resource_v2["languages"] = metadata.get("language", []) + resource_v2["subject"] = metadata.get("subject", []) + resource_v2["keywords"] = metadata.get("keywords", []) + resource_v2["publicationDate"] = metadata.get("publicationDate") + + # Set to null to avoid validation errors: Date + resource_v2["embargoPeriod"]["start"] = None + resource_v2["embargoPeriod"]["end"] = None + + resource_v2["context"] = metadata.get("context", {}) + + # Set to null to avoid validation errors: URI + resource_v2["spatial"]["location"]["@id"] = None + resource_v2["spatial"]["extent"]["name"] = metadata.get("spatial", {}).get("extent") + # Set to null to avoid validation errors: URI + resource_v2["spatial"]["extent"]["@id"] = None + resource_v2["spatial"]["extent"]["resolutionValue"], resource_v2["spatial"]["extent"]["resolutionUnit"] = ( + metadata.get("spatial", {}).get("resolution", "").split(" ", 1) + ) + + resource_v2["temporal"] = metadata.get("temporal", {}) + # # Populate timeseries + # for i_ts, timeseries in enumerate(metadata.get("temporal", {}).get("timeseries", [])): + # if i_ts >= len(resource_v2["temporal"]["timeseries"]): + + # Populate sources + for i_source, source in enumerate(metadata.get("sources", [])): + if i_source >= len(resource_v2["sources"]): + resource_v2["sources"].append(deepcopy(metadata_v2["resources"][0]["sources"][0])) + + sources_v2 = resource_v2["sources"][i_source] + sources_v2["title"] = source.get("title") + sources_v2["description"] = source.get("description") + sources_v2["path"] = source.get("path") + for i_s_license, s_license in enumerate(source.get("licenses", [])): + if i_s_license >= len(sources_v2["licenses"]): + resource_v2["sources"].append(deepcopy(metadata_v2["resources"][0]["sources"][0]["licenses"][0])) + + licenses_v2 = resource_v2["licenses"][i_s_license] + licenses_v2.update(s_license) + licenses_v2["copyrightStatement"] = None + + # _license to avoid shadowing python internal + for i_license, _license in enumerate(metadata.get("licenses", [])): + if i_license >= len(resource_v2["licenses"]): + resource_v2["licenses"].append(deepcopy(metadata_v2["resources"][0]["licenses"][0])) + + licenses_v2 = resource_v2["licenses"][i_license] + licenses_v2.update(_license) + licenses_v2["copyrightStatement"] = None + + for i_contribution, contribution in enumerate(metadata.get("contributors", [])): + if i_contribution >= len(resource_v2["contributors"]): + resource_v2["contributors"].append(deepcopy(metadata_v2["resources"][0]["contributors"][0])) + + contributors_v2 = resource_v2["contributors"][i_contribution] + contributors_v2["title"] = contribution.get("title") + contributors_v2["path"] = contribution.get("path") + contributors_v2["organization"] = contribution.get("organization") + contributors_v2["date"] = contribution.get("date") + contributors_v2["object"] = contribution.get("object") + contributors_v2["comment"] = contribution.get("comment") + + # data resource/distribution definition + resource_v2["type"] = None + resource_v2["format"] = resource.get("format") + resource_v2["encoding"] = resource.get("encoding") + + for i_s_field, field in enumerate(resource.get("schema", {}).get("fields", [])): + if i_s_field >= len(resource_v2["schema"]["fields"]): + resource_v2["schema"]["fields"].append(deepcopy(metadata_v2["resources"][0]["schema"]["fields"][0])) + + schema_fields_v2 = resource_v2["schema"]["fields"][i_s_field] + schema_fields_v2["nullable"] = None + schema_fields_v2.update(field) + + resource_v2["schema"]["primaryKey"] = resource.get("schema", {}).get("primaryKey", []) + resource_v2["schema"]["foreignKeys"] = resource.get("schema", {}).get("foreignKeys", []) + + resource_v2["dialect"] = resource.get("dialect", {}) + resource_v2["review"] = metadata.get("review", {}) + + # Update metaMetadata section + metadata_v2["metaMetadata"]["metadataVersion"] = "OEMetadata-2.0.0" + metadata_v2["metaMetadata"]["metadataLicense"] = metadata.get("metaMetadata", {}).get("metadataLicense") + + return metadata_v2 + + METADATA_CONVERSIONS = { ("OEP-1.5.2", "OEP-1.6.0"): __convert_oep_152_to_160, } From f913ae6fdd9049ce5a94f53c7f7a8d2ec11c8f03 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 11:18:57 +0100 Subject: [PATCH 38/63] add conversion to the chain #102 --- src/omi/conversion.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/omi/conversion.py b/src/omi/conversion.py index 6a1923b..6aaf971 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -223,4 +223,5 @@ def __convert_oep_160_to_200(metadata: dict) -> dict: # noqa: C901, PLR0915 METADATA_CONVERSIONS = { ("OEP-1.5.2", "OEP-1.6.0"): __convert_oep_152_to_160, + ("OEP-1.6.0", "OEMetadata-2.0.0"): __convert_oep_160_to_200, } From ccf9d5ecafd65b2524427f424e1a8f419f68f37d Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 11:24:03 +0100 Subject: [PATCH 39/63] extend license check to handle checking all licenses in any resource (in oemetadata v2 there is a license per resource) #102 --- src/omi/license.py | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/omi/license.py b/src/omi/license.py index 6e4f4e7..9425c40 100644 --- a/src/omi/license.py +++ b/src/omi/license.py @@ -4,6 +4,8 @@ import re from pathlib import Path +from omi.base import get_metadata_version + LICENCES_FILE = Path(__file__).parent / "data" / "licenses.json" @@ -82,27 +84,42 @@ def validate_oemetadata_licenses(metadata: dict) -> None: Returns ------- None - if licences are valid, otherwise LicenseError is raised + if licenses are valid, otherwise LicenseError is raised """ if metadata is None: msg = "Metadata is empty." raise LicenseError(msg) - licenses = metadata.get("licenses", []) + version = get_metadata_version(metadata) + licenses_info = _find_license_field(metadata, version) - if not licenses: + if not licenses_info: msg = "No license information available in the metadata." raise LicenseError(msg) - for i, license_ in enumerate(licenses): - if not license_.get("name"): - raise LicenseError(f"The license name is missing in {i}. license ({license_})") - - if not validate_license(license_["name"]): - raise LicenseError( - f"The (normalized) license name '{license_['name']}' was not found in the SPDX licenses list. " - "(See https://github.com/spdx/license-list-data/blob/main/json/licenses.json).", - ) + for resource_index, licenses in licenses_info: + for i, license_ in enumerate(licenses or []): + if not license_.get("name"): + raise LicenseError( + f"The license name is missing in resource {resource_index}, license {i} ({license_}).", + ) + + if not validate_license(license_["name"]): + raise LicenseError( + f"The (normalized) license name '{license_['name']}' in resource {resource_index}, license {i} " + "was not found in the SPDX licenses list. " + "(See https://github.com/spdx/license-list-data/blob/main/json/licenses.json).", + ) + + +def _find_license_field(metadata: dict, version: str) -> list: + version = get_metadata_version(metadata) + if version == "OEMetadata-2.0.0": + # Include resource index with each license for traceability + return [(i, resource.get("licenses")) for i, resource in enumerate(metadata.get("resources", []))] + else: # noqa: RET505 + # Return -1 as a placeholder index for top-level licenses + return [(-1, metadata.get("licenses", []))] LICENSES = read_licenses() From c0a0dea28a40e3c0faf6691a3b6cc2988fb3a8fa Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 11:24:29 +0100 Subject: [PATCH 40/63] add test for v16 to v2 metadata conversion #102 --- tests/test_conversion.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 6c3e1e4..9d1b647 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -1,4 +1,5 @@ """Tests for OMIs conversion module.""" + import pytest import omi.base @@ -13,6 +14,14 @@ def test_conversion_from_oep_152_to_160(): validation.validate_metadata(converted_metadata_152) +def test_conversion_from_oep_160_to_200(): + """Test conversion from OEP v1.6.0 -> v2.0.0.""" + metadata_schema_160 = omi.base.get_metadata_specification("OEP-1.6.0").example + converted_metadata_160 = conversion.convert_metadata(metadata_schema_160, "OEMetadata-2.0.0") + assert base.get_metadata_version(converted_metadata_160) == "OEMetadata-2.0.0" + validation.validate_metadata(converted_metadata_160) + + def test_conversion_chain(): """Test conversion chain with conversion tree structure.""" From 2c6394a4081d46dc9b0fde7ed0f4c8f4f4b68f52 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 11:28:00 +0100 Subject: [PATCH 41/63] update changelog #102 --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 48bbcd8..6462f0c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,7 @@ Changelog current -------------------- * Fully rewrite OMI and implement the json schema spec only, remove python class based parsing (#104)[https://github.com/OpenEnergyPlatform/omi/pull/104] +* Add a new conversion functionality to convert form v160 to v200 oemetadata [(#111)](https://github.com/rl-institut/super-repo/pull/111) 0.2.1 (2024-01-26) -------------------- From d2671790cba621508fff6a14aeb3eea2b632ec4b Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 13:23:07 +0100 Subject: [PATCH 42/63] refactor conversion function to make flake8 more happy and reduce complexity #102 --- src/omi/conversion.py | 224 +++++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 102 deletions(-) diff --git a/src/omi/conversion.py b/src/omi/conversion.py index 6aaf971..80f10a6 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -96,7 +96,7 @@ def __convert_oep_152_to_160(metadata: dict) -> dict: return metadata -def __convert_oep_160_to_200(metadata: dict) -> dict: # noqa: C901, PLR0915 +def __convert_oep_160_to_200(metadata: dict) -> dict: """ Convert metadata with version "OEP-1.6.0" to "OEMetadata-2.0.0" using the v2.0 template. @@ -110,109 +110,13 @@ def __convert_oep_160_to_200(metadata: dict) -> dict: # noqa: C901, PLR0915 dict Updated metadata dictionary in v2.0 format """ - metadata_v2 = get_metadata_specification("OEMetadata-2.0.0") - # Deep copy template to avoid mutating the original template - metadata_v2 = deepcopy(metadata_v2.template) + metadata_v2 = deepcopy(get_metadata_specification("OEMetadata-2.0.0").template) + metadata_v2["name"] = metadata_v2["title"] = metadata_v2["id"] = None - # Map v1.6 fields to v2.0 fields - metadata_v2["name"] = None - metadata_v2["title"] = None - metadata_v2["id"] = None - - # Populate resources + # Populate metadata v2 resources for i, resource in enumerate(metadata.get("resources", [])): - if i >= len(metadata_v2["resources"]): - metadata_v2["resources"].append(deepcopy(metadata_v2["resources"][0])) - - resource_v2 = metadata_v2["resources"][i] - resource_v2["@id"] = metadata.get("@id") - resource_v2["@context"] = metadata.get("@context") - resource_v2["name"] = resource.get("name").split(".")[1] - resource_v2["topics"] = [resource.get("name", "").split(".")[0]] - resource_v2["title"] = metadata.get("title") - resource_v2["path"] = metadata.get("id") - resource_v2["description"] = metadata.get("description") - resource_v2["languages"] = metadata.get("language", []) - resource_v2["subject"] = metadata.get("subject", []) - resource_v2["keywords"] = metadata.get("keywords", []) - resource_v2["publicationDate"] = metadata.get("publicationDate") - - # Set to null to avoid validation errors: Date - resource_v2["embargoPeriod"]["start"] = None - resource_v2["embargoPeriod"]["end"] = None - - resource_v2["context"] = metadata.get("context", {}) - - # Set to null to avoid validation errors: URI - resource_v2["spatial"]["location"]["@id"] = None - resource_v2["spatial"]["extent"]["name"] = metadata.get("spatial", {}).get("extent") - # Set to null to avoid validation errors: URI - resource_v2["spatial"]["extent"]["@id"] = None - resource_v2["spatial"]["extent"]["resolutionValue"], resource_v2["spatial"]["extent"]["resolutionUnit"] = ( - metadata.get("spatial", {}).get("resolution", "").split(" ", 1) - ) - - resource_v2["temporal"] = metadata.get("temporal", {}) - # # Populate timeseries - # for i_ts, timeseries in enumerate(metadata.get("temporal", {}).get("timeseries", [])): - # if i_ts >= len(resource_v2["temporal"]["timeseries"]): - - # Populate sources - for i_source, source in enumerate(metadata.get("sources", [])): - if i_source >= len(resource_v2["sources"]): - resource_v2["sources"].append(deepcopy(metadata_v2["resources"][0]["sources"][0])) - - sources_v2 = resource_v2["sources"][i_source] - sources_v2["title"] = source.get("title") - sources_v2["description"] = source.get("description") - sources_v2["path"] = source.get("path") - for i_s_license, s_license in enumerate(source.get("licenses", [])): - if i_s_license >= len(sources_v2["licenses"]): - resource_v2["sources"].append(deepcopy(metadata_v2["resources"][0]["sources"][0]["licenses"][0])) - - licenses_v2 = resource_v2["licenses"][i_s_license] - licenses_v2.update(s_license) - licenses_v2["copyrightStatement"] = None - - # _license to avoid shadowing python internal - for i_license, _license in enumerate(metadata.get("licenses", [])): - if i_license >= len(resource_v2["licenses"]): - resource_v2["licenses"].append(deepcopy(metadata_v2["resources"][0]["licenses"][0])) - - licenses_v2 = resource_v2["licenses"][i_license] - licenses_v2.update(_license) - licenses_v2["copyrightStatement"] = None - - for i_contribution, contribution in enumerate(metadata.get("contributors", [])): - if i_contribution >= len(resource_v2["contributors"]): - resource_v2["contributors"].append(deepcopy(metadata_v2["resources"][0]["contributors"][0])) - - contributors_v2 = resource_v2["contributors"][i_contribution] - contributors_v2["title"] = contribution.get("title") - contributors_v2["path"] = contribution.get("path") - contributors_v2["organization"] = contribution.get("organization") - contributors_v2["date"] = contribution.get("date") - contributors_v2["object"] = contribution.get("object") - contributors_v2["comment"] = contribution.get("comment") - - # data resource/distribution definition - resource_v2["type"] = None - resource_v2["format"] = resource.get("format") - resource_v2["encoding"] = resource.get("encoding") - - for i_s_field, field in enumerate(resource.get("schema", {}).get("fields", [])): - if i_s_field >= len(resource_v2["schema"]["fields"]): - resource_v2["schema"]["fields"].append(deepcopy(metadata_v2["resources"][0]["schema"]["fields"][0])) - - schema_fields_v2 = resource_v2["schema"]["fields"][i_s_field] - schema_fields_v2["nullable"] = None - schema_fields_v2.update(field) - - resource_v2["schema"]["primaryKey"] = resource.get("schema", {}).get("primaryKey", []) - resource_v2["schema"]["foreignKeys"] = resource.get("schema", {}).get("foreignKeys", []) - - resource_v2["dialect"] = resource.get("dialect", {}) - resource_v2["review"] = metadata.get("review", {}) + resource_v2 = ensure_resource_entry(metadata_v2, i) + populate_resource_v2(resource_v2, metadata, resource) # Update metaMetadata section metadata_v2["metaMetadata"]["metadataVersion"] = "OEMetadata-2.0.0" @@ -221,6 +125,122 @@ def __convert_oep_160_to_200(metadata: dict) -> dict: # noqa: C901, PLR0915 return metadata_v2 +def ensure_resource_entry(metadata_v2: dict, index: int) -> dict: + """Ensure a resource entry exists in metadata_v2 resources for the given index.""" + if index >= len(metadata_v2["resources"]): + metadata_v2["resources"].append(deepcopy(metadata_v2["resources"][0])) + return metadata_v2["resources"][index] + + +def populate_resource_v2(resource_v2: dict, metadata: dict, resource: dict) -> None: + """Populate resource_v2 fields based on metadata and resource from v1.6.""" + resource_v2.update( + { + "@id": metadata.get("@id"), + "@context": metadata.get("@context"), + "name": resource.get("name").split(".")[1], + "topics": [resource.get("name", "").split(".")[0]], + "title": metadata.get("title"), + "path": metadata.get("id"), + "description": metadata.get("description"), + "languages": metadata.get("language", []), + "subject": metadata.get("subject", []), + "keywords": metadata.get("keywords", []), + "publicationDate": metadata.get("publicationDate"), + "context": metadata.get("context", {}), + "temporal": metadata.get("temporal", {}), + "type": None, + "format": resource.get("format"), + "encoding": resource.get("encoding"), + "schema": { + "fields": resource.get("schema", {}).get("fields", []), + "primaryKey": resource.get("schema", {}).get("primaryKey", []), + "foreignKeys": resource.get("schema", {}).get("foreignKeys", []), + }, + "dialect": resource.get("dialect", {}), + "review": metadata.get("review", {}), + }, + ) + + resource_v2["embargoPeriod"]["start"] = None + resource_v2["embargoPeriod"]["end"] = None + + # Set to null to avoid validation errors: URI + resource_v2["spatial"]["location"]["@id"] = None + resource_v2["spatial"]["extent"]["address"] = None + resource_v2["spatial"]["extent"]["name"] = metadata.get("spatial", {}).get("extent") + resource_v2["spatial"]["extent"]["latitude"] = None + resource_v2["spatial"]["extent"]["longitude"] = None + # Set to null to avoid validation errors: URI + resource_v2["spatial"]["extent"]["@id"] = None + resource_v2["spatial"]["extent"]["resolutionValue"], resource_v2["spatial"]["extent"]["resolutionUnit"] = ( + metadata.get("spatial", {}).get("resolution", "").split(" ", 1) + ) + + populate_sources(resource_v2, metadata.get("sources", [])) + populate_contributors(resource_v2, metadata.get("contributors", [])) + populate_licenses(resource_v2, metadata.get("licenses", [])) + populate_schema_fields(resource_v2, resource) + + +def populate_sources(resource_v2: dict, sources: list) -> None: + """Populate sources in resource_v2 from sources in v1.6.""" + for i_source, source in enumerate(sources): + if i_source >= len(resource_v2["sources"]): + resource_v2["sources"].append(deepcopy(resource_v2["sources"][0])) + source_v2 = resource_v2["sources"][i_source] + source_v2.update( + {"title": source.get("title"), "description": source.get("description"), "path": source.get("path")}, + ) + populate_source_licenses(source_v2, source.get("licenses", [])) + + +def populate_source_licenses(source_v2: dict, licenses: list) -> None: + """Populate licenses in source_v2 from licenses in v1.6.""" + for i_license, license_entry in enumerate(licenses): + if i_license >= len(source_v2["licenses"]): + source_v2["licenses"].append(deepcopy(source_v2["licenses"][0])) + source_v2["licenses"][i_license].update(license_entry) + source_v2["licenses"][i_license]["copyrightStatement"] = None + + +def populate_contributors(resource_v2: dict, contributors: list) -> None: + """Populate contributors in resource_v2 from contributors in v1.6.""" + for i_contribution, contributor in enumerate(contributors): + if i_contribution >= len(resource_v2["contributors"]): + resource_v2["contributors"].append(deepcopy(resource_v2["contributors"][0])) + contributor_v2 = resource_v2["contributors"][i_contribution] + contributor_v2.update( + { + "title": contributor.get("title"), + "path": contributor.get("path"), + "organization": contributor.get("organization"), + "date": contributor.get("date"), + "object": contributor.get("object"), + "comment": contributor.get("comment"), + }, + ) + + +def populate_licenses(resource_v2: dict, licenses: list) -> None: + """Populate licenses in resource_v2 from licenses in v1.6.""" + for i_license, license_entry in enumerate(licenses): + if i_license >= len(resource_v2["licenses"]): + resource_v2["licenses"].append(deepcopy(resource_v2["licenses"][0])) + resource_v2["licenses"][i_license].update(license_entry) + resource_v2["licenses"][i_license]["copyrightStatement"] = None + + +def populate_schema_fields(resource_v2: dict, resource: dict) -> None: + """Populate schema fields in resource_v2 from resource in v1.6.""" + for i_field, field in enumerate(resource.get("schema", {}).get("fields", [])): + if i_field >= len(resource_v2["schema"]["fields"]): + resource_v2["schema"]["fields"].append(deepcopy(resource_v2["schema"]["fields"][0])) + schema_field_v2 = resource_v2["schema"]["fields"][i_field] + schema_field_v2.update(field) + schema_field_v2["nullable"] = None + + METADATA_CONVERSIONS = { ("OEP-1.5.2", "OEP-1.6.0"): __convert_oep_152_to_160, ("OEP-1.6.0", "OEMetadata-2.0.0"): __convert_oep_160_to_200, From 4159d89a3b489bfaf6787138feceecaa299cea76 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 13:41:26 +0100 Subject: [PATCH 43/63] enhance spatial conversion #102 --- src/omi/conversion.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/omi/conversion.py b/src/omi/conversion.py index 80f10a6..9ea8042 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -167,15 +167,16 @@ def populate_resource_v2(resource_v2: dict, metadata: dict, resource: dict) -> N # Set to null to avoid validation errors: URI resource_v2["spatial"]["location"]["@id"] = None - resource_v2["spatial"]["extent"]["address"] = None - resource_v2["spatial"]["extent"]["name"] = metadata.get("spatial", {}).get("extent") - resource_v2["spatial"]["extent"]["latitude"] = None - resource_v2["spatial"]["extent"]["longitude"] = None + resource_v2["spatial"]["location"]["address"] = metadata.get("spatial", {}).get("location") + resource_v2["spatial"]["location"]["latitude"] = None + resource_v2["spatial"]["location"]["longitude"] = None # Set to null to avoid validation errors: URI + resource_v2["spatial"]["extent"]["name"] = metadata.get("spatial", {}).get("extent") resource_v2["spatial"]["extent"]["@id"] = None resource_v2["spatial"]["extent"]["resolutionValue"], resource_v2["spatial"]["extent"]["resolutionUnit"] = ( metadata.get("spatial", {}).get("resolution", "").split(" ", 1) ) + resource_v2["spatial"]["extent"]["crs"] = None populate_sources(resource_v2, metadata.get("sources", [])) populate_contributors(resource_v2, metadata.get("contributors", [])) From 1c1bff56adce8fda2fbc35195ce3b38a06b23633 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 13:43:00 +0100 Subject: [PATCH 44/63] fix license check to raise error if license is empty #102 --- src/omi/license.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/omi/license.py b/src/omi/license.py index 9425c40..4d74636 100644 --- a/src/omi/license.py +++ b/src/omi/license.py @@ -93,20 +93,19 @@ def validate_oemetadata_licenses(metadata: dict) -> None: version = get_metadata_version(metadata) licenses_info = _find_license_field(metadata, version) - if not licenses_info: - msg = "No license information available in the metadata." - raise LicenseError(msg) - for resource_index, licenses in licenses_info: + if not licenses: + raise LicenseError(f"No license information available in the metadata for resource: {resource_index + 1}.") for i, license_ in enumerate(licenses or []): if not license_.get("name"): raise LicenseError( - f"The license name is missing in resource {resource_index}, license {i} ({license_}).", + f"The license name is missing in resource {resource_index + 1}, license {i + 1} ({license_}).", ) if not validate_license(license_["name"]): raise LicenseError( - f"The (normalized) license name '{license_['name']}' in resource {resource_index}, license {i} " + f"The (normalized) license name '{license_['name']}' in resource" + f"{resource_index + 1}, license {i + 1} " "was not found in the SPDX licenses list. " "(See https://github.com/spdx/license-list-data/blob/main/json/licenses.json).", ) @@ -116,10 +115,14 @@ def _find_license_field(metadata: dict, version: str) -> list: version = get_metadata_version(metadata) if version == "OEMetadata-2.0.0": # Include resource index with each license for traceability - return [(i, resource.get("licenses")) for i, resource in enumerate(metadata.get("resources", []))] - else: # noqa: RET505 + licenses_per_resource = [ + (i, resource.get("licenses")) for i, resource in enumerate(metadata.get("resources", [])) + ] + else: # Return -1 as a placeholder index for top-level licenses - return [(-1, metadata.get("licenses", []))] + licenses_per_resource = [(0, metadata.get("licenses", []))] + + return licenses_per_resource LICENSES = read_licenses() From e2f5dacdc7a97238c0141474556241883ceb7381 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 13:43:50 +0100 Subject: [PATCH 45/63] update expected error msg in license empty test #102 --- tests/test_metadata_validation.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_metadata_validation.py b/tests/test_metadata_validation.py index c4b8db5..a5aba38 100644 --- a/tests/test_metadata_validation.py +++ b/tests/test_metadata_validation.py @@ -35,7 +35,10 @@ def test_invalid_oep_metadata(): with (INVALID_METADAT_PATH / "missing_fields.json").open("r") as f: invalid_oep_metadata = json.load(f) - with pytest.raises(license.LicenseError, match="No license information available in the metadata."): + with pytest.raises( + license.LicenseError, + match=r"No license information available in the metadata for resource: \d+\.?", + ): validation.validate_metadata(invalid_oep_metadata) with (INVALID_METADAT_PATH / "wrongly_placed_null_value.json").open("r") as f: From ca25bb726e2a7c9e6284c4cfb33454c17d9511da Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 16:39:44 +0100 Subject: [PATCH 46/63] handle new fields see #102 --- src/omi/conversion.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/omi/conversion.py b/src/omi/conversion.py index 9ea8042..3242314 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -111,7 +111,7 @@ def __convert_oep_160_to_200(metadata: dict) -> dict: Updated metadata dictionary in v2.0 format """ metadata_v2 = deepcopy(get_metadata_specification("OEMetadata-2.0.0").template) - metadata_v2["name"] = metadata_v2["title"] = metadata_v2["id"] = None + metadata_v2["name"] = metadata_v2["title"] = metadata_v2["id"] = metadata_v2["description"] = None # Populate metadata v2 resources for i, resource in enumerate(metadata.get("resources", [])): @@ -134,6 +134,7 @@ def ensure_resource_entry(metadata_v2: dict, index: int) -> dict: def populate_resource_v2(resource_v2: dict, metadata: dict, resource: dict) -> None: """Populate resource_v2 fields based on metadata and resource from v1.6.""" + # Bulk update keys without resource_v2.update( { "@id": metadata.get("@id"), @@ -162,6 +163,8 @@ def populate_resource_v2(resource_v2: dict, metadata: dict, resource: dict) -> N }, ) + resource_v2["context"]["publisher"] = None + resource_v2["embargoPeriod"]["start"] = None resource_v2["embargoPeriod"]["end"] = None @@ -191,7 +194,13 @@ def populate_sources(resource_v2: dict, sources: list) -> None: resource_v2["sources"].append(deepcopy(resource_v2["sources"][0])) source_v2 = resource_v2["sources"][i_source] source_v2.update( - {"title": source.get("title"), "description": source.get("description"), "path": source.get("path")}, + { + "title": source.get("title"), + "description": source.get("description"), + "path": source.get("path"), + "publicationYear": None, + "authors": [], + }, ) populate_source_licenses(source_v2, source.get("licenses", [])) From e94edb122e816a396f4167e9f83668ebb6526369 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 17:40:12 +0100 Subject: [PATCH 47/63] provide basic documentation for new omi implementation #55 --- README.rst | 85 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/README.rst b/README.rst index e89bbfe..79e4df4 100644 --- a/README.rst +++ b/README.rst @@ -84,14 +84,42 @@ of the oemetadata-specification to help users stick with the latest enhancements **Conversion** -To ease the conversion of oemetadata from the outdated version 1.4 to the latest version, we provide -conversion functionality. The following example shows how to convert the oemetadata from v1.4 to v1.5 -by using a CLI command. +To ease the conversion of oemetadata from any outdated version to the latest version, we provide a +conversion functionality. The following example shows how to convert the oemetadata from v1.6 to v2.0. -CLI - oemetadata conversion from v1.4 to v1.5:: +CLI - oemetadata conversion:: + # Not implemented yet omi convert -i {input/path} -o {output/path} +Module usage - In python scripts you can use the conversion:: + + from omi.conversion import convert_metadata + + import json + + # you a function like this one to read you oemetadata json file + def read_json_file(file_path: str) -> dict: + with open(file_path, "r") as file: + data = json.load(file) + return data + + # for example you can use the oemetdata example.json for version 1.6.0 + # find it here https://github.com/OpenEnergyPlatform/oemetadata/blob/develop/metadata/v160/example.json + # make sure to provide a valid path relative to where you store the python environment + file_path = "example_v16.json" + + # read the metadata document + meta = read_json_file(file_path) + + # use omi to convert it to the latest release + converted = convert_metadata(meta, "OEMetadata-2.0.0") + + # now you can store the result as json file + with open("result.json", "w", encoding="utf-8") as json_file: + json.dump(converted, json_file, ensure_ascii=False, indent=4) # `indent=4` makes the JSON file easier to read + + **Validation** The validation is based on `jsonschema`. We release a schema with each `oemetadata` release, that schema @@ -103,42 +131,41 @@ the validation will try to get the matching schema for the current metadata. Module usage:: - # You can import the JSONParser directly like this: import json - from omi import validation - - with open("tests/data/metadata_v15.json", "r", encoding="utf-8") as f: - metadata = json.load(f) - - result = validation(metadata) - # TBD - -**Additional Fields - not related to the OEMetadata specification** + from omi.validation import validate_oemetadata_licenses, validate_metadata -Sometimes it is necessary to store additional key-value pairs along with the keys included in the OEMetadata specification. -OMI's compiler methods are capable of handling additional arguments or key-value arguments, but this must be -be explicitly specified. -To add additional key-value pairs, you must: + # use a function like this one to read you oemetadata json file + def read_json_file(file_path: str) -> dict: + with open(file_path, "r") as file: + data = json.load(file) + return data - NOTE: If you save the renderer return value in a json file and try to parse the file, the extra field is not included. - You must read the json file using Python and then add the extra field back oemetadata object as shown below. + # for example you can use the oemetdata example.json for version 2.0.0 + # find it here https://github.com/OpenEnergyPlatform/oemetadata/blob/develop/metadata/v20/example.json + # make sure to provide a valid path relative to where you store the python environment + file_path = "example_v16.json" -1 Parse the oemetadata from json file / variable into omis internal structure:: + # read the new input from file + meta = read_json_file(file_path) - from omi.dialects.oep.dialect import OEP_V_1_5_Dialect + # validate the oemetadata: This will return noting or the errors including descriptions + validate_metadata(meta) - min_inp = '{"id":"unique_id"} # or read from json file - minimal_oemetadata15 = OEP_V_1_5_Dialect.parse(min_inp) + # As we are prone to open data we use this license check to validate the license name that + # is available in the metadata document for each data resource/distribution. + validate_oemetadata_licenses(meta) -2 Now you can get(from json file)/define the additional data:: - data = "test" +**Additional Fields ** -3 And add it to the OEMetadata object that was parsed in step 1 by ading a key-value argument:: +To be in line with the oemetadata specification we do not allow for additional properties or fields in the metadata. +We want to keep the oemetadata relatively lean and readable still linking to other documents or to +propose a new property to extend the oemetadata would be a possibility here. - compiled = OEP_V_1_5_Dialect.compile(minimal_oemetadata15, _additionalField=data) - rendered = OEP_V_1_5_Dialect.render(compiled) +Still some times it becomes necessary to add additional information then this would be a use case outside of the OpenEnergyPlatform +specifically for your own use. You are welcome to use the oemetadata as base and add new fields we are happy to integrate them +back into the oeplatform and oemetadata if they seem relevant to other users. Development =========== From 488b6a892f6ed01080a90e0b3a4b5b52427836a5 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Wed, 30 Oct 2024 22:53:50 +0100 Subject: [PATCH 48/63] add inspection --- README.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.rst b/README.rst index 79e4df4..801cae7 100644 --- a/README.rst +++ b/README.rst @@ -157,6 +157,16 @@ Module usage:: validate_oemetadata_licenses(meta) +**Inspection** + +Describing your data structure is a quite technical task. OMI offers functionality to describe your data automatically. +You need to provide yor data in tabular text based format for this, for example a CSV file. Using frictionless OMI +guesses the data schema specification you can use this you provide required fields in an oemetadata document. + +Module usage:: + + + **Additional Fields ** To be in line with the oemetadata specification we do not allow for additional properties or fields in the metadata. From 96fd036fa866c1ee5bee62eda1717372a97c85cb Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 11:51:47 +0100 Subject: [PATCH 49/63] prepare for release --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 48bbcd8..838a98b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,8 @@ current -------------------- * Fully rewrite OMI and implement the json schema spec only, remove python class based parsing (#104)[https://github.com/OpenEnergyPlatform/omi/pull/104] +1.0.0 (2024-10-31) + 0.2.1 (2024-01-26) -------------------- * Reorder metadata fields after the json input was compiled & prevent removing context fields if they are Null (#96)[https://github.com/OpenEnergyPlatform/omi/pull/96] From deb3c01ffaaeafdc7b6e4ca0947454580ede8b09 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 12:57:51 +0100 Subject: [PATCH 50/63] add usage example for infer metadata functionality --- README.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.rst b/README.rst index 801cae7..1f7b43c 100644 --- a/README.rst +++ b/README.rst @@ -129,6 +129,12 @@ This will create a report.json containing information to debug possible errors. two arguments the first one is the metadata and the second optional one is the schmea. By default (if no schema is passed) the validation will try to get the matching schema for the current metadata. + +CLI - oemetadata conversion:: + + # Not implemented yet + + Module usage:: import json @@ -163,9 +169,26 @@ Describing your data structure is a quite technical task. OMI offers functionali You need to provide yor data in tabular text based format for this, for example a CSV file. Using frictionless OMI guesses the data schema specification you can use this you provide required fields in an oemetadata document. +CLI - oemetadata conversion:: + + # Not implemented yet + Module usage:: + import json + + import pathlib + + from omi.inspection import infer_metadata + + CSV_DATA_FILE = pathlib.Path(__file__).parent / "data" / "data.csv" + # infer the data fields from CSV fuile and add to an empty metadata template + with CSV_DATA_FILE.open("r") as f: + metadata = infer_metadata(f, "OEP") + # Save to a JSON file + with open("script/metadata/result_inspection.json", "w", encoding="utf-8") as json_file: + json.dump(metadata, json_file, ensure_ascii=False, indent=4) # `indent=4` makes the JSON file easier to read **Additional Fields ** From c51870551f72fa538dc82818aaa4fe89315443df Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 12:59:14 +0100 Subject: [PATCH 51/63] fix code block --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 1f7b43c..8fdec07 100644 --- a/README.rst +++ b/README.rst @@ -174,6 +174,7 @@ CLI - oemetadata conversion:: # Not implemented yet Module usage:: + import json import pathlib From 12019c9d9662ded799b1ac3a3517519c779722ed Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 13:43:49 +0100 Subject: [PATCH 52/63] enhance helper function names for oem v2 conversion functionality --- src/omi/conversion.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/omi/conversion.py b/src/omi/conversion.py index 3242314..57bee0c 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -115,8 +115,8 @@ def __convert_oep_160_to_200(metadata: dict) -> dict: # Populate metadata v2 resources for i, resource in enumerate(metadata.get("resources", [])): - resource_v2 = ensure_resource_entry(metadata_v2, i) - populate_resource_v2(resource_v2, metadata, resource) + resource_v2 = ___v2_ensure_resource_entry(metadata_v2, i) + ___v2_populate_resource_v2(resource_v2, metadata, resource) # Update metaMetadata section metadata_v2["metaMetadata"]["metadataVersion"] = "OEMetadata-2.0.0" @@ -125,14 +125,14 @@ def __convert_oep_160_to_200(metadata: dict) -> dict: return metadata_v2 -def ensure_resource_entry(metadata_v2: dict, index: int) -> dict: +def ___v2_ensure_resource_entry(metadata_v2: dict, index: int) -> dict: """Ensure a resource entry exists in metadata_v2 resources for the given index.""" if index >= len(metadata_v2["resources"]): metadata_v2["resources"].append(deepcopy(metadata_v2["resources"][0])) return metadata_v2["resources"][index] -def populate_resource_v2(resource_v2: dict, metadata: dict, resource: dict) -> None: +def ___v2_populate_resource_v2(resource_v2: dict, metadata: dict, resource: dict) -> None: """Populate resource_v2 fields based on metadata and resource from v1.6.""" # Bulk update keys without resource_v2.update( @@ -181,13 +181,13 @@ def populate_resource_v2(resource_v2: dict, metadata: dict, resource: dict) -> N ) resource_v2["spatial"]["extent"]["crs"] = None - populate_sources(resource_v2, metadata.get("sources", [])) - populate_contributors(resource_v2, metadata.get("contributors", [])) - populate_licenses(resource_v2, metadata.get("licenses", [])) - populate_schema_fields(resource_v2, resource) + ___v2_populate_sources(resource_v2, metadata.get("sources", [])) + ___v2_populate_contributors(resource_v2, metadata.get("contributors", [])) + ___v2_populate_licenses(resource_v2, metadata.get("licenses", [])) + ___v2_populate_schema_fields(resource_v2, resource) -def populate_sources(resource_v2: dict, sources: list) -> None: +def ___v2_populate_sources(resource_v2: dict, sources: list) -> None: """Populate sources in resource_v2 from sources in v1.6.""" for i_source, source in enumerate(sources): if i_source >= len(resource_v2["sources"]): @@ -202,10 +202,10 @@ def populate_sources(resource_v2: dict, sources: list) -> None: "authors": [], }, ) - populate_source_licenses(source_v2, source.get("licenses", [])) + ___v2_populate_source_licenses(source_v2, source.get("licenses", [])) -def populate_source_licenses(source_v2: dict, licenses: list) -> None: +def ___v2_populate_source_licenses(source_v2: dict, licenses: list) -> None: """Populate licenses in source_v2 from licenses in v1.6.""" for i_license, license_entry in enumerate(licenses): if i_license >= len(source_v2["licenses"]): @@ -214,7 +214,7 @@ def populate_source_licenses(source_v2: dict, licenses: list) -> None: source_v2["licenses"][i_license]["copyrightStatement"] = None -def populate_contributors(resource_v2: dict, contributors: list) -> None: +def ___v2_populate_contributors(resource_v2: dict, contributors: list) -> None: """Populate contributors in resource_v2 from contributors in v1.6.""" for i_contribution, contributor in enumerate(contributors): if i_contribution >= len(resource_v2["contributors"]): @@ -232,7 +232,7 @@ def populate_contributors(resource_v2: dict, contributors: list) -> None: ) -def populate_licenses(resource_v2: dict, licenses: list) -> None: +def ___v2_populate_licenses(resource_v2: dict, licenses: list) -> None: """Populate licenses in resource_v2 from licenses in v1.6.""" for i_license, license_entry in enumerate(licenses): if i_license >= len(resource_v2["licenses"]): @@ -241,7 +241,7 @@ def populate_licenses(resource_v2: dict, licenses: list) -> None: resource_v2["licenses"][i_license]["copyrightStatement"] = None -def populate_schema_fields(resource_v2: dict, resource: dict) -> None: +def ___v2_populate_schema_fields(resource_v2: dict, resource: dict) -> None: """Populate schema fields in resource_v2 from resource in v1.6.""" for i_field, field in enumerate(resource.get("schema", {}).get("fields", [])): if i_field >= len(resource_v2["schema"]["fields"]): From 206d314010188800d0b8af90956fdb07192743e9 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 13:45:15 +0100 Subject: [PATCH 53/63] remove deprecated badges --- README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 8fdec07..6fc474c 100644 --- a/README.rst +++ b/README.rst @@ -17,8 +17,7 @@ Overview * - docs - |docs| * - tests - - | |travis| |appveyor| |requires| - | |codecov| + - | |codecov| * - package - | |version| |wheel| |supported-versions| |supported-implementations| | |commits-since| From 2f1b9cc38f4d6fe6c9db7620eadbb6a8de019eaf Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 13:46:37 +0100 Subject: [PATCH 54/63] show tests runs badge --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 6fc474c..9b97a4b 100644 --- a/README.rst +++ b/README.rst @@ -17,7 +17,7 @@ Overview * - docs - |docs| * - tests - - | |codecov| + - | |Automated test| |codecov| * - package - | |version| |wheel| |supported-versions| |supported-implementations| | |commits-since| @@ -25,7 +25,7 @@ Overview :target: https://readthedocs.org/projects/omi :alt: Documentation Status -.. |Automated Test| image:: https://github.com/OpenEnergyPlatform/omi/actions/workflows/automated-testing.yml/badge.svg +.. |Automated test| image:: https://github.com/OpenEnergyPlatform/omi/actions/workflows/automated-testing.yml/badge.svg :target: https://github.com/OpenEnergyPlatform/omi/actions/workflows/automated-testing.yml :alt: Test status From 53715ccd33a823f2d782b8eafc4bab6f525e35a7 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 13:48:32 +0100 Subject: [PATCH 55/63] enhance description --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9b97a4b..746f226 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ Open Energy Family - Open Metadata Integration OMI ================================================== -A library to process and translate and work with the open energy metadata. +A library to work with the open energy metadata. Its main features are validation, version conversion and infer data schemas from CSV to oemetadata. * Free software: AGPL-3.0 From e08ca8ae32860fcd967f969936b9cccb909d6010 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 16:00:40 +0100 Subject: [PATCH 56/63] update to new oemetadata v2.0.1 --- README.rst | 2 +- src/omi/base.py | 4 ++-- src/omi/conversion.py | 8 ++++---- src/omi/license.py | 2 +- tests/test_conversion.py | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index 746f226..3577151 100644 --- a/README.rst +++ b/README.rst @@ -112,7 +112,7 @@ Module usage - In python scripts you can use the conversion:: meta = read_json_file(file_path) # use omi to convert it to the latest release - converted = convert_metadata(meta, "OEMetadata-2.0.0") + converted = convert_metadata(meta, "OEMetadata-2.0.1") # now you can store the result as json file with open("result.json", "w", encoding="utf-8") as json_file: diff --git a/src/omi/base.py b/src/omi/base.py index f6d6a4b..7804a80 100644 --- a/src/omi/base.py +++ b/src/omi/base.py @@ -12,7 +12,7 @@ from .settings import OEP_URL # Order matters! First entry equals latest version of metadata format -METADATA_FORMATS = {"OEP": ["OEMetadata-2.0.0", "OEP-1.6.0", "OEP-1.5.2"], "INSPIRE": []} +METADATA_FORMATS = {"OEP": ["OEMetadata-2.0.1", "OEP-1.6.0", "OEP-1.5.2"], "INSPIRE": []} METADATA_VERSIONS = {version: md_format for md_format, versions in METADATA_FORMATS.items() for version in versions} @@ -148,7 +148,7 @@ def __get_metadata_specs_for_oep(metadata_version: str) -> MetadataSpecification MetadataSpecification Metadata schema for given metadata version including template and example. """ - metadata_modules = {"OEP-1.5.2": v152, "OEP-1.6.0": v160, "OEMetadata-2.0.0": v20} + metadata_modules = {"OEP-1.5.2": v152, "OEP-1.6.0": v160, "OEMetadata-2.0.1": v20} metadata_module = metadata_modules[metadata_version] module_path = pathlib.Path(metadata_module.__file__).parent specs = {} diff --git a/src/omi/conversion.py b/src/omi/conversion.py index 57bee0c..d7e5b71 100644 --- a/src/omi/conversion.py +++ b/src/omi/conversion.py @@ -98,7 +98,7 @@ def __convert_oep_152_to_160(metadata: dict) -> dict: def __convert_oep_160_to_200(metadata: dict) -> dict: """ - Convert metadata with version "OEP-1.6.0" to "OEMetadata-2.0.0" using the v2.0 template. + Convert metadata with version "OEP-1.6.0" to "OEMetadata-2.0.1" using the v2.0 template. Parameters ---------- @@ -110,7 +110,7 @@ def __convert_oep_160_to_200(metadata: dict) -> dict: dict Updated metadata dictionary in v2.0 format """ - metadata_v2 = deepcopy(get_metadata_specification("OEMetadata-2.0.0").template) + metadata_v2 = deepcopy(get_metadata_specification("OEMetadata-2.0.1").template) metadata_v2["name"] = metadata_v2["title"] = metadata_v2["id"] = metadata_v2["description"] = None # Populate metadata v2 resources @@ -119,7 +119,7 @@ def __convert_oep_160_to_200(metadata: dict) -> dict: ___v2_populate_resource_v2(resource_v2, metadata, resource) # Update metaMetadata section - metadata_v2["metaMetadata"]["metadataVersion"] = "OEMetadata-2.0.0" + metadata_v2["metaMetadata"]["metadataVersion"] = "OEMetadata-2.0.1" metadata_v2["metaMetadata"]["metadataLicense"] = metadata.get("metaMetadata", {}).get("metadataLicense") return metadata_v2 @@ -253,5 +253,5 @@ def ___v2_populate_schema_fields(resource_v2: dict, resource: dict) -> None: METADATA_CONVERSIONS = { ("OEP-1.5.2", "OEP-1.6.0"): __convert_oep_152_to_160, - ("OEP-1.6.0", "OEMetadata-2.0.0"): __convert_oep_160_to_200, + ("OEP-1.6.0", "OEMetadata-2.0.1"): __convert_oep_160_to_200, } diff --git a/src/omi/license.py b/src/omi/license.py index 4d74636..f0f6f9e 100644 --- a/src/omi/license.py +++ b/src/omi/license.py @@ -113,7 +113,7 @@ def validate_oemetadata_licenses(metadata: dict) -> None: def _find_license_field(metadata: dict, version: str) -> list: version = get_metadata_version(metadata) - if version == "OEMetadata-2.0.0": + if version == "OEMetadata-2.0.1": # Include resource index with each license for traceability licenses_per_resource = [ (i, resource.get("licenses")) for i, resource in enumerate(metadata.get("resources", [])) diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 9d1b647..c848bf3 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -17,8 +17,8 @@ def test_conversion_from_oep_152_to_160(): def test_conversion_from_oep_160_to_200(): """Test conversion from OEP v1.6.0 -> v2.0.0.""" metadata_schema_160 = omi.base.get_metadata_specification("OEP-1.6.0").example - converted_metadata_160 = conversion.convert_metadata(metadata_schema_160, "OEMetadata-2.0.0") - assert base.get_metadata_version(converted_metadata_160) == "OEMetadata-2.0.0" + converted_metadata_160 = conversion.convert_metadata(metadata_schema_160, "OEMetadata-2.0.1") + assert base.get_metadata_version(converted_metadata_160) == "OEMetadata-2.0.1" validation.validate_metadata(converted_metadata_160) From bf2f4d50f60a288645aacb5ee53538535608df43 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 16:02:34 +0100 Subject: [PATCH 57/63] deactivate test that depend on a OEP table for now. Setup a better test structure later --- tests/test_base.py | 5 +++-- tests/test_data_validation.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_base.py b/tests/test_base.py index 7fbd7f0..c6faae6 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,10 +1,11 @@ """Tests for OMIs `base` package.""" + import pytest from omi import base, validation -def test_metadata_from_oep(): +def deactivate_test_metadata_from_oep(): """Test metadata from OEP.""" metadata = base.get_metadata_from_oep_table("ind_steel_pellet_1") validation.validate_metadata(metadata) @@ -19,7 +20,7 @@ def test_metadata_from_oep_non_existing_table(): base.get_metadata_from_oep_table("non_existing_table") -def test_metadata_from_oep_empty(): +def deactivate_test_metadata_from_oep_empty(): """Test error for empty metadata.""" with pytest.raises( base.MetadataError, diff --git a/tests/test_data_validation.py b/tests/test_data_validation.py index 512fb53..6772e47 100644 --- a/tests/test_data_validation.py +++ b/tests/test_data_validation.py @@ -1,4 +1,5 @@ """Tests for validating data via OMI.""" + import json import pathlib @@ -9,7 +10,7 @@ from omi import validation -def test_data_validation_against_oep(): +def deactivate_test_data_validation_against_oep(): """Test data validation with example file against OEP table.""" valid_data_file = pathlib.Path(__file__).parent / "test_data" / "validation" / "hackathon_lignite_hh_valid.csv" valid_data = pd.read_csv(valid_data_file, delimiter=";") @@ -26,7 +27,7 @@ def test_data_validation_against_metadata(): validation.validate_data(valid_data, metadata=metadata) -def test_data_validation_report(): +def deactivate_test_data_validation_report(): """Test data validation with example file.""" valid_data_file = pathlib.Path(__file__).parent / "test_data" / "validation" / "hackathon_lignite_hh_valid.csv" valid_data = pd.read_csv(valid_data_file, delimiter=";") From 1c36b73bd3d772b2b99f67d2101cf3b3be5de64b Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 16:24:40 +0100 Subject: [PATCH 58/63] bump version -> 1.0.0 --- .bumpversion.toml | 2 +- pyproject.toml | 2 +- setup.cfg | 23 ++++++++++++----------- src/omi/__init__.py | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index 978825e..62cc0de 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.2.1" +current_version = "1.0.0" parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)" serialize = ["{major}.{minor}.{patch}"] search = "{current_version}" diff --git a/pyproject.toml b/pyproject.toml index 02e670d..e58d855 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "omi" -version = "0.2.1" +version = "1.0.0" description = "A library to process and translate open energy metadata." authors = [ "Hendrik Huyskens ", diff --git a/setup.cfg b/setup.cfg index 935dd3a..15556f8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,9 @@ +[bumpversion] +current_version = 1.0.0 + [bdist_wheel] universal = 1 - [flake8] max-line-length = 140 exclude = */migrations/* @@ -9,18 +11,17 @@ exclude = */migrations/* [tool:pytest] testpaths = tests norecursedirs = - migrations - + migrations python_files = - test_*.py - *_test.py - tests.py + test_*.py + *_test.py + tests.py addopts = - -ra - --strict - --doctest-modules - --doctest-glob=\*.rst - --tb=short + -ra + --strict + --doctest-modules + --doctest-glob=\*.rst + --tb=short [isort] force_single_line = True diff --git a/src/omi/__init__.py b/src/omi/__init__.py index 3ced358..5becc17 100644 --- a/src/omi/__init__.py +++ b/src/omi/__init__.py @@ -1 +1 @@ -__version__ = "0.2.1" +__version__ = "1.0.0" From 7e58ea1c229252851054dbad2829b3453b428e8e Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 16:25:16 +0100 Subject: [PATCH 59/63] Update changelog for v1.0.0 release --- CHANGELOG.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f62ecc0..63c7836 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,10 +4,13 @@ Changelog current -------------------- -* Fully rewrite OMI and implement the json schema spec only, remove python class based parsing (#104)[https://github.com/OpenEnergyPlatform/omi/pull/104] -* Add a new conversion functionality to convert form v160 to v200 oemetadata [(#111)](https://github.com/rl-institut/super-repo/pull/111) +* + 1.0.0 (2024-10-31) +-------------------- +* Fully rewrite OMI and implement the json schema spec only, remove python class based parsing (#104)[https://github.com/OpenEnergyPlatform/omi/pull/104] +* Add a new conversion functionality to convert form v160 to v200 oemetadata [(#111)](https://github.com/rl-institut/super-repo/pull/111) 0.2.1 (2024-01-26) -------------------- From e70260b42925c86aa3dc85198a753bc461c88f1e Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 16:25:31 +0100 Subject: [PATCH 60/63] update version in badge --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 3577151..b1c43ff 100644 --- a/README.rst +++ b/README.rst @@ -37,9 +37,9 @@ Overview :alt: PyPI Package latest release :target: https://pypi.org/project/omi -.. |commits-since| image:: https://img.shields.io/github/commits-since/OpenEnergyPlatform/omi/v0.2.1.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/OpenEnergyPlatform/omi/v1.0.0.svg :alt: Commits since latest release - :target: https://github.com/OpenEnergyPlatform/omi/compare/v0.2.1...master + :target: https://github.com/OpenEnergyPlatform/omi/compare/v1.0.0...master .. |wheel| image:: https://img.shields.io/pypi/wheel/omi.svg :alt: PyPI Wheel From 8f597a7dc5c9f74510be368fbe4ae68b314f085c Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 16:25:54 +0100 Subject: [PATCH 61/63] remove deprecated documentation --- docs/authors.rst | 1 - docs/changelog.rst | 1 - docs/conf.py | 53 -------------------------------------- docs/contributing.rst | 1 - docs/index.rst | 22 ---------------- docs/installation.rst | 7 ----- docs/readme.rst | 1 - docs/reference/index.rst | 7 ----- docs/reference/omi.rst | 9 ------- docs/requirements.txt | 4 --- docs/spelling_wordlist.txt | 11 -------- docs/usage.rst | 13 ---------- 12 files changed, 130 deletions(-) delete mode 100644 docs/authors.rst delete mode 100644 docs/changelog.rst delete mode 100644 docs/conf.py delete mode 100644 docs/contributing.rst delete mode 100644 docs/index.rst delete mode 100644 docs/installation.rst delete mode 100644 docs/readme.rst delete mode 100644 docs/reference/index.rst delete mode 100644 docs/reference/omi.rst delete mode 100644 docs/requirements.txt delete mode 100644 docs/spelling_wordlist.txt delete mode 100644 docs/usage.rst diff --git a/docs/authors.rst b/docs/authors.rst deleted file mode 100644 index e122f91..0000000 --- a/docs/authors.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../AUTHORS.rst diff --git a/docs/changelog.rst b/docs/changelog.rst deleted file mode 100644 index 565b052..0000000 --- a/docs/changelog.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../CHANGELOG.rst diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 7100b12..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -import os - - -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.autosummary", - "sphinx.ext.coverage", - "sphinx.ext.doctest", - "sphinx.ext.extlinks", - "sphinx.ext.ifconfig", - "sphinx.ext.napoleon", - "sphinx.ext.todo", - "sphinx.ext.viewcode", - "sphinx_click.ext", -] - -if os.getenv("SPELLCHECK"): - extensions += ("sphinxcontrib.spelling",) - spelling_show_suggestions = True - spelling_lang = "en_US" - -source_suffix = ".rst" -master_doc = "index" -project = "omi" -year = "2019" -author = "Martin Glauer" -copyright = "{0}, {1}".format(year, author) -version = release = "0.2.1" - -pygments_style = "trac" -templates_path = ["."] -extlinks = { - "issue": ("https://github.com/OpenEnergyPlatform/omi/issues/%structure", "#"), - "pr": ("https://github.com/OpenEnergyPlatform/omi/pull/%structure", "PR #"), -} -# on_rtd is whether we are on readthedocs.org -on_rtd = os.environ.get("READTHEDOCS", None) == "True" - -if not on_rtd: # only set the theme if we"re building docs locally - html_theme = "sphinx_rtd_theme" - -html_use_smartypants = True -html_last_updated_fmt = "%b %d, %Y" -html_split_index = False -html_sidebars = {"**": ["searchbox.html", "globaltoc.html", "sourcelink.html"]} -html_short_title = "%s-%s" % (project, version) - -napoleon_use_ivar = True -napoleon_use_rtype = False -napoleon_use_param = False diff --git a/docs/contributing.rst b/docs/contributing.rst deleted file mode 100644 index e582053..0000000 --- a/docs/contributing.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../CONTRIBUTING.rst diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 40f35b5..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -======== -Contents -======== - -.. toctree:: - :maxdepth: 2 - - readme - installation - usage - reference/index - contributing - authors - changelog - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/docs/installation.rst b/docs/installation.rst deleted file mode 100644 index 3ddbdf9..0000000 --- a/docs/installation.rst +++ /dev/null @@ -1,7 +0,0 @@ -============ -Installation -============ - -At the command line:: - - pip install omi diff --git a/docs/readme.rst b/docs/readme.rst deleted file mode 100644 index 72a3355..0000000 --- a/docs/readme.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../README.rst diff --git a/docs/reference/index.rst b/docs/reference/index.rst deleted file mode 100644 index 7adef7f..0000000 --- a/docs/reference/index.rst +++ /dev/null @@ -1,7 +0,0 @@ -Reference -========= - -.. toctree:: - :glob: - - omi* diff --git a/docs/reference/omi.rst b/docs/reference/omi.rst deleted file mode 100644 index 2ce1117..0000000 --- a/docs/reference/omi.rst +++ /dev/null @@ -1,9 +0,0 @@ -omi -============= - -.. testsetup:: - - from omi import * - -.. automodule:: omi - :members: diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 2c15642..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -sphinx>=1.3 -sphinx-rtd-theme -sphinx-click --e . diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt deleted file mode 100644 index f95eb78..0000000 --- a/docs/spelling_wordlist.txt +++ /dev/null @@ -1,11 +0,0 @@ -builtin -builtins -classmethod -staticmethod -classmethods -staticmethods -args -kwargs -callstack -Changelog -Indices diff --git a/docs/usage.rst b/docs/usage.rst deleted file mode 100644 index 36242ec..0000000 --- a/docs/usage.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _cli: - -===== -Usage -===== - -.. click:: omi.cli:cli - :prog: omi - :show-nested: - -To use omi in a project:: - - import omi From 4bb1c03edaa913004ea5c59d93dc7ede61b76471 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 16:30:31 +0100 Subject: [PATCH 62/63] fix error in readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b1c43ff..841b453 100644 --- a/README.rst +++ b/README.rst @@ -190,7 +190,7 @@ Module usage:: with open("script/metadata/result_inspection.json", "w", encoding="utf-8") as json_file: json.dump(metadata, json_file, ensure_ascii=False, indent=4) # `indent=4` makes the JSON file easier to read -**Additional Fields ** +**Additional Fields** To be in line with the oemetadata specification we do not allow for additional properties or fields in the metadata. We want to keep the oemetadata relatively lean and readable still linking to other documents or to From 719508f1747df7c8370b3b156ac6647bddfd6619 Mon Sep 17 00:00:00 2001 From: Jonas Huber Date: Thu, 31 Oct 2024 17:03:01 +0100 Subject: [PATCH 63/63] remove docs from test --- tox.ini | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tox.ini b/tox.ini index c2957ca..4b495f3 100644 --- a/tox.ini +++ b/tox.ini @@ -55,14 +55,6 @@ deps = sphinxcontrib-spelling pyenchant -[testenv:docs] -deps = - -r{toxinidir}/docs/requirements.txt -commands = - sphinx-build {posargs:-E} -b doctest docs dist/docs - sphinx-build {posargs:-E} -b html docs dist/docs -; sphinx-build -b linkcheck docs dist/docs - [testenv:codecov] deps = codecov