440 lines
19 KiB
Python
440 lines
19 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
lager_yaml_export.py - Lagerplanung aus BricsCAD -> Overlay-YAML fuer das RFQ-Programm.
|
|
|
|
Liest die von LAGER_EXPORT (Lisp/LagerInsert.lsp) geschriebene Rohdatei
|
|
<zeichnung>_lager_raw.json und erzeugt daraus
|
|
|
|
<projekt>_drawing.yaml gleiches Schema wie die Hand-YAML (z.B.
|
|
cfg/example_goh_storage.yaml), aber NUR mit den
|
|
Feldern, die die Zeichnung liefert, plus
|
|
_provenance (Herkunft/Formel je Wert),
|
|
_conflicts (Abweichungen zur Hand-YAML) und
|
|
_warnings.
|
|
<projekt>_nachweis.html interner Mengen-Nachweis (optional, --nachweis)
|
|
|
|
Merge-Vertrag (umgesetzt im RFQ-Repo): Hand-Wert vorhanden und nicht null ->
|
|
Hand gewinnt; sonst Zeichnungswert. Abweichungen erscheinen in _conflicts.
|
|
|
|
Python aggregiert nur - jede fachliche Regel lebt in LISP
|
|
(Lisp/LagerPruefung.lsp, siehe doc/plan/lager_rfq.md).
|
|
|
|
Aufruf:
|
|
python lager_yaml_export.py <lager_raw.json> <out_drawing.yaml>
|
|
[--hand <hand.yaml>] [--nachweis <out.html>]
|
|
"""
|
|
|
|
import argparse
|
|
import html
|
|
import json
|
|
import sys
|
|
from collections import Counter, OrderedDict
|
|
|
|
import yaml
|
|
|
|
# Vergleichstoleranz Hand <-> Zeichnung: relativ 1 % ODER absolut 0,005
|
|
REL_TOL = 0.01
|
|
ABS_TOL = 0.005
|
|
|
|
|
|
# ------------------------------------------------------------------ Einlesen
|
|
|
|
def lade_raw(pfad):
|
|
"""Rohdaten laden. AutoLISP schreibt im ANSI-Codepage -> cp1252-Rueckfall."""
|
|
with open(pfad, "rb") as f:
|
|
roh = f.read()
|
|
try:
|
|
text = roh.decode("utf-8")
|
|
except UnicodeDecodeError:
|
|
text = roh.decode("cp1252")
|
|
return json.loads(text)
|
|
|
|
|
|
def _zahl(v, vorgabe=0.0):
|
|
return float(v) if isinstance(v, (int, float)) else vorgabe
|
|
|
|
|
|
def _m(mm, stellen=3):
|
|
return round(_zahl(mm) / 1000.0, stellen)
|
|
|
|
|
|
def _m2(mm2, stellen=1):
|
|
return round(_zahl(mm2) / 1e6, stellen)
|
|
|
|
|
|
# ------------------------------------------------------------------ Aggregation
|
|
|
|
class Sammler:
|
|
"""Werte mit Herkunft sammeln: setze() schreibt in das Overlay und die Provenienz."""
|
|
|
|
def __init__(self):
|
|
self.provenance = OrderedDict()
|
|
self.warnungen = []
|
|
|
|
def herkunft(self, pfad, wert, quelle, formel):
|
|
self.provenance[pfad] = {"wert": wert, "quelle": quelle, "formel": formel}
|
|
return wert
|
|
|
|
def warnung(self, text):
|
|
if text not in self.warnungen:
|
|
self.warnungen.append(text)
|
|
|
|
|
|
def _haeufigster(werte, pfad, s):
|
|
"""Haeufigster Wert; bei mehreren verschiedenen Werten eine Warnung."""
|
|
werte = [w for w in werte if w is not None]
|
|
if not werte:
|
|
return None
|
|
zaehlung = Counter(werte)
|
|
wert, _ = zaehlung.most_common(1)[0]
|
|
if len(zaehlung) > 1:
|
|
s.warnung("%s: Bereiche mit unterschiedlichen Werten %s - haeufigster Wert %s uebernommen"
|
|
% (pfad, sorted(zaehlung), wert))
|
|
return wert
|
|
|
|
|
|
def _haengeebenen(reihen):
|
|
"""Laenge je Haengeebene (Schienenhoehe, Warenlaenge) summieren.
|
|
|
|
Jede Reihe traegt ihre Laenge in jede ihrer Haengeebenen ein -
|
|
rack_length_m ist die Summe aller Reihen der Ebene.
|
|
"""
|
|
summen = OrderedDict()
|
|
for r in reihen:
|
|
for e in r.get("haengeebenen") or []:
|
|
key = (_zahl(e.get("schienenhoehe_mm")), _zahl(e.get("warenlaenge_mm")))
|
|
summen[key] = summen.get(key, 0.0) + _zahl(r.get("laenge_mm"))
|
|
return [{"rail_height_m": _m(h), "rack_length_m": round(l / 1000.0, 1), "goods_length_m": _m(w)}
|
|
for (h, w), l in sorted(summen.items(), key=lambda kv: -kv[0][0])]
|
|
|
|
|
|
def aggregiere(raw):
|
|
"""Rohdaten -> (overlay, sammler). overlay enthaelt nur Zeichnungswerte."""
|
|
s = Sammler()
|
|
for w in raw.get("warnungen") or []:
|
|
s.warnung(w)
|
|
|
|
belag_yaml = raw.get("belag_yaml") or {}
|
|
reihen_als_gitter = bool(raw.get("reihen_als_gitterrost"))
|
|
geschosse = sorted(raw.get("geschosse") or [],
|
|
key=lambda g: (_zahl(g.get("hoehe_belag_mm")), g.get("nr") or 0))
|
|
bereiche = raw.get("bereiche") or []
|
|
alle_reihen = list(raw.get("reihen") or [])
|
|
racks = raw.get("goh_racks") or []
|
|
gaenge = raw.get("gaenge") or []
|
|
|
|
# goh_rack-Bloecke zaehlen wie Regalreihen (Herkunft wird mitgefuehrt)
|
|
for r in racks:
|
|
alle_reihen.append(dict(r, quelle="goh_rack"))
|
|
ohne_geschoss = [r for r in alle_reihen if r.get("geschoss") is None]
|
|
if ohne_geschoss and geschosse:
|
|
s.warnung("%d Regalreihe(n) ohne Geschoss - nicht in levels enthalten" % len(ohne_geschoss))
|
|
|
|
overlay = OrderedDict()
|
|
|
|
# ---------------------------------------------------------- layout
|
|
layout = OrderedDict()
|
|
if bereiche:
|
|
quelle = "LG_BEREICH (%d Bereich(e))" % len(bereiche)
|
|
felder = [
|
|
("aisle_width_m", "gangbreite_mm", lambda v: _m(v), "GANGBREITE_mm / 1000"),
|
|
("rack_rows_per_aisle", "reihen_je_gang", int, "REIHEN_JE_GANG"),
|
|
("rack_depth_m", "regaltiefe_mm", lambda v: _m(v), "REGALTIEFE_mm / 1000"),
|
|
("trolley_rails_per_aisle", "schienen_je_gang", int, "SCHIENEN_JE_GANG"),
|
|
]
|
|
for feld, key, conv, formel in felder:
|
|
w = _haeufigster([conv(b[key]) for b in bereiche if b.get(key) is not None],
|
|
"layout." + feld, s)
|
|
if w is not None:
|
|
layout[feld] = s.herkunft("layout." + feld, w, quelle, formel)
|
|
elif alle_reihen:
|
|
w = _haeufigster([_m(r["tiefe_mm"]) for r in alle_reihen if r.get("tiefe_mm") is not None],
|
|
"layout.rack_depth_m", s)
|
|
if w is not None:
|
|
layout["rack_depth_m"] = s.herkunft("layout.rack_depth_m", w,
|
|
"Regalreihen (Tiefe)", "haeufigste Reihentiefe")
|
|
|
|
# Gaenge je Geschoss: Anzahl und Summe der Laengen (nur Gaenge zwischen den
|
|
# Reihen, ART=gang - auch manuelle). Randgaenge entlang der Kontur zaehlen
|
|
# nicht als aisle, stecken aber im Belag (walkway) und werden im Detail genannt.
|
|
gang_je_gs = {}
|
|
rand_je_gs = {}
|
|
for g in gaenge:
|
|
if g.get("art") == "gang":
|
|
n, l = gang_je_gs.get(g.get("geschoss"), (0, 0.0))
|
|
gang_je_gs[g.get("geschoss")] = (n + 1, l + _zahl(g.get("laenge_mm")))
|
|
elif g.get("art") == "randgang":
|
|
rand_je_gs[g.get("geschoss")] = rand_je_gs.get(g.get("geschoss"), 0.0) + _zahl(g.get("laenge_mm"))
|
|
if gang_je_gs:
|
|
gs_max = max(gang_je_gs, key=lambda k: gang_je_gs[k][1])
|
|
detail = ", ".join("G%s: %d / %.1f m" % (k, v[0], v[1] / 1000.0)
|
|
for k, v in sorted(gang_je_gs.items(), key=lambda kv: str(kv[0])))
|
|
if rand_je_gs:
|
|
detail += "; ohne Randgaenge " + ", ".join(
|
|
"G%s: %.1f m" % (k, v / 1000.0)
|
|
for k, v in sorted(rand_je_gs.items(), key=lambda kv: str(kv[0])))
|
|
layout["aisles"] = s.herkunft("layout.aisles", gang_je_gs[gs_max][0],
|
|
"LG_GANG_* (ART=gang)",
|
|
"Anzahl Hauptgaenge, Geschoss mit der groessten Ganglaenge (%s)" % detail)
|
|
layout["aisle_length_m"] = s.herkunft("layout.aisle_length_m",
|
|
round(gang_je_gs[gs_max][1] / 1000.0, 1),
|
|
"LG_GANG_* (ART=gang)",
|
|
"Summe Hauptganglaengen je Ebene, groesste (%s)" % detail)
|
|
|
|
bboxen = [g["kontur_bbox"] for g in geschosse if g.get("kontur_bbox")]
|
|
if bboxen:
|
|
xmin = min(b[0] for b in bboxen)
|
|
ymin = min(b[1] for b in bboxen)
|
|
xmax = max(b[2] for b in bboxen)
|
|
ymax = max(b[3] for b in bboxen)
|
|
dx, dy = xmax - xmin, ymax - ymin
|
|
layout["block_length_m"] = s.herkunft("layout.block_length_m", round(max(dx, dy) / 1000.0, 2),
|
|
"Belagskonturen LG_GESCHOSS",
|
|
"achsparallele Huelle aller Konturen, laengere Seite")
|
|
layout["block_width_m"] = s.herkunft("layout.block_width_m", round(min(dx, dy) / 1000.0, 2),
|
|
"Belagskonturen LG_GESCHOSS",
|
|
"achsparallele Huelle aller Konturen, kuerzere Seite")
|
|
if layout:
|
|
overlay["layout"] = layout
|
|
|
|
# ---------------------------------------------------------- levels
|
|
levels = []
|
|
for i, g in enumerate(geschosse):
|
|
p = "levels[%d]" % i
|
|
nr = g.get("nr")
|
|
lv = OrderedDict()
|
|
if g.get("bezeichnung"):
|
|
lv["remark"] = s.herkunft(p + ".remark", g["bezeichnung"], "LG_GESCHOSS %s" % nr, "BEZEICHNUNG")
|
|
lv["floor_elevation_m"] = s.herkunft(p + ".floor_elevation_m", _m(g.get("hoehe_belag_mm")),
|
|
"LG_GESCHOSS %s" % nr, "HOEHE_BELAG_mm / 1000")
|
|
if g.get("lichte_hoehe_mm") is not None:
|
|
lv["clear_height_m"] = s.herkunft(p + ".clear_height_m", _m(g["lichte_hoehe_mm"]),
|
|
"LG_GESCHOSS %s" % nr, "LICHTE_HOEHE_mm (Vorgabe)")
|
|
elif i + 1 < len(geschosse):
|
|
nx = geschosse[i + 1]
|
|
lichte = _zahl(nx.get("hoehe_belag_mm")) - _zahl(nx.get("konstr_hoehe_mm")) \
|
|
- _zahl(g.get("hoehe_belag_mm"))
|
|
lv["clear_height_m"] = s.herkunft(
|
|
p + ".clear_height_m", _m(lichte), "LG_GESCHOSS %s / %s" % (nr, nx.get("nr")),
|
|
"Belag darueber (%s) - Konstruktionshoehe darueber (%s) - eigener Belag (%s), mm"
|
|
% (nx.get("hoehe_belag_mm"), nx.get("konstr_hoehe_mm"), g.get("hoehe_belag_mm")))
|
|
else:
|
|
s.warnung("Geschoss %s (oberstes): clear_height_m nicht berechenbar - LICHTE_HOEHE_mm setzen" % nr)
|
|
|
|
reihen = [r for r in alle_reihen if r.get("geschoss") == nr]
|
|
hl = _haengeebenen(reihen)
|
|
if hl:
|
|
lv["hanging_levels"] = hl
|
|
n_rack = sum(1 for r in reihen if r.get("quelle") == "goh_rack")
|
|
for j, e in enumerate(hl):
|
|
s.herkunft("%s.hanging_levels[%d].rack_length_m" % (p, j), e["rack_length_m"],
|
|
"%d Regalreihe(n) Geschoss %s (davon %d goh_rack, Haengeebenen aus Vorgabe)"
|
|
% (len(reihen), nr, n_rack),
|
|
"Summe LAENGE aller Reihen mit Schienenhoehe %s / Warenlaenge %s"
|
|
% (e["rail_height_m"], e["goods_length_m"]))
|
|
|
|
spec = belag_yaml.get(g.get("belag_art") or "", g.get("belag_art"))
|
|
if spec: # Buehne (kein Hallenboden)
|
|
gs_gaenge = [x for x in gaenge if x.get("geschoss") == nr]
|
|
belag = sum(_zahl(x.get("laenge_mm")) * _zahl(x.get("breite_mm"))
|
|
for x in gs_gaenge if x.get("belag") != "gitterrost")
|
|
gitter = sum(_zahl(x.get("laenge_mm")) * _zahl(x.get("breite_mm"))
|
|
for x in gs_gaenge if x.get("belag") == "gitterrost")
|
|
reihen_fl = sum(_zahl(r.get("laenge_mm")) * _zahl(r.get("tiefe_mm")) for r in reihen)
|
|
if reihen_als_gitter:
|
|
gitter += reihen_fl
|
|
if gs_gaenge:
|
|
formel = "Summe LAENGE x BREITE der Gaenge/Quergaenge mit Belag"
|
|
quelle = "LG_GANG_* Geschoss %s, %d Flaeche(n)" % (nr, len(gs_gaenge))
|
|
else:
|
|
# Ohne Gangflaechen: Belag = Kontur minus Reihengrundflaeche
|
|
belag = _zahl(g.get("kontur_flaeche_mm2")) - reihen_fl
|
|
formel = "Konturflaeche - Grundflaeche der Regalreihen (keine LG_GANG-Flaechen)"
|
|
quelle = "Belagskontur Geschoss %s" % nr
|
|
lv["walkway"] = OrderedDict([
|
|
("area_m2", s.herkunft(p + ".walkway.area_m2", _m2(belag), quelle, formel)),
|
|
("spec", s.herkunft(p + ".walkway.spec", spec, "LG_GESCHOSS %s" % nr, "BELAG_ART")),
|
|
])
|
|
lv["grating"] = s.herkunft(
|
|
p + ".grating", _m2(gitter), "Geschoss %s" % nr,
|
|
"Gaenge mit Belag gitterrost" + (" + Grundflaeche der Regalreihen (Laenge x Tiefe)"
|
|
if reihen_als_gitter else ""))
|
|
levels.append(lv)
|
|
if levels:
|
|
overlay["levels"] = levels
|
|
return overlay, s
|
|
|
|
|
|
# ------------------------------------------------------------------ Abgleich
|
|
|
|
def _gleich(a, b):
|
|
if isinstance(a, bool) or isinstance(b, bool):
|
|
return a == b
|
|
if isinstance(a, (int, float)) and isinstance(b, (int, float)):
|
|
return abs(a - b) <= max(ABS_TOL, REL_TOL * max(abs(a), abs(b)))
|
|
return str(a).strip() == str(b).strip()
|
|
|
|
|
|
def _blaetter(obj, pfad=""):
|
|
"""(pfad, wert) aller Blattwerte; Listen per Index, _-Schluessel ausgenommen."""
|
|
if isinstance(obj, dict):
|
|
for k, v in obj.items():
|
|
if str(k).startswith("_"):
|
|
continue
|
|
yield from _blaetter(v, "%s.%s" % (pfad, k) if pfad else str(k))
|
|
elif isinstance(obj, list):
|
|
for i, v in enumerate(obj):
|
|
yield from _blaetter(v, "%s[%d]" % (pfad, i))
|
|
else:
|
|
yield pfad, obj
|
|
|
|
|
|
def _hole(obj, pfad):
|
|
"""Wert unter pfad ("levels[1].walkway.area_m2"); (gefunden, wert)."""
|
|
teile = []
|
|
for t in pfad.split("."):
|
|
while "[" in t:
|
|
name, rest = t.split("[", 1)
|
|
if name:
|
|
teile.append(name)
|
|
idx, t = rest.split("]", 1)
|
|
teile.append(int(idx))
|
|
if t:
|
|
teile.append(t)
|
|
cur = obj
|
|
for t in teile:
|
|
if isinstance(t, int):
|
|
if not isinstance(cur, list) or t >= len(cur):
|
|
return False, None
|
|
cur = cur[t]
|
|
else:
|
|
if not isinstance(cur, dict) or t not in cur:
|
|
return False, None
|
|
cur = cur[t]
|
|
return True, cur
|
|
|
|
|
|
def abgleich(overlay, hand):
|
|
"""Overlay gegen Hand-YAML: (konflikte, fuellt).
|
|
|
|
konflikte = Hand hat einen anderen Wert (Hand gewinnt beim Merge),
|
|
fuellt = Hand-Feld fehlt oder ist null (Zeichnung fuellt es).
|
|
"""
|
|
konflikte, fuellt = [], []
|
|
for pfad, wert in _blaetter(overlay):
|
|
gefunden, h = _hole(hand, pfad)
|
|
if not gefunden or h is None:
|
|
fuellt.append({"pfad": pfad, "zeichnung": wert})
|
|
elif not isinstance(h, (dict, list)) and not _gleich(h, wert):
|
|
k = {"pfad": pfad, "hand": h, "zeichnung": wert}
|
|
if isinstance(h, (int, float)) and isinstance(wert, (int, float)) and h:
|
|
k["abweichung_prozent"] = round((wert - h) / abs(h) * 100.0, 1)
|
|
konflikte.append(k)
|
|
n_hand = len(hand.get("levels") or []) if isinstance(hand, dict) else 0
|
|
n_zg = len(overlay.get("levels") or [])
|
|
if n_hand and n_zg and n_hand != n_zg:
|
|
konflikte.append({"pfad": "levels", "hand": "%d Ebenen" % n_hand,
|
|
"zeichnung": "%d Ebenen" % n_zg})
|
|
return konflikte, fuellt
|
|
|
|
|
|
# ------------------------------------------------------------------ Ausgabe
|
|
|
|
def _yaml_text(daten):
|
|
def _dict_repr(dumper, d):
|
|
return dumper.represent_mapping("tag:yaml.org,2002:map", d.items())
|
|
yaml.SafeDumper.add_representer(OrderedDict, _dict_repr)
|
|
return yaml.safe_dump(daten, sort_keys=False, allow_unicode=True,
|
|
default_flow_style=False, width=120)
|
|
|
|
|
|
def baue_ausgabe(raw, hand=None):
|
|
overlay, s = aggregiere(raw)
|
|
ausgabe = OrderedDict(overlay)
|
|
ausgabe["_provenance"] = s.provenance
|
|
konflikte, fuellt = abgleich(overlay, hand) if hand is not None else ([], [])
|
|
ausgabe["_conflicts"] = konflikte
|
|
ausgabe["_warnings"] = s.warnungen
|
|
return ausgabe, fuellt
|
|
|
|
|
|
def schreibe_yaml(pfad, ausgabe, raw):
|
|
kopf = ("# Overlay aus der Zeichnung - erzeugt von lib/lager_yaml_export.py\n"
|
|
"# Zeichnung: %s (%s)\n"
|
|
"# Merge im RFQ-Programm: Hand-YAML gewinnt, diese Werte fuellen nur fehlende/null-Felder.\n"
|
|
"# _provenance/_conflicts/_warnings sind Zusatzinformation und kein Teil des RFQ-Schemas.\n"
|
|
% (raw.get("zeichnung", "?"), raw.get("erzeugt", "?")))
|
|
with open(pfad, "w", encoding="utf-8", newline="\n") as f:
|
|
f.write(kopf)
|
|
f.write(_yaml_text(ausgabe))
|
|
|
|
|
|
def schreibe_nachweis(pfad, ausgabe, fuellt, raw):
|
|
e = html.escape
|
|
|
|
def tabelle(kopf, zeilen):
|
|
z = "".join("<tr>%s</tr>" % "".join("<td>%s</td>" % e(str(c)) for c in r) for r in zeilen)
|
|
return "<table><thead><tr>%s</tr></thead><tbody>%s</tbody></table>" % (
|
|
"".join("<th>%s</th>" % e(k) for k in kopf), z or "<tr><td colspan=9>-</td></tr>")
|
|
|
|
teile = [
|
|
"<!doctype html><html lang='de'><head><meta charset='utf-8'><title>Lager-Nachweis</title>",
|
|
"<style>body{font-family:Segoe UI,Arial,sans-serif;margin:24px;color:#222}"
|
|
"table{border-collapse:collapse;margin:8px 0 24px}td,th{border:1px solid #bbb;padding:4px 8px;"
|
|
"text-align:left;vertical-align:top}th{background:#eee}.w{color:#a40}</style></head><body>",
|
|
"<h1>Lager-Nachweis (Mengen)</h1>",
|
|
"<p>Zeichnung: %s<br>Erzeugt: %s<br>Regelprofil: %s</p>" % (
|
|
e(str(raw.get("zeichnung"))), e(str(raw.get("erzeugt"))), e(str(raw.get("land")))),
|
|
"<p class='w'>Interner Nachweis - die Pruefung gegen ASR/DGUV/EN ISO 14122 folgt in Phase 4.</p>",
|
|
"<h2>Warnungen</h2>",
|
|
"<ul>%s</ul>" % ("".join("<li class='w'>%s</li>" % e(w) for w in ausgabe["_warnings"]) or "<li>keine</li>"),
|
|
"<h2>Werte aus der Zeichnung</h2>",
|
|
tabelle(["Feld", "Wert", "Quelle", "Formel"],
|
|
[(k, v["wert"], v["quelle"], v["formel"]) for k, v in ausgabe["_provenance"].items()]),
|
|
"<h2>Konflikte mit der Hand-YAML (Hand gewinnt)</h2>",
|
|
tabelle(["Feld", "Hand", "Zeichnung", "Abweichung %"],
|
|
[(k["pfad"], k["hand"], k["zeichnung"], k.get("abweichung_prozent", ""))
|
|
for k in ausgabe["_conflicts"]]),
|
|
"<h2>Von der Zeichnung gefuellte Felder (Hand leer/null)</h2>",
|
|
tabelle(["Feld", "Zeichnung"], [(f["pfad"], f["zeichnung"]) for f in fuellt]),
|
|
"</body></html>",
|
|
]
|
|
with open(pfad, "w", encoding="utf-8", newline="\n") as f:
|
|
f.write("\n".join(teile))
|
|
|
|
|
|
def main(argv=None):
|
|
ap = argparse.ArgumentParser(description="Lager-Rohdaten -> RFQ-Overlay-YAML")
|
|
ap.add_argument("raw", help="<zeichnung>_lager_raw.json aus LAGER_EXPORT")
|
|
ap.add_argument("yaml", help="Ziel: <projekt>_drawing.yaml")
|
|
ap.add_argument("--hand", help="Hand-YAML fuer den Abgleich (Konfliktliste)")
|
|
ap.add_argument("--nachweis", help="Ziel: interner Nachweis als HTML")
|
|
a = ap.parse_args(argv)
|
|
|
|
try:
|
|
raw = lade_raw(a.raw)
|
|
except (OSError, ValueError) as ex:
|
|
print("FEHLER: Rohdaten nicht lesbar: %s" % ex, file=sys.stderr)
|
|
return 2
|
|
hand = None
|
|
if a.hand:
|
|
try:
|
|
with open(a.hand, encoding="utf-8") as f:
|
|
hand = yaml.safe_load(f) or {}
|
|
except (OSError, yaml.YAMLError) as ex:
|
|
print("FEHLER: Hand-YAML nicht lesbar: %s" % ex, file=sys.stderr)
|
|
return 2
|
|
|
|
ausgabe, fuellt = baue_ausgabe(raw, hand)
|
|
schreibe_yaml(a.yaml, ausgabe, raw)
|
|
if a.nachweis:
|
|
schreibe_nachweis(a.nachweis, ausgabe, fuellt, raw)
|
|
print("Overlay: %s (%d Werte, %d Konflikt(e), %d Warnung(en))"
|
|
% (a.yaml, len(ausgabe["_provenance"]), len(ausgabe["_conflicts"]), len(ausgabe["_warnings"])))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|