# dashboard_refresh.py — Guida di Riferimento

## Principi

- **Solo dati reali.** Niente stime, niente fuffa, niente placeholder.
- DeepSeek V4 Flash su api.deepseek.com = **gratuito** (nessun costo reale).
- Claude Sonnet 4 via OpenRouter = costo reale da `estimated_cost_usd` nella sessione.
- Se non hai una fonte dati, non mettere il widget.

## Struttura dati output (`dashboard_data.json`)

```json
{
  "timestamp": "14/06/2026 HH:MM",
  "salute": { "cpu": "X%", "ram": "Y%", "disk": "Z%" },
  "server": { "hostname", "uptime", "ip", "port" },
  "provider": { "attuale", "modello", "fallback" },
  "costi": {
    "today": { "cost", "calls", "provider", "model", "note" },
    "week": { "cost", "days" },
    "month": 0.0
  },
  "tickets": { "ticket_attivo": {...}, "totali", "ultimo_aggiornamento" },
  "cron_jobs": [...],
  "skills": { "emma", "total", "shared" }
}
```

## Cron job — no_agent

Lo script è eseguito dal cron `dashboard-refresh` in modalita `no_agent=True`.
Questo significa che:

- **PATH non include `~/.local/bin`** — va aggiunto esplicitamente per chiamare `hermes cron list`
- Non ha accesso alle hermes_tools (execute_code) — solo subprocess
- L'output stampato su stdout non arriva da nessuna parte (deliver=local)

## Funzioni chiave

### get_cron_jobs()

Parsa output testuale di `hermes cron list` con regex.
Usare `line.rstrip()` non `line.strip()` — toglie solo spazi a destra.

### get_skills_info()

Conta directory in `~/.hermes/skills/`. Non usare `hermes skills list` — non c'e flag `--json`.

### load_tickets()

Legge TUTTI i file `.json` in `~/.hermes/tickets/`, in ordine alfabetico inverso (nome file più recente prima).

Per ogni file JSON crea un entry con i campi:
- `id` — da `ticket_id` o `id` o nome file
- `titolo` — da `title` o `titolo`
- `stato`, `priorità` — mapping diretto
- `assegnato_a` — puo essere stringa o lista; se lista, appiattire a stringa con `", ".join()`
- `completati` — `len(ticket_data.get("work_completed", []))`
- `deliverables` — `len(ticket_data.get("deliverables", []))`
- `json_file` — **il nome del file** (es. `talent_risk_2026_06_14.json`)

Quest'ultimo campo è cruciale per i **link cliccabili** nell'HTML della dashboard (vedi Sezione Ticket nell'HTML).

L'HTML renderizza:
```javascript
const ticketUrl = '/tickets/' + (t.json_file || t.id.toLowerCase() + '.json');
// Link arancione (#f0883e) con target=_blank
```

**Controllo `assegnato_a`**: se il ticket JSON usa una lista invece di stringa:
```python
if isinstance(entry["assegnato_a"], list):
    entry["assegnato_a"] = ", ".join(entry["assegnato_a"])
```

**Badge colori HTML:**
- `stato === 'aperto'` o `'open'` → `badge-red` (rosso, non verde!)
- `priorità === 'alta'` o `'high'` → `badge-red`
- `priorità === 'media'` o `'medium'` → `badge-yellow`

**Totali bug:** Se ci sono file residui (es. `current_status.json` vecchio), il conteggio `totali` è gonfiato. Pulire i file vecchi dalla cartella.

### Token tracking

- `last_lpt.txt` memorizza l'ultimo `last_prompt_tokens` processato
- A ogni esecuzione calcola `delta = current_lpt - last_lpt`
- Se delta > 0 e session_cost > 0 e modello è Sonnet -> costo reale
- Se delta > 0 e session_cost = 0 -> DeepSeek gratuito, conta solo chiamate
- Se delta = 0 -> nessuna attività recente

### ⚠️ Limite del delta tracking — costi storici persi

`last_lpt.txt` traccia solo la sessione **corrente**. Sessioni Sonnet passate (giorni precedenti) non vengono rilevate dal delta. Se `token_costs.json` viene perso o resettato:

1. I costi Sonnet storici **non vengono recuperati automaticamente**
2. Vanno cercati nello storico sessioni: `session_search(query="Sonnet 4 cost")`
3. Recuperare `estimated_cost_usd` dalla sessione giusta
4. Scrivere manualmente la voce in `token_costs.json`
5. Rilanciare lo script per ricalcolare settimana/mese

## Costanti PREZZI

Non usare `PRICING` per stimare DeepSeek — e gratuito. Tenere solo per riferimento Sonnet:

```python
PRICING = {
    "claude-sonnet-4": {"input": 15.00, "output": 75.00},
}
```

KNOWN_COSTS e stato rimosso — non forzare costi hardcoded se la sessione li fornisce gia.
