labfy-investigation/docs/tickets/closed/TICKET-029.md

18 KiB
Raw Blame History

Ticket #029 — Intégrer InvestigationSession au cycle de vie GTK

Contexte

Le ticket #028 a ajouté InvestigationSession, qui permet douvrir une enquête existante de manière contrôlée.

Une session valide possède désormais :

  • un InvestigationProject ;
  • une connexion Database ouverte ;
  • un InvestigationRecord chargé depuis SQLite.

Lapplication GTK utilise encore lancien objet Investigation pour représenter le dossier sélectionné.

Son fonctionnement actuel est approximativement le suivant :

FolderDialog
    ↓
Investigation
    ↓
InvestigationTreeBuilder
    ↓
InvestigationTreeModel
    ↓
MainWindow

Cette organisation ne conserve pas la connexion SQLite et nutilise pas les informations persistées dans la table investigation.

Il faut désormais remplacer lancien objet détenu par Application par une véritable InvestigationSession.

Objectif

Faire de InvestigationSession le contexte actif de lapplication.

Après la sélection dun dossier :

  1. ouvrir une nouvelle InvestigationSession ;
  2. récupérer le chemin racine depuis son InvestigationProject ;
  3. construire le nouvel InvestigationTreeModel ;
  4. ne remplacer lancienne enquête quaprès validation complète ;
  5. mettre à jour la fenêtre principale ;
  6. conserver la session jusquà la fermeture de lapplication.

Lapplication ne doit posséder quune seule session active à la fois.

Architecture attendue

FolderDialog
    │
    ▼
Application
    │
    ├── InvestigationSession
    │       ├── InvestigationProject
    │       ├── Database
    │       └── InvestigationRecord
    │
    ├── InvestigationTreeModel
    │
    └── MainWindow
            ├── Sidebar
            ├── Workspace
            └── Barre détat

Principe de remplacement transactionnel

Louverture dune nouvelle enquête doit suivre cet ordre :

ancienne session toujours active
        ↓
ouvrir la nouvelle session
        ↓
construire le nouvel arbre
        ↓
vérifier que les deux objets sont valides
        ↓
remplacer lancien arbre
        ↓
fermer lancienne session
        ↓
installer la nouvelle session
        ↓
mettre à jour MainWindow

En cas déchec avant le remplacement :

ancienne session conservée
ancienne arborescence conservée
nouvelle ressource libérée
message derreur produit

Lapplication ne doit jamais perdre une enquête déjà ouverte simplement parce quune nouvelle sélection est invalide.


Travail à réaliser

1. Remplacer lancien objet dans Application

Modifier :

src/core/application.c

La structure privée actuelle contient notamment :

Investigation *investigation;
InvestigationTreeModel *tree_model;

Remplacer le premier champ par :

InvestigationSession *session;

La structure doit devenir au minimum :

struct Application
{
    GtkApplication *gtk_application;
    MainWindow *main_window;
    InvestigationSession *session;
    InvestigationTreeModel *tree_model;
};

Ne pas exposer cette structure dans le header public.

2. Modifier les dépendances de application.c

Supprimer :

#include "core/investigation.h"

Ajouter :

#include "core/investigation_session.h"
#include "core/investigation_project.h"
#include "models/investigation_record.h"

Conserver les dépendances nécessaires à :

FolderDialog
MainWindow
InvestigationTreeBuilder
InvestigationTreeModel
GTK

application.c ne doit pas inclure directement :

#include <sqlite3.h>

et ne doit appeler aucune fonction sqlite3_*.

3. Ouvrir la session après la sélection du dossier

Dans :

application_on_folder_selected()

remplacer la création de lancien objet :

investigation_new(folder_path)

par :

investigation_session_open(
    folder_path,
    &error
);

Utiliser des variables temporaires :

InvestigationSession *new_session = NULL;
InvestigationTreeModel *new_tree_model = NULL;
GError *error = NULL;

Ne jamais écrire directement dans :

application->session
application->tree_model

avant que toute louverture soit validée.

4. Gérer une sélection annulée

Lorsque :

folder_path == NULL

