feat(persons): allow confidence editing from details

This commit is contained in:
grayTerminal-sh 2026-07-22 19:12:43 +02:00
parent f585041736
commit 18e59cc5e6
9 changed files with 225 additions and 0 deletions

View file

@ -32,5 +32,15 @@ gboolean person_entity_service_create(Database *database,
/** @brief Enregistre le rôle d'une personne existante. */ /** @brief Enregistre le rôle d'une personne existante. */
gboolean person_entity_service_update_role(Database *database, gboolean person_entity_service_update_role(Database *database,
const char *entity_identifier, PersonRole role, GError **error); const char *entity_identifier, PersonRole role, GError **error);
/**
* @brief Enregistre le niveau de confiance d'une personne existante.
* @param database Connexion ouverte empruntée.
* @param entity_identifier UUID de la personne.
* @param confidence Valeur comprise entre 0 et 100.
* @param error Destination facultative d'une erreur.
* @return TRUE lorsque la modification est validée.
*/
gboolean person_entity_service_update_confidence(Database *database,
const char *entity_identifier, gint confidence, GError **error);
G_END_DECLS G_END_DECLS
#endif #endif

View file

@ -152,6 +152,9 @@ typedef void (*MainWindowRelationSelectedCallback)(
/** @brief Callback appelé pour catégoriser une personne. */ /** @brief Callback appelé pour catégoriser une personne. */
typedef void (*MainWindowPersonRoleCallback)(const char *entity_identifier, typedef void (*MainWindowPersonRoleCallback)(const char *entity_identifier,
PersonRole role, gpointer user_data); PersonRole role, gpointer user_data);
/** @brief Callback appelé pour modifier la confiance d'une personne. */
typedef void (*MainWindowPersonConfidenceCallback)(
const char *entity_identifier, gint confidence, gpointer user_data);
/** /**
* @brief Callback appelé lors du déclenchement d'une action OSINT. * @brief Callback appelé lors du déclenchement d'une action OSINT.
@ -266,6 +269,9 @@ void main_window_set_relation_evidences(
/** @brief Définit le callback de catégorisation d'une personne. */ /** @brief Définit le callback de catégorisation d'une personne. */
void main_window_set_person_role_callback(MainWindow *main_window, void main_window_set_person_role_callback(MainWindow *main_window,
MainWindowPersonRoleCallback callback, gpointer user_data); MainWindowPersonRoleCallback callback, gpointer user_data);
/** @brief Définit le callback de confiance d'une personne. */
void main_window_set_person_confidence_callback(MainWindow *main_window,
MainWindowPersonConfidenceCallback callback, gpointer user_data);
/** /**
* @brief Définit le callback de déclenchement des actions OSINT. * @brief Définit le callback de déclenchement des actions OSINT.

View file

@ -53,6 +53,13 @@ typedef void (*EntityDetailsPanelPersonRoleCallback)(
gpointer user_data gpointer user_data
); );
/** @brief Callback appelé pour enregistrer la confiance d'une personne. */
typedef void (*EntityDetailsPanelPersonConfidenceCallback)(
const char *entity_identifier,
gint confidence,
gpointer user_data
);
/** /**
* @brief Crée un volet de détails fermé. * @brief Crée un volet de détails fermé.
* *
@ -135,6 +142,19 @@ void entity_details_panel_set_person_role_callback(
gpointer user_data gpointer user_data
); );
/**
* @brief Définit le callback de modification de la confiance d'une personne.
*
* @param details_panel Volet à configurer.
* @param callback Callback à appeler, ou NULL.
* @param user_data Données empruntées du callback.
*/
void entity_details_panel_set_person_confidence_callback(
EntityDetailsPanel *details_panel,
EntityDetailsPanelPersonConfidenceCallback callback,
gpointer user_data
);
/** /**
* @brief Indique si le volet est actuellement ouvert. * @brief Indique si le volet est actuellement ouvert.
* *

View file

@ -94,6 +94,9 @@ typedef void (*WorkspaceRelationSelectedCallback)(
/** @brief Callback appelé pour catégoriser une personne. */ /** @brief Callback appelé pour catégoriser une personne. */
typedef void (*WorkspacePersonRoleCallback)(const char *entity_identifier, typedef void (*WorkspacePersonRoleCallback)(const char *entity_identifier,
PersonRole role, gpointer user_data); PersonRole role, gpointer user_data);
/** @brief Callback appelé pour modifier la confiance d'une personne. */
typedef void (*WorkspacePersonConfidenceCallback)(
const char *entity_identifier, gint confidence, gpointer user_data);
/** /**
* @brief Callback appelé lors du déclenchement d'une action OSINT. * @brief Callback appelé lors du déclenchement d'une action OSINT.
@ -348,6 +351,9 @@ void workspace_set_relation_evidences(
/** @brief Définit le callback de catégorisation d'une personne. */ /** @brief Définit le callback de catégorisation d'une personne. */
void workspace_set_person_role_callback(Workspace *workspace, void workspace_set_person_role_callback(Workspace *workspace,
WorkspacePersonRoleCallback callback, gpointer user_data); WorkspacePersonRoleCallback callback, gpointer user_data);
/** @brief Définit le callback de confiance d'une personne. */
void workspace_set_person_confidence_callback(Workspace *workspace,
WorkspacePersonConfidenceCallback callback, gpointer user_data);
/** /**
* @brief Affiche l'état de chargement du graphe. * @brief Affiche l'état de chargement du graphe.

View file

@ -5004,6 +5004,35 @@ static void application_on_person_role_changed(const char *entity_identifier,
investigation_project_get_database_path(project)); investigation_project_get_database_path(project));
} }
/** @brief Enregistre la confiance choisie dans la fiche personne. */
static void application_on_person_confidence_changed(
const char *entity_identifier, gint confidence, gpointer user_data)
{
Application *application = user_data;
Database *database = NULL;
const InvestigationProject *project = NULL;
GError *error = NULL;
if (application == NULL || application->session == NULL) return;
database = investigation_session_get_database(application->session);
project = investigation_session_get_project(application->session);
if (!person_entity_service_update_confidence(database, entity_identifier,
confidence, &error))
{
application_present_error(application, "Confiance non enregistrée",
error != NULL ? error->message :
"Impossible de modifier la confiance de cette personne.");
g_clear_error(&error);
return;
}
g_free(application->pending_entity_selection_identifier);
application->pending_entity_selection_identifier =
g_strdup(entity_identifier);
main_window_set_status(application->main_window,
"Confiance enregistrée. Actualisation du graphe…");
application_start_graph_loading(application,
investigation_project_get_database_path(project));
}
/** @brief Libère le contexte d'édition d'une relation. */ /** @brief Libère le contexte d'édition d'une relation. */
static void application_edit_relation_context_free( static void application_edit_relation_context_free(
ApplicationEditRelationContext *context) ApplicationEditRelationContext *context)
@ -6674,6 +6703,8 @@ static void application_on_activate(
application_on_relation_selected, application); application_on_relation_selected, application);
main_window_set_person_role_callback(application->main_window, main_window_set_person_role_callback(application->main_window,
application_on_person_role_changed, application); application_on_person_role_changed, application);
main_window_set_person_confidence_callback(application->main_window,
application_on_person_confidence_changed, application);
main_window_set_osint_action_callback( main_window_set_osint_action_callback(
application->main_window, application->main_window,

View file

@ -17,6 +17,68 @@ static gboolean person_entity_service_status_valid(const char *status)
g_strcmp0(status, "confirmed") == 0; g_strcmp0(status, "confirmed") == 0;
} }
gboolean person_entity_service_update_confidence(Database *database,
const char *entity_identifier, gint confidence, GError **error)
{
static const char validate_sql[] =
"SELECT 1 FROM entites e JOIN types_entite t ON t.id=e.type_id "
"WHERE e.id=? AND t.code='person';";
static const char sql[] =
"UPDATE entites SET confiance=?, updated_at=? WHERE id=? AND "
"type_id=(SELECT id FROM types_entite WHERE code='person');";
DatabaseStatement *statement = NULL;
GDateTime *now = NULL;
char *timestamp = NULL;
gboolean active = FALSE, success = FALSE;
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
if (database == NULL || entity_identifier == NULL ||
!g_uuid_string_is_valid(entity_identifier) || confidence < 0 ||
confidence > 100)
{
g_set_error_literal(error, g_quark_from_static_string(
"person-entity-service-error"), 4,
"La personne ou le niveau de confiance est invalide.");
return FALSE;
}
statement = database_statement_prepare(database, validate_sql);
if (statement == NULL || !database_statement_bind_text(statement, 1,
entity_identifier) || database_statement_step(statement) !=
DATABASE_STATEMENT_STEP_ROW)
{
database_statement_finalize(statement);
g_set_error_literal(error, g_quark_from_static_string(
"person-entity-service-error"), 4,
"La personne sélectionnée n'existe pas.");
return FALSE;
}
database_statement_finalize(statement); statement = NULL;
now = g_date_time_new_now_utc();
timestamp = now != NULL ? g_date_time_format(now,
"%Y-%m-%dT%H:%M:%SZ") : NULL;
if (timestamp == NULL || !database_transaction_begin(database))
goto cleanup;
active = TRUE;
statement = database_statement_prepare(database, sql);
if (statement == NULL ||
!database_statement_bind_int64(statement, 1, confidence) ||
!database_statement_bind_text(statement, 2, timestamp) ||
!database_statement_bind_text(statement, 3, entity_identifier) ||
database_statement_step(statement) != DATABASE_STATEMENT_STEP_DONE)
goto cleanup;
database_statement_finalize(statement); statement = NULL;
if (!database_transaction_commit(database)) goto cleanup;
active = FALSE; success = TRUE;
cleanup:
database_statement_finalize(statement);
if (!success && active) database_transaction_rollback(database);
if (!success && error != NULL && *error == NULL)
g_set_error_literal(error, g_quark_from_static_string(
"person-entity-service-error"), 5,
"Le niveau de confiance n'a pas pu être enregistré.");
g_free(timestamp); g_clear_pointer(&now, g_date_time_unref);
return success;
}
gboolean person_entity_service_update_role(Database *database, gboolean person_entity_service_update_role(Database *database,
const char *entity_identifier, PersonRole role, GError **error) const char *entity_identifier, PersonRole role, GError **error)
{ {

View file

@ -133,6 +133,8 @@ struct MainWindow
gpointer edit_relation_user_data; gpointer edit_relation_user_data;
MainWindowPersonRoleCallback person_role_callback; MainWindowPersonRoleCallback person_role_callback;
gpointer person_role_user_data; gpointer person_role_user_data;
MainWindowPersonConfidenceCallback person_confidence_callback;
gpointer person_confidence_user_data;
MainWindowOsintActionCallback MainWindowOsintActionCallback
osint_action_callback; osint_action_callback;
@ -538,6 +540,17 @@ static void main_window_on_person_role_changed(const char *entity_identifier,
main_window->person_role_user_data); main_window->person_role_user_data);
} }
/** @brief Relaie la modification de confiance d'une personne. */
static void main_window_on_person_confidence_changed(
const char *entity_identifier, gint confidence, gpointer user_data)
{
MainWindow *main_window = user_data;
if (main_window != NULL &&
main_window->person_confidence_callback != NULL)
main_window->person_confidence_callback(entity_identifier, confidence,
main_window->person_confidence_user_data);
}
/** /**
* @brief Relaie la demande de vérification provenant du Workspace. * @brief Relaie la demande de vérification provenant du Workspace.
*/ */
@ -880,6 +893,8 @@ MainWindow *main_window_new(
main_window_on_edit_relation_requested, main_window); main_window_on_edit_relation_requested, main_window);
workspace_set_person_role_callback(main_window->workspace, workspace_set_person_role_callback(main_window->workspace,
main_window_on_person_role_changed, main_window); main_window_on_person_role_changed, main_window);
workspace_set_person_confidence_callback(main_window->workspace,
main_window_on_person_confidence_changed, main_window);
workspace_set_osint_action_callback( workspace_set_osint_action_callback(
main_window->workspace, main_window->workspace,
@ -1695,6 +1710,14 @@ void main_window_set_person_role_callback(MainWindow *main_window,
main_window->person_role_user_data = user_data; main_window->person_role_user_data = user_data;
} }
void main_window_set_person_confidence_callback(MainWindow *main_window,
MainWindowPersonConfidenceCallback callback, gpointer user_data)
{
if (main_window == NULL) return;
main_window->person_confidence_callback = callback;
main_window->person_confidence_user_data = user_data;
}
void main_window_set_quit_callback( void main_window_set_quit_callback(
MainWindow *main_window, MainWindow *main_window,
MainWindowQuitCallback callback, MainWindowQuitCallback callback,

View file

@ -31,6 +31,8 @@ struct EntityDetailsPanel
GtkWidget *entity_identifier_label; GtkWidget *entity_identifier_label;
GtkWidget *person_role_box; GtkWidget *person_role_box;
GtkDropDown *person_role_dropdown; GtkDropDown *person_role_dropdown;
GtkSpinButton *person_confidence_spin;
GtkWidget *person_confidence_button;
char *selected_entity_identifier; char *selected_entity_identifier;
@ -46,8 +48,26 @@ struct EntityDetailsPanel
EntityDetailsPanelPersonRoleCallback person_role_callback; EntityDetailsPanelPersonRoleCallback person_role_callback;
gpointer person_role_user_data; gpointer person_role_user_data;
gboolean updating_person_role; gboolean updating_person_role;
EntityDetailsPanelPersonConfidenceCallback person_confidence_callback;
gpointer person_confidence_user_data;
}; };
/** @brief Relaie la confiance explicitement enregistrée par l'utilisateur. */
static void entity_details_panel_on_person_confidence_clicked(
GtkButton *button, gpointer user_data)
{
EntityDetailsPanel *details_panel = user_data;
(void) button;
if (details_panel == NULL ||
details_panel->person_confidence_callback == NULL ||
details_panel->selected_entity_identifier == NULL) return;
details_panel->person_confidence_callback(
details_panel->selected_entity_identifier,
gtk_spin_button_get_value_as_int(
details_panel->person_confidence_spin),
details_panel->person_confidence_user_data);
}
/** @brief Relaie une catégorie choisie explicitement par l'utilisateur. */ /** @brief Relaie une catégorie choisie explicitement par l'utilisateur. */
static void entity_details_panel_on_person_role_changed(GObject *object, static void entity_details_panel_on_person_role_changed(GObject *object,
GParamSpec *parameter, gpointer user_data) GParamSpec *parameter, gpointer user_data)
@ -734,9 +754,22 @@ EntityDetailsPanel *entity_details_panel_new(void)
gtk_label_new("Catégorie dans lenquête")); gtk_label_new("Catégorie dans lenquête"));
gtk_box_append(GTK_BOX(details_panel->person_role_box), gtk_box_append(GTK_BOX(details_panel->person_role_box),
GTK_WIDGET(details_panel->person_role_dropdown)); GTK_WIDGET(details_panel->person_role_dropdown));
details_panel->person_confidence_spin = GTK_SPIN_BUTTON(
gtk_spin_button_new_with_range(0.0, 100.0, 1.0));
details_panel->person_confidence_button = gtk_button_new_with_label(
"Enregistrer la confiance");
gtk_box_append(GTK_BOX(details_panel->person_role_box),
gtk_label_new("Niveau de confiance (%)"));
gtk_box_append(GTK_BOX(details_panel->person_role_box),
GTK_WIDGET(details_panel->person_confidence_spin));
gtk_box_append(GTK_BOX(details_panel->person_role_box),
details_panel->person_confidence_button);
gtk_box_append(GTK_BOX(details_box), details_panel->person_role_box); gtk_box_append(GTK_BOX(details_box), details_panel->person_role_box);
g_signal_connect(details_panel->person_role_dropdown, "notify::selected", g_signal_connect(details_panel->person_role_dropdown, "notify::selected",
G_CALLBACK(entity_details_panel_on_person_role_changed), details_panel); G_CALLBACK(entity_details_panel_on_person_role_changed), details_panel);
g_signal_connect(details_panel->person_confidence_button, "clicked",
G_CALLBACK(entity_details_panel_on_person_confidence_clicked),
details_panel);
if (details_panel->entity_value_label == NULL || if (details_panel->entity_value_label == NULL ||
details_panel->entity_type_label == NULL || details_panel->entity_type_label == NULL ||
@ -861,6 +894,8 @@ void entity_details_panel_set_entity(
details_panel->updating_person_role = TRUE; details_panel->updating_person_role = TRUE;
gtk_drop_down_set_selected(details_panel->person_role_dropdown, gtk_drop_down_set_selected(details_panel->person_role_dropdown,
(guint) entity_record_get_person_role(entity_record)); (guint) entity_record_get_person_role(entity_record));
gtk_spin_button_set_value(details_panel->person_confidence_spin,
entity_record_get_confidence(entity_record));
details_panel->updating_person_role = FALSE; details_panel->updating_person_role = FALSE;
entity_details_panel_update_add_relation_button( entity_details_panel_update_add_relation_button(
@ -1117,6 +1152,15 @@ void entity_details_panel_set_person_role_callback(
details_panel->person_role_user_data = user_data; details_panel->person_role_user_data = user_data;
} }
void entity_details_panel_set_person_confidence_callback(
EntityDetailsPanel *details_panel,
EntityDetailsPanelPersonConfidenceCallback callback, gpointer user_data)
{
if (details_panel == NULL) return;
details_panel->person_confidence_callback = callback;
details_panel->person_confidence_user_data = user_data;
}
gboolean entity_details_panel_is_open( gboolean entity_details_panel_is_open(
const EntityDetailsPanel *details_panel const EntityDetailsPanel *details_panel
) )

View file

@ -136,6 +136,8 @@ struct Workspace
gpointer edit_relation_user_data; gpointer edit_relation_user_data;
WorkspacePersonRoleCallback person_role_callback; WorkspacePersonRoleCallback person_role_callback;
gpointer person_role_user_data; gpointer person_role_user_data;
WorkspacePersonConfidenceCallback person_confidence_callback;
gpointer person_confidence_user_data;
WorkspaceOsintActionCallback WorkspaceOsintActionCallback
osint_action_callback; osint_action_callback;
@ -882,6 +884,16 @@ static void workspace_on_person_role_changed(const char *entity_identifier,
workspace->person_role_user_data); workspace->person_role_user_data);
} }
/** @brief Relaie la modification de confiance d'une personne. */
static void workspace_on_person_confidence_changed(
const char *entity_identifier, gint confidence, gpointer user_data)
{
Workspace *workspace = user_data;
if (workspace != NULL && workspace->person_confidence_callback != NULL)
workspace->person_confidence_callback(entity_identifier, confidence,
workspace->person_confidence_user_data);
}
/** /**
* @brief Désélectionne le nœud lorsque le volet est fermé manuellement. * @brief Désélectionne le nœud lorsque le volet est fermé manuellement.
*/ */
@ -1801,6 +1813,9 @@ Workspace *workspace_new(void)
entity_details_panel_set_person_role_callback( entity_details_panel_set_person_role_callback(
workspace->entity_details_panel, workspace_on_person_role_changed, workspace->entity_details_panel, workspace_on_person_role_changed,
workspace); workspace);
entity_details_panel_set_person_confidence_callback(
workspace->entity_details_panel,
workspace_on_person_confidence_changed, workspace);
gtk_overlay_set_child( gtk_overlay_set_child(
GTK_OVERLAY( GTK_OVERLAY(
@ -3258,6 +3273,14 @@ void workspace_set_person_role_callback(Workspace *workspace,
workspace->person_role_user_data = user_data; workspace->person_role_user_data = user_data;
} }
void workspace_set_person_confidence_callback(Workspace *workspace,
WorkspacePersonConfidenceCallback callback, gpointer user_data)
{
if (workspace == NULL) return;
workspace->person_confidence_callback = callback;
workspace->person_confidence_user_data = user_data;
}
void workspace_reset_graph_layout( void workspace_reset_graph_layout(
Workspace *workspace Workspace *workspace
) )