From 82cb4cff3e24d64c26c0bbadbaf545771c3d59f8 Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Thu, 13 Aug 2026 14:32:55 +0200 Subject: [PATCH] =?UTF-8?q?Stats=20:=20lire=20voiceSessions,=20et=20l'aper?= =?UTF-8?q?=C3=A7u=20cesse=20de=20polluer=20le=20rapport=20du=20client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _voiceSessions lisait appTypeDistribution['Voice'], qui ne se serait jamais rempli : le vocal est un mode d'interaction, pas une plateforme. Le serveur expose désormais « sessions ayant utilisé le vocal », qui est vrai. Et l'aperçu de conversation du Guide IA journalisait chaque essai comme une question de visiteur : le gestionnaire qui testait sa personnalité polluait son propre onglet « Ce que demandent vos visiteurs » et gonflait le bloc des questions sans réponse. isVisitorQuestion: false. manager_api_new étendu à la main (voiceSessions, renommage isVisitorQuestion). flutter analyze lib sans erreur, flutter build web vert. Co-Authored-By: Claude Opus 5 --- lib/Screens/GuideIa/guide_ia_screen.dart | 4 ++++ lib/Screens/Statistics/statistics_screen.dart | 10 ++++++++-- manager_api_new/lib/model/ai_chat_request.dart | 12 +++++++----- manager_api_new/lib/model/stats_summary_dto.dart | 8 ++++++++ 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/Screens/GuideIa/guide_ia_screen.dart b/lib/Screens/GuideIa/guide_ia_screen.dart index 172b539..dc8a082 100644 --- a/lib/Screens/GuideIa/guide_ia_screen.dart +++ b/lib/Screens/GuideIa/guide_ia_screen.dart @@ -630,6 +630,10 @@ class _GuideIaScreenState extends State { instanceId: ctx.instanceId, appType: AppType.Web, language: _editingLanguage, + // ⚠️ C'est le gestionnaire qui teste sa personnalité, pas un visiteur qui pose une + // question : sans ce drapeau, chaque essai atterrissait dans son propre onglet + // « Ce que demandent vos visiteurs » et gonflait le bloc des questions sans réponse. + isVisitorQuestion: false, )); if (!mounted) return; setState(() { diff --git a/lib/Screens/Statistics/statistics_screen.dart b/lib/Screens/Statistics/statistics_screen.dart index 533619e..32d570d 100644 --- a/lib/Screens/Statistics/statistics_screen.dart +++ b/lib/Screens/Statistics/statistics_screen.dart @@ -202,8 +202,14 @@ class _StatisticsScreenState extends State { double _contentsPerVisit(StatsSummaryDTO stats) => stats.totalSessions == 0 ? 0 : _totalViews(stats) / stats.totalSessions; - int _voiceSessions(StatsSummaryDTO stats) => - stats.appTypeDistribution[AppType.Voice.name] ?? 0; + /// ⚠️ Lisait `appTypeDistribution['Voice']`, qui ne se remplira jamais : **le vocal + /// n'est pas une plateforme mais un mode d'interaction**. Un visiteur passe des + /// lunettes à l'écran dans une même session, or la distribution ne compte qu'une + /// entrée par session, tranchée sur l'événement le plus ancien — le chiffre aurait + /// dépendu de l'ordre dans lequel le visiteur a touché ses appareils. + /// + /// Le serveur expose désormais « sessions ayant utilisé le vocal », qui est vrai. + int _voiceSessions(StatsSummaryDTO stats) => stats.voiceSessions ?? 0; double _voiceShare(StatsSummaryDTO stats) => stats.totalSessions == 0 ? 0 diff --git a/manager_api_new/lib/model/ai_chat_request.dart b/manager_api_new/lib/model/ai_chat_request.dart index d1b8a16..337b2e2 100644 --- a/manager_api_new/lib/model/ai_chat_request.dart +++ b/manager_api_new/lib/model/ai_chat_request.dart @@ -21,7 +21,7 @@ class AiChatRequest { this.conversationId, this.history = const [], this.isVoice = false, - this.isAutoTriggered = false, + this.isVisitorQuestion = true, }); String? message; @@ -50,9 +50,11 @@ class AiChatRequest { /// true = interaction vocale (lunettes) — prompt audio-friendly, pas de navigation bool? isVoice; - /// true = tour déclenché par le système (mode proactif), pas demandé par le visiteur. - /// Le backend compte les jetons mais ne journalise pas la question : elle n'en est pas une. - bool? isAutoTriggered; + /// false = ce tour n'est pas une question de visiteur (prompt du mode proactif, ou + /// gestionnaire testant sa personnalité dans l'aperçu du Guide IA). Le backend compte + /// les jetons mais ne journalise rien. Défaut à true : ne pas envoyer le champ revient + /// à journaliser, ce qui est le bon comportement pour une app visiteur. + bool? isVisitorQuestion; @override bool operator ==(Object other) => @@ -117,7 +119,7 @@ class AiChatRequest { json[r'conversationId'] = null; } json[r'isVoice'] = this.isVoice ?? false; - json[r'isAutoTriggered'] = this.isAutoTriggered ?? false; + json[r'isVisitorQuestion'] = this.isVisitorQuestion ?? true; return json; } diff --git a/manager_api_new/lib/model/stats_summary_dto.dart b/manager_api_new/lib/model/stats_summary_dto.dart index bf5b073..aae3854 100644 --- a/manager_api_new/lib/model/stats_summary_dto.dart +++ b/manager_api_new/lib/model/stats_summary_dto.dart @@ -15,6 +15,7 @@ class StatsSummaryDTO { this.visitsByDay = const [], this.languageDistribution = const {}, this.appTypeDistribution = const {}, + this.voiceSessions, this.topPois = const [], this.topAgendaEvents = const [], this.quizStats = const [], @@ -30,6 +31,12 @@ class StatsSummaryDTO { List visitsByDay; Map languageDistribution; Map appTypeDistribution; + + /// Sessions ayant utilisé le vocal au moins une fois. Volontairement **hors** de + /// `appTypeDistribution` : le vocal est un mode d'interaction, pas une plateforme — + /// une même session mêle la voix et le tactile. + int? voiceSessions; + List topPois; List topAgendaEvents; List quizStats; @@ -52,6 +59,7 @@ class StatsSummaryDTO { appTypeDistribution: (json[r'appTypeDistribution'] is Map) ? (json[r'appTypeDistribution'] as Map).cast() : {}, + voiceSessions: (json[r'voiceSessions'] as num?)?.toInt(), topPois: PoiStatDTO.listFromJson(json[r'topPois']), topAgendaEvents: AgendaEventStatDTO.listFromJson(json[r'topAgendaEvents']), quizStats: QuizStatDTO.listFromJson(json[r'quizStats']),