Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016679RwmHsuTFfxt26wP6rk
83 lines
4.2 KiB
Python
83 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""MK-Lemumi-as-Code pārbaudītājs.
|
|
|
|
python3 tools/validate.py [data/…xml …] [--registry …] [--pasvaldibas …] [--valdibas …]
|
|
|
|
Katrai sēdes datnei (pēc noklusējuma — visas data/lv-mk-sede-*.xml):
|
|
- atbilst shēmai schemas/mk-lemumi-0.1.xsd (arī atslēgas: jautājumu, lēmumu un punktu identifikatori unikāli);
|
|
- datnes nosaukums = sēdes identifikators; visi identifikatori sākas ar sēdes identifikatoru;
|
|
- valdība ir valdību reģistrā; VPK ID — Valsts institūciju reģistrā; ATVK kods — Pašvaldību reģistrā;
|
|
- katra lēmuma SourceFile ir repozitorijā, un katra punkta teksts tajā ir burtiski (rindā aiz punkta numura);
|
|
- termiņa datums ir derīgs un teksts „līdz …” ir punkta tekstā.
|
|
Reģistri pēc noklusējuma — no ProcessGit.
|
|
"""
|
|
import glob
|
|
import os
|
|
import sys
|
|
|
|
from lxml import etree
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
import convert # noqa: E402
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
N = {"m": convert.NS}
|
|
|
|
|
|
def ids_of(xml, local, attr):
|
|
return {e.get(attr) for e in etree.fromstring(xml).iter() if isinstance(e.tag, str) and etree.QName(e).localname == local}
|
|
|
|
|
|
def main(argv):
|
|
opt = {k: (argv[argv.index("--" + k) + 1] if "--" + k in argv else v) for k, v in convert.REG.items()}
|
|
files = [a for a in argv if a.endswith(".xml") and a not in opt.values()] or sorted(glob.glob(os.path.join(ROOT, "data", "lv-mk-sede-*.xml")))
|
|
orgs = ids_of(convert.load(opt["registry"]), "Organization", "id")
|
|
atvk = ids_of(convert.load(opt["pasvaldibas"]), "Municipality", "atvk")
|
|
govs = ids_of(convert.load(opt["valdibas"]), "Government", "id")
|
|
xsd = etree.XMLSchema(etree.parse(os.path.join(ROOT, "schemas", "mk-lemumi-0.1.xsd")))
|
|
errors, totals = [], [0, 0, 0]
|
|
for path in files:
|
|
tag = os.path.basename(path)
|
|
doc = etree.parse(path)
|
|
if not xsd.validate(doc):
|
|
errors += [f"{tag}: XSD {e.line}: {e.message}" for e in list(xsd.error_log)[:10]]
|
|
continue
|
|
r = doc.getroot()
|
|
mid = r.get("id")
|
|
if tag != mid + ".xml":
|
|
errors.append(f"{tag}: datnes nosaukumam jābūt {mid}.xml")
|
|
if r.get("government") not in govs:
|
|
errors.append(f"{tag}: valdība {r.get('government')} nav valdību reģistrā")
|
|
for e in r.iter():
|
|
if isinstance(e.tag, str) and e.get("id") and e.get("id") != mid and not e.get("id").startswith(mid + "."):
|
|
errors.append(f"{tag}: {e.get('id')} nesākas ar {mid}")
|
|
if isinstance(e.tag, str) and e.get("org") and e.get("org") not in orgs:
|
|
errors.append(f"{tag}: VPK ID {e.get('org')} nav Valsts institūciju reģistrā")
|
|
if isinstance(e.tag, str) and e.get("atvk") and e.get("atvk") not in atvk:
|
|
errors.append(f"{tag}: ATVK {e.get('atvk')} nav Pašvaldību reģistrā")
|
|
for it in r.iterfind("m:Section/m:Item", N):
|
|
totals[0] += 1
|
|
for d in it.iterfind("m:Decision", N):
|
|
totals[1] += 1
|
|
src = os.path.join(ROOT, d.findtext("m:SourceFile", namespaces=N))
|
|
if not os.path.exists(src):
|
|
errors.append(f"{tag}: {d.get('id')}: nav avota teksta {src}")
|
|
continue
|
|
lines = [ln.strip() for ln in open(src, encoding="utf-8")]
|
|
for p in d.iter(f"{{{convert.NS}}}Point"):
|
|
totals[2] += 1
|
|
want = ((p.get("n") + " ") if p.get("n") else "") + p.findtext("m:Text", namespaces=N)
|
|
if want not in lines:
|
|
errors.append(f"{tag}: {p.get('id')}: punkta teksts nav avota tekstā")
|
|
dl = p.find("m:Deadline", N)
|
|
if dl is not None and dl.text not in p.findtext("m:Text", namespaces=N):
|
|
errors.append(f"{tag}: {p.get('id')}: termiņa teksts nav punktā")
|
|
for e in errors:
|
|
print("KĻŪDA", e)
|
|
print(f"sēdes: {len(files)}, jautājumi: {totals[0]}, lēmumi: {totals[1]}, punkti: {totals[2]}, kļūdas: {len(errors)}")
|
|
return 1 if errors else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv[1:]))
|