Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016679RwmHsuTFfxt26wP6rk
141 lines
7.1 KiB
Python
141 lines
7.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Pašvaldību reģistra datņu sagatavošana.
|
|
|
|
python3 tools/build.py
|
|
|
|
Izveido:
|
|
data/pasvaldibas-atvk.xml reģistrs (shēma schemas/pasvaldibu-registrs-0.1.xsd) — galvenā datne
|
|
data/pasvaldibas-atvk.csv tas pats saraksts tabulā (apskatei ProcessGit un lietošanai izklājlapās)
|
|
exports/pasvaldibas-atvk-sdmx21.xml tas pats saraksts SDMX-ML 2.1 kodu saraksta (Codelist) formā
|
|
|
|
Unikālais identifikators — ATVK kods (7 cipari). Pašvaldībām VPK ID Valsts institūciju reģistrā nepiešķir.
|
|
|
|
Avots: MK 15.06.2021. noteikumi Nr. 379 „Administratīvo teritoriju un teritoriālā iedalījuma vienību klasifikatora
|
|
noteikumi”, pielikums, MK 18.08.2026. noteikumu Nr. 492 redakcijā (spēkā no 22.08.2026), https://likumi.lv/ta/id/324030
|
|
"""
|
|
import csv
|
|
import os
|
|
from xml.sax.saxutils import escape
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
VALSTSPILSETAS = [
|
|
("0001000", "Rīga"), ("0002000", "Daugavpils"), ("0003000", "Jelgava"),
|
|
("0004000", "Jūrmala"), ("0005000", "Liepāja"), ("0006000", "Rēzekne"),
|
|
("0007000", "Ventspils"),
|
|
]
|
|
NOVADI = [
|
|
("0020000", "Aizkraukles novads"), ("0021000", "Alūksnes novads"),
|
|
("0022000", "Augšdaugavas novads"), ("0023000", "Ādažu novads"),
|
|
("0024000", "Balvu novads"), ("0025000", "Bauskas novads"),
|
|
("0026000", "Cēsu novads"), ("0027000", "Dienvidkurzemes novads"),
|
|
("0028000", "Dobeles novads"), ("0029000", "Gulbenes novads"),
|
|
("0030000", "Jelgavas novads"), ("0031000", "Jēkabpils novads"),
|
|
("0032000", "Krāslavas novads"), ("0033000", "Kuldīgas novads"),
|
|
("0034000", "Ķekavas novads"), ("0035000", "Limbažu novads"),
|
|
("0036000", "Līvānu novads"), ("0037000", "Ludzas novads"),
|
|
("0038001", "Madonas novads"), ("0039000", "Mārupes novads"),
|
|
("0040000", "Ogres novads"), ("0041000", "Olaines novads"),
|
|
("0042000", "Preiļu novads"), ("0043000", "Rēzeknes novads"),
|
|
("0044000", "Ropažu novads"), ("0045000", "Salaspils novads"),
|
|
("0046000", "Saldus novads"), ("0047000", "Saulkrastu novads"),
|
|
("0048000", "Siguldas novads"), ("0049000", "Smiltenes novads"),
|
|
("0051000", "Talsu novads"), ("0052000", "Tukuma novads"),
|
|
("0053000", "Valkas novads"), ("0054000", "Valmieras novads"),
|
|
("0056000", "Ventspils novads"),
|
|
]
|
|
|
|
# Iepriekšējie ATVK kodi (teritorija mainīta)
|
|
FORMER = {"0038001": ["0038000"]}
|
|
|
|
ACT = ("MK 15.06.2021. noteikumi Nr. 379 „Administratīvo teritoriju un teritoriālā iedalījuma vienību klasifikatora "
|
|
"noteikumi”, pielikums, MK 18.08.2026. noteikumu Nr. 492 redakcijā")
|
|
URL = "https://likumi.lv/ta/id/324030"
|
|
IN_FORCE = "2026-08-22"
|
|
DATA_VERSION, DATA_DATE = 2, "2026-10-12"
|
|
NS = "urn:pppa:vpk:pasvaldibas:0.1"
|
|
|
|
|
|
def rows():
|
|
out = [(c, n, "valstspilseta") for c, n in VALSTSPILSETAS] + [(c, n, "novads") for c, n in NOVADI]
|
|
return [{"atvk": c, "nosaukums": n, "tips": k, "iepriekšējie_kodi": " ".join(FORMER.get(c, []))} for c, n, k in out]
|
|
|
|
|
|
def registry_xml(rs):
|
|
lines = ['<?xml version="1.0" encoding="UTF-8"?>',
|
|
f'<MunicipalityRegistry xmlns="{NS}" schemaVersion="0.1">',
|
|
' <Metadata>',
|
|
' <Title>Latvijas pašvaldību reģistrs</Title>',
|
|
' <Classification>ATVK — Administratīvo teritoriju un teritoriālā iedalījuma vienību klasifikators; '
|
|
'uztur Centrālā statistikas pārvalde</Classification>',
|
|
f' <Source inForce="{IN_FORCE}">',
|
|
f' <Act>{escape(ACT)}</Act>',
|
|
f' <Url>{URL}</Url>',
|
|
' </Source>',
|
|
f' <DataVersion number="{DATA_VERSION}" date="{DATA_DATE}">',
|
|
' <Change>2. versija: atribūts vpkId noņemts — pašvaldības identificē tikai ar ATVK kodu; Valsts institūciju reģistrā '
|
|
'resors 90 slēgts un tā identifikatori atsaukti.</Change>',
|
|
' <Change>1. versija: 42 pašvaldības (7 valstspilsētas, 35 novadi) no MK noteikumu Nr. 379 pielikuma.</Change>',
|
|
' </DataVersion>',
|
|
' </Metadata>']
|
|
for r in rs:
|
|
attrs = f'atvk="{r["atvk"]}" kind="{r["tips"]}"'
|
|
former = "".join(f"<FormerCode>{x}</FormerCode>" for x in FORMER.get(r["atvk"], []))
|
|
lines.append(f' <Municipality {attrs}><Name>{escape(r["nosaukums"])}</Name>{former}</Municipality>')
|
|
lines.append("</MunicipalityRegistry>")
|
|
return "\n".join(lines) + "\n"
|
|
|
|
|
|
def sdmx_xml(rs):
|
|
from lxml import etree
|
|
M, S, C = ("http://www.sdmx.org/resources/sdmxml/schemas/v2_1/" + x for x in ("message", "structure", "common"))
|
|
XL = "{http://www.w3.org/XML/1998/namespace}lang"
|
|
root = etree.Element(f"{{{M}}}Structure", nsmap={"mes": M, "str": S, "com": C})
|
|
h = etree.SubElement(root, f"{{{M}}}Header")
|
|
for tag, val in (("ID", f"CL_LV_PASVALDIBA_{IN_FORCE.replace('-', '')}"), ("Test", "false"),
|
|
("Prepared", f"{DATA_DATE}T00:00:00+03:00")):
|
|
etree.SubElement(h, f"{{{M}}}{tag}").text = val
|
|
etree.SubElement(h, f"{{{M}}}Sender", id="PPPA")
|
|
cls = etree.SubElement(etree.SubElement(root, f"{{{M}}}Structures"), f"{{{S}}}Codelists")
|
|
urn = "urn:sdmx:org.sdmx.infomodel.codelist"
|
|
cl = etree.SubElement(cls, f"{{{S}}}Codelist", id="CL_LV_PASVALDIBA", agencyID="PPPA", version="1.0",
|
|
isFinal="true", validFrom=f"{IN_FORCE}T00:00:00+03:00",
|
|
urn=f"{urn}.Codelist=PPPA:CL_LV_PASVALDIBA(1.0)")
|
|
|
|
def ann(parent, atype, text):
|
|
a = etree.SubElement(parent, f"{{{C}}}Annotation")
|
|
etree.SubElement(a, f"{{{C}}}AnnotationType").text = atype
|
|
t = etree.SubElement(a, f"{{{C}}}AnnotationText")
|
|
t.set(XL, "lv")
|
|
t.text = text
|
|
|
|
anns = etree.SubElement(cl, f"{{{C}}}Annotations")
|
|
ann(anns, "AVOTS", f"{ACT}. {URL}")
|
|
ann(anns, "PIEZIME", "Kods ir ATVK kods. Galvenā datne — data/pasvaldibas-atvk.xml (Pasvaldibu-registrs).")
|
|
for lang, name in (("lv", "Latvijas pašvaldības (ATVK)"), ("en", "Municipalities of Latvia (ATVK)")):
|
|
n = etree.SubElement(cl, f"{{{C}}}Name")
|
|
n.set(XL, lang)
|
|
n.text = name
|
|
for r in rs:
|
|
c = etree.SubElement(cl, f"{{{S}}}Code", id=r["atvk"], urn=f"{urn}.Code=PPPA:CL_LV_PASVALDIBA(1.0).{r['atvk']}")
|
|
a = etree.SubElement(c, f"{{{C}}}Annotations")
|
|
ann(a, "TIPS", "valstspilsēta" if r["tips"] == "valstspilseta" else "novads")
|
|
n = etree.SubElement(c, f"{{{C}}}Name")
|
|
n.set(XL, "lv")
|
|
n.text = r["nosaukums"]
|
|
return etree.tostring(root, xml_declaration=True, encoding="UTF-8", pretty_print=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
rs = rows()
|
|
with open(os.path.join(ROOT, "data", "pasvaldibas-atvk.xml"), "w", encoding="utf-8") as f:
|
|
f.write(registry_xml(rs))
|
|
with open(os.path.join(ROOT, "data", "pasvaldibas-atvk.csv"), "w", encoding="utf-8", newline="") as f:
|
|
w = csv.DictWriter(f, fieldnames=list(rs[0]))
|
|
w.writeheader()
|
|
w.writerows(rs)
|
|
os.makedirs(os.path.join(ROOT, "exports"), exist_ok=True)
|
|
with open(os.path.join(ROOT, "exports", "pasvaldibas-atvk-sdmx21.xml"), "wb") as f:
|
|
f.write(sdmx_xml(rs))
|
|
print(f"{len(VALSTSPILSETAS)} valstspilsētas + {len(NOVADI)} novadi = {len(rs)}")
|