init
This commit is contained in:
280
client/lib/features/reading/models/reading_stats.dart
Normal file
280
client/lib/features/reading/models/reading_stats.dart
Normal file
@@ -0,0 +1,280 @@
|
||||
class ReadingStats {
|
||||
final int totalArticlesRead;
|
||||
final int practicesDone;
|
||||
final double averageScore;
|
||||
final int totalReadingTime; // in minutes
|
||||
final double averageReadingSpeed; // words per minute
|
||||
final double comprehensionAccuracy; // percentage
|
||||
final int vocabularyMastered;
|
||||
final int consecutiveDays;
|
||||
final Map<String, int> categoryStats; // category -> articles read
|
||||
final Map<String, double> difficultyStats; // difficulty -> average score
|
||||
final List<DailyReadingRecord> dailyRecords;
|
||||
|
||||
const ReadingStats({
|
||||
required this.totalArticlesRead,
|
||||
required this.practicesDone,
|
||||
required this.averageScore,
|
||||
required this.totalReadingTime,
|
||||
required this.averageReadingSpeed,
|
||||
required this.comprehensionAccuracy,
|
||||
required this.vocabularyMastered,
|
||||
required this.consecutiveDays,
|
||||
required this.categoryStats,
|
||||
required this.difficultyStats,
|
||||
required this.dailyRecords,
|
||||
});
|
||||
|
||||
factory ReadingStats.fromJson(Map<String, dynamic> json) {
|
||||
final totalArticlesRead = json['totalArticlesRead'];
|
||||
final practicesDone = json['practicesDone'];
|
||||
final averageScore = json['averageScore'];
|
||||
final totalReadingTime = json['totalReadingTime'];
|
||||
final averageReadingSpeed = json['averageReadingSpeed'];
|
||||
final comprehensionAccuracy = json['comprehensionAccuracy'];
|
||||
final vocabularyMastered = json['vocabularyMastered'];
|
||||
final consecutiveDays = json['consecutiveDays'];
|
||||
|
||||
final rawCategoryStats = json['categoryStats'];
|
||||
final Map<String, int> categoryStats = {};
|
||||
if (rawCategoryStats is Map) {
|
||||
rawCategoryStats.forEach((key, value) {
|
||||
final k = key is String ? key : key.toString();
|
||||
final v = value is num ? value.toInt() : 0;
|
||||
categoryStats[k] = v;
|
||||
});
|
||||
}
|
||||
|
||||
final rawDifficultyStats = json['difficultyStats'];
|
||||
final Map<String, double> difficultyStats = {};
|
||||
if (rawDifficultyStats is Map) {
|
||||
rawDifficultyStats.forEach((key, value) {
|
||||
final k = key is String ? key : key.toString();
|
||||
final v = value is num ? value.toDouble() : 0.0;
|
||||
difficultyStats[k] = v;
|
||||
});
|
||||
}
|
||||
|
||||
final rawDailyRecords = json['dailyRecords'];
|
||||
final List<DailyReadingRecord> dailyRecords = [];
|
||||
if (rawDailyRecords is List) {
|
||||
for (final record in rawDailyRecords) {
|
||||
if (record is Map<String, dynamic>) {
|
||||
dailyRecords.add(DailyReadingRecord.fromJson(record));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ReadingStats(
|
||||
totalArticlesRead: totalArticlesRead is num ? totalArticlesRead.toInt() : 0,
|
||||
practicesDone: practicesDone is num ? practicesDone.toInt() : 0,
|
||||
averageScore: averageScore is num ? averageScore.toDouble() : 0.0,
|
||||
totalReadingTime: totalReadingTime is num ? totalReadingTime.toInt() : 0,
|
||||
averageReadingSpeed: averageReadingSpeed is num ? averageReadingSpeed.toDouble() : 0.0,
|
||||
comprehensionAccuracy: comprehensionAccuracy is num ? comprehensionAccuracy.toDouble() : 0.0,
|
||||
vocabularyMastered: vocabularyMastered is num ? vocabularyMastered.toInt() : 0,
|
||||
consecutiveDays: consecutiveDays is num ? consecutiveDays.toInt() : 0,
|
||||
categoryStats: categoryStats,
|
||||
difficultyStats: difficultyStats,
|
||||
dailyRecords: dailyRecords,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'totalArticlesRead': totalArticlesRead,
|
||||
'practicesDone': practicesDone,
|
||||
'averageScore': averageScore,
|
||||
'totalReadingTime': totalReadingTime,
|
||||
'averageReadingSpeed': averageReadingSpeed,
|
||||
'comprehensionAccuracy': comprehensionAccuracy,
|
||||
'vocabularyMastered': vocabularyMastered,
|
||||
'consecutiveDays': consecutiveDays,
|
||||
'categoryStats': categoryStats,
|
||||
'difficultyStats': difficultyStats,
|
||||
'dailyRecords': dailyRecords.map((record) => record.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
ReadingStats copyWith({
|
||||
int? totalArticlesRead,
|
||||
int? practicesDone,
|
||||
double? averageScore,
|
||||
int? totalReadingTime,
|
||||
double? averageReadingSpeed,
|
||||
double? comprehensionAccuracy,
|
||||
int? vocabularyMastered,
|
||||
int? consecutiveDays,
|
||||
Map<String, int>? categoryStats,
|
||||
Map<String, double>? difficultyStats,
|
||||
List<DailyReadingRecord>? dailyRecords,
|
||||
}) {
|
||||
return ReadingStats(
|
||||
totalArticlesRead: totalArticlesRead ?? this.totalArticlesRead,
|
||||
practicesDone: practicesDone ?? this.practicesDone,
|
||||
averageScore: averageScore ?? this.averageScore,
|
||||
totalReadingTime: totalReadingTime ?? this.totalReadingTime,
|
||||
averageReadingSpeed: averageReadingSpeed ?? this.averageReadingSpeed,
|
||||
comprehensionAccuracy: comprehensionAccuracy ?? this.comprehensionAccuracy,
|
||||
vocabularyMastered: vocabularyMastered ?? this.vocabularyMastered,
|
||||
consecutiveDays: consecutiveDays ?? this.consecutiveDays,
|
||||
categoryStats: categoryStats ?? this.categoryStats,
|
||||
difficultyStats: difficultyStats ?? this.difficultyStats,
|
||||
dailyRecords: dailyRecords ?? this.dailyRecords,
|
||||
);
|
||||
}
|
||||
|
||||
String get readingTimeFormatted {
|
||||
final hours = totalReadingTime ~/ 60;
|
||||
final minutes = totalReadingTime % 60;
|
||||
if (hours > 0) {
|
||||
return '${hours}小时${minutes}分钟';
|
||||
}
|
||||
return '${minutes}分钟';
|
||||
}
|
||||
|
||||
String get averageScoreFormatted {
|
||||
return '${averageScore.toStringAsFixed(1)}分';
|
||||
}
|
||||
|
||||
String get comprehensionAccuracyFormatted {
|
||||
return '${comprehensionAccuracy.toStringAsFixed(1)}%';
|
||||
}
|
||||
|
||||
String get averageReadingSpeedFormatted {
|
||||
return '${averageReadingSpeed.toStringAsFixed(0)}词/分钟';
|
||||
}
|
||||
}
|
||||
|
||||
class DailyReadingRecord {
|
||||
final DateTime date;
|
||||
final int articlesRead;
|
||||
final int practicesDone;
|
||||
final int readingTime; // in minutes
|
||||
final double averageScore;
|
||||
final int vocabularyLearned;
|
||||
|
||||
const DailyReadingRecord({
|
||||
required this.date,
|
||||
required this.articlesRead,
|
||||
required this.practicesDone,
|
||||
required this.readingTime,
|
||||
required this.averageScore,
|
||||
required this.vocabularyLearned,
|
||||
});
|
||||
|
||||
factory DailyReadingRecord.fromJson(Map<String, dynamic> json) {
|
||||
final dateRaw = json['date'];
|
||||
final articlesRead = json['articlesRead'];
|
||||
final practicesDone = json['practicesDone'];
|
||||
final readingTime = json['readingTime'];
|
||||
final averageScore = json['averageScore'];
|
||||
final vocabularyLearned = json['vocabularyLearned'];
|
||||
|
||||
return DailyReadingRecord(
|
||||
date: dateRaw is String ? DateTime.parse(dateRaw) : DateTime.fromMillisecondsSinceEpoch(0),
|
||||
articlesRead: articlesRead is num ? articlesRead.toInt() : 0,
|
||||
practicesDone: practicesDone is num ? practicesDone.toInt() : 0,
|
||||
readingTime: readingTime is num ? readingTime.toInt() : 0,
|
||||
averageScore: averageScore is num ? averageScore.toDouble() : 0.0,
|
||||
vocabularyLearned: vocabularyLearned is num ? vocabularyLearned.toInt() : 0,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'date': date.toIso8601String(),
|
||||
'articlesRead': articlesRead,
|
||||
'practicesDone': practicesDone,
|
||||
'readingTime': readingTime,
|
||||
'averageScore': averageScore,
|
||||
'vocabularyLearned': vocabularyLearned,
|
||||
};
|
||||
}
|
||||
|
||||
DailyReadingRecord copyWith({
|
||||
DateTime? date,
|
||||
int? articlesRead,
|
||||
int? practicesDone,
|
||||
int? readingTime,
|
||||
double? averageScore,
|
||||
int? vocabularyLearned,
|
||||
}) {
|
||||
return DailyReadingRecord(
|
||||
date: date ?? this.date,
|
||||
articlesRead: articlesRead ?? this.articlesRead,
|
||||
practicesDone: practicesDone ?? this.practicesDone,
|
||||
readingTime: readingTime ?? this.readingTime,
|
||||
averageScore: averageScore ?? this.averageScore,
|
||||
vocabularyLearned: vocabularyLearned ?? this.vocabularyLearned,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ReadingProgress {
|
||||
final String category;
|
||||
final int totalArticles;
|
||||
final int completedArticles;
|
||||
final double averageScore;
|
||||
final String difficulty;
|
||||
|
||||
const ReadingProgress({
|
||||
required this.category,
|
||||
required this.totalArticles,
|
||||
required this.completedArticles,
|
||||
required this.averageScore,
|
||||
required this.difficulty,
|
||||
});
|
||||
|
||||
factory ReadingProgress.fromJson(Map<String, dynamic> json) {
|
||||
return ReadingProgress(
|
||||
category: json['category'] as String,
|
||||
totalArticles: json['totalArticles'] as int,
|
||||
completedArticles: json['completedArticles'] as int,
|
||||
averageScore: (json['averageScore'] as num).toDouble(),
|
||||
difficulty: json['difficulty'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'category': category,
|
||||
'totalArticles': totalArticles,
|
||||
'completedArticles': completedArticles,
|
||||
'averageScore': averageScore,
|
||||
'difficulty': difficulty,
|
||||
};
|
||||
}
|
||||
|
||||
double get progressPercentage {
|
||||
if (totalArticles == 0) return 0.0;
|
||||
return (completedArticles / totalArticles) * 100;
|
||||
}
|
||||
|
||||
String get progressText {
|
||||
return '$completedArticles/$totalArticles';
|
||||
}
|
||||
|
||||
String get categoryLabel {
|
||||
switch (category.toLowerCase()) {
|
||||
case 'cet4':
|
||||
return '四级阅读';
|
||||
case 'cet6':
|
||||
return '六级阅读';
|
||||
case 'toefl':
|
||||
return '托福阅读';
|
||||
case 'ielts':
|
||||
return '雅思阅读';
|
||||
case 'daily':
|
||||
return '日常阅读';
|
||||
case 'business':
|
||||
return '商务阅读';
|
||||
case 'academic':
|
||||
return '学术阅读';
|
||||
case 'news':
|
||||
return '新闻阅读';
|
||||
default:
|
||||
return category;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user