headless_lms_server/programs/seed/seed_courses/
seed_lock_chapter_course.rs

1use crate::programs::seed::builder::chapter::ChapterBuilder;
2use crate::programs::seed::builder::context::SeedContext;
3use crate::programs::seed::builder::course::{CourseBuilder, CourseInstanceConfig};
4use crate::programs::seed::builder::exercise::{ExerciseBuilder, ExerciseIds};
5use crate::programs::seed::builder::json_source::JsonSource;
6use crate::programs::seed::builder::module::ModuleBuilder;
7use crate::programs::seed::builder::page::PageBuilder;
8use crate::programs::seed::seed_courses::CommonCourseData;
9use anyhow::Result;
10use headless_lms_models::roles::UserRole;
11use headless_lms_utils::{attributes, document_schema_processor::GutenbergBlock};
12use serde_json::json;
13use tracing::info;
14use uuid::Uuid;
15
16pub async fn seed_lock_chapter_course(
17    course_id: Uuid,
18    course_name: &str,
19    course_slug: &str,
20    common_course_data: CommonCourseData,
21) -> Result<Uuid> {
22    let CommonCourseData {
23        db_pool,
24        organization_id: org,
25        teacher_user_id,
26        student_user_id: _student,
27        langs_user_id: _langs_user_id,
28        example_normal_user_ids: _users,
29        jwt_key: _jwt_key,
30        base_url: _base_url,
31    } = common_course_data;
32
33    let mut conn = db_pool.acquire().await?;
34    let cx = SeedContext {
35        teacher: teacher_user_id,
36        org,
37        base_course_ns: course_id,
38    };
39
40    info!("inserting lock chapter course {}", course_name);
41
42    let course = CourseBuilder::new(course_name, course_slug)
43        .desc("Course for testing chapter locking feature.")
44        .course_id(course_id)
45        .chapter_locking_enabled(true)
46        .instance(CourseInstanceConfig {
47            name: None,
48            description: None,
49            support_email: None,
50            teacher_in_charge_name: "admin".to_string(),
51            teacher_in_charge_email: "admin@example.com".to_string(),
52            opening_time: None,
53            closing_time: None,
54            instance_id: Some(cx.v5(b"a1b2c3d4-e5f6-7890-abcd-ef1234567890")),
55        })
56        .role(teacher_user_id, UserRole::Teacher)
57        .module(
58            ModuleBuilder::new()
59                .order(0)
60                .chapter(
61                    ChapterBuilder::new(1, "Chapter 1 - Lockable")
62                        .fixed_ids(
63                            cx.v5(b"b2c3d4e5-f6a7-8901-bcde-f12345678901"),
64                            cx.v5(b"c3d4e5f6-a7b8-9012-cdef-123456789012"),
65                        )
66                        .page(
67                            PageBuilder::new("/chapter-1/lock-page", "Lock Chapter Page")
68                                .block(
69                                    GutenbergBlock::block_with_name_attributes_and_inner_blocks(
70                                        "moocfi/lock-chapter",
71                                        attributes! {},
72                                        vec![
73                                            GutenbergBlock::block_with_name_and_attributes(
74                                                "core/heading",
75                                                attributes! {
76                                                    "content": "Model Solution",
77                                                    "level": 2,
78                                                    "anchor": "model-solution-title"
79                                                },
80                                            )
81                                            .with_id(cx.v5(b"f1a2b3c4-d5e6-7890-abcd-ef1234567890")),
82                                            GutenbergBlock::paragraph("Congratulations on completing Chapter 1! Here's a model solution for the Customer Behavior Analysis Project.")
83                                                .with_id(cx.v5(b"f2a3b4c5-d6e7-8901-bcde-f1234567891")),
84                                            GutenbergBlock::paragraph("The project involved analyzing customer purchase data to identify patterns and segment customers. The first step was data cleaning, which included handling missing values using appropriate imputation methods and removing outliers using the IQR method.")
85                                                .with_id(cx.v5(b"f3a4b5c6-d7e8-9012-cdef-1234567892")),
86                                            GutenbergBlock::block_with_name_and_attributes(
87                                                "core/heading",
88                                                attributes! {
89                                                    "content": "Data Cleaning",
90                                                    "level": 3,
91                                                    "anchor": "data-cleaning"
92                                                },
93                                            )
94                                            .with_id(cx.v5(b"f3b4c5d6-e7f8-9012-def3-3456789012")),
95                                            GutenbergBlock::block_with_name_and_attributes(
96                                                "core/code",
97                                                attributes! {
98                                                    "content": "import pandas as pd
99import numpy as np
100from sklearn.cluster import KMeans
101
102# Load and clean data
103df = pd.read_csv('customer_data.csv')
104df['age'].fillna(df['age'].median(), inplace=True)
105df['income'].fillna(df['income'].mean(), inplace=True)
106
107# Remove outliers
108Q1 = df['purchase_amount'].quantile(0.25)
109Q3 = df['purchase_amount'].quantile(0.75)
110IQR = Q3 - Q1
111df = df[(df['purchase_amount'] >= Q1 - 1.5*IQR) &
112        (df['purchase_amount'] <= Q3 + 1.5*IQR)]"
113                                                },
114                                            )
115                                            .with_id(cx.v5(b"f4a5b6c7-d8e9-0123-def4-2345678903")),
116                                            GutenbergBlock::block_with_name_and_attributes(
117                                                "core/heading",
118                                                attributes! {
119                                                    "content": "Key Findings",
120                                                    "level": 3,
121                                                    "anchor": "key-findings"
122                                                },
123                                            )
124                                            .with_id(cx.v5(b"f4b5c6d7-e8f9-0123-ef45-4567890123")),
125                                            GutenbergBlock::block_with_name_attributes_and_inner_blocks(
126                                                "core/list",
127                                                attributes! {
128                                                    "ordered": false
129                                                },
130                                                vec![
131                                                    GutenbergBlock::block_with_name_and_attributes(
132                                                        "core/list-item",
133                                                        attributes! {
134                                                            "content": "Strong positive correlation (r=0.72) between income and purchase amount"
135                                                        },
136                                                    )
137                                                    .with_id(cx.v5(b"f5a6b7c8-d9e0-1234-ef45-3456789014")),
138                                                    GutenbergBlock::block_with_name_and_attributes(
139                                                        "core/list-item",
140                                                        attributes! {
141                                                            "content": "Electronics category generates 35% more revenue than other categories"
142                                                        },
143                                                    )
144                                                    .with_id(cx.v5(b"f6a7b8c9-d0e1-2345-f456-4567890125")),
145                                                    GutenbergBlock::block_with_name_and_attributes(
146                                                        "core/list-item",
147                                                        attributes! {
148                                                            "content": "High-value customers represent 18% of customers but 42% of total revenue"
149                                                        },
150                                                    )
151                                                    .with_id(cx.v5(b"f7a8b9c0-d1e2-3456-5678-5678901236")),
152                                                ],
153                                            )
154                                            .with_id(cx.v5(b"f8a9b0c1-d2e3-4567-6789-6789012347")),
155                                            GutenbergBlock::block_with_name_and_attributes(
156                                                "core/quote",
157                                                attributes! {
158                                                    "value": "The key insight from this analysis is that customer segmentation reveals distinct purchasing behaviors that can inform targeted marketing strategies."
159                                                },
160                                            )
161                                            .with_id(cx.v5(b"f0a1b2c3-d4e5-6789-8901-8901234569")),
162                                            GutenbergBlock::block_with_name_and_attributes(
163                                                "core/heading",
164                                                attributes! {
165                                                    "content": "Recommendations",
166                                                    "level": 3,
167                                                    "anchor": "recommendations"
168                                                },
169                                            )
170                                            .with_id(cx.v5(b"f1b2c3d4-e5f6-7890-f012-0123456789")),
171                                            GutenbergBlock::paragraph("Based on the analysis, recommendations include developing a VIP program for high-value customers and increasing marketing focus on the Electronics category during peak seasons. The methodology demonstrated here provides a solid framework for data-driven decision making.")
172                                                .with_id(cx.v5(b"f1a2b3c4-d5e6-7890-9012-9012345670")),
173                                        ],
174                                    )
175                                    .with_id(cx.v5(b"d4e5f6a7-b8c9-0123-def4-234567890123")),
176                                )
177                                .block(
178                                    GutenbergBlock::paragraph("This is Chapter 1. You can lock this chapter when you're done.")
179                                        .with_id(cx.v5(b"e5f6a7b8-c9d0-1234-ef45-345678901234")),
180                                ),
181                        )
182                        .page(
183                            PageBuilder::new("/chapter-1/exercise-page", "Exercise in Chapter 1")
184                                .exercise(ExerciseBuilder::quizzes(
185                                    "Chapter 1 Exercise",
186                                    ExerciseIds {
187                                        exercise_id: cx.v5(b"f6a7b8c9-d0e1-2345-f456-456789012345"),
188                                        slide_id: cx.v5(b"a7b8c9d0-e1f2-3456-5678-567890123456"),
189                                        task_id: cx.v5(b"b8c9d0e1-f2a3-4567-6789-678901234567"),
190                                        block_id: cx.v5(b"c9d0e1f2-a3b4-5678-7890-789012345678"),
191                                    },
192                                    false,
193                                    None,
194                                    JsonSource::Inline(json!({
195                                      "version": "2",
196                                      "title": "Chapter 1 Exercise",
197                                      "body": "Select the correct answer.",
198                                      "awardPointsEvenIfWrong": false,
199                                      "grantPointsPolicy": "grant_whenever_possible",
200                                      "quizItemDisplayDirection": "vertical",
201                                      "submitMessage": "",
202                                      "items": [
203                                        {
204                                          "type": "multiple-choice",
205                                          "id": "d0e1f2a3-b4c5-6789-8901-890123456789",
206                                          "failureMessage": "",
207                                          "options": [
208                                            {
209                                              "order": 1,
210                                              "additionalCorrectnessExplanationOnModelSolution": "",
211                                              "body": "",
212                                              "correct": true,
213                                              "id": "e1f2a3b4-c5d6-7890-9012-901234567890",
214                                              "messageAfterSubmissionWhenSelected": "",
215                                              "title": "Correct answer"
216                                            },
217                                            {
218                                              "order": 2,
219                                              "additionalCorrectnessExplanationOnModelSolution": "",
220                                              "body": "",
221                                              "correct": false,
222                                              "id": "f2a3b4c5-d6e7-8901-0123-012345678901",
223                                              "messageAfterSubmissionWhenSelected": "",
224                                              "title": "Wrong answer"
225                                            }
226                                          ],
227                                          "order": 0,
228                                          "successMessage": "",
229                                          "title": "What is the correct answer?",
230                                          "body": ""
231                                        }
232                                      ]
233                                    })),
234                                    vec![],
235                                )),
236                        ),
237                )
238                .chapter(
239                    ChapterBuilder::new(2, "Chapter 2 - Add Lock Later")
240                        .fixed_ids(
241                            cx.v5(b"z1y2x3w4-v5u6-7890-tsrq-pq9876543210"),
242                            cx.v5(b"y2x3w4v5-u6t7-8901-srqp-op8765432109"),
243                        )
244                        .page(
245                            PageBuilder::new("/chapter-2/lock-page", "Lock Chapter Page")
246                                .block(
247                                    GutenbergBlock::block_with_name_attributes_and_inner_blocks(
248                                        "moocfi/lock-chapter",
249                                        attributes! {},
250                                        vec![
251                                            GutenbergBlock::block_with_name_and_attributes(
252                                                "core/heading",
253                                                attributes! {
254                                                    "content": "Model Solution",
255                                                    "level": 2,
256                                                    "anchor": "model-solution-title"
257                                                },
258                                            )
259                                            .with_id(cx.v5(b"g1h2i3j4-k5l6-7890-mnop-qr1234567890")),
260                                            GutenbergBlock::paragraph("Congratulations on completing Chapter 2! Here's a model solution for the exercises.")
261                                                .with_id(cx.v5(b"g2h3i4j5-k6l7-8901-nopq-rs2345678901")),
262                                        ],
263                                    )
264                                    .with_id(cx.v5(b"g3h4i5j6-k7l8-9012-opqr-st3456789012")),
265                                )
266                                .block(
267                                    GutenbergBlock::paragraph("This is Chapter 2. You can lock this chapter when you're done.")
268                                        .with_id(cx.v5(b"g4h5i6j7-k8l9-0123-pqrs-tu4567890123")),
269                                ),
270                        )
271                        .page(
272                            PageBuilder::new("/chapter-2/exercise-page", "Exercise in Chapter 2")
273                                .block(
274                                    GutenbergBlock::paragraph("This is Chapter 2.")
275                                        .with_id(cx.v5(b"x3w4v5u6-t7s8-9012-rqpo-no7654321098")),
276                                )
277                                .exercise(ExerciseBuilder::quizzes(
278                                    "Chapter 2 Exercise",
279                                    ExerciseIds {
280                                        exercise_id: cx.v5(b"w4v5u6t7-s8r9-0123-qpon-mn6543210987"),
281                                        slide_id: cx.v5(b"v5u6t7s8-r9q0-1234-ponm-lm5432109876"),
282                                        task_id: cx.v5(b"u6t7s8r9-q0p1-2345-onml-kl4321098765"),
283                                        block_id: cx.v5(b"t7s8r9q0-p1o2-3456-nmlk-jk3210987654"),
284                                    },
285                                    false,
286                                    None,
287                                    JsonSource::Inline(json!({
288                                      "version": "2",
289                                      "title": "Chapter 2 Exercise",
290                                      "body": "Select the correct answer.",
291                                      "awardPointsEvenIfWrong": false,
292                                      "grantPointsPolicy": "grant_whenever_possible",
293                                      "quizItemDisplayDirection": "vertical",
294                                      "submitMessage": "",
295                                      "items": [
296                                        {
297                                          "type": "multiple-choice",
298                                          "id": "b8c9d0e1-f2a3-4567-6789-678901234567",
299                                          "failureMessage": "",
300                                          "options": [
301                                            {
302                                              "order": 1,
303                                              "additionalCorrectnessExplanationOnModelSolution": "",
304                                              "body": "",
305                                              "correct": true,
306                                              "id": "c9d0e1f2-a3b4-5678-7890-789012345678",
307                                              "messageAfterSubmissionWhenSelected": "",
308                                              "title": "Correct answer"
309                                            },
310                                            {
311                                              "order": 2,
312                                              "additionalCorrectnessExplanationOnModelSolution": "",
313                                              "body": "",
314                                              "correct": false,
315                                              "id": "d0e1f2a3-b4c5-6789-8901-890123456789",
316                                              "messageAfterSubmissionWhenSelected": "",
317                                              "title": "Wrong answer"
318                                            }
319                                          ],
320                                          "order": 0,
321                                          "successMessage": "",
322                                          "title": "What is the correct answer?",
323                                          "body": ""
324                                        }
325                                      ]
326                                    })),
327                                    vec![],
328                                )),
329                        ),
330                ),
331        );
332
333    let (course, _default_instance, _last_module) = course.seed(&mut conn, &cx).await?;
334
335    Ok(course.id)
336}