Initial commit: Grafana + Elasticsearch POC con dashboard Activity e Tasks
This commit is contained in:
@@ -0,0 +1,94 @@
|
|||||||
|
# Grafana & Elasticsearch Know-How
|
||||||
|
|
||||||
|
## 1. Elasticsearch Datasource Provisioning (YAML)
|
||||||
|
|
||||||
|
Quando si configura il datasource Elasticsearch via provisioning (es. `datasource.yml`), utilizzare il blocco `jsonData` per le configurazioni specifiche.
|
||||||
|
|
||||||
|
**Campi Chiave in `jsonData`:**
|
||||||
|
|
||||||
|
* **`index`**: Definisce il pattern dell'indice (es. `geodata`, `[logs-]YYYY.MM.DD`). **Nota:** Usare questo invece del campo top-level `database` che è deprecato.
|
||||||
|
* **`timeField`**: Il nome del campo timestamp (es. `timestamp` o `@timestamp`).
|
||||||
|
* **`esVersion`**: La versione del cluster Elasticsearch (es. `7.10.0`).
|
||||||
|
* **`interval`**: L'intervallo di tempo predefinito per i raggruppamenti (es. `Daily`, `Hourly`, `1m`). **Attenzione:** Non impostare a `None` o valori non validi, altrimenti Grafana potrebbe crashare all'avvio.
|
||||||
|
* **`maxConcurrentShardRequests`**: Limita le richieste concorrenti agli shard.
|
||||||
|
|
||||||
|
**Esempio `datasource.yml`:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
datasources:
|
||||||
|
- name: Elasticsearch
|
||||||
|
type: elasticsearch
|
||||||
|
uid: elasticsearch-uid
|
||||||
|
access: proxy
|
||||||
|
url: http://elasticsearch:9200
|
||||||
|
isDefault: true
|
||||||
|
jsonData:
|
||||||
|
index: "geodata"
|
||||||
|
timeField: "timestamp"
|
||||||
|
esVersion: "7.10.0"
|
||||||
|
interval: "Daily"
|
||||||
|
maxConcurrentShardRequests: 5
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Grafana Geomap Panel con Elasticsearch
|
||||||
|
|
||||||
|
Per visualizzare punti GeoJSON da Elasticsearch, si utilizza l'aggregazione **Geohash Grid**.
|
||||||
|
|
||||||
|
**Configurazione Query:**
|
||||||
|
|
||||||
|
1. **Query Type:** `Metric`.
|
||||||
|
2. **Metric:** `Count` (o altra metrica).
|
||||||
|
3. **Group By:**
|
||||||
|
* Type: `Geohash Grid`
|
||||||
|
* Field: Il campo mappato come `geo_point` (es. `location`).
|
||||||
|
* Precision: Livello di dettaglio (es. `5`).
|
||||||
|
|
||||||
|
**Configurazione Pannello (JSON Model):**
|
||||||
|
|
||||||
|
* **`geohashField`**: Nella configurazione del layer, specifica quale campo contiene l'hash. Solitamente è `key` quando si usa l'aggregazione.
|
||||||
|
* **`location.mode`**: Deve essere impostato su `geohash`.
|
||||||
|
|
||||||
|
**Troubleshooting "Mappa Vuota":**
|
||||||
|
|
||||||
|
* **Precisione:** Se troppo alta (es. 12) su una mappa zoomata indietro, i punti potrebbero non apparire.
|
||||||
|
* **Mapping:** Il campo in Elasticsearch DEVE essere di tipo `geo_point`.
|
||||||
|
* **Geohash Field:** Assicurarsi che nel pannello Geomap, sotto "Layer" -> "Location", il "Geohash field" sia impostato correttamente (spesso `key`).
|
||||||
|
|
||||||
|
**Esempio JSON Dashboard (Snippet):**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "geomap",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"bucketAggs": [
|
||||||
|
{
|
||||||
|
"type": "geohash_grid",
|
||||||
|
"field": "location",
|
||||||
|
"settings": { "precision": "5" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeField": "timestamp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"mode": "geohash",
|
||||||
|
"geohashField": "key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Troubleshooting & Best Practices
|
||||||
|
|
||||||
|
* **Controllare sempre i log:** Prima di terminare un task o assumere che una fix funzioni, controllare sempre i log del container (es. `docker compose logs grafana`). Errori di provisioning o crash all'avvio sono spesso visibili solo lì.
|
||||||
|
* **Riavvio Container:** Le modifiche ai file di provisioning (`datasource.yml`, `dashboard.yml`) richiedono spesso il riavvio del container Grafana (`docker compose restart grafana`) per essere applicate.
|
||||||
|
* **Validazione YAML:** Assicurarsi che i file YAML siano validi e che l'indentazione sia corretta.
|
||||||
|
* **Datasource UID:** Se si riscontrano errori "Datasource provisioning error: data source not found", provare a rimuovere il campo `uid` dal file YAML del datasource e fare riferimento al datasource tramite il suo `name` (stringa) nel JSON della dashboard, invece che tramite l'oggetto `{ type: ..., uid: ... }`.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
grafana_data/
|
||||||
|
es_data/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
|
*.log
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
# Dashboard Activity & Tasks su Elasticsearch
|
||||||
|
|
||||||
|
**Autore:** Matteo Benedetto
|
||||||
|
**Email:** matteo.benedetto@e-geos.it
|
||||||
|
**Data:** 03/12/2025
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Struttura dei dati in Elasticsearch
|
||||||
|
|
||||||
|
### 1.1 Indice
|
||||||
|
|
||||||
|
Tutti i dati sono indicizzati in Elasticsearch nell'indice:
|
||||||
|
|
||||||
|
- **Nome indice:** `geodata`
|
||||||
|
|
||||||
|
L'indice viene creato automaticamente dal servizio `data_loader` (contenitore Docker) oppure tramite lo script di inizializzazione usato nella fase di sviluppo.
|
||||||
|
|
||||||
|
### 1.2 Mapping principale
|
||||||
|
|
||||||
|
Il mapping dell’indice `geodata` è stato definito in modo esplicito per evitare problemi con i tipi dinamici e per supportare aggregazioni e visualizzazioni in Grafana.
|
||||||
|
|
||||||
|
Campi principali:
|
||||||
|
|
||||||
|
- `location` (`geo_point`)
|
||||||
|
- Coordinate geografiche del punto (latitudine/longitudine).
|
||||||
|
- `timestamp` (`date`)
|
||||||
|
- Data/ora dell’evento.
|
||||||
|
- `status` (`keyword`)
|
||||||
|
- Stato del ticket/attività. Valori possibili: `OPEN`, `CLOSED`, `IN_PROGRESS`, `ON_HOLD`.
|
||||||
|
- `timing_status` (`keyword`)
|
||||||
|
- Stato di puntualità, definito solo quando `status = CLOSED`.
|
||||||
|
- Valori possibili: `CLOSED_ON_TIME`, `CLOSED_LATE`.
|
||||||
|
- `lead_time` (`integer`)
|
||||||
|
- Tempo di lavorazione (in minuti) per le attività chiuse, usato per le medie nella dashboard.
|
||||||
|
- `operator` (`keyword`)
|
||||||
|
- Operatore responsabile (es. `Operator A`, `Operator B`, …).
|
||||||
|
- `duration` (`integer`)
|
||||||
|
- Durata generica dell’evento (valore grezzo usato solo come informativo).
|
||||||
|
- `task_type` (`keyword`)
|
||||||
|
- Tipo di task, usato nella dashboard **Tasks**. Valori esemplificativi:
|
||||||
|
- `Create Activity`
|
||||||
|
- `Close Activity`
|
||||||
|
- `Create WF`
|
||||||
|
- `Close WF`
|
||||||
|
- `Select DTO`
|
||||||
|
- `Report Generation`
|
||||||
|
- `Report QC`
|
||||||
|
- `min_duration` (`integer`)
|
||||||
|
- Durata minima simulata per il tipo di task (in secondi).
|
||||||
|
- `max_duration` (`integer`)
|
||||||
|
- Durata massima simulata per il tipo di task (in secondi).
|
||||||
|
- `avg_duration` (`integer`)
|
||||||
|
- Durata media simulata per il tipo di task (in secondi).
|
||||||
|
- `task_duration` (`integer`)
|
||||||
|
- Durata effettiva del singolo evento (in secondi).
|
||||||
|
- `id` (`integer`)
|
||||||
|
- Identificativo numerico univoco del record nella generazione mock.
|
||||||
|
|
||||||
|
### 1.3 Generazione e caricamento dati
|
||||||
|
|
||||||
|
- **Generazione dati**: file `generate_mock_data.py`
|
||||||
|
- Genera un file GeoJSON `data/sample.geojson` con 1000 feature.
|
||||||
|
- Popola per ogni feature:
|
||||||
|
- coordinate casuali in Europa,
|
||||||
|
- campi di stato (`status`, `timing_status`, `lead_time`),
|
||||||
|
- informazioni di task (`task_type`, durate, operatore, timestamp).
|
||||||
|
- **Caricamento in Elasticsearch**: file `loader/load_data.py`
|
||||||
|
- Attende che Elasticsearch sia disponibile.
|
||||||
|
- Crea l’indice `geodata` con il mapping descritto sopra.
|
||||||
|
- Legge `sample.geojson` e per ogni feature crea un documento con:
|
||||||
|
- `location.lat` / `location.lon`
|
||||||
|
- tutti i campi in `properties` (timestamp, status, operator, ecc.).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Dashboard "Activity"
|
||||||
|
|
||||||
|
La prima dashboard si chiama **"Activity"** ed è salvata in `grafana/dashboards/dashboard.json`.
|
||||||
|
|
||||||
|
### 2.1 Pannelli principali
|
||||||
|
|
||||||
|
1. **Activity Status** (table)
|
||||||
|
- **Datasource:** `Elasticsearch`
|
||||||
|
- **Query:** aggregazione `terms` sul campo `status` + metrica `count`.
|
||||||
|
- **Trasformazioni:**
|
||||||
|
- `organize` per rinominare i campi in `Status`, `Count`, `Avg Lead Time`.
|
||||||
|
- `calculateField` per calcolare il totale e la colonna `Proportion` (percentuale sul totale).
|
||||||
|
- **Colori:** override sul campo `Status` con testo colorato (OPEN giallo, CLOSED blu, IN_PROGRESS azzurro, ON_HOLD viola).
|
||||||
|
- **Uso:** fornisce il riepilogo delle attività per stato con la proporzione e il tempo medio di lavorazione.
|
||||||
|
|
||||||
|
2. **Activity Distribution** (geomap heatmap)
|
||||||
|
- **Datasource:** `Elasticsearch`
|
||||||
|
- **Query:** metrica `raw_data` con `query: "*"` e `timeField: "timestamp"`.
|
||||||
|
- **Layer:** tipo `heatmap` con:
|
||||||
|
- `location.mode = "coords"`
|
||||||
|
- `latitude = "location.lat"`, `longitude = "location.lon"`
|
||||||
|
- **Uso:** visualizza la distribuzione geografica degli eventi come heatmap sull’Europa.
|
||||||
|
|
||||||
|
3. **Timing Status** (table)
|
||||||
|
- **Datasource:** `Elasticsearch`
|
||||||
|
- **Query:** aggregazione `terms` su `timing_status` + metrica `count`.
|
||||||
|
- **Colori:**
|
||||||
|
- `CLOSED_ON_TIME` con background verde.
|
||||||
|
- `CLOSED_LATE` con background arancione.
|
||||||
|
- **Uso:** mostra il numero di attività chiuse in tempo vs in ritardo.
|
||||||
|
|
||||||
|
4. **On-Time vs Late** (pie chart)
|
||||||
|
- **Datasource:** `Elasticsearch`
|
||||||
|
- **Query:** identica a `Timing Status` (terms su `timing_status`).
|
||||||
|
- **Trasformazioni:**
|
||||||
|
- trasformazione per convertire i risultati dell’aggregazione in valori per il pie chart.
|
||||||
|
- **Uso:** rappresentazione grafica (torta) della ripartizione CLOSED_ON_TIME vs CLOSED_LATE.
|
||||||
|
|
||||||
|
5. **Activity Over Time** (time series)
|
||||||
|
- **Datasource:** `Elasticsearch`
|
||||||
|
- **Query:** `date_histogram` su `timestamp` + metrica `count`.
|
||||||
|
- **Uso:** andamento temporale del numero di eventi.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Dashboard "Tasks"
|
||||||
|
|
||||||
|
La seconda dashboard si chiama **"Tasks"** ed è salvata in `grafana/dashboards/tasks_dashboard.json`.
|
||||||
|
|
||||||
|
### 3.1 Tasks (tabella in alto a sinistra)
|
||||||
|
|
||||||
|
- **Datasource:** `Elasticsearch`
|
||||||
|
- **Query:**
|
||||||
|
- `terms` su `task_type` (raggruppamento per tipo di task).
|
||||||
|
- Metriche:
|
||||||
|
- `count` (Total)
|
||||||
|
- `min` su `task_duration` (Min Duration)
|
||||||
|
- `max` su `task_duration` (Max Duration)
|
||||||
|
- **Trasformazioni:**
|
||||||
|
- `organize` per rinominare i campi:
|
||||||
|
- `task_type → Task`
|
||||||
|
- `Count → Total`
|
||||||
|
- `Min task_duration → Min Duration`
|
||||||
|
- `Max task_duration → Max Duration`
|
||||||
|
- **Colori:** testo del campo `Task` colorato in azzurro.
|
||||||
|
- **Uso:** riepilogo per tipo di task con numero totale e range di durata.
|
||||||
|
|
||||||
|
### 3.2 Tasks by Operator (grafico a barre orizzontali)
|
||||||
|
|
||||||
|
- **Datasource:** `Elasticsearch`
|
||||||
|
- **Query:**
|
||||||
|
- Primo livello: `terms` su `operator` (asse Y del grafico).
|
||||||
|
- Secondo livello: `terms` su `task_type` (serie colorate).
|
||||||
|
- Metrica: `count`.
|
||||||
|
- **Trasformazioni:**
|
||||||
|
- `groupingToMatrix` (o equivalente) per trasformare l’output dell’aggregazione in una matrice con:
|
||||||
|
- `rowField = operator`
|
||||||
|
- `columnField = task_type`
|
||||||
|
- `valueField = Count`
|
||||||
|
- **Opzioni grafiche:**
|
||||||
|
- `orientation = horizontal`.
|
||||||
|
- `stacking = normal` per avere barre impilate per operatore.
|
||||||
|
- Legenda a destra con un colore per ogni tipo di task (`Create Activity`, `Close Activity`, `Create WF`, `Close WF`, `Select DTO`, `Report Generation`, `Report QC`).
|
||||||
|
- **Uso:** mostra per ogni operatore il numero di task, suddivisi per tipologia come barre orizzontali impilate.
|
||||||
|
|
||||||
|
### 3.3 Tasks (tabella in basso)
|
||||||
|
|
||||||
|
- **Datasource:** `Elasticsearch`
|
||||||
|
- **Query:**
|
||||||
|
- `terms` su `task_type` e `operator` (raggruppamento per Task + Operator).
|
||||||
|
- Metriche:
|
||||||
|
- `count` (Total)
|
||||||
|
- `min`/`max`/`avg` su `task_duration`.
|
||||||
|
- **Trasformazioni:**
|
||||||
|
- `organize` per ordinare le colonne e rinominare i campi in:
|
||||||
|
- `Task`, `Operator`, `Total`, `Min`, `Max`, `Average`.
|
||||||
|
- **Unità di misura:**
|
||||||
|
- Durate espresse in secondi, visualizzate in Grafana come secondi/minuti (in base al formato scelto nella colonna).
|
||||||
|
- **Uso:** tabella dettagliata per tipo di task e operatore, con statistiche di durata.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Note operative
|
||||||
|
|
||||||
|
- **Provisioning Grafana**
|
||||||
|
- I file delle dashboard sono montati nel container Grafana tramite Docker Compose.
|
||||||
|
- Ogni modifica ai file JSON (`dashboard.json`, `tasks_dashboard.json`) richiede un **riavvio del container Grafana** (`docker compose restart grafana`).
|
||||||
|
|
||||||
|
- **Datasource Elasticsearch**
|
||||||
|
- È definito tramite provisioning (file `datasource.yml`) usando:
|
||||||
|
- `jsonData.index = "geodata"`
|
||||||
|
- `jsonData.timeField = "timestamp"`
|
||||||
|
- Non viene usata la `uid` del datasource nelle dashboard: i pannelli si riferiscono al datasource tramite `"datasource": "Elasticsearch"` per evitare problemi di provisioning.
|
||||||
|
|
||||||
|
- **Rigenerazione dati**
|
||||||
|
- Per rigenerare i dati mock:
|
||||||
|
1. Eseguire `python3 generate_mock_data.py` nella root del progetto.
|
||||||
|
2. Cancellare l’indice `geodata` in Elasticsearch.
|
||||||
|
3. Riavviare il container `data_loader` in modo che ricrei l’indice con il mapping corretto e ricarichi `sample.geojson`.
|
||||||
|
|
||||||
|
Questo documento descrive lo stato corrente della soluzione (stack Docker Elasticsearch + Grafana + loader Python) usata per visualizzare dati geospaziali e metriche di attività/task nelle dashboard **Activity** e **Tasks**.
|
||||||
Binary file not shown.
+22513
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
|||||||
|
services:
|
||||||
|
elasticsearch:
|
||||||
|
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.10
|
||||||
|
container_name: elasticsearch
|
||||||
|
environment:
|
||||||
|
- node.name=elasticsearch
|
||||||
|
- discovery.type=single-node
|
||||||
|
- bootstrap.memory_lock=true
|
||||||
|
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
||||||
|
- xpack.security.enabled=false
|
||||||
|
ulimits:
|
||||||
|
memlock:
|
||||||
|
soft: -1
|
||||||
|
hard: -1
|
||||||
|
volumes:
|
||||||
|
- ./es_data:/usr/share/elasticsearch/data
|
||||||
|
ports:
|
||||||
|
- "9200:9200"
|
||||||
|
networks:
|
||||||
|
- grafana-net
|
||||||
|
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:latest
|
||||||
|
container_name: grafana
|
||||||
|
volumes:
|
||||||
|
- ./grafana_data:/var/lib/grafana
|
||||||
|
- ./grafana/provisioning:/etc/grafana/provisioning
|
||||||
|
- ./grafana/dashboards:/var/lib/grafana/dashboards
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- GF_SECURITY_ADMIN_USER=admin
|
||||||
|
- GF_SECURITY_ADMIN_PASSWORD=admin
|
||||||
|
depends_on:
|
||||||
|
- elasticsearch
|
||||||
|
networks:
|
||||||
|
- grafana-net
|
||||||
|
|
||||||
|
data_loader:
|
||||||
|
build: ./loader
|
||||||
|
container_name: data_loader
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
depends_on:
|
||||||
|
- elasticsearch
|
||||||
|
networks:
|
||||||
|
- grafana-net
|
||||||
|
|
||||||
|
networks:
|
||||||
|
grafana-net:
|
||||||
|
driver: bridge
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
file_path = 'grafana/dashboards/dashboard.json'
|
||||||
|
|
||||||
|
with open(file_path, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
def fix_datasource(obj):
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
if 'datasource' in obj and isinstance(obj['datasource'], dict):
|
||||||
|
if obj['datasource'].get('type') == 'elasticsearch':
|
||||||
|
obj['datasource'] = 'Elasticsearch'
|
||||||
|
|
||||||
|
for key, value in obj.items():
|
||||||
|
fix_datasource(value)
|
||||||
|
elif isinstance(obj, list):
|
||||||
|
for item in obj:
|
||||||
|
fix_datasource(item)
|
||||||
|
|
||||||
|
fix_datasource(data)
|
||||||
|
|
||||||
|
with open(file_path, 'w') as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import json
|
||||||
|
import random
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
def generate_random_point():
|
||||||
|
# Coordinate approssimative dell'Europa
|
||||||
|
lon = random.uniform(-10.0, 30.0)
|
||||||
|
lat = random.uniform(35.0, 60.0)
|
||||||
|
return [lon, lat]
|
||||||
|
|
||||||
|
def generate_data(num_points=500):
|
||||||
|
features = []
|
||||||
|
operators = ["Operator A", "Operator B", "Operator C", "Operator D", "Operator E"]
|
||||||
|
# Status principali (senza timing)
|
||||||
|
statuses = ["OPEN", "CLOSED", "IN_PROGRESS", "ON_HOLD"]
|
||||||
|
# Timing status solo per CLOSED
|
||||||
|
timing_statuses = ["CLOSED_ON_TIME", "CLOSED_LATE"]
|
||||||
|
# Task types
|
||||||
|
task_types = ["Create Activity", "Close Activity", "Create WF", "Close WF", "Select DTO", "Report Generation", "Report QC"]
|
||||||
|
|
||||||
|
base_time = datetime.now()
|
||||||
|
|
||||||
|
for i in range(num_points):
|
||||||
|
status = random.choice(statuses)
|
||||||
|
operator = random.choice(operators)
|
||||||
|
duration = random.randint(10, 300)
|
||||||
|
task_type = random.choice(task_types)
|
||||||
|
|
||||||
|
# Durations per task (in secondi) - min, max, avg variano per task
|
||||||
|
min_duration = random.randint(10, 25)
|
||||||
|
max_duration = random.randint(40, 60)
|
||||||
|
avg_duration = random.randint(min_duration + 5, max_duration - 5)
|
||||||
|
task_duration = random.randint(min_duration, max_duration + 20)
|
||||||
|
|
||||||
|
# Lead time in minuti (per Avg Lead Time) - solo per CLOSED
|
||||||
|
lead_time = None
|
||||||
|
timing_status = None
|
||||||
|
if status == "CLOSED":
|
||||||
|
lead_time = random.randint(5, 120) # minuti
|
||||||
|
# 70% on time, 30% late (come in figura ~24 vs 11)
|
||||||
|
timing_status = random.choices(timing_statuses, weights=[70, 30])[0]
|
||||||
|
|
||||||
|
# Random timestamp negli ultimi 7 giorni
|
||||||
|
time_offset = random.randint(0, 7 * 24 * 60 * 60)
|
||||||
|
timestamp = (base_time - timedelta(seconds=time_offset)).isoformat()
|
||||||
|
|
||||||
|
properties = {
|
||||||
|
"status": status,
|
||||||
|
"operator": operator,
|
||||||
|
"duration": duration,
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"id": i,
|
||||||
|
"task_type": task_type,
|
||||||
|
"min_duration": min_duration,
|
||||||
|
"max_duration": max_duration,
|
||||||
|
"avg_duration": avg_duration,
|
||||||
|
"task_duration": task_duration
|
||||||
|
}
|
||||||
|
|
||||||
|
# Aggiungi lead_time e timing_status solo se CLOSED
|
||||||
|
if lead_time is not None:
|
||||||
|
properties["lead_time"] = lead_time
|
||||||
|
if timing_status is not None:
|
||||||
|
properties["timing_status"] = timing_status
|
||||||
|
|
||||||
|
feature = {
|
||||||
|
"type": "Feature",
|
||||||
|
"properties": properties,
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": generate_random_point()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
features.append(feature)
|
||||||
|
|
||||||
|
geojson = {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": features
|
||||||
|
}
|
||||||
|
|
||||||
|
with open('data/sample.geojson', 'w') as f:
|
||||||
|
json.dump(geojson, f, indent=2)
|
||||||
|
|
||||||
|
print(f"Generated {num_points} data points in data/sample.geojson")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_data(1000)
|
||||||
@@ -0,0 +1,716 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": {
|
||||||
|
"type": "grafana",
|
||||||
|
"uid": "-- Grafana --"
|
||||||
|
},
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"fiscalYearStartMonth": 0,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"id": 1,
|
||||||
|
"links": [],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"align": "auto",
|
||||||
|
"cellOptions": {
|
||||||
|
"type": "auto"
|
||||||
|
},
|
||||||
|
"inspect": false
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Status"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "mappings",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"CLOSED": {
|
||||||
|
"color": "blue",
|
||||||
|
"index": 1,
|
||||||
|
"text": "CLOSED"
|
||||||
|
},
|
||||||
|
"IN_PROGRESS": {
|
||||||
|
"color": "light-blue",
|
||||||
|
"index": 2,
|
||||||
|
"text": "IN_PROGRESS"
|
||||||
|
},
|
||||||
|
"ON_HOLD": {
|
||||||
|
"color": "purple",
|
||||||
|
"index": 3,
|
||||||
|
"text": "ON_HOLD"
|
||||||
|
},
|
||||||
|
"OPEN": {
|
||||||
|
"color": "yellow",
|
||||||
|
"index": 0,
|
||||||
|
"text": "OPEN"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custom.cellOptions",
|
||||||
|
"value": {
|
||||||
|
"type": "color-text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Proportion"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "percentunit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "decimals",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Avg Lead Time"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "m"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "decimals",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 8,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 1,
|
||||||
|
"options": {
|
||||||
|
"cellHeight": "sm",
|
||||||
|
"footer": {
|
||||||
|
"countRows": false,
|
||||||
|
"fields": "",
|
||||||
|
"reducer": [
|
||||||
|
"sum"
|
||||||
|
],
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
"showHeader": true
|
||||||
|
},
|
||||||
|
"pluginVersion": "12.2.0-16711121739",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"bucketAggs": [
|
||||||
|
{
|
||||||
|
"field": "status",
|
||||||
|
"id": "2",
|
||||||
|
"settings": {
|
||||||
|
"min_doc_count": "1",
|
||||||
|
"order": "desc",
|
||||||
|
"orderBy": "_count",
|
||||||
|
"size": "10"
|
||||||
|
},
|
||||||
|
"type": "terms"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"metrics": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"type": "count"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "lead_time",
|
||||||
|
"id": "3",
|
||||||
|
"type": "avg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "",
|
||||||
|
"refId": "A",
|
||||||
|
"timeField": "timestamp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Activity Status",
|
||||||
|
"transformations": [
|
||||||
|
{
|
||||||
|
"id": "organize",
|
||||||
|
"options": {
|
||||||
|
"excludeByName": {},
|
||||||
|
"indexByName": {},
|
||||||
|
"renameByName": {
|
||||||
|
"Average lead_time": "Avg Lead Time",
|
||||||
|
"Count": "Count",
|
||||||
|
"status": "Status"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "calculateField",
|
||||||
|
"options": {
|
||||||
|
"alias": "Total",
|
||||||
|
"mode": "reduceRow",
|
||||||
|
"reduce": {
|
||||||
|
"reducer": "sum"
|
||||||
|
},
|
||||||
|
"replaceFields": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "calculateField",
|
||||||
|
"options": {
|
||||||
|
"alias": "Proportion",
|
||||||
|
"binary": {
|
||||||
|
"left": "Count",
|
||||||
|
"operator": "/",
|
||||||
|
"right": "Total"
|
||||||
|
},
|
||||||
|
"mode": "binary",
|
||||||
|
"replaceFields": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "organize",
|
||||||
|
"options": {
|
||||||
|
"excludeByName": {
|
||||||
|
"Total": true
|
||||||
|
},
|
||||||
|
"indexByName": {
|
||||||
|
"Avg Lead Time": 3,
|
||||||
|
"Count": 1,
|
||||||
|
"Proportion": 2,
|
||||||
|
"Status": 0
|
||||||
|
},
|
||||||
|
"renameByName": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "table"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"custom": {
|
||||||
|
"hideFrom": {
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false,
|
||||||
|
"viz": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 16,
|
||||||
|
"x": 8,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 2,
|
||||||
|
"options": {
|
||||||
|
"basemap": {
|
||||||
|
"config": {},
|
||||||
|
"name": "Layer 0",
|
||||||
|
"type": "default"
|
||||||
|
},
|
||||||
|
"controls": {
|
||||||
|
"mouseWheelZoom": true,
|
||||||
|
"showAttribution": true,
|
||||||
|
"showDebug": false,
|
||||||
|
"showMeasure": false,
|
||||||
|
"showScale": false,
|
||||||
|
"showZoom": true
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"blur": 4,
|
||||||
|
"radius": 5,
|
||||||
|
"weight": {
|
||||||
|
"fixed": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"latitude": "location.lat",
|
||||||
|
"longitude": "location.lon",
|
||||||
|
"mode": "coords"
|
||||||
|
},
|
||||||
|
"name": "Heatmap",
|
||||||
|
"tooltip": true,
|
||||||
|
"type": "heatmap"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "details"
|
||||||
|
},
|
||||||
|
"view": {
|
||||||
|
"allLayers": true,
|
||||||
|
"id": "europe",
|
||||||
|
"lat": 48,
|
||||||
|
"lon": 15,
|
||||||
|
"noRepeat": false,
|
||||||
|
"zoom": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "12.2.0-16711121739",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"alias": "",
|
||||||
|
"bucketAggs": [],
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"metrics": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"settings": {
|
||||||
|
"size": "500"
|
||||||
|
},
|
||||||
|
"type": "raw_data"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "*",
|
||||||
|
"refId": "A",
|
||||||
|
"timeField": "timestamp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Activity Distribution",
|
||||||
|
"type": "geomap"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"align": "auto",
|
||||||
|
"cellOptions": {
|
||||||
|
"type": "auto"
|
||||||
|
},
|
||||||
|
"inspect": false
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Timing Status"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "mappings",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"CLOSED_LATE": {
|
||||||
|
"color": "orange",
|
||||||
|
"index": 1,
|
||||||
|
"text": "CLOSED_LATE"
|
||||||
|
},
|
||||||
|
"CLOSED_ON_TIME": {
|
||||||
|
"color": "green",
|
||||||
|
"index": 0,
|
||||||
|
"text": "CLOSED_ON_TIME"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custom.cellOptions",
|
||||||
|
"value": {
|
||||||
|
"type": "color-background"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 7,
|
||||||
|
"w": 11,
|
||||||
|
"x": 0,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"id": 3,
|
||||||
|
"options": {
|
||||||
|
"cellHeight": "sm",
|
||||||
|
"footer": {
|
||||||
|
"countRows": false,
|
||||||
|
"fields": "",
|
||||||
|
"reducer": [
|
||||||
|
"sum"
|
||||||
|
],
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
"showHeader": true
|
||||||
|
},
|
||||||
|
"pluginVersion": "12.2.0-16711121739",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"bucketAggs": [
|
||||||
|
{
|
||||||
|
"field": "timing_status",
|
||||||
|
"id": "2",
|
||||||
|
"settings": {
|
||||||
|
"min_doc_count": "1",
|
||||||
|
"order": "desc",
|
||||||
|
"orderBy": "_count",
|
||||||
|
"size": "10"
|
||||||
|
},
|
||||||
|
"type": "terms"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"metrics": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"type": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "timing_status:*",
|
||||||
|
"refId": "A",
|
||||||
|
"timeField": "timestamp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Timing Status",
|
||||||
|
"transformations": [
|
||||||
|
{
|
||||||
|
"id": "organize",
|
||||||
|
"options": {
|
||||||
|
"excludeByName": {},
|
||||||
|
"indexByName": {},
|
||||||
|
"renameByName": {
|
||||||
|
"Count": "Count",
|
||||||
|
"timing_status": "Timing Status"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "table"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"hideFrom": {
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false,
|
||||||
|
"viz": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": []
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "CLOSED_ON_TIME"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "green",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "CLOSED_LATE"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "red",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 7,
|
||||||
|
"w": 13,
|
||||||
|
"x": 11,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"id": 5,
|
||||||
|
"options": {
|
||||||
|
"displayLabels": [],
|
||||||
|
"legend": {
|
||||||
|
"displayMode": "table",
|
||||||
|
"placement": "right",
|
||||||
|
"showLegend": true,
|
||||||
|
"values": [
|
||||||
|
"value"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pieType": "pie",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"hideZeros": false,
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "12.2.0-16711121739",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"bucketAggs": [
|
||||||
|
{
|
||||||
|
"field": "timing_status",
|
||||||
|
"id": "2",
|
||||||
|
"settings": {
|
||||||
|
"min_doc_count": "1",
|
||||||
|
"order": "desc",
|
||||||
|
"orderBy": "_count",
|
||||||
|
"size": "10"
|
||||||
|
},
|
||||||
|
"type": "terms"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"metrics": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"type": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "timing_status:*",
|
||||||
|
"refId": "A",
|
||||||
|
"timeField": "timestamp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "On-Time vs Late",
|
||||||
|
"transformations": [
|
||||||
|
{
|
||||||
|
"id": "rowsToFields",
|
||||||
|
"options": {
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"fieldName": "timing_status",
|
||||||
|
"handlerKey": "field.name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName": "Count",
|
||||||
|
"handlerKey": "field.value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "piechart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisBorderShow": false,
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"barWidthFactor": 0.6,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 0,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false,
|
||||||
|
"viz": false
|
||||||
|
},
|
||||||
|
"insertNulls": false,
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "auto",
|
||||||
|
"spanNulls": false,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
},
|
||||||
|
"thresholdsStyle": {
|
||||||
|
"mode": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 15
|
||||||
|
},
|
||||||
|
"id": 4,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"hideZeros": false,
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "12.2.0-16711121739",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"bucketAggs": [
|
||||||
|
{
|
||||||
|
"field": "timestamp",
|
||||||
|
"id": "2",
|
||||||
|
"settings": {
|
||||||
|
"interval": "auto",
|
||||||
|
"min_doc_count": "0",
|
||||||
|
"trimEdges": "0"
|
||||||
|
},
|
||||||
|
"type": "date_histogram"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"metrics": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"type": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "",
|
||||||
|
"refId": "A",
|
||||||
|
"timeField": "timestamp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Activity Over Time",
|
||||||
|
"type": "timeseries"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preload": false,
|
||||||
|
"refresh": "",
|
||||||
|
"schemaVersion": 41,
|
||||||
|
"tags": [],
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-90d",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "Activity",
|
||||||
|
"uid": "geo-dashboard-01",
|
||||||
|
"version": 17
|
||||||
|
}
|
||||||
@@ -0,0 +1,586 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"fiscalYearStartMonth": 0,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"id": null,
|
||||||
|
"links": [],
|
||||||
|
"liveNow": false,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"align": "auto",
|
||||||
|
"cellOptions": {
|
||||||
|
"type": "auto"
|
||||||
|
},
|
||||||
|
"inspect": false
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Task"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "custom.cellOptions",
|
||||||
|
"value": {
|
||||||
|
"type": "color-text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "light-blue",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Min Duration"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "s"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Max Duration"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "s"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 10,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 1,
|
||||||
|
"options": {
|
||||||
|
"footer": {
|
||||||
|
"fields": "",
|
||||||
|
"reducer": ["sum"],
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
"showHeader": true
|
||||||
|
},
|
||||||
|
"pluginVersion": "10.0.0",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"bucketAggs": [
|
||||||
|
{
|
||||||
|
"field": "task_type",
|
||||||
|
"id": "2",
|
||||||
|
"settings": {
|
||||||
|
"min_doc_count": "1",
|
||||||
|
"order": "desc",
|
||||||
|
"orderBy": "_count",
|
||||||
|
"size": "10"
|
||||||
|
},
|
||||||
|
"type": "terms"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"metrics": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"type": "count"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"type": "min",
|
||||||
|
"field": "task_duration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5",
|
||||||
|
"type": "max",
|
||||||
|
"field": "task_duration"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "",
|
||||||
|
"refId": "A",
|
||||||
|
"timeField": "timestamp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Tasks",
|
||||||
|
"transformations": [
|
||||||
|
{
|
||||||
|
"id": "organize",
|
||||||
|
"options": {
|
||||||
|
"excludeByName": {},
|
||||||
|
"indexByName": {},
|
||||||
|
"renameByName": {
|
||||||
|
"task_type": "Task",
|
||||||
|
"Count": "Total",
|
||||||
|
"Min task_duration": "Min Duration",
|
||||||
|
"Max task_duration": "Max Duration"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "table"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisBorderShow": false,
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"fillOpacity": 80,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false,
|
||||||
|
"viz": false
|
||||||
|
},
|
||||||
|
"lineWidth": 1,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"thresholdsStyle": {
|
||||||
|
"mode": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Create Activity"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "blue",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Close Activity"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "green",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Create WF"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "light-blue",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Close WF"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "dark-green",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Select DTO"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "yellow",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Report Generation"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "orange",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Report QC"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "purple",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 10,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 2,
|
||||||
|
"options": {
|
||||||
|
"barRadius": 0,
|
||||||
|
"barWidth": 0.8,
|
||||||
|
"fullHighlight": false,
|
||||||
|
"groupWidth": 0.7,
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "right",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"orientation": "horizontal",
|
||||||
|
"showValue": "never",
|
||||||
|
"stacking": "normal",
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
},
|
||||||
|
"xTickLabelRotation": 0,
|
||||||
|
"xTickLabelSpacing": 0
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"bucketAggs": [
|
||||||
|
{
|
||||||
|
"field": "operator",
|
||||||
|
"id": "2",
|
||||||
|
"settings": {
|
||||||
|
"min_doc_count": "1",
|
||||||
|
"order": "asc",
|
||||||
|
"orderBy": "_term",
|
||||||
|
"size": "10"
|
||||||
|
},
|
||||||
|
"type": "terms"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "task_type",
|
||||||
|
"id": "3",
|
||||||
|
"settings": {
|
||||||
|
"min_doc_count": "1",
|
||||||
|
"order": "desc",
|
||||||
|
"orderBy": "_count",
|
||||||
|
"size": "10"
|
||||||
|
},
|
||||||
|
"type": "terms"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"metrics": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"type": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "",
|
||||||
|
"refId": "A",
|
||||||
|
"timeField": "timestamp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Tasks by Operator",
|
||||||
|
"transformations": [
|
||||||
|
{
|
||||||
|
"id": "groupingToMatrix",
|
||||||
|
"options": {
|
||||||
|
"columnField": "task_type",
|
||||||
|
"rowField": "operator",
|
||||||
|
"valueField": "Count",
|
||||||
|
"emptyValue": "null"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "barchart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"align": "auto",
|
||||||
|
"cellOptions": {
|
||||||
|
"type": "auto"
|
||||||
|
},
|
||||||
|
"inspect": false
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Task"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "custom.cellOptions",
|
||||||
|
"value": {
|
||||||
|
"type": "color-text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "light-blue",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Min"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "s"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Max"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "s"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Average"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "decimals",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 10,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 10
|
||||||
|
},
|
||||||
|
"id": 3,
|
||||||
|
"options": {
|
||||||
|
"footer": {
|
||||||
|
"fields": "",
|
||||||
|
"reducer": ["sum"],
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
"showHeader": true
|
||||||
|
},
|
||||||
|
"pluginVersion": "10.0.0",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"bucketAggs": [
|
||||||
|
{
|
||||||
|
"field": "task_type",
|
||||||
|
"id": "2",
|
||||||
|
"settings": {
|
||||||
|
"min_doc_count": "1",
|
||||||
|
"order": "desc",
|
||||||
|
"orderBy": "_count",
|
||||||
|
"size": "10"
|
||||||
|
},
|
||||||
|
"type": "terms"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "operator",
|
||||||
|
"id": "3",
|
||||||
|
"settings": {
|
||||||
|
"min_doc_count": "1",
|
||||||
|
"order": "asc",
|
||||||
|
"orderBy": "_term",
|
||||||
|
"size": "10"
|
||||||
|
},
|
||||||
|
"type": "terms"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasource": "Elasticsearch",
|
||||||
|
"metrics": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"type": "count"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"type": "min",
|
||||||
|
"field": "task_duration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5",
|
||||||
|
"type": "max",
|
||||||
|
"field": "task_duration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6",
|
||||||
|
"type": "avg",
|
||||||
|
"field": "task_duration"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "",
|
||||||
|
"refId": "A",
|
||||||
|
"timeField": "timestamp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Tasks",
|
||||||
|
"transformations": [
|
||||||
|
{
|
||||||
|
"id": "organize",
|
||||||
|
"options": {
|
||||||
|
"excludeByName": {},
|
||||||
|
"indexByName": {
|
||||||
|
"task_type": 0,
|
||||||
|
"operator": 1,
|
||||||
|
"Count": 2,
|
||||||
|
"Min task_duration": 3,
|
||||||
|
"Max task_duration": 4,
|
||||||
|
"Average task_duration": 5
|
||||||
|
},
|
||||||
|
"renameByName": {
|
||||||
|
"task_type": "Task",
|
||||||
|
"operator": "Operator",
|
||||||
|
"Count": "Total",
|
||||||
|
"Min task_duration": "Min",
|
||||||
|
"Max task_duration": "Max",
|
||||||
|
"Average task_duration": "Average"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "table"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh": "",
|
||||||
|
"schemaVersion": 38,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [],
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-90d",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "Tasks",
|
||||||
|
"uid": "tasks-dashboard-01",
|
||||||
|
"version": 1,
|
||||||
|
"weekStart": ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: 'Default'
|
||||||
|
orgId: 1
|
||||||
|
folder: ''
|
||||||
|
type: file
|
||||||
|
disableDeletion: false
|
||||||
|
updateIntervalSeconds: 10
|
||||||
|
options:
|
||||||
|
path: /var/lib/grafana/dashboards
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
datasources:
|
||||||
|
- name: Elasticsearch
|
||||||
|
type: elasticsearch
|
||||||
|
access: proxy
|
||||||
|
url: http://elasticsearch:9200
|
||||||
|
isDefault: true
|
||||||
|
jsonData:
|
||||||
|
index: "geodata"
|
||||||
|
timeField: "timestamp"
|
||||||
|
esVersion: "7.10.0"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
FROM python:3.9-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY load_data.py .
|
||||||
|
|
||||||
|
CMD ["python", "load_data.py"]
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
from elasticsearch import Elasticsearch, helpers
|
||||||
|
|
||||||
|
# Configurazione logging
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
|
||||||
|
# Configurazione Elasticsearch
|
||||||
|
ES_HOST = os.environ.get('ES_HOST', 'elasticsearch')
|
||||||
|
ES_PORT = os.environ.get('ES_PORT', '9200')
|
||||||
|
INDEX_NAME = 'geodata'
|
||||||
|
GEOJSON_FILE = '/app/data/sample.geojson'
|
||||||
|
|
||||||
|
def wait_for_elasticsearch(es):
|
||||||
|
"""Attende che Elasticsearch sia pronto."""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
if es.ping():
|
||||||
|
logging.info("Elasticsearch è pronto!")
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
logging.info("In attesa di Elasticsearch...")
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
def create_index(es):
|
||||||
|
"""Crea l'indice con il mapping corretto per i dati geospaziali."""
|
||||||
|
mapping = {
|
||||||
|
"mappings": {
|
||||||
|
"properties": {
|
||||||
|
"location": {
|
||||||
|
"type": "geo_point"
|
||||||
|
},
|
||||||
|
"timestamp": {
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"operator": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"duration": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"timing_status": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"lead_time": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"task_type": {
|
||||||
|
"type": "keyword"
|
||||||
|
},
|
||||||
|
"min_duration": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"max_duration": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"avg_duration": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"task_duration": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if es.indices.exists(index=INDEX_NAME):
|
||||||
|
logging.info(f"L'indice '{INDEX_NAME}' esiste già.")
|
||||||
|
else:
|
||||||
|
es.indices.create(index=INDEX_NAME, body=mapping)
|
||||||
|
logging.info(f"Indice '{INDEX_NAME}' creato con successo.")
|
||||||
|
|
||||||
|
def process_geojson(file_path):
|
||||||
|
"""Legge il file GeoJSON e prepara i documenti per Elasticsearch."""
|
||||||
|
with open(file_path, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
actions = []
|
||||||
|
for feature in data.get('features', []):
|
||||||
|
geometry = feature.get('geometry')
|
||||||
|
properties = feature.get('properties', {})
|
||||||
|
|
||||||
|
# Assicuriamoci che ci sia una geometria di tipo Point
|
||||||
|
if geometry and geometry.get('type') == 'Point':
|
||||||
|
lon, lat = geometry.get('coordinates')
|
||||||
|
|
||||||
|
# Creiamo il documento
|
||||||
|
doc = {
|
||||||
|
"_index": INDEX_NAME,
|
||||||
|
"_source": {
|
||||||
|
"location": {
|
||||||
|
"lat": lat,
|
||||||
|
"lon": lon
|
||||||
|
},
|
||||||
|
# Usa il timestamp presente o quello attuale
|
||||||
|
"timestamp": properties.get('timestamp', datetime.now().isoformat()),
|
||||||
|
"status": properties.get('status', 'UNKNOWN'),
|
||||||
|
"operator": properties.get('operator', 'Unknown Operator'),
|
||||||
|
"duration": properties.get('duration', 0),
|
||||||
|
**properties # Includi tutte le altre proprietà
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actions.append(doc)
|
||||||
|
return actions
|
||||||
|
|
||||||
|
def main():
|
||||||
|
es = Elasticsearch([f"http://{ES_HOST}:{ES_PORT}"])
|
||||||
|
|
||||||
|
wait_for_elasticsearch(es)
|
||||||
|
create_index(es)
|
||||||
|
|
||||||
|
# Controllo se ci sono già dati per evitare duplicati
|
||||||
|
try:
|
||||||
|
count = es.count(index=INDEX_NAME)['count']
|
||||||
|
if count > 0:
|
||||||
|
logging.info(f"L'indice '{INDEX_NAME}' contiene già {count} documenti. Salto il caricamento.")
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if os.path.exists(GEOJSON_FILE):
|
||||||
|
logging.info(f"Caricamento dati da {GEOJSON_FILE}...")
|
||||||
|
actions = process_geojson(GEOJSON_FILE)
|
||||||
|
if actions:
|
||||||
|
helpers.bulk(es, actions)
|
||||||
|
logging.info(f"Caricati {len(actions)} documenti in Elasticsearch.")
|
||||||
|
else:
|
||||||
|
logging.warning("Nessun dato valido trovato nel file GeoJSON.")
|
||||||
|
else:
|
||||||
|
logging.error(f"File {GEOJSON_FILE} non trovato. Assicurati di montare il volume correttamente.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
elasticsearch==7.17.0
|
||||||
|
requests
|
||||||
Reference in New Issue
Block a user