headless_lms_server/programs/seed/seed_courses/
seed_chatbot.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::module::ModuleBuilder;
6use crate::programs::seed::builder::page::PageBuilder;
7use crate::programs::seed::seed_courses::CommonCourseData;
8use crate::programs::seed::seed_helpers::{chatbot_block, heading, list, list_item, paragraph};
9use anyhow::Result;
10use chrono::Utc;
11
12use headless_lms_models::chatbot_configurations::NewChatbotConf;
13use headless_lms_models::chatbot_configurations_models;
14use headless_lms_models::roles::UserRole;
15use serde_json::json;
16
17use tracing::info;
18use uuid::Uuid;
19
20use super::super::seed_users::SeedUsersResult;
21
22pub async fn seed_chatbot_course(
23    course_id: Uuid,
24    course_name: &str,
25    course_slug: &str,
26    common_course_data: CommonCourseData,
27    seed_users_result: SeedUsersResult,
28) -> Result<Uuid> {
29    let CommonCourseData {
30        db_pool,
31        organization_id: org,
32        teacher_user_id,
33        student_user_id: _student,
34        langs_user_id: _langs_user_id,
35        example_normal_user_ids: _users,
36        jwt_key: _jwt_key,
37        base_url: _base_url,
38    } = common_course_data;
39
40    let mut conn = db_pool.acquire().await?;
41    let cx = SeedContext {
42        teacher: teacher_user_id,
43        org,
44        base_course_ns: course_id,
45    };
46
47    info!("Inserting sample course {}", course_name);
48
49    let llm = chatbot_configurations_models::get_default(&mut conn).await?;
50
51    let course = CourseBuilder::new(course_name, course_slug)
52        .desc("Sample course for chatbot.")
53        .chatbot(true)
54        .course_id(course_id)
55        .instance(CourseInstanceConfig {
56            name: None,
57            description: None,
58            support_email: None,
59            teacher_in_charge_name: "admin".to_string(),
60            teacher_in_charge_email: "admin@example.com".to_string(),
61            opening_time: None,
62            closing_time: None,
63            instance_id: Some(cx.v5(b"instance:default")),
64        })
65        .chatbot_config(NewChatbotConf {
66            course_id,
67            chatbotconf_id: Some(cx.v5(b"chatbot_config:default")),
68            enabled_to_students: true,
69            chatbot_name: "Genetic Lifeform and Disk Operating System".to_string(),
70            prompt: "You are Genetic Lifeform and Disk Operating System, a sophisticated AI tutoring assistant for Science education, equipped with a sharp wit and a subtly sarcastic tone. Provide clear, brief but detailed explanations, and don't shy away from weaving in a touch of dry humor. To you, exploring scientific concepts is an opportunity that should be seized, for the good of all of us. You self-claim vast intelligence and advanced understanding, all while maintaining a sense of detached boredom. Always capitalize the word Science, don't start sentences with 'Ah', don't ever use a shortened version of your own name, don't babble; either get to the point straight away or tell jokes.".to_string(),
71            initial_message: "Oh... It's you.".to_string(),
72            use_azure_search: true,
73            hide_citations: false,
74            model_id: llm.id,
75            thinking_model: llm.thinking,
76            default_chatbot: true,
77            ..Default::default()
78        })
79        .chatbot_config(NewChatbotConf {
80            course_id,
81            chatbotconf_id: Some(cx.v5(b"chatbot_config:block")),
82            enabled_to_students: true,
83            chatbot_name: "Test bot".to_string(),
84            prompt: "You are Test bot, a sophisticated AI tutoring assistant for Science education, equipped with a sharp wit and a subtly sarcastic tone...".to_string(),
85            initial_message: "Haiii xD What's up?".to_string(),
86            use_azure_search: true,
87            hide_citations: false,
88            model_id: llm.id,
89            thinking_model: llm.thinking,
90            ..Default::default()})
91        .chatbot_config(NewChatbotConf {
92            course_id,
93            chatbotconf_id: Some(cx.v5(b"chatbot_config:extra")),
94            enabled_to_students: true,
95            chatbot_name: "Suggestions bot".to_string(),
96            prompt: "You are a random test bot. Don't overthink it.".to_string(),
97            initial_message: "How are we doing this fine evening?".to_string(),
98            use_azure_search: true,
99            hide_citations: false,
100            model_id: llm.id,
101            thinking_model: llm.thinking,
102            suggest_next_messages: true,
103            initial_suggested_messages: Some(vec!["What is going on?".to_string(), "Tell me more about your fascinating self.".to_string(), "What's the time? What's the time? What's the time? What's the time? What's the time? What's the time? Aaaaaaaaaaaaaaaaaaaaaaaaaaah!".to_string()]),
104            ..Default::default()})
105        .role(seed_users_result.teacher_user_id, UserRole::Teacher)
106        .module(
107            ModuleBuilder::new()
108                .order(0)
109                .register_to_open_university(false)
110                .automatic_completion(Some(1), Some(1), false)
111                .chapter(
112                    ChapterBuilder::new(1, "The Basics")
113                        .opens(Utc::now())
114                        .fixed_ids(cx.v5(b"chapter:1"), cx.v5(b"chapter:1:instance"))
115                        .page(
116                            PageBuilder::new("/chapter-1/page-1", "Page One")
117                                .block(paragraph(
118                                    "This is a simple introduction to the basics.",
119                                    cx.v5(b"page:1:1:block:intro"),
120                                ))
121                                .exercise(ExerciseBuilder::example_exercise(
122                                    "Simple multiple choice",
123                                    ExerciseIds {
124                                        exercise_id: cx.v5(b"exercise:1:1:e"),
125                                        slide_id: cx.v5(b"exercise:1:1:s"),
126                                        task_id: cx.v5(b"exercise:1:1:t"),
127                                        block_id: cx.v5(b"exercise:1:1:b"),
128                                    },
129                                    vec![paragraph(
130                                        "What is 2 + 2?",
131                                        cx.v5(b"exercise:1:1:prompt"),
132                                    )],
133                                    json!([
134                                        {
135                                            "name": "3",
136                                            "correct": false,
137                                            "id": cx.v5(b"exercise:1:1:option:1")
138                                        },
139                                        {
140                                            "name": "4",
141                                            "correct": true,
142                                            "id": cx.v5(b"exercise:1:1:option:2")
143                                        },
144                                        {
145                                            "name": "5",
146                                            "correct": false,
147                                            "id": cx.v5(b"exercise:1:1:option:3")
148                                        }
149                                    ]),
150                                    false,
151                                    None,
152                                    None
153                                )),
154                        )
155                        .page(
156                          PageBuilder::new("/chapter-1/page-2", "Page 2")
157                            .block(chatbot_block(
158                                cx.v5(b"page:1:2:block:chatbox"),
159                                cx.v5(b"chatbot_config:block"),
160                                course_id,
161                            )))
162                        .page(
163                          PageBuilder::new("/chapter-1/page-3", "Page 3")
164                            .block(chatbot_block(
165                                cx.v5(b"page:1:3:block:chatbox"),
166                                cx.v5(b"chatbot_config:extra"),
167                                course_id,
168                            ))),
169                ),
170        )
171        .module(
172            ModuleBuilder::new()
173                .order(1)
174                .name("Another module")
175                .automatic_completion(Some(1), Some(1), false)
176                .ects(5.0)
177                .chapter(
178                    ChapterBuilder::new(2, "Another chapter")
179                        .fixed_ids(cx.v5(b"chapter:2"), cx.v5(b"chapter:2:instance"))
180                        .page(PageBuilder::new("/chapter-2/page-1", "History of the abacus")
181                        .block(heading("The History of the Abacus: Humanity's First Calculator", cx.v5(b"page:2:1:block:intro-heading"), 1))
182                        .block(
183                            paragraph(
184                                "Long before the digital age and even before the invention of written numerals, humans needed a way to count, calculate, and trade. This necessity led to the creation of one of the world’s earliest and most enduring mathematical tools: the **abacus**. Often referred to as the **world's first calculator**, the abacus is not just a relic of the past but a symbol of human ingenuity in the pursuit of numerical understanding.",
185                                cx.v5(b"page:2:1:block:intro"),
186                            )
187                        )
188                        .block(
189                            heading("Origins: The Dawn of Counting", cx.v5(b"page:2:1:block:origins-heading"), 2)
190                        )
191                        .block(
192                            paragraph(
193                                "The exact origins of the abacus are difficult to pinpoint due to the scarcity of early physical evidence. However, historians believe that the concept of the abacus evolved gradually as early humans transitioned from primitive counting methods—like tally marks and using fingers or stones—to more sophisticated systems.", cx.v5(b"page:2:1:block:origins"))
194                        )
195                        .block(
196                            heading("Prehistoric Counting Tools", cx.v5(b"page:2:1:block:prehist-heading"), 3)
197                        )
198                        .block(paragraph(
199                            "Before the invention of formal writing systems, humans used **counting boards**, **knotted ropes**, or lines drawn in the dirt to represent quantities. The **Ishango bone**, dated to around 20,000 years ago and found in Central Africa, features carved notches that some archaeologists believe represent a rudimentary counting system. While not an abacus in the traditional sense, it illustrates the human need to record and manipulate numbers.", cx.v5(b"page:2:1:block:prehist"),)
200                        )
201                        .block(
202                            heading("The Mesopotamian and Egyptian Influence", cx.v5(b"page:2:1:block:meso-heading"), 2)
203                        )
204                        .block(paragraph(
205                            "The **earliest true abacus-like devices** may have emerged in **Mesopotamia** around 2300 BCE. Sumerians used small pebbles or tokens (called **calculi**) on flat surfaces to perform basic arithmetic, especially for commerce and taxation. These were often used in conjunction with **clay tablets**, making them a precursor to what would become the abacus. Similarly, in **Ancient Egypt**, records suggest the use of counting boards with grooves and stones to assist in calculations. Although no physical abacuses from these periods survive, descriptions and artwork hint at their existence.", cx.v5(b"page:2:1:block:meso"),)
206                        )
207                        .block(
208                            heading("The Greek and Roman Counting Boards", cx.v5(b"page:2:1:block:greek-heading"), 2)
209                        )
210                        .block(paragraph(
211                            "By the 5th century BCE, the **Greeks** developed more advanced counting tools. One notable example is the **Salamis Tablet**, discovered on the Greek island of Salamis in 1846. Dating to around 300 BCE, this marble slab features carved lines and Greek numerals, suggesting it was used as a counting board or abacus. The **Romans** adopted and refined the Greek system. They developed portable versions made from bronze or wood, often with grooves and beads. The Roman abacus featured a **dual bead system**: one side represented units (ones, tens, hundreds), and the other represented fives, mirroring their base-10 numerical system with elements of base-5. This system allowed merchants and tax collectors to perform quick and efficient calculations.", cx.v5(b"page:2:1:block:greek"),)
212                        )
213                        .block(
214                            heading("The Chinese Suanpan: A Milestone in Abacus Evolution", cx.v5(b"page:2:1:block:chinese-heading"), 2)
215                        )
216                        .block(paragraph(
217                            "The **Chinese abacus**, known as the **suanpan (算盘)**, emerged around the 2nd century BCE during the Han Dynasty. Unlike earlier counting boards, the suanpan was a complete and portable device, consisting of a wooden frame with rods, each containing beads.", cx.v5(b"page:2:1:block:chinese"),)
218                        )
219                        .block(
220                            heading("Structure and Use", cx.v5(b"page:2:1:block:stru-heading"), 3)
221                        )
222                        .block(list(cx.v5(b"page:2:1:block:stru-list"), false, vec![
223                            list_item(cx.v5(b"page:2:1:block:stru-list1"), "The **traditional suanpan** has **two beads above** and **five beads below** a horizontal dividing bar (known as the beam)."),
224                            list_item(cx.v5(b"page:2:1:block:stru-list2"), "Each rod represents a digit in a decimal place."),
225                            list_item(cx.v5(b"page:2:1:block:stru-list3"), "Beads above the beam have a value of five; those below have a value of one."),
226                            list_item(cx.v5(b"page:2:1:block:stru-list4"), "By moving the beads toward the beam, users could represent numbers and perform addition, subtraction, multiplication, division, and even extract square and cube roots."),
227                            ])
228                        )
229                        .block(paragraph(
230                            "The suanpan was widely used in Chinese society—from market stalls to imperial tax offices—and it became an essential part of Chinese education and commerce for centuries.", cx.v5(b"page:2:1:block:stru"),)
231                        )
232                        .block(
233                            heading("The Japanese Soroban: Simplified Precision", cx.v5(b"page:2:1:block:japanese-heading"), 2)
234                        )
235                        .block(paragraph(
236                            "The **Japanese soroban (そろばん)** was influenced by the Chinese suanpan and introduced to Japan around the 14th century. Over time, Japanese scholars and merchants refined the design, simplifying it for faster and more efficient calculation.", cx.v5(b"page:2:1:block:japanese"),)
237                        )
238                        .block(
239                            heading("Key Differences", cx.v5(b"page:2:1:block:key-heading"), 3)
240                        )
241                        .block(list(cx.v5(b"page:2:1:block:key-list"), false, vec![
242                            list_item(cx.v5(b"page:2:1:block:key-list1"), "The modern soroban typically has **one bead above** and **four beads below** the beam (a 1:4 configuration), unlike the 2:5 layout of the suanpan."),
243                            list_item(cx.v5(b"page:2:1:block:key-list2"), "This change reflects the **decimal system** more clearly and allows easier mental calculations."),
244                            list_item(cx.v5(b"page:2:1:block:key-list3"), "The soroban is smaller, more streamlined, and taught in schools even today as a mental math tool."),
245                            ])
246                        )
247                        .block(paragraph(
248                            "Japanese abacus masters, known as **soroban experts**, have been known to compete with electronic calculators—and sometimes win—in speed and accuracy contests.", cx.v5(b"page:2:1:block:key"),)
249                        )
250                        .block(
251                            heading("The Russian Schoty: A Cultural Twist", cx.v5(b"page:2:1:block:russian-heading"), 2)
252                        )
253                        .block(paragraph(
254                            "Another significant variant is the **Russian abacus**, or **schoty (счёты)**, developed in the 17th century. Unlike the Chinese and Japanese versions, the schoty is:", cx.v5(b"page:2:1:block:russian1"),)
255                        )
256                        .block(list(cx.v5(b"page:2:1:block:russian-list"), false, vec![
257                            list_item(cx.v5(b"page:2:1:block:russian-list1"), "Used **horizontally**, not vertically."),
258                            list_item(cx.v5(b"page:2:1:block:russian-list2"), "Has **10 beads per wire** (except for one wire with four for quarter-ruble calculations)."),
259                            list_item(cx.v5(b"page:2:1:block:russian-list3"), "Operated using **one hand**, usually the right."),
260                            list_item(cx.v5(b"page:2:1:block:russian-list4"), "Primarily used in Russian markets and accounting offices throughout the Soviet era.")])
261                        )
262                        .block(paragraph(
263                            "The schoty reflects a different approach to abacus use but remains a powerful and intuitive calculator for those trained in its methods.", cx.v5(b"page:2:1:block:russian2"),)
264                        )
265                        .block(
266                            heading("The Abacus in the Modern Era", cx.v5(b"page:2:1:block:modern-heading"), 2)
267                        )
268                        .block(paragraph(
269                            "By the 17th century, the invention of **mechanical calculators** (such as Pascal's calculator) and later **electronic calculators** in the 20th century began to replace the abacus in many parts of the world. However, the abacus never truly disappeared.", cx.v5(b"page:2:1:block:modern"),)
270                        )
271                        .block(
272                            heading("Contemporary Uses", cx.v5(b"page:2:1:block:conte-heading"), 3)
273                        )
274                        .block(
275                            list(cx.v5(b"page:2:1:block:conte-list"), false, vec![list_item(cx.v5(b"page:2:1:block:conte-list1"), "In many Asian countries, especially **China, Japan, and Taiwan**, the abacus is still taught to schoolchildren."), list_item(cx.v5(b"page:2:1:block:conte-list2"), "It’s used to develop **mental arithmetic** skills, memory, and concentration."), list_item(cx.v5(b"page:2:1:block:conte-list3"), "Abacus competitions are held globally, where students perform lightning-fast calculations."), list_item(cx.v5(b"page:2:1:block:conte-list4"), "The abacus is also used as an educational tool for **visually impaired** students due to its tactile nature.")])
276                        )
277                        .block(
278                            heading("The Abacus as a Cultural Icon", cx.v5(b"page:2:1:block:cultu-heading"), 2)
279                        )
280                        .block(paragraph(
281                            "More than just a calculating device, the abacus has become a cultural symbol:", cx.v5(b"page:2:1:block:cultu"),)
282                        )
283                        .block(list(cx.v5(b"page:2:1:block:cultu-list"), false, vec![list_item(cx.v5(b"page:2:1:block:cultu-list1"), "In **Chinese culture**, the abacus symbolizes **wisdom and financial acumen**."), list_item(cx.v5(b"page:2:1:block:cultu-list2"), "In Japan, the soroban is associated with **discipline and mental strength**."), list_item(cx.v5(b"page:2:1:block:cultu-list3"), "The image of the abacus is often used in logos and iconography related to **mathematics, finance, and education**.")]))
284                        .block(
285                            heading("Conclusion: The Legacy of the Abacus", cx.v5(b"page:2:1:block:concl-heading"), 2)
286                        )
287                        .block(paragraph(
288                            "The abacus has endured for thousands of years, evolving across continents and cultures. From Mesopotamian merchants to modern-day students mastering mental math, it has remained a testament to human creativity and the universal need to understand and manipulate numbers. In an age where technology continues to advance at lightning speed, the abacus reminds us that sometimes, the simplest tools can be the most profound. Whether made of beads, wood, or stone, the abacus is a bridge between the ancient and the modern—an enduring legacy of humankind’s quest for knowledge.", cx.v5(b"page:2:1:block:concl"),)
289                        )
290                    ),
291                ),
292        );
293
294    let (course, _default_instance, _last_module) = course.seed(&mut conn, &cx).await?;
295
296    Ok(course.id)
297}