# Migration Laravel — Application "Analyses"

## Contexte

Ce dossier est une application PHP/Python de surveillance des biotoxines marines pour
des sites de mytiliculture (moules). Elle récupère des données depuis l'API INTECMAR
(Galice, Espagne), les stocke en MySQL et les affiche sur une carte SVG interactive.

Le fichier `analyses_dump.sql` contient la base de données complète (structure + données).
L'objectif est de recréer cette application dans Laravel en conservant le front-end JS
intact et les scripts Python tels quels.

---

## Ce qu'il faut faire

### 1. Base de données

Importer `analyses_dump.sql` dans la base MySQL Laravel :

```bash
mysql -u USER -p DATABASE < analyses_dump.sql
```

Tables actives (à modéliser avec Eloquent) :

| Table | Modèle Eloquent | Rôle |
|-------|----------------|------|
| `analyses_sites` | `Site` | Catalogue des sites de production |
| `analyses_releves` | `Releve` | Analyses biotoxines par site/date/profondeur |
| `analyses_sites_historique` | `SiteHistorique` | Historique des changements d'état administratif |
| `analyses_zones` | `Zone` | Zones géographiques (regroupent des sites) |
| `lieux` | `Lieu` | Lieux (regroupent des zones) |

Tables legacy présentes dans le dump (ne pas modéliser, ne pas supprimer) :
- `sites2`, `zones`, `toxines_seuils`

Relations :
```
Lieu (1) ──── (N) Zone (1) ──── (N) Site
                                     ├── (N) Releve
                                     └── (N) SiteHistorique
```

Clé de liaison entre `analyses_sites` et les autres tables :
- `analyses_sites.id` → clé primaire interne (Laravel)
- `analyses_sites.id_site` → identifiant externe INTECMAR (utilisé dans `analyses_releves`)

### 2. Configuration .env Laravel

Ajouter ces variables au `.env` Laravel (en plus des variables DB standard) :

```
# Email alertes biotoxines
MAIL_MAILER=smtp
MAIL_HOST=ssl0.ovh.net
MAIL_PORT=465
MAIL_ENCRYPTION=ssl
MAIL_USERNAME=alertes@medithau.fr
MAIL_PASSWORD=
MAIL_FROM_ADDRESS=alertes@medithau.fr
MAIL_FROM_NAME="Analyses Medithau"
ALERT_TO=si@medithau.com,qualite@medithau.com,achat@medithau.com,orders@medithau.com,prod.fronti@medithau.com

# Scripts Python (chemin absolu vers le venv)
PYTHON_BIN=/var/www/html/analyses/venv/bin/python3
ANALYSES_SCRIPTS_DIR=/var/www/html/analyses
ANALYSES_PDFS_DIR=/var/www/html/analyses/pdfs
```

### 3. Routes API

Toutes les actions API sont actuellement des POST sur `/` avec un champ `action`.
Les fichiers JS appellent ces endpoints — **ne pas changer les URLs JS**.

Créer dans `routes/web.php` :

```php
// API actions (appelées par le JS existant)
Route::post('/', [AnalysesController::class, 'handle'])->name('analyses.api');

// Téléchargement / visualisation PDF
Route::get('/', [AnalysesController::class, 'index'])->name('analyses.index');
Route::get('/pdf/view/{filename}',     [AnalysesController::class, 'pdfView']);
Route::get('/pdf/download/{filename}', [AnalysesController::class, 'pdfDownload']);
Route::get('/pdf/source',              [AnalysesController::class, 'pdfSource']);

// Admin SVG mapping
Route::post('/admin/svg-mapping', [MapSvgMappingController::class, 'update']);
```

### 4. Controllers à créer

#### `AnalysesController`

Méthode `handle(Request $request)` qui dispatch selon `$request->input('action')` :

