commit
977c60e579
15 changed files with 24451 additions and 0 deletions
@ -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.
@ -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() |
||||||
Loading…
Reference in new issue