Files
saqel/apps/teacher_app/lib/data/models/teacher_models.dart
T

334 lines
10 KiB
Dart

/**
* ==============================================================================
* SAQEL TEACHER STUDIO - DATA MODELS
* ==============================================================================
*
* نماذج البيانات الخاصة باستوديو المعلم:
* - نتائج فحص الجودة الإدراكي والأكاديمي (Quality Gate & MIT 20-25 Min Limit)
* - بيانات التسييل المالي وحسابات الحصص (55% المعلم / 15% المديرية / 30% المنصة)
* - أوراق العمل والواجبات المدرسية المصححة آلياً
* - استفسارات وأسئلة الطلبة الميدانية (غرفة الشكوك) والردود السقراطية المسجلة.
*/
class TeacherLessonAuditModel {
final String lessonTitle;
final String subject;
final double durationMinutes;
final bool durationGatePassed;
final String? durationWarning;
final int qualityScore;
final String approvalStatus;
final String curriculumAlignment;
final String audioClarity;
final int socraticStopsCount;
final String decision;
const TeacherLessonAuditModel({
required this.lessonTitle,
required this.subject,
required this.durationMinutes,
required this.durationGatePassed,
this.durationWarning,
required this.qualityScore,
required this.approvalStatus,
required this.curriculumAlignment,
required this.audioClarity,
required this.socraticStopsCount,
required this.decision,
});
factory TeacherLessonAuditModel.fromJson(Map<String, dynamic> json) {
final durationSeconds = (json['duration_seconds'] as num?)?.toDouble() ?? 0;
final readiness = (json['pedagogical_readiness_score'] as num?)?.toInt();
final visual = (json['visual_clarity_score'] as num?)?.toInt();
final decisionValue = json['decision']?.toString() ?? 'pending';
final ai = json['ai_analysis'] is Map ? Map<String, dynamic>.from(json['ai_analysis'] as Map) : null;
final qa = ai != null && ai['quality_assessment'] is Map ? Map<String, dynamic>.from(ai['quality_assessment'] as Map) : null;
final estimatedCp = (durationSeconds / 600).clamp(1, 4).toInt();
final cpCount = (ai?['checkpoints_count'] as num?)?.toInt() ??
(json['socratic_stops_count'] as num?)?.toInt() ??
(durationSeconds > 0 ? estimatedCp : 0);
final alignScore = (qa?['alignment_score'] as num?)?.toInt() ?? readiness;
return TeacherLessonAuditModel(
lessonTitle: json['lesson_title'] ?? '',
subject: json['subject'] ?? '',
durationMinutes: (json['duration_minutes'] as num?)?.toDouble() ??
durationSeconds / 60,
durationGatePassed: json['duration_gate_passed'] ??
(durationSeconds > 0 && durationSeconds <= 1500),
durationWarning: json['duration_warning'],
qualityScore: (qa?['clarity_score'] as num?)?.toInt() ?? (json['quality_score'] as num?)?.toInt() ?? readiness ?? 0,
approvalStatus: qa?['status']?.toString() ?? json['approval_status'] ?? decisionValue,
curriculumAlignment: alignScore != null
? 'تطابق معتمد بنسبة $alignScore% مع المنهاج الرسمي'
: (json['curriculum_alignment'] ?? 'بانتظار تحليل الملف الفعلي'),
audioClarity: json['audio_clarity'] ??
(visual == null
? 'تم التحقق من وجود مسار صوت فعلي'
: 'وضوح الصورة $visual%، وتم التحقق من وجود الصوت'),
socraticStopsCount: cpCount,
decision: json['report'] ?? json['reason'] ?? decisionValue,
);
}
}
class TeacherHomeworkModel {
final String id;
final String title;
final String className;
final String submitted;
final String dueDate;
final String averageScore;
final String status; // 'active', 'completed'
const TeacherHomeworkModel({
required this.id,
required this.title,
required this.className,
required this.submitted,
required this.dueDate,
required this.averageScore,
required this.status,
});
}
class StudentDoubtModel {
final String id;
final int studentId;
final String studentName;
final String className;
final String question;
final String time;
final bool replied;
final bool voiceReply;
const StudentDoubtModel({
required this.id,
required this.studentId,
required this.studentName,
required this.className,
required this.question,
required this.time,
required this.replied,
required this.voiceReply,
});
StudentDoubtModel copyWith({
bool? replied,
bool? voiceReply,
}) {
return StudentDoubtModel(
id: id,
studentId: studentId,
studentName: studentName,
className: className,
question: question,
time: time,
replied: replied ?? this.replied,
voiceReply: voiceReply ?? this.voiceReply,
);
}
}
class TeacherCourseModel {
final String id;
final String title;
final String grade;
final int totalLessons;
final int militaryStudents;
final int externalSubscribers;
final double priceJod;
final double netRevenueJod;
const TeacherCourseModel({
required this.id,
required this.title,
required this.grade,
required this.totalLessons,
required this.militaryStudents,
required this.externalSubscribers,
required this.priceJod,
required this.netRevenueJod,
});
}
class TeacherPayoutRequestModel {
final String cliqAlias;
final double amountJod;
final String requestedAt;
final String status; // 'queued', 'approved', 'completed'
const TeacherPayoutRequestModel({
required this.cliqAlias,
required this.amountJod,
required this.requestedAt,
required this.status,
});
}
class TeacherProfileModel {
final int id;
final String uuid;
final String name;
final String specialization;
final String schoolName;
final List<String> gradesTaught;
final String bio;
final double rating;
final int studentsCount;
final int lessonsCount;
const TeacherProfileModel({
required this.id,
required this.uuid,
required this.name,
required this.specialization,
required this.schoolName,
required this.gradesTaught,
required this.bio,
this.rating = 0,
this.studentsCount = 0,
this.lessonsCount = 0,
});
factory TeacherProfileModel.fromJson(Map<String, dynamic> json) {
final user = json['user'] as Map<String, dynamic>? ?? {};
final profile = json['profile'] as Map<String, dynamic>? ?? {};
List<String> grades = [];
if (profile['grades_taught'] is List) {
grades =
(profile['grades_taught'] as List).map((e) => e.toString()).toList();
} else if (profile['grades_taught'] is String) {
grades = [profile['grades_taught'].toString()];
}
if (grades.isEmpty) {
grades = [];
}
return TeacherProfileModel(
id: user['id'] is int ? user['id'] : 1,
uuid: user['uuid']?.toString() ?? '',
name: user['full_name']?.toString() ?? 'حساب معلم غير مكتمل',
specialization:
profile['specialization']?.toString() ?? 'بانتظار استكمال التخصص',
schoolName: profile['school_name']?.toString() ?? '',
gradesTaught: grades,
bio: profile['bio']?.toString() ?? '',
rating: 0,
studentsCount: 0,
lessonsCount: 0,
);
}
TeacherProfileModel copyWith({
String? name,
String? specialization,
String? schoolName,
List<String>? gradesTaught,
String? bio,
}) {
return TeacherProfileModel(
id: id,
uuid: uuid,
name: name ?? this.name,
specialization: specialization ?? this.specialization,
schoolName: schoolName ?? this.schoolName,
gradesTaught: gradesTaught ?? this.gradesTaught,
bio: bio ?? this.bio,
rating: rating,
studentsCount: studentsCount,
lessonsCount: lessonsCount,
);
}
}
class ChatConversationModel {
final int userId;
final String userUuid;
final String fullName;
final String role;
final String? specialization;
final String lastMessage;
final String lastMessageType;
final String? lastMessageAt;
final int unreadCount;
const ChatConversationModel({
required this.userId,
required this.userUuid,
required this.fullName,
required this.role,
this.specialization,
required this.lastMessage,
required this.lastMessageType,
this.lastMessageAt,
this.unreadCount = 0,
});
factory ChatConversationModel.fromJson(Map<String, dynamic> json) {
return ChatConversationModel(
userId: json['user_id'] is int
? json['user_id']
: int.tryParse(json['user_id']?.toString() ?? '0') ?? 0,
userUuid: json['user_uuid']?.toString() ?? '',
fullName: json['full_name']?.toString() ?? 'طالب صَقِل',
role: json['role']?.toString() ?? 'student',
specialization: json['specialization']?.toString(),
lastMessage: json['last_message']?.toString() ?? '',
lastMessageType: json['last_message_type']?.toString() ?? 'text',
lastMessageAt: json['last_message_at']?.toString(),
unreadCount: json['unread_count'] is int ? json['unread_count'] : 0,
);
}
}
class ChatMessageModel {
final int id;
final String uuid;
final bool isMine;
final int senderId;
final int receiverId;
final String message;
final String messageType; // 'text', 'voice', 'image', 'file'
final String? mediaUrl;
final bool isRead;
final String createdAt;
const ChatMessageModel({
required this.id,
required this.uuid,
required this.isMine,
required this.senderId,
required this.receiverId,
required this.message,
required this.messageType,
this.mediaUrl,
required this.isRead,
required this.createdAt,
});
factory ChatMessageModel.fromJson(Map<String, dynamic> json) {
return ChatMessageModel(
id: json['id'] is int
? json['id']
: int.tryParse(json['id']?.toString() ?? '0') ?? 0,
uuid: json['uuid']?.toString() ?? '',
isMine: json['is_mine'] ?? false,
senderId: json['sender_id'] is int
? json['sender_id']
: int.tryParse(json['sender_id']?.toString() ?? '0') ?? 0,
receiverId: json['receiver_id'] is int
? json['receiver_id']
: int.tryParse(json['receiver_id']?.toString() ?? '0') ?? 0,
message: json['message']?.toString() ?? '',
messageType: json['message_type']?.toString() ?? 'text',
mediaUrl: json['media_url']?.toString(),
isRead: json['is_read'] ?? false,
createdAt: json['created_at']?.toString() ?? '',
);
}
}