la fonction doit simplement retourner.

Lenquête déjà ouverte doit rester active.

Aucune session ne doit être fermée.

Aucune erreur ne doit être affichée.

Un message de diagnostic avec g_print() reste acceptable :

Sélection annulée.

5. Gérer léchec douverture de session

Si :

new_session == NULL

la fonction doit :

  • afficher un avertissement contenant le message du GError ;
  • libérer le GError ;
  • conserver lancienne session ;
  • conserver lancien arbre ;
  • ne pas modifier la fenêtre principale.

Exemple de diagnostic acceptable :

g_warning(
    "Impossible d'ouvrir l'enquête : %s",
    error != NULL ? error->message : "erreur inconnue"
);

Le dialogue graphique derreur est hors périmètre de ce ticket.

6. Construire larborescence depuis la session

Récupérer le projet de la nouvelle session :

const InvestigationProject *project = NULL;

avec :

project = investigation_session_get_project(
    new_session
);

Récupérer ensuite son chemin racine :

const char *root_path = NULL;

avec :

root_path = investigation_project_get_root_path(
    project
);

Construire le nouvel arbre avec :

new_tree_model = investigation_tree_builder_build(
    root_path
);

La logique de construction du chemin ne doit pas être dupliquée dans application.c.

7. Gérer léchec de construction de larbre

Si InvestigationTreeBuilder échoue après louverture de la session :

new_session valide
new_tree_model == NULL

la fonction doit :

investigation_session_close(new_session);

puis retourner.

Lancienne session et lancien arbre doivent rester actifs.

Le message suivant est acceptable :

Impossible de construire l'arborescence de l'enquête.

8. Remplacer les anciens objets uniquement après validation

Lorsque new_session et new_tree_model sont tous deux valides :

investigation_tree_model_free(
    application->tree_model
);

investigation_session_close(
    application->session
);

application->tree_model = new_tree_model;
application->session = new_session;

Après le transfert :

new_tree_model = NULL;
new_session = NULL;

Cette remise à NULL nest pas obligatoire si la fonction retourne immédiatement, mais elle est recommandée pour rendre la propriété explicite.

9. Conserver la connexion SQLite ouverte

Après une ouverture réussie, la session doit rester stockée dans :

application->session

Il est interdit de fermer la session à la fin de :

application_on_folder_selected()

La connexion Database doit rester utilisable pendant toute la durée douverture de lenquête.

10. Ajouter un accesseur interne ou public

Ajouter dans :

include/core/application.h

la déclaration suivante :

const InvestigationSession *application_get_session(
    const Application *application
);

Implémenter dans :

src/core/application.c
const InvestigationSession *application_get_session(
    const Application *application
)
{
    if (application == NULL)
    {
        return NULL;
    }

    return application->session;
}

Le pointeur retourné appartient à Application.

Le code appelant ne doit pas fermer cette session.

Cet accesseur servira aux futurs contrôleurs et DAO.

11. Mettre à jour la fenêtre principale

Ajouter dans :

include/views/main_window.h

la fonction :

void main_window_set_investigation(
    MainWindow *main_window,
    const char *investigation_name,
    const char *investigation_root_path
);

Implémenter dans :

src/views/main_window.c

Cette fonction doit :

  • accepter main_window == NULL ;
  • accepter des chaînes NULL sans planter ;
  • mettre à jour le titre de la fenêtre ;
  • mettre à jour la barre détat.

Titre attendu

Lorsque le nom est valide :

Labfy Investigation — <nom>

Exemple :

Labfy Investigation — Enquete_Session

Lorsque le nom est absent :

Labfy Investigation

Construire le titre avec GLib :

g_strdup_printf()

puis le libérer avec :

g_free()

Barre détat attendue

Lorsque lenquête est ouverte :

Enquête ouverte : <nom> — <chemin racine>

Lorsque certaines valeurs sont absentes, utiliser une formulation sûre sans déréférencer NULL.

La barre détat ne doit pas conserver un pointeur vers une chaîne temporaire.

gtk_label_set_text() copie le texte fourni.

12. Récupérer le nom persistant

