# Design — Onglet Utilisateurs Amadea **Date :** 2026-04-02 **Statut :** Approuvé --- ## Contexte Ajouter un onglet "Utilisateurs" à l'application de supervision, affichant en temps réel les utilisateurs connectés à Amadea Web 8 x64, leur statut d'activité, et un graphique d'utilisation horaire. Le chemin des logs Amadea et les seuils de statut sont rendus configurables dans l'onglet Configuration existant. --- ## Architecture générale ### Nouveaux fichiers - **`user_monitor.py`** — classe `UserMonitor` : thread de fond, parsing des logs, cache thread-safe. Miroir exact de `SystemMonitor`. ### Fichiers modifiés | Fichier | Modification | |---|---| | `config_manager.py` | Ajout des clés `amadea_log_path` et `user_status_thresholds` dans la config par défaut | | `app.py` | Instanciation de `UserMonitor`, routes `/users` et `/api/users` | | `templates/base.html` | Lien "Utilisateurs" dans la navbar | | `templates/settings.html` | 2 nouveaux blocs de configuration | | `templates/users.html` | Nouvelle page (tableau + graphique CSS) | ### Flux de données ``` UserMonitor (background thread) ├─ parse awevents_YY-MM-DD_*.log → activité utilisateur + déconnexions explicites └─ parse isoft_YY-MM-DD_*.log → événements de session (login/timeout) ↓ cache thread-safe (dict par login) /api/users → JSON /users → rendu Jinja2 initial + auto-refresh JS (30s, même pattern que dashboard) ``` --- ## Parsing et modèle de données ### Sélection des fichiers du jour Les fichiers de logs suivent le pattern `PREFIX_YY-MM-DD_N.ext` (ex: `awevents_26-04-02_1.log`). - Date du jour formatée en `%y-%m-%d` (ex: `26-04-02` pour 2026-04-02) - Tous les fichiers du jour sont lus, triés par index `N` croissant - Si plusieurs fichiers du même jour existent (index incrémental), ils sont tous parsés dans l'ordre ### Format `awevents_YY-MM-DD_N.log` (source principale) ``` 2026-03-30 10:34:24.034;INFO ;;;;"login=JENKINS,action=SelectionChange,Label=BAO_Main/..." ``` Regex d'extraction : ```python r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*login=([^,]+),action=([^,]+),Label=(.+)"?$' ``` - `timestamp` → groupe 1 - `login` → groupe 2 (identifiant utilisateur) - `action` → groupe 3 (SelectionChange, Action, Click, ValueChange…) - `label` → groupe 4 (contexte UI) **Déconnexion explicite :** ligne dont le label contient `se deconnecter` ### Format `isoft_YY-MM-DD_N.log` (complément sessions) ``` 2026-03-30 10:33:05.830;INFO ;"ISExecutingThread...";...;"method=OpenUserSession,...,login=JENKINS" ``` Regex pour login réussi : ```python r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*method=OpenUserSession.*login=([A-Za-z0-9_]+)' ``` Regex pour timeout de session : ```python r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*method=closeSession' ``` ### Modèle par utilisateur (cache) ```python { "login": str, # identifiant (ex: "JENKINS") "last_action_time": datetime, # horodatage de la dernière action "last_action_label": str, # label de la dernière action (tronqué à 60 chars) "action_count_24h": int, # nombre d'actions dans les dernières 24h "status": str, # "actif" | "inactif" | "deconnecte" "explicit_logout": bool, # True si déconnexion explicite détectée "connected_since": datetime | None, # heure de la première action du jour (premier awevents du jour pour cet utilisateur) } ``` ### Règles de statut Seuils configurables (clé `user_status_thresholds`, défauts : `active_minutes=5`, `inactive_minutes=30`) : | Condition | Statut | |---|---| | Déconnexion explicite détectée | DÉCONNECTÉ | | Dernière action > `inactive_minutes` | DÉCONNECTÉ | | Dernière action entre `active_minutes` et `inactive_minutes` | INACTIF | | Dernière action < `active_minutes` | ACTIF | ### Graphique d'activité horaire Comptage du nombre d'utilisateurs distincts ayant au moins une action par tranche horaire (H:00 → H:59). Données issues uniquement des fichiers du jour (`awevents_*.log`). Pour le sélecteur "7 derniers jours" : si les fichiers zippés ne sont pas disponibles (cas nominal en prod), afficher un `alert-info` : *"Les données historiques des jours précédents ne sont pas disponibles (fichiers archivés)."* --- ## Configuration ### Nouvelles clés dans `config.json` ```json { "amadea_log_path": "C:\\ProgramData\\ISoft\\Amadea Web 8 x64\\data\\logs", "user_status_thresholds": { "active_minutes": 5, "inactive_minutes": 30 } } ``` ### Nouveaux blocs dans `settings.html` **Bloc 1 — Chemin des logs Amadea** (même style `card` + `form-control` + `btn btn-primary`) : - Champ texte pré-rempli avec la valeur actuelle - Bouton "Enregistrer" - Route POST : `/settings/amadea-log-path` **Bloc 2 — Seuils statut utilisateurs** (même style que le bloc "Seuils d'alerte") : - Champ numérique "Actif si dernière action < N min" (défaut: 5) - Champ numérique "Inactif si dernière action < N min" (défaut: 30) - Bouton "Enregistrer" - Route POST : `/settings/user-thresholds` --- ## Interface utilisateurs (`users.html`) ### Tableau Colonnes : **Utilisateur | Statut | Dernière action | Actions (24h) | Depuis** Tri par défaut : Actifs → Inactifs → Déconnectés (ordre de priorité statut). Badges Bootstrap cohérents avec l'existant : - ACTIF → `ACTIF` - INACTIF → `INACTIF` - DÉCONNECTÉ → `DÉCONNECTÉ` ### Graphique d'activité Barres CSS Bootstrap (divs avec hauteur proportionnelle), aucune librairie externe. Une barre par heure de la journée (00h–23h), largeur fixe, hauteur = `(valeur / max) * 100%`. Couleur : `bg-primary`. Tooltip au survol (attribut `title`). ### Auto-refresh `setInterval` toutes les 30 secondes, appel `fetch('/api/users')`, même pattern que `refreshMetrics()` dans `dashboard.html`. ### Gestion d'erreurs | Cas | Affichage | |---|---| | Dossier de logs introuvable | `alert alert-warning` avec le chemin configuré | | Aucun fichier du jour trouvé | `alert alert-info "Aucun log disponible pour aujourd'hui"` | | Fichier verrouillé/illisible | Ignoré silencieusement, parsing continue | | Aucun utilisateur détecté | Message `text-muted` dans le tableau | --- ## Choix techniques - **Librairie graphique** : barres CSS Bootstrap pures (aucune dépendance externe) - **Thread** : daemon thread (comme `SystemMonitor`), s'arrête avec l'application - **Encodage fichiers** : `utf-8` avec `errors='ignore'` pour tolérer les caractères invalides - **Performance** : les fichiers `isoft_*.log` peuvent être très volumineux (index > 80). Le parsing lit ligne par ligne sans charger le fichier en mémoire (`for line in f`)