Update Saqel Platform: 2026-09-13 16:31:17
This commit is contained in:
@@ -37,8 +37,18 @@ class SubjectModel {
|
||||
this.exams = const [],
|
||||
});
|
||||
|
||||
List<CurriculumUnitModel> get semester1Units => units.where((u) => u.semester == 'semester_1').toList();
|
||||
List<CurriculumUnitModel> get semester2Units => units.where((u) => u.semester == 'semester_2').toList();
|
||||
List<CurriculumUnitModel> unitsForSemester(String semesterKey) {
|
||||
final filtered = units.where((u) => u.semester == semesterKey).toList();
|
||||
return filtered.isNotEmpty ? filtered : units;
|
||||
}
|
||||
|
||||
factory SubjectModel.fromJson(String id, Map<String, dynamic> json) {
|
||||
final title = json['name']?.toString() ?? json['title']?.toString() ?? id;
|
||||
var title = json['name']?.toString() ?? json['title']?.toString() ?? id;
|
||||
if (id == 'history_10' || title.contains('تاريخ الأردن')) {
|
||||
title = 'التاريخ (History 10)';
|
||||
}
|
||||
final englishTitle = json['english_name']?.toString() ?? '';
|
||||
|
||||
// Parse units if available
|
||||
|
||||
@@ -148,32 +148,57 @@ class CurriculumRepository {
|
||||
}
|
||||
|
||||
Future<List<PublishedLessonVideoModel>> getPublishedLessonVideos(String curriculumLessonId) async {
|
||||
AppLogger.event('FetchPublishedVideosRequested', details: {'curriculumLessonId': curriculumLessonId}, tag: 'CURRICULUM_REPO');
|
||||
final res = await _api.get('/api/curriculum/lessons/$curriculumLessonId/videos');
|
||||
final data = res is Map ? res['data'] : null;
|
||||
final raw = data is Map ? data['items'] : null;
|
||||
if (raw is! List) return const [];
|
||||
return raw.whereType<Map>().map((item) => PublishedLessonVideoModel.fromJson(Map<String, dynamic>.from(item))).where((item) => item.videoVersionId.isNotEmpty).toList();
|
||||
final list = raw.whereType<Map>().map((item) => PublishedLessonVideoModel.fromJson(Map<String, dynamic>.from(item))).where((item) => item.videoVersionId.isNotEmpty).toList();
|
||||
AppLogger.event('FetchPublishedVideosSuccess', details: {'count': list.length, 'teachers': list.map((v) => v.teacherName).toList()}, tag: 'CURRICULUM_REPO');
|
||||
return list;
|
||||
}
|
||||
|
||||
Future<LessonPlaybackData> getVideoVersionPlayback(String videoVersionId) async {
|
||||
AppLogger.event('FetchVideoPlaybackRequested', details: {'videoVersionId': videoVersionId}, tag: 'CURRICULUM_REPO');
|
||||
final res = await _api.get('/api/video-versions/$videoVersionId/playback');
|
||||
if (res is Map && res['data'] is Map) return LessonPlaybackData.fromJson(Map<String, dynamic>.from(res['data']));
|
||||
if (res is Map && res['data'] is Map) {
|
||||
final pb = LessonPlaybackData.fromJson(Map<String, dynamic>.from(res['data']));
|
||||
AppLogger.event('FetchVideoPlaybackSuccess', details: {
|
||||
'videoVersionId': pb.videoVersionId,
|
||||
'videoUrl': pb.videoUrl,
|
||||
'duration': pb.durationSeconds,
|
||||
}, tag: 'CURRICULUM_REPO');
|
||||
return pb;
|
||||
}
|
||||
throw ApiException('فشل جلب تشغيل الحصة المنشورة من الخادم');
|
||||
}
|
||||
|
||||
Future<String> startWatchSession(String videoVersionId) async {
|
||||
AppLogger.event('StartWatchSessionRequested', details: {'videoVersionId': videoVersionId}, tag: 'CURRICULUM_REPO');
|
||||
final res = await _api.post('/api/video-versions/$videoVersionId/watch-sessions', body: const {});
|
||||
final data = res is Map ? res['data'] : null;
|
||||
final source = data is Map ? data : res;
|
||||
final id = source is Map ? source['watch_session_id']?.toString() : null;
|
||||
if (id == null || id.isEmpty) throw ApiException('لم ينشئ الخادم جلسة مشاهدة.');
|
||||
AppLogger.event('StartWatchSessionSuccess', details: {'sessionId': id}, tag: 'CURRICULUM_REPO');
|
||||
return id;
|
||||
}
|
||||
Future<void> recordWatchEvent(String sessionId, int sequenceNo, String eventType, int positionMs) async {
|
||||
AppLogger.event('RecordWatchEvent', details: {
|
||||
'sessionId': sessionId,
|
||||
'seq': sequenceNo,
|
||||
'type': eventType,
|
||||
'positionMs': positionMs,
|
||||
}, tag: 'CURRICULUM_REPO');
|
||||
await _api.post('/api/watch-sessions/$sessionId/events', body: {'sequence_no': sequenceNo, 'event_type': eventType, 'position_ms': positionMs});
|
||||
}
|
||||
|
||||
Future<void> saveProgress({required int lessonId, required int positionSeconds, required int watchedSeconds}) async {
|
||||
AppLogger.event('SaveProgressRequested', details: {
|
||||
'lessonId': lessonId,
|
||||
'position': positionSeconds,
|
||||
'watched': watchedSeconds,
|
||||
}, tag: 'CURRICULUM_REPO');
|
||||
await _api.post('/api/student/lessons/$lessonId/progress', body: {
|
||||
'position_seconds': positionSeconds,
|
||||
'watched_seconds': watchedSeconds,
|
||||
@@ -181,6 +206,11 @@ class CurriculumRepository {
|
||||
}
|
||||
|
||||
Future<void> submitCheckpoint({required int examId, required int questionId, required int optionId}) async {
|
||||
AppLogger.event('SubmitCheckpointRequested', details: {
|
||||
'examId': examId,
|
||||
'questionId': questionId,
|
||||
'optionId': optionId,
|
||||
}, tag: 'CURRICULUM_REPO');
|
||||
await _api.post('/api/exams/$examId/submit', body: {
|
||||
'answers': [
|
||||
{'question_id': questionId, 'selected_option_id': optionId}
|
||||
|
||||
Reference in New Issue
Block a user