Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make strict schema #1480

Open
wants to merge 35 commits into
base: 1.2-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4009180
Run ocdskit schema-strict
duncandewhurst Nov 30, 2021
046a99f
Add additional schema validation (#881)
duncandewhurst Nov 30, 2021
bd449d3
Make id, name required on Organization[Reference] (#679)
duncandewhurst Nov 30, 2021
5339a6e
Make id required on Amendment and RelatedProcess (650)
duncandewhurst Feb 10, 2022
86fed0b
Run manage.py pre-commit
duncandewhurst Feb 10, 2022
d1ae51c
Move strict schema files to subfolder
duncandewhurst Feb 10, 2022
3f2d113
Checkout schema files from 1.2-dev
duncandewhurst Feb 10, 2022
4476d72
Update derivative strict schema files
duncandewhurst Feb 10, 2022
e3fde8c
Merge branch '1.2-dev' into 1046-strict-schema
duncandewhurst Feb 10, 2022
62de1d3
Generate strict schema files programmatically
duncandewhurst Feb 11, 2022
5ff4ac6
Fix Flake8 errors
duncandewhurst Feb 11, 2022
7041fbe
strict schema: remove integer type from id fields
duncandewhurst Feb 11, 2022
ce18ec2
strict schema: remove null types from package schemas
duncandewhurst Feb 11, 2022
eacd129
Move key-based validation properties logic from OCDS Kit
duncandewhurst Feb 28, 2022
85af27b
Move UnitValue definition to regular release schema
duncandewhurst Feb 28, 2022
7643c47
Fix flake8 errors
duncandewhurst Feb 28, 2022
c0ac04f
Run ./manage.py pre-commit
duncandewhurst Feb 28, 2022
db72bed
Strict schema: Remove integer type from projectID
duncandewhurst Feb 28, 2022
062a8f1
Merge branch '1.2-dev' into 1046-strict-schema
duncandewhurst Jul 31, 2022
c441759
Merge branch '1.2-dev' into 1046-strict-schema
duncandewhurst Jul 31, 2022
0abecc7
Update changelog
duncandewhurst Jul 31, 2022
886c4b5
Merge branch '1.2-dev' into 1046-strict-schema
jpmckinney Oct 17, 2023
747171d
manage: Update strict code to use merge_props=True
jpmckinney Oct 17, 2023
51d5d72
chore: flake8
jpmckinney Oct 17, 2023
a28caaa
Merge branch '1.2-dev' into 1046-strict-schema
duncandewhurst Mar 28, 2024
a90d378
manage.py: Sort imports
duncandewhurst Mar 28, 2024
e5a409b
schema: Remove strict files
duncandewhurst Mar 28, 2024
c1865ac
Update changelog, reinstate whitespace
duncandewhurst Mar 28, 2024
6948447
Merge branch '1.2-dev' into 1046-strict-schema
duncandewhurst Oct 23, 2024
a53d550
manage.py: Update pre-commit to reflect changes in json_dump/json_load
duncandewhurst Oct 23, 2024
e6eb4f1
manage.py (pre-commit): Make strict record schema
duncandewhurst Oct 23, 2024
31eed0b
Merge branch '1.2-dev' into 1046-strict-schema
duncandewhurst Oct 29, 2024
c54ec0a
.pre-commit-config.yaml: Reinstate ruff-format
duncandewhurst Oct 29, 2024
84cd062
manage.py (pre-commit): Create a compiled release schema, format code
duncandewhurst Nov 21, 2024
fccb5f6
manage.py (pre-commit): Update references to strict schemas
duncandewhurst Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/history/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ Per the [normative and non-normative content and changes policy](../governance/n
* [#1450](https://github.com/open-contracting/standard/pull/1450) Replace a repeated example in schema/merging/ with a link to guidance/build/merging/.
* [#1665](https://github.com/open-contracting/standard/pull/1665) Abandon in-file translations.

* [#1480](https://github.com/open-contracting/standard/pull/1480) Add `UnitValue` definition.

### Documentation

* [#1115](https://github.com/open-contracting/standard/pull/1115) Add guidance on when having multiple suppliers per award.
Expand Down
147 changes: 146 additions & 1 deletion manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from babel.messages.pofile import read_po
from docutils.utils import relative_path
from lxml import etree
from ocdskit.schema import get_schema_fields
from ocdskit.schema import add_validation_properties, get_schema_fields

basedir = Path(__file__).resolve().parent
schemadir = basedir / "schema"
Expand Down Expand Up @@ -392,6 +392,126 @@ def get_versioned_release_schema(schema):
return schema


def add_key_based_validation_properties(schema):
"""
Add validation properties based on key names.

* "format": "email" if the key is "email"
* "minimum": 0 to "quantity", "durationInDays" and "numberOfTenderers fields
* "required": ["id", "name"] to "Organization" and "OrganizationReference"
* "required": ["id"] to "Amendment" and "RelatedProcess"
* "$ref": "#/definitions/UnitValue" to "Unit.value"

Removes "integer" type from "id" and "projectID" fields.

:param dict schema: a JSON schema
"""
if isinstance(schema, list):
for item in schema:
add_key_based_validation_properties(item)
elif isinstance(schema, dict):
for key, value in schema.items():
if key == "email":
value["format"] = "email"
elif key in ["quantity", "durationInDays", "numberOfTenderers"]:
value["minimum"] = 0
elif key in ["Organization", "OrganizationReference"]:
value["required"] = ["id", "name"]
value["properties"]["name"]["type"] = "string"
value["properties"]["id"]["type"] = "string"
elif key in ["Amendment", "RelatedProcess"]:
value["required"] = ["id"]
value["properties"]["id"]["type"] = "string"
elif key in ["id", "projectID"]:
if "type" in value and "integer" in value["type"]:
value["type"].remove("integer")
elif key == "Unit":
value["properties"]["value"]["$ref"] = "#/definitions/UnitValue"

add_key_based_validation_properties(value)


def get_strict_schema(schema):
"""
Return the strict version of the schema.
"""
# Update schema metadata.
identifier = schema["id"].split("/")
identifier[-1] = f"strict/{identifier[-1]}"
schema["id"] = "/".join(identifier)
schema["title"] = f'Strict {schema["title"][0].lower()}{schema["title"][1:]}'
schema["description"] = (
f'{schema["description"]} The strict schema adds additional validation rules planned for inclusion in OCDS 2.0. Use of the strict schema is a voluntary opportunity to improve data quality.' # noqa: E501
)

# Update references to other schemas.
reference_strict_schemas(schema)

# Reference compiled release schema.
if schema["id"].endswith("record-schema.json"):
schema["properties"]["compiledRelease"]["$ref"] = schema["properties"]["compiledRelease"]["$ref"].replace(
"release-schema.json", "compiled-release-schema.json"
)

# Add validation properties
add_validation_properties(schema)

# Add key-based validation properties
add_key_based_validation_properties(schema)

# Remove null types from package schemas
if "package" in schema["id"]:
return remove_nulls(schema)

return schema


def get_compiled_release_schema(schema):
"""
Return the compiled release schema.
"""
# Update schema metadata.
schema["id"] = schema["id"].replace("release-schema", "compiled-release-schema")
schema["title"] = schema["title"].replace("Open Contracting Release", "Open Contracting Compiled Release")

# Remove null types and enum values.
return remove_nulls(schema)


def remove_nulls(schema):
"""
Remove null types and enum values.
"""
if isinstance(schema, dict):
for key, value in schema.items():
if key == "type" and isinstance(value, list) and "null" in value:
value.remove("null")
if key == "enum" and isinstance(value, list) and None in value:
value.remove(None)

remove_nulls(value)

return schema


def reference_strict_schemas(schema):
"""
Update $refs to reference strict schemas.
"""
if isinstance(schema, dict):
for key, value in schema.items():
if key == "$ref" and value.startswith("https://standard.open-contracting.org/schema/"):
reference = value.split("/")
reference[-1] = f"strict/{reference[-1]}"
schema[key] = "/".join(reference)

reference_strict_schemas(value)

if isinstance(schema, list):
for subschema in schema:
reference_strict_schemas(subschema)


@click.group()
def cli():
pass
Expand Down Expand Up @@ -494,6 +614,12 @@ def pre_commit():
- meta-schema.json
- dereferenced-release-schema.json
- versioned-release-validation-schema.json
- strict/release-schema.json
- strict/release-package.json
- strict/record-package.json
- strict/dereferenced-release-schema.json
- strict/versioned-release-validation-schema.json
- strict/compiled-release-schema.json
"""
nonmultilingual = {
# Identifiers.
Expand All @@ -516,6 +642,7 @@ def pre_commit():
}

release_schema = json_load("release-schema.json")
strict_release_schema = get_strict_schema(deepcopy(release_schema))
jsonref_release_schema = json_load("release-schema.json", jsonref, merge_props=True)

counts = defaultdict(list)
Expand Down Expand Up @@ -568,6 +695,24 @@ def pre_commit():
json_dump("dereferenced-release-schema.json", jsonref_release_schema)
json_dump("versioned-release-validation-schema.json", get_versioned_release_schema(release_schema))

# Strict schemas.
directory = Path("strict")
json_dump(directory / "release-schema.json", strict_release_schema)
json_dump(directory / "record-schema.json", get_strict_schema(json_load("record-schema.json")))

strict_dereferenced_release_schema = json_load(directory / "release-schema.json", jsonref, merge_props=True)
json_dump(directory / "dereferenced-release-schema.json", strict_dereferenced_release_schema)
json_dump(
directory / "versioned-release-validation-schema.json", get_versioned_release_schema(strict_release_schema)
)

json_dump(directory / "release-package-schema.json", get_strict_schema(json_load("release-package-schema.json")))
json_dump(directory / "record-package-schema.json", get_strict_schema(json_load("record-package-schema.json")))
json_dump(
directory / "compiled-release-schema.json",
remove_nulls(get_compiled_release_schema(json_load(directory / "release-schema.json"))),
)


@cli.command()
@click.argument("file", type=click.File())
Expand Down
Loading