@readerseye2/cr_type 1.0.152 → 1.0.153
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,32 +1,8 @@
|
|
|
1
|
-
import type { EyeTrackerType } from '../gaze/eye-tracker.types';
|
|
2
1
|
/** 읽은 구간 (globalIndex 범위, inclusive) */
|
|
3
2
|
export interface ReadRange {
|
|
4
3
|
from: number;
|
|
5
4
|
to: number;
|
|
6
5
|
}
|
|
7
|
-
/** 캘리브레이션 종류 */
|
|
8
|
-
export type CalibrationType = "quick" | "full";
|
|
9
|
-
/** 섹션 읽기 중 발생한 캘리브레이션 기간 */
|
|
10
|
-
export interface CalibrationPeriod {
|
|
11
|
-
type: CalibrationType;
|
|
12
|
-
/** 사용한 시선추적 트래커 */
|
|
13
|
-
trackerType: EyeTrackerType;
|
|
14
|
-
/** 사용한 포인트 수 (quick: 3, full: 5) */
|
|
15
|
-
points: number;
|
|
16
|
-
startedAt: string;
|
|
17
|
-
endedAt: string;
|
|
18
|
-
durationMs: number;
|
|
19
|
-
/** 캘리브레이션 결과 품질 (0~1, 기기 제공) */
|
|
20
|
-
quality?: number;
|
|
21
|
-
/** 화면 가로 해상도 (px) */
|
|
22
|
-
screenWidth?: number;
|
|
23
|
-
/** 화면 세로 해상도 (px) */
|
|
24
|
-
screenHeight?: number;
|
|
25
|
-
/** 평균 오차 (px) */
|
|
26
|
-
accuracyPx?: number;
|
|
27
|
-
/** 평균 오차 (degree) */
|
|
28
|
-
accuracyDeg?: number;
|
|
29
|
-
}
|
|
30
6
|
/** 퀴즈 시도 결과 (개별 퀴즈, 서버 저장용) */
|
|
31
7
|
export interface QuizAttemptResult {
|
|
32
8
|
quizId: string;
|
|
@@ -83,6 +59,15 @@ export interface LookedUpWord {
|
|
|
83
59
|
translatedText?: string;
|
|
84
60
|
lookedUpAt: string;
|
|
85
61
|
}
|
|
62
|
+
/** 읽기 상태 비율 (세션 전체) */
|
|
63
|
+
export interface ReadingStateRatios {
|
|
64
|
+
reading: number;
|
|
65
|
+
scanning: number;
|
|
66
|
+
blink: number;
|
|
67
|
+
away: number;
|
|
68
|
+
lost: number;
|
|
69
|
+
etc: number;
|
|
70
|
+
}
|
|
86
71
|
export interface ReadingProgressReport {
|
|
87
72
|
bookIdx: number;
|
|
88
73
|
sectionId: string;
|
|
@@ -95,25 +80,14 @@ export interface ReadingProgressReport {
|
|
|
95
80
|
/**
|
|
96
81
|
* GI별 시선 dwell time (ms)
|
|
97
82
|
* - 키: globalIndex (string), 값: 해당 5초 윈도우 내 누적 dwell ms
|
|
98
|
-
* - 임계값 필터링 없이 전체 포함 (서버에서 threshold 적용)
|
|
99
83
|
* - 시선 추적 기기 없으면 빈 객체 {}
|
|
100
|
-
*
|
|
101
|
-
* 예: { "101": 300, "102": 450, "103": 67 }
|
|
102
84
|
*/
|
|
103
85
|
gazeDwellMap: Record<string, number>;
|
|
104
|
-
/** 이 구간 내 눈 깜빡임 횟수
|
|
86
|
+
/** 이 구간 내 눈 깜빡임 횟수 */
|
|
105
87
|
blinkCount: number;
|
|
106
|
-
/**
|
|
107
|
-
* 이 구간에서 시선이 텍스트([data-g]) 위에 있었던 시간 (ms)
|
|
108
|
-
* → 집중도(concentration) 계산용: gazeOnTextMs / durationMs * 100
|
|
109
|
-
* → 시선 추적 없으면 0
|
|
110
|
-
*/
|
|
88
|
+
/** 이 구간에서 시선이 텍스트([data-g]) 위에 있었던 시간 (ms) */
|
|
111
89
|
gazeOnTextMs: number;
|
|
112
|
-
/**
|
|
113
|
-
* 오디오 재생 중 시선이 현재 하이라이트 GI 근처에 있었던 시간 (ms)
|
|
114
|
-
* → 시선일치도(gazeAlignment) 계산용
|
|
115
|
-
* → 오디오 미재생 또는 시선추적 없으면 null
|
|
116
|
-
*/
|
|
90
|
+
/** 오디오 재생 중 시선이 현재 하이라이트 GI 근처에 있었던 시간 (ms) */
|
|
117
91
|
gazeAlignedMs: number | null;
|
|
118
92
|
/** 오디오 재생 중 시선추적이 활성이었던 총 시간 (ms) */
|
|
119
93
|
audioGazeActiveMs: number | null;
|
|
@@ -124,203 +98,92 @@ export interface ReadingProgressReport {
|
|
|
124
98
|
/** 소요 시간 ms */
|
|
125
99
|
durationMs: number;
|
|
126
100
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
bookIdx: number;
|
|
101
|
+
/** 읽기세션 내 1개 섹션 읽기 구간 스냅샷 */
|
|
102
|
+
export interface SegmentSnapshot {
|
|
130
103
|
sectionId: string;
|
|
131
104
|
sectionGIMax: number;
|
|
132
|
-
/** 섹션 단어 수 (SectionSummary.word_count, WPM 계산용) */
|
|
133
105
|
sectionWordCount: number;
|
|
106
|
+
startedAt: string;
|
|
107
|
+
endedAt: string;
|
|
108
|
+
durationMs: number;
|
|
134
109
|
exposedRanges: ReadRange[];
|
|
135
|
-
/** unique GI 수 (mergeReadRanges 후) */
|
|
136
|
-
exposedCount: number;
|
|
137
|
-
/** exposedCount / sectionGIMax */
|
|
138
110
|
exposedCoverage: number;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
/** 누적: 오디오 재생 중 시선추적이 활성이었던 시간 */
|
|
160
|
-
totalAudioGazeActiveMs: number;
|
|
161
|
-
/** 시선일치도: totalGazeAlignedMs / totalAudioGazeActiveMs (0~1, 데이터 없으면 null) */
|
|
162
|
-
gazeAlignmentScore: number | null;
|
|
111
|
+
validReadGIs: number;
|
|
112
|
+
rawReadGIs: number;
|
|
113
|
+
totalBlinks: number;
|
|
114
|
+
estimatedWpm: number | null;
|
|
115
|
+
}
|
|
116
|
+
/** 읽기세션 전체 기록 */
|
|
117
|
+
export interface ReadingSessionRecord {
|
|
118
|
+
testeeIdx: number;
|
|
119
|
+
readingSessionId: string;
|
|
120
|
+
bookIdx: number;
|
|
121
|
+
bookTitle?: string;
|
|
122
|
+
startedAt: string;
|
|
123
|
+
endedAt: string;
|
|
124
|
+
totalDurationMs: number;
|
|
125
|
+
segments: SegmentSnapshot[];
|
|
126
|
+
totalValidReadGIs: number;
|
|
127
|
+
/** 중복허용 읽은량 합산 */
|
|
128
|
+
totalRawReadGIs: number;
|
|
129
|
+
totalValidReadMs: number;
|
|
130
|
+
estimatedWpm: number | null;
|
|
163
131
|
totalBlinks: number;
|
|
164
|
-
|
|
132
|
+
blkPerMinute: number | null;
|
|
133
|
+
totalWordLookups: number;
|
|
165
134
|
lookedUpWords: LookedUpWord[];
|
|
166
|
-
/** 개별 퀴즈 결과 (quizId 기준 최신 1건만 유지, 재시도 시 덮어쓰기) */
|
|
167
135
|
quizResults: QuizAttemptResult[];
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
quizAccuracy: number | null;
|
|
174
|
-
/** 누적 읽기 점수 (알고리즘 산출, 읽기마다 추가) */
|
|
136
|
+
totalQuizScore: number;
|
|
137
|
+
totalQuizMaxScore: number;
|
|
138
|
+
readingStateRatios: ReadingStateRatios | null;
|
|
139
|
+
totalAudioPlayMs: number;
|
|
140
|
+
focusListeningScore: number | null;
|
|
175
141
|
readingScore: number;
|
|
176
|
-
calibrations: CalibrationPeriod[];
|
|
177
|
-
createdAt: string;
|
|
178
|
-
updatedAt: string;
|
|
179
142
|
}
|
|
180
|
-
|
|
181
|
-
|
|
143
|
+
/** 섹션별 머지된 진도 (전 세션 합산) */
|
|
144
|
+
export interface SectionMergedProgress {
|
|
145
|
+
sectionId: string;
|
|
146
|
+
sectionGIMax: number;
|
|
147
|
+
/** 전 세션 머지된 읽은 구간 [0~180, 190~200] */
|
|
148
|
+
mergedRanges: ReadRange[];
|
|
149
|
+
/** mergedRanges unique GI 수 / sectionGIMax */
|
|
150
|
+
coverage: number;
|
|
151
|
+
}
|
|
152
|
+
/** 책별 누적 진도 */
|
|
153
|
+
export interface BookProgress {
|
|
154
|
+
testeeIdx: number;
|
|
182
155
|
bookIdx: number;
|
|
183
|
-
|
|
156
|
+
sectionProgress: SectionMergedProgress[];
|
|
157
|
+
lastSectionId: string;
|
|
184
158
|
lastGlobalIndex: number;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
totalEyeReadCount: number;
|
|
189
|
-
totalEyeReadCoverage: number;
|
|
190
|
-
totalReadingScore: number;
|
|
191
|
-
/** 책 전체 평균 WPM (검증된 속도) */
|
|
192
|
-
avgReadingSpeedWPM: number | null;
|
|
193
|
-
/** 책 전체 집중도 (%) */
|
|
194
|
-
avgConcentrationPercent: number | null;
|
|
195
|
-
/** 책 전체 퀴즈 정답률 */
|
|
196
|
-
totalQuizAccuracy: number | null;
|
|
159
|
+
totalReadMs: number;
|
|
160
|
+
avgWpm: number | null;
|
|
161
|
+
totalReadingSessions: number;
|
|
197
162
|
createdAt: string;
|
|
198
163
|
updatedAt: string;
|
|
199
164
|
}
|
|
200
|
-
|
|
201
|
-
|
|
165
|
+
/** Redis 실시간 읽기 상태 */
|
|
166
|
+
export interface LiveReadingState {
|
|
167
|
+
testeeIdx: number;
|
|
168
|
+
readingSessionId: string;
|
|
202
169
|
bookIdx: number;
|
|
170
|
+
bookTitle: string;
|
|
203
171
|
sectionId: string;
|
|
204
|
-
|
|
205
|
-
exposedTo: number;
|
|
206
|
-
/** 전체 dwell map 그대로 보존 */
|
|
207
|
-
gazeDwellMap: Record<string, number>;
|
|
208
|
-
blinkCount: number;
|
|
209
|
-
/** 시선이 텍스트 위에 있었던 시간 */
|
|
210
|
-
gazeOnTextMs: number;
|
|
211
|
-
/** 오디오 재생 중 시선 일치 시간 */
|
|
212
|
-
gazeAlignedMs: number | null;
|
|
213
|
-
audioGazeActiveMs: number | null;
|
|
214
|
-
/** 찾아본 단어 */
|
|
215
|
-
lookedUpWords: LookedUpWord[];
|
|
216
|
-
/** 퀴즈 결과 */
|
|
217
|
-
quizResults: QuizAttemptResult[];
|
|
218
|
-
durationMs: number;
|
|
219
|
-
createdAt: string;
|
|
220
|
-
}
|
|
221
|
-
export interface ChildCalibrationLog {
|
|
222
|
-
childIdx: number;
|
|
223
|
-
/** 읽기 세션 중 발생 시 읽기 세션 ID */
|
|
224
|
-
readingSessionId?: string;
|
|
225
|
-
/** 읽기 중이었던 섹션 */
|
|
226
|
-
sectionId?: string;
|
|
227
|
-
type: CalibrationType;
|
|
228
|
-
/** 사용한 시선추적 트래커 */
|
|
229
|
-
trackerType: EyeTrackerType;
|
|
230
|
-
/** 사용한 포인트 수 */
|
|
231
|
-
points: number;
|
|
172
|
+
sectionTitle: string;
|
|
232
173
|
startedAt: string;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
quality?: number;
|
|
236
|
-
/** 화면 가로 해상도 (px) */
|
|
237
|
-
screenWidth?: number;
|
|
238
|
-
/** 화면 세로 해상도 (px) */
|
|
239
|
-
screenHeight?: number;
|
|
240
|
-
/** 평균 오차 (px) */
|
|
241
|
-
accuracyPx?: number;
|
|
242
|
-
/** 평균 오차 (degree) */
|
|
243
|
-
accuracyDeg?: number;
|
|
244
|
-
createdAt: string;
|
|
245
|
-
}
|
|
246
|
-
export interface SegmentReadingSummary {
|
|
247
|
-
sectionId: string;
|
|
248
|
-
bookIdx: number;
|
|
249
|
-
sectionGIMax: number;
|
|
250
|
-
sectionWordCount: number;
|
|
251
|
-
startedAt: string;
|
|
252
|
-
endedAt: string;
|
|
253
|
-
durationMs: number;
|
|
254
|
-
exposedFrom: number;
|
|
255
|
-
exposedTo: number;
|
|
256
|
-
exposedGICount: number;
|
|
257
|
-
eyeReadGICount: number;
|
|
258
|
-
rawExposedGICount: number;
|
|
259
|
-
rawEyeReadGICount: number;
|
|
260
|
-
/** eyeReadGICount / (durationMs / 60000) — WPM 근사 */
|
|
261
|
-
readingSpeedWPM: number | null;
|
|
262
|
-
/** exposedGICount / (durationMs / 60000) */
|
|
263
|
-
unverifiedReadingSpeedWPM: number;
|
|
264
|
-
/** eyeReadGICount / exposedGICount (시선 없으면 null) */
|
|
265
|
-
focusRatio: number | null;
|
|
266
|
-
/** 평균 dwell time (ms per GI) */
|
|
267
|
-
avgDwellMs: number | null;
|
|
268
|
-
/** 집중도: gazeOnTextMs / durationMs * 100 */
|
|
269
|
-
concentrationPercent: number | null;
|
|
270
|
-
/** 시선일치도 (0~1, 오디오+시선추적 시에만) */
|
|
271
|
-
gazeAlignmentScore: number | null;
|
|
174
|
+
elapsedMs: number;
|
|
175
|
+
wpm: number | null;
|
|
272
176
|
totalBlinks: number;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
readingSessionId: string;
|
|
284
|
-
startedAt: string;
|
|
285
|
-
endedAt: string;
|
|
286
|
-
totalDurationMs: number;
|
|
287
|
-
segments: SegmentReadingSummary[];
|
|
288
|
-
totalExposedGICount: number;
|
|
289
|
-
totalEyeReadGICount: number;
|
|
290
|
-
overallFocusRatio: number | null;
|
|
291
|
-
avgReadingSpeedWPM: number | null;
|
|
292
|
-
avgUnverifiedReadingSpeedWPM: number;
|
|
293
|
-
overallConcentrationPercent: number | null;
|
|
294
|
-
overallGazeAlignmentScore: number | null;
|
|
295
|
-
totalBlinks: number;
|
|
296
|
-
avgBlinksPerMinute: number;
|
|
297
|
-
totalLookedUpWords: number;
|
|
298
|
-
totalQuizScore: number;
|
|
299
|
-
totalQuizMaxScore: number;
|
|
300
|
-
overallQuizAccuracy: number | null;
|
|
301
|
-
/** 이 세션에서 획득한 읽기 점수 */
|
|
302
|
-
sessionReadingScore: number;
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* 읽기 점수 산출 입력값
|
|
306
|
-
* - 각 메트릭 가중치를 적용하여 종합 점수 산출
|
|
307
|
-
* - 점수는 누적 (읽을수록 올라감)
|
|
308
|
-
*/
|
|
309
|
-
export interface ReadingScoreInput {
|
|
310
|
-
/** 진도 (중첩비허용 coverage, 0~1) */
|
|
311
|
-
exposedCoverage: number;
|
|
312
|
-
/** 검증된 진도 (eyeRead coverage, 0~1) */
|
|
313
|
-
eyeReadCoverage: number;
|
|
314
|
-
/** 집중도 (%, 0~100) */
|
|
315
|
-
concentrationPercent: number | null;
|
|
316
|
-
/** 시선일치도 (0~1) */
|
|
317
|
-
gazeAlignmentScore: number | null;
|
|
318
|
-
/** 퀴즈 정답률 (0~1) */
|
|
319
|
-
quizAccuracy: number | null;
|
|
320
|
-
/** 읽기 시간 (ms) */
|
|
321
|
-
durationMs: number;
|
|
322
|
-
/** 찾아본 단어 수 */
|
|
323
|
-
lookedUpWordCount: number;
|
|
177
|
+
blkPerMinute: number | null;
|
|
178
|
+
/** 중복허용 읽은량 */
|
|
179
|
+
rawReadGIs: number;
|
|
180
|
+
totalWordLookups: number;
|
|
181
|
+
readingStateRatios: ReadingStateRatios | null;
|
|
182
|
+
isAudioPlaying: boolean;
|
|
183
|
+
focusListeningScore: number | null;
|
|
184
|
+
focusListeningDurationMs: number;
|
|
185
|
+
currentSectionCoverage: number;
|
|
186
|
+
currentSectionGIMax: number;
|
|
324
187
|
}
|
|
325
188
|
/**
|
|
326
189
|
* 현재 섹션 읽기 중 클라이언트에서 실시간 누적하는 상태
|
|
@@ -356,83 +219,7 @@ export interface ReadingAccumulatedState {
|
|
|
356
219
|
estimatedWpm: number | null;
|
|
357
220
|
estimatedReadingScore: number;
|
|
358
221
|
}
|
|
359
|
-
/**
|
|
360
|
-
* 읽기 세션 전체를 아우르는 요약 — 세션 종료 시 localStorage 저장 + 추후 서버 전송
|
|
361
|
-
* - 섹션 전환마다 accumulated 병합하여 누적
|
|
362
|
-
* - accumulated는 섹션 단위 리셋, sessionSummary는 세션 동안 유지
|
|
363
|
-
*/
|
|
364
|
-
export interface ReadingSessionSummary {
|
|
365
|
-
/** 세션 전체 GI progress (0~1, 전 섹션 합산) */
|
|
366
|
-
totalExposedCoverage: number;
|
|
367
|
-
/** 속도 필터 통과한 총 GI 수 */
|
|
368
|
-
totalValidReadGIs: number;
|
|
369
|
-
/** 유효 읽기 시간 (ms) */
|
|
370
|
-
totalValidReadMs: number;
|
|
371
|
-
/** 추정 WPM (800 초과 시 null) */
|
|
372
|
-
estimatedWpm: number | null;
|
|
373
|
-
totalBlinks: number;
|
|
374
|
-
/** 분당 깜빡임 (totalValidReadMs 기준) */
|
|
375
|
-
blkPerMinute: number | null;
|
|
376
|
-
totalWordLookups: number;
|
|
377
|
-
lookedUpWords: LookedUpWord[];
|
|
378
|
-
totalQuizScore: number;
|
|
379
|
-
totalQuizMaxScore: number;
|
|
380
|
-
quizResults: QuizAttemptResult[];
|
|
381
|
-
/** 집중듣기 점수: 오디오+시선 GI LINEBREAK 패턴 기반 (placeholder) */
|
|
382
|
-
readingPlayFocusGrade: number | null;
|
|
383
|
-
}
|
|
384
|
-
export interface ChildDailySummary {
|
|
385
|
-
childIdx: number;
|
|
386
|
-
/** KST 기준 날짜 "YYYY-MM-DD" */
|
|
387
|
-
date: string;
|
|
388
|
-
totalReadMs: number;
|
|
389
|
-
sessionCount: number;
|
|
390
|
-
/** 오늘 읽은 bookIdx 목록 (unique) */
|
|
391
|
-
booksRead: number[];
|
|
392
|
-
/** 오늘 노출 GI 합 (세션별 누적) */
|
|
393
|
-
totalExposedGICount: number;
|
|
394
|
-
/** 오늘 시선 확인 GI 합 (세션별 누적) */
|
|
395
|
-
totalEyeReadGICount: number;
|
|
396
|
-
avgConcentrationPercent: number | null;
|
|
397
|
-
avgFocusRatio: number | null;
|
|
398
|
-
avgReadingSpeedWPM: number | null;
|
|
399
|
-
totalQuizScore: number;
|
|
400
|
-
totalQuizMaxScore: number;
|
|
401
|
-
totalBlinks: number;
|
|
402
|
-
totalLookedUpWords: number;
|
|
403
|
-
/** 집중도 가중합 (concentrationPercent × durationMs 누적) */
|
|
404
|
-
_weightedConcentrationMs: number;
|
|
405
|
-
/** 집중도 분모 (concentration 데이터가 있는 세션의 durationMs 누적) */
|
|
406
|
-
_concentrationDurationMs: number;
|
|
407
|
-
/** 속도 가중합 (speedWPM × durationMs 누적) */
|
|
408
|
-
_weightedSpeedMs: number;
|
|
409
|
-
/** 속도 분모 (speed 데이터가 있는 세션의 durationMs 누적) */
|
|
410
|
-
_speedDurationMs: number;
|
|
411
|
-
/** focusRatio 가중합 */
|
|
412
|
-
_weightedFocusRatioMs: number;
|
|
413
|
-
/** focusRatio 분모 */
|
|
414
|
-
_focusRatioDurationMs: number;
|
|
415
|
-
lastReadingSessionId: string;
|
|
416
|
-
createdAt: string;
|
|
417
|
-
updatedAt: string;
|
|
418
|
-
}
|
|
419
222
|
/** GET /api/reading-progress/:bookIdx */
|
|
420
|
-
export interface
|
|
421
|
-
|
|
422
|
-
sections: ChildSectionProgress[];
|
|
423
|
-
}
|
|
424
|
-
/** 일별 읽기 통계 (aggregation 결과) */
|
|
425
|
-
export interface DailyReadingStat {
|
|
426
|
-
date: string;
|
|
427
|
-
totalMs: number;
|
|
428
|
-
books: number[];
|
|
429
|
-
/** 그 날 획득한 읽기 점수 */
|
|
430
|
-
readingScore: number;
|
|
431
|
-
}
|
|
432
|
-
/** GET /api/reading-progress/daily-stats */
|
|
433
|
-
export interface DailyReadingStatsResponse {
|
|
434
|
-
childIdx: number;
|
|
435
|
-
from: string;
|
|
436
|
-
to: string;
|
|
437
|
-
stats: DailyReadingStat[];
|
|
223
|
+
export interface BookProgressResponse {
|
|
224
|
+
progress: BookProgress | null;
|
|
438
225
|
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// src/book/child-reading-progress.type.ts
|
|
3
|
-
//
|
|
3
|
+
// 읽기 진행 데이터 v2 — MongoDB 2컬렉션 + Redis 실시간 + WS/REST 타입
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// 진도(coverage, 중첩비허용) 읽은양(raw, 중첩허용) 읽기속도(WPM)
|
|
8
|
-
// 시선일치도(gazeAlignment, 오디오재생+시선추적시)
|
|
9
|
-
// 집중도(concentration, gazeOnText/total %)
|
|
10
|
-
// 찾아본단어(lookedUpWords) 퀴즈점수/정답률
|
|
11
|
-
// 미검증 읽은양/속도(exposedOnly) 종합 읽기점수(readingScore, 누적)
|
|
5
|
+
// MongoDB: reading_sessions (세션당 1개) + book_progress (책별 1개)
|
|
6
|
+
// Redis: reading:live:{testeeIdx} (실시간 부모 모니터링)
|
|
12
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|