Skip to content

Commit

Permalink
Merge pull request #10 from DSD-DBS/raillabel_4
Browse files Browse the repository at this point in the history
validate_schema()
  • Loading branch information
unexcellent authored Nov 25, 2024
2 parents b0e8c1b + 71703d5 commit 6aad142
Show file tree
Hide file tree
Showing 20 changed files with 307 additions and 40 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dependencies = [
"raillabel>=4.0.0",
"pyyaml>=6.0.0",
"numpy>=1.24.4",
"pydantic<3.0.0",
]

[project.urls]
Expand Down
3 changes: 2 additions & 1 deletion raillabel_providerkit/validation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"""Package for validating raillabel data regarding the format requirements."""

from .validate_onthology.validate_onthology import validate_onthology
from .validate_schema import validate_schema

__all__ = ["validate_onthology"]
__all__ = ["validate_onthology", "validate_schema"]
21 changes: 8 additions & 13 deletions raillabel_providerkit/validation/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,16 @@

from __future__ import annotations

from pathlib import Path
from . import validate_schema

import raillabel

from . import validate_onthology


def validate(scene: raillabel.Scene, onthology: dict | Path) -> list[str]:
def validate(scene_dict: dict) -> list[str]:
"""Validate a scene based on the Deutsche Bahn Requirements.
Parameters
----------
scene : raillabel.Scene
The scene containing the annotations.
onthology : dict or Path
Onthology YAML-data or file containing a information about all classes and their
attributes. The onthology must adhere to the onthology_schema. If a path is provided, the
file is loaded as a YAML.
scene_dict : dict
The scene as a dictionary directly from `json.load()` in the raillabel format.
Returns
-------
Expand All @@ -31,6 +23,9 @@ def validate(scene: raillabel.Scene, onthology: dict | Path) -> list[str]:
"""
errors = []

errors += validate_onthology(scene, onthology)
errors.extend(validate_schema(scene_dict))

if len(errors) > 0:
return errors

return errors
8 changes: 8 additions & 0 deletions raillabel_providerkit/validation/validate_schema/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

"""Code for schema validation."""

from .validate_schema import validate_schema

