validibot-shared 0.11.0__tar.gz → 0.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/PKG-INFO +2 -2
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/pyproject.toml +2 -2
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/test_schematron_envelopes.py +70 -49
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/test_schematron_svrl.py +1 -3
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/schematron/__init__.py +2 -2
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/schematron/envelopes.py +52 -57
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/schematron/svrl.py +3 -8
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/.gitignore +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/LICENSE +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/NOTICE +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/README.md +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/__init__.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/test_energyplus_envelopes.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/test_energyplus_models.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/test_evidence_manifest.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/test_fmu_envelopes.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/test_fmu_models.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/test_package_init.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/test_shacl_envelopes.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/tests/test_validations_envelopes.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/__init__.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/energyplus/__init__.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/energyplus/envelopes.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/energyplus/models.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/evidence/__init__.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/evidence/manifest.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/fmu/__init__.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/fmu/envelopes.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/fmu/models.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/py.typed +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/shacl/__init__.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/shacl/envelopes.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/validations/__init__.py +0 -0
- {validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/validations/envelopes.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: validibot-shared
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: Shared library for data interchange between Validibot and validator containers
|
|
5
5
|
Project-URL: Homepage, https://validibot.com
|
|
6
6
|
Project-URL: Documentation, https://docs.validibot.com
|
|
@@ -28,7 +28,7 @@ Requires-Dist: defusedxml<0.8,>=0.7.1
|
|
|
28
28
|
Requires-Dist: pydantic<3.0,>=2.13
|
|
29
29
|
Provides-Extra: dev
|
|
30
30
|
Requires-Dist: pytest==9.1.1; extra == 'dev'
|
|
31
|
-
Requires-Dist: ruff==0.15.
|
|
31
|
+
Requires-Dist: ruff==0.15.20; extra == 'dev'
|
|
32
32
|
Description-Content-Type: text/markdown
|
|
33
33
|
|
|
34
34
|
<div align="center">
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "validibot-shared"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.12.0"
|
|
8
8
|
description = "Shared library for data interchange between Validibot and validator containers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -37,7 +37,7 @@ classifiers = [
|
|
|
37
37
|
dependencies = ["pydantic>=2.13,<3.0", "defusedxml>=0.7.1,<0.8"]
|
|
38
38
|
|
|
39
39
|
[project.optional-dependencies]
|
|
40
|
-
dev = ["pytest==9.1.1", "ruff==0.15.
|
|
40
|
+
dev = ["pytest==9.1.1", "ruff==0.15.20"]
|
|
41
41
|
|
|
42
42
|
[project.urls]
|
|
43
43
|
Homepage = "https://validibot.com"
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
"""Tests for the Schematron container contract (ADR-2026-07-01, D4b/D9).
|
|
2
2
|
|
|
3
|
-
The Schematron validator ships
|
|
4
|
-
|
|
5
|
-
``engine_status`` failure taxonomy where ``passed`` is
|
|
6
|
-
True/False when the engine ran, ``None`` (unknown) when it
|
|
7
|
-
tests pin those contract properties so a refactor cannot
|
|
8
|
-
them: the Django side and the validator backend both program
|
|
9
|
-
this shape.
|
|
3
|
+
The Schematron validator ships the author's rules **inline as text** (the
|
|
4
|
+
SHACL ``shapes_text`` pattern — the container compiles them itself), and its
|
|
5
|
+
outputs carry an ``engine_status`` failure taxonomy where ``passed`` is
|
|
6
|
+
*tri-state* — True/False when the engine ran, ``None`` (unknown) when it
|
|
7
|
+
could not. These tests pin those contract properties so a refactor cannot
|
|
8
|
+
silently weaken them: the Django side and the validator backend both program
|
|
9
|
+
against exactly this shape.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import pytest
|
|
13
13
|
from pydantic import ValidationError
|
|
14
14
|
|
|
15
15
|
from validibot_shared.schematron.envelopes import (
|
|
16
|
+
ENGINE_ERROR_RULES_INVALID,
|
|
16
17
|
ENGINE_STATUS_OK,
|
|
17
18
|
ENGINE_STATUS_TIMEOUT,
|
|
18
19
|
SchematronFinding,
|
|
@@ -32,7 +33,13 @@ from validibot_shared.validations.envelopes import (
|
|
|
32
33
|
DEFAULT_MAX_FINDINGS = 500
|
|
33
34
|
DEFAULT_XSLT_TIMEOUT = 60
|
|
34
35
|
TEST_ERROR_COUNT = 3
|
|
35
|
-
|
|
36
|
+
RULES_SHA = "b" * 64
|
|
37
|
+
|
|
38
|
+
SCH_SOURCE = (
|
|
39
|
+
'<schema xmlns="http://purl.oclc.org/dsdl/schematron">'
|
|
40
|
+
"<pattern><rule context='/'><assert test='true()'>ok</assert></rule>"
|
|
41
|
+
"</pattern></schema>"
|
|
42
|
+
)
|
|
36
43
|
|
|
37
44
|
|
|
38
45
|
class _ValidatorStub:
|
|
@@ -45,46 +52,43 @@ class _ValidatorStub:
|
|
|
45
52
|
|
|
46
53
|
def _inputs(**overrides) -> SchematronInputs:
|
|
47
54
|
base = {
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"artifact_uri": "gs://bucket/run-1/pack.xslt",
|
|
51
|
-
"artifact_sha256": ARTIFACT_SHA,
|
|
52
|
-
"query_binding": "xslt2",
|
|
55
|
+
"schematron_text": SCH_SOURCE,
|
|
56
|
+
"schematron_sha256": RULES_SHA,
|
|
53
57
|
}
|
|
54
58
|
base.update(overrides)
|
|
55
59
|
return SchematronInputs(**base)
|
|
56
60
|
|
|
57
61
|
|
|
58
|
-
def
|
|
59
|
-
"""Inputs
|
|
62
|
+
def test_inputs_carry_inline_rules_and_limit_defaults():
|
|
63
|
+
"""Inputs ship the rules inline with provenance sha and D8 defaults.
|
|
60
64
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
Inline text is the whole delivery model (no staging, no artefact URIs):
|
|
66
|
+
the container gets everything it needs from the envelope alone, exactly
|
|
67
|
+
like SHACL's shapes_text.
|
|
64
68
|
"""
|
|
65
69
|
inputs = _inputs()
|
|
66
70
|
|
|
67
|
-
assert inputs.
|
|
68
|
-
assert inputs.
|
|
71
|
+
assert inputs.schematron_text == SCH_SOURCE
|
|
72
|
+
assert inputs.schematron_sha256 == RULES_SHA
|
|
69
73
|
assert inputs.max_findings == DEFAULT_MAX_FINDINGS
|
|
70
74
|
assert inputs.xslt_timeout_seconds == DEFAULT_XSLT_TIMEOUT
|
|
71
75
|
|
|
72
76
|
|
|
73
|
-
def
|
|
74
|
-
"""Omitting
|
|
77
|
+
def test_inputs_require_the_rules_text():
|
|
78
|
+
"""Omitting schematron_text is a validation error, not a default.
|
|
75
79
|
|
|
76
|
-
An input envelope without
|
|
77
|
-
|
|
80
|
+
An input envelope without rules would force the container to run
|
|
81
|
+
nothing and report... something. Refuse at the contract layer.
|
|
78
82
|
"""
|
|
79
83
|
with pytest.raises(ValidationError):
|
|
80
|
-
SchematronInputs(
|
|
84
|
+
SchematronInputs()
|
|
81
85
|
|
|
82
86
|
|
|
83
87
|
def test_outputs_default_to_unknown_passed_not_false():
|
|
84
88
|
"""``passed`` defaults to None — unknown, not failed (D9).
|
|
85
89
|
|
|
86
90
|
A default of False would make an unpopulated envelope read as "the
|
|
87
|
-
|
|
91
|
+
document failed the rules"; None forces every consumer to distinguish
|
|
88
92
|
"engine didn't run" from "rules failed".
|
|
89
93
|
"""
|
|
90
94
|
outputs = SchematronOutputs()
|
|
@@ -92,35 +96,46 @@ def test_outputs_default_to_unknown_passed_not_false():
|
|
|
92
96
|
assert outputs.engine_status == ENGINE_STATUS_OK
|
|
93
97
|
|
|
94
98
|
|
|
95
|
-
def
|
|
96
|
-
"""
|
|
99
|
+
def test_engine_failure_shapes_round_trip():
|
|
100
|
+
"""Timeout and rules-invalid outputs serialize and re-validate losslessly.
|
|
97
101
|
|
|
98
102
|
The callback path deserializes output.json with this model; the D9
|
|
99
|
-
fields must survive the JSON round trip exactly
|
|
103
|
+
fields must survive the JSON round trip exactly, including the
|
|
104
|
+
machine hint that lets Django distinguish "your rules don't compile"
|
|
105
|
+
from a generic engine error.
|
|
100
106
|
"""
|
|
101
|
-
|
|
107
|
+
timeout = SchematronOutputs(
|
|
102
108
|
engine_status=ENGINE_STATUS_TIMEOUT,
|
|
103
109
|
engine_message="Transform exceeded 60s",
|
|
104
110
|
passed=None,
|
|
105
|
-
error_count=0,
|
|
106
|
-
)
|
|
107
|
-
envelope = SchematronOutputEnvelope(
|
|
108
|
-
run_id="run-1",
|
|
109
|
-
validator={"id": "v1", "type": ValidatorType.SCHEMATRON, "version": "1"},
|
|
110
|
-
status=ValidationStatus.FAILED_RUNTIME,
|
|
111
|
-
timing={},
|
|
112
|
-
outputs=outputs,
|
|
113
111
|
)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
invalid_rules = SchematronOutputs(
|
|
113
|
+
engine_status="error",
|
|
114
|
+
engine_error_code=ENGINE_ERROR_RULES_INVALID,
|
|
115
|
+
engine_message="Schematron failed to compile: unexpected element",
|
|
116
|
+
passed=None,
|
|
117
117
|
)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
for outputs, status in (
|
|
119
|
+
(timeout, ValidationStatus.FAILED_RUNTIME),
|
|
120
|
+
(invalid_rules, ValidationStatus.FAILED_RUNTIME),
|
|
121
|
+
):
|
|
122
|
+
envelope = SchematronOutputEnvelope(
|
|
123
|
+
run_id="run-1",
|
|
124
|
+
validator={"id": "v1", "type": ValidatorType.SCHEMATRON, "version": "1"},
|
|
125
|
+
status=status,
|
|
126
|
+
timing={},
|
|
127
|
+
outputs=outputs,
|
|
128
|
+
)
|
|
129
|
+
restored = SchematronOutputEnvelope.model_validate(
|
|
130
|
+
envelope.model_dump(mode="json"),
|
|
131
|
+
)
|
|
132
|
+
assert restored.outputs.engine_status == outputs.engine_status
|
|
133
|
+
assert restored.outputs.engine_error_code == outputs.engine_error_code
|
|
134
|
+
assert restored.outputs.passed is None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_findings_map_and_provenance_round_trip():
|
|
138
|
+
"""Findings keep native ids/locations; provenance keeps the rules sha."""
|
|
124
139
|
outputs = SchematronOutputs(
|
|
125
140
|
engine_status=ENGINE_STATUS_OK,
|
|
126
141
|
passed=False,
|
|
@@ -135,12 +150,17 @@ def test_findings_and_rule_id_map_round_trip():
|
|
|
135
150
|
flag="fatal",
|
|
136
151
|
),
|
|
137
152
|
],
|
|
153
|
+
schematron_sha256=RULES_SHA,
|
|
154
|
+
query_binding="xslt2",
|
|
155
|
+
engine="SaxonC-HE 12.9",
|
|
138
156
|
)
|
|
139
157
|
|
|
140
158
|
restored = SchematronOutputs.model_validate(outputs.model_dump(mode="json"))
|
|
141
159
|
assert restored.finding_rule_ids_by_severity["BR-CO-15"] == "ERROR"
|
|
142
160
|
assert restored.findings[0].rule_id == "BR-CO-15"
|
|
143
161
|
assert restored.findings[0].location_xpath == "/Invoice/LegalMonetaryTotal"
|
|
162
|
+
assert restored.schematron_sha256 == RULES_SHA
|
|
163
|
+
assert restored.query_binding == "xslt2"
|
|
144
164
|
|
|
145
165
|
|
|
146
166
|
def test_outputs_forbid_unknown_fields():
|
|
@@ -148,9 +168,10 @@ def test_outputs_forbid_unknown_fields():
|
|
|
148
168
|
|
|
149
169
|
Contract models must reject unknown keys so a mismatched backend/Django
|
|
150
170
|
version pair surfaces as an explicit error instead of dropped data.
|
|
171
|
+
This also guards the 0.11 → 0.12 break: old pack_* fields are refused.
|
|
151
172
|
"""
|
|
152
173
|
with pytest.raises(ValidationError):
|
|
153
|
-
SchematronOutputs(engine_status="ok",
|
|
174
|
+
SchematronOutputs(engine_status="ok", pack_id="stale-field")
|
|
154
175
|
|
|
155
176
|
|
|
156
177
|
def test_build_schematron_input_envelope_assembles_the_xml_submission():
|
|
@@ -180,4 +201,4 @@ def test_build_schematron_input_envelope_assembles_the_xml_submission():
|
|
|
180
201
|
assert file_item.name == "submission.xml"
|
|
181
202
|
assert file_item.mime_type == SupportedMimeType.APPLICATION_XML
|
|
182
203
|
assert file_item.role == "primary-model"
|
|
183
|
-
assert envelope.inputs.
|
|
204
|
+
assert envelope.inputs.schematron_text == SCH_SOURCE
|
|
@@ -64,9 +64,7 @@ def _failed_assert(
|
|
|
64
64
|
if role:
|
|
65
65
|
attrs += f' role="{role}"'
|
|
66
66
|
return (
|
|
67
|
-
f"<svrl:failed-assert{attrs}>"
|
|
68
|
-
f"<svrl:text>{text}</svrl:text>"
|
|
69
|
-
"</svrl:failed-assert>"
|
|
67
|
+
f"<svrl:failed-assert{attrs}><svrl:text>{text}</svrl:text></svrl:failed-assert>"
|
|
70
68
|
)
|
|
71
69
|
|
|
72
70
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Schematron Advanced validator envelopes and models."""
|
|
2
2
|
|
|
3
3
|
from validibot_shared.schematron.envelopes import (
|
|
4
|
-
ENGINE_ERROR_ARTIFACT_MISMATCH,
|
|
5
4
|
ENGINE_ERROR_BACKEND_UNAVAILABLE,
|
|
5
|
+
ENGINE_ERROR_RULES_INVALID,
|
|
6
6
|
ENGINE_STATUS_ERROR,
|
|
7
7
|
ENGINE_STATUS_OK,
|
|
8
8
|
ENGINE_STATUS_TIMEOUT,
|
|
@@ -26,8 +26,8 @@ from validibot_shared.schematron.svrl import (
|
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
__all__ = [
|
|
29
|
-
"ENGINE_ERROR_ARTIFACT_MISMATCH",
|
|
30
29
|
"ENGINE_ERROR_BACKEND_UNAVAILABLE",
|
|
30
|
+
"ENGINE_ERROR_RULES_INVALID",
|
|
31
31
|
"ENGINE_STATUS_ERROR",
|
|
32
32
|
"ENGINE_STATUS_OK",
|
|
33
33
|
"ENGINE_STATUS_TIMEOUT",
|
{validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/schematron/envelopes.py
RENAMED
|
@@ -2,27 +2,27 @@
|
|
|
2
2
|
Pydantic envelopes for the Schematron Advanced validator backend.
|
|
3
3
|
|
|
4
4
|
The Schematron validator is an Advanced validator — it runs in an isolated
|
|
5
|
-
container/Cloud Run Job rather than inside the Django worker.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
These schemas define the contract between Django and the Schematron
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
5
|
+
container/Cloud Run Job rather than inside the Django worker. Schematron
|
|
6
|
+
rules compile to XSLT (a full programming language), so author-uploaded
|
|
7
|
+
rules are executable code and only ever run inside the sandboxed container:
|
|
8
|
+
no database, no secrets, no network egress, locked-down engine
|
|
9
|
+
(ADR-2026-07-01, decisions D4/D8).
|
|
10
|
+
|
|
11
|
+
These schemas define the contract between Django and the Schematron
|
|
12
|
+
container:
|
|
13
|
+
|
|
14
|
+
- **Input envelope**: the XML submission (as an ``InputFileItem`` URI) plus
|
|
15
|
+
the author's Schematron rules **inline as text** — exactly how SHACL
|
|
16
|
+
ships its merged shapes text. Django resolves the rules from the step's
|
|
17
|
+
``Ruleset`` before dispatch; the container compiles them (SchXslt2 →
|
|
18
|
+
XSLT, baked into the image as fixed tooling) and runs the result over the
|
|
19
|
+
submission. The D8 resource limits ride along, already clamped
|
|
20
20
|
Django-side; the container re-clamps defensively.
|
|
21
21
|
- **Output envelope**: the parsed SVRL summary — per-severity counts, the
|
|
22
|
-
``finding_rule_ids_by_severity`` map, structured findings preserving
|
|
23
|
-
rule ids/locations — plus ``engine_status`` (D9: findings are only
|
|
24
|
-
meaningful when the engine actually ran) and the
|
|
25
|
-
|
|
22
|
+
``finding_rule_ids_by_severity`` map, structured findings preserving
|
|
23
|
+
native rule ids/locations — plus ``engine_status`` (D9: findings are only
|
|
24
|
+
meaningful when the engine actually ran) and provenance (the sha256 of
|
|
25
|
+
the executed rules, the detected query binding, and the engine that ran).
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
28
|
from __future__ import annotations
|
|
@@ -49,10 +49,15 @@ ENGINE_STATUS_TIMEOUT = "timeout"
|
|
|
49
49
|
|
|
50
50
|
# Machine hints for ``SchematronOutputs.engine_error_code`` — Django maps
|
|
51
51
|
# these to its reserved ``schematron.*`` finding codes.
|
|
52
|
-
|
|
52
|
+
#
|
|
53
|
+
# ``rules_invalid``: the author's uploaded Schematron failed to compile.
|
|
54
|
+
# That's a workflow-authoring problem, not a fact about the submitted
|
|
55
|
+
# document — the submitter's run still reads "the check couldn't run".
|
|
56
|
+
ENGINE_ERROR_RULES_INVALID = "rules_invalid"
|
|
53
57
|
ENGINE_ERROR_BACKEND_UNAVAILABLE = "backend_unavailable"
|
|
54
58
|
|
|
55
|
-
#
|
|
59
|
+
# Schematron query bindings (declared by the .sch root's ``queryBinding``
|
|
60
|
+
# attribute; the container detects and echoes it for provenance).
|
|
56
61
|
QUERY_BINDING_XSLT1 = "xslt1"
|
|
57
62
|
QUERY_BINDING_XSLT2 = "xslt2"
|
|
58
63
|
|
|
@@ -60,38 +65,26 @@ QUERY_BINDING_XSLT2 = "xslt2"
|
|
|
60
65
|
class SchematronInputs(BaseModel):
|
|
61
66
|
"""Schematron run configuration resolved by Django for the container.
|
|
62
67
|
|
|
63
|
-
Everything the container needs without database access:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
Everything the container needs without database access: the author's
|
|
69
|
+
rules inline (the SHACL ``shapes_text`` pattern — a ``.sch`` is a text
|
|
70
|
+
document, typically tens to hundreds of KB) and the D8 resource limits
|
|
71
|
+
(already clamped Django-side; re-clamped in the container).
|
|
67
72
|
"""
|
|
68
73
|
|
|
69
|
-
|
|
70
|
-
pack_version: str = Field(description="Pinned version of the rule pack.")
|
|
71
|
-
artifact_uri: str = Field(
|
|
74
|
+
schematron_text: str = Field(
|
|
72
75
|
description=(
|
|
73
|
-
"
|
|
74
|
-
"
|
|
76
|
+
"The Schematron source (.sch) to compile and run, resolved from "
|
|
77
|
+
"the step's Ruleset by Django. The container compiles it with "
|
|
78
|
+
"the SchXslt2 transpiler baked into the image."
|
|
75
79
|
),
|
|
76
80
|
)
|
|
77
|
-
|
|
81
|
+
schematron_sha256: str = Field(
|
|
82
|
+
default="",
|
|
78
83
|
description=(
|
|
79
|
-
"sha256 of
|
|
80
|
-
"
|
|
84
|
+
"sha256 of schematron_text, computed by Django at dispatch — "
|
|
85
|
+
"the provenance identity of the rules this run executed."
|
|
81
86
|
),
|
|
82
87
|
)
|
|
83
|
-
source_sha256: str = Field(
|
|
84
|
-
default="",
|
|
85
|
-
description="sha256 of the pinned .sch source (provenance echo).",
|
|
86
|
-
)
|
|
87
|
-
query_binding: str = Field(
|
|
88
|
-
default=QUERY_BINDING_XSLT2,
|
|
89
|
-
description="Schematron query binding of the pack: xslt1 | xslt2.",
|
|
90
|
-
)
|
|
91
|
-
engine: str = Field(
|
|
92
|
-
default="saxonc-he",
|
|
93
|
-
description="XSLT engine the pack requires (e.g. saxonc-he).",
|
|
94
|
-
)
|
|
95
88
|
|
|
96
89
|
# ── D8 resource limits (clamped Django-side; re-clamped in container) ──
|
|
97
90
|
max_input_bytes: int = Field(default=10_000_000, gt=0)
|
|
@@ -108,7 +101,7 @@ class SchematronFinding(BaseModel):
|
|
|
108
101
|
|
|
109
102
|
Both ``svrl:failed-assert`` and ``svrl:successful-report`` entries are
|
|
110
103
|
active findings (a ``<report>`` can carry a publisher-authored error).
|
|
111
|
-
``rule_id`` is the
|
|
104
|
+
``rule_id`` is the rule's native identifier (``BR-CO-15``,
|
|
112
105
|
``PEPPOL-EN16931-R010``) and becomes ``ValidationFinding.code`` in
|
|
113
106
|
Django; ``location_xpath`` preserves the SVRL ``@location`` so the
|
|
114
107
|
finding can point at the offending element. ``flag``/``role`` carry the
|
|
@@ -155,7 +148,7 @@ class SchematronOutputs(BaseModel):
|
|
|
155
148
|
engine_error_code: str = Field(
|
|
156
149
|
default="",
|
|
157
150
|
description=(
|
|
158
|
-
"Machine hint for the failure kind (e.g.
|
|
151
|
+
"Machine hint for the failure kind (e.g. rules_invalid, "
|
|
159
152
|
"backend_unavailable); maps to Django's reserved codes."
|
|
160
153
|
),
|
|
161
154
|
)
|
|
@@ -200,18 +193,21 @@ class SchematronOutputs(BaseModel):
|
|
|
200
193
|
description="How many findings the cap suppressed (counts stay full).",
|
|
201
194
|
)
|
|
202
195
|
|
|
203
|
-
# ── Provenance of the
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
196
|
+
# ── Provenance of the executed rules + engine (D5) ──
|
|
197
|
+
schematron_sha256: str = Field(
|
|
198
|
+
default="",
|
|
199
|
+
description="sha256 of the Schematron source that was executed.",
|
|
200
|
+
)
|
|
201
|
+
query_binding: str = Field(
|
|
208
202
|
default="",
|
|
209
|
-
description=
|
|
203
|
+
description=(
|
|
204
|
+
"Query binding detected from the .sch root (xslt1/xslt2/…), "
|
|
205
|
+
"echoed for provenance."
|
|
206
|
+
),
|
|
210
207
|
)
|
|
211
|
-
query_binding: str = Field(default="")
|
|
212
208
|
engine: str = Field(
|
|
213
209
|
default="",
|
|
214
|
-
description="Engine name + version that ran, e.g. 'SaxonC-HE 12.
|
|
210
|
+
description="Engine name + version that ran, e.g. 'SaxonC-HE 12.9'.",
|
|
215
211
|
)
|
|
216
212
|
|
|
217
213
|
execution_seconds: float = Field(default=0.0, ge=0)
|
|
@@ -259,8 +255,7 @@ def build_schematron_input_envelope(
|
|
|
259
255
|
org_id / org_name: Organization identity.
|
|
260
256
|
workflow_id / step_id / step_name: Workflow context.
|
|
261
257
|
submission_uri: Storage URI for the XML submission (gs:// or file://).
|
|
262
|
-
inputs: Fully-resolved ``SchematronInputs`` (
|
|
263
|
-
reference + checksums + limits).
|
|
258
|
+
inputs: Fully-resolved ``SchematronInputs`` (inline rules + limits).
|
|
264
259
|
callback_url: URL the container POSTs to on completion.
|
|
265
260
|
execution_bundle_uri: Base URI for this run's bundle.
|
|
266
261
|
callback_id: Idempotency key echoed back in the callback.
|
|
@@ -152,9 +152,7 @@ def parse_svrl(
|
|
|
152
152
|
raise SvrlParseError("Empty SVRL content.")
|
|
153
153
|
|
|
154
154
|
raw = (
|
|
155
|
-
svrl_content.encode("utf-8")
|
|
156
|
-
if isinstance(svrl_content, str)
|
|
157
|
-
else svrl_content
|
|
155
|
+
svrl_content.encode("utf-8") if isinstance(svrl_content, str) else svrl_content
|
|
158
156
|
)
|
|
159
157
|
try:
|
|
160
158
|
root = SafeET.fromstring(raw, forbid_dtd=True)
|
|
@@ -192,13 +190,10 @@ def parse_svrl(
|
|
|
192
190
|
existing is None
|
|
193
191
|
or _SEVERITY_RANK[finding.severity] < _SEVERITY_RANK[existing]
|
|
194
192
|
):
|
|
195
|
-
summary.finding_rule_ids_by_severity[finding.rule_id] =
|
|
196
|
-
finding.severity
|
|
197
|
-
)
|
|
193
|
+
summary.finding_rule_ids_by_severity[finding.rule_id] = finding.severity
|
|
198
194
|
else:
|
|
199
195
|
logger.warning(
|
|
200
|
-
"SVRL %s finding has no @id; kept without a rule id "
|
|
201
|
-
"(message=%r)",
|
|
196
|
+
"SVRL %s finding has no @id; kept without a rule id (message=%r)",
|
|
202
197
|
finding.element,
|
|
203
198
|
finding.message[:120],
|
|
204
199
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/energyplus/envelopes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/validations/__init__.py
RENAMED
|
File without changes
|
{validibot_shared-0.11.0 → validibot_shared-0.12.0}/validibot_shared/validations/envelopes.py
RENAMED
|
File without changes
|