464 lines
15 KiB
Dart
464 lines
15 KiB
Dart
import 'package:dartz/dartz.dart';
|
|
import '../../../../core/error/failures.dart';
|
|
import '../../../../core/usecases/usecase.dart';
|
|
import '../entities/evaluation_aide.dart';
|
|
import '../repositories/solidarite_repository.dart';
|
|
|
|
/// Cas d'usage pour créer une nouvelle évaluation
|
|
class CreerEvaluationUseCase implements UseCase<EvaluationAide, CreerEvaluationParams> {
|
|
final SolidariteRepository repository;
|
|
|
|
CreerEvaluationUseCase(this.repository);
|
|
|
|
@override
|
|
Future<Either<Failure, EvaluationAide>> call(CreerEvaluationParams params) async {
|
|
return await repository.creerEvaluation(params.evaluation);
|
|
}
|
|
}
|
|
|
|
class CreerEvaluationParams {
|
|
final EvaluationAide evaluation;
|
|
|
|
CreerEvaluationParams({required this.evaluation});
|
|
}
|
|
|
|
/// Cas d'usage pour mettre à jour une évaluation
|
|
class MettreAJourEvaluationUseCase implements UseCase<EvaluationAide, MettreAJourEvaluationParams> {
|
|
final SolidariteRepository repository;
|
|
|
|
MettreAJourEvaluationUseCase(this.repository);
|
|
|
|
@override
|
|
Future<Either<Failure, EvaluationAide>> call(MettreAJourEvaluationParams params) async {
|
|
return await repository.mettreAJourEvaluation(params.evaluation);
|
|
}
|
|
}
|
|
|
|
class MettreAJourEvaluationParams {
|
|
final EvaluationAide evaluation;
|
|
|
|
MettreAJourEvaluationParams({required this.evaluation});
|
|
}
|
|
|
|
/// Cas d'usage pour obtenir une évaluation par ID
|
|
class ObtenirEvaluationUseCase implements UseCase<EvaluationAide, ObtenirEvaluationParams> {
|
|
final SolidariteRepository repository;
|
|
|
|
ObtenirEvaluationUseCase(this.repository);
|
|
|
|
@override
|
|
Future<Either<Failure, EvaluationAide>> call(ObtenirEvaluationParams params) async {
|
|
return await repository.obtenirEvaluation(params.id);
|
|
}
|
|
}
|
|
|
|
class ObtenirEvaluationParams {
|
|
final String id;
|
|
|
|
ObtenirEvaluationParams({required this.id});
|
|
}
|
|
|
|
/// Cas d'usage pour obtenir les évaluations d'une demande
|
|
class ObtenirEvaluationsDemandeUseCase implements UseCase<List<EvaluationAide>, ObtenirEvaluationsDemandeParams> {
|
|
final SolidariteRepository repository;
|
|
|
|
ObtenirEvaluationsDemandeUseCase(this.repository);
|
|
|
|
@override
|
|
Future<Either<Failure, List<EvaluationAide>>> call(ObtenirEvaluationsDemandeParams params) async {
|
|
return await repository.obtenirEvaluationsDemande(params.demandeId);
|
|
}
|
|
}
|
|
|
|
class ObtenirEvaluationsDemandeParams {
|
|
final String demandeId;
|
|
|
|
ObtenirEvaluationsDemandeParams({required this.demandeId});
|
|
}
|
|
|
|
/// Cas d'usage pour obtenir les évaluations d'une proposition
|
|
class ObtenirEvaluationsPropositionUseCase implements UseCase<List<EvaluationAide>, ObtenirEvaluationsPropositionParams> {
|
|
final SolidariteRepository repository;
|
|
|
|
ObtenirEvaluationsPropositionUseCase(this.repository);
|
|
|
|
@override
|
|
Future<Either<Failure, List<EvaluationAide>>> call(ObtenirEvaluationsPropositionParams params) async {
|
|
return await repository.obtenirEvaluationsProposition(params.propositionId);
|
|
}
|
|
}
|
|
|
|
class ObtenirEvaluationsPropositionParams {
|
|
final String propositionId;
|
|
|
|
ObtenirEvaluationsPropositionParams({required this.propositionId});
|
|
}
|
|
|
|
/// Cas d'usage pour signaler une évaluation
|
|
class SignalerEvaluationUseCase implements UseCase<EvaluationAide, SignalerEvaluationParams> {
|
|
final SolidariteRepository repository;
|
|
|
|
SignalerEvaluationUseCase(this.repository);
|
|
|
|
@override
|
|
Future<Either<Failure, EvaluationAide>> call(SignalerEvaluationParams params) async {
|
|
return await repository.signalerEvaluation(
|
|
evaluationId: params.evaluationId,
|
|
motif: params.motif,
|
|
);
|
|
}
|
|
}
|
|
|
|
class SignalerEvaluationParams {
|
|
final String evaluationId;
|
|
final String motif;
|
|
|
|
SignalerEvaluationParams({
|
|
required this.evaluationId,
|
|
required this.motif,
|
|
});
|
|
}
|
|
|
|
/// Cas d'usage pour calculer la note moyenne d'une demande
|
|
class CalculerMoyenneDemandeUseCase implements UseCase<StatistiquesEvaluation, CalculerMoyenneDemandeParams> {
|
|
final SolidariteRepository repository;
|
|
|
|
CalculerMoyenneDemandeUseCase(this.repository);
|
|
|
|
@override
|
|
Future<Either<Failure, StatistiquesEvaluation>> call(CalculerMoyenneDemandeParams params) async {
|
|
return await repository.calculerMoyenneDemande(params.demandeId);
|
|
}
|
|
}
|
|
|
|
class CalculerMoyenneDemandeParams {
|
|
final String demandeId;
|
|
|
|
CalculerMoyenneDemandeParams({required this.demandeId});
|
|
}
|
|
|
|
/// Cas d'usage pour calculer la note moyenne d'une proposition
|
|
class CalculerMoyennePropositionUseCase implements UseCase<StatistiquesEvaluation, CalculerMoyennePropositionParams> {
|
|
final SolidariteRepository repository;
|
|
|
|
CalculerMoyennePropositionUseCase(this.repository);
|
|
|
|
@override
|
|
Future<Either<Failure, StatistiquesEvaluation>> call(CalculerMoyennePropositionParams params) async {
|
|
return await repository.calculerMoyenneProposition(params.propositionId);
|
|
}
|
|
}
|
|
|
|
class CalculerMoyennePropositionParams {
|
|
final String propositionId;
|
|
|
|
CalculerMoyennePropositionParams({required this.propositionId});
|
|
}
|
|
|
|
/// Cas d'usage pour valider une évaluation avant création
|
|
class ValiderEvaluationUseCase implements UseCase<bool, ValiderEvaluationParams> {
|
|
ValiderEvaluationUseCase();
|
|
|
|
@override
|
|
Future<Either<Failure, bool>> call(ValiderEvaluationParams params) async {
|
|
try {
|
|
final evaluation = params.evaluation;
|
|
final erreurs = <String>[];
|
|
|
|
// Validation de la note globale
|
|
if (evaluation.noteGlobale < 1.0 || evaluation.noteGlobale > 5.0) {
|
|
erreurs.add('La note globale doit être comprise entre 1 et 5');
|
|
}
|
|
|
|
// Validation des notes détaillées
|
|
final notesDetaillees = [
|
|
evaluation.noteDelaiReponse,
|
|
evaluation.noteCommunication,
|
|
evaluation.noteProfessionnalisme,
|
|
evaluation.noteRespectEngagements,
|
|
];
|
|
|
|
for (final note in notesDetaillees) {
|
|
if (note != null && (note < 1.0 || note > 5.0)) {
|
|
erreurs.add('Toutes les notes détaillées doivent être comprises entre 1 et 5');
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Validation du commentaire principal
|
|
if (evaluation.commentairePrincipal.trim().isEmpty) {
|
|
erreurs.add('Le commentaire principal est obligatoire');
|
|
} else if (evaluation.commentairePrincipal.length < 20) {
|
|
erreurs.add('Le commentaire principal doit contenir au moins 20 caractères');
|
|
} else if (evaluation.commentairePrincipal.length > 1000) {
|
|
erreurs.add('Le commentaire principal ne peut pas dépasser 1000 caractères');
|
|
}
|
|
|
|
// Validation de la cohérence entre note et commentaire
|
|
if (evaluation.noteGlobale <= 2.0 && evaluation.commentairePrincipal.length < 50) {
|
|
erreurs.add('Un commentaire détaillé est requis pour les notes faibles');
|
|
}
|
|
|
|
// Validation des points positifs et d'amélioration
|
|
if (evaluation.pointsPositifs != null && evaluation.pointsPositifs!.length > 500) {
|
|
erreurs.add('Les points positifs ne peuvent pas dépasser 500 caractères');
|
|
}
|
|
|
|
if (evaluation.pointsAmelioration != null && evaluation.pointsAmelioration!.length > 500) {
|
|
erreurs.add('Les points d\'amélioration ne peuvent pas dépasser 500 caractères');
|
|
}
|
|
|
|
// Validation des recommandations
|
|
if (evaluation.recommandations != null && evaluation.recommandations!.length > 500) {
|
|
erreurs.add('Les recommandations ne peuvent pas dépasser 500 caractères');
|
|
}
|
|
|
|
// Validation de la cohérence de la recommandation
|
|
if (evaluation.recommande == true && evaluation.noteGlobale < 3.0) {
|
|
erreurs.add('Impossible de recommander avec une note inférieure à 3');
|
|
}
|
|
|
|
if (evaluation.recommande == false && evaluation.noteGlobale >= 4.0) {
|
|
erreurs.add('Une note de 4 ou plus devrait normalement être recommandée');
|
|
}
|
|
|
|
// Détection de contenu inapproprié
|
|
if (_contientContenuInapproprie(evaluation.commentairePrincipal)) {
|
|
erreurs.add('Le commentaire contient du contenu inapproprié');
|
|
}
|
|
|
|
if (erreurs.isNotEmpty) {
|
|
return Left(ValidationFailure(erreurs.join(', ')));
|
|
}
|
|
|
|
return const Right(true);
|
|
} catch (e) {
|
|
return Left(UnexpectedFailure('Erreur lors de la validation: ${e.toString()}'));
|
|
}
|
|
}
|
|
|
|
bool _contientContenuInapproprie(String texte) {
|
|
// Liste simple de mots inappropriés (à étendre selon les besoins)
|
|
final motsInappropries = [
|
|
'spam', 'arnaque', 'escroquerie', 'fraude',
|
|
// Ajouter d'autres mots selon le contexte
|
|
];
|
|
|
|
final texteMinuscule = texte.toLowerCase();
|
|
return motsInappropries.any((mot) => texteMinuscule.contains(mot));
|
|
}
|
|
}
|
|
|
|
class ValiderEvaluationParams {
|
|
final EvaluationAide evaluation;
|
|
|
|
ValiderEvaluationParams({required this.evaluation});
|
|
}
|
|
|
|
/// Cas d'usage pour calculer le score de qualité d'une évaluation
|
|
class CalculerScoreQualiteEvaluationUseCase implements UseCase<double, CalculerScoreQualiteEvaluationParams> {
|
|
CalculerScoreQualiteEvaluationUseCase();
|
|
|
|
@override
|
|
Future<Either<Failure, double>> call(CalculerScoreQualiteEvaluationParams params) async {
|
|
try {
|
|
final evaluation = params.evaluation;
|
|
double score = 50.0; // Score de base
|
|
|
|
// Bonus pour la longueur du commentaire
|
|
final longueurCommentaire = evaluation.commentairePrincipal.length;
|
|
if (longueurCommentaire >= 100) {
|
|
score += 15.0;
|
|
} else if (longueurCommentaire >= 50) {
|
|
score += 10.0;
|
|
} else if (longueurCommentaire >= 20) {
|
|
score += 5.0;
|
|
}
|
|
|
|
// Bonus pour les notes détaillées
|
|
final notesDetaillees = [
|
|
evaluation.noteDelaiReponse,
|
|
evaluation.noteCommunication,
|
|
evaluation.noteProfessionnalisme,
|
|
evaluation.noteRespectEngagements,
|
|
];
|
|
|
|
final nombreNotesDetaillees = notesDetaillees.where((note) => note != null).length;
|
|
score += nombreNotesDetaillees * 5.0; // 5 points par note détaillée
|
|
|
|
// Bonus pour les sections optionnelles remplies
|
|
if (evaluation.pointsPositifs != null && evaluation.pointsPositifs!.isNotEmpty) {
|
|
score += 5.0;
|
|
}
|
|
|
|
if (evaluation.pointsAmelioration != null && evaluation.pointsAmelioration!.isNotEmpty) {
|
|
score += 5.0;
|
|
}
|
|
|
|
if (evaluation.recommandations != null && evaluation.recommandations!.isNotEmpty) {
|
|
score += 5.0;
|
|
}
|
|
|
|
// Bonus pour la cohérence
|
|
if (_estCoherente(evaluation)) {
|
|
score += 10.0;
|
|
}
|
|
|
|
// Malus pour les évaluations extrêmes sans justification
|
|
if ((evaluation.noteGlobale <= 1.5 || evaluation.noteGlobale >= 4.5) &&
|
|
longueurCommentaire < 50) {
|
|
score -= 15.0;
|
|
}
|
|
|
|
// Malus pour les signalements
|
|
score -= evaluation.nombreSignalements * 10.0;
|
|
|
|
return Right(score.clamp(0.0, 100.0));
|
|
} catch (e) {
|
|
return Left(UnexpectedFailure('Erreur lors du calcul du score de qualité: ${e.toString()}'));
|
|
}
|
|
}
|
|
|
|
bool _estCoherente(EvaluationAide evaluation) {
|
|
// Vérifier la cohérence entre la note globale et les notes détaillées
|
|
final notesDetaillees = [
|
|
evaluation.noteDelaiReponse,
|
|
evaluation.noteCommunication,
|
|
evaluation.noteProfessionnalisme,
|
|
evaluation.noteRespectEngagements,
|
|
].where((note) => note != null).cast<double>().toList();
|
|
|
|
if (notesDetaillees.isEmpty) return true;
|
|
|
|
final moyenneDetaillees = notesDetaillees.reduce((a, b) => a + b) / notesDetaillees.length;
|
|
final ecart = (evaluation.noteGlobale - moyenneDetaillees).abs();
|
|
|
|
// Cohérent si l'écart est inférieur à 1 point
|
|
return ecart < 1.0;
|
|
}
|
|
}
|
|
|
|
class CalculerScoreQualiteEvaluationParams {
|
|
final EvaluationAide evaluation;
|
|
|
|
CalculerScoreQualiteEvaluationParams({required this.evaluation});
|
|
}
|
|
|
|
/// Cas d'usage pour analyser les tendances d'évaluation
|
|
class AnalyserTendancesEvaluationUseCase implements UseCase<AnalyseTendancesEvaluation, AnalyserTendancesEvaluationParams> {
|
|
AnalyserTendancesEvaluationUseCase();
|
|
|
|
@override
|
|
Future<Either<Failure, AnalyseTendancesEvaluation>> call(AnalyserTendancesEvaluationParams params) async {
|
|
try {
|
|
// Simulation d'analyse des tendances d'évaluation
|
|
// Dans une vraie implémentation, on analyserait les données historiques
|
|
|
|
final analyse = AnalyseTendancesEvaluation(
|
|
noteMoyenneGlobale: 4.2,
|
|
nombreTotalEvaluations: 1247,
|
|
repartitionNotes: {
|
|
5: 456,
|
|
4: 523,
|
|
3: 189,
|
|
2: 58,
|
|
1: 21,
|
|
},
|
|
pourcentageRecommandations: 78.5,
|
|
tempsReponseEvaluationMoyen: const Duration(days: 3),
|
|
criteresLesMieuxNotes: [
|
|
CritereNote('Respect des engagements', 4.6),
|
|
CritereNote('Communication', 4.3),
|
|
CritereNote('Professionnalisme', 4.1),
|
|
CritereNote('Délai de réponse', 3.9),
|
|
],
|
|
typeEvaluateursPlusActifs: [
|
|
TypeEvaluateurActivite(TypeEvaluateur.beneficiaire, 67.2),
|
|
TypeEvaluateurActivite(TypeEvaluateur.proposant, 23.8),
|
|
TypeEvaluateurActivite(TypeEvaluateur.evaluateurOfficial, 6.5),
|
|
TypeEvaluateurActivite(TypeEvaluateur.administrateur, 2.5),
|
|
],
|
|
evolutionSatisfaction: EvolutionSatisfaction(
|
|
dernierMois: 4.2,
|
|
moisPrecedent: 4.0,
|
|
tendance: TendanceSatisfaction.hausse,
|
|
),
|
|
recommandationsAmelioration: [
|
|
'Améliorer les délais de réponse des proposants',
|
|
'Encourager plus d\'évaluations détaillées',
|
|
'Former les proposants à la communication',
|
|
],
|
|
);
|
|
|
|
return Right(analyse);
|
|
} catch (e) {
|
|
return Left(UnexpectedFailure('Erreur lors de l\'analyse des tendances: ${e.toString()}'));
|
|
}
|
|
}
|
|
}
|
|
|
|
class AnalyserTendancesEvaluationParams {
|
|
final String organisationId;
|
|
final DateTime? dateDebut;
|
|
final DateTime? dateFin;
|
|
|
|
AnalyserTendancesEvaluationParams({
|
|
required this.organisationId,
|
|
this.dateDebut,
|
|
this.dateFin,
|
|
});
|
|
}
|
|
|
|
/// Classes pour l'analyse des tendances d'évaluation
|
|
class AnalyseTendancesEvaluation {
|
|
final double noteMoyenneGlobale;
|
|
final int nombreTotalEvaluations;
|
|
final Map<int, int> repartitionNotes;
|
|
final double pourcentageRecommandations;
|
|
final Duration tempsReponseEvaluationMoyen;
|
|
final List<CritereNote> criteresLesMieuxNotes;
|
|
final List<TypeEvaluateurActivite> typeEvaluateursPlusActifs;
|
|
final EvolutionSatisfaction evolutionSatisfaction;
|
|
final List<String> recommandationsAmelioration;
|
|
|
|
const AnalyseTendancesEvaluation({
|
|
required this.noteMoyenneGlobale,
|
|
required this.nombreTotalEvaluations,
|
|
required this.repartitionNotes,
|
|
required this.pourcentageRecommandations,
|
|
required this.tempsReponseEvaluationMoyen,
|
|
required this.criteresLesMieuxNotes,
|
|
required this.typeEvaluateursPlusActifs,
|
|
required this.evolutionSatisfaction,
|
|
required this.recommandationsAmelioration,
|
|
});
|
|
}
|
|
|
|
class CritereNote {
|
|
final String nom;
|
|
final double noteMoyenne;
|
|
|
|
const CritereNote(this.nom, this.noteMoyenne);
|
|
}
|
|
|
|
class TypeEvaluateurActivite {
|
|
final TypeEvaluateur type;
|
|
final double pourcentage;
|
|
|
|
const TypeEvaluateurActivite(this.type, this.pourcentage);
|
|
}
|
|
|
|
class EvolutionSatisfaction {
|
|
final double dernierMois;
|
|
final double moisPrecedent;
|
|
final TendanceSatisfaction tendance;
|
|
|
|
const EvolutionSatisfaction({
|
|
required this.dernierMois,
|
|
required this.moisPrecedent,
|
|
required this.tendance,
|
|
});
|
|
}
|
|
|
|
enum TendanceSatisfaction { hausse, baisse, stable }
|