class WritingStats { final String userId; final int totalTasks; final int completedTasks; final int totalWords; final int totalTimeSpent; // 秒 final double averageScore; final Map taskTypeStats; final Map difficultyStats; final List progressData; final WritingSkillAnalysis skillAnalysis; final DateTime lastUpdated; const WritingStats({ required this.userId, required this.totalTasks, required this.completedTasks, required this.totalWords, required this.totalTimeSpent, required this.averageScore, required this.taskTypeStats, required this.difficultyStats, required this.progressData, required this.skillAnalysis, required this.lastUpdated, }); double get completionRate => totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0; double get averageWordsPerTask => completedTasks > 0 ? totalWords / completedTasks : 0; double get averageTimePerTask => completedTasks > 0 ? totalTimeSpent / completedTasks : 0; factory WritingStats.fromJson(Map json) { final userIdVal = json['userId']; final totalTasksVal = json['totalTasks'] ?? json['total_submissions']; final completedTasksVal = json['completedTasks'] ?? json['completed_submissions']; final totalWordsVal = json['totalWords'] ?? json['total_word_count']; final totalTimeSpentVal = json['totalTimeSpent'] ?? json['total_time_spent']; final averageScoreVal = json['averageScore'] ?? json['average_score']; final difficultyStatsVal = json['difficultyStats'] ?? json['difficulty_stats'] ?? {}; final progressDataVal = json['progressData'] ?? []; final skillAnalysisVal = json['skillAnalysis']; final lastUpdatedVal = json['lastUpdated']; final stats = WritingStats( userId: userIdVal?.toString() ?? '', totalTasks: _asInt(totalTasksVal), completedTasks: _asInt(completedTasksVal), totalWords: _asInt(totalWordsVal), totalTimeSpent: _asInt(totalTimeSpentVal), averageScore: _asDouble(averageScoreVal), taskTypeStats: Map.from(json['taskTypeStats'] ?? {}), difficultyStats: Map.from(difficultyStatsVal), progressData: (progressDataVal is List) ? progressDataVal .map((e) => WritingProgressData.fromJson(Map.from(e))) .toList() : [], skillAnalysis: skillAnalysisVal is Map ? WritingSkillAnalysis.fromJson(skillAnalysisVal) : WritingSkillAnalysis( criteriaScores: { 'grammar': _normalize01(_asDouble(json['average_grammar_score'])), 'coherence': _normalize01(_asDouble(json['average_coherence_score'])), 'vocab': _normalize01(_asDouble(json['average_vocab_score'])), }, errorCounts: {}, strengths: const [], weaknesses: const [], recommendations: const [], improvementRate: 0.0, lastAnalyzed: DateTime.now(), ), lastUpdated: lastUpdatedVal is String ? DateTime.parse(lastUpdatedVal) : DateTime.now(), ); return stats; } Map toJson() { return { 'userId': userId, 'totalTasks': totalTasks, 'completedTasks': completedTasks, 'totalWords': totalWords, 'totalTimeSpent': totalTimeSpent, 'averageScore': averageScore, 'taskTypeStats': taskTypeStats, 'difficultyStats': difficultyStats, 'progressData': progressData.map((e) => e.toJson()).toList(), 'skillAnalysis': skillAnalysis.toJson(), 'lastUpdated': lastUpdated.toIso8601String(), }; } WritingStats copyWith({ String? userId, int? totalTasks, int? completedTasks, int? totalWords, int? totalTimeSpent, double? averageScore, Map? taskTypeStats, Map? difficultyStats, List? progressData, WritingSkillAnalysis? skillAnalysis, DateTime? lastUpdated, }) { return WritingStats( userId: userId ?? this.userId, totalTasks: totalTasks ?? this.totalTasks, completedTasks: completedTasks ?? this.completedTasks, totalWords: totalWords ?? this.totalWords, totalTimeSpent: totalTimeSpent ?? this.totalTimeSpent, averageScore: averageScore ?? this.averageScore, taskTypeStats: taskTypeStats ?? this.taskTypeStats, difficultyStats: difficultyStats ?? this.difficultyStats, progressData: progressData ?? this.progressData, skillAnalysis: skillAnalysis ?? this.skillAnalysis, lastUpdated: lastUpdated ?? this.lastUpdated, ); } } class WritingProgressData { final DateTime date; final double score; final int wordCount; final int timeSpent; final String taskType; final String difficulty; const WritingProgressData({ required this.date, required this.score, required this.wordCount, required this.timeSpent, required this.taskType, required this.difficulty, }); factory WritingProgressData.fromJson(Map json) { return WritingProgressData( date: DateTime.parse(json['date'] as String), score: (json['score'] as num).toDouble(), wordCount: json['wordCount'] as int, timeSpent: json['timeSpent'] as int, taskType: json['taskType'] as String, difficulty: json['difficulty'] as String, ); } Map toJson() { return { 'date': date.toIso8601String(), 'score': score, 'wordCount': wordCount, 'timeSpent': timeSpent, 'taskType': taskType, 'difficulty': difficulty, }; } } class WritingSkillAnalysis { final Map criteriaScores; final Map errorCounts; final List strengths; final List weaknesses; final List recommendations; final double improvementRate; final DateTime lastAnalyzed; const WritingSkillAnalysis({ required this.criteriaScores, required this.errorCounts, required this.strengths, required this.weaknesses, required this.recommendations, required this.improvementRate, required this.lastAnalyzed, }); factory WritingSkillAnalysis.fromJson(Map json) { return WritingSkillAnalysis( criteriaScores: Map.from( (json['criteriaScores'] as Map).map( (k, v) => MapEntry(k, (v as num).toDouble()), ), ), errorCounts: Map.from(json['errorCounts'] ?? {}), strengths: List.from(json['strengths'] ?? []), weaknesses: List.from(json['weaknesses'] ?? []), recommendations: List.from(json['recommendations'] ?? []), improvementRate: (json['improvementRate'] as num).toDouble(), lastAnalyzed: DateTime.parse(json['lastAnalyzed'] as String), ); } Map toJson() { return { 'criteriaScores': criteriaScores, 'errorCounts': errorCounts, 'strengths': strengths, 'weaknesses': weaknesses, 'recommendations': recommendations, 'improvementRate': improvementRate, 'lastAnalyzed': lastAnalyzed.toIso8601String(), }; } } int _asInt(dynamic v) { if (v == null) return 0; if (v is int) return v; if (v is num) return v.toInt(); if (v is String) { final parsed = double.tryParse(v); return parsed?.toInt() ?? int.tryParse(v) ?? 0; } return 0; } double _asDouble(dynamic v) { if (v == null) return 0.0; if (v is double) return v; if (v is num) return v.toDouble(); if (v is String) return double.tryParse(v) ?? 0.0; return 0.0; } double _normalize01(double v) { if (v <= 1.0) return v; return v / 100.0; }