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                                    true,
236                                )),
237                        )
238                        .page(
239                            PageBuilder::new(
240                                "/chapter-1/auto-graded-exercise-page",
241                                "Auto-graded Exercise in Chapter 1",
242                            )
243                            .exercise(ExerciseBuilder::quizzes(
244                                "Chapter 1 Auto-graded Exercise",
245                                ExerciseIds {
246                                    exercise_id: cx.v5(b"a1b2c3d4-e5f6-7890-abcd-ef1234567891"),
247                                    slide_id: cx.v5(b"b2c3d4e5-f6a7-8901-bcde-f123456789012"),
248                                    task_id: cx.v5(b"c3d4e5f6-a7b8-9012-cdef-123456789013"),
249                                    block_id: cx.v5(b"d4e5f6a7-b8c9-0123-def4-234567890124"),
250                                },
251                                false,
252                                None,
253                                JsonSource::Inline(json!({
254                                  "version": "2",
255                                  "title": "Chapter 1 Auto-graded Exercise",
256                                  "body": "Select the correct answer.",
257                                  "awardPointsEvenIfWrong": false,
258                                  "grantPointsPolicy": "grant_whenever_possible",
259                                  "quizItemDisplayDirection": "vertical",
260                                  "submitMessage": "",
261                                  "items": [
262                                    {
263                                      "type": "multiple-choice",
264                                      "id": "e4f5a6b7-c8d9-0123-ef45-345678901235",
265                                      "failureMessage": "",
266                                      "options": [
267                                        {
268                                          "order": 1,
269                                          "additionalCorrectnessExplanationOnModelSolution": "",
270                                          "body": "",
271                                          "correct": true,
272                                          "id": "f5a6b7c8-d9e0-1234-f456-456789012346",
273                                          "messageAfterSubmissionWhenSelected": "",
274                                          "title": "Correct answer"
275                                        },
276                                        {
277                                          "order": 2,
278                                          "additionalCorrectnessExplanationOnModelSolution": "",
279                                          "body": "",
280                                          "correct": false,
281                                          "id": "a6b7c8d9-e0f1-2345-5678-567890123457",
282                                          "messageAfterSubmissionWhenSelected": "",
283                                          "title": "Wrong answer"
284                                        }
285                                      ],
286                                      "order": 0,
287                                      "successMessage": "",
288                                      "title": "What is the correct answer?",
289                                      "body": ""
290                                    }
291                                  ]
292                                })),
293                                vec![],
294                                false,
295                            )),
296                        ),
297                )
298                .chapter(
299                    ChapterBuilder::new(2, "Chapter 2 - Add Lock Later")
300                        .fixed_ids(
301                            cx.v5(b"z1y2x3w4-v5u6-7890-tsrq-pq9876543210"),
302                            cx.v5(b"y2x3w4v5-u6t7-8901-srqp-op8765432109"),
303                        )
304                        .page(
305                            PageBuilder::new("/chapter-2/lock-page", "Lock Chapter Page")
306                                .block(
307                                    GutenbergBlock::block_with_name_attributes_and_inner_blocks(
308                                        "moocfi/lock-chapter",
309                                        attributes! {},
310                                        vec![
311                                            GutenbergBlock::block_with_name_and_attributes(
312                                                "core/heading",
313                                                attributes! {
314                                                    "content": "Model Solution",
315                                                    "level": 2,
316                                                    "anchor": "model-solution-title"
317                                                },
318                                            )
319                                            .with_id(cx.v5(b"g1h2i3j4-k5l6-7890-mnop-qr1234567890")),
320                                            GutenbergBlock::paragraph("Congratulations on completing Chapter 2! Here's a model solution for the exercises.")
321                                                .with_id(cx.v5(b"g2h3i4j5-k6l7-8901-nopq-rs2345678901")),
322                                        ],
323                                    )
324                                    .with_id(cx.v5(b"g3h4i5j6-k7l8-9012-opqr-st3456789012")),
325                                )
326                                .block(
327                                    GutenbergBlock::paragraph("This is Chapter 2. You can lock this chapter when you're done.")
328                                        .with_id(cx.v5(b"g4h5i6j7-k8l9-0123-pqrs-tu4567890123")),
329                                ),
330                        )
331                        .page(
332                            PageBuilder::new("/chapter-2/exercise-page", "Exercise in Chapter 2")
333                                .block(
334                                    GutenbergBlock::paragraph("This is Chapter 2.")
335                                        .with_id(cx.v5(b"x3w4v5u6-t7s8-9012-rqpo-no7654321098")),
336                                )
337                                .exercise(ExerciseBuilder::quizzes(
338                                    "Chapter 2 Exercise",
339                                    ExerciseIds {
340                                        exercise_id: cx.v5(b"w4v5u6t7-s8r9-0123-qpon-mn6543210987"),
341                                        slide_id: cx.v5(b"v5u6t7s8-r9q0-1234-ponm-lm5432109876"),
342                                        task_id: cx.v5(b"u6t7s8r9-q0p1-2345-onml-kl4321098765"),
343                                        block_id: cx.v5(b"t7s8r9q0-p1o2-3456-nmlk-jk3210987654"),
344                                    },
345                                    false,
346                                    None,
347                                    JsonSource::Inline(json!({
348                                      "version": "2",
349                                      "title": "Chapter 2 Exercise",
350                                      "body": "Select the correct answer.",
351                                      "awardPointsEvenIfWrong": false,
352                                      "grantPointsPolicy": "grant_whenever_possible",
353                                      "quizItemDisplayDirection": "vertical",
354                                      "submitMessage": "",
355                                      "items": [
356                                        {
357                                          "type": "multiple-choice",
358                                          "id": "b8c9d0e1-f2a3-4567-6789-678901234567",
359                                          "failureMessage": "",
360                                          "options": [
361                                            {
362                                              "order": 1,
363                                              "additionalCorrectnessExplanationOnModelSolution": "",
364                                              "body": "",
365                                              "correct": true,
366                                              "id": "c9d0e1f2-a3b4-5678-7890-789012345678",
367                                              "messageAfterSubmissionWhenSelected": "",
368                                              "title": "Correct answer"
369                                            },
370                                            {
371                                              "order": 2,
372                                              "additionalCorrectnessExplanationOnModelSolution": "",
373                                              "body": "",
374                                              "correct": false,
375                                              "id": "d0e1f2a3-b4c5-6789-8901-890123456789",
376                                              "messageAfterSubmissionWhenSelected": "",
377                                              "title": "Wrong answer"
378                                            }
379                                          ],
380                                          "order": 0,
381                                          "successMessage": "",
382                                          "title": "What is the correct answer?",
383                                          "body": ""
384                                        }
385                                      ]
386                                    })),
387                                    vec![],
388                                    true,
389                                )),
390                        ),
391                ),
392        );
393
394    let (course, _default_instance, _last_module) = course.seed(&mut conn, &cx).await?;
395
396    Ok(course.id)
397}