Dans application_on_folder_selected(), récupérer :

const InvestigationRecord *record = NULL;
const char *investigation_name = NULL;

avec :

record = investigation_session_get_record(
    application->session
);

investigation_name = investigation_record_get_name(
    record
);

Utiliser les données du InvestigationRecord.

Ne pas reconstruire le nom à partir du dernier composant du chemin.

13. Mettre à jour la fenêtre après le modèle

Après linstallation de la nouvelle session et du nouvel arbre :

main_window_set_tree_model(
    application->main_window,
    application->tree_model
);

puis :

main_window_set_investigation(
    application->main_window,
    investigation_name,
    root_path
);

Lordre attendu est :

nouvelle session installée
nouvel arbre installé
sidebar mise à jour
titre et statut mis à jour

14. Fermer la session dans application_free()

Remplacer :

investigation_free(application->investigation);

par :

investigation_session_close(
    application->session
);

Lordre de nettoyage conseillé est :

investigation_tree_model_free(
    application->tree_model
);

investigation_session_close(
    application->session
);

main_window_free(
    application->main_window
);

Puis libérer GtkApplication et Application comme actuellement.

application_free(NULL) doit rester valide.

15. Ne plus utiliser lancien objet dans lapplication

À la fin du ticket, les symboles suivants ne doivent plus apparaître dans application.c :

Investigation *
investigation_new
investigation_free
investigation_get_root_path
investigation_get_database_path

Le module historique investigation.c nest pas supprimé dans ce ticket.

Sa suppression éventuelle sera effectuée séparément après vérification quaucun autre composant ne lutilise.


Tests et validations

16. Tests automatisés existants

Aucun nouveau test GTK automatisé nest obligatoire dans ce ticket.

Tous les tests existants doivent rester valides :

make test

En particulier :

InvestigationProject
InvestigationSession
InvestigationTreeBuilder
InvestigationTreeModel
InvestigationDao

17. Test manuel douverture valide

Créer ou utiliser une enquête valide.

Lancer :

make
make run

Sélectionner le dossier racine de lenquête.

Vérifier :

  • la fenêtre reste ouverte ;
  • larborescence apparaît dans la sidebar ;
  • le titre contient le nom persistant de lenquête ;
  • la barre détat contient le nom et le chemin racine ;
  • aucune erreur SQLite napparaît ;
  • la session reste active après la fin du callback.

18. Test manuel dannulation

Relancer lapplication et annuler la sélection.

Vérifier :

  • aucun crash ;
  • aucun warning critique ;
  • la fenêtre reste ouverte ;
  • la barre détat reste sur :
Aucune enquête ouverte

19. Test manuel dun dossier invalide

Sélectionner un dossier qui ne contient pas :

00_BaseDeDonnees/Enquete.sqlite

Vérifier :

  • aucun crash ;
  • un warning explicite est affiché ;
  • aucun fichier SQLite nest créé ;
  • la fenêtre reste utilisable ;
  • aucune fausse enquête napparaît dans la sidebar.

20. Test manuel de remplacement

Si linterface permet une seconde sélection pendant la même exécution :

  1. ouvrir une enquête valide A ;
  2. tenter douvrir un dossier invalide ;
  3. vérifier que A reste affichée ;
  4. ouvrir une enquête valide B ;
  5. vérifier que B remplace A.

Si une seconde sélection nest pas encore accessible dans linterface, cette validation sera complétée lors de lajout de laction « Ouvrir ».

La logique du callback doit néanmoins déjà préserver lancienne session.


Gestion de la mémoire

Les propriétaires doivent être clairement définis.

Application possède

GtkApplication
MainWindow
InvestigationSession
InvestigationTreeModel

InvestigationSession possède

InvestigationProject
Database
InvestigationRecord

Variables temporaires du callback

new_session
new_tree_model
GError

En cas déchec :

new_session fermée si elle existe
new_tree_model libéré si nécessaire
GError libéré
ancienne session conservée
ancien tree model conservé

En cas de succès :

anciens objets libérés
nouveaux objets transférés dans Application
aucun double free

