[ADD] Modul zum Erstellen von Hängewarenlagerbereichen und Stockwerken mit Attributsdefinition für den Export in ein Anforderungsdokument für Ausschreibungen
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user