iac/docs/architecture/to_open_exchange.py

107 lines
4.8 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))
out.append((node.get("id"), node.get("archimateElement"),
x, y, int(b.get("width", 120)), int(b.get("height", 55))))
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 in nodes:
out.append(f' <node identifier="{nid}" elementRef="{eref}" xsi:type="Element" '
f'x="{x}" y="{y}" w="{w}" h="{h}"/>')
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)}")