Critères dacceptation

  • Application possède un InvestigationSession *.
  • Application ne possède plus dInvestigation *.
  • Le callback ouvre une session avec investigation_session_open().
  • Le chemin racine provient de InvestigationProject.
  • Le nom provient dInvestigationRecord.
  • Larbre est construit depuis le chemin de la session.
  • Lancienne session reste active si louverture échoue.
  • Lancien arbre reste actif si louverture échoue.
  • La nouvelle session est fermée si la construction de larbre échoue.
  • Les anciens objets ne sont remplacés quaprès validation complète.
  • La session reste ouverte après le callback.
  • application_free() ferme la session.
  • application_get_session(NULL) retourne NULL.
  • main_window_set_investigation() accepte NULL.
  • Le titre affiche le nom de lenquête.
  • La barre détat affiche le nom et le chemin racine.
  • application.c nappelle aucune fonction SQLite.
  • Aucun chemin SQLite nest reconstruit dans application.c.
  • Les anciens tests restent valides.
  • make réussit sans warning.
  • make test réussit.
  • Le test manuel douverture valide réussit.
  • Le test manuel dannulation réussit.
  • Le test manuel de dossier invalide réussit.
  • git diff --check ne retourne aucune erreur.

Audit attendu

La commande suivante ne doit rien afficher :

rg -n \
    'investigation_new|investigation_free|investigation_get_root_path|investigation_get_database_path|Investigation \*' \
    src/core/application.c

La commande suivante ne doit rien afficher :

rg -n \
    'sqlite3_|#include <sqlite3.h>|00_BaseDeDonnees|Enquete.sqlite' \
    src/core/application.c

Vérifier la présence de la nouvelle session :

rg -n \
    'InvestigationSession|investigation_session_' \
    src/core/application.c \
    include/core/application.h

Vérifier la mise à jour de la fenêtre :

rg -n \
    'main_window_set_investigation' \
    include/views/main_window.h \
    src/views/main_window.c \
    src/core/application.c

Hors périmètre

Ce ticket ne doit pas ajouter :

  • une action de menu « Ouvrir » ;
  • un raccourci clavier ;
  • un dialogue graphique détaillé pour les erreurs ;
  • la création dune enquête ;
  • la fermeture manuelle dune enquête ;
  • la réparation dun chemin racine incohérent ;
  • la modification de la base SQLite ;
  • les DAO des preuves ou des entités ;
  • la restauration automatique de la dernière enquête ;
  • la persistance des préférences utilisateur ;
  • le verrouillage multi-instance ;
  • la suppression de lancien module Investigation.

Fichiers principalement concernés

include/core/application.h
src/core/application.c

include/views/main_window.h
src/views/main_window.c

Fichiers utilisés sans modification attendue :

include/core/investigation_session.h
src/core/investigation_session.c

include/core/investigation_project.h
src/core/investigation_project.c

include/models/investigation_record.h
src/models/investigation_record.c

include/core/investigation_tree_builder.h
src/core/investigation_tree_builder.c

Le Makefile ne devrait pas nécessiter de nouvelle cible, car les sources de production sont détectées automatiquement avec :

SRC := $(shell find src -name "*.c")

Résultat attendu

À la fin du ticket, la sélection dun dossier doit ouvrir une véritable session denquête.

Lapplication doit conserver ensemble :

la connexion SQLite
les métadonnées persistées
le contexte de fichiers
larborescence
létat visuel de la fenêtre

Le flux final doit être :

sélection du dossier
        ↓
InvestigationSession ouverte
        ↓
InvestigationTreeModel construit
        ↓
session installée dans Application
        ↓
arbre affiché dans MainWindow
        ↓
nom et chemin affichés

Commit attendu

Avant le commit :

make clean
make
make test
git diff --check
git status --short

Préparer les fichiers :

git add \
    include/core/application.h \
    src/core/application.c \
    include/views/main_window.h \
    src/views/main_window.c

Contrôler :

git diff --cached --stat
git diff --cached

Créer le commit :

git commit -m "feat(app): integrate investigation session"

Puis pousser après validation complète :

git push