mymuseum-visitapp/lib/Services/statisticsService.dart
Thomas Fransolet e967678dd2 Émission des événements vocaux — le vocal est un attribut, pas un canal
StatisticsService.track porte isVoice, qui pose "voice": true dans
VisitEvent.Metadata. Le flux lunettes n'émettait aucun VisitEvent : une visite
vocale ne produisait ni session, ni section vue, ni durée.

Le sectionView vocal part après la synthèse, pas avant : un déclenchement dont le
TTS échoue n'a rien fait entendre, le compter serait faux. Aucun événement par
question posée — elles sont déjà dans VisitorQuestion et remontent dans l'onglet
Guide IA ; les compter ici serait le même fait dans deux écrans.

isAutoTriggered suit le renommage serveur en isVisitorQuestion.

flutter analyze lib sans erreur, flutter build apk --debug --flavor dev vert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:32:56 +02:00

69 lines
2.3 KiB
Dart

import 'dart:convert';
import 'dart:math';
import 'package:manager_api_new/api.dart';
import 'package:mymuseum_visitapp/client.dart';
class StatisticsService {
final Client clientAPI;
final String? instanceId;
final String? configurationId;
final String? appType; // "Mobile" or "Tablet"
final String? language;
final String sessionId;
StatisticsService({
required this.clientAPI,
required this.instanceId,
required this.configurationId,
this.appType = 'Mobile',
this.language,
}) : sessionId = _generateSessionId();
static String _generateSessionId() {
final rand = Random();
final bytes = List<int>.generate(16, (_) => rand.nextInt(256));
return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
}
/// [isVoice] marque un événement **consommé à la voix** — le guide a lu la section
/// à haute voix, par déclenchement proactif ou en réponse à une question.
///
/// ⚠️ C'est un attribut de l'événement, **pas un `appType`**. Un visiteur passe des
/// lunettes à l'écran dans une même session — c'est tout l'objet du miroir — et
/// `AppTypeDistribution` ne compte qu'une entrée par session, tranchée sur
/// l'événement le plus ancien : commencer au doigt puis mettre les lunettes aurait
/// rendu le vocal invisible, l'inverse aurait compté la navigation tactile comme
/// vocale. Le KPI serveur devient « part des sessions ayant utilisé le vocal ».
///
/// Même conception que `VisitorQuestion`, qui porte un booléen `IsVoice` **à côté**
/// de son `AppType`.
Future<void> track(
String eventType, {
String? sectionId,
int? durationSeconds,
Map<String, dynamic>? metadata,
bool isVoice = false,
}) async {
final payload = isVoice
? {...?metadata, 'voice': true}
: metadata;
try {
await clientAPI.statsApi!.statsTrackEvent(VisitEventDTO(
instanceId: instanceId ?? '',
configurationId: configurationId,
sectionId: sectionId,
sessionId: sessionId,
eventType: eventType,
appType: appType,
language: language,
durationSeconds: durationSeconds,
metadata: payload != null ? jsonEncode(payload) : null,
timestamp: DateTime.now(),
));
} catch (_) {
// fire-and-forget — never block the UI on stats errors
}
}
}