"""Рендерит представления из .archimate в один самодостаточный HTML со встроенным SVG.
Нужен, чтобы схемы можно было посмотреть без установки Archi."""
import sys, io, pathlib, html, re
import xml.etree.ElementTree as ET
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
XSI = "{http://www.w3.org/2001/XMLSchema-instance}type"
src, dst = pathlib.Path(sys.argv[1]), pathlib.Path(sys.argv[2])
root = ET.parse(src).getroot()
LAYER = { # цвета Archi по слоям
"ApplicationComponent": "#B5FFFF", "ApplicationService": "#B5FFFF",
"Node": "#C9E7B7", "SystemSoftware": "#C9E7B7",
"TechnologyService": "#C9E7B7", "Artifact": "#C9E7B7",
"Grouping": "#F4F4F4",
}
STROKE = {"Application": "#2a7f8f", "Technology": "#4a7f3a", "Grouping": "#888"}
concepts, rels = {}, {}
for folder in root.findall("folder"):
for e in folder.findall("element"):
et = (e.get(XSI) or "").split(":")[-1]
if et.endswith("Relationship"):
rels[e.get("id")] = (et[:-len("Relationship")], e.get("source"), e.get("target"))
elif et != "ArchimateDiagramModel":
concepts[e.get("id")] = (et, e.get("name") or "",
(e.findtext("documentation") or "").strip())
def collect(node, ox, oy, nodes, conns, notes):
b = node.find("bounds")
x, y = ox + int(b.get("x", 0)), oy + int(b.get("y", 0))
w, h = int(b.get("width", 120)), int(b.get("height", 55))
if (node.get(XSI) or "") == "archimate:Note":
notes.append((node.findtext("content") or "", x, y, w, h))
return
nodes[node.get("id")] = (node.get("archimateElement"), x, y, w, h)
for c in node.findall("sourceConnection"):
conns.append((c.get("relationship"), c.get("source"), c.get("target")))
for ch in node.findall("child"):
collect(ch, x, y, nodes, conns, notes)
def tooltip(doc, limit=260):
"""Первый абзац описания, обрезанный по границе предложения: подсказка должна
давать понять, что это за элемент, а не пересказывать весь текст."""
s = doc.split("\n\n")[0].replace("\n", " ").replace("`", "").strip()
if len(s) <= limit:
return s
cut = s[:limit]
end = max(cut.rfind(". "), cut.rfind("; "))
return (cut[:end + 1] if end > limit // 2 else cut.rstrip() + "…")
def paragraphs(doc):
return [p.replace("\n", " ").strip() for p in doc.split("\n\n") if p.strip()]
def rich(p):
"""Описания пришли из markdown-файлов: обратные кавычки там означают код."""
return re.sub(r"`([^`]+)`", r" {html.escape(doc.text)} {total} компонентов в том же порядке, что и на карте A0. '
'Описания выведены не из манифестов контура, а из исходного кода компонентных '
'репозиториев, и потому проверяются чтением кода, а не сверкой с конфигурацией. '
'То же описание всплывает подсказкой, если задержать курсор на блоке любой схемы. {rich(p)} {rich(p)} описание не заполнено — см. {html.escape(doc or "манифесты")}\1", html.escape(p))
def wrap(s, width, limit=4):
out, line = [], ""
for word in s.split():
if len(line) + len(word) + 1 > width and line:
out.append(line); line = word
else:
line = f"{line} {word}".strip()
if line:
out.append(line)
return out[:limit]
def edge_point(x, y, w, h, tx, ty):
"""Точка пересечения луча к (tx,ty) с границей прямоугольника."""
cx, cy = x + w / 2, y + h / 2
dx, dy = tx - cx, ty - cy
if dx == 0 and dy == 0:
return cx, cy
sx = (w / 2) / abs(dx) if dx else float("inf")
sy = (h / 2) / abs(dy) if dy else float("inf")
s = min(sx, sy)
return cx + dx * s, cy + dy * s
MARKERS = """
{html.escape(v.get("name") or "")}
'
+ (f'Прикладной слой: описания компонентов
'
f'{html.escape(gname)}
'
for p in paragraphs(gdoc):
app_html += f''
for i in members:
_, name, doc = concepts[i]
# запасное значение сборщика — путь к манифестам, а не описание
body = ("" if doc.startswith("apps/") else
"".join(f"
"
app_html += "
Автогенерация из {html.escape(src.name)}. Зелёный — технологический слой,
голубой — прикладной. Пунктир с полым треугольником — realization, стрелка — serving,
ромб — composition, линия с шариком — assignment, полый ромб — aggregation. Подробнее — в представлении
«L1. Как читать схемы». Описание элемента всплывает подсказкой при наведении на блок.
{html.escape(p)}
' for p in paragraphs(purpose))} {''.join(parts)} {app_html} """ dst.write_text(page, encoding="utf-8", newline="\n") print(f"записано: {dst} ({len(parts)} представлений)")