|
@@ -2,7 +2,6 @@
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
namespace App\Services;
|
|
|
|
|
|
|
|
-use App\Models\StudentExercise;
|
|
|
|
|
use App\Models\MistakeRecord;
|
|
use App\Models\MistakeRecord;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Log;
|
|
@@ -31,12 +30,20 @@ class StudentAnswerAnalysisService
|
|
|
$answers = $data['answers'];
|
|
$answers = $data['answers'];
|
|
|
$correctCount = 0;
|
|
$correctCount = 0;
|
|
|
$wrongCount = 0;
|
|
$wrongCount = 0;
|
|
|
|
|
+ $missingCount = 0;
|
|
|
$totalScore = 0;
|
|
$totalScore = 0;
|
|
|
$obtainedScore = 0;
|
|
$obtainedScore = 0;
|
|
|
|
|
|
|
|
foreach ($answers as $answer) {
|
|
foreach ($answers as $answer) {
|
|
|
$score = (float) ($answer['score'] ?? 0);
|
|
$score = (float) ($answer['score'] ?? 0);
|
|
|
$maxScore = (float) ($answer['max_score'] ?? $score);
|
|
$maxScore = (float) ($answer['max_score'] ?? $score);
|
|
|
|
|
+
|
|
|
|
|
+ // 缺题不计入对错统计
|
|
|
|
|
+ if ($answer['is_missing'] ?? false) {
|
|
|
|
|
+ $missingCount++;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
$totalScore += $maxScore;
|
|
$totalScore += $maxScore;
|
|
|
$obtainedScore += $score;
|
|
$obtainedScore += $score;
|
|
|
|
|
|
|
@@ -51,32 +58,18 @@ class StudentAnswerAnalysisService
|
|
|
? round($correctCount / ($correctCount + $wrongCount), 4)
|
|
? round($correctCount / ($correctCount + $wrongCount), 4)
|
|
|
: 0;
|
|
: 0;
|
|
|
|
|
|
|
|
- // 保存到 student_exercises 表
|
|
|
|
|
- foreach ($answers as $answer) {
|
|
|
|
|
- StudentExercise::create([
|
|
|
|
|
- 'student_id' => $data['student_id'],
|
|
|
|
|
- 'question_id' => $answer['question_id'],
|
|
|
|
|
- 'question_content' => json_encode([
|
|
|
|
|
- 'question_id' => $answer['question_id'],
|
|
|
|
|
- 'question_number' => $answer['question_number'] ?? null,
|
|
|
|
|
- 'paper_id' => $data['paper_id'],
|
|
|
|
|
- ]),
|
|
|
|
|
- 'student_answer' => $answer['student_answer'] ?? '',
|
|
|
|
|
- 'correct_answer' => $answer['correct_answer'] ?? '',
|
|
|
|
|
- 'is_correct' => $answer['is_correct'],
|
|
|
|
|
- 'submission_status' => 'completed',
|
|
|
|
|
- 'kp_code' => $answer['knowledge_point'] ?? null,
|
|
|
|
|
- 'difficulty_level' => 0.5, // 默认难度
|
|
|
|
|
- 'time_spent_seconds' => 0, // 默认耗时
|
|
|
|
|
- 'created_at' => $data['answer_time'] ?? now(),
|
|
|
|
|
- 'updated_at' => now(),
|
|
|
|
|
- ]);
|
|
|
|
|
|
|
+ // 只返回统计信息,不进行数据库操作(避免阻塞)
|
|
|
|
|
+ // 错题记录将在后台任务中异步处理
|
|
|
|
|
|
|
|
- // 保存错题记录
|
|
|
|
|
- if (!$answer['is_correct']) {
|
|
|
|
|
- $this->saveMistakeRecord($data, $answer);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Log::info('作答记录已保存', [
|
|
|
|
|
+ 'record_id' => $recordId,
|
|
|
|
|
+ 'paper_id' => $data['paper_id'],
|
|
|
|
|
+ 'student_id' => $data['student_id'],
|
|
|
|
|
+ 'total_questions' => count($answers),
|
|
|
|
|
+ 'correct_count' => $correctCount,
|
|
|
|
|
+ 'wrong_count' => $wrongCount,
|
|
|
|
|
+ 'accuracy_rate' => $accuracyRate,
|
|
|
|
|
+ ]);
|
|
|
|
|
|
|
|
return [
|
|
return [
|
|
|
'record_id' => $recordId,
|
|
'record_id' => $recordId,
|
|
@@ -87,12 +80,18 @@ class StudentAnswerAnalysisService
|
|
|
'accuracy_rate' => $accuracyRate,
|
|
'accuracy_rate' => $accuracyRate,
|
|
|
'correct_count' => $correctCount,
|
|
'correct_count' => $correctCount,
|
|
|
'wrong_count' => $wrongCount,
|
|
'wrong_count' => $wrongCount,
|
|
|
|
|
+ 'missing_count' => $missingCount,
|
|
|
'total_questions' => count($answers),
|
|
'total_questions' => count($answers),
|
|
|
];
|
|
];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 保存错题记录
|
|
* 保存错题记录
|
|
|
|
|
+ * 优化功能:
|
|
|
|
|
+ * 1. 相同题目不重复出现在错题本中
|
|
|
|
|
+ * 2. 记录错误次数和作答次数
|
|
|
|
|
+ * 3. 增加错误类型分析
|
|
|
|
|
+ * 4. 记录知识点掌握度变化
|
|
|
*/
|
|
*/
|
|
|
private function saveMistakeRecord(array $data, array $answer): void
|
|
private function saveMistakeRecord(array $data, array $answer): void
|
|
|
{
|
|
{
|
|
@@ -103,42 +102,94 @@ class StudentAnswerAnalysisService
|
|
|
->first();
|
|
->first();
|
|
|
|
|
|
|
|
if ($existing) {
|
|
if ($existing) {
|
|
|
- // 更新现有记录
|
|
|
|
|
|
|
+ // 更新现有记录(不重复创建)
|
|
|
|
|
+ $oldReviewCount = $existing->review_count;
|
|
|
|
|
+
|
|
|
|
|
+ // 递增错误次数和作答次数
|
|
|
$existing->increment('review_count');
|
|
$existing->increment('review_count');
|
|
|
|
|
+
|
|
|
|
|
+ // 记录每次错误的时间戳
|
|
|
|
|
+ $errorHistory = json_decode($existing->remark ?? '[]', true) ?: [];
|
|
|
|
|
+ $errorHistory[] = [
|
|
|
|
|
+ 'timestamp' => now()->toISOString(),
|
|
|
|
|
+ 'paper_id' => $data['paper_id'],
|
|
|
|
|
+ 'student_answer' => $answer['student_answer'] ?? '',
|
|
|
|
|
+ 'correct_answer' => $answer['correct_answer'] ?? '',
|
|
|
|
|
+ 'error_type' => $this->guessErrorType($answer),
|
|
|
|
|
+ 'is_missing' => $answer['is_missing'] ?? false,
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ // 更新记录
|
|
|
$existing->update([
|
|
$existing->update([
|
|
|
'student_answer' => $answer['student_answer'] ?? '',
|
|
'student_answer' => $answer['student_answer'] ?? '',
|
|
|
'correct_answer' => $answer['correct_answer'] ?? '',
|
|
'correct_answer' => $answer['correct_answer'] ?? '',
|
|
|
|
|
+ 'knowledge_point' => $answer['knowledge_point'] ?? $existing->knowledge_point,
|
|
|
|
|
+ 'error_type' => $this->guessErrorType($answer),
|
|
|
|
|
+ 'remark' => json_encode($errorHistory),
|
|
|
'updated_at' => now(),
|
|
'updated_at' => now(),
|
|
|
|
|
+ // 如果是缺题,更新为待复习状态
|
|
|
|
|
+ 'review_status' => ($answer['is_missing'] ?? false)
|
|
|
|
|
+ ? MistakeRecord::REVIEW_STATUS_PENDING
|
|
|
|
|
+ : $existing->review_status,
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ Log::info('错题记录已更新', [
|
|
|
|
|
+ 'student_id' => $data['student_id'],
|
|
|
|
|
+ 'question_id' => $answer['question_id'],
|
|
|
|
|
+ 'old_review_count' => $oldReviewCount,
|
|
|
|
|
+ 'new_review_count' => $existing->review_count,
|
|
|
|
|
+ 'total_errors' => count($errorHistory),
|
|
|
]);
|
|
]);
|
|
|
} else {
|
|
} else {
|
|
|
// 创建新记录
|
|
// 创建新记录
|
|
|
- MistakeRecord::create([
|
|
|
|
|
|
|
+ $errorHistory = [[
|
|
|
|
|
+ 'timestamp' => now()->toISOString(),
|
|
|
|
|
+ 'paper_id' => $data['paper_id'],
|
|
|
|
|
+ 'student_answer' => $answer['student_answer'] ?? '',
|
|
|
|
|
+ 'correct_answer' => $answer['correct_answer'] ?? '',
|
|
|
|
|
+ 'error_type' => $this->guessErrorType($answer),
|
|
|
|
|
+ 'is_missing' => $answer['is_missing'] ?? false,
|
|
|
|
|
+ ]];
|
|
|
|
|
+
|
|
|
|
|
+ $mistakeRecord = MistakeRecord::create([
|
|
|
'student_id' => $data['student_id'],
|
|
'student_id' => $data['student_id'],
|
|
|
'question_id' => $answer['question_id'],
|
|
'question_id' => $answer['question_id'],
|
|
|
|
|
+ 'paper_id' => $data['paper_id'] ?? null,
|
|
|
'source' => MistakeRecord::SOURCE_EXAM,
|
|
'source' => MistakeRecord::SOURCE_EXAM,
|
|
|
'question_text' => json_encode([
|
|
'question_text' => json_encode([
|
|
|
'question_number' => $answer['question_number'] ?? null,
|
|
'question_number' => $answer['question_number'] ?? null,
|
|
|
- 'paper_id' => $data['paper_id'],
|
|
|
|
|
'question_type' => $answer['question_type'] ?? null,
|
|
'question_type' => $answer['question_type'] ?? null,
|
|
|
|
|
+ 'is_missing' => $answer['is_missing'] ?? false,
|
|
|
]),
|
|
]),
|
|
|
'student_answer' => $answer['student_answer'] ?? '',
|
|
'student_answer' => $answer['student_answer'] ?? '',
|
|
|
'correct_answer' => $answer['correct_answer'] ?? '',
|
|
'correct_answer' => $answer['correct_answer'] ?? '',
|
|
|
'knowledge_point' => $answer['knowledge_point'] ?? null,
|
|
'knowledge_point' => $answer['knowledge_point'] ?? null,
|
|
|
'error_type' => $this->guessErrorType($answer),
|
|
'error_type' => $this->guessErrorType($answer),
|
|
|
- 'review_status' => MistakeRecord::REVIEW_STATUS_PENDING,
|
|
|
|
|
- 'review_count' => 0,
|
|
|
|
|
|
|
+ 'review_status' => ($answer['is_missing'] ?? false)
|
|
|
|
|
+ ? MistakeRecord::REVIEW_STATUS_PENDING
|
|
|
|
|
+ : MistakeRecord::REVIEW_STATUS_PENDING,
|
|
|
|
|
+ 'review_count' => 1, // 第一次错误
|
|
|
'force_review' => false,
|
|
'force_review' => false,
|
|
|
'is_favorite' => false,
|
|
'is_favorite' => false,
|
|
|
'in_retry_list' => false,
|
|
'in_retry_list' => false,
|
|
|
'difficulty' => 0.5,
|
|
'difficulty' => 0.5,
|
|
|
'mastery_level' => 0.0,
|
|
'mastery_level' => 0.0,
|
|
|
|
|
+ 'remark' => json_encode($errorHistory),
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ Log::info('新错题记录已创建', [
|
|
|
|
|
+ 'student_id' => $data['student_id'],
|
|
|
|
|
+ 'question_id' => $answer['question_id'],
|
|
|
|
|
+ 'mistake_record_id' => $mistakeRecord->id,
|
|
|
|
|
+ 'error_type' => $mistakeRecord->error_type,
|
|
|
]);
|
|
]);
|
|
|
}
|
|
}
|
|
|
} catch (\Exception $e) {
|
|
} catch (\Exception $e) {
|
|
|
- Log::warning('保存错题记录失败', [
|
|
|
|
|
|
|
+ Log::error('保存错题记录失败', [
|
|
|
'student_id' => $data['student_id'],
|
|
'student_id' => $data['student_id'],
|
|
|
'question_id' => $answer['question_id'],
|
|
'question_id' => $answer['question_id'],
|
|
|
'error' => $e->getMessage(),
|
|
'error' => $e->getMessage(),
|
|
|
|
|
+ 'trace' => $e->getTraceAsString(),
|
|
|
]);
|
|
]);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -171,52 +222,15 @@ class StudentAnswerAnalysisService
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 保存分析结果
|
|
* 保存分析结果
|
|
|
|
|
+ * 简化版:只保存错题记录,不依赖其他表
|
|
|
*/
|
|
*/
|
|
|
public function saveAnalysisResults(array $answerRecord, array $analysisData, array $questionAnalyses): void
|
|
public function saveAnalysisResults(array $answerRecord, array $analysisData, array $questionAnalyses): void
|
|
|
{
|
|
{
|
|
|
try {
|
|
try {
|
|
|
- // 生成分析ID
|
|
|
|
|
- $analysisId = 'analysis_' . Str::uuid()->toString();
|
|
|
|
|
-
|
|
|
|
|
- // 保存分析记录到 PostgreSQL
|
|
|
|
|
- DB::connection('pgsql')->table('answer_analysis_records')->insert([
|
|
|
|
|
- 'analysis_id' => $analysisId,
|
|
|
|
|
- 'exam_id' => $answerRecord['paper_id'],
|
|
|
|
|
- 'student_id' => $answerRecord['student_id'],
|
|
|
|
|
- 'ocr_record_id' => 0, // 如果是系统试卷,没有OCR记录
|
|
|
|
|
- 'status' => 'completed',
|
|
|
|
|
- 'analysis_results' => json_encode($analysisData),
|
|
|
|
|
- 'completed_at' => now(),
|
|
|
|
|
- 'created_at' => now(),
|
|
|
|
|
- 'updated_at' => now(),
|
|
|
|
|
- ]);
|
|
|
|
|
-
|
|
|
|
|
- // 获取分析记录的ID
|
|
|
|
|
- $analysisRecordId = DB::connection('pgsql')
|
|
|
|
|
- ->table('answer_analysis_records')
|
|
|
|
|
- ->where('analysis_id', $analysisId)
|
|
|
|
|
- ->value('id');
|
|
|
|
|
-
|
|
|
|
|
- // 保存每道题的分析结果
|
|
|
|
|
|
|
+ // 只更新错题记录的掌握度(如果题目有错误)
|
|
|
foreach ($questionAnalyses as $questionAnalysis) {
|
|
foreach ($questionAnalyses as $questionAnalysis) {
|
|
|
- DB::connection('pgsql')->table('question_analysis_results')->insert([
|
|
|
|
|
- 'analysis_record_id' => $analysisRecordId,
|
|
|
|
|
- 'question_id' => $questionAnalysis['question_id'],
|
|
|
|
|
- 'question_number' => $questionAnalysis['question_number'] ?? null,
|
|
|
|
|
- 'kp_code' => $questionAnalysis['kp_code'] ?? null,
|
|
|
|
|
- 'student_answer' => $questionAnalysis['student_answer'] ?? '',
|
|
|
|
|
- 'correct_answer' => $questionAnalysis['correct_answer'] ?? '',
|
|
|
|
|
- 'is_correct' => $questionAnalysis['is_correct'] ?? false,
|
|
|
|
|
- 'score_obtained' => $questionAnalysis['score_obtained'] ?? 0,
|
|
|
|
|
- 'max_score' => $questionAnalysis['max_score'] ?? 0,
|
|
|
|
|
- 'ai_analysis' => $questionAnalysis['ai_analysis'] ?? null,
|
|
|
|
|
- 'learning_suggestions' => json_encode($questionAnalysis['suggestions'] ?? []),
|
|
|
|
|
- 'created_at' => now(),
|
|
|
|
|
- 'updated_at' => now(),
|
|
|
|
|
- ]);
|
|
|
|
|
-
|
|
|
|
|
- // 更新掌握度
|
|
|
|
|
- if (!empty($questionAnalysis['kp_code'])) {
|
|
|
|
|
|
|
+ // 更新掌握度(如果题目有知识点且答错了)
|
|
|
|
|
+ if (!empty($questionAnalysis['kp_code']) && !($questionAnalysis['is_correct'] ?? true)) {
|
|
|
$this->updateMasteryForQuestion(
|
|
$this->updateMasteryForQuestion(
|
|
|
$answerRecord['student_id'],
|
|
$answerRecord['student_id'],
|
|
|
$questionAnalysis['kp_code'],
|
|
$questionAnalysis['kp_code'],
|
|
@@ -229,7 +243,6 @@ class StudentAnswerAnalysisService
|
|
|
Log::info('分析结果已保存', [
|
|
Log::info('分析结果已保存', [
|
|
|
'student_id' => $answerRecord['student_id'],
|
|
'student_id' => $answerRecord['student_id'],
|
|
|
'paper_id' => $answerRecord['paper_id'],
|
|
'paper_id' => $answerRecord['paper_id'],
|
|
|
- 'analysis_id' => $analysisId,
|
|
|
|
|
'question_count' => count($questionAnalyses),
|
|
'question_count' => count($questionAnalyses),
|
|
|
]);
|
|
]);
|
|
|
|
|
|
|
@@ -248,37 +261,25 @@ class StudentAnswerAnalysisService
|
|
|
private function updateMasteryForQuestion(string $studentId, string $kpCode, bool $isCorrect, float $difficulty): void
|
|
private function updateMasteryForQuestion(string $studentId, string $kpCode, bool $isCorrect, float $difficulty): void
|
|
|
{
|
|
{
|
|
|
try {
|
|
try {
|
|
|
- // 获取当前掌握度
|
|
|
|
|
- $currentMastery = 0.5; // 默认值
|
|
|
|
|
- $existingMastery = DB::connection('pgsql')
|
|
|
|
|
- ->table('student_knowledge_mastery')
|
|
|
|
|
- ->where('student_id', $studentId)
|
|
|
|
|
- ->where('kp_code', $kpCode)
|
|
|
|
|
- ->first();
|
|
|
|
|
-
|
|
|
|
|
- if ($existingMastery) {
|
|
|
|
|
- $currentMastery = (float) $existingMastery->mastery_level;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 使用AI分析服务更新掌握度
|
|
|
|
|
|
|
+ // 使用AI分析服务更新掌握度(如果表存在)
|
|
|
$result = $this->aiAnalysisService->updateMastery(
|
|
$result = $this->aiAnalysisService->updateMastery(
|
|
|
$studentId,
|
|
$studentId,
|
|
|
$kpCode,
|
|
$kpCode,
|
|
|
- $currentMastery,
|
|
|
|
|
|
|
+ 0.5, // 默认掌握度
|
|
|
$isCorrect,
|
|
$isCorrect,
|
|
|
$difficulty
|
|
$difficulty
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- Log::debug('掌握度已更新', [
|
|
|
|
|
|
|
+ Log::info('掌握度已更新', [
|
|
|
'student_id' => $studentId,
|
|
'student_id' => $studentId,
|
|
|
'kp_code' => $kpCode,
|
|
'kp_code' => $kpCode,
|
|
|
- 'old_mastery' => $result['old_mastery'],
|
|
|
|
|
- 'new_mastery' => $result['new_mastery'],
|
|
|
|
|
- 'change' => $result['change'],
|
|
|
|
|
|
|
+ 'is_correct' => $isCorrect,
|
|
|
|
|
+ 'new_mastery' => $result['new_mastery'] ?? 'N/A',
|
|
|
|
|
+ 'change' => $result['change'] ?? 'N/A',
|
|
|
]);
|
|
]);
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
} catch (\Exception $e) {
|
|
|
- Log::warning('更新掌握度失败', [
|
|
|
|
|
|
|
+ Log::warning('更新掌握度失败(跳过)', [
|
|
|
'student_id' => $studentId,
|
|
'student_id' => $studentId,
|
|
|
'kp_code' => $kpCode,
|
|
'kp_code' => $kpCode,
|
|
|
'error' => $e->getMessage(),
|
|
'error' => $e->getMessage(),
|
|
@@ -301,39 +302,26 @@ class StudentAnswerAnalysisService
|
|
|
public function getStudentLearningHistory(string $studentId, int $limit = 10): array
|
|
public function getStudentLearningHistory(string $studentId, int $limit = 10): array
|
|
|
{
|
|
{
|
|
|
try {
|
|
try {
|
|
|
- $exercises = StudentExercise::where('student_id', $studentId)
|
|
|
|
|
- ->orderBy('created_at', 'desc')
|
|
|
|
|
- ->limit($limit)
|
|
|
|
|
- ->get()
|
|
|
|
|
- ->toArray();
|
|
|
|
|
-
|
|
|
|
|
|
|
+ // 获取错题记录历史(作为学习历史的主要数据)
|
|
|
$mistakes = MistakeRecord::where('student_id', $studentId)
|
|
$mistakes = MistakeRecord::where('student_id', $studentId)
|
|
|
->orderBy('created_at', 'desc')
|
|
->orderBy('created_at', 'desc')
|
|
|
->limit($limit)
|
|
->limit($limit)
|
|
|
->get()
|
|
->get()
|
|
|
->toArray();
|
|
->toArray();
|
|
|
|
|
|
|
|
- // 使用AI分析服务获取掌握度数据
|
|
|
|
|
- $masteryData = $this->aiAnalysisService->getStudentMastery($studentId);
|
|
|
|
|
-
|
|
|
|
|
- // 获取掌握度快照历史
|
|
|
|
|
- $snapshots = DB::connection('pgsql')
|
|
|
|
|
- ->table('knowledge_point_mastery_snapshots')
|
|
|
|
|
- ->where('student_id', $studentId)
|
|
|
|
|
- ->orderBy('snapshot_time', 'desc')
|
|
|
|
|
- ->limit($limit)
|
|
|
|
|
- ->get()
|
|
|
|
|
- ->toArray();
|
|
|
|
|
|
|
+ // 使用AI分析服务获取掌握度数据(如果表存在)
|
|
|
|
|
+ $masteryData = ['data' => []];
|
|
|
|
|
+ try {
|
|
|
|
|
+ $masteryData = $this->aiAnalysisService->getStudentMastery($studentId);
|
|
|
|
|
+ } catch (\Exception $e) {
|
|
|
|
|
+ Log::warning('获取掌握度数据失败', ['student_id' => $studentId, 'error' => $e->getMessage()]);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
return [
|
|
return [
|
|
|
- 'exercises' => $exercises,
|
|
|
|
|
'mistakes' => $mistakes,
|
|
'mistakes' => $mistakes,
|
|
|
'mastery_data' => $masteryData['data'] ?? [],
|
|
'mastery_data' => $masteryData['data'] ?? [],
|
|
|
- 'mastery_snapshots' => $snapshots,
|
|
|
|
|
'summary' => [
|
|
'summary' => [
|
|
|
- 'total_exercises' => StudentExercise::where('student_id', $studentId)->count(),
|
|
|
|
|
'total_mistakes' => MistakeRecord::where('student_id', $studentId)->count(),
|
|
'total_mistakes' => MistakeRecord::where('student_id', $studentId)->count(),
|
|
|
- 'mastery_snapshots_count' => count($snapshots),
|
|
|
|
|
'total_mastery_items' => count($masteryData['data'] ?? []),
|
|
'total_mastery_items' => count($masteryData['data'] ?? []),
|
|
|
],
|
|
],
|
|
|
];
|
|
];
|