__all__ = ["validate_schema"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

from __future__ import annotations

import json

from pydantic_core import ValidationError
from raillabel.json_format import JSONScene


def validate_schema(data: dict) -> list[str]:
"""Validate a scene for adherence to the raillabel schema."""
try:
JSONScene(**data)
except ValidationError as errors:
return _make_errors_readable(errors)
else:
return []


def _make_errors_readable(errors: ValidationError) -> list[str]: # noqa: C901
readable_errors = []
for error in json.loads(errors.json()):
if error["type"] == "missing":
readable_errors.append(_convert_missing_error_to_string(error))
elif error["type"] == "extra_forbidden":
readable_errors.append(_convert_unexpected_field_error_to_string(error))
elif error["type"] == "literal_error":
readable_errors.append(_convert_literal_error_to_string(error))
elif error["type"] in ["bool_type", "bool_parsing"]:
readable_errors.append(_convert_false_type_error_to_string(error, "bool"))
elif error["type"] in ["int_type", "int_parsing", "int_from_float"]:
readable_errors.append(_convert_false_type_error_to_string(error, "int"))
elif error["type"] in ["decimal_type", "decimal_parsing"]:
readable_errors.append(_convert_false_type_error_to_string(error, "Decimal"))
elif error["type"] in ["string_type", "string_parsing"]:
readable_errors.append(_convert_false_type_error_to_string(error, "str"))
elif error["type"] in ["float_type", "float_parsing"]:
readable_errors.append(_convert_false_type_error_to_string(error, "float"))
elif error["type"] in ["uuid_type", "uuid_parsing"]:
readable_errors.append(_convert_false_type_error_to_string(error, "UUID"))
elif error["type"] == "too_long":
readable_errors.append(_convert_too_long_error_to_string(error))
else:
readable_errors.append(str(error))

return readable_errors


def _build_error_path(loc: list[str]) -> str:
path = "$"
for part in loc:
path += f".{part}"
return path


def _convert_missing_error_to_string(error: dict) -> str:
return f"{_build_error_path(error["loc"][:-1])}: required field '{error["loc"][-1]}' is missing."


def _convert_unexpected_field_error_to_string(error: dict) -> str:
return f"{_build_error_path(error["loc"][:-1])}: found unexpected field '{error["loc"][-1]}'."


def _convert_literal_error_to_string(error: dict) -> str:
return (
f"{_build_error_path(error["loc"])}: value '{error["input"]}' does not match allowed values "
f"({error["ctx"]["expected"]})."
)


def _convert_false_type_error_to_string(error: dict, target_type: str) -> str:
if "[key]" in error["loc"]:
error_path = _build_error_path(error["loc"][:-2])
else:
error_path = _build_error_path(error["loc"])

return f"{error_path}: value '{error["input"]}' could not be interpreted " f"as {target_type}."


def _convert_too_long_error_to_string(error: dict) -> str:
return (
f"{_build_error_path(error["loc"])}: should have length of {error["ctx"]["actual_length"]} "
f"but has length of {error["ctx"]["max_length"]}."
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from uuid import UUID

import pytest
import raillabel.format.understand_ai as uai_format
from raillabel.format.understand_ai._translation import translate_class_id
import raillabel_providerkit.format.understand_ai as uai_format
from raillabel_providerkit.format.understand_ai._translation import translate_class_id

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from uuid import UUID

import pytest
import raillabel.format.understand_ai as uai_format
from raillabel.format.understand_ai._translation import translate_class_id
import raillabel_providerkit.format.understand_ai as uai_format
from raillabel_providerkit.format.understand_ai._translation import translate_class_id

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

import pytest
import raillabel.format.understand_ai as uai_format
import raillabel_providerkit.format.understand_ai as uai_format

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# SPDX-License-Identifier: Apache-2.0

import pytest
import raillabel.format.understand_ai as uai_format
from raillabel._util._warning import _WarningsLogger
import raillabel_providerkit.format.understand_ai as uai_format
from raillabel_providerkit._util._warning import _WarningsLogger

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

import pytest
import raillabel.format.understand_ai as uai_format
import raillabel_providerkit.format.understand_ai as uai_format

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

import pytest
import raillabel.format.understand_ai as uai_format
import raillabel_providerkit.format.understand_ai as uai_format

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from uuid import UUID

import pytest
import raillabel.format.understand_ai as uai_format
from raillabel.format.understand_ai._translation import translate_class_id
import raillabel_providerkit.format.understand_ai as uai_format
from raillabel_providerkit.format.understand_ai._translation import translate_class_id

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from uuid import UUID

import pytest
import raillabel.format.understand_ai as uai_format
from raillabel.format.understand_ai._translation import translate_class_id
import raillabel_providerkit.format.understand_ai as uai_format
from raillabel_providerkit.format.understand_ai._translation import translate_class_id

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

import pytest
import raillabel.format.understand_ai as uai_format
import raillabel_providerkit.format.understand_ai as uai_format

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# SPDX-License-Identifier: Apache-2.0

import pytest
import raillabel.format.understand_ai as uai_format
from raillabel._util._warning import _WarningsLogger
import raillabel_providerkit.format.understand_ai as uai_format
from raillabel_providerkit._util._warning import _WarningsLogger

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from uuid import UUID

import pytest
import raillabel.format.understand_ai as uai_format
from raillabel.format.understand_ai._translation import translate_class_id
import raillabel_providerkit.format.understand_ai as uai_format
from raillabel_providerkit.format.understand_ai._translation import translate_class_id

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from decimal import Decimal

import pytest
import raillabel.format.understand_ai as uai_format
import raillabel_providerkit.format.understand_ai as uai_format

# == Fixtures =========================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

import pytest
import raillabel.format.understand_ai as uai_format
import raillabel_providerkit.format.understand_ai as uai_format

# == Fixtures =========================

Expand Down
14 changes: 8 additions & 6 deletions tests/test_raillabel_providerkit/validation/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@

from raillabel_providerkit import validate

# == Tests ============================

def test_no_errors_in_empty_scene():
scene_dict = {"openlabel": {"metadata": {"schema_version": "1.0.0"}}}
actual = validate(scene_dict)
assert len(actual) == 0

def test_no_errors(demo_onthology, valid_onthology_scene):
assert validate(valid_onthology_scene, demo_onthology) == []


def test_onthology_errors(demo_onthology, invalid_onthology_scene):
assert len(validate(invalid_onthology_scene, demo_onthology)) == 1
def test_schema_errors():
scene_dict = {"openlabel": {}}
actual = validate(scene_dict)
assert len(actual) == 1


if __name__ == "__main__":
Expand Down
Loading

0 comments on commit 6aad142

Please sign in to comment.