Skip to content

Commit d12c1ce

Browse files
committed
Added more schematron validation
Added a new schematron file to validate business rules (for example making azimuth/tilt/tracker_azimuth fields required/forbidden in rack depending on rack type)
1 parent 08fac87 commit d12c1ce

File tree

4 files changed

+108
-5
lines changed

4 files changed

+108
-5
lines changed

.github/workflows/validate_xml_with_xsd_and_schematron.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44

55
if __name__ == '__main__':
6-
assert len(sys.argv) == 5
7-
xml_filepath, xsd_filepath, sch_structure_filepath, sch_references_filepath = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
6+
assert len(sys.argv) == 6
7+
xml_filepath, xsd_filepath, sch_structure_filepath, sch_references_filepath, sch_business_filepath = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]
88

99
# Parse XML document once
1010
with open(xml_filepath, 'rb') as xml:
@@ -63,6 +63,26 @@
6363
else:
6464
print('Schematron references validation passed.')
6565

66+
# Validate against Business Rules Schematron
67+
with open(sch_business_filepath, 'rb') as sch:
68+
sch_doc = etree.parse(sch)
69+
schematron = isoschematron.Schematron(sch_doc, store_report=True)
70+
sch_business_valid = schematron.validate(xml_doc)
71+
72+
if not sch_business_valid:
73+
print('! Schematron business validation failed.')
74+
svrl = schematron.validation_report
75+
if svrl is not None:
76+
for failed in svrl.xpath('//svrl:failed-assert',
77+
namespaces={'svrl': 'http://purl.oclc.org/dsdl/svrl'}):
78+
location = failed.get('location', 'unknown')
79+
messages = failed.xpath('svrl:text/text()',
80+
namespaces={'svrl': 'http://purl.oclc.org/dsdl/svrl'})
81+
message = messages[0].strip() if messages else 'No message provided'
82+
print(f' - {location}: {message}')
83+
else:
84+
print('Schematron business validation passed.')
85+
6686
# Exit with error if any validation failed
67-
if not (xsd_valid and sch_structure_valid and sch_references_valid):
87+
if not (xsd_valid and sch_structure_valid and sch_references_valid and sch_business_valid):
6888
exit(1)

.github/workflows/validate_xml_with_xsd_and_schematron.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ jobs:
6767
schema/pvcollada_schema_0.1.xsd
6868
schema/pvcollada_structure_2.0.sch
6969
schema/pvcollada_references_2.0.sch
70+
schema/pvcollada_business_2.0.sch
7071
schema/collada_schema_1_5.xsd
7172
.github/workflows/validate_xml_with_xsd_and_schematron.py
7273
${{ matrix.xml_doc }}
@@ -79,4 +80,5 @@ jobs:
7980
"$GITHUB_WORKSPACE"/"${{ matrix.xml_doc }}" \
8081
pvcollada_schema_0.1.xsd \
8182
pvcollada_structure_2.0.sch \
82-
pvcollada_references_2.0.sch
83+
pvcollada_references_2.0.sch \
84+
pvcollada_business_2.0.sch

