diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 123014908..5e4251f20 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,5 @@ updates: directory: "/" schedule: interval: "daily" + cooldown: + default-days: 7 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b92694c15..2da3211e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,10 @@ name: CI on: [push, pull_request] jobs: - build: + ci: uses: open-contracting/.github/.github/workflows/ci-profile.yml@main + permissions: + contents: read secrets: private-key: ${{ secrets.PRIVATE_KEY }} elasticsearch-password: ${{ secrets.ELASTICSEARCH_PASSWORD }} diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 47d8e09f7..ab79551b1 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -1,13 +1,7 @@ name: Lint JavaScript on: [push, pull_request] jobs: - build: - if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 18 - - run: npm install standard - - run: npx standard docs/_static/script.js + js: + uses: open-contracting/.github/.github/workflows/js.yml@main + permissions: + contents: read diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 600ad9d5a..2b658c540 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,8 +4,4 @@ jobs: lint: uses: open-contracting/.github/.github/workflows/lint.yml@main permissions: - contents: write - secrets: - personal-access-token: ${{ secrets.PAT }} - with: - python-version: '3.10' + contents: read diff --git a/.github/workflows/shell.yml b/.github/workflows/shell.yml index dfdb0acfb..db657bed6 100644 --- a/.github/workflows/shell.yml +++ b/.github/workflows/shell.yml @@ -1,5 +1,7 @@ name: Lint Shell on: [push, pull_request] jobs: - lint: + shell: uses: open-contracting/.github/.github/workflows/shell.yml@main + permissions: + contents: read diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 5d16e5fe0..0e4f3cca8 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -1,13 +1,10 @@ name: Spell-check on: [push, pull_request] jobs: - build: - if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - run: pip install codespell - - run: codespell -S .git,docson,locale,examples,country.csv,currency.csv,language.csv,mediaType.csv -L fo,zar . + spellcheck: + uses: open-contracting/.github/.github/workflows/spellcheck.yml@main + permissions: + contents: read + with: + ignore: fo,sme,zar + skip: docson,locale,examples,country.csv,currency.csv,language.csv,mediaType.csv diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6286a0316..2f2e1cbdb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,20 @@ ci: autoupdate_schedule: quarterly skip: [pip-compile] -default_language_version: - python: python3.10 repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.6 + rev: v0.16.10 hooks: - - id: ruff + - id: ruff-check - id: ruff-format - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.5.14 + rev: 0.12.23 hooks: - id: pip-compile name: pip-compile common-requirements.in args: [common-requirements.in, -o, common-requirements.txt] files: ^common-requirements\.(in|txt)$ + - repo: https://github.com/biomejs/pre-commit + rev: v2.5.15 + hooks: + - id: biome-check diff --git a/.python-version b/.python-version index c8cfe3959..e4fba2183 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.10 +3.12 diff --git a/.tx/config b/.tx/config deleted file mode 100644 index da5197060..000000000 --- a/.tx/config +++ /dev/null @@ -1,351 +0,0 @@ -[main] -host = https://www.transifex.com - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:404] -file_filter = docs/locale//LC_MESSAGES/404.po -source_file = build/locale/404.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:codelists] -file_filter = docs/locale//LC_MESSAGES/codelists.po -source_file = build/locale/codelists.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:governance--deprecation] -file_filter = docs/locale//LC_MESSAGES/governance/deprecation.po -source_file = build/locale/governance/deprecation.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:governance--index] -file_filter = docs/locale//LC_MESSAGES/governance/index.po -source_file = build/locale/governance/index.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--build] -file_filter = docs/locale//LC_MESSAGES/guidance/build.po -source_file = build/locale/guidance/build.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--build--change_history] -file_filter = docs/locale//LC_MESSAGES/guidance/build/change_history.po -source_file = build/locale/guidance/build/change_history.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--build--data_collection_tools] -file_filter = docs/locale//LC_MESSAGES/guidance/build/data_collection_tools.po -source_file = build/locale/guidance/build/data_collection_tools.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--build--easy_releases] -file_filter = docs/locale//LC_MESSAGES/guidance/build/easy_releases.po -source_file = build/locale/guidance/build/easy_releases.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--build--hosting] -file_filter = docs/locale//LC_MESSAGES/guidance/build/hosting.po -source_file = build/locale/guidance/build/hosting.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--build--merging] -file_filter = docs/locale//LC_MESSAGES/guidance/build/merging.po -source_file = build/locale/guidance/build/merging.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--build--serialization] -file_filter = docs/locale//LC_MESSAGES/guidance/build/serialization.po -source_file = build/locale/guidance/build/serialization.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--build--system_architectures] -file_filter = docs/locale//LC_MESSAGES/guidance/build/system_architectures.po -source_file = build/locale/guidance/build/system_architectures.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--design] -file_filter = docs/locale//LC_MESSAGES/guidance/design.po -source_file = build/locale/guidance/design.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--design--user_needs] -file_filter = docs/locale//LC_MESSAGES/guidance/design/user_needs.po -source_file = build/locale/guidance/design/user_needs.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--index] -file_filter = docs/locale//LC_MESSAGES/guidance/index.po -source_file = build/locale/guidance/index.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map] -file_filter = docs/locale//LC_MESSAGES/guidance/map.po -source_file = build/locale/guidance/map.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--amendments] -file_filter = docs/locale//LC_MESSAGES/guidance/map/amendments.po -source_file = build/locale/guidance/map/amendments.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--awards_contracts] -file_filter = docs/locale//LC_MESSAGES/guidance/map/awards_contracts.po -source_file = build/locale/guidance/map/awards_contracts.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--beneficial_ownership] -file_filter = docs/locale//LC_MESSAGES/guidance/map/beneficial_ownership.po -source_file = build/locale/guidance/map/beneficial_ownership.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--buyers_suppliers] -file_filter = docs/locale//LC_MESSAGES/guidance/map/buyers_suppliers.po -source_file = build/locale/guidance/map/buyers_suppliers.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--electronic_catalogues] -file_filter = docs/locale//LC_MESSAGES/guidance/map/electronic_catalogues.po -source_file = build/locale/guidance/map/electronic_catalogues.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--extensions] -file_filter = docs/locale//LC_MESSAGES/guidance/map/extensions.po -source_file = build/locale/guidance/map/extensions.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--framework_agreements] -file_filter = docs/locale//LC_MESSAGES/guidance/map/framework_agreements.po -source_file = build/locale/guidance/map/framework_agreements.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--linked_standards] -file_filter = docs/locale//LC_MESSAGES/guidance/map/linked_standards.po -source_file = build/locale/guidance/map/linked_standards.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--localization] -file_filter = docs/locale//LC_MESSAGES/guidance/map/localization.po -source_file = build/locale/guidance/map/localization.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--milestones] -file_filter = docs/locale//LC_MESSAGES/guidance/map/milestones.po -source_file = build/locale/guidance/map/milestones.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--organization_classifications] -file_filter = docs/locale//LC_MESSAGES/guidance/map/organization_classifications.po -source_file = build/locale/guidance/map/organization_classifications.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--organization_identifiers] -file_filter = docs/locale//LC_MESSAGES/guidance/map/organization_identifiers.po -source_file = build/locale/guidance/map/organization_identifiers.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--organization_personal_identifiers] -file_filter = docs/locale//LC_MESSAGES/guidance/map/organization_personal_identifiers.po -source_file = build/locale/guidance/map/organization_personal_identifiers.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--organization_reference] -file_filter = docs/locale//LC_MESSAGES/guidance/map/organization_reference.po -source_file = build/locale/guidance/map/organization_reference.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--organizational_units] -file_filter = docs/locale//LC_MESSAGES/guidance/map/organizational_units.po -source_file = build/locale/guidance/map/organizational_units.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--pre-qualification] -file_filter = docs/locale//LC_MESSAGES/guidance/map/pre-qualification.po -source_file = build/locale/guidance/map/pre-qualification.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--map--unsuccessful_processes] -file_filter = docs/locale//LC_MESSAGES/guidance/map/unsuccessful_processes.po -source_file = build/locale/guidance/map/unsuccessful_processes.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--publish] -file_filter = docs/locale//LC_MESSAGES/guidance/publish.po -source_file = build/locale/guidance/publish.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:guidance--publish--quality] -file_filter = docs/locale//LC_MESSAGES/guidance/publish/quality.po -source_file = build/locale/guidance/publish/quality.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:history--changelog] -file_filter = docs/locale//LC_MESSAGES/history/changelog.po -source_file = build/locale/history/changelog.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:history--history_and_development] -file_filter = docs/locale//LC_MESSAGES/history/history_and_development.po -source_file = build/locale/history/history_and_development.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:history--index] -file_filter = docs/locale//LC_MESSAGES/history/index.po -source_file = build/locale/history/index.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:index] -file_filter = docs/locale//LC_MESSAGES/index.po -source_file = build/locale/index.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:primer--how] -file_filter = docs/locale//LC_MESSAGES/primer/how.po -source_file = build/locale/primer/how.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:primer--index] -file_filter = docs/locale//LC_MESSAGES/primer/index.po -source_file = build/locale/primer/index.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:primer--next] -file_filter = docs/locale//LC_MESSAGES/primer/next.po -source_file = build/locale/primer/next.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:primer--releases_and_records] -file_filter = docs/locale//LC_MESSAGES/primer/releases_and_records.po -source_file = build/locale/primer/releases_and_records.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:primer--what] -file_filter = docs/locale//LC_MESSAGES/primer/what.po -source_file = build/locale/primer/what.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema] -file_filter = docs/locale//LC_MESSAGES/schema.po -source_file = build/locale/schema.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema--codelists] -file_filter = docs/locale//LC_MESSAGES/schema/codelists.po -source_file = build/locale/schema/codelists.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema--conformance_and_extensions] -file_filter = docs/locale//LC_MESSAGES/schema/conformance_and_extensions.po -source_file = build/locale/schema/conformance_and_extensions.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema--identifiers] -file_filter = docs/locale//LC_MESSAGES/schema/identifiers.po -source_file = build/locale/schema/identifiers.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema--index] -file_filter = docs/locale//LC_MESSAGES/schema/index.po -source_file = build/locale/schema/index.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema--merging] -file_filter = docs/locale//LC_MESSAGES/schema/merging.po -source_file = build/locale/schema/merging.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema--record_package] -file_filter = docs/locale//LC_MESSAGES/schema/record_package.po -source_file = build/locale/schema/record_package.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema--records_reference] -file_filter = docs/locale//LC_MESSAGES/schema/records_reference.po -source_file = build/locale/schema/records_reference.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema--reference] -file_filter = docs/locale//LC_MESSAGES/schema/reference.po -source_file = build/locale/schema/reference.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema--release] -file_filter = docs/locale//LC_MESSAGES/schema/release.po -source_file = build/locale/schema/release.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:schema--release_package] -file_filter = docs/locale//LC_MESSAGES/schema/release_package.po -source_file = build/locale/schema/release_package.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:sphinx] -file_filter = docs/locale//LC_MESSAGES/sphinx.po -source_file = build/locale/sphinx.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:support--index] -file_filter = docs/locale//LC_MESSAGES/support/index.po -source_file = build/locale/support/index.pot -source_lang = en -type = PO - -[o:open-contracting-partnership-1:p:open-contracting-standard-1-1:r:theme] -file_filter = docs/locale//LC_MESSAGES/theme.po -source_file = build/locale/theme.pot -source_lang = en -type = PO - diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..4c0f77229 --- /dev/null +++ b/biome.json @@ -0,0 +1,16 @@ +{ + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true, + "defaultBranch": "main" + }, + "formatter": { + "indentStyle": "space", + "lineWidth": 119, + "includes": ["**", "!docs/examples/**/*.json", "!schema/**/*.json"] + }, + "files": { + "includes": ["**", "!docs/_static/docson", "!docs/_templates", "!docs/_static/renderjson.*"] + } +} diff --git a/common-requirements.in b/common-requirements.in index bc75f3234..2e26689f4 100644 --- a/common-requirements.in +++ b/common-requirements.in @@ -2,7 +2,8 @@ linkify-it-py myst-parser ocds-babel Sphinx -git+https://github.com/open-contracting/standard_theme.git@open_contracting#egg=standard_theme +sphinxcontrib-opendataservices +sphinxcontrib-opendataservices-jsonschema # Profile ocdsextensionregistry diff --git a/common-requirements.txt b/common-requirements.txt index be75c1832..1ed5edfd6 100644 --- a/common-requirements.txt +++ b/common-requirements.txt @@ -4,26 +4,25 @@ alabaster==0.7.12 # via sphinx anyio==4.4.0 # via + # elasticsearch # starlette # watchfiles async-generator==1.10 - # via - # trio - # trio-websocket -attrs==23.1.0 + # via trio-typing +attrs==25.4.0 # via # cattrs # outcome - # pytest + # referencing # requests-cache # trio -babel==2.9.1 +babel==2.18.0 # via # sphinx # sphinx-intl cattrs==23.1.2 # via requests-cache -certifi==2024.7.4 +certifi==2026.1.4 # via # elastic-transport # requests @@ -42,39 +41,49 @@ docutils==0.18 # via # myst-parser # sphinx -elastic-transport==8.4.0 + # sphinxcontrib-opendataservices + # sphinxcontrib-opendataservices-jsonschema +elastic-transport==9.2.1 # via elasticsearch -elasticsearch==8.6.2 +elasticsearch[requests]==9.3.0 # via ocdsindex -exceptiongroup==1.2.2 - # via - # anyio - # cattrs - # pytest -h11==0.13.0 +h11==0.16.0 # via # uvicorn # wsproto -idna==3.7 +idna==3.15 # via # anyio # requests # trio imagesize==1.4.1 # via sphinx +importlib-metadata==8.7.1 + # via trio-typing iniconfig==1.1.1 # via pytest -jinja2==3.1.5 +jinja2==3.1.6 # via # myst-parser # sphinx +jscc==0.5.0 + # via sphinxcontrib-opendataservices-jsonschema json-merge-patch==0.2 - # via ocdsextensionregistry + # via + # jscc + # ocdsextensionregistry +jsonpointer==3.1.1 + # via + # sphinxcontrib-opendataservices + # sphinxcontrib-opendataservices-jsonschema jsonref==1.0.0.post1 - # via ocdsextensionregistry + # via + # jscc + # ocdsextensionregistry + # sphinxcontrib-opendataservices-jsonschema linkify-it-py==1.0.1 # via -r common-requirements.in -lxml==4.9.1 +lxml==6.1.0 # via ocdsindex markdown-it-py==2.2.0 # via @@ -86,51 +95,68 @@ mdit-py-plugins==0.3.1 # via myst-parser mdurl==0.1.2 # via markdown-it-py +mypy-extensions==1.1.0 + # via trio-typing myst-parser==0.18.1 - # via -r common-requirements.in -ocds-babel==0.3.6 + # via + # -r common-requirements.in + # sphinxcontrib-opendataservices + # sphinxcontrib-opendataservices-jsonschema +ocds-babel==0.3.7 # via -r common-requirements.in ocdsextensionregistry==0.6.5 # via -r common-requirements.in -ocdsindex==0.2.0 +ocdsindex==0.4.1 # via -r common-requirements.in -outcome==1.1.0 - # via trio +outcome==1.3.0.post0 + # via + # trio + # trio-websocket packaging==24.1 # via # pytest # sphinx + # trio-typing platformdirs==3.9.1 # via requests-cache -pluggy==0.13.1 +pluggy==1.6.0 # via pytest -pygments==2.15.1 - # via sphinx +pygments==2.20.0 + # via + # pytest + # sphinx pysocks==1.7.1 # via urllib3 -pytest==7.2.0 +pytest==9.0.3 # via -r common-requirements.in -pytz==2021.1 - # via babel +python-dateutil==2.9.0.post0 + # via elasticsearch pyyaml==6.0.1 # via myst-parser -requests==2.32.2 +referencing==0.37.0 + # via sphinxcontrib-opendataservices-jsonschema +requests==2.33.0 # via # elasticsearch + # jscc # ocdsextensionregistry # requests-cache # sphinx requests-cache==1.1.0 # via ocdsextensionregistry -selenium==4.11.2 +rpds-py==2026.6.3 + # via referencing +selenium==4.40.0 # via -r common-requirements.in -setuptools==75.2.0 - # via sphinx-intl six==1.16.0 - # via url-normalize -sniffio==1.2.0 + # via + # python-dateutil + # url-normalize +sniffio==1.3.1 # via # anyio + # elastic-transport + # elasticsearch # trio snowballstemmer==2.1.0 # via sphinx @@ -142,6 +168,7 @@ sphinx==5.3.0 # myst-parser # sphinx-autobuild # sphinx-intl + # sphinxcontrib-opendataservices sphinx-autobuild==2024.9.3 # via -r common-requirements.in sphinx-intl==2.2.0 @@ -154,33 +181,42 @@ sphinxcontrib-htmlhelp==2.0.0 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx +sphinxcontrib-opendataservices==0.6.0 + # via -r common-requirements.in +sphinxcontrib-opendataservices-jsonschema==0.7.1 + # via + # -r common-requirements.in + # sphinxcontrib-opendataservices sphinxcontrib-qthelp==1.0.3 # via sphinx sphinxcontrib-serializinghtml==1.1.5 # via sphinx -standard-theme @ git+https://github.com/open-contracting/standard_theme.git@5de343d1d8e342b5f2a42c6132db37aebe382e36#egg=standard_theme - # via -r common-requirements.in -starlette==0.40.0 +starlette==1.3.1 # via sphinx-autobuild -tomli==2.0.1 - # via pytest -trio==0.20.0 +trio==0.32.0 # via # selenium + # trio-typing # trio-websocket -trio-websocket==0.9.2 +trio-typing==0.10.0 + # via selenium +trio-websocket==0.12.2 # via selenium -typing-extensions==4.4.0 +types-certifi==2021.10.8.3 + # via selenium +types-urllib3==1.26.25.14 + # via selenium +typing-extensions==4.15.0 # via - # anyio - # cattrs + # elasticsearch # myst-parser - # uvicorn + # selenium + # trio-typing uc-micro-py==1.0.1 # via linkify-it-py url-normalize==1.4.3 # via requests-cache -urllib3==1.26.19 +urllib3[socks]==2.7.0 # via # elastic-transport # requests @@ -188,9 +224,13 @@ urllib3==1.26.19 # selenium uvicorn==0.30.6 # via sphinx-autobuild -watchfiles==0.24.0 +watchfiles==1.1.1 # via sphinx-autobuild +websocket-client==1.9.0 + # via selenium websockets==13.0.1 # via sphinx-autobuild wsproto==1.1.0 # via trio-websocket +zipp==3.23.0 + # via importlib-metadata diff --git a/docs/_static/basic.css b/docs/_static/custom.css similarity index 59% rename from docs/_static/basic.css rename to docs/_static/custom.css index 355bdc65a..a5153f1f2 100644 --- a/docs/_static/basic.css +++ b/docs/_static/custom.css @@ -2,7 +2,8 @@ margin: 0; padding: 0; } -.wy-side-nav-search > a, .wy-side-nav-search .wy-dropdown > a { +.wy-side-nav-search > a, +.wy-side-nav-search .wy-dropdown > a { margin: 0; padding: 0 6px; } @@ -21,8 +22,8 @@ } img[alt="org-id.guide"] { - width: 200px; - float: right; + width: 200px; + float: right; } .process-table { @@ -65,9 +66,9 @@ img[alt="org-id.guide"] { /* Media */ @media only print { - .wy-table-responsive { - overflow: visible !important; - } + .wy-table-responsive { + overflow: visible; + } } /* sphinx-design */ @@ -77,19 +78,44 @@ img[alt="org-id.guide"] { } .rst-content img.sd-card-img-top { width: auto; - height: 3rem !important; + height: 3rem; margin: 1rem auto 0; } /* renderjson.js */ -pre.renderjson { overflow: scroll; font-size:smaller; border: 1px solid grey;} -.renderjson a { text-decoration: none; } -.renderjson .disclosure { color: crimson; font-size: 150%; } -.renderjson .syntax { color: grey; } -.renderjson .string { color: darkred; } -.renderjson .number { color: darkcyan; } -.renderjson .boolean { color: blueviolet; } -.renderjson .key { color: darkblue; } -.renderjson .keyword { color: blue; } -.renderjson .object.syntax { color: lightseagreen; } -.renderjson .array.syntax { color: orange; } +pre.renderjson { + overflow: scroll; + font-size: smaller; + border: 1px solid grey; +} +.renderjson a { + text-decoration: none; +} +.renderjson .disclosure { + color: crimson; + font-size: 150%; +} +.renderjson .syntax { + color: grey; +} +.renderjson .string { + color: darkred; +} +.renderjson .number { + color: darkcyan; +} +.renderjson .boolean { + color: blueviolet; +} +.renderjson .key { + color: darkblue; +} +.renderjson .keyword { + color: blue; +} +.renderjson .object.syntax { + color: lightseagreen; +} +.renderjson .array.syntax { + color: orange; +} diff --git a/docs/_static/favicon-16x16.ico b/docs/_static/favicon-16x16.ico deleted file mode 100644 index 40159444d..000000000 Binary files a/docs/_static/favicon-16x16.ico and /dev/null differ diff --git a/docs/_static/script.js b/docs/_static/script.js index e46cd638a..ffd653486 100644 --- a/docs/_static/script.js +++ b/docs/_static/script.js @@ -1,35 +1,40 @@ /* global XMLHttpRequest, location, renderjson */ -const pathComponents = window.location.pathname.split('/') -if (pathComponents[pathComponents.length - 2] === 'codelists') { - document.querySelector('.wy-nav-content').style.maxWidth = 'none' +const pathComponents = window.location.pathname.split("/"); +if (pathComponents[pathComponents.length - 2] === "codelists") { + document.querySelector(".wy-nav-content").style.maxWidth = "none"; } // FYI: extensionlist appears many times on /schema/reference/ -if (document.querySelector('.extension_list')) { - const language = location.pathname.split('/')[2] +if (document.querySelector(".extension_list")) { + const language = location.pathname.split("/")[2]; // Append an empty list for community extensions. - document.querySelectorAll('.extension_list .hide').forEach(element => { - const dl = document.createElement('dl') - dl.className = 'simple community-list hide' - element.insertAdjacentElement('afterend', dl) - element.innerHTML = `${element.innerHTML}` - }) + for (const element of document.querySelectorAll(".extension_list .hide")) { + const dl = document.createElement("dl"); + dl.className = "simple community-list hide"; + element.insertAdjacentElement("afterend", dl); + element.innerHTML = `${element.innerHTML}`; + } // Get the community extensions to add to the documentation. - const request = new XMLHttpRequest() - request.open('GET', 'https://raw.githubusercontent.com/open-contracting/extension_registry/main/build/extensions.json') - request.responseType = 'json' + const request = new XMLHttpRequest(); + request.open( + "GET", + "https://raw.githubusercontent.com/open-contracting/extension_registry/main/build/extensions.json", + ); + request.responseType = "json"; request.onload = () => { if (request.status >= 200 && request.status < 400) { // Add community extensions. - request.response.extensions.forEach(extension => { + for (const extension of request.response.extensions) { if (!extension.core) { - const div = document.getElementById(`extensionlist-${extension.category}`) + const div = document.getElementById(`extensionlist-${extension.category}`); if (div) { - div.querySelector('.community-list').insertAdjacentHTML('beforeend', ` + div.querySelector(".community-list").insertAdjacentHTML( + "beforeend", + `
${extension.name[language] || extension.name.en} @@ -38,79 +43,80 @@ if (document.querySelector('.extension_list')) {
${extension.description[language] || extension.description.en}
- `) - div.querySelectorAll('.hide').forEach(element => { - element.classList.remove('hide') - }) + `, + ); + for (const element of div.querySelectorAll(".hide")) { + element.classList.remove("hide"); + } } } - }) + } // Remove empty extension lists. - document.querySelectorAll('.extension_list').forEach(element => { - if (!element.querySelector('a')) { - element.remove() + for (const element of document.querySelectorAll(".extension_list")) { + if (!element.querySelector("a")) { + element.remove(); } - }) + } } - } + }; - request.send() + request.send(); } -document.querySelectorAll('.expandjson').forEach(element => { - const defaultOpen = [] - let fileClassName - element.classList.forEach(className => { - if (className.substring(0, 7) === 'expand-' && className.length > 7) { - defaultOpen.push(className.substring(7)) +for (const element of document.querySelectorAll(".expandjson")) { + const defaultOpen = []; + let fileClassName; + for (const className of element.classList) { + if (className.substring(0, 7) === "expand-" && className.length > 7) { + defaultOpen.push(className.substring(7)); } - if (className.substring(0, 5) === 'file-') { - fileClassName = className + if (className.substring(0, 5) === "file-") { + fileClassName = className; } - }) + } - let data = JSON.parse(element.textContent) + let data = JSON.parse(element.textContent); // If the jsoninclude directive indexed to a JSON array (a common mistake), only display the first entry. if (data.length) { - data = data[0] + data = data[0]; } - const replacement = renderjson.set_show_to_level(1).set_max_string_length(100).set_default_open(defaultOpen)(data) + const replacement = renderjson.set_show_to_level(1).set_max_string_length(100).set_default_open(defaultOpen)(data); // element.firstElementChild.replaceWith(replacement) // https://caniuse.com/mdn-api_parentnode_firstelementchild - element.replaceChild(replacement, element.querySelector('.highlight-json')) + element.replaceChild(replacement, element.querySelector(".highlight-json")); - const container = element.previousElementSibling - let select - if (container && container.classList.contains('selection-container')) { + const container = element.previousElementSibling; + let select; + if (container?.classList.contains("selection-container")) { // Hide additional examples. - element.style.display = 'none' - container.appendChild(element) + element.style.display = "none"; + container.appendChild(element); // Display the select element if there are multiple options. - select = container.querySelector('select') - select.style.display = '' + select = container.querySelector("select"); + select.style.display = ""; } else { - const div = document.createElement('div') - element.insertAdjacentElement('beforebegin', div) - div.className = 'selection-container' - div.appendChild(element) + const div = document.createElement("div"); + element.insertAdjacentElement("beforebegin", div); + div.className = "selection-container"; + div.appendChild(element); - select = document.createElement('select') - div.insertAdjacentElement('afterbegin', select) + select = document.createElement("select"); + div.insertAdjacentElement("afterbegin", select); // Hide the select element if there is one option. - select.style.display = 'none' - select.addEventListener('change', () => { - div.querySelectorAll('.expandjson').forEach(child => { - child.style.display = 'none' - }) - div.querySelector(`.${select.value}`).style.display = '' - }) + select.style.display = "none"; + select.addEventListener("change", () => { + for (const child of div.querySelectorAll(".expandjson")) { + child.style.display = "none"; + } + div.querySelector(`.${select.value}`).style.display = ""; + }); } - const option = document.createElement('option') - option.value = fileClassName - option.textContent = fileClassName.replace('file-', '') - select.appendChild(option) -}) + const option = document.createElement("option"); + option.value = fileClassName; + option.textContent = fileClassName.replace("file-", ""); + select.appendChild(option); +} diff --git a/docs/_templates/footer.html b/docs/_templates/footer.html deleted file mode 100644 index 91589f404..000000000 --- a/docs/_templates/footer.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "!footer.html" %} - -{% block extrafooter %} -{% trans %}Privacy notice{% endtrans %}. -{% endblock %} diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html index c68368339..07bded6c7 100644 --- a/docs/_templates/layout.html +++ b/docs/_templates/layout.html @@ -1,5 +1,3 @@ -{% set css_files = css_files + ["_static/basic.css"] %} -{% set script_files = script_files + ["_static/renderjson.js", "_static/script.js"] %} {% extends "!layout.html" %} {% block mobile_nav %} @@ -22,16 +20,3 @@
  • {% trans %}Data Registry{% endtrans %}
  • {% endblock %} - -{% block language_options %} - - - - - - -{% endblock %} - -{% block version_options %} - -{% endblock %} diff --git a/docs/conf.py b/docs/conf.py index 09daf8877..ce755efa9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,31 +1,21 @@ # Configuration file for the Sphinx documentation builder. # -# This file only contains a selection of the most common options. For a full -# list see the documentation: +# For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) import csv import json import os from glob import glob from pathlib import Path -import standard_theme from docutils.nodes import make_id from ocds_babel.translate import translate from ocdskit.mapping_sheet import mapping_sheet from sphinx.locale import get_translation # -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "Open Contracting Data Standard" copyright = "Open Contracting Partnership" @@ -34,12 +24,9 @@ version = "1.1" release = "1.1.5" - # -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = [ "myst_parser", "sphinx.ext.ifconfig", @@ -49,28 +36,16 @@ "sphinx_design", ] -# Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "**/docson/[!p]**", "**/docson/package*.json"] - # -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# html_theme = "standard_theme" # 'pydata_sphinx_theme' -html_theme_path = [standard_theme.get_html_theme_path()] -html_favicon = "_static/favicon-16x16.ico" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] +html_css_files = ["custom.css"] +html_js_files = ["renderjson.js", "script.js"] # -- Local configuration ----------------------------------------------------- @@ -84,7 +59,7 @@ gettext_compact = False # `DOMAIN_PREFIX` from `config.mk`. gettext_domain_prefix = f"{profile_identifier}-" if profile_identifier else "" -locale_dirs = ["locale/", os.path.join(standard_theme.get_html_theme_path(), "locale")] +locale_dirs = ["locale/"] # We use single quotes for codes, which docutils will change to double quotes. # https://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/docutils/utils/smartquotes.py smartquotes = False @@ -102,15 +77,13 @@ "analytics_id": "HTWZHRIZ", } html_theme_options = { - "analytics_id": "HTWZHRIZ", "display_version": True, "root_url": f"/profiles/{profile_identifier}" if profile_identifier else "", + "languages": {"en": "English", "es": "Español", "fr": "Français"}, "short_project": project.replace("Open Contracting Data Standard", "OCDS"), - "copyright": copyright, - "license_name": "Apache License 2.0", - "license_url": f"{repository_url}/blob/HEAD/LICENSE", "repository_url": repository_url, } +html_short_title = f"{html_theme_options['short_project']} v{release}" # List the extension identifiers and versions that should be part of this specification. The extensions must be in # the extension registry: https://github.com/open-contracting/extension_registry/blob/main/extension_versions.csv diff --git a/docs/guidance/build.md b/docs/guidance/build.md index 10e78aaea..4eec6da63 100644 --- a/docs/guidance/build.md +++ b/docs/guidance/build.md @@ -115,7 +115,7 @@ Contact the [Data Support Team](../support/index) for guidance on customizing a **Resource:** To learn about how to create a spreadsheet input template for OCDS, check out our blog series on prototyping OCDS data using spreadsheets ([Part 1](https://www.open-contracting.org/2020/04/24/prototyping-ocds-data-using-spreadsheets/), [Part 2](https://www.open-contracting.org/2020/05/11/prototyping-ocds-data-using-spreadsheets-part-ii/), [Part 3](https://www.open-contracting.org/2020/05/28/prototyping-ocds-data-using-spreadsheets-part-iii/)). ```{note} -Re-using tools isn't always easy. [Tool Reuse in Open Contracting: A Primer](https://www.open-contracting.org/resources/tool-re-use-in-open-contracting-a-primer/) is a step-by-step guide to help you determine what you need, evaluate which tool is the right fit, and evaluate whether the right conditions are in place for successful reuse of a tool. +Reusing tools isn't always easy. [Tool Reuse in Open Contracting: A Primer](https://www.open-contracting.org/resources/tool-re-use-in-open-contracting-a-primer/) is a step-by-step guide to help you determine what you need, evaluate which tool is the right fit, and evaluate whether the right conditions are in place for successful reuse of a tool. ``` ## Build your extensions diff --git a/docs/guidance/index.md b/docs/guidance/index.md index 10bfe575a..d2692297b 100644 --- a/docs/guidance/index.md +++ b/docs/guidance/index.md @@ -6,7 +6,7 @@ The four phases of implementation described in this guide have helped implemente Read the guidance to understand the main steps to implement OCDS and to find supporting resources. Detailed guidance on specific topics and tasks is provided in sub-pages. -Use the [OCDS Implementation Checklist](https://www.open-contracting.org/resources/ocds-implementation-checklist) to keep track of your progress. +Use the [OCDS Implementation Checklist](https://www.open-contracting.org/resources/ocds-implementation-checklist/) to keep track of your progress. ```{toctree} :maxdepth: 2 diff --git a/docs/guidance/map/organization_classifications.md b/docs/guidance/map/organization_classifications.md index 1726bcf4c..7477eb11d 100644 --- a/docs/guidance/map/organization_classifications.md +++ b/docs/guidance/map/organization_classifications.md @@ -29,7 +29,7 @@ In the example below, Moldova has disclosed information about the 'Companie mic ### Option 2: Organization classification extension -In the examples below, two different publishers have disclosed information about organizations involved in their contracting processes. An organization classification needs to consist of at least two parts: an identifier for the list (scheme) from which the classification is taken, and an identifier for the category from that list being applied. It is useful to also publish a text label and/or URI that users can draw on to interpret the classification. In the first example below, the publisher re-uses an existing `classification.scheme`. In the second example below, where a publisher wishes to track specific policy-related data, a local list of categories is used in preference to mapping to a generic set. +In the examples below, two different publishers have disclosed information about organizations involved in their contracting processes. An organization classification needs to consist of at least two parts: an identifier for the list (scheme) from which the classification is taken, and an identifier for the category from that list being applied. It is useful to also publish a text label and/or URI that users can draw on to interpret the classification. In the first example below, the publisher reuses an existing `classification.scheme`. In the second example below, where a publisher wishes to track specific policy-related data, a local list of categories is used in preference to mapping to a generic set. #### Classification schemes diff --git a/docs/guidance/publish.md b/docs/guidance/publish.md index 1624665d8..b6f949079 100644 --- a/docs/guidance/publish.md +++ b/docs/guidance/publish.md @@ -35,7 +35,7 @@ The publication policy ought to also contain information about future plans for Publishing data under an open license is an important part of open contracting. Without this, restrictions on reuse can prevent many of the important [use cases](design/user_needs) for open contracting information being realized. -A license statement sets out the permission that users have to access, use and reuse the data. This can take the form of a [Public Domain Dedication or Certification](https://creativecommons.org/publicdomain/) which transfers a dataset into the public domain, or re-asserts that there are no existing copyrights or database rights inherent in the dataset (which is the case for government datasets in some jurisdictions), or the application of a license which sets out the terms under which a dataset can be re-used. +A license statement sets out the permission that users have to access, use and reuse the data. This can take the form of a [Public Domain Dedication or Certification](https://creativecommons.org/publicdomain/) which transfers a dataset into the public domain, or re-asserts that there are no existing copyrights or database rights inherent in the dataset (which is the case for government datasets in some jurisdictions), or the application of a license which sets out the terms under which a dataset can be reused. We encourage the use of either a public domain dedication/certification, or an attribution only license. diff --git a/docs/history/changelog.md b/docs/history/changelog.md index de29b17fd..186e72a16 100644 --- a/docs/history/changelog.md +++ b/docs/history/changelog.md @@ -109,7 +109,7 @@ See the changelogs for: * [Enquiries](https://extensions.open-contracting.org/en/extensions/enquiries/master/#changelog) * [Location](https://extensions.open-contracting.org/en/extensions/location/master/#changelog) * [Lots](https://extensions.open-contracting.org/en/extensions/lots/master/#changelog) -* [Milestone documents](https://extensions.open-contracting.org/en/extensions/milestone_documents/master/#changelog) +* [Milestone documents](https://github.com/open-contracting-extensions/ocds_milestone_documents_extension#changelog) * [Participation fees](https://extensions.open-contracting.org/en/extensions/participation_fee/master/#changelog) * [Process level title and description](https://extensions.open-contracting.org/en/extensions/process_title/master/#changelog) diff --git a/docs/locale/es/LC_MESSAGES/guidance/build.po b/docs/locale/es/LC_MESSAGES/guidance/build.po index a684a1fe4..64d20f232 100644 --- a/docs/locale/es/LC_MESSAGES/guidance/build.po +++ b/docs/locale/es/LC_MESSAGES/guidance/build.po @@ -548,11 +548,11 @@ msgstr "" #: ../../docs/guidance/build.md:118 msgid "" -"Re-using tools isn't always easy. [Tool Re-Use in Open Contracting: A " +"Reusing tools isn't always easy. [Tool Re-Use in Open Contracting: A " "Primer](https://www.open-contracting.org/resources/tool-re-use-in-open-" "contracting-a-primer/) is a step-by-step guide to help you determine what " "you need, evaluate which tool is the right fit, and evaluate whether the " -"right conditions are in place for successful re-use of a tool." +"right conditions are in place for successful reuse of a tool." msgstr "" "Reutilizar herramientas no siempre es fácil. [Reutilización de herramientas " "en contrataciones abiertas: Una introducción](https://www.open-" diff --git a/docs/locale/es/LC_MESSAGES/guidance/index.po b/docs/locale/es/LC_MESSAGES/guidance/index.po index 457625b13..06d357115 100644 --- a/docs/locale/es/LC_MESSAGES/guidance/index.po +++ b/docs/locale/es/LC_MESSAGES/guidance/index.po @@ -55,11 +55,11 @@ msgstr "" #: ../../docs/guidance/index.md:9 msgid "" "Use the [OCDS Implementation Checklist](https://www.open-" -"contracting.org/resources/ocds-implementation-checklist) to keep track of " +"contracting.org/resources/ocds-implementation-checklist/) to keep track of " "your progress." msgstr "" "Utilice la [Lista de Verificación de Implementación](https://www.open-" -"contracting.org/resources/ocds-implementation-checklist) para hacer un " +"contracting.org/resources/ocds-implementation-checklist/) para hacer un " "seguimiento de su progreso." #: ../../docs/guidance/index.md:20 diff --git a/docs/locale/es/LC_MESSAGES/guidance/map/organization_classifications.po b/docs/locale/es/LC_MESSAGES/guidance/map/organization_classifications.po index 4e04a4789..3975fe8f1 100644 --- a/docs/locale/es/LC_MESSAGES/guidance/map/organization_classifications.po +++ b/docs/locale/es/LC_MESSAGES/guidance/map/organization_classifications.po @@ -192,7 +192,7 @@ msgid "" "the list (scheme) from which the classification is taken, and an identifier " "for the category from that list being applied. It is useful to also publish " "a text label and/or URI that users can draw on to interpret the " -"classification. In the first example below, the publisher re-uses an " +"classification. In the first example below, the publisher reuses an " "existing `classification.scheme`. In the second example below, where a " "publisher wishes to track specific policy-related data, a local list of " "categories is used in preference to mapping to a generic set." @@ -238,7 +238,7 @@ msgstr "" msgid "" "Where an appropriate scheme is not listed in the [itemClassificationScheme " "codelist](../../schema/codelists.md#item-classification-scheme), publishers " -"can specify their own scheme. Publishers can either re-use an alternative " +"can specify their own scheme. Publishers can either reuse an alternative " "scheme, or provide their own. Where publishers provide their own local " "schemes, they ought to prefix their `scheme` code with an [ISO-3166-1 " "alpha-3 country code](https://en.wikipedia.org/wiki/ISO_3166-1) to preserve " diff --git a/docs/locale/es/LC_MESSAGES/guidance/map/pre-qualification.po b/docs/locale/es/LC_MESSAGES/guidance/map/pre-qualification.po index 62badd467..f15ac02d7 100644 --- a/docs/locale/es/LC_MESSAGES/guidance/map/pre-qualification.po +++ b/docs/locale/es/LC_MESSAGES/guidance/map/pre-qualification.po @@ -443,7 +443,7 @@ msgid "" "suppliers to pre-qualify for two tenders for road construction in different " "neighbourhoods](https://contrataciones.gov.py/licitaciones/convocatoria/338229-servicios-" "consultoria-estudios-factibilidad-diseno-final-ingenieria-tramos-" -"caminos-1/precalificacion.html). Each tender will re-use the list of pre-" +"caminos-1/precalificacion.html). Each tender will reuse the list of pre-" "qualified suppliers established as a result of this first procedure." msgstr "" "El Ministerio de Obras Públicas y Comunicaciones emite una [invitación a " diff --git a/docs/locale/es/LC_MESSAGES/guidance/publish.po b/docs/locale/es/LC_MESSAGES/guidance/publish.po index f62b667f1..687fc04df 100644 --- a/docs/locale/es/LC_MESSAGES/guidance/publish.po +++ b/docs/locale/es/LC_MESSAGES/guidance/publish.po @@ -136,7 +136,7 @@ msgstr "Licencia tus datos" #: ../../docs/guidance/publish.md:36 msgid "" "Publishing data under an open license is an important part of open " -"contracting. Without this, restrictions on re-use can prevent many of the " +"contracting. Without this, restrictions on reuse can prevent many of the " "important [use cases](design/user_needs) for open contracting information " "being realized." msgstr "" @@ -149,12 +149,12 @@ msgstr "" #: ../../docs/guidance/publish.md:38 msgid "" "A license statement sets out the permission that users have to access, use " -"and re-use the data. This can take the form of a [Public Domain Dedication " +"and reuse the data. This can take the form of a [Public Domain Dedication " "or Certification](https://creativecommons.org/publicdomain/) which transfers" " a dataset into the public domain, or re-asserts that there are no existing " "copyrights or database rights inherent in the dataset (which is the case for" " government datasets in some jurisdictions), or the application of a license" -" which sets out the terms under which a dataset can be re-used." +" which sets out the terms under which a dataset can be reused." msgstr "" "Una declaración de licencia establece el permiso que los usuarios tienen " "para acceder, utilizar y volver a utilizar los datos. Esto puede tomar la " diff --git a/docs/locale/es/LC_MESSAGES/history/changelog.po b/docs/locale/es/LC_MESSAGES/history/changelog.po index 30c2aa5a8..8aa20aada 100644 --- a/docs/locale/es/LC_MESSAGES/history/changelog.po +++ b/docs/locale/es/LC_MESSAGES/history/changelog.po @@ -792,11 +792,9 @@ msgstr "" #: ../../docs/history/changelog.md:111 msgid "" -"[Milestone documents](https://extensions.open-" -"contracting.org/en/extensions/milestone_documents/master/#changelog)" +"[Milestone documents](https://github.com/open-contracting-extensions/ocds_milestone_documents_extension#changelog)" msgstr "" -"[Documentos de hito](https://extensions.open-" -"contracting.org/en/extensions/milestone_documents/master/#changelog)" +"[Documentos de hito](https://github.com/open-contracting-extensions/ocds_milestone_documents_extension#changelog)" #: ../../docs/history/changelog.md:112 ../../docs/history/changelog.md:190 msgid "" diff --git a/docs/locale/es/LC_MESSAGES/schema/identifiers.po b/docs/locale/es/LC_MESSAGES/schema/identifiers.po index 0470f8ee7..e5019a425 100644 --- a/docs/locale/es/LC_MESSAGES/schema/identifiers.po +++ b/docs/locale/es/LC_MESSAGES/schema/identifiers.po @@ -694,7 +694,7 @@ msgstr "" #: ../../docs/schema/identifiers.md:168 msgid "" -"The same `id` value may be re-used in another array of items within the same" +"The same `id` value may be reused in another array of items within the same" " release, and no cross-reference between these identifiers is implied." msgstr "" "El mismo valor `id` puede reutilizarse en otra lista de artículos en la " diff --git a/docs/locale/es/LC_MESSAGES/schema/reference.po b/docs/locale/es/LC_MESSAGES/schema/reference.po index 2dc459861..266110034 100644 --- a/docs/locale/es/LC_MESSAGES/schema/reference.po +++ b/docs/locale/es/LC_MESSAGES/schema/reference.po @@ -846,7 +846,7 @@ msgstr "Referencia de los bloques" #: ../../docs/schema/reference.md:273 msgid "" -"The following building blocks are commonly re-used throughout the standard." +"The following building blocks are commonly reused throughout the standard." msgstr "" "Los siguientes bloques de se reutilizan comúnmente en todo el estándar." diff --git a/docs/locale/fr/LC_MESSAGES/guidance/build.po b/docs/locale/fr/LC_MESSAGES/guidance/build.po index cbe3ea9e1..c2c16235f 100644 --- a/docs/locale/fr/LC_MESSAGES/guidance/build.po +++ b/docs/locale/fr/LC_MESSAGES/guidance/build.po @@ -366,11 +366,11 @@ msgstr "" #: ../../docs/guidance/build.md:3 msgid "" -"Re-using tools isn't always easy. [Tool Re-Use in Open Contracting: A " +"Reusing tools isn't always easy. [Tool Re-Use in Open Contracting: A " "Primer](https://www.open-contracting.org/resources/tool-re-use-in-open-" "contracting-a-primer/) is a step-by-step guide to help you determine what " "you need, evaluate which tool is the right fit, and evaluate whether the " -"right conditions are in place for successful re-use of a tool." +"right conditions are in place for successful reuse of a tool." msgstr "" #: ../../docs/guidance/build.md:5 diff --git a/docs/locale/fr/LC_MESSAGES/guidance/index.po b/docs/locale/fr/LC_MESSAGES/guidance/index.po index 736c5359a..21f566677 100644 --- a/docs/locale/fr/LC_MESSAGES/guidance/index.po +++ b/docs/locale/fr/LC_MESSAGES/guidance/index.po @@ -46,7 +46,7 @@ msgstr "" #: ../../docs/guidance/index.md:9 msgid "" "Use the [OCDS Implementation Checklist](https://www.open-" -"contracting.org/resources/ocds-implementation-checklist) to keep track of " +"contracting.org/resources/ocds-implementation-checklist/) to keep track of " "your progress." msgstr "" diff --git a/docs/locale/fr/LC_MESSAGES/guidance/map/organization_classifications.po b/docs/locale/fr/LC_MESSAGES/guidance/map/organization_classifications.po index 45e298081..6daf34683 100644 --- a/docs/locale/fr/LC_MESSAGES/guidance/map/organization_classifications.po +++ b/docs/locale/fr/LC_MESSAGES/guidance/map/organization_classifications.po @@ -129,7 +129,7 @@ msgid "" "the list (scheme) from which the classification is taken, and an identifier " "for the category from that list being applied. It is useful to also publish " "a text label and/or URI that users can draw on to interpret the " -"classification. In the first example below, the publisher re-uses an " +"classification. In the first example below, the publisher reuses an " "existing `classification.scheme`. In the second example below, where a " "publisher wishes to track specific policy-related data, a local list of " "categories is used in preference to mapping to a generic set." @@ -155,7 +155,7 @@ msgstr "" msgid "" "Where an appropriate scheme is not listed in the [itemClassificationScheme " "codelist](../../schema/codelists.md#item-classification-scheme), publishers " -"can specify their own scheme. Publishers can either re-use an alternative " +"can specify their own scheme. Publishers can either reuse an alternative " "scheme, or provide their own. Where publishers provide their own local " "schemes, they ought to prefix their `scheme` code with an [ISO-3166-1 " "alpha-3 country code](https://en.wikipedia.org/wiki/ISO_3166-1) to preserve " diff --git a/docs/locale/fr/LC_MESSAGES/guidance/map/pre-qualification.po b/docs/locale/fr/LC_MESSAGES/guidance/map/pre-qualification.po index c439e6150..8b9d523a4 100644 --- a/docs/locale/fr/LC_MESSAGES/guidance/map/pre-qualification.po +++ b/docs/locale/fr/LC_MESSAGES/guidance/map/pre-qualification.po @@ -346,7 +346,7 @@ msgid "" "suppliers to pre-qualify for two tenders for road construction in different " "neighbourhoods](https://contrataciones.gov.py/licitaciones/convocatoria/338229-servicios-" "consultoria-estudios-factibilidad-diseno-final-ingenieria-tramos-" -"caminos-1/precalificacion.html). Each tender will re-use the list of pre-" +"caminos-1/precalificacion.html). Each tender will reuse the list of pre-" "qualified suppliers established as a result of this first procedure." msgstr "" diff --git a/docs/locale/fr/LC_MESSAGES/guidance/publish.po b/docs/locale/fr/LC_MESSAGES/guidance/publish.po index 01ecf959e..93c17c35a 100644 --- a/docs/locale/fr/LC_MESSAGES/guidance/publish.po +++ b/docs/locale/fr/LC_MESSAGES/guidance/publish.po @@ -113,7 +113,7 @@ msgstr "" #: ../../docs/guidance/publish.md:36 msgid "" "Publishing data under an open license is an important part of open " -"contracting. Without this, restrictions on re-use can prevent many of the " +"contracting. Without this, restrictions on reuse can prevent many of the " "important [use cases](design/user_needs) for open contracting information " "being realized." msgstr "" @@ -121,12 +121,12 @@ msgstr "" #: ../../docs/guidance/publish.md:38 msgid "" "A license statement sets out the permission that users have to access, use " -"and re-use the data. This can take the form of a [Public Domain Dedication " +"and reuse the data. This can take the form of a [Public Domain Dedication " "or Certification](https://creativecommons.org/publicdomain/) which transfers" " a dataset into the public domain, or re-asserts that there are no existing " "copyrights or database rights inherent in the dataset (which is the case for" " government datasets in some jurisdictions), or the application of a license" -" which sets out the terms under which a dataset can be re-used." +" which sets out the terms under which a dataset can be reused." msgstr "" "Une déclaration de licence indique les autorisations dont jouissent les " "usagers pour accéder aux données, les utiliser et les réutiliser. La Licence" diff --git a/docs/locale/fr/LC_MESSAGES/history/changelog.po b/docs/locale/fr/LC_MESSAGES/history/changelog.po index 84975e6b1..7e457580e 100644 --- a/docs/locale/fr/LC_MESSAGES/history/changelog.po +++ b/docs/locale/fr/LC_MESSAGES/history/changelog.po @@ -601,8 +601,7 @@ msgstr "" #: ../../docs/history/changelog.md:110 msgid "" -"[Milestone documents](https://extensions.open-" -"contracting.org/en/extensions/milestone_documents/master/#changelog)" +"[Milestone documents](https://github.com/open-contracting-extensions/ocds_milestone_documents_extension#changelog)" msgstr "" #: ../../docs/history/changelog.md:111 ../../docs/history/changelog.md:189 diff --git a/docs/locale/fr/LC_MESSAGES/schema/identifiers.po b/docs/locale/fr/LC_MESSAGES/schema/identifiers.po index 22a2450a0..62e10a46c 100644 --- a/docs/locale/fr/LC_MESSAGES/schema/identifiers.po +++ b/docs/locale/fr/LC_MESSAGES/schema/identifiers.po @@ -688,7 +688,7 @@ msgstr "" #: ../../docs/schema/identifiers.md:168 msgid "" -"The same `id` value may be re-used in another array of items within the same" +"The same `id` value may be reused in another array of items within the same" " release, and no cross-reference between these identifiers is implied." msgstr "" "Le même identifiant peut être réutilisé dans une autre liste au sein d'une " diff --git a/docs/locale/fr/LC_MESSAGES/schema/reference.po b/docs/locale/fr/LC_MESSAGES/schema/reference.po index a87ce00e9..4c9cce18e 100644 --- a/docs/locale/fr/LC_MESSAGES/schema/reference.po +++ b/docs/locale/fr/LC_MESSAGES/schema/reference.po @@ -840,7 +840,7 @@ msgstr "Documentation sur les composants essentiels" #: ../../docs/schema/reference.md:273 msgid "" -"The following building blocks are commonly re-used throughout the standard." +"The following building blocks are commonly reused throughout the standard." msgstr "" "Les composants essentiels sont des composants couramment réutilisés dans le " "standard." diff --git a/docs/schema/identifiers.md b/docs/schema/identifiers.md index 2c6eceab0..0d1f5ee9d 100644 --- a/docs/schema/identifiers.md +++ b/docs/schema/identifiers.md @@ -165,7 +165,7 @@ Contracts must cross-reference a related award (using the `awardID` field), as k An item, document or milestone identifier must be unique within a given array of items, and must be used consistently across all the releases in a contracting process. -The same `id` value may be re-used in another array of items within the same release, and no cross-reference between these identifiers is implied. +The same `id` value may be reused in another array of items within the same release, and no cross-reference between these identifiers is implied. The use of an identifier means that subsequent releases can update prior identified items, documents or milestones, without needing to republish all the items, documents or milestones. diff --git a/docs/schema/reference.md b/docs/schema/reference.md index 334cc758c..64507ac01 100644 --- a/docs/schema/reference.md +++ b/docs/schema/reference.md @@ -270,7 +270,7 @@ See the [amendment implementation guidance](../guidance/map/amendments) for more ## Building block reference -The following building blocks are commonly re-used throughout the standard. +The following building blocks are commonly reused throughout the standard. ### OrganizationReference diff --git a/include/common.mk b/include/common.mk index 63cb5b5cb..12a9c619b 100644 --- a/include/common.mk +++ b/include/common.mk @@ -63,37 +63,6 @@ docs/locale: $(TRANSLATIONS:.%=docs/locale/%) pocount: find $(LOCALE_DIR) -name LC_MESSAGES -exec pocount --incomplete --short "{}" + -### Transifex - -.PHONY: clean_txconfig -clean_txconfig: - rm -f .tx/config - sphinx-intl create-txconfig - -.PHONY: update_txconfig -update_txconfig: - sphinx-intl update-txconfig-resources --transifex-organization-name $(TRANSIFEX_ORGANIZATION) --transifex-project-name $(TRANSIFEX_PROJECT) --pot-dir $(POT_DIR) --locale-dir $(LOCALE_DIR) - -# Builds and pushes the .pot files (`source_file` in .tx/config) to Transifex. -.PHONY: push -push: extract - tx push -s - -force_push.%: extract - tx push -f -s -t -l $* - -# Also pushes the translation .po files (`file_filter` in .tx/config) to Transifex. -.PHONY: force_push -force_push: extract - tx push -f -s -t -l $(COMMA_SEPARATED_TRANSLATIONS) - -pull.%: FORCE - tx pull -f -l $* - -.PHONY: pull -pull: - tx pull -f -l $(COMMA_SEPARATED_TRANSLATIONS) - ### Current language # Create a symlink for the language, so that file paths in `jsonschema` directives resolve. diff --git a/include/config.mk b/include/config.mk index ca85dfda9..9bca0278f 100644 --- a/include/config.mk +++ b/include/config.mk @@ -12,15 +12,13 @@ LOCALE_DIR=docs/locale # Directory in which to build documentation files. BUILD_DIR=build # Extra build files or directories. (These should match paths in .gitignore.) -EXTRA_BUILD_FILES=chromedriver* +EXTRA_BUILD_FILES= # Files that are built and distributed (you may use Bash extended globbing). DIST_FILES= # Directory in which to build .pot files. POT_DIR=$(BUILD_DIR)/locale # The prefix, if any, to the schema and codelists domains. DOMAIN_PREFIX= -# The Transifex project name. -TRANSIFEX_PROJECT=open-contracting-standard-1-1 # Any additional extract targets. EXTRACT_TARGETS= # Extra arguments for sphinx-autobuild. diff --git a/manage.py b/manage.py index 1ae27af14..c930fc2e0 100755 --- a/manage.py +++ b/manage.py @@ -8,20 +8,20 @@ import sys import warnings from contextlib import contextmanager -from copy import deepcopy from glob import glob from io import StringIO from pathlib import Path import click import json_merge_patch -import jsonref import lxml.etree import lxml.html import requests from babel.messages.pofile import read_po from docutils.utils import relative_path from lxml import etree +from ocdsextensionregistry import get_versioned_release_schema +from ocdsextensionregistry.util import replace_refs basedir = Path(__file__).resolve().parent schemadir = basedir / "schema" @@ -29,7 +29,7 @@ sys.path.append(str(basedir / "docs")) -from conf import release # noqa isort:skip +from conf import release # noqa: E402 def custom_warning_formatter(message, category, filename, lineno, line=None): @@ -37,109 +37,30 @@ def custom_warning_formatter(message, category, filename, lineno, line=None): warnings.formatwarning = custom_warning_formatter +logger = logging.getLogger(__name__) -versioned_template = json.loads(""" -{ - "type": "array", - "items": { - "type": "object", - "properties": { - "releaseDate": { - "format": "date-time", - "type": "string" - }, - "releaseID": { - "type": "string" - }, - "value": {}, - "releaseTag": { - "type": "array", - "items": { - "type": "string" - } - } - } - } -} -""") - -common_versioned_definitions = { - "StringNullUriVersioned": { - "type": ["string", "null"], - "format": "uri", - }, - "StringNullDateTimeVersioned": { - "type": ["string", "null"], - "format": "date-time", - }, - "StringNullVersioned": { - "type": ["string", "null"], - "format": None, - }, -} - -recognized_types = ( - # Array - ["array"], - ["array", "null"], # optional string arrays - # Object - ["object"], - ["object", "null"], # /Organization/details - # String - ["string"], - ["string", "null"], - # Literal - ["boolean", "null"], - ["integer", "null"], - ["number", "null"], - # Mixed - ["string", "integer"], - ["string", "integer", "null"], -) - -keywords_to_remove = ( - # Metadata keywords - # https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6 - "title", - "description", - "default", - # Extended keywords - # http://os4d.opendataservices.coop/development/schema/#extended-json-schema - "omitWhenMerged", - "wholeListMerge", -) - - -def json_load(filename, library=json, **kwargs): - """ - Loads JSON data from the given filename. - """ + +def json_load(filename): + """Load JSON data from the given filename.""" with (schemadir / filename).open() as f: - return library.load(f, **kwargs) + return json.load(f) def json_dump(filename, data): - """ - Writes JSON data to the given filename. - """ + """Write JSON data to the given filename.""" with (schemadir / filename).open("w") as f: json.dump(data, f, indent=2) f.write("\n") def csv_load(url, delimiter=","): - """ - Loads CSV data into a ``csv.DictReader`` from the given URL. - """ - reader = csv.DictReader(StringIO(get(url).text), delimiter=delimiter) - return reader + """Load CSV data into a ``csv.DictReader`` from the given URL.""" + return csv.DictReader(StringIO(get(url).text), delimiter=delimiter) @contextmanager def csv_dump(filename, fieldnames): - """ - Writes CSV headers to the given filename, and yields a ``csv.writer``. - """ + """Write CSV headers to the given filename, and yield a ``csv.writer``.""" f = (schemadir / "codelists" / filename).open("w") writer = csv.writer(f, lineterminator="\n") writer.writerow(fieldnames) @@ -150,268 +71,19 @@ def csv_dump(filename, fieldnames): def get(url): - """ - GETs a URL and returns the response. Raises an exception if the status code is not successful. - """ - response = requests.get(url) + """GET a URL and returns the response. Raise an exception if the status code is not successful.""" + response = requests.get(url, timeout=10) response.raise_for_status() return response -def coerce_to_list(data, key): - """ - Returns the value of the ``key`` key in the ``data`` mapping. If the value is a string, wraps it in an array. - """ - item = data.get(key, []) - if isinstance(item, str): - return [item] - return item - - def get_metaschema(): - """ - Patches and returns the JSON Schema Draft 4 metaschema. - """ + """Patches and returns the JSON Schema Draft 4 metaschema.""" return json_merge_patch.merge( json_load("metaschema/json-schema-draft-4.json"), json_load("metaschema/meta-schema-patch.json") ) -def get_common_definition_ref(item): - """ - Returns a schema that references the common definition that the ``item`` matches: "StringNullUriVersioned", - "StringNullDateTimeVersioned" or "StringNullVersioned". - """ - for name, keywords in common_versioned_definitions.items(): - # If the item matches the definition. - if any(item.get(keyword) != value for keyword, value in keywords.items()): - continue - # And adds no keywords to the definition. - if any(keyword not in (*keywords, *keywords_to_remove) for keyword in item): - continue - return {"$ref": f"#/definitions/{name}"} - - -def add_versioned(schema, unversioned_pointers, pointer=""): - """ - An outer function that calls ``_add_versioned`` on each field. - """ - for key, value in schema["properties"].items(): - new_pointer = f"{pointer}/properties/{key}" - _add_versioned(schema, unversioned_pointers, new_pointer, key, value) - - for key, value in schema.get("definitions", {}).items(): - new_pointer = f"{pointer}/definitions/{key}" - add_versioned(value, unversioned_pointers, pointer=new_pointer) - - -def _add_versioned(schema, unversioned_pointers, pointer, key, value): - """ - An inner function that performs the changes to the schema to refer to versioned/unversioned definitions. - - :param schema dict: the schema of the object on which the field is defined - :param unversioned_pointers set: JSON Pointers to ``id`` fields to leave unversioned if the object is in an array - :param pointer str: the field's pointer - :param key str: the field's name - :param value str: the field's schema - """ - # Skip unversioned fields. - if pointer in unversioned_pointers: - return - - types = coerce_to_list(value, "type") - - # If a type is unrecognized, we might need to update this script. - if "$ref" not in value and types not in recognized_types: - warnings.warn(f"{pointer} has unrecognized type {types}") - - # For example, if $ref is used. - if not types: - # Ignore the `amendment` field, which had no `id` field in OCDS 1.0. - if "deprecated" not in value: - versioned_pointer = f"{value['$ref'][1:]}/properties/id" - # If the `id` field is on an object not in an array, it needs to be versioned (e.g. buyer/properties/id). - if versioned_pointer in unversioned_pointers: - value["$ref"] = value["$ref"] + "VersionedId" - return - - # Reference a common versioned definition if possible, to limit the size of the schema. - ref = get_common_definition_ref(value) - if ref: - schema["properties"][key] = ref - - # Iterate into objects with properties like `Item.unit`. Otherwise, version objects with no properties as a - # whole, like `Organization.details`. - elif types == ["object"] and "properties" in value: - add_versioned(value, unversioned_pointers, pointer=pointer) - - else: - new_value = deepcopy(value) - - if types == ["array"]: - item_types = coerce_to_list(value["items"], "type") - - # See https://standard.open-contracting.org/latest/en/schema/merging/#whole-list-merge - if value.get("wholeListMerge"): - # Update `$ref` to the unversioned definition. - if "$ref" in value["items"]: - new_value["items"]["$ref"] = value["items"]["$ref"] + "Unversioned" - # Otherwise, similarly, don't iterate over item properties. - # See https://standard.open-contracting.org/latest/en/schema/merging/#lists - elif "$ref" in value["items"]: - # Leave `$ref` to the versioned definition. - return - # Exceptional case for deprecated `Amendment.changes`. - elif item_types == ["object"] and pointer == "/definitions/Amendment/properties/changes": - return - # Warn in case new combinations are added to the release schema. - elif item_types != ["string"]: - # Note: Versioning the properties of un-$ref'erenced objects in arrays isn't implemented. However, - # this combination hasn't occurred, with the exception of `Amendment/changes`. - warnings.warn(f"{pointer}/items has unexpected type {item_types}") - - versioned = deepcopy(versioned_template) - versioned["items"]["properties"]["value"] = new_value - schema["properties"][key] = versioned - - -def update_refs_to_unversioned_definitions(schema): - """ - Replaces ``$ref`` values with unversioned definitions. - """ - for key, value in schema.items(): - if key == "$ref": - schema[key] = value + "Unversioned" - elif isinstance(value, dict): - update_refs_to_unversioned_definitions(value) - - -def get_unversioned_pointers(schema, fields, pointer=""): - """ - Returns the JSON Pointers to ``id`` fields that must not be versioned if the object is in an array. - """ - if isinstance(schema, list): - for index, item in enumerate(schema): - get_unversioned_pointers(item, fields, pointer=f"{pointer}/{index}") - elif isinstance(schema, dict): - # Follows the logic of _get_merge_rules in merge.py from ocds-merge. - types = coerce_to_list(schema, "type") - - # If an array is whole list merge, its items are unversioned. - if "array" in types and schema.get("wholeListMerge"): - return - if "array" in types and "items" in schema: - item_types = coerce_to_list(schema["items"], "type") - # If an array mixes objects and non-objects, it is whole list merge. - if any(item_type != "object" for item_type in item_types): - return - # If it is an array of objects, any `id` fields are unversioned. - if "id" in schema["items"]["properties"]: - if hasattr(schema["items"], "__reference__"): - reference = schema["items"].__reference__["$ref"][1:] - else: - reference = pointer - fields.add(f"{reference}/properties/id") - - for key, value in schema.items(): - get_unversioned_pointers(value, fields, pointer=f"{pointer}/{key}") - - -def remove_omit_when_merged(schema): - """ - Removes properties that set ``omitWhenMerged``. - """ - if isinstance(schema, list): - for item in schema: - remove_omit_when_merged(item) - elif isinstance(schema, dict): - for key, value in schema.items(): - if key == "properties": - for prop in list(value): - if value[prop].get("omitWhenMerged"): - del value[prop] - if prop in schema["required"]: - schema["required"].remove(prop) - remove_omit_when_merged(value) - - -def remove_metadata_and_extended_keywords(schema): - """ - Removes metadata and extended keywords from properties and definitions. - """ - if isinstance(schema, list): - for item in schema: - remove_metadata_and_extended_keywords(item) - elif isinstance(schema, dict): - for key, value in schema.items(): - if key in ("definitions", "properties"): - for subschema in value.values(): - for keyword in keywords_to_remove: - subschema.pop(keyword, None) - remove_metadata_and_extended_keywords(value) - - -def get_versioned_release_schema(schema): - """ - Returns the versioned release schema. - """ - # Update schema metadata. - release_with_underscores = release.replace(".", "__") - schema["id"] = ( - f"https://standard.open-contracting.org/schema/{release_with_underscores}/versioned-release-validation-schema.json" # noqa - ) - schema["title"] = "Schema for a compiled, versioned Open Contracting Release." - - # Release IDs, dates and tags appear alongside values in the versioned release schema. - remove_omit_when_merged(schema) - - # Create unversioned copies of all definitions. - unversioned_definitions = {k + "Unversioned": deepcopy(v) for k, v in schema["definitions"].items()} - update_refs_to_unversioned_definitions(unversioned_definitions) - - # Determine which `id` fields occur on objects in arrays. - unversioned_pointers = set() - get_unversioned_pointers(jsonref.replace_refs(schema), unversioned_pointers) - - # Omit `ocid` from versioning. - ocid = schema["properties"].pop("ocid") - add_versioned(schema, unversioned_pointers) - schema["properties"]["ocid"] = ocid - - # Add the common versioned definitions. - for name, keywords in common_versioned_definitions.items(): - versioned = deepcopy(versioned_template) - for keyword, value in keywords.items(): - if value: - versioned["items"]["properties"]["value"][keyword] = value - schema["definitions"][name] = versioned - - # Add missing definitions. - while True: - try: - jsonref.replace_refs(schema, lazy_load=False) - break - except jsonref.JsonRefError as e: - name = e.cause.args[0] - - if name.endswith("VersionedId"): - # Add a copy of an definition with a versioned `id` field, using the same logic as before. - definition = deepcopy(schema["definitions"][name[:-11]]) - pointer = f"/definitions/{name[:-11]}/properties/id" - pointers = unversioned_pointers - {pointer} - _add_versioned(definition, pointers, pointer, "id", definition["properties"]["id"]) - else: - # Add a copy of an definition with no versioned fields. - definition = unversioned_definitions[name] - - schema["definitions"][name] = definition - - # Remove all metadata and extended keywords. - remove_metadata_and_extended_keywords(schema) - - return schema - - @click.group() def cli(): pass @@ -445,10 +117,7 @@ def unused_terms(filename): @cli.command() @click.option("--ignore-base", help="A base branch to ignore, e.g. 1.2-dev") def missing_changelog(ignore_base): - """ - Print pull requests not mentioned in the changelog. - """ - + """Print pull requests not mentioned in the changelog.""" # Ignore PRs to the ppp-extension branch, which became OCDS for PPPs. ignore = ["ppp-extension"] if ignore_base: @@ -492,7 +161,7 @@ def missing_changelog(ignore_base): base_ref = pr["base"]["ref"] # Include merged PRs, not in the "Minor:" or "1.0-RC" milestones, not syncing branches, and not ignored. - if not merged_at or milestone_number in (26, 27, 28, 29, 2) or pattern.search(title) or base_ref in ignore: + if not merged_at or milestone_number in {26, 27, 28, 29, 2} or pattern.search(title) or base_ref in ignore: if number in prs: click.echo(f"WARNING: #{number} should not be in changelog", file=sys.stderr) continue @@ -519,11 +188,14 @@ def pre_commit(): - versioned-release-validation-schema.json """ release_schema = json_load("release-schema.json") - jsonref_release_schema = json_load("release-schema.json", jsonref, merge_props=True) + jsonref_release_schema = replace_refs(json_load("release-schema.json"), keep_defs=True) json_dump("meta-schema.json", get_metaschema()) json_dump("dereferenced-release-schema.json", jsonref_release_schema) - json_dump("versioned-release-validation-schema.json", get_versioned_release_schema(release_schema)) + json_dump( + "versioned-release-validation-schema.json", + get_versioned_release_schema(release_schema, release.replace(".", "__")), + ) @cli.command() @@ -556,7 +228,8 @@ def update_country(file): # Clean "Western Sahara*", "United Arab Emirates (the)", etc. codes[d[str(offset + 9)]] = re.sub(r" \(the\)|\*", "", d[str(offset + 13)]) # The country code appears at offsets 9 and 15. Check that they are always the same. - assert d[str(offset + 9)] == d[str(offset + 15)] + if d[str(offset + 9)] != d[str(offset + 15)]: + raise AssertionError with open(schemadir / "codelists" / "country.csv", "w") as f: writer = csv.writer(f, lineterminator="\n") @@ -567,44 +240,41 @@ def update_country(file): @cli.command() def update_currency(): - """ - Update currency.csv from ISO 4217. - """ + """Update currency.csv from ISO 4217.""" # https://www.iso.org/iso-4217-currency-codes.html # https://www.six-group.com/en/products-services/financial-information/data-standards.html#scrollTo=currency-codes - # "List One: Current Currency & Funds" + # List One: Current Currency & Funds current_codes = {} - url = "https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/amendments/lists/list_one.xml" # noqa: E501 - tree = etree.fromstring(get(url).content) + url = "https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/amendments/lists/list_one.xml" + tree = etree.fromstring(get(url).content) # trusted external for node in tree.xpath("//CcyNtry"): - match = node.xpath("./Ccy") # Entries like Antarctica have no universal currency. - if match: + if node.xpath("./Ccy"): code = node.xpath("./Ccy")[0].text title = node.xpath("./CcyNm")[0].text.strip() if code not in current_codes: current_codes[code] = title # We should expect currency titles to be consistent across countries. elif current_codes[code] != title: - raise Exception(f"expected {current_codes[code]}, got {title}") + raise click.ClickException(f"expected {current_codes[code]}, got {title}") - # "List Three: Historic Denominations (Currencies & Funds)" + # List Three: Historic Denominations (Currencies & Funds) historic_codes = {} - url = "https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/amendments/lists/list_three.xml" # noqa: E501 - tree = etree.fromstring(get(url).content) + url = "https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/amendments/lists/list_three.xml" + tree = etree.fromstring(get(url).content) # trusted external for node in tree.xpath("//HstrcCcyNtry"): code = node.xpath("./Ccy")[0].text title = node.xpath("./CcyNm")[0].text.strip() valid_until = node.xpath("./WthdrwlDt")[0].text # Use ISO8601 interval notation. valid_until = re.sub(r"^(\d{4})-(\d{4})$", r"\1/\2", valid_until.replace(" to ", "/")) - if code not in current_codes: - if code not in historic_codes: - historic_codes[code] = {"Title": title, "Valid Until": valid_until} - # If the code is historical, use the most recent title and valid date. - elif valid_until > historic_codes[code]["Valid Until"]: - historic_codes[code] = {"Title": title, "Valid Until": valid_until} + if ( + code not in current_codes + # Last condition: If the code is historical, use the most recent title and valid date. + and (code not in historic_codes or valid_until > historic_codes[code]["Valid Until"]) + ): + historic_codes[code] = {"Title": title, "Valid Until": valid_until} with csv_dump("currency.csv", ["Code", "Title", "Valid Until"]) as writer: for code in sorted(current_codes): @@ -613,23 +283,20 @@ def update_currency(): writer.writerow([code, historic_codes[code]["Title"], historic_codes[code]["Valid Until"]]) release_schema = json_load("release-schema.json") - codes = sorted(list(current_codes) + list(historic_codes)) - release_schema["definitions"]["Value"]["properties"]["currency"]["enum"] = codes + [None] + codes = sorted([*current_codes, historic_codes]) + release_schema["definitions"]["Value"]["properties"]["currency"]["enum"] = [*codes, None] json_dump("release-schema.json", release_schema) @cli.command() def update_language(): - """ - Update language.csv from ISO 639-1. - """ + """Update language.csv from ISO 639-1.""" # https://www.iso.org/iso-639-language-codes.html # https://id.loc.gov/vocabulary/iso639-1.html with csv_dump("language.csv", ["Code", "Title"]) as writer: - reader = csv_load("https://id.loc.gov/vocabulary/iso639-1.tsv", delimiter="\t") - for row in reader: + for row in csv_load("https://id.loc.gov/vocabulary/iso639-1.tsv", delimiter="\t"): # Remove parentheses, like "Greek, Modern (1453-)", and split alternatives. titles = re.split(r" *\| *", re.sub(r" \(.+\)", "", row["Label (English)"])) # Remove duplication like "Ndebele, North | North Ndebele" and join alternatives using a comma instead of @@ -663,8 +330,7 @@ def update_media_type(): with csv_dump("mediaType.csv", ["Code", "Title"]) as writer: for registry in registries: # See "Available Formats" under each heading. - reader = csv_load(f"https://www.iana.org/assignments/media-types/{registry}.csv") - for row in reader: + for row in csv_load(f"https://www.iana.org/assignments/media-types/{registry}.csv"): if " " in row["Name"]: name, message = row["Name"].split(" ", 1) else: @@ -673,10 +339,10 @@ def update_media_type(): template = row["Template"] # All messages are expected to be about deprecation and obsoletion. if message: - logging.warning("%s: %s", message, code) + logger.warning("%s: %s", message, code) # "x-emf" has "image/emf" in its "Template" value (but it is deprecated). elif template and template != code: - raise Exception(f"expected {code}, got {template}") + raise click.ClickException(f"expected {code}, got {template}") else: writer.writerow([code, name]) @@ -686,9 +352,7 @@ def update_media_type(): @cli.command() @click.pass_context def update(ctx): - """ - Update codelists except country.csv. - """ + """Update codelists except country.csv.""" ctx.invoke(update_currency) ctx.invoke(update_language) ctx.invoke(update_media_type) @@ -740,9 +404,7 @@ def text(node, xpath): def add_translation_note(path, language, domain): - """ - Adds a translation note to a file. - """ + """Add a translation note to a file.""" base_url = "https://standard.open-contracting.org/1.1" with open(path) as f: @@ -752,10 +414,10 @@ def add_translation_note(path, language, domain): _ = translator.gettext pattern = f"{base_url}/{{}}/{domain}/" - response = requests.get(pattern.format(language)) + response = requests.get(pattern.format(language), timeout=10) # If it's a new page, add the note to the current version of the page. - if response.status_code == 404: + if response.status_code == requests.codes.not_found: message = _( 'This page was recently added to the English documentation. ' "It has not yet been translated." diff --git a/pyproject.toml b/pyproject.toml index 8cd10b2a3..66170be1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,12 +4,11 @@ version = "0.0.0" [tool.ruff] line-length = 119 -target-version = "py310" [tool.ruff.lint] select = ["ALL"] ignore = [ - "ANN", "C901", "COM812", "D203", "D212", "D415", "EM", "ISC001", "PERF203", "PLR091", "Q000", + "ANN", "C901", "COM812", "CPY001", "D203", "D212", "D415", "EM", "ISC001", "PERF203", "PLR091", "Q000", "D1", "D205", "PTH", "D200", # https://github.com/astral-sh/ruff/issues/6269 @@ -33,3 +32,6 @@ ignore-variadic-names = true "D301", # click escapes "TRY003", # errors ] + +[tool.uv.pip] +exclude-newer = "1 week" diff --git a/requirements.txt b/requirements.txt index 33d3d98dd..ec8f625cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,8 @@ -r common-requirements.txt +-r theme-requirements.txt # Add your own requirements below. ocdskit==1.3.0 sphinx-design==0.4.1 sphinxcontrib-opencontracting==0.0.8 -sphinxcontrib-opendataservices-jsonschema==0.7.1 -sphinxcontrib-opendataservices==0.5.0 diff --git a/script/diff b/script/diff index 85fb10c99..18f5fbdcc 100755 --- a/script/diff +++ b/script/diff @@ -3,14 +3,12 @@ # No -e, because diff uses exit status 1 when differences are found. set -u -curl -sS https://raw.githubusercontent.com/open-contracting/standard_profile_template/latest/docs/conf.py | diff -u - docs/conf.py - -for f in .github/workflows/ci.yml .github/workflows/spellcheck.yml include/config.mk; do - curl -sS https://raw.githubusercontent.com/open-contracting/standard_profile_template/latest/$f | diff -u - $f +for f in biome.json .github/workflows/ci.yml .github/workflows/lint.yml .github/workflows/spellcheck.yml docs/conf.py include/config.mk; do + curl -sS https://raw.githubusercontent.com/open-contracting/standard_profile_template/refs/heads/latest/$f | diff -u - $f done if [ -d util ] || [ -d schema/project-level ]; then - curl -sS https://raw.githubusercontent.com/open-contracting/standard_profile_template/latest/.gitignore | diff -u - .gitignore + curl -sS https://raw.githubusercontent.com/open-contracting/standard_profile_template/refs/heads/latest/.gitignore | diff -u - .gitignore else - curl -sS https://raw.githubusercontent.com/open-contracting/standard_profile_template/latest/manage.py | diff -u - manage.py + curl -sS https://raw.githubusercontent.com/open-contracting/standard_profile_template/refs/heads/latest/manage.py | diff -u - manage.py fi diff --git a/script/update b/script/update index a1ec03c34..3be6fa8ca 100755 --- a/script/update +++ b/script/update @@ -5,12 +5,12 @@ set -eu main() { mkdir -p script include tests - for f in .pre-commit-config.yaml .python-version Makefile common-requirements.in common-requirements.txt pyproject.toml .github/dependabot.yml .github/workflows/lint.yml .github/workflows/shell.yml docs/_static/favicon-16x16.ico include/common.mk include/prologue.mk script/diff script/update tests/conftest.py tests/test_common.py; do - curl -sS -o $f https://raw.githubusercontent.com/open-contracting/standard_profile_template/latest/$f + for f in .pre-commit-config.yaml .python-version Makefile common-requirements.in common-requirements.txt pyproject.toml .github/dependabot.yml .github/workflows/js.yml .github/workflows/shell.yml include/common.mk include/prologue.mk script/diff script/update tests/conftest.py tests/test_common.py theme-requirements.txt; do + curl -sS -o $f https://raw.githubusercontent.com/open-contracting/standard_profile_template/refs/heads/latest/$f done if [ ! -d schema/project-level ]; then - curl -sS -o .gitignore https://raw.githubusercontent.com/open-contracting/standard_profile_template/latest/.gitignore + curl -sS -o .gitignore https://raw.githubusercontent.com/open-contracting/standard_profile_template/refs/heads/latest/.gitignore fi chmod +x script/* diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 419f5c252..000000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[isort] -known_local_folder = helper diff --git a/tests/__init__.py b/tests/__init__.py index 3977e370d..29bd6731d 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -13,6 +13,6 @@ test_search_params = [ ("en", r"found \d+ page\(s\) matching"), # See https://github.com/sphinx-doc/sphinx/issues/11008 - # ('es', r'encontraron \d+ páginas que coinciden'), - # ('fr', r'\d+ page\(s\) correspondant'), + # ('es', r'encontraron \d+ páginas que coinciden'), # noqa: ERA001 + # ('fr', r'\d+ page\(s\) correspondant'), # noqa: ERA001 ] diff --git a/tests/conftest.py b/tests/conftest.py index f5a6035f0..2a393883b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,7 +11,6 @@ def browser(request): options = Options() options.add_argument("--headless") - options.add_argument("--no-sandbox") browser = webdriver.Chrome(options=options) browser.implicitly_wait(3) diff --git a/tests/test_docs.py b/tests/test_docs.py index 0ddc8b47a..29f89b2ef 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -18,6 +18,3 @@ def test_examples(browser, server, lang): select.select_by_visible_text("tenderAmendment") assert '"date": "2016-02-05T10:30:00Z"' in examples.text assert '"date": "2016-01-01T09:30:00Z"' not in examples.text - - # test collapse expand - # xs = examples.find_element(By.LINK_TEXT, '⊖') diff --git a/tests/test_schema_integrity.py b/tests/test_schema_integrity.py index c311a54d1..fdbab6b76 100644 --- a/tests/test_schema_integrity.py +++ b/tests/test_schema_integrity.py @@ -3,14 +3,19 @@ """ import json -import os.path import sys +from pathlib import Path import jsonref +from ocdsextensionregistry import get_versioned_release_schema -sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) +basedir = Path(__file__).resolve().parent.parent -from manage import get_metaschema, get_versioned_release_schema # noqa isort:skip +sys.path.extend([str(basedir), str(basedir / "docs")]) + +from conf import release # noqa: E402 + +from manage import get_metaschema # noqa: E402 def test_versioned_release_schema_is_in_sync(): @@ -18,7 +23,7 @@ def test_versioned_release_schema_is_in_sync(): actual = json.load(f) with open("schema/release-schema.json") as f: - expected = get_versioned_release_schema(json.load(f)) + expected = get_versioned_release_schema(json.load(f), release.replace(".", "__")) assert actual == expected, "Run: python manage.py pre-commit" diff --git a/theme-requirements.txt b/theme-requirements.txt new file mode 100644 index 000000000..dd74015dd --- /dev/null +++ b/theme-requirements.txt @@ -0,0 +1,2 @@ +# Not compiled, so that the theme tracks the branch instead of a commit. +git+https://github.com/open-contracting/standard_theme.git@open_contracting#egg=standard_theme