11import 'dart:convert' ;
2- import 'package:arabic_learning/package_replacement/storage.dart' ;
2+
3+ import 'package:arabic_learning/funcs/utili.dart' ;
34import 'package:fsrs/fsrs.dart' ;
5+ import 'package:logging/logging.dart' ;
6+
7+ import 'package:arabic_learning/package_replacement/storage.dart' ;
48
59class FSRS {
610 List <Card > cards = [];
711 List <ReviewLog > reviewLogs = [];
812 late Scheduler scheduler;
9- late SharedPreferences prefs;
10- late Rater rater;
13+ late final SharedPreferences prefs;
1114 late Map <String , dynamic > settingData;
12-
15+ late final Logger logger;
1316 // index != cardId; cardId = wordId = the index of word in global.wordData[words]
1417
15- Future <bool > init () async {
16- prefs = await SharedPreferences .getInstance ();
18+ bool init ({required SharedPreferences outerPrefs}) {
19+ prefs = outerPrefs;
20+ logger = Logger ('FSRS' );
21+ logger.fine ("构建FSRS模块" );
22+ settingData = {
23+ 'enabled' : false ,
24+ 'scheduler' : {},
25+ 'cards' : [],
26+ 'reviewLog' : [],
27+ 'rater' : {
28+ "desiredRetention" : 0.9 ,
29+ "easyDuration" : 3000 ,
30+ "goodDuration" : 6000
31+ },
32+ };
33+
1734 if (! prefs.containsKey ("fsrsData" )) {
18- settingData = {
19- 'enabled' : false ,
20- 'scheduler' : {},
21- 'cards' : [],
22- 'reviewLog' : [],
23- 'rater' : {'scheme' : 0 },
24- };
35+ logger.info ("未发现FSRS配置,加载默认配置" );
2536 prefs.setString ("fsrsData" , jsonEncode (settingData));
2637 return false ;
38+ } else {
39+ settingData = deepMerge (settingData, jsonDecode (prefs.getString ("fsrsData" )! ));
2740 }
28- settingData = jsonDecode (prefs. getString ( "fsrsData" ) ! ) as Map < String , dynamic >;
41+
2942 if (isEnabled ()){
3043 scheduler = Scheduler .fromMap (settingData['scheduler' ]);
3144 for (int i = 0 ; i < settingData['cards' ].length; i++ ) {
3245 cards.add (Card .fromMap (settingData['cards' ][i]));
3346 reviewLogs.add (ReviewLog .fromMap (settingData['reviewLog' ][i]));
3447 }
35- rater = Rater (settingData[ 'rater' ][ 'scheme' ] );
48+ logger. info ( "FSRS配置加载完成" );
3649 return true ;
3750 }
51+ logger.info ("FSRS未启用" );
3852 return false ;
3953 }
4054
4155 void save () async {
56+ logger.info ("正在保存FSRS配置" );
4257 settingData['scheduler' ] = scheduler.toMap ();
4358 List cardsCache = [];
4459 List logCache = [];
@@ -55,33 +70,33 @@ class FSRS {
5570 return settingData['enabled' ];
5671 }
5772
58- Future <void > createScheduler (int scheme) async {
59- await init ();
60- List <double > desiredRetention = [0.85 , 0.9 , 0.95 , 0.95 , 0.99 ];
61- scheduler = Scheduler (desiredRetention: desiredRetention[scheme]);
62- settingData['rater' ]['scheme' ] = scheme;
73+ void createScheduler ({required SharedPreferences prefs}) {
74+ logger.info ("初始化scheduler,选择相关配置 ${settingData ["rater" ].toString ()}" );
75+ scheduler = Scheduler (desiredRetention: settingData["rater" ]["desiredRetention" ]);
6376 settingData['enabled' ] = true ;
6477 settingData['scheduler' ] = scheduler.toMap ();
65- rater = Rater (scheme);
6678 save ();
6779 }
6880
6981 int willDueIn (int index) {
70- return cards[index].due.difference (DateTime .now ()).inDays;
82+ return cards[index].due.toLocal (). difference (DateTime .now ()).inDays;
7183 }
7284
7385 void reviewCard (int wordId, int duration, bool isCorrect) {
86+ logger.fine ("记录复习卡片: Id: $wordId ; duration: $duration ; isCorrect: $isCorrect " );
7487 int index = cards.indexWhere ((Card card) => card.cardId == wordId); // 避免有时候cardId != wordId
75- final (: card, : reviewLog) = scheduler.reviewCard (cards[index], rater.calculate (duration, isCorrect));
88+ logger.fine ("定位复习卡片地址: $index , 目前阶段: ${cards [index ].step }, 难度: ${cards [index ].difficulty }, 稳定: ${cards [index ].stability }, 过期时间(+8): ${cards [index ].due .toLocal ()}" );
89+ final (: card, : reviewLog) = scheduler.reviewCard (cards[index], calculate (duration, isCorrect), reviewDateTime: DateTime .now ().toUtc (), reviewDuration: duration);
7690 cards[index] = card;
7791 reviewLogs[index] = reviewLog;
92+ logger.fine ("卡片 $index 复习后: 目前阶段: ${cards [index ].step }, 难度: ${cards [index ].difficulty }, 稳定: ${cards [index ].stability }, 过期时间(+8): ${cards [index ].due .toLocal ()}" );
7893 save ();
7994 }
8095
8196 int getWillDueCount () {
8297 int dueCards = 0 ;
8398 for (int i = 0 ; i < cards.length; i++ ) {
84- if (willDueIn (i) == 0 ) {
99+ if (willDueIn (i) < 1 ) {
85100 dueCards++ ;
86101 }
87102 }
@@ -91,7 +106,7 @@ class FSRS {
91106 int getLeastDueCard () {
92107 int leastDueIndex = 0 ;
93108 for (int i = 1 ; i < cards.length; i++ ) {
94- if (cards[i].due.isBefore (cards[leastDueIndex].due) && cards[i].due.difference (DateTime .now ()) < Duration (days: 1 )) {
109+ if (cards[i].due.toLocal (). isBefore (cards[leastDueIndex].due. toLocal ()) && cards[i].due. toLocal () .difference (DateTime .now ()) < Duration (days: 1 )) {
95110 leastDueIndex = i;
96111 }
97112 }
@@ -112,31 +127,22 @@ class FSRS {
112127 reviewLogs.add (ReviewLog (cardId: wordId, rating: Rating .good, reviewDateTime: DateTime .now ()));
113128 save ();
114129 }
115- }
116-
117- class Rater {
118- Rating get easy => Rating .easy;
119- Rating get good => Rating .good;
120- Rating get hard => Rating .hard;
121- Rating get forget => Rating .again;
122-
123- late int scheme;
124-
125- Rater (this .scheme);
126-
127- static const List <List <int >> _difficultyScheme = [
128- [3000 , 8000 ], // Easy
129- [2000 , 6000 ], // Fine
130- [1500 , 4000 ], // OK~
131- [1000 , 2000 ], // Emm...
132- [1000 , 1500 ], // Impossible
133- ];
134130
135131 Rating calculate (int duration, bool isCorrect) {
136132 // duration in milliseconds
137- if (! isCorrect) return Rating .again;
138- if (duration < _difficultyScheme[scheme][0 ]) return Rating .easy;
139- if (duration < _difficultyScheme[scheme][1 ]) return Rating .good;
133+ if (! isCorrect) {
134+ logger.fine ("计算得分: again" );
135+ return Rating .again;
136+ }
137+ if (duration < settingData['rater' ]['easyDuration' ]) {
138+ logger.fine ("计算得分: easy" );
139+ return Rating .easy;
140+ }
141+ if (duration < settingData['rater' ]['goodDuration' ]) {
142+ logger.fine ("计算得分: good" );
143+ return Rating .good;
144+ }
145+ logger.fine ("计算得分: hard" );
140146 return Rating .hard;
141147 }
142148}
0 commit comments