init
This commit is contained in:
244
client/lib/features/speaking/models/speaking_stats.dart
Normal file
244
client/lib/features/speaking/models/speaking_stats.dart
Normal file
@@ -0,0 +1,244 @@
|
||||
import 'speaking_scenario.dart';
|
||||
import 'pronunciation_assessment.dart';
|
||||
|
||||
class SpeakingStats {
|
||||
final int totalSessions;
|
||||
final int totalMinutes;
|
||||
final double averageScore;
|
||||
final Map<SpeakingScenario, int> scenarioStats;
|
||||
final Map<SpeakingDifficulty, int> difficultyStats;
|
||||
final List<SpeakingProgressData> 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<String, dynamic> json) {
|
||||
final scenarioStatsJson = json['scenarioStats'] as Map<String, dynamic>? ?? {};
|
||||
final scenarioStats = <SpeakingScenario, int>{};
|
||||
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<String, dynamic>?;
|
||||
final levelStatsBackend = json['stats_by_level'] as Map<String, dynamic>?;
|
||||
final difficultyStats = <SpeakingDifficulty, int>{};
|
||||
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<String, dynamic>? ?? {};
|
||||
final averageScore = (json['averageScore'] as num?)?.toDouble() ?? (avgScores['overall'] as num?)?.toDouble() ?? 0.0;
|
||||
|
||||
final skillAnalysisJson = json['skillAnalysis'] as Map<String, dynamic>?;
|
||||
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<dynamic>? ?? [])
|
||||
.map((e) => SpeakingProgressData.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
skillAnalysis: skillAnalysis,
|
||||
lastUpdated: (json['lastUpdated'] is String)
|
||||
? DateTime.parse(json['lastUpdated'] as String)
|
||||
: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final scenarioStatsJson = <String, int>{};
|
||||
for (final entry in scenarioStats.entries) {
|
||||
scenarioStatsJson[entry.key.name] = entry.value;
|
||||
}
|
||||
|
||||
final difficultyStatsJson = <String, int>{};
|
||||
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<PronunciationCriteria, double> criteriaScores;
|
||||
|
||||
const SpeakingProgressData({
|
||||
required this.date,
|
||||
required this.averageScore,
|
||||
required this.sessionCount,
|
||||
required this.totalMinutes,
|
||||
required this.criteriaScores,
|
||||
});
|
||||
|
||||
factory SpeakingProgressData.fromJson(Map<String, dynamic> json) {
|
||||
final criteriaScoresJson = json['criteriaScores'] as Map<String, dynamic>? ?? {};
|
||||
final criteriaScores = <PronunciationCriteria, double>{};
|
||||
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<String, dynamic> toJson() {
|
||||
final criteriaScoresJson = <String, double>{};
|
||||
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<PronunciationCriteria, double> criteriaScores;
|
||||
final Map<String, int> commonErrors;
|
||||
final List<String> strengths;
|
||||
final List<String> weaknesses;
|
||||
final List<String> 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<String, dynamic> json) {
|
||||
final criteriaScoresJson = json['criteriaScores'] as Map<String, dynamic>? ?? {};
|
||||
final criteriaScores = <PronunciationCriteria, double>{};
|
||||
for (final criteria in PronunciationCriteria.values) {
|
||||
criteriaScores[criteria] = (criteriaScoresJson[criteria.name] as num?)?.toDouble() ?? 0.0;
|
||||
}
|
||||
|
||||
return SpeakingSkillAnalysis(
|
||||
criteriaScores: criteriaScores,
|
||||
commonErrors: Map<String, int>.from(json['commonErrors'] ?? {}),
|
||||
strengths: List<String>.from(json['strengths'] ?? []),
|
||||
weaknesses: List<String>.from(json['weaknesses'] ?? []),
|
||||
recommendations: List<String>.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<String, dynamic> toJson() {
|
||||
final criteriaScoresJson = <String, double>{};
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user