iac/docs/architecture/to_open_exchange.py

115 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Конвертирует нативный .archimate (формат Archi) в The Open Group
ArchiMate Model Exchange File Format — его понимают сторонние инструменты.
Вложенность узлов на схемах разворачивается в плоский список с абсолютными
координатами: в обменном формате трактовка координат вложенных узлов
неоднозначна, а плоская раскладка даёт одинаковый результат везде.
Контейнер выводится раньше содержимого, поэтому визуально вложенность
сохраняется.
"""
import sys, io, pathlib
import xml.etree.ElementTree as ET
from xml.sax.saxutils import escape, quoteattr
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
XSI = "{http://www.w3.org/2001/XMLSchema-instance}type"
NS = "http://www.opengroup.org/xsd/archimate/3.0/"
src, dst = pathlib.Path(sys.argv[1]), pathlib.Path(sys.argv[2])
root = ET.parse(src).getroot()
def t(e):
return (e.get(XSI) or "").split(":")[-1]
def text(e, tag):
c = e.find(tag)
return c.text if c is not None and c.text else ""
elements, relationships, views = [], [], []
for folder in root.findall("folder"):
ftype = folder.get("type")
for e in folder.findall("element"):
et = t(e)
if ftype == "diagrams":
views.append(e)
elif et.endswith("Relationship"):
relationships.append((e.get("id"), et[:-len("Relationship")],
e.get("source"), e.get("target")))
else:
elements.append((e.get("id"), et, e.get("name") or "", text(e, "documentation")))
def flatten(node, ox=0, oy=0, out=None, conns=None):
"""Возвращает плоский список узлов с абсолютными координатами и соединения."""
out = [] if out is None else out
conns = [] if conns is None else conns
b = node.find("bounds")
x = ox + int(b.get("x", 0)); y = oy + int(b.get("y", 0))
# у надписей (Note) нет archimateElement — в обменном формате это узел типа Label
out.append((node.get("id"), node.get("archimateElement"),
x, y, int(b.get("width", 120)), int(b.get("height", 55)),
text(node, "content")))
for c in node.findall("sourceConnection"):
conns.append((c.get("id"), c.get("relationship"),
c.get("source"), c.get("target")))
for ch in node.findall("child"):
flatten(ch, x, y, out, conns)
return out, conns
out = ['<?xml version="1.0" encoding="UTF-8"?>',
f'<model xmlns="{NS}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
f' xsi:schemaLocation="{NS} https://www.opengroup.org/xsd/archimate/3.1/archimate3_Diagram.xsd"',
f' identifier="{root.get("id")}">',
f' <name xml:lang="ru">{escape(root.get("name") or "model")}</name>']
purpose = root.find("purpose")
if purpose is not None and purpose.text:
out.append(f' <documentation xml:lang="ru">{escape(purpose.text)}</documentation>')
out.append(" <elements>")
for eid, etype, name, doc in elements:
out.append(f' <element identifier="{eid}" xsi:type="{etype}">')
out.append(f' <name xml:lang="ru">{escape(name)}</name>')
if doc:
out.append(f' <documentation xml:lang="ru">{escape(doc)}</documentation>')
out.append(" </element>")
out.append(" </elements>")
out.append(" <relationships>")
for rid, rtype, s, tg in relationships:
out.append(f' <relationship identifier="{rid}" source="{s}" target="{tg}" xsi:type="{rtype}"/>')
out.append(" </relationships>")
out.append(" <views>")
out.append(" <diagrams>")
for v in views:
out.append(f' <view identifier="{v.get("id")}" xsi:type="Diagram">')
out.append(f' <name xml:lang="ru">{escape(v.get("name") or "")}</name>')
doc = text(v, "documentation")
if doc:
out.append(f' <documentation xml:lang="ru">{escape(doc)}</documentation>')
nodes, conns = [], []
for child in v.findall("child"):
flatten(child, 0, 0, nodes, conns)
for nid, eref, x, y, w, h, content in nodes:
if eref:
out.append(f' <node identifier="{nid}" elementRef="{eref}" xsi:type="Element" '
f'x="{x}" y="{y}" w="{w}" h="{h}"/>')
else:
out.append(f' <node identifier="{nid}" xsi:type="Label" '
f'x="{x}" y="{y}" w="{w}" h="{h}">')
out.append(f' <label xml:lang="ru">{escape(content)}</label>')
out.append(' </node>')
for cid, rref, s, tg in conns:
out.append(f' <connection identifier="{cid}" relationshipRef="{rref}" '
f'xsi:type="Relationship" source="{s}" target="{tg}"/>')
out.append(" </view>")
out.append(" </diagrams>")
out.append(" </views>")
out.append("</model>")
dst.write_text("\n".join(out) + "\n", encoding="utf-8", newline="\n")
print(f"записано: {dst}")
print(f" элементов: {len(elements)}")
print(f" отношений: {len(relationships)}")
print(f" представлений: {len(views)}")