Complete Socratic adaptive exams in Flutter, Guardian WhatsApp alerts, and curriculum robustness
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
/// 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: (json['id'] as num?)?.toInt() ?? 0,
|
||||
uuid: json['uuid']?.toString() ?? '',
|
||||
courseId: (json['course_id'] as num?)?.toInt() ?? 0,
|
||||
lessonId: (json['lesson_id'] as num?)?.toInt(),
|
||||
title: json['title']?.toString() ?? 'امتحان تشخيصي',
|
||||
description: json['description']?.toString() ?? '',
|
||||
scope: json['scope']?.toString() ?? 'general',
|
||||
passingPercentage: (json['passing_percentage'] as num?)?.toDouble() ?? 60.0,
|
||||
durationMinutes: (json['duration_minutes'] as num?)?.toInt() ?? 20,
|
||||
questionsCount: (json['questions_count'] as num?)?.toInt() ?? parsedQuestions.length,
|
||||
questions: parsedQuestions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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: (json['id'] as num?)?.toInt() ?? 0,
|
||||
questionText: json['question_text']?.toString() ?? '',
|
||||
questionType: json['question_type']?.toString() ?? 'multiple_choice',
|
||||
points: (json['points'] as num?)?.toInt() ?? 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: (json['id'] as num?)?.toInt() ?? 0,
|
||||
optionText: json['option_text']?.toString() ?? '',
|
||||
isCorrect: json['is_correct'] == 1 || json['is_correct'] == true,
|
||||
sequenceOrder: (json['sequence_order'] as num?)?.toInt() ?? 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: (json['attempt_id'] as num?)?.toInt() ?? 0,
|
||||
score: (json['score'] as num?)?.toInt() ?? 0,
|
||||
totalScore: (json['total_score'] as num?)?.toInt() ?? 0,
|
||||
percentage: (json['percentage'] as num?)?.toDouble() ?? 0.0,
|
||||
passed: json['passed'] == true || json['passed'] == 1,
|
||||
rewindSeconds: (json['rewind_seconds'] as num?)?.toInt() ?? 0,
|
||||
aiDiagnosticReport: json['ai_diagnostic_report']?.toString() ?? '',
|
||||
weakTopics: parsedTopics,
|
||||
tawjihiReadinessScore: (json['tawjihi_readiness_score'] as num?)?.toDouble() ?? 0.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: (json['question_id'] as num?)?.toInt() ?? 0,
|
||||
selectedOptionId: (json['selected_option_id'] as num?)?.toInt() ?? 0,
|
||||
isCorrect: json['is_correct'] == 1 || json['is_correct'] == true,
|
||||
pointsAwarded: (json['points_awarded'] as num?)?.toInt() ?? 0,
|
||||
explanation: json['explanation']?.toString(),
|
||||
aiHint: json['ai_hint']?.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user