Data seeding, filter overhaul, UI scaling fixes, sanitized AlarmAnalysis copy

Data & simulation:
- simulation_tags.json: re-prioritize 10 tags to Medium so all four
  priorities appear in live data (backfilled journal history to match)
- SimHarness probe P2: ack-user/includeData ground truth + filtered-bundle
  verification against the live journal

Data layer (PrimeControls.calc/alarms):
- filters now drive every metric: queryStatus rows (Active Now/Unacked/
  Standing) filtered via calc.apply_status_filters; event_count counts only
  filtered instances (per-instance n_events); dropdown options enumerate
  unfiltered data so choices never collapse
- build_bundle refactored into _assemble(); when filters are active an
  unfiltered mirror is attached as bundle.overview - the Overview tab stays
  a plant-wide summary while all other consumers bind filtered sections
- _fetch_journal now honors includeData (required for ackUserName; without
  it ack attribution is always None); enabled for journal rows + source
  detail so Top Ack Users and the Ack By column populate

FilterBar/Dashboard:
- root-cause fix: bidirectional bindings never wrote dropdown selections
  back to session props, so filters silently did nothing; Apply now pulls
  values directly from the sibling controls, Clear resets both
- multi-select dropdowns cap at 280px and wrap chips; filter bar row is
  auto-height so wrapped chips push tabs down instead of overlapping
- filter bar hidden on the Overview tab (not applicable there)

UI scaling:
- Header: Prime logo added; KpiCard compacted to fit the 71px KPI row
  (basis-95% overflow fix, chip hugs text, embeds clipped) - no scrollbars
- Overview timeline: thinned x labels (minDistance 140), labels anchored
  below the axis, smaller legend markers
- Bad Actors focus chip shows the full source string with the clear X
- popup repeaters (Daily Activity, ScoreBars) no longer overflow
  (useDefaultViewWidth/Height off, taller strip); 5px table cell padding

Tooling/tests:
- lint_project.py: accept propConfig params.* as param declarations
  (Designer saves strip null params entries)
- tests: 31 -> 34 (status-filter semantics, overview mirror, filtered
  event counts)
- .gitignore: runtime churn (valueStore.idb, local-system-properties,
  migration logs, thumbnails, .schemas)

New project: AlarmAnalysis - submission copy of PrimeBAT with client-visible
Prime Controls branding removed (logo, subtitle, project title, shift report
heading); PrimeControls organizational namespaces retained per spec.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 16:27:48 -05:00
parent b70b4d6991
commit d353c5001e
165 changed files with 13988 additions and 525 deletions

View File

@@ -11,7 +11,91 @@
import json
OUT_DIR = "/usr/local/bin/ignition/data/projects/.probe"
PROBE_NAME = "P1"
PROBE_NAME = "P2"
def _load_pc():
"""Exec PrimeBAT's real PrimeControls modules (calc/fmt/alarms) into a
package object and return it (shared by _p1/_p2)."""
import imp
base = "/usr/local/bin/ignition/data/projects/PrimeBAT/ignition/script-python/PrimeControls/"
pkg = imp.new_module("PrimeControls")
pkg.__path__ = [base]
sys_modules = __import__("sys").modules
sys_modules["PrimeControls"] = pkg
for name in ("calc", "fmt", "alarms"):
m = imp.new_module("PrimeControls." + name)
m.__dict__["system"] = system
code = open(base + name + "/code.py").read()
compiled = compile(code, name, "exec")
exec compiled in m.__dict__
sys_modules["PrimeControls." + name] = m
setattr(pkg, name, m)
return pkg
def _p2():
"""Ack-user visibility + filtered-bundle ground truth."""
out = {}
end = system.date.toMillis(system.date.now())
start = end - 4 * 3600 * 1000
def ack_facts(entries):
acks = []
for e in entries:
try:
if e.getAckData() is None:
continue
except:
continue
f = {}
try:
f["ackUserName"] = repr(e.get("ackUserName"))[:80]
except:
f["ackUserName"] = "ERR"
try:
f["ackUser"] = repr(e.get("ackUser"))[:120]
except:
f["ackUser"] = "ERR"
acks.append(f)
named = [a for a in acks if a["ackUserName"] not in ("None", "ERR", "u''", "''")]
return {"ack_count": len(acks), "named_count": len(named),
"samples": named[:3] or acks[:3]}
try:
r = system.alarm.queryJournal(startDate=start, endDate=end)
out["no_includeData"] = ack_facts([e for e in r])
except:
out["no_includeData"] = "ERR"
try:
r = system.alarm.queryJournal(startDate=start, endDate=end, includeData=True)
out["includeData_true"] = ack_facts([e for e in r])
except:
out["includeData_true"] = "ERR"
pkg = _load_pc()
al = pkg.alarms
def pick(b):
return {"event_count": b["meta"]["event_count"],
"activation_count": b["meta"]["activation_count"],
"areas_n": len(b["meta"]["areas"]),
"prios_seen": [p["label"] for p in b["meta"]["priorities_seen"]],
"kpis": dict((k, b["kpis"][k]["value"]) for k in b["kpis"]),
"pareto_top": (b["pareto"]["rows"][0]["label"], b["pareto"]["rows"][0]["count"]) if b["pareto"]["rows"] else None,
"overview_act": (b.get("overview") or {}).get("meta", {}).get("activation_count"),
"overview_insights": len((b.get("overview") or {}).get("insights") or []),
"error": b["meta"]["error"]}
out["bundle_all"] = pick(al.getDashboardBundle(start, end, None))
out["bundle_medium"] = pick(al.getDashboardBundle(start, end, {"priorities": [2]}))
out["bundle_area_tankfarm"] = pick(al.getDashboardBundle(start, end, {"areas": ["TankFarm"]}))
d = al.getSourceDetail(
"prov:default:/tag:BuildathonSim/Intake/ConveyorJam_Chatter:/alm:ConveyorJam_Chatter",
start, end)
out["source_detail_top_ack"] = d["stats"]["top_ack_users"]
out["source_detail_error"] = d["error"]
return out
def _p1():
@@ -227,6 +311,6 @@ def _p0():
def run():
result = {"probe": PROBE_NAME,
"ranAt": str(system.date.now())}
result["payload"] = _safe(_p1)
result["payload"] = _safe(_p2)
_write(result)
system.util.getLogger("probe").info("probe %s written to %s" % (PROBE_NAME, OUT_DIR))

View File

@@ -12,4 +12,4 @@
"fixedDelay": true,
"enabled": false
}
}
}