import 'package:equatable/equatable.dart'; import 'analytics_data.dart'; /// Point de données pour une tendance KPI class PointDonnee extends Equatable { const PointDonnee({ required this.date, required this.valeur, this.libelle, this.anomalie = false, this.prediction = false, this.metadonnees, }); final DateTime date; final double valeur; final String? libelle; final bool anomalie; final bool prediction; final String? metadonnees; @override List get props => [ date, valeur, libelle, anomalie, prediction, metadonnees, ]; PointDonnee copyWith({ DateTime? date, double? valeur, String? libelle, bool? anomalie, bool? prediction, String? metadonnees, }) { return PointDonnee( date: date ?? this.date, valeur: valeur ?? this.valeur, libelle: libelle ?? this.libelle, anomalie: anomalie ?? this.anomalie, prediction: prediction ?? this.prediction, metadonnees: metadonnees ?? this.metadonnees, ); } } /// Entité représentant les tendances et évolutions d'un KPI class KPITrend extends Equatable { const KPITrend({ required this.id, required this.typeMetrique, required this.periodeAnalyse, this.organisationId, this.nomOrganisation, required this.dateDebut, required this.dateFin, required this.pointsDonnees, required this.valeurActuelle, this.valeurMinimale, this.valeurMaximale, this.valeurMoyenne, this.ecartType, this.coefficientVariation, this.tendanceGenerale, this.coefficientCorrelation, this.pourcentageEvolutionGlobale, this.predictionProchainePeriode, this.margeErreurPrediction, this.seuilAlerteBas, this.seuilAlerteHaut, this.alerteActive = false, this.typeAlerte, this.messageAlerte, this.configurationGraphique, this.intervalleRegroupement, this.formatDate, this.dateDerniereMiseAJour, this.frequenceMiseAJourMinutes, }); final String id; final TypeMetrique typeMetrique; final PeriodeAnalyse periodeAnalyse; final String? organisationId; final String? nomOrganisation; final DateTime dateDebut; final DateTime dateFin; final List pointsDonnees; final double valeurActuelle; final double? valeurMinimale; final double? valeurMaximale; final double? valeurMoyenne; final double? ecartType; final double? coefficientVariation; final double? tendanceGenerale; final double? coefficientCorrelation; final double? pourcentageEvolutionGlobale; final double? predictionProchainePeriode; final double? margeErreurPrediction; final double? seuilAlerteBas; final double? seuilAlerteHaut; final bool alerteActive; final String? typeAlerte; final String? messageAlerte; final String? configurationGraphique; final String? intervalleRegroupement; final String? formatDate; final DateTime? dateDerniereMiseAJour; final int? frequenceMiseAJourMinutes; /// Retourne le libellé de la métrique String get libelleMetrique => typeMetrique.libelle; /// Retourne l'unité de mesure String get unite => typeMetrique.unite; /// Retourne l'icône de la métrique String get icone => typeMetrique.icone; /// Retourne la couleur de la métrique String get couleur => typeMetrique.couleur; /// Vérifie si la tendance est positive bool get isTendancePositive { return tendanceGenerale != null && tendanceGenerale! > 0; } /// Vérifie si la tendance est négative bool get isTendanceNegative { return tendanceGenerale != null && tendanceGenerale! < 0; } /// Vérifie si la tendance est stable bool get isTendanceStable { return tendanceGenerale != null && tendanceGenerale! == 0; } /// Retourne la volatilité du KPI String get volatilite { if (coefficientVariation == null) return 'inconnue'; if (coefficientVariation! <= 0.1) return 'faible'; if (coefficientVariation! <= 0.3) return 'moyenne'; return 'élevée'; } /// Vérifie si la prédiction est fiable bool get isPredictionFiable { return coefficientCorrelation != null && coefficientCorrelation! >= 0.7; } /// Retourne le nombre de points de données int get nombrePointsDonnees => pointsDonnees.length; /// Vérifie si des anomalies ont été détectées bool get hasAnomalies { return pointsDonnees.any((point) => point.anomalie); } /// Retourne les points d'anomalies List get pointsAnomalies { return pointsDonnees.where((point) => point.anomalie).toList(); } /// Retourne les points de prédiction List get pointsPredictions { return pointsDonnees.where((point) => point.prediction).toList(); } /// Formate la valeur actuelle String get valeurActuelleFormatee { switch (typeMetrique.typeValeur) { case 'amount': return '${valeurActuelle.toStringAsFixed(0)} ${unite}'; case 'percentage': return '${valeurActuelle.toStringAsFixed(1)}${unite}'; case 'average': return valeurActuelle.toStringAsFixed(1); default: return valeurActuelle.toStringAsFixed(0); } } /// Formate l'évolution globale String get evolutionGlobaleFormatee { if (pourcentageEvolutionGlobale == null) return ''; final signe = pourcentageEvolutionGlobale! >= 0 ? '+' : ''; return '$signe${pourcentageEvolutionGlobale!.toStringAsFixed(1)}%'; } /// Formate la prédiction String get predictionFormatee { if (predictionProchainePeriode == null) return ''; switch (typeMetrique.typeValeur) { case 'amount': return '${predictionProchainePeriode!.toStringAsFixed(0)} ${unite}'; case 'percentage': return '${predictionProchainePeriode!.toStringAsFixed(1)}${unite}'; case 'average': return predictionProchainePeriode!.toStringAsFixed(1); default: return predictionProchainePeriode!.toStringAsFixed(0); } } /// Retourne la description de la tendance String get descriptionTendance { if (isTendancePositive) { return 'Tendance à la hausse'; } else if (isTendanceNegative) { return 'Tendance à la baisse'; } else { return 'Tendance stable'; } } /// Retourne l'icône de la tendance String get iconeTendance { if (isTendancePositive) { return 'trending_up'; } else if (isTendanceNegative) { return 'trending_down'; } else { return 'trending_flat'; } } /// Retourne la couleur de la tendance String get couleurTendance { if (isTendancePositive) { return '#4CAF50'; // Vert } else if (isTendanceNegative) { return '#F44336'; // Rouge } else { return '#FF9800'; // Orange } } /// Retourne le niveau de confiance de la prédiction String get niveauConfiancePrediction { if (coefficientCorrelation == null) return 'Inconnu'; if (coefficientCorrelation! >= 0.9) return 'Très élevé'; if (coefficientCorrelation! >= 0.7) return 'Élevé'; if (coefficientCorrelation! >= 0.5) return 'Moyen'; if (coefficientCorrelation! >= 0.3) return 'Faible'; return 'Très faible'; } @override List get props => [ id, typeMetrique, periodeAnalyse, organisationId, nomOrganisation, dateDebut, dateFin, pointsDonnees, valeurActuelle, valeurMinimale, valeurMaximale, valeurMoyenne, ecartType, coefficientVariation, tendanceGenerale, coefficientCorrelation, pourcentageEvolutionGlobale, predictionProchainePeriode, margeErreurPrediction, seuilAlerteBas, seuilAlerteHaut, alerteActive, typeAlerte, messageAlerte, configurationGraphique, intervalleRegroupement, formatDate, dateDerniereMiseAJour, frequenceMiseAJourMinutes, ]; KPITrend copyWith({ String? id, TypeMetrique? typeMetrique, PeriodeAnalyse? periodeAnalyse, String? organisationId, String? nomOrganisation, DateTime? dateDebut, DateTime? dateFin, List? pointsDonnees, double? valeurActuelle, double? valeurMinimale, double? valeurMaximale, double? valeurMoyenne, double? ecartType, double? coefficientVariation, double? tendanceGenerale, double? coefficientCorrelation, double? pourcentageEvolutionGlobale, double? predictionProchainePeriode, double? margeErreurPrediction, double? seuilAlerteBas, double? seuilAlerteHaut, bool? alerteActive, String? typeAlerte, String? messageAlerte, String? configurationGraphique, String? intervalleRegroupement, String? formatDate, DateTime? dateDerniereMiseAJour, int? frequenceMiseAJourMinutes, }) { return KPITrend( id: id ?? this.id, typeMetrique: typeMetrique ?? this.typeMetrique, periodeAnalyse: periodeAnalyse ?? this.periodeAnalyse, organisationId: organisationId ?? this.organisationId, nomOrganisation: nomOrganisation ?? this.nomOrganisation, dateDebut: dateDebut ?? this.dateDebut, dateFin: dateFin ?? this.dateFin, pointsDonnees: pointsDonnees ?? this.pointsDonnees, valeurActuelle: valeurActuelle ?? this.valeurActuelle, valeurMinimale: valeurMinimale ?? this.valeurMinimale, valeurMaximale: valeurMaximale ?? this.valeurMaximale, valeurMoyenne: valeurMoyenne ?? this.valeurMoyenne, ecartType: ecartType ?? this.ecartType, coefficientVariation: coefficientVariation ?? this.coefficientVariation, tendanceGenerale: tendanceGenerale ?? this.tendanceGenerale, coefficientCorrelation: coefficientCorrelation ?? this.coefficientCorrelation, pourcentageEvolutionGlobale: pourcentageEvolutionGlobale ?? this.pourcentageEvolutionGlobale, predictionProchainePeriode: predictionProchainePeriode ?? this.predictionProchainePeriode, margeErreurPrediction: margeErreurPrediction ?? this.margeErreurPrediction, seuilAlerteBas: seuilAlerteBas ?? this.seuilAlerteBas, seuilAlerteHaut: seuilAlerteHaut ?? this.seuilAlerteHaut, alerteActive: alerteActive ?? this.alerteActive, typeAlerte: typeAlerte ?? this.typeAlerte, messageAlerte: messageAlerte ?? this.messageAlerte, configurationGraphique: configurationGraphique ?? this.configurationGraphique, intervalleRegroupement: intervalleRegroupement ?? this.intervalleRegroupement, formatDate: formatDate ?? this.formatDate, dateDerniereMiseAJour: dateDerniereMiseAJour ?? this.dateDerniereMiseAJour, frequenceMiseAJourMinutes: frequenceMiseAJourMinutes ?? this.frequenceMiseAJourMinutes, ); } }