use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_json; use uuid::Uuid; #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct Course { pub id: Uuid, pub organization_id: Uuid, pub title: String, pub description: Option, pub instructor_id: Uuid, pub pacing_mode: String, // "self_paced" or "instructor_led" pub start_date: Option>, pub end_date: Option>, pub passing_percentage: i32, pub certificate_template: Option, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct Module { pub id: Uuid, pub course_id: Uuid, pub title: String, pub position: i32, pub created_at: DateTime, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct Lesson { pub id: Uuid, pub module_id: Uuid, pub title: String, pub content_type: String, pub content_url: Option, pub summary: Option, pub transcription: Option, pub metadata: Option, pub grading_category_id: Option, pub is_graded: bool, pub max_attempts: Option, pub allow_retry: bool, pub position: i32, pub due_date: Option>, pub important_date_type: Option, // "exam", "assignment", "milestone", etc. pub created_at: DateTime, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow, Clone)] pub struct GradingCategory { pub id: Uuid, pub course_id: Uuid, pub name: String, pub weight: i32, // 0-100 pub drop_count: i32, pub created_at: DateTime, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct UserGrade { pub id: Uuid, pub user_id: Uuid, pub course_id: Uuid, pub lesson_id: Uuid, pub score: f32, // 0.0 to 1.0 pub attempts_count: i32, pub metadata: Option, pub created_at: DateTime, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct AuditLog { pub id: Uuid, pub user_id: Uuid, pub organization_id: Option, pub action: String, pub entity_type: String, pub entity_id: Uuid, pub changes: serde_json::Value, pub created_at: DateTime, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct AuditLogResponse { pub id: Uuid, pub user_id: Uuid, pub user_full_name: Option, pub action: String, pub entity_type: String, pub entity_id: Uuid, pub changes: serde_json::Value, pub created_at: DateTime, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct Enrollment { pub id: Uuid, pub user_id: Uuid, pub organization_id: Uuid, pub course_id: Uuid, pub enrolled_at: DateTime, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct Asset { pub id: Uuid, pub organization_id: Uuid, pub filename: String, pub storage_path: String, pub mimetype: String, pub size_bytes: i64, pub created_at: DateTime, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct User { pub id: Uuid, pub organization_id: Uuid, pub email: String, pub password_hash: String, pub full_name: String, pub role: String, // admin, instructor, student pub xp: i32, pub level: i32, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct UserResponse { pub id: Uuid, pub email: String, pub full_name: String, pub role: String, pub organization_id: Uuid, pub xp: i32, pub level: i32, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow, Clone)] pub struct Organization { pub id: Uuid, pub name: String, pub domain: Option, pub logo_url: Option, pub primary_color: Option, pub secondary_color: Option, pub certificate_template: Option, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Serialize, Deserialize)] pub struct AuthResponse { pub user: UserResponse, pub token: String, } #[derive(Debug, Serialize, Deserialize)] pub struct PublishedCourse { pub course: Course, pub grading_categories: Vec, pub modules: Vec, } #[derive(Debug, Serialize, Deserialize)] pub struct PublishedModule { pub module: Module, pub lessons: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CourseAnalytics { pub course_id: Uuid, pub total_enrollments: i64, pub average_score: f32, // 0.0-1.0 pub lessons: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LessonAnalytics { pub lesson_id: Uuid, pub lesson_title: String, pub average_score: f32, // 0.0-1.0 pub submission_count: i64, } #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct CohortData { pub period: String, pub count: i64, pub completion_rate: f32, } #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct RetentionData { pub lesson_id: Uuid, pub lesson_title: String, pub student_count: i64, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AdvancedAnalytics { pub cohorts: Vec, pub retention: Vec, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow, Clone)] pub struct Webhook { pub id: Uuid, pub organization_id: Uuid, pub url: String, pub events: Vec, pub secret: Option, pub is_active: bool, pub created_at: DateTime, pub updated_at: DateTime, } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn test_published_course_serialization() { let lesson_id = Uuid::new_v4(); let module_id = Uuid::new_v4(); let course_id = Uuid::new_v4(); let lesson = Lesson { id: lesson_id, module_id, title: "Test Lesson".to_string(), content_type: "activity".to_string(), content_url: None, summary: None, transcription: None, metadata: Some(json!({ "blocks": [ { "id": "b1", "type": "fill-in-the-blanks", "content": "The capital of France is [[Paris]]." }, { "id": "b2", "type": "matching", "pairs": [{"left": "Term", "right": "Definition"}] } ] })), grading_category_id: None, is_graded: false, max_attempts: None, allow_retry: true, position: 1, due_date: None, important_date_type: None, created_at: Utc::now(), }; let pub_module = PublishedModule { module: Module { id: module_id, course_id, title: "Test Module".to_string(), position: 1, created_at: Utc::now(), }, lessons: vec![lesson], }; let pub_course = PublishedCourse { course: Course { id: course_id, organization_id: Uuid::new_v4(), title: "Test Course".to_string(), description: None, instructor_id: Uuid::new_v4(), pacing_mode: "self_paced".to_string(), start_date: None, end_date: None, passing_percentage: 70, certificate_template: None, created_at: Utc::now(), updated_at: Utc::now(), }, grading_categories: vec![], modules: vec![pub_module], }; let serialized = serde_json::to_string(&pub_course).unwrap(); let deserialized: PublishedCourse = serde_json::from_str(&serialized).unwrap(); assert_eq!(pub_course.course.title, deserialized.course.title); assert_eq!(pub_course.modules.len(), deserialized.modules.len()); assert_eq!(deserialized.modules[0].lessons[0].title, "Test Lesson"); } }