Files
saqel/apps/student_app/lib/data/models/exam_model.dart
T

215 lines
6.5 KiB
Dart

/// Exam, Question, and Socratic Diagnostic Models for Saqel Student App
class ExamModel {
final int id;
final String uuid;
final int courseId;
final int? lessonId;
final String title;
final String description;
final String scope;
final double passingPercentage;
final int durationMinutes;
final int questionsCount;
final List<QuestionModel> questions;
const ExamModel({
required this.id,
required this.uuid,
required this.courseId,
this.lessonId,
required this.title,
required this.description,
required this.scope,
required this.passingPercentage,
required this.durationMinutes,
this.questionsCount = 0,
this.questions = const [],
});
factory ExamModel.fromJson(Map<String, dynamic> json) {
var rawQuestions = json['questions'];
List<QuestionModel> parsedQuestions = [];
if (rawQuestions is List) {
parsedQuestions = rawQuestions
.map((q) => QuestionModel.fromJson(Map<String, dynamic>.from(q)))
.toList();
}
return ExamModel(
id: _toInt(json['id']),
uuid: json['uuid']?.toString() ?? '',
courseId: _toInt(json['course_id']),
lessonId: json['lesson_id'] != null ? _toInt(json['lesson_id']) : null,
title: json['title']?.toString() ?? 'امتحان تشخيصي',
description: json['description']?.toString() ?? '',
scope: json['scope']?.toString() ?? 'unit_exam',
passingPercentage: _toDouble(json['passing_percentage'], 60.0),
durationMinutes: _toInt(json['duration_minutes'], 20),
questionsCount: _toInt(json['questions_count'], parsedQuestions.length),
questions: parsedQuestions,
);
}
}
int _toInt(dynamic value, [int defaultValue = 0]) {
if (value == null) return defaultValue;
if (value is num) return value.toInt();
return int.tryParse(value.toString()) ?? defaultValue;
}
double _toDouble(dynamic value, [double defaultValue = 0.0]) {
if (value == null) return defaultValue;
if (value is num) return value.toDouble();
return double.tryParse(value.toString()) ?? defaultValue;
}
class QuestionModel {
final int id;
final String questionText;
final String questionType;
final int points;
final String topicTag;
final String? explanationText;
final String? aiHint;
final List<QuestionOptionModel> options;
const QuestionModel({
required this.id,
required this.questionText,
required this.questionType,
required this.points,
required this.topicTag,
this.explanationText,
this.aiHint,
this.options = const [],
});
factory QuestionModel.fromJson(Map<String, dynamic> json) {
var rawOptions = json['options'];
List<QuestionOptionModel> parsedOptions = [];
if (rawOptions is List) {
parsedOptions = rawOptions
.map((opt) => QuestionOptionModel.fromJson(Map<String, dynamic>.from(opt)))
.toList();
}
return QuestionModel(
id: _toInt(json['id']),
questionText: json['question_text']?.toString() ?? '',
questionType: json['question_type']?.toString() ?? 'multiple_choice',
points: _toInt(json['points'], 10),
topicTag: json['topic_tag']?.toString() ?? 'المفاهيم العامة',
explanationText: json['explanation_text']?.toString(),
aiHint: json['ai_hint']?.toString(),
options: parsedOptions,
);
}
}
class QuestionOptionModel {
final int id;
final String optionText;
final bool isCorrect;
final int sequenceOrder;
const QuestionOptionModel({
required this.id,
required this.optionText,
this.isCorrect = false,
this.sequenceOrder = 1,
});
factory QuestionOptionModel.fromJson(Map<String, dynamic> json) {
return QuestionOptionModel(
id: _toInt(json['id']),
optionText: json['option_text']?.toString() ?? '',
isCorrect: json['is_correct'] == 1 || json['is_correct'] == true || json['is_correct'] == '1',
sequenceOrder: _toInt(json['sequence_order'], 1),
);
}
}
class ExamSubmissionResultModel {
final int attemptId;
final int score;
final int totalScore;
final double percentage;
final bool passed;
final int rewindSeconds;
final String aiDiagnosticReport;
final List<String> weakTopics;
final double tawjihiReadinessScore;
final List<DetailedAnswerModel> detailedAnswers;
const ExamSubmissionResultModel({
required this.attemptId,
required this.score,
required this.totalScore,
required this.percentage,
required this.passed,
required this.rewindSeconds,
required this.aiDiagnosticReport,
required this.weakTopics,
required this.tawjihiReadinessScore,
this.detailedAnswers = const [],
});
factory ExamSubmissionResultModel.fromJson(Map<String, dynamic> json) {
var rawTopics = json['weak_topics'];
List<String> parsedTopics = [];
if (rawTopics is List) {
parsedTopics = rawTopics.map((e) => e.toString()).toList();
}
var rawDetails = json['detailed_answers'];
List<DetailedAnswerModel> parsedDetails = [];
if (rawDetails is List) {
parsedDetails = rawDetails
.map((d) => DetailedAnswerModel.fromJson(Map<String, dynamic>.from(d)))
.toList();
}
return ExamSubmissionResultModel(
attemptId: _toInt(json['attempt_id']),
score: _toInt(json['score']),
totalScore: _toInt(json['total_score'], 100),
percentage: _toDouble(json['percentage']),
passed: json['passed'] == true || json['passed'] == 1 || json['passed'] == '1',
rewindSeconds: _toInt(json['rewind_seconds']),
aiDiagnosticReport: json['ai_diagnostic_report']?.toString() ?? '',
weakTopics: parsedTopics,
tawjihiReadinessScore: _toDouble(json['tawjihi_readiness_score'], 50.0),
detailedAnswers: parsedDetails,
);
}
}
class DetailedAnswerModel {
final int questionId;
final int selectedOptionId;
final bool isCorrect;
final int pointsAwarded;
final String? explanation;
final String? aiHint;
const DetailedAnswerModel({
required this.questionId,
required this.selectedOptionId,
required this.isCorrect,
required this.pointsAwarded,
this.explanation,
this.aiHint,
});
factory DetailedAnswerModel.fromJson(Map<String, dynamic> json) {
return DetailedAnswerModel(
questionId: _toInt(json['question_id']),
selectedOptionId: _toInt(json['selected_option_id']),
isCorrect: json['is_correct'] == 1 || json['is_correct'] == true || json['is_correct'] == '1',
pointsAwarded: _toInt(json['points_awarded']),
explanation: json['explanation']?.toString(),
aiHint: json['ai_hint']?.toString(),
);
}
}