Datu struktūra schemas/mk-noteikumi-0.1.xsd; rīki convert.py (vestnesis.lv un likumi.lv), build_all.py (lejuplāde, pārveide, pārbaude pret avota lapu), validate.py. Noteikumi Nr. 322 (2006), Nr. 982 (2006), Nr. 635 (2023), Nr. 821 (2023) no oficiālās publikācijas — likumi.lv slēgts, vēlākie grozījumi nav iestrādāti. Datu līgums contracts/B19-mk-noteikumi.odcs.yaml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016679RwmHsuTFfxt26wP6rk
78 lines
3.2 KiB
Python
78 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Pārbauda visas data/*.xml datnes:
|
|
1. atbilst XSD schemas/mk-noteikumi-0.1.xsd (arī identifikatoru unikalitāte);
|
|
2. datnes nosaukums = noteikumu identifikators, un identifikators = gads un numurs no Act;
|
|
3. Source/SHA256 = sources/<id>.txt sha256;
|
|
4. katrs teksts (Text, Note, Title, LegalBasis, Lead, Item, Line, Heading) ir avota teksta datnē tādā pašā secībā;
|
|
5. katra elementa identifikators sākas ar noteikumu identifikatoru, punkta identifikators atbilst drukātajam numuram.
|
|
|
|
python3 tools/validate.py → kopsavilkums; izejas kods 1, ja kāda pārbaude neizdodas
|
|
"""
|
|
import glob
|
|
import hashlib
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
from lxml import etree
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
NS = {"n": "urn:pppa:vpk:mk-noteikumi:0.1"}
|
|
SUPC = "⁰¹²³⁴⁵⁶⁷⁸⁹"
|
|
schema = etree.XMLSchema(etree.parse(f"{ROOT}/schemas/mk-noteikumi-0.1.xsd"))
|
|
|
|
|
|
def pid(num):
|
|
return "p" + re.sub("[" + SUPC + "]+", lambda m: "s" + m.group(0).translate(str.maketrans(SUPC, "0123456789")), num)
|
|
|
|
|
|
def check(path):
|
|
errs = []
|
|
doc = etree.parse(path)
|
|
if not schema.validate(doc):
|
|
errs += [f"XSD: {e.message}" for e in list(schema.error_log)[:5]]
|
|
r = doc.getroot()
|
|
did = r.get("id")
|
|
if os.path.basename(path) != f"{did}.xml":
|
|
errs.append(f"datnes nosaukums {os.path.basename(path)} ≠ {did}.xml")
|
|
num, adopted = r.findtext("n:Act/n:Number", namespaces=NS), r.findtext("n:Act/n:Adopted", namespaces=NS) or ""
|
|
if did != f"lv-mkn-{adopted[:4]}-{num}":
|
|
errs.append(f"identifikators {did} ≠ lv-mkn-{adopted[:4]}-{num}")
|
|
src = f"{ROOT}/{r.findtext('n:Source/n:File', namespaces=NS)}"
|
|
if not os.path.exists(src):
|
|
return errs + [f"nav avota datnes {src}"]
|
|
body = open(src, encoding="utf-8").read()
|
|
if hashlib.sha256(body.encode("utf-8")).hexdigest() != r.findtext("n:Source/n:SHA256", namespaces=NS):
|
|
errs.append("SHA256 nesakrīt ar avota datni")
|
|
pos, missing = 0, []
|
|
for e in r.iter():
|
|
if not isinstance(e.tag, str):
|
|
continue
|
|
tag = etree.QName(e).localname
|
|
if tag in ("Text", "Note", "LegalBasis", "Lead", "Line", "Heading") or (tag == "Title" and etree.QName(e.getparent()).localname != "Act"):
|
|
for part in (e.text or "").split("\n"):
|
|
i = body.find(part, pos)
|
|
if i < 0:
|
|
missing.append(f"{tag}: {part[:70]}")
|
|
else:
|
|
pos = i
|
|
if e is not r and e.get("id") and not e.get("id").startswith(did + "."):
|
|
errs.append(f"{e.get('id')} nesākas ar {did}")
|
|
if tag == "Point" and not e.get("n").endswith(")") and not e.get("id").endswith("." + pid(e.get("n"))):
|
|
errs.append(f"punkts n={e.get('n')} id={e.get('id')}")
|
|
errs += [f"nav avotā secībā — {m}" for m in missing[:5]]
|
|
return errs
|
|
|
|
|
|
if __name__ == "__main__":
|
|
bad = 0
|
|
for p in sorted(glob.glob(f"{ROOT}/data/*.xml")):
|
|
e = check(p)
|
|
r = etree.parse(p).getroot()
|
|
pts = len(r.findall(".//n:Point", NS))
|
|
print(f"{'OK ' if not e else 'FAIL'} {os.path.basename(p)} punkti {pts}")
|
|
for x in e:
|
|
print(" ", x)
|
|
bad += bool(e)
|
|
sys.exit(1 if bad else 0)
|