Heatmap drill-down to Journal, layout fixes, healthy-plant sim mode (B grade)
Heatmap -> Journal drill-down (PrimeBAT only): - clicking an Activations-by-hour/day cell jumps to the Journal filtered to that recurring day-of-week + hour (new query.filterDow/filterHour session props; same local-time bucketing as calc.heatmap) - Journal shows a "Time filter: Thu 12:00-12:59" chip with an X to clear; filter composes with existing filters and is included in CSV export - getJournalPage clamps out-of-range pages when a filter shrinks the set Layout: - filter bar moved below the tab strip so the tabs no longer shift when the bar hides on Overview (tab bar y verified pixel-stable across tabs) - popup titles (AlarmDetail/EventDetail) auto-shrink their font to fit long source paths on one line without colliding with the state/priority chips Alarm health -> B (score ~88): - alarmsim gains a MODE flag: "healthy" (default) generates ~12 activations/hr with an 80/15/5 priority mix and no chatter/standing/fleeting/floods; "chaos" restores the original stress profile - SimHarness gains a SimReset one-shot timer (disabled) that clears all sim tags; probe P3 reports health-grade ground truth for the 8h window - Dashboard onStartup now re-anchors relative range presets (4h/8h/24h/7d) to now on session start - fixes stale Designer-baked startMs/endMs pinning every new session to an old window Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -108,7 +108,7 @@
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx} for c in hm['rows'][0]][:24]\n\texcept:\n\t\treturn []",
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx, 'dow': 0, 'hour': i} for i, c in enumerate(hm['rows'][0])][:24]\n\texcept:\n\t\treturn []",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
@@ -174,7 +174,7 @@
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx} for c in hm['rows'][1]][:24]\n\texcept:\n\t\treturn []",
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx, 'dow': 1, 'hour': i} for i, c in enumerate(hm['rows'][1])][:24]\n\texcept:\n\t\treturn []",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
@@ -240,7 +240,7 @@
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx} for c in hm['rows'][2]][:24]\n\texcept:\n\t\treturn []",
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx, 'dow': 2, 'hour': i} for i, c in enumerate(hm['rows'][2])][:24]\n\texcept:\n\t\treturn []",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
@@ -306,7 +306,7 @@
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx} for c in hm['rows'][3]][:24]\n\texcept:\n\t\treturn []",
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx, 'dow': 3, 'hour': i} for i, c in enumerate(hm['rows'][3])][:24]\n\texcept:\n\t\treturn []",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
@@ -372,7 +372,7 @@
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx} for c in hm['rows'][4]][:24]\n\texcept:\n\t\treturn []",
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx, 'dow': 4, 'hour': i} for i, c in enumerate(hm['rows'][4])][:24]\n\texcept:\n\t\treturn []",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
@@ -438,7 +438,7 @@
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx} for c in hm['rows'][5]][:24]\n\texcept:\n\t\treturn []",
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx, 'dow': 5, 'hour': i} for i, c in enumerate(hm['rows'][5])][:24]\n\texcept:\n\t\treturn []",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
@@ -504,7 +504,7 @@
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx} for c in hm['rows'][6]][:24]\n\texcept:\n\t\treturn []",
|
||||
"code": "\ttry:\n\t\thm = value['heatmap']\n\t\tmx = int(hm['max_count'] or 0)\n\t\treturn [{'count': int(c or 0), 'max': mx, 'dow': 6, 'hour': i} for i, c in enumerate(hm['rows'][6])][:24]\n\texcept:\n\t\treturn []",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
"custom.pageData": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"expression": "{session.custom.PrimeControls.query.refreshToken} + '|' + {session.custom.PrimeControls.query.startMs} + '|' + {session.custom.PrimeControls.query.endMs} + '|' + {view.custom.page}"
|
||||
"expression": "{session.custom.PrimeControls.query.refreshToken} + '|' + {session.custom.PrimeControls.query.startMs} + '|' + {session.custom.PrimeControls.query.endMs} + '|' + {session.custom.PrimeControls.query.filterDow} + '|' + {session.custom.PrimeControls.query.filterHour} + '|' + {view.custom.page}"
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"code": "\tsafe = {'rows': [], 'total': 0, 'page': 0, 'page_size': 50, 'truncated': False, 'error': None}\n\ttry:\n\t\tq = self.session.custom.PrimeControls.query\n\t\tif not q.startMs or not q.endMs:\n\t\t\treturn safe\n\t\ttry:\n\t\t\tpage = max(0, int(self.view.custom.page or 0))\n\t\texcept:\n\t\t\tpage = 0\n\t\treturn PrimeControls.alarms.getJournalPage(q.startMs, q.endMs, {'priorities': list(q.priorities), 'areas': list(q.areas), 'states': list(q.states), 'search': q.search}, page, 50)\n\texcept:\n\t\timport sys\n\t\tsafe['error'] = '%s' % (sys.exc_info()[1],)\n\t\treturn safe",
|
||||
"code": "\tsafe = {'rows': [], 'total': 0, 'page': 0, 'page_size': 50, 'truncated': False, 'error': None}\n\ttry:\n\t\tq = self.session.custom.PrimeControls.query\n\t\tif not q.startMs or not q.endMs:\n\t\t\treturn safe\n\t\ttry:\n\t\t\tpage = max(0, int(self.view.custom.page or 0))\n\t\texcept:\n\t\t\tpage = 0\n\t\treturn PrimeControls.alarms.getJournalPage(q.startMs, q.endMs, {'priorities': list(q.priorities), 'areas': list(q.areas), 'states': list(q.states), 'search': q.search, 'dow': q.filterDow, 'hour': q.filterHour}, page, 50)\n\texcept:\n\t\timport sys\n\t\tsafe['error'] = '%s' % (sys.exc_info()[1],)\n\t\treturn safe",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
@@ -66,6 +66,99 @@
|
||||
},
|
||||
"type": "ia.display.label"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "lblTimeFilter"
|
||||
},
|
||||
"position": {
|
||||
"basis": "auto",
|
||||
"grow": 0,
|
||||
"shrink": 0
|
||||
},
|
||||
"propConfig": {
|
||||
"props.text": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"struct": {
|
||||
"dow": "{session.custom.PrimeControls.query.filterDow}",
|
||||
"hour": "{session.custom.PrimeControls.query.filterHour}"
|
||||
},
|
||||
"waitOnAll": false
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"code": "\tdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n\ttry:\n\t\td = int(value['dow'])\n\t\th = int(value['hour'])\n\texcept:\n\t\treturn ''\n\tif d < 0 and h < 0:\n\t\treturn ''\n\tday = days[d] if 0 <= d < 7 else 'Any day'\n\tif h >= 0:\n\t\treturn 'Time filter: %s %02d:00-%02d:59' % (day, h, h)\n\treturn 'Time filter: %s' % day",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
"type": "expr-struct"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"style": {
|
||||
"whiteSpace": "nowrap"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.label"
|
||||
},
|
||||
{
|
||||
"events": {
|
||||
"dom": {
|
||||
"onClick": {
|
||||
"config": {
|
||||
"script": "\tq = self.session.custom.PrimeControls.query\n\tq.filterDow = -1\n\tq.filterHour = -1"
|
||||
},
|
||||
"scope": "G",
|
||||
"type": "script"
|
||||
}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"name": "icoClearTimeFilter"
|
||||
},
|
||||
"position": {
|
||||
"basis": "20px",
|
||||
"grow": 0,
|
||||
"shrink": 0
|
||||
},
|
||||
"props": {
|
||||
"path": "material/close",
|
||||
"style": {
|
||||
"cursor": "pointer",
|
||||
"fontSize": "16px"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.icon"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "chipTimeFilter"
|
||||
},
|
||||
"position": {
|
||||
"shrink": 0
|
||||
},
|
||||
"propConfig": {
|
||||
"position.display": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"expression": "{session.custom.PrimeControls.query.filterDow} > -1 || {session.custom.PrimeControls.query.filterHour} > -1"
|
||||
},
|
||||
"type": "expr"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"alignItems": "center",
|
||||
"style": {
|
||||
"classes": "PrimeControls/Chip/Flat",
|
||||
"gap": "6px"
|
||||
}
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
},
|
||||
{
|
||||
"meta": {
|
||||
"name": "lblError"
|
||||
@@ -242,7 +335,7 @@
|
||||
"component": {
|
||||
"onActionPerformed": {
|
||||
"config": {
|
||||
"script": "\ttry:\n\t\tq = self.session.custom.PrimeControls.query\n\t\tcsv = PrimeControls.alarms.journalCsv(q.startMs, q.endMs, {'priorities': list(q.priorities), 'areas': list(q.areas), 'states': list(q.states), 'search': q.search})\n\t\tsystem.perspective.download('alarm_journal.csv', csv)\n\texcept:\n\t\tpass"
|
||||
"script": "\ttry:\n\t\tq = self.session.custom.PrimeControls.query\n\t\tcsv = PrimeControls.alarms.journalCsv(q.startMs, q.endMs, {'priorities': list(q.priorities), 'areas': list(q.areas), 'states': list(q.states), 'search': q.search, 'dow': q.filterDow, 'hour': q.filterHour})\n\t\tsystem.perspective.download('alarm_journal.csv', csv)\n\texcept:\n\t\tpass"
|
||||
},
|
||||
"scope": "G",
|
||||
"type": "script"
|
||||
|
||||
Reference in New Issue
Block a user