Skip to main content

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