fix(ucepsa): improve Shop Floor health dashboard presentation
This commit is contained in:
parent
2afc19105b
commit
10e5582b2c
@ -0,0 +1,120 @@
|
||||
# UCEPSA — Presentación dashboard Shop Floor v0.3.6.2
|
||||
|
||||
## Objetivo
|
||||
|
||||
Corregir exclusivamente la presentación del dashboard:
|
||||
|
||||
```text
|
||||
MESAVAULT — UCEPSA · Salud y contexto Shop Floor
|
||||
```
|
||||
|
||||
No modifica PostgreSQL, Odoo, sesiones, paros, sincronizadores ni el Ledger.
|
||||
|
||||
## Correcciones
|
||||
|
||||
### Indicadores superiores
|
||||
|
||||
```text
|
||||
Salud general
|
||||
HEALTHY → verde
|
||||
WARNING → naranja
|
||||
CRITICAL → rojo
|
||||
|
||||
Sincronizador
|
||||
HEALTHY → verde
|
||||
DELAYED → naranja
|
||||
STALE/ERROR → rojo
|
||||
|
||||
Advertencias
|
||||
0 → verde
|
||||
>= 1 → naranja
|
||||
|
||||
Críticas
|
||||
0 → verde
|
||||
>= 1 → rojo
|
||||
```
|
||||
|
||||
Los paneles de texto se alimentan mediante códigos numéricos para evitar el
|
||||
`No data` que Grafana mostraba al reducir valores de texto.
|
||||
|
||||
### Estado por máquina
|
||||
|
||||
Columnas visibles:
|
||||
|
||||
```text
|
||||
Máquina
|
||||
Marcha
|
||||
Telemetría
|
||||
Orden
|
||||
Workorder
|
||||
Operario
|
||||
Estado
|
||||
Severidad
|
||||
```
|
||||
|
||||
Se eliminan de esta tabla `Sync` y `Acción`, ya disponibles en otros paneles.
|
||||
|
||||
Los estados técnicos se traducen a lenguaje operativo:
|
||||
|
||||
```text
|
||||
RUNNING_WITHOUT_SHOPFLOOR_SESSION
|
||||
→ En marcha sin sesión Shop Floor
|
||||
|
||||
TEMPORARY_STOP_INSIDE_SESSION
|
||||
→ Parada dentro de sesión
|
||||
|
||||
IDLE_NO_ACTIVE_CONTEXT
|
||||
→ Inactiva sin contexto
|
||||
```
|
||||
|
||||
### Incidencias activas
|
||||
|
||||
Columnas:
|
||||
|
||||
```text
|
||||
Hora
|
||||
Severidad
|
||||
Máquina
|
||||
Situación
|
||||
Orden
|
||||
Operario
|
||||
Acción
|
||||
```
|
||||
|
||||
Los códigos técnicos quedan traducidos a frases operativas.
|
||||
|
||||
## Despliegue
|
||||
|
||||
```bash
|
||||
read -rsp "Token temporal de Grafana: " GRAFANA_API_TOKEN
|
||||
echo
|
||||
export GRAFANA_API_TOKEN
|
||||
|
||||
SCRIPT=/srv/mesavault/40-clients/ucepsa/edge-oee-demo/ops/deploy_shopfloor_context_health_dashboard_v0362.sh
|
||||
|
||||
"$SCRIPT" deploy
|
||||
"$SCRIPT" validate
|
||||
|
||||
unset GRAFANA_API_TOKEN
|
||||
```
|
||||
|
||||
El despliegue reutiliza el mismo UID:
|
||||
|
||||
```text
|
||||
ucepsa-shopfloor-context-health
|
||||
```
|
||||
|
||||
Por tanto, actualiza el dashboard existente sin crear duplicados.
|
||||
|
||||
## Validación esperada
|
||||
|
||||
```text
|
||||
status = ok
|
||||
panel_count = 10
|
||||
datasource_uids = [bfnbcasbm6hhca]
|
||||
numeric_health_stat = true
|
||||
numeric_sync_stat = true
|
||||
compact_machine_table = true
|
||||
friendly_incident_text = true
|
||||
errors = []
|
||||
```
|
||||
@ -15,7 +15,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Supervisión SHADOW de máquina física, orden Odoo, sesión Shop Floor, sincronizador y calidad temporal. No alimenta el Ledger oficial.",
|
||||
"description": "Supervisión SHADOW de máquina física, orden Odoo, sesión Shop Floor, sincronizador y calidad temporal. Presentación operativa v0.3.6.2. No alimenta el Ledger oficial.",
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 1,
|
||||
@ -35,24 +35,29 @@
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"type": "value",
|
||||
"options": {
|
||||
"HEALTHY": {
|
||||
"0": {
|
||||
"text": "HEALTHY",
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"text": "HEALTHY"
|
||||
"index": 0
|
||||
},
|
||||
"WARNING": {
|
||||
"1": {
|
||||
"text": "WARNING",
|
||||
"color": "orange",
|
||||
"index": 1,
|
||||
"text": "WARNING"
|
||||
"index": 1
|
||||
},
|
||||
"CRITICAL": {
|
||||
"2": {
|
||||
"text": "CRITICAL",
|
||||
"color": "red",
|
||||
"index": 2,
|
||||
"text": "CRITICAL"
|
||||
}
|
||||
"index": 2
|
||||
},
|
||||
"type": "value"
|
||||
"3": {
|
||||
"text": "UNKNOWN",
|
||||
"color": "red",
|
||||
"index": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": {
|
||||
@ -61,6 +66,14 @@
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -89,7 +102,7 @@
|
||||
"values": false
|
||||
},
|
||||
"showPercentChange": false,
|
||||
"textMode": "auto",
|
||||
"textMode": "value",
|
||||
"wideLayout": true
|
||||
},
|
||||
"pluginVersion": "11.0.0",
|
||||
@ -102,7 +115,7 @@
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT overall_health_status AS \"Estado\"\n FROM mv_reports_ucepsa_prod.li_shopfloor_context_health_summary_v1",
|
||||
"rawSql": "SELECT\n CASE overall_health_status\n WHEN 'HEALTHY' THEN 0\n WHEN 'WARNING' THEN 1\n WHEN 'CRITICAL' THEN 2\n ELSE 3\n END AS \"Estado\"\nFROM\n mv_reports_ucepsa_prod\n .li_shopfloor_context_health_summary_v1",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@ -121,34 +134,34 @@
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"type": "value",
|
||||
"options": {
|
||||
"HEALTHY": {
|
||||
"0": {
|
||||
"text": "HEALTHY",
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"text": "HEALTHY"
|
||||
"index": 0
|
||||
},
|
||||
"DELAYED": {
|
||||
"1": {
|
||||
"text": "DELAYED",
|
||||
"color": "orange",
|
||||
"index": 1,
|
||||
"text": "DELAYED"
|
||||
"index": 1
|
||||
},
|
||||
"STALE": {
|
||||
"2": {
|
||||
"text": "STALE",
|
||||
"color": "red",
|
||||
"index": 2,
|
||||
"text": "STALE"
|
||||
"index": 2
|
||||
},
|
||||
"ERROR": {
|
||||
"3": {
|
||||
"text": "ERROR",
|
||||
"color": "red",
|
||||
"index": 3,
|
||||
"text": "ERROR"
|
||||
"index": 3
|
||||
},
|
||||
"NOT_INITIALIZED": {
|
||||
"4": {
|
||||
"text": "NOT INITIALIZED",
|
||||
"color": "red",
|
||||
"index": 4,
|
||||
"text": "NOT INITIALIZED"
|
||||
"index": 4
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
],
|
||||
"thresholds": {
|
||||
@ -157,6 +170,14 @@
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -185,7 +206,7 @@
|
||||
"values": false
|
||||
},
|
||||
"showPercentChange": false,
|
||||
"textMode": "auto",
|
||||
"textMode": "value",
|
||||
"wideLayout": true
|
||||
},
|
||||
"pluginVersion": "11.0.0",
|
||||
@ -198,7 +219,7 @@
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT sync_health_status AS \"Estado\"\n FROM mv_reports_ucepsa_prod.li_shopfloor_context_health_summary_v1",
|
||||
"rawSql": "SELECT\n CASE sync_health_status\n WHEN 'HEALTHY' THEN 0\n WHEN 'DELAYED' THEN 1\n WHEN 'STALE' THEN 2\n WHEN 'ERROR' THEN 3\n WHEN 'NOT_INITIALIZED' THEN 4\n ELSE 4\n END AS \"Estado\"\nFROM\n mv_reports_ucepsa_prod\n .li_shopfloor_context_health_summary_v1",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@ -222,6 +243,14 @@
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 45
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 120
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -250,7 +279,7 @@
|
||||
"values": false
|
||||
},
|
||||
"showPercentChange": false,
|
||||
"textMode": "auto",
|
||||
"textMode": "value",
|
||||
"wideLayout": true
|
||||
},
|
||||
"pluginVersion": "11.0.0",
|
||||
@ -263,7 +292,7 @@
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT seconds_since_sync_success AS \"Segundos\"\n FROM mv_reports_ucepsa_prod.li_shopfloor_context_health_summary_v1",
|
||||
"rawSql": "SELECT\n seconds_since_sync_success AS \"Segundos\"\nFROM\n mv_reports_ucepsa_prod\n .li_shopfloor_context_health_summary_v1",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@ -315,7 +344,7 @@
|
||||
"values": false
|
||||
},
|
||||
"showPercentChange": false,
|
||||
"textMode": "auto",
|
||||
"textMode": "value",
|
||||
"wideLayout": true
|
||||
},
|
||||
"pluginVersion": "11.0.0",
|
||||
@ -328,7 +357,7 @@
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT open_session_count AS \"Sesiones\"\n FROM mv_reports_ucepsa_prod.li_shopfloor_context_health_summary_v1",
|
||||
"rawSql": "SELECT\n open_session_count AS \"Sesiones\"\nFROM\n mv_reports_ucepsa_prod\n .li_shopfloor_context_health_summary_v1",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@ -352,6 +381,10 @@
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -380,7 +413,7 @@
|
||||
"values": false
|
||||
},
|
||||
"showPercentChange": false,
|
||||
"textMode": "auto",
|
||||
"textMode": "value",
|
||||
"wideLayout": true
|
||||
},
|
||||
"pluginVersion": "11.0.0",
|
||||
@ -393,7 +426,7 @@
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT warning_incident_count AS \"Incidencias\"\n FROM mv_reports_ucepsa_prod.li_shopfloor_context_health_summary_v1",
|
||||
"rawSql": "SELECT\n warning_incident_count AS \"Advertencias\"\nFROM\n mv_reports_ucepsa_prod\n .li_shopfloor_context_health_summary_v1",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@ -417,6 +450,10 @@
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -445,7 +482,7 @@
|
||||
"values": false
|
||||
},
|
||||
"showPercentChange": false,
|
||||
"textMode": "auto",
|
||||
"textMode": "value",
|
||||
"wideLayout": true
|
||||
},
|
||||
"pluginVersion": "11.0.0",
|
||||
@ -458,7 +495,7 @@
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT critical_incident_count AS \"Incidencias\"\n FROM mv_reports_ucepsa_prod.li_shopfloor_context_health_summary_v1",
|
||||
"rawSql": "SELECT\n critical_incident_count AS \"Críticas\"\nFROM\n mv_reports_ucepsa_prod\n .li_shopfloor_context_health_summary_v1",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@ -498,24 +535,60 @@
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Estado"
|
||||
"options": "Máquina"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 260
|
||||
"value": 95
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Acción"
|
||||
"options": "Marcha"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 420
|
||||
"value": 75
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Telemetría"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 95
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Orden"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 145
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Workorder"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 95
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -527,7 +600,19 @@
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 220
|
||||
"value": 235
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Estado"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 300
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -541,29 +626,29 @@
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"type": "value",
|
||||
"options": {
|
||||
"OK": {
|
||||
"text": "OK",
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"text": "OK"
|
||||
"index": 0
|
||||
},
|
||||
"INFO": {
|
||||
"text": "INFO",
|
||||
"color": "blue",
|
||||
"index": 1,
|
||||
"text": "INFO"
|
||||
"index": 1
|
||||
},
|
||||
"WARNING": {
|
||||
"text": "WARNING",
|
||||
"color": "orange",
|
||||
"index": 2,
|
||||
"text": "WARNING"
|
||||
"index": 2
|
||||
},
|
||||
"CRITICAL": {
|
||||
"text": "CRITICAL",
|
||||
"color": "red",
|
||||
"index": 3,
|
||||
"text": "CRITICAL"
|
||||
"index": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -616,7 +701,7 @@
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\n machine_id AS \"Máquina\",\n machine_running AS \"Marcha\",\n telemetry_fresh AS \"Telemetría fresca\",\n COALESCE(published_production_order, '—') AS \"Orden publicada\",\n COALESCE(published_workorder_id, '—') AS \"WO publicado\",\n COALESCE(odoo_workorder_id::text, '—') AS \"WO sesión\",\n COALESCE(operator_name, operator_candidate_name, '—') AS \"Operario\",\n sync_health_status AS \"Sync\",\n live_context_status AS \"Estado\",\n live_severity AS \"Severidad\",\n recommended_action AS \"Acción\"\nFROM mv_reports_ucepsa_prod.li_shopfloor_machine_live_context_v1\nORDER BY machine_id",
|
||||
"rawSql": "SELECT\n machine_id AS \"Máquina\",\n CASE\n WHEN machine_running THEN 'Sí'\n ELSE 'No'\n END AS \"Marcha\",\n CASE\n WHEN telemetry_fresh THEN 'Sí'\n ELSE 'No'\n END AS \"Telemetría\",\n COALESCE(\n published_production_order,\n '—'\n ) AS \"Orden\",\n COALESCE(\n odoo_workorder_id::text,\n NULLIF(published_workorder_id, ''),\n '—'\n ) AS \"Workorder\",\n COALESCE(\n operator_name,\n operator_candidate_name,\n '—'\n ) AS \"Operario\",\n CASE live_context_status\n WHEN 'HEALTHY_OPEN_SESSION'\n THEN 'Produciendo con sesión Shop Floor'\n WHEN 'TEMPORARY_STOP_INSIDE_SESSION'\n THEN 'Parada dentro de sesión'\n WHEN 'RUNNING_WITHOUT_SHOPFLOOR_SESSION'\n THEN 'En marcha sin sesión Shop Floor'\n WHEN 'RUNNING_WITH_ORDER_NO_SHOPFLOOR_SESSION'\n THEN 'En marcha con orden, sin sesión'\n WHEN 'ORDER_WITHOUT_SHOPFLOOR_SESSION'\n THEN 'Orden preparada, sesión no iniciada'\n WHEN 'RUNNING_SESSION_WITHOUT_PUBLISHED_ORDER'\n THEN 'Sesión activa sin orden publicada'\n WHEN 'OPEN_SESSION_WITHOUT_PUBLISHED_ORDER'\n THEN 'Sesión abierta sin orden publicada'\n WHEN 'OPEN_SESSION_ORDER_MISMATCH'\n THEN 'Orden y sesión no coinciden'\n WHEN 'MULTIPLE_OPEN_SESSIONS'\n THEN 'Varias sesiones abiertas'\n WHEN 'OPEN_SESSION_DATA_QUALITY_ERROR'\n THEN 'Error de calidad en sesión'\n WHEN 'IDLE_NO_ACTIVE_CONTEXT'\n THEN 'Inactiva sin contexto'\n ELSE live_context_status\n END AS \"Estado\",\n CASE live_severity\n WHEN 'OK' THEN 'OK'\n WHEN 'INFO' THEN 'INFO'\n WHEN 'WARNING' THEN 'WARNING'\n WHEN 'CRITICAL' THEN 'CRITICAL'\n ELSE live_severity\n END AS \"Severidad\"\nFROM\n mv_reports_ucepsa_prod\n .li_shopfloor_machine_live_context_v1\nORDER BY machine_id",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@ -650,6 +735,18 @@
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Hora"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 175
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
@ -660,19 +757,19 @@
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"type": "value",
|
||||
"options": {
|
||||
"WARNING": {
|
||||
"text": "WARNING",
|
||||
"color": "orange",
|
||||
"index": 0,
|
||||
"text": "WARNING"
|
||||
"index": 0
|
||||
},
|
||||
"CRITICAL": {
|
||||
"text": "CRITICAL",
|
||||
"color": "red",
|
||||
"index": 1,
|
||||
"text": "CRITICAL"
|
||||
"index": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -688,6 +785,18 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Máquina"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 95
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
@ -700,6 +809,30 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Orden"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 145
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Operario"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 220
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
@ -708,7 +841,7 @@
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 460
|
||||
"value": 520
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -744,7 +877,7 @@
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\n observed_at AS \"Hora\",\n severity AS \"Severidad\",\n COALESCE(machine_id, '—') AS \"Máquina\",\n incident_scope AS \"Ámbito\",\n incident_code AS \"Código\",\n COALESCE(production_order, '—') AS \"Orden\",\n COALESCE(odoo_workorder_id::text, '—') AS \"Workorder\",\n COALESCE(operator_name, '—') AS \"Operario\",\n detail AS \"Situación\",\n recommended_action AS \"Acción\"\nFROM mv_reports_ucepsa_prod.li_shopfloor_context_incidents_v1\nORDER BY\n CASE severity\n WHEN 'CRITICAL' THEN 1\n WHEN 'WARNING' THEN 2\n ELSE 3\n END,\n machine_id NULLS FIRST,\n incident_code",
|
||||
"rawSql": "SELECT\n observed_at AS \"Hora\",\n severity AS \"Severidad\",\n COALESCE(machine_id, '—') AS \"Máquina\",\n CASE incident_code\n WHEN 'RUNNING_WITHOUT_SHOPFLOOR_SESSION'\n THEN 'Máquina en marcha sin orden ni sesión Shop Floor'\n WHEN 'RUNNING_WITH_ORDER_NO_SHOPFLOOR_SESSION'\n THEN 'Máquina en marcha con orden, pero sin sesión Shop Floor'\n WHEN 'ORDER_WITHOUT_SHOPFLOOR_SESSION'\n THEN 'Orden publicada sin sesión Shop Floor'\n WHEN 'RUNNING_SESSION_WITHOUT_PUBLISHED_ORDER'\n THEN 'Sesión abierta y máquina en marcha, pero sin orden publicada'\n WHEN 'OPEN_SESSION_WITHOUT_PUBLISHED_ORDER'\n THEN 'Sesión abierta sin orden publicada'\n WHEN 'OPEN_SESSION_ORDER_MISMATCH'\n THEN 'La orden publicada no coincide con la sesión abierta'\n WHEN 'MULTIPLE_OPEN_SESSIONS'\n THEN 'Existen varias sesiones abiertas en la misma máquina'\n WHEN 'OPEN_SESSION_DATA_QUALITY_ERROR'\n THEN 'La sesión abierta presenta un error de calidad'\n WHEN 'SHOPFLOOR_SYNC_DELAYED'\n THEN 'La sincronización Shop Floor está retrasada'\n WHEN 'SHOPFLOOR_SYNC_STALE'\n THEN 'La sincronización Shop Floor está obsoleta'\n WHEN 'SHOPFLOOR_SYNC_ERROR'\n THEN 'El sincronizador Shop Floor ha registrado un error'\n WHEN 'SHOPFLOOR_SYNC_NOT_INITIALIZED'\n THEN 'El sincronizador Shop Floor no está inicializado'\n ELSE incident_code\n END AS \"Situación\",\n COALESCE(production_order, '—') AS \"Orden\",\n COALESCE(operator_name, '—') AS \"Operario\",\n recommended_action AS \"Acción\"\nFROM\n mv_reports_ucepsa_prod\n .li_shopfloor_context_incidents_v1\nORDER BY\n CASE severity\n WHEN 'CRITICAL' THEN 1\n WHEN 'WARNING' THEN 2\n ELSE 3\n END,\n machine_id NULLS FIRST,\n incident_code",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@ -778,6 +911,54 @@
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Máquina"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 95
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Orden"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 145
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Workorder"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 95
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Operario"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 235
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
@ -787,6 +968,10 @@
|
||||
{
|
||||
"id": "unit",
|
||||
"value": "m"
|
||||
},
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 125
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -799,18 +984,70 @@
|
||||
{
|
||||
"id": "unit",
|
||||
"value": "m"
|
||||
},
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 125
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Acción"
|
||||
"options": "Estado"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 420
|
||||
"value": 300
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Severidad"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"type": "value",
|
||||
"options": {
|
||||
"OK": {
|
||||
"text": "OK",
|
||||
"color": "green",
|
||||
"index": 0
|
||||
},
|
||||
"INFO": {
|
||||
"text": "INFO",
|
||||
"color": "blue",
|
||||
"index": 1
|
||||
},
|
||||
"WARNING": {
|
||||
"text": "WARNING",
|
||||
"color": "orange",
|
||||
"index": 2
|
||||
},
|
||||
"CRITICAL": {
|
||||
"text": "CRITICAL",
|
||||
"color": "red",
|
||||
"index": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "custom.cellOptions",
|
||||
"value": {
|
||||
"type": "color-text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 110
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -846,7 +1083,7 @@
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\n machine_id AS \"Máquina\",\n production_order AS \"Orden\",\n odoo_workorder_id AS \"Workorder\",\n COALESCE(operator_name, operator_candidate_name, '—') AS \"Operario\",\n session_started_at AS \"Inicio\",\n session_age_min AS \"Minutos abierta\",\n machine_running AS \"Marcha\",\n telemetry_fresh AS \"Telemetría fresca\",\n open_stop_duration_min AS \"Paro abierto min\",\n watch_status AS \"Estado\",\n watch_severity AS \"Severidad\",\n recommended_action AS \"Acción\"\nFROM mv_reports_ucepsa_prod.li_shopfloor_open_session_watch_v1\nORDER BY machine_id, session_started_at",
|
||||
"rawSql": "SELECT\n machine_id AS \"Máquina\",\n production_order AS \"Orden\",\n odoo_workorder_id AS \"Workorder\",\n COALESCE(\n operator_name,\n operator_candidate_name,\n '—'\n ) AS \"Operario\",\n session_started_at AS \"Inicio\",\n session_age_min AS \"Minutos abierta\",\n CASE\n WHEN machine_running THEN 'Sí'\n ELSE 'No'\n END AS \"Marcha\",\n COALESCE(\n open_stop_duration_min,\n 0\n ) AS \"Paro abierto min\",\n CASE watch_status\n WHEN 'HEALTHY_OPEN_SESSION'\n THEN 'Produciendo con sesión Shop Floor'\n WHEN 'TEMPORARY_STOP_INSIDE_SESSION'\n THEN 'Parada dentro de sesión'\n WHEN 'RUNNING_SESSION_WITHOUT_PUBLISHED_ORDER'\n THEN 'Sesión activa sin orden publicada'\n WHEN 'OPEN_SESSION_WITHOUT_PUBLISHED_ORDER'\n THEN 'Sesión abierta sin orden publicada'\n WHEN 'OPEN_SESSION_ORDER_MISMATCH'\n THEN 'Orden y sesión no coinciden'\n WHEN 'MULTIPLE_OPEN_SESSIONS'\n THEN 'Varias sesiones abiertas'\n WHEN 'OPEN_SESSION_DATA_QUALITY_ERROR'\n THEN 'Error de calidad en sesión'\n ELSE watch_status\n END AS \"Estado\",\n watch_severity AS \"Severidad\"\nFROM\n mv_reports_ucepsa_prod\n .li_shopfloor_open_session_watch_v1\nORDER BY machine_id, session_started_at",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@ -883,31 +1120,79 @@
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "DQ"
|
||||
"options": "Máquina"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 95
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Orden"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 145
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Workorder"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 95
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Operario"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 235
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Calidad"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"type": "value",
|
||||
"options": {
|
||||
"OK": {
|
||||
"text": "OK",
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"text": "OK"
|
||||
"index": 0
|
||||
},
|
||||
"WARNING": {
|
||||
"text": "WARNING",
|
||||
"color": "orange",
|
||||
"index": 1,
|
||||
"text": "WARNING"
|
||||
"index": 1
|
||||
},
|
||||
"CRITICAL": {
|
||||
"text": "CRITICAL",
|
||||
"color": "red",
|
||||
"index": 2,
|
||||
"text": "CRITICAL"
|
||||
"index": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -916,6 +1201,22 @@
|
||||
"value": {
|
||||
"type": "color-text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 100
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Incidencias"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 280
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -951,7 +1252,7 @@
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\n machine_id AS \"Máquina\",\n production_order AS \"Orden\",\n odoo_workorder_id AS \"Workorder\",\n session_status AS \"Sesión\",\n workorder_state AS \"Estado Odoo\",\n COALESCE(operator_name, operator_candidate_name, '—') AS \"Operario\",\n operator_confirmation_status AS \"Operario\",\n quantity_confirmation_status AS \"Cantidad\",\n confidence AS \"Confianza\",\n dq_status AS \"DQ\",\n array_to_string(issue_codes, ', ') AS \"Incidencias DQ\",\n started_at AS \"Inicio\",\n ended_at AS \"Fin\"\nFROM mv_reports_ucepsa_prod.li_shopfloor_session_data_quality_v1\nORDER BY\n CASE dq_status\n WHEN 'CRITICAL' THEN 1\n WHEN 'WARNING' THEN 2\n ELSE 3\n END,\n COALESCE(ended_at, started_at) DESC NULLS LAST,\n odoo_workorder_id DESC\nLIMIT 50",
|
||||
"rawSql": "SELECT\n machine_id AS \"Máquina\",\n production_order AS \"Orden\",\n odoo_workorder_id AS \"Workorder\",\n CASE session_status\n WHEN 'OPEN' THEN 'Abierta'\n WHEN 'CLOSED' THEN 'Cerrada'\n WHEN 'PENDING' THEN 'Pendiente'\n ELSE session_status\n END AS \"Sesión\",\n workorder_state AS \"Estado Odoo\",\n COALESCE(\n operator_name,\n operator_candidate_name,\n '—'\n ) AS \"Operario\",\n operator_confirmation_status\n AS \"Confirmación operario\",\n quantity_confirmation_status\n AS \"Confirmación cantidad\",\n confidence AS \"Confianza\",\n dq_status AS \"Calidad\",\n COALESCE(\n NULLIF(\n array_to_string(\n issue_codes,\n ', '\n ),\n ''\n ),\n 'Sin incidencias'\n ) AS \"Incidencias\"\nFROM\n mv_reports_ucepsa_prod\n .li_shopfloor_session_data_quality_v1\nORDER BY\n CASE dq_status\n WHEN 'CRITICAL' THEN 1\n WHEN 'WARNING' THEN 2\n ELSE 3\n END,\n COALESCE(ended_at, started_at) DESC NULLS LAST,\n odoo_workorder_id DESC\nLIMIT 50",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@ -966,7 +1267,8 @@
|
||||
"ucepsa",
|
||||
"shop-floor",
|
||||
"context-health",
|
||||
"shadow"
|
||||
"shadow",
|
||||
"presentation-v0362"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
|
||||
335
ucepsa/edge-oee-demo/ops/deploy_shopfloor_context_health_dashboard_v0362.sh
Executable file
335
ucepsa/edge-oee-demo/ops/deploy_shopfloor_context_health_dashboard_v0362.sh
Executable file
@ -0,0 +1,335 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ACTION="${1:-deploy}"
|
||||
|
||||
GRAFANA_CONTAINER="${GRAFANA_CONTAINER:-mv_ucepsa_grafana}"
|
||||
REPO_ROOT="${SHOPFLOOR_DASHBOARD_REPO_ROOT:-/srv/mesavault/40-clients/ucepsa/edge-oee-demo}"
|
||||
DASHBOARD_FILE="${SHOPFLOOR_DASHBOARD_FILE:-$REPO_ROOT/grafana/dashboards/ucepsa-shopfloor-context-health.dashboard.json}"
|
||||
FOLDER_UID="${SHOPFLOOR_DASHBOARD_FOLDER_UID:-ucepsa-mesavault}"
|
||||
FOLDER_TITLE="${SHOPFLOOR_DASHBOARD_FOLDER_TITLE:-MESAVAULT — UCEPSA}"
|
||||
DASHBOARD_UID="ucepsa-shopfloor-context-health"
|
||||
DATASOURCE_UID="bfnbcasbm6hhca"
|
||||
|
||||
resolve_url() {
|
||||
if [[ -n "${GRAFANA_URL:-}" ]]; then
|
||||
printf '%s' "$GRAFANA_URL"
|
||||
return
|
||||
fi
|
||||
|
||||
local port_line host_port container_ip
|
||||
|
||||
port_line="$(
|
||||
docker port "$GRAFANA_CONTAINER" 3000/tcp 2>/dev/null \
|
||||
| head -n 1 || true
|
||||
)"
|
||||
|
||||
if [[ -n "$port_line" ]]; then
|
||||
host_port="${port_line##*:}"
|
||||
printf 'http://127.0.0.1:%s' "$host_port"
|
||||
return
|
||||
fi
|
||||
|
||||
container_ip="$(
|
||||
docker inspect "$GRAFANA_CONTAINER" \
|
||||
--format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
|
||||
)"
|
||||
|
||||
if [[ -z "$container_ip" ]]; then
|
||||
echo "ERROR: no se pudo resolver la URL de Grafana." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'http://%s:3000' "$container_ip"
|
||||
}
|
||||
|
||||
if [[ -z "${GRAFANA_API_TOKEN:-}" ]]; then
|
||||
cat >&2 <<'MSG'
|
||||
ERROR: falta GRAFANA_API_TOKEN.
|
||||
|
||||
Cárguelo sin mostrarlo:
|
||||
|
||||
read -rsp "Token temporal de Grafana: " GRAFANA_API_TOKEN
|
||||
echo
|
||||
export GRAFANA_API_TOKEN
|
||||
MSG
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export GRAFANA_URL_RESOLVED
|
||||
GRAFANA_URL_RESOLVED="$(resolve_url)"
|
||||
|
||||
python3 - \
|
||||
"$ACTION" \
|
||||
"$DASHBOARD_FILE" \
|
||||
"$FOLDER_UID" \
|
||||
"$FOLDER_TITLE" \
|
||||
"$DASHBOARD_UID" \
|
||||
"$DATASOURCE_UID" <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
(
|
||||
action,
|
||||
dashboard_file,
|
||||
folder_uid,
|
||||
folder_title,
|
||||
dashboard_uid,
|
||||
datasource_uid,
|
||||
) = sys.argv[1:]
|
||||
|
||||
base_url = os.environ["GRAFANA_URL_RESOLVED"].rstrip("/")
|
||||
token = os.environ["GRAFANA_API_TOKEN"]
|
||||
|
||||
|
||||
def request(method, path, payload=None, allow_404=False):
|
||||
url = base_url + path
|
||||
body = None
|
||||
headers = {
|
||||
"Accept": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
}
|
||||
|
||||
if payload is not None:
|
||||
body = json.dumps(
|
||||
payload,
|
||||
ensure_ascii=False,
|
||||
).encode("utf-8")
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=body,
|
||||
headers=headers,
|
||||
method=method,
|
||||
)
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(
|
||||
req,
|
||||
timeout=20,
|
||||
) as response:
|
||||
raw = response.read()
|
||||
if not raw:
|
||||
return None
|
||||
return json.loads(raw.decode("utf-8"))
|
||||
except urllib.error.HTTPError as exc:
|
||||
detail = exc.read().decode(
|
||||
"utf-8",
|
||||
errors="replace",
|
||||
)
|
||||
if allow_404 and exc.code == 404:
|
||||
return None
|
||||
raise RuntimeError(
|
||||
f"Grafana API {method} {path}: "
|
||||
f"HTTP {exc.code}: {detail}"
|
||||
) from exc
|
||||
|
||||
|
||||
health = request("GET", "/api/health")
|
||||
if not health or health.get("database") != "ok":
|
||||
raise RuntimeError(
|
||||
f"Grafana no está saludable: {health!r}"
|
||||
)
|
||||
|
||||
folder = request(
|
||||
"GET",
|
||||
f"/api/folders/{urllib.parse.quote(folder_uid)}",
|
||||
allow_404=True,
|
||||
)
|
||||
|
||||
if action == "deploy" and folder is None:
|
||||
request(
|
||||
"POST",
|
||||
"/api/folders",
|
||||
{
|
||||
"uid": folder_uid,
|
||||
"title": folder_title,
|
||||
},
|
||||
)
|
||||
|
||||
if action == "deploy":
|
||||
with open(
|
||||
dashboard_file,
|
||||
encoding="utf-8",
|
||||
) as handle:
|
||||
dashboard = json.load(handle)
|
||||
|
||||
result = request(
|
||||
"POST",
|
||||
"/api/dashboards/db",
|
||||
{
|
||||
"dashboard": dashboard,
|
||||
"folderUid": folder_uid,
|
||||
"overwrite": True,
|
||||
"message": (
|
||||
"MESAVAULT Shop Floor Context Health "
|
||||
"presentation fix v0.3.6.2"
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
print(json.dumps(
|
||||
{
|
||||
"status": "deployed",
|
||||
"grafana_url": base_url,
|
||||
"dashboard_uid": dashboard_uid,
|
||||
"folder_uid": folder_uid,
|
||||
"response": result,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
))
|
||||
|
||||
elif action == "validate":
|
||||
result = request(
|
||||
"GET",
|
||||
(
|
||||
"/api/dashboards/uid/"
|
||||
+ urllib.parse.quote(dashboard_uid)
|
||||
),
|
||||
)
|
||||
|
||||
dashboard = result["dashboard"]
|
||||
panels = {
|
||||
panel.get("id"): panel
|
||||
for panel in dashboard.get("panels", [])
|
||||
}
|
||||
|
||||
datasource_uids = sorted({
|
||||
panel.get("datasource", {}).get("uid")
|
||||
for panel in dashboard.get("panels", [])
|
||||
if isinstance(
|
||||
panel.get("datasource"),
|
||||
dict,
|
||||
)
|
||||
and panel.get("datasource", {}).get("uid")
|
||||
})
|
||||
|
||||
errors = []
|
||||
|
||||
if dashboard.get("uid") != dashboard_uid:
|
||||
errors.append("UID inesperado")
|
||||
|
||||
if result.get("meta", {}).get("folderUid") != folder_uid:
|
||||
errors.append("Carpeta inesperada")
|
||||
|
||||
if len(dashboard.get("panels", [])) != 10:
|
||||
errors.append("El dashboard no tiene 10 paneles")
|
||||
|
||||
if datasource_uids != [datasource_uid]:
|
||||
errors.append(
|
||||
f"Datasource inesperado: {datasource_uids!r}"
|
||||
)
|
||||
|
||||
health_sql = (
|
||||
panels.get(1, {})
|
||||
.get("targets", [{}])[0]
|
||||
.get("rawSql", "")
|
||||
)
|
||||
sync_sql = (
|
||||
panels.get(2, {})
|
||||
.get("targets", [{}])[0]
|
||||
.get("rawSql", "")
|
||||
)
|
||||
machine_sql = (
|
||||
panels.get(7, {})
|
||||
.get("targets", [{}])[0]
|
||||
.get("rawSql", "")
|
||||
)
|
||||
incident_sql = (
|
||||
panels.get(8, {})
|
||||
.get("targets", [{}])[0]
|
||||
.get("rawSql", "")
|
||||
)
|
||||
|
||||
if "CASE overall_health_status" not in health_sql:
|
||||
errors.append(
|
||||
"Salud general no usa código numérico"
|
||||
)
|
||||
|
||||
if "CASE sync_health_status" not in sync_sql:
|
||||
errors.append(
|
||||
"Sincronizador no usa código numérico"
|
||||
)
|
||||
|
||||
for forbidden in ('AS "Sync"', 'AS "Acción"'):
|
||||
if forbidden in machine_sql:
|
||||
errors.append(
|
||||
f"Tabla de máquinas conserva columna {forbidden}"
|
||||
)
|
||||
|
||||
if 'AS "Situación"' not in incident_sql:
|
||||
errors.append(
|
||||
"Incidencias no usa situación operativa"
|
||||
)
|
||||
|
||||
output = {
|
||||
"status": "ok" if not errors else "error",
|
||||
"grafana_url": base_url,
|
||||
"dashboard_uid": dashboard.get("uid"),
|
||||
"title": dashboard.get("title"),
|
||||
"folder_uid": result.get("meta", {}).get(
|
||||
"folderUid"
|
||||
),
|
||||
"panel_count": len(
|
||||
dashboard.get("panels", [])
|
||||
),
|
||||
"datasource_uids": datasource_uids,
|
||||
"version": dashboard.get("version"),
|
||||
"url": result.get("meta", {}).get("url"),
|
||||
"presentation_checks": {
|
||||
"numeric_health_stat": (
|
||||
"CASE overall_health_status"
|
||||
in health_sql
|
||||
),
|
||||
"numeric_sync_stat": (
|
||||
"CASE sync_health_status"
|
||||
in sync_sql
|
||||
),
|
||||
"compact_machine_table": (
|
||||
'AS "Sync"' not in machine_sql
|
||||
and 'AS "Acción"' not in machine_sql
|
||||
),
|
||||
"friendly_incident_text": (
|
||||
'AS "Situación"' in incident_sql
|
||||
),
|
||||
},
|
||||
"errors": errors,
|
||||
}
|
||||
|
||||
print(json.dumps(
|
||||
output,
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
))
|
||||
|
||||
if errors:
|
||||
raise SystemExit(1)
|
||||
|
||||
elif action == "export":
|
||||
result = request(
|
||||
"GET",
|
||||
(
|
||||
"/api/dashboards/uid/"
|
||||
+ urllib.parse.quote(dashboard_uid)
|
||||
),
|
||||
)
|
||||
|
||||
print(json.dumps(
|
||||
result["dashboard"],
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
))
|
||||
|
||||
else:
|
||||
raise SystemExit(
|
||||
"Uso: "
|
||||
"deploy_shopfloor_context_health_dashboard_v0362.sh "
|
||||
"{deploy|validate|export}"
|
||||
)
|
||||
PY
|
||||
Loading…
x
Reference in New Issue
Block a user