import 'speaking_scenario.dart'; import 'pronunciation_assessment.dart'; class SpeakingStats { final int totalSessions; final int totalMinutes; final double averageScore; final Map scenarioStats; final Map difficultyStats; final List progressData; final SpeakingSkillAnalysis skillAnalysis; final DateTime lastUpdated; const SpeakingStats({ required this.totalSessions, required this.totalMinutes, required this.averageScore, required this.scenarioStats, required this.difficultyStats, required this.progressData, required this.skillAnalysis, required this.lastUpdated, }); factory SpeakingStats.fromJson(Map json) { final scenarioStatsJson = json['scenarioStats'] as Map? ?? {}; final scenarioStats = {}; for (final scenario in SpeakingScenario.values) { final v = scenarioStatsJson[scenario.name]; scenarioStats[scenario] = (v is num) ? v.toInt() : 0; } final difficultyStatsLegacy = json['difficultyStats'] as Map?; final levelStatsBackend = json['stats_by_level'] as Map?; final difficultyStats = {}; if (difficultyStatsLegacy != null) { for (final difficulty in SpeakingDifficulty.values) { final v = difficultyStatsLegacy[difficulty.name]; difficultyStats[difficulty] = (v is num) ? v.toInt() : 0; } } else { for (final difficulty in SpeakingDifficulty.values) { final key = { SpeakingDifficulty.beginner: 'beginner', SpeakingDifficulty.elementary: 'elementary', SpeakingDifficulty.intermediate: 'intermediate', SpeakingDifficulty.upperIntermediate: 'upper_intermediate', SpeakingDifficulty.advanced: 'advanced', }[difficulty]!; final entry = levelStatsBackend?[key]; int count = 0; if (entry is Map) { final c = entry['count']; if (c is num) count = c.toInt(); } difficultyStats[difficulty] = count; } } final avgScores = json['average_scores'] as Map? ?? {}; final averageScore = (json['averageScore'] as num?)?.toDouble() ?? (avgScores['overall'] as num?)?.toDouble() ?? 0.0; final skillAnalysisJson = json['skillAnalysis'] as Map?; final skillAnalysis = skillAnalysisJson != null ? SpeakingSkillAnalysis.fromJson(skillAnalysisJson) : SpeakingSkillAnalysis( criteriaScores: { PronunciationCriteria.accuracy: (avgScores['accuracy'] as num?)?.toDouble() ?? 0.0, PronunciationCriteria.fluency: (avgScores['fluency'] as num?)?.toDouble() ?? 0.0, PronunciationCriteria.completeness: (avgScores['completeness'] as num?)?.toDouble() ?? 0.0, PronunciationCriteria.prosody: (avgScores['prosody'] as num?)?.toDouble() ?? 0.0, }, commonErrors: {}, strengths: const [], weaknesses: const [], recommendations: const [], improvementRate: 0.0, lastAnalyzed: DateTime.now(), ); return SpeakingStats( totalSessions: (json['totalSessions'] as int?) ?? (json['total_records'] as num?)?.toInt() ?? 0, totalMinutes: (json['totalMinutes'] as int?) ?? (json['total_duration'] as num?)?.toInt() ?? 0, averageScore: averageScore, scenarioStats: scenarioStats, difficultyStats: difficultyStats, progressData: (json['progressData'] as List? ?? []) .map((e) => SpeakingProgressData.fromJson(e as Map)) .toList(), skillAnalysis: skillAnalysis, lastUpdated: (json['lastUpdated'] is String) ? DateTime.parse(json['lastUpdated'] as String) : DateTime.now(), ); } Map toJson() { final scenarioStatsJson = {}; for (final entry in scenarioStats.entries) { scenarioStatsJson[entry.key.name] = entry.value; } final difficultyStatsJson = {}; for (final entry in difficultyStats.entries) { difficultyStatsJson[entry.key.name] = entry.value; } return { 'totalSessions': totalSessions, 'totalMinutes': totalMinutes, 'averageScore': averageScore, 'scenarioStats': scenarioStatsJson, 'difficultyStats': difficultyStatsJson, 'progressData': progressData.map((e) => e.toJson()).toList(), 'skillAnalysis': skillAnalysis.toJson(), 'lastUpdated': lastUpdated.toIso8601String(), }; } } class SpeakingProgressData { final DateTime date; final double averageScore; final int sessionCount; final int totalMinutes; final Map criteriaScores; const SpeakingProgressData({ required this.date, required this.averageScore, required this.sessionCount, required this.totalMinutes, required this.criteriaScores, }); factory SpeakingProgressData.fromJson(Map json) { final criteriaScoresJson = json['criteriaScores'] as Map? ?? {}; final criteriaScores = {}; for (final criteria in PronunciationCriteria.values) { criteriaScores[criteria] = (criteriaScoresJson[criteria.name] as num?)?.toDouble() ?? 0.0; } return SpeakingProgressData( date: DateTime.parse(json['date'] as String), averageScore: (json['averageScore'] as num).toDouble(), sessionCount: (json['sessionCount'] as num).toInt(), totalMinutes: (json['totalMinutes'] as num).toInt(), criteriaScores: criteriaScores, ); } Map toJson() { final criteriaScoresJson = {}; for (final entry in criteriaScores.entries) { criteriaScoresJson[entry.key.name] = entry.value; } return { 'date': date.toIso8601String(), 'averageScore': averageScore, 'sessionCount': sessionCount, 'totalMinutes': totalMinutes, 'criteriaScores': criteriaScoresJson, }; } } class SpeakingSkillAnalysis { final Map criteriaScores; final Map commonErrors; final List strengths; final List weaknesses; final List recommendations; final double improvementRate; // 改进速度 final DateTime lastAnalyzed; const SpeakingSkillAnalysis({ required this.criteriaScores, required this.commonErrors, required this.strengths, required this.weaknesses, required this.recommendations, required this.improvementRate, required this.lastAnalyzed, }); factory SpeakingSkillAnalysis.fromJson(Map json) { final criteriaScoresJson = json['criteriaScores'] as Map? ?? {}; final criteriaScores = {}; for (final criteria in PronunciationCriteria.values) { criteriaScores[criteria] = (criteriaScoresJson[criteria.name] as num?)?.toDouble() ?? 0.0; } return SpeakingSkillAnalysis( criteriaScores: criteriaScores, commonErrors: Map.from(json['commonErrors'] ?? {}), strengths: List.from(json['strengths'] ?? []), weaknesses: List.from(json['weaknesses'] ?? []), recommendations: List.from(json['recommendations'] ?? []), improvementRate: (json['improvementRate'] as num?)?.toDouble() ?? 0.0, lastAnalyzed: (json['lastAnalyzed'] is String) ? DateTime.parse(json['lastAnalyzed'] as String) : DateTime.now(), ); } Map toJson() { final criteriaScoresJson = {}; for (final entry in criteriaScores.entries) { criteriaScoresJson[entry.key.name] = entry.value; } return { 'criteriaScores': criteriaScoresJson, 'commonErrors': commonErrors, 'strengths': strengths, 'weaknesses': weaknesses, 'recommendations': recommendations, 'improvementRate': improvementRate, 'lastAnalyzed': lastAnalyzed.toIso8601String(), }; } String get overallLevel { final averageScore = criteriaScores.values.reduce((a, b) => a + b) / criteriaScores.length; if (averageScore >= 90) return '优秀'; if (averageScore >= 80) return '良好'; if (averageScore >= 70) return '中等'; if (averageScore >= 60) return '及格'; return '需要改进'; } PronunciationCriteria get strongestSkill { return criteriaScores.entries .reduce((a, b) => a.value > b.value ? a : b) .key; } PronunciationCriteria get weakestSkill { return criteriaScores.entries .reduce((a, b) => a.value < b.value ? a : b) .key; } }