Personen-Belegung pro Raum + Stempel-Aggregation
Architekten-Workflow: bei Schulen/Buero/Versammlungsstaetten muss die Personenzahl pro Raum erfasst werden (SIA: m²/Person als Indikator). Backend: - UserString dossier_raum_personen (int) - _attach_meta + _read_meta + state-emit - _update_wall raum-Branch akzeptiert "personen" im Patch - compute_sia_bilanz aggregiert personen-Summe ueber Scope - Stempel: neue Show-Flag stempel_show_personen (default false) + Bilanz-Renderer-Zeile "N Personen" - Stempel-Stil-Field showPersonen mit dabei Frontend (RaumProperties): - Personen-Input (number, min 0) zwischen Funktion + Layout-Builder - Nur sichtbar bei normalen Raeumen (nicht GF/AGF) - Live-Anzeige "m²/Person" Suffix wenn area + personen > 0 → User sieht sofort ob die Belegung sinnvoll ist (SIA-Vergleich) Frontend (StempelProperties): - Neuer Show-Toggle "Personen" (default off) - Live-Vorschau zeigt Personen-Summe wenn aktiv + > 0 - _OFF_BY_DEFAULT Set generalisiert Default-Handling (showCount, showPersonen)
This commit is contained in:
+37
-6
@@ -2722,7 +2722,8 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
|
||||
stempel_show_nf=None, stempel_show_vf=None,
|
||||
stempel_show_ff=None, stempel_show_ngf=None,
|
||||
stempel_show_gf=None, stempel_show_agf=None,
|
||||
stempel_show_count=None, stempel_show_seps=None,
|
||||
stempel_show_count=None, stempel_show_personen=None,
|
||||
stempel_show_seps=None,
|
||||
stempel_stil_id=None,
|
||||
wand_layered=None, wand_layers=None, wand_layer_idx=None,
|
||||
wand_chain_members=None,
|
||||
@@ -2962,6 +2963,7 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over,
|
||||
(_KEY_STEMPEL_SHOW_GF, stempel_show_gf),
|
||||
(_KEY_STEMPEL_SHOW_AGF, stempel_show_agf),
|
||||
(_KEY_STEMPEL_SHOW_COUNT, stempel_show_count),
|
||||
(_KEY_STEMPEL_SHOW_PERS, stempel_show_personen),
|
||||
(_KEY_STEMPEL_SHOW_SEPS, stempel_show_seps),
|
||||
):
|
||||
if _v is not None:
|
||||
@@ -3200,6 +3202,7 @@ def _read_meta(obj):
|
||||
st_show_gf = _sh_on(_KEY_STEMPEL_SHOW_GF, True)
|
||||
st_show_agf = _sh_on(_KEY_STEMPEL_SHOW_AGF, True)
|
||||
st_show_count = _sh_on(_KEY_STEMPEL_SHOW_COUNT, False)
|
||||
st_show_pers = _sh_on(_KEY_STEMPEL_SHOW_PERS, False)
|
||||
st_show_seps = _sh_on(_KEY_STEMPEL_SHOW_SEPS, True)
|
||||
st_stil_id = a.GetUserString(_KEY_STEMPEL_STIL_ID) or ""
|
||||
# Field-Layout — parsed JSON list of rows
|
||||
@@ -3311,6 +3314,7 @@ def _read_meta(obj):
|
||||
"raum_align": r_align,
|
||||
"raum_sia": r_sia,
|
||||
"raum_fuellung": r_fuell,
|
||||
"raum_personen": r_personen,
|
||||
"raum_txt_font": r_font,
|
||||
"raum_txt_bold": r_bold,
|
||||
"raum_txt_italic": r_ital,
|
||||
@@ -3340,6 +3344,7 @@ def _read_meta(obj):
|
||||
"stempel_show_gf": st_show_gf,
|
||||
"stempel_show_agf": st_show_agf,
|
||||
"stempel_show_count": st_show_count,
|
||||
"stempel_show_personen": st_show_pers,
|
||||
"stempel_show_seps": st_show_seps,
|
||||
"stempel_stil_id": st_stil_id,
|
||||
"wand_layered": w_layered,
|
||||
@@ -3978,6 +3983,7 @@ _KEY_STEMPEL_SHOW_NGF = "dossier_stempel_show_ngf" # default "1"
|
||||
_KEY_STEMPEL_SHOW_GF = "dossier_stempel_show_gf" # default "1"
|
||||
_KEY_STEMPEL_SHOW_AGF = "dossier_stempel_show_agf" # default "1"
|
||||
_KEY_STEMPEL_SHOW_COUNT = "dossier_stempel_show_count" # default "0"
|
||||
_KEY_STEMPEL_SHOW_PERS = "dossier_stempel_show_pers" # default "0"
|
||||
_KEY_STEMPEL_SHOW_SEPS = "dossier_stempel_show_seps" # default "1"
|
||||
_KEY_STEMPEL_STIL_ID = "dossier_stempel_stil_id" # aktiver Stil
|
||||
# Storage-Key fuer Stempel-Stile (Presets, per Doc)
|
||||
@@ -3985,7 +3991,8 @@ _KEY_STEMPEL_STILE = "dossier_stempel_stile"
|
||||
_STEMPEL_STIL_FIELDS = (
|
||||
"header", "showScope",
|
||||
"showHnf", "showNnf", "showNf", "showVf", "showFf",
|
||||
"showNgf", "showGf", "showAgf", "showCount", "showSeparators",
|
||||
"showNgf", "showGf", "showAgf", "showCount", "showPersonen",
|
||||
"showSeparators",
|
||||
"font", "bold", "italic", "txtH",
|
||||
)
|
||||
|
||||
@@ -5160,7 +5167,7 @@ def compute_sia_bilanz(doc, scope="total"):
|
||||
laut SIA 416).
|
||||
"""
|
||||
out = {"hnf": 0.0, "nnf": 0.0, "vf": 0.0, "ff": 0.0,
|
||||
"gf": 0.0, "agf": 0.0, "count": 0,
|
||||
"gf": 0.0, "agf": 0.0, "count": 0, "personen": 0,
|
||||
"scope": scope, "geschossName": ""}
|
||||
if doc is None: return out
|
||||
target_gid = None
|
||||
@@ -5181,6 +5188,8 @@ def compute_sia_bilanz(doc, scope="total"):
|
||||
if sia not in ("hnf", "nnf", "vf", "ff", "gf", "agf"): continue
|
||||
out[sia] += float(area)
|
||||
out["count"] += 1
|
||||
try: out["personen"] += int(m.get("raum_personen", 0) or 0)
|
||||
except Exception: pass
|
||||
except Exception: pass
|
||||
out["nf"] = out["hnf"] + out["nnf"]
|
||||
out["ngf"] = out["nf"] + out["vf"] + out["ff"]
|
||||
@@ -5188,7 +5197,8 @@ def compute_sia_bilanz(doc, scope="total"):
|
||||
|
||||
|
||||
def _format_bilanz_lines(bilanz, rundung="0.1", visibility=None,
|
||||
show_separators=True, show_count=False):
|
||||
show_separators=True, show_count=False,
|
||||
show_personen=False):
|
||||
"""Baut die Stempel-Textzeilen aus einer Bilanz. Zeigt nur Kategorien
|
||||
mit Flaeche > 0 UND deren visibility-Flag True ist.
|
||||
|
||||
@@ -5231,6 +5241,11 @@ def _format_bilanz_lines(bilanz, rundung="0.1", visibility=None,
|
||||
if show_count and bilanz.get("count", 0) > 0:
|
||||
if show_separators and lines: lines.append(sep)
|
||||
lines.append("{} Räume".format(bilanz["count"]))
|
||||
# Personen-Total optional (Summe ueber alle Raeume im Scope)
|
||||
if show_personen and bilanz.get("personen", 0) > 0:
|
||||
if not (show_count and show_separators) and show_separators and lines:
|
||||
lines.append(sep)
|
||||
lines.append("{} Personen".format(bilanz["personen"]))
|
||||
return lines
|
||||
|
||||
|
||||
@@ -5238,6 +5253,7 @@ def _make_stempel_text(pos, scope, doc, text_height=0.20, z=0.0,
|
||||
font=None, bold=False, italic=False,
|
||||
header_text="Nutzflächen", show_scope=True,
|
||||
show_separators=True, show_count=False,
|
||||
show_personen=False,
|
||||
visibility=None):
|
||||
"""Baut die Stempel-TextEntity fuer einen Scope ("total" oder
|
||||
"geschoss:<id>"). header_text + show_* steuern Layout-Customisation.
|
||||
@@ -5262,7 +5278,8 @@ def _make_stempel_text(pos, scope, doc, text_height=0.20, z=0.0,
|
||||
header = " · ".join(header_parts) if header_parts else ""
|
||||
body = _format_bilanz_lines(bilanz, visibility=visibility,
|
||||
show_separators=show_separators,
|
||||
show_count=show_count)
|
||||
show_count=show_count,
|
||||
show_personen=show_personen)
|
||||
if not body:
|
||||
body = ["(keine klassifizierten Räume)"]
|
||||
lines = []
|
||||
@@ -6771,6 +6788,7 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
||||
show_scope=bool(meta.get("stempel_show_scope", True)),
|
||||
show_separators=bool(meta.get("stempel_show_seps", True)),
|
||||
show_count=bool(meta.get("stempel_show_count", False)),
|
||||
show_personen=bool(meta.get("stempel_show_personen", False)),
|
||||
visibility=visibility)
|
||||
if new_te is None: return False
|
||||
# In-place replace
|
||||
@@ -7144,6 +7162,7 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
"layout": meta.get("raum_layout") or [],
|
||||
"txtModus": meta.get("raum_txt_modus", "fix"),
|
||||
"stilId": meta.get("raum_stil_id", ""),
|
||||
"personen": int(meta.get("raum_personen", 0) or 0),
|
||||
"area": area,
|
||||
"areaFmt": _format_area(area, rnd_eff),
|
||||
"umfang": perim,
|
||||
@@ -7174,6 +7193,7 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
"showGf": bool(meta.get("stempel_show_gf", True)),
|
||||
"showAgf": bool(meta.get("stempel_show_agf", True)),
|
||||
"showCount": bool(meta.get("stempel_show_count", False)),
|
||||
"showPersonen": bool(meta.get("stempel_show_personen", False)),
|
||||
"showSeparators": bool(meta.get("stempel_show_seps", True)),
|
||||
"stilId": meta.get("stempel_stil_id", ""),
|
||||
"bilanz": bilanz,
|
||||
@@ -11015,6 +11035,13 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
old_meta.get("raum_show_area", True)))
|
||||
r_show_sia = bool(p.get("showSia",
|
||||
old_meta.get("raum_show_sia", False)))
|
||||
# Personen-Belegung (int, default 0)
|
||||
try:
|
||||
r_personen = int(p.get("personen",
|
||||
old_meta.get("raum_personen", 0)))
|
||||
except Exception:
|
||||
r_personen = int(old_meta.get("raum_personen", 0) or 0)
|
||||
if r_personen < 0: r_personen = 0
|
||||
# Layout: Liste-of-Rows aus Frontend (JSON-serializable). Wenn
|
||||
# nicht im Patch → vom alten meta uebernehmen.
|
||||
r_layout = p.get("layout", old_meta.get("raum_layout", []))
|
||||
@@ -11072,7 +11099,8 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
raum_show_area=r_show_are,
|
||||
raum_show_sia=r_show_sia,
|
||||
raum_layout=r_layout,
|
||||
raum_txt_modus=r_modus)
|
||||
raum_txt_modus=r_modus,
|
||||
raum_personen=r_personen)
|
||||
axis_obj.Attributes = attrs
|
||||
axis_obj.CommitChanges()
|
||||
_save_last(raum_name_last=r_name, raum_rundung=r_rnd,
|
||||
@@ -11123,6 +11151,8 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
old_meta.get("stempel_show_agf", True)))
|
||||
st_show_count = bool(p.get("showCount",
|
||||
old_meta.get("stempel_show_count", False)))
|
||||
st_show_pers = bool(p.get("showPersonen",
|
||||
old_meta.get("stempel_show_personen", False)))
|
||||
st_show_seps = bool(p.get("showSeparators",
|
||||
old_meta.get("stempel_show_seps", True)))
|
||||
attrs = axis_obj.Attributes
|
||||
@@ -11142,6 +11172,7 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
stempel_show_gf=st_show_gf,
|
||||
stempel_show_agf=st_show_agf,
|
||||
stempel_show_count=st_show_count,
|
||||
stempel_show_personen=st_show_pers,
|
||||
stempel_show_seps=st_show_seps)
|
||||
axis_obj.Attributes = attrs
|
||||
axis_obj.CommitChanges()
|
||||
|
||||
+47
-5
@@ -937,6 +937,7 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo
|
||||
const [name, setName] = useState(raum.name || 'Raum')
|
||||
const [nummer, setNummer] = useState(raum.nummer || '')
|
||||
const [funktion, setFunktion] = useState(raum.funktion || '')
|
||||
const [personenStr, setPersonenStr] = useState(String(raum.personen || 0))
|
||||
// Texthoehe (raum.txtH) + Ausrichtung (raum.align) werden via Oberleiste
|
||||
// gesetzt — kein Local-State noetig, Stil-Speichern liest direkt aus raum.
|
||||
const txtModus = raum.txtModus || 'fix'
|
||||
@@ -948,7 +949,8 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo
|
||||
setName(raum.name || 'Raum')
|
||||
setNummer(raum.nummer || '')
|
||||
setFunktion(raum.funktion || '')
|
||||
}, [raum.id, raum.name, raum.nummer, raum.funktion])
|
||||
setPersonenStr(String(raum.personen || 0))
|
||||
}, [raum.id, raum.name, raum.nummer, raum.funktion, raum.personen])
|
||||
|
||||
// Aktueller Wert von raum_fuellung: "" | "Solid" | "Hatch1" | … | "ByLayer"
|
||||
const fuell = raum.fuellung || ''
|
||||
@@ -1133,6 +1135,33 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Personen-Belegung (SIA: Schulen, Bueros, Versammlungen) —
|
||||
aggregiert im Bilanz-Stempel als "Personen"-Zeile wenn aktiv. */}
|
||||
{!isFlaeche && (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Personen</span>
|
||||
<input type="number" min="0" step="1" value={personenStr}
|
||||
onChange={(e) => setPersonenStr(e.target.value)}
|
||||
onBlur={() => {
|
||||
const n = parseInt(personenStr, 10)
|
||||
const v = Number.isFinite(n) && n >= 0 ? n : 0
|
||||
if (v !== (raum.personen || 0)) onUpdate({ personen: v })
|
||||
setPersonenStr(String(v))
|
||||
}}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
||||
placeholder="0"
|
||||
title="Anzahl Personen — wird im Bilanz-Stempel summiert (m²/Person nach SIA)"
|
||||
style={{ width: 64, fontSize: 11, textAlign: 'right',
|
||||
fontFamily: 'DM Mono, monospace' }} />
|
||||
{raum.area > 0 && raum.personen > 0 && (
|
||||
<span style={{ fontSize: 9, color: 'var(--text-muted)',
|
||||
fontFamily: 'DM Mono, monospace' }}>
|
||||
{(raum.area / raum.personen).toFixed(1)} m²/Person
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Stempel-Layout — Drag-and-Drop. Jede Row ist eine Textzeile,
|
||||
Felder innerhalb einer Row landen in derselben Zeile. Drag
|
||||
zwischen Rows um umzuordnen. Klick auf Field oben fügt es in
|
||||
@@ -1185,7 +1214,8 @@ function StempelProperties({ stempel, geschosse, stempelStile, onUpdate, onDelet
|
||||
setHeaderDraft(stempel.header || 'Nutzflächen')
|
||||
}, [stempel.id, stempel.header])
|
||||
|
||||
// Show-Flags vom Backend (default true ausser showCount)
|
||||
// Show-Flags vom Backend — default true ausser folgende:
|
||||
const _OFF_BY_DEFAULT = new Set(['showCount', 'showPersonen'])
|
||||
const _show = (k, def = true) =>
|
||||
(stempel[k] !== undefined ? !!stempel[k] : def)
|
||||
|
||||
@@ -1205,6 +1235,7 @@ function StempelProperties({ stempel, geschosse, stempelStile, onUpdate, onDelet
|
||||
showGf: _show('showGf'),
|
||||
showAgf: _show('showAgf'),
|
||||
showCount: _show('showCount', false),
|
||||
showPersonen: _show('showPersonen', false),
|
||||
showSeparators: _show('showSeparators'),
|
||||
font: stempel.font || '',
|
||||
bold: !!stempel.bold,
|
||||
@@ -1239,9 +1270,9 @@ function StempelProperties({ stempel, geschosse, stempelStile, onUpdate, onDelet
|
||||
|
||||
const ShowTog = ({ field, label, hint }) => (
|
||||
<BarToggle label={label}
|
||||
icon={_show(field, field === 'showCount' ? false : true) ? 'check_box' : 'check_box_outline_blank'}
|
||||
active={_show(field, field === 'showCount' ? false : true)}
|
||||
onClick={() => onUpdate({ [field]: !_show(field, field === 'showCount' ? false : true) })}
|
||||
icon={_show(field, _OFF_BY_DEFAULT.has(field) ? false : true) ? 'check_box' : 'check_box_outline_blank'}
|
||||
active={_show(field, _OFF_BY_DEFAULT.has(field) ? false : true)}
|
||||
onClick={() => onUpdate({ [field]: !_show(field, _OFF_BY_DEFAULT.has(field) ? false : true) })}
|
||||
title={hint || label} />
|
||||
)
|
||||
|
||||
@@ -1327,6 +1358,7 @@ function StempelProperties({ stempel, geschosse, stempelStile, onUpdate, onDelet
|
||||
<ShowTog field="showGf" label="GF" />
|
||||
<ShowTog field="showAgf" label="AGF" />
|
||||
<ShowTog field="showCount" label="Anzahl" hint="Anzahl klassifizierter Räume" />
|
||||
<ShowTog field="showPersonen" label="Personen" hint="Summe der Personen-Belegung (SIA)" />
|
||||
<ShowTog field="showSeparators" label="Linien" hint="Trennlinien zwischen Sections" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -1357,6 +1389,16 @@ function StempelProperties({ stempel, geschosse, stempelStile, onUpdate, onDelet
|
||||
<span>{r.val.toFixed(1)} m²</span>
|
||||
</div>
|
||||
))}
|
||||
{_show('showPersonen', false) && (bilanz.personen || 0) > 0 && (
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between',
|
||||
padding: '2px 0',
|
||||
borderTop: '1px dashed var(--border-light)',
|
||||
marginTop: 2 }}
|
||||
title="Summe Personen über alle Räume im Scope">
|
||||
<span>Personen</span>
|
||||
<span>{bilanz.personen}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
|
||||
Reference in New Issue
Block a user