Implement verified curriculum, video review, watch sessions, ledger safeguards, and remove demo data
This commit is contained in:
@@ -79,6 +79,7 @@ class SocraticOptionModel {
|
||||
/// Model for Lesson Playback Data (Streams, Versions, Chapters, Checkpoints)
|
||||
class LessonPlaybackData {
|
||||
final int lessonId;
|
||||
final String? videoVersionId;
|
||||
final String title;
|
||||
final int durationSeconds;
|
||||
final String videoUrl;
|
||||
@@ -89,6 +90,7 @@ class LessonPlaybackData {
|
||||
|
||||
const LessonPlaybackData({
|
||||
required this.lessonId,
|
||||
this.videoVersionId,
|
||||
required this.title,
|
||||
required this.durationSeconds,
|
||||
required this.videoUrl,
|
||||
@@ -129,9 +131,10 @@ class LessonPlaybackData {
|
||||
final lastPos = (json['last_position_seconds'] ?? playback['last_position_seconds'] ?? json['progress']?['position_seconds']) as num?;
|
||||
|
||||
return LessonPlaybackData(
|
||||
lessonId: (lesson['id'] as num?)?.toInt() ?? 0,
|
||||
title: lesson['title']?.toString() ?? 'الدرس التفاعلي',
|
||||
durationSeconds: (lesson['duration_seconds'] as num?)?.toInt() ?? 0,
|
||||
lessonId: (lesson['id'] as num?)?.toInt() ?? (json['lesson_id'] as num?)?.toInt() ?? 0,
|
||||
videoVersionId: json['video_version_id']?.toString(),
|
||||
title: lesson['title']?.toString() ?? json['title']?.toString() ?? 'الدرس التفاعلي',
|
||||
durationSeconds: (lesson['duration_seconds'] as num?)?.toInt() ?? (json['duration_seconds'] as num?)?.toInt() ?? 0,
|
||||
videoUrl: vidUrl,
|
||||
storageType: playback['storage_type']?.toString() ?? 'api_upload',
|
||||
availableVersions: versions,
|
||||
@@ -142,6 +145,7 @@ class LessonPlaybackData {
|
||||
|
||||
LessonPlaybackData copyWith({
|
||||
int? lessonId,
|
||||
String? videoVersionId,
|
||||
String? title,
|
||||
int? durationSeconds,
|
||||
String? videoUrl,
|
||||
@@ -152,6 +156,7 @@ class LessonPlaybackData {
|
||||
}) {
|
||||
return LessonPlaybackData(
|
||||
lessonId: lessonId ?? this.lessonId,
|
||||
videoVersionId: videoVersionId ?? this.videoVersionId,
|
||||
title: title ?? this.title,
|
||||
durationSeconds: durationSeconds ?? this.durationSeconds,
|
||||
videoUrl: videoUrl ?? this.videoUrl,
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user