schema/pvcollada_business_2.0.sch

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
3+
4+
<title>PVCollada Business Rules - Conditional requirements and domain logic</title>
5+
6+
<ns prefix="pv" uri="http://www.example.com/pvcollada"/>
7+
<ns prefix="collada" uri="http://www.collada.org/2008/03/COLLADASchema"/>
8+
9+
<!-- Pattern: Rack field requirements based on rack_type -->
10+
<pattern id="rack_fixed_tilt_requirements">
11+
<rule context="pv:rack[pv:rack_type='fixed_tilt']">
12+
<!-- Required fields for fixed_tilt -->
13+
<assert test="pv:azimuth">
14+
Fixed tilt rack must have azimuth element
15+
</assert>
16+
<assert test="pv:tilt">
17+
Fixed tilt rack must have tilt element
18+
</assert>
19+
20+
<!-- Forbidden fields for fixed_tilt -->
21+
<assert test="not(pv:tracker_azimuth)">
22+
Fixed tilt rack must not have tracker_azimuth element
23+
</assert>
24+
<assert test="not(pv:tracker_slope)">
25+
Fixed tilt rack must not have tracker_slope element
26+
</assert>
27+
<assert test="not(pv:tracker_height)">
28+
Fixed tilt rack must not have tracker_height element
29+
</assert>
30+
</rule>
31+
</pattern>
32+
33+
<pattern id="rack_tracker_requirements">
34+
<rule context="pv:rack[pv:rack_type='tracker']">
35+
<!-- Required fields for tracker -->
36+
<assert test="pv:tracker_azimuth">
37+
Tracker rack must have tracker_azimuth element
38+
</assert>
39+
40+
<!-- Forbidden fields for tracker -->
41+
<assert test="not(pv:tilt)">
42+
Tracker rack must not have tilt element
43+
</assert>
44+
<assert test="not(pv:azimuth)">
45+
Tracker rack must not have azimuth element
46+
</assert>
47+
<assert test="not(pv:slope)">
48+
Tracker rack must not have slope element
49+
</assert>
50+
<assert test="not(pv:height_above_ground)">
51+
Tracker rack must not have height_above_ground element
52+
</assert>
53+
</rule>
54+
</pattern>
55+
56+
</schema>

schema/validate.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
XSD_FILE_PATH = os.path.join(SCRIPT_DIR, "pvcollada_schema_0.1.xsd")
1919
SCH_STRUCTURE_FILE = os.path.join(SCRIPT_DIR, "pvcollada_structure_2.0.sch")
2020
SCH_REFERENCES_FILE = os.path.join(SCRIPT_DIR, "pvcollada_references_2.0.sch")
21+
SCH_BUSINESS_FILE = os.path.join(SCRIPT_DIR, "pvcollada_business_2.0.sch")
2122

2223
# Get XML file from command line or use default
2324
if len(sys.argv) > 1:
@@ -29,6 +30,7 @@
2930
print(f"XSD file: {XSD_FILE_PATH}")
3031
print(f"SCH structure file: {SCH_STRUCTURE_FILE}")
3132
print(f"SCH references file: {SCH_REFERENCES_FILE}")
33+
print(f"SCH business file: {SCH_BUSINESS_FILE}")
3234
print("-" * 50)
3335

3436
# Parse XML document once
@@ -94,10 +96,33 @@
9496
print(f" - {location}")
9597
print(f" {message}")
9698

99+
# Validate against Business Rules Schematron
100+
print()
101+
with open(SCH_BUSINESS_FILE, "rb") as sch:
102+
sch_doc = etree.parse(sch)
103+
104+
schematron = isoschematron.Schematron(sch_doc, store_report=True)
105+
sch_business_valid = schematron.validate(xml_doc)
106+
107+
if sch_business_valid:
108+
print("Schematron business rules validation passed.")
109+
else:
110+
print("! Schematron business rules validation failed.")
111+
svrl = schematron.validation_report
112+
if svrl is not None:
113+
for failed in svrl.xpath('//svrl:failed-assert',
114+
namespaces={'svrl': 'http://purl.oclc.org/dsdl/svrl'}):
115+
location = failed.get('location', 'unknown')
116+
messages = failed.xpath('svrl:text/text()',
117+
namespaces={'svrl': 'http://purl.oclc.org/dsdl/svrl'})
118+
message = messages[0].strip() if messages else 'No message provided'
119+
print(f" - {location}")
120+
print(f" {message}")
121+
97122
# Summary
98123
print()
99124
print("=" * 50)
100-
if xsd_valid and sch_structure_valid and sch_references_valid:
125+
if xsd_valid and sch_structure_valid and sch_references_valid and sch_business_valid:
101126
print("✓ All validations passed")
102127
else:
103128
print("✗ Validation failed")

0 commit comments

Comments
 (0)