| action | Paramètres POST | Ce que ça fait |
|--------|----------------|----------------|
| `get_all_results` | `date` (optionnel, YYYY-MM-DD) | JOIN analyses_sites + analyses_releves + analyses_sites_historique. Retourne état admin + résultat par site pour une date donnée (ou la plus récente). |
| `get_analyses` | `id_site`, `date` (optionnel) | Trouve la date effective (MAX date_analyse <= date demandée), retourne lignes de analyses_releves + meta admin depuis analyses_sites. |
| `get_site_history` | `id_site` | 500 dernières lignes de analyses_releves pour ce site, triées DESC. |
| `get_summary_lieu` | `id_lieu` | Retourne sites[], dates[], releves[], historiques[] pour construire la matrice de synthèse. |
| `get_pdf_history` | `date` (optionnel) | Liste les fichiers PDF dans ANALYSES_PDFS_DIR pour la date donnée (pattern yyyymmdd-*.pdf). |
| `download_pdf_now` | `date` | Exécute le script Python `download_zonas_pdf.py` et retourne le nom du fichier généré. |

Format de réponse JSON uniforme (conserve la structure attendue par le JS) :
```json
{ "success": true, "data": { ... } }
{ "success": false, "error": "message" }
```

Méthodes PDF :
- `pdfView($filename)` : `response()->file(...)` avec `Content-Disposition: inline`
- `pdfDownload($filename)` : `response()->download(...)`
- `pdfSource()` : proxy ou redirect vers `https://www.intecmar.gal/PDFs/Zonas_1401.pdf`

#### `MapSvgMappingController`

Une seule méthode `update(Request $request)` :
- `type` = 'zone' ou 'site', `db_id`, `svg_id`
- UPDATE `analyses_zones` ou `analyses_sites` SET svg_id = ?

### 5. Vue principale (Blade)

Convertir `public/index.php` en `resources/views/analyses/index.blade.php`.

Points d'attention :
- La page injecte `window.AnalysesData` (JSON zones/sites/lieux) dans une balise `<script>`
  au chargement — à recréer via `@json()` Blade
- Inclure les partials : `@include('analyses.carte')`, `@include('analyses.panel')`,
  `@include('analyses.synthese')`
- Les assets JS/CSS dans `assets/` → les copier dans `public/assets/` Laravel ou
  utiliser Vite avec `@vite`
- Le fichier SVG `carte_analyses_final.svg` → à placer dans `public/` et inclure via
  `file_get_contents(public_path('carte_analyses_final.svg'))`

Données injectées en JS par la vue (à reproduire côté Blade) :
```javascript
window.AnalysesData   = { zones, zonesById, sites, sitesById, sitesBySvgId, lieux, lieuxById }
window.ANALYSES_TODAY      = "YYYY-MM-DD"
window.ANALYSES_FIRST_DATE = "YYYY-MM-DD"   // date du premier relevé en base
window.ANALYSES_SLIDER_MAX = N              // nombre de jours depuis premier relevé
```

### 6. Alertes email (état des sites)

Remplacer la fonction Python `send_alert_email()` de `import_situations_api.py` par un
`Mailable` Laravel si les alertes doivent aussi être déclenchables depuis PHP.
Pour l'instant, les alertes sont envoyées uniquement par le script Python — laisser
tel quel.

État actuel du script Python :
- l'alerte email a déjà été modernisée côté Python avec une version HTML multipart
  (texte + HTML)
- l'email contient un bouton vers la plateforme Analyses
- chaque site en changement d'état est affiché dans un bloc dédié avec badge visuel
- le fond du bloc est coloré en pastel selon l'état final (`FERME` / `OUVERT`)
- le contenu affiché dans l'alerte est volontairement simple : `Site`, changement
  d'état, date du changement
- le nom du script n'est pas mentionné dans le corps du message

Si tu veux migrer les alertes côté Laravel :
- Créer `app/Mail/SiteStateChangedMail.php` (Mailable)
- Lire `ALERT_TO` depuis `.env` et exploser par virgule
- Déclencher depuis une commande Artisan (voir section crons)

### 7. Commandes Artisan pour les scripts Python

Note sur l'existant Python :
- `import_situations_api.py` n'est plus un gros script monolithique ; il sert désormais
  de point d'entrée léger
- la logique a été découpée dans le package Python `import_situations/`
- ce découpage n'a pas changé l'interface d'exécution : le cron et les commandes peuvent
  continuer à appeler `python3 import_situations_api.py`

