Implement verified curriculum, video review, watch sessions, ledger safeguards, and remove demo data

This commit is contained in:
Hamza-Ayed
2026-09-09 17:04:08 +03:00
parent 0dc5df7b5e
commit 5af684a892
49 changed files with 1809 additions and 3005 deletions
@@ -30,7 +30,7 @@ class SubjectModel {
this.stream = 'scientific',
this.totalUnits = 0,
this.totalLessons = 0,
this.masteryScore = 85.0,
this.masteryScore = 0,
this.units = const [],
this.textbooks = const [],
this.worksheets = const [],
@@ -95,7 +95,7 @@ class SubjectModel {
stream: json['stream']?.toString() ?? 'scientific',
totalUnits: parsedUnits.isNotEmpty ? parsedUnits.length : styling.defaultUnits,
totalLessons: parsedUnits.fold(0, (sum, u) => sum + u.lessons.length),
masteryScore: (json['mastery_score'] as num?)?.toDouble() ?? 88.0,
masteryScore: (json['mastery_score'] as num?)?.toDouble() ?? 0,
units: parsedUnits,
textbooks: parsedTextbooks,
worksheets: parsedWorksheets,
@@ -198,6 +198,7 @@ class CurriculumLessonItemModel {
final String title;
final List<String> outcomes;
final String? markdownFilePath;
final String? curriculumLessonId;
final int durationSeconds;
final int checkpointsCount;
final bool isCompleted;
@@ -208,8 +209,9 @@ class CurriculumLessonItemModel {
required this.title,
this.outcomes = const [],
this.markdownFilePath,
this.durationSeconds = 1200, // 20 mins default
this.checkpointsCount = 3,
this.curriculumLessonId,
this.durationSeconds = 0,
this.checkpointsCount = 0,
this.isCompleted = false,
this.hasVideo = false,
});
@@ -233,24 +235,17 @@ class CurriculumLessonItemModel {
? filePath.replaceAll(RegExp(r'[^a-zA-Z0-9_]'), '_')
: 'lesson_${(json['title']?.toString() ?? 'default').hashCode.abs()}');
// Video availability:
// 1. Live server enriched: json['has_video'] == true or json['video_url'] is present
// 2. Exact uploaded lessons in Grade 10 Math Unit 1 (Intro, Lesson 1, Lesson 2)
final bool hasVideoExplicit = (json['has_video'] == true) ||
(json['video_url'] != null && json['video_url'].toString().isNotEmpty) ||
(filePath != null && (
filePath.contains('math_10/semester_1/unit_01/intro_and_project') ||
filePath.contains('math_10/semester_1/unit_01/lesson_01') ||
filePath.contains('math_10/semester_1/unit_01/lesson_02')
));
// Only the server may declare a published lesson playable.
final bool hasVideoExplicit = json['has_video'] == true;
return CurriculumLessonItemModel(
id: stableId,
title: json['title']?.toString() ?? 'درس بدون عنوان',
outcomes: outs,
markdownFilePath: filePath,
durationSeconds: (json['duration_seconds'] as num?)?.toInt() ?? 1200,
checkpointsCount: (json['checkpoints_count'] as num?)?.toInt() ?? 3,
curriculumLessonId: json['curriculum_lesson_id']?.toString(),
durationSeconds: (json['duration_seconds'] as num?)?.toInt() ?? 0,
checkpointsCount: (json['checkpoints_count'] as num?)?.toInt() ?? 0,
isCompleted: (json['is_completed'] as bool?) ?? false,
hasVideo: hasVideoExplicit,
);