Modules actuels du package `import_situations/` :
- `config.py` : chargement du `.env` et construction de la configuration
- `logging_utils.py` : configuration des logs fichier + console
- `db.py` : connexion MySQL
- `parsers.py` : conversion des dates et normalisation des champs INTECMAR
- `repository.py` : lecture du mapping des sites, mise à jour et historisation
- `email_alerts.py` : génération et envoi des alertes email
- `service.py` : orchestration complète de l'import et mode `--test-email`

Créer trois commandes Artisan qui wrappent les scripts Python existants :

```bash
php artisan make:command Analyses/RunScraper
php artisan make:command Analyses/ImportSituations
php artisan make:command Analyses/DownloadPdf
```

Chaque commande exécute le script Python via `Process` (Symfony) ou `exec()` :

```php
// Exemple pour ImportSituations
$python = config('analyses.python_bin');  // depuis .env PYTHON_BIN
$script = config('analyses.scripts_dir') . '/import_situations_api.py';
$process = new Process([$python, $script]);
$process->setTimeout(120);
$process->run();
```

Signatures :
```
analyses:run-scraper        # Lance analyse_scraper.py
analyses:import-situations  # Lance import_situations_api.py
analyses:download-pdf       # Lance download_zonas_pdf.py
```

### 8. Planification (crons)

Dans `app/Console/Kernel.php` (`schedule()`) :

```php
// Scraping des analyses biotoxines — tous les jours à 8h et 14h
$schedule->command('analyses:run-scraper')->twiceDaily(8, 14);

// Import des situations administratives — toutes les heures
$schedule->command('analyses:import-situations')->hourly();

// Téléchargement PDF INTECMAR — tous les jours à 9h
$schedule->command('analyses:download-pdf')->dailyAt('09:00');
```

Ajouter le cron Laravel sur le serveur (une seule ligne suffit) :
```
* * * * * cd /path/to/laravel && php artisan schedule:run >> /dev/null 2>&1
```

---

## Fichiers à conserver tels quels (ne pas réécrire)

| Fichier | Destination Laravel | Note |
|---------|---------------------|------|
| `assets/js/*.js` | `public/assets/js/` | Aucune modification — les URLs POST restent `/` |
| `assets/css/*.css` | `public/assets/css/` | Aucune modification |
| `carte_analyses_final.svg` | `public/` | Inclus dans la vue via file_get_contents |
| `analyse_scraper.py` | Même chemin | Appelé par commande Artisan |
| `import_situations_api.py` | Même chemin | Wrapper léger appelé par commande Artisan + cron |
| `import_situations/*.py` | Même chemin | Package interne de l'import des situations administratives |
| `download_zonas_pdf.py` | Même chemin | Appelé par commande Artisan |
| `venv/` | Même chemin | Environnement Python à ne pas déplacer |
| `pdfs/` | Même chemin ou `storage/app/pdfs/` | Stocker les PDFs générés |

---

## Ordre de migration recommandé

1. Importer `analyses_dump.sql` → vérifier que les 5 tables sont présentes
2. Créer les modèles Eloquent (sans migrations — la DB existe déjà)
3. Créer `AnalysesController` avec les 6 actions API → tester chaque action avec Postman/curl
4. Créer la vue Blade `index.blade.php` → vérifier que le JS charge et fonctionne
5. Créer les commandes Artisan → tester manuellement (`php artisan analyses:import-situations`)
6. Configurer le scheduler → vérifier les logs

---

## Notes importantes

- **`id_site` vs `id`** : dans `analyses_releves`, la jointure se fait sur `analyses_sites.id_site`
  (identifiant INTECMAR), pas sur `analyses_sites.id` (clé primaire Laravel). Bien configurer
  `$primaryKey` et `$foreignKey` dans les modèles.
- **Pas de `updated_at` automatique** sur toutes les tables — désactiver `$timestamps` sur les
  modèles qui ne l'ont pas (`analyses_zones`, `lieux`, `analyses_sites_historique`).
- **PDF sécurité** : valider `$filename` (whitelist pattern `\d{8}-\d{6}\.pdf`) avant de servir
  les fichiers pour éviter tout path traversal.
- **`download_pdf_now`** appelle un script Python via `exec()` — conserver ce mécanisme ou
  le remplacer par la commande Artisan `analyses:download-pdf`.
- **Mode test email** : le script Python expose déjà `python3 import_situations_api.py --test-email`
  pour valider la configuration SMTP et le rendu d'un email de démonstration.
