headless_lms_server/programs/seed/seed_organizations/
uh_mathstat.rs

1use std::sync::Arc;
2
3use headless_lms_models::{
4    PKeyPolicy,
5    chatbot_configurations::{self, NewChatbotConf},
6    chatbot_configurations_models::{self, NewChatbotConfigurationModel},
7    course_instances::{self, NewCourseInstance},
8    course_modules::{self, AutomaticCompletionRequirements, CompletionPolicy},
9    courses::NewCourse,
10    library::{self, content_management::CreateNewCourseFixedIds, copying::copy_course},
11    organizations,
12    roles::{self, RoleDomain, UserRole},
13};
14use uuid::Uuid;
15
16use sqlx::{Pool, Postgres};
17
18use crate::{
19    domain::models_requests::{self, JwtKey},
20    programs::seed::{
21        seed_courses::{
22            CommonCourseData, seed_chatbot::seed_chatbot_course,
23            seed_course_with_peer_review::seed_peer_review_course,
24            seed_peer_review_course_without_submissions, seed_sample_course,
25            seed_switching_course_instances_course,
26        },
27        seed_file_storage::SeedFileStorageResult,
28        seed_helpers::get_seed_spec_fetcher,
29    },
30};
31
32use super::super::seed_users::SeedUsersResult;
33
34pub async fn seed_organization_uh_mathstat(
35    db_pool: Pool<Postgres>,
36    seed_users_result: SeedUsersResult,
37    base_url: String,
38    jwt_key: Arc<JwtKey>,
39    // Passed to this function to ensure the seed file storage has been ran before this. This function will not work is seed file storage has not been ran
40    seed_file_storage_result: SeedFileStorageResult,
41) -> anyhow::Result<Uuid> {
42    info!("seeding organization uh-mathstat");
43
44    let SeedUsersResult {
45        teacher_user_id,
46        admin_user_id: _,
47        language_teacher_user_id: _,
48        material_viewer_user_id,
49        assistant_user_id: _,
50        course_or_exam_creator_user_id: _,
51        example_normal_user_ids,
52        teaching_and_learning_services_user_id: _,
53        student_without_research_consent: _,
54        student_without_country: _,
55        user_user_id: _,
56        student_1_user_id: _,
57        student_2_user_id: _,
58        student_3_user_id,
59        student_4_user_id: _,
60        student_5_user_id: _,
61        student_6_user_id: _,
62        langs_user_id,
63        sign_up_user: _,
64    } = seed_users_result;
65    let _ = seed_file_storage_result;
66
67    let mut conn = db_pool.acquire().await?;
68
69    let uh_mathstat_id = organizations::insert(
70        &mut conn,
71        PKeyPolicy::Fixed(Uuid::parse_str("269d28b2-a517-4572-9955-3ed5cecc69bd")?),
72        "University of Helsinki, Department of Mathematics and Statistics",
73        "uh-mathstat",
74        Some("Organization for Mathematics and Statistics courses. This organization creates courses that do require prior experience in mathematics, such as integration and induction."),
75        false,
76    )
77    .await?;
78
79    roles::insert(
80        &mut conn,
81        material_viewer_user_id,
82        UserRole::MaterialViewer,
83        RoleDomain::Organization(uh_mathstat_id),
84    )
85    .await?;
86
87    let new_course = NewCourse {
88        name: "Introduction to Statistics".to_string(),
89        slug: "introduction-to-statistics".to_string(),
90        organization_id: uh_mathstat_id,
91        language_code: "en-US".to_string(),
92        teacher_in_charge_name: "admin".to_string(),
93        teacher_in_charge_email: "admin@example.com".to_string(),
94        description: "Introduces you to the wonderful world of statistics!".to_string(),
95        is_draft: false,
96        is_test_mode: false,
97        is_unlisted: false,
98        copy_user_permissions: false,
99        is_joinable_by_code_only: false,
100        join_code: None,
101        ask_marketing_consent: false,
102        flagged_answers_threshold: Some(3),
103        can_add_chatbot: false,
104    };
105    let (
106        statistics_course,
107        _statistics_front_page,
108        _statistics_default_course_instancem,
109        _statistics_default_course_module,
110    ) = library::content_management::create_new_course(
111        &mut conn,
112        PKeyPolicy::Fixed(CreateNewCourseFixedIds {
113            course_id: Uuid::parse_str("f307d05f-be34-4148-bb0c-21d6f7a35cdb")?,
114            default_course_instance_id: Uuid::parse_str("8e4aeba5-1958-49bc-9b40-c9f0f0680911")?,
115        }),
116        new_course,
117        teacher_user_id,
118        get_seed_spec_fetcher(),
119        models_requests::fetch_service_info,
120    )
121    .await?;
122    let _statistics_course_instance = course_instances::insert(
123        &mut conn,
124        PKeyPolicy::Fixed(Uuid::parse_str("c4a99a18-fd43-491a-9500-4673cb900be0")?),
125        NewCourseInstance {
126            course_id: statistics_course.id,
127            name: Some("Non-default instance"),
128            description: Some("This appears to be a non-default instance"),
129            support_email: Some("contact@example.com"),
130            teacher_in_charge_name: "admin",
131            teacher_in_charge_email: "admin@example.com",
132            opening_time: None,
133            closing_time: None,
134        },
135    )
136    .await?;
137
138    let draft_course = NewCourse {
139        name: "Introduction to Drafts".to_string(),
140        slug: "introduction-to-drafts".to_string(),
141        organization_id: uh_mathstat_id,
142        language_code: "en-US".to_string(),
143        teacher_in_charge_name: "admin".to_string(),
144        teacher_in_charge_email: "admin@example.com".to_string(),
145        description: "Just a draft.".to_string(),
146        is_draft: true,
147        is_test_mode: false,
148        is_unlisted: false,
149        copy_user_permissions: false,
150        is_joinable_by_code_only: false,
151        join_code: None,
152        ask_marketing_consent: false,
153        flagged_answers_threshold: Some(3),
154        can_add_chatbot: false,
155    };
156    library::content_management::create_new_course(
157        &mut conn,
158        PKeyPolicy::Fixed(CreateNewCourseFixedIds {
159            course_id: Uuid::parse_str("963a9caf-1e2d-4560-8c88-9c6d20794da3")?,
160            default_course_instance_id: Uuid::parse_str("5cb4b4d6-4599-4f81-ab7e-79b415f8f584")?,
161        }),
162        draft_course,
163        teacher_user_id,
164        get_seed_spec_fetcher(),
165        models_requests::fetch_service_info,
166    )
167    .await?;
168
169    let (cody_only_course, _, _, _) = library::content_management::create_new_course(
170        &mut conn,
171        PKeyPolicy::Fixed(CreateNewCourseFixedIds {
172            course_id: Uuid::parse_str("39a52e8c-ebbf-4b9a-a900-09aa344f3691")?,
173            default_course_instance_id: Uuid::parse_str("5b7286ce-22c5-4874-ade1-262949c4a604")?,
174        }),
175        NewCourse {
176            name: "Joinable by code only".to_string(),
177            slug: "joinable-by-code-only".to_string(),
178            organization_id: uh_mathstat_id,
179            language_code: "en-US".to_string(),
180            teacher_in_charge_name: "admin".to_string(),
181            teacher_in_charge_email: "admin@example.com".to_string(),
182            description: "Just a draft.".to_string(),
183            is_draft: false,
184            is_test_mode: false,
185            is_unlisted: false,
186            copy_user_permissions: false,
187            is_joinable_by_code_only: true,
188            join_code: Some(
189                "zARvZARjYhESMPVceEgZyJGQZZuUHVVgcUepyzEqzSqCMdbSCDrTaFhkJTxBshWU".to_string(),
190            ),
191            ask_marketing_consent: false,
192            flagged_answers_threshold: Some(3),
193            can_add_chatbot: false,
194        },
195        teacher_user_id,
196        get_seed_spec_fetcher(),
197        models_requests::fetch_service_info,
198    )
199    .await?;
200
201    roles::insert(
202        &mut conn,
203        teacher_user_id,
204        UserRole::Teacher,
205        RoleDomain::Course(cody_only_course.id),
206    )
207    .await?;
208
209    let uh_data = CommonCourseData {
210        db_pool: db_pool.clone(),
211        organization_id: uh_mathstat_id,
212        teacher_user_id,
213        student_user_id: student_3_user_id,
214        langs_user_id,
215        example_normal_user_ids: Arc::new(example_normal_user_ids.to_vec()),
216        jwt_key: Arc::clone(&jwt_key),
217        base_url,
218    };
219    let introduction_to_citations = seed_sample_course(
220        Uuid::parse_str("049061ba-ac30-49f1-aa9d-b7566dc22b78")?,
221        "Introduction to citations",
222        "introduction-to-citations",
223        uh_data.clone(),
224        false,
225        seed_users_result,
226    )
227    .await?;
228
229    copy_course(
230        &mut conn,
231        introduction_to_citations,
232        &NewCourse {
233            name: "Johdatus sitaatioihin".to_string(),
234            slug: "johdatus-sitaatioihin".to_string(),
235            organization_id: uh_mathstat_id,
236            language_code: "fi-FI".to_string(),
237            teacher_in_charge_name: "admin".to_string(),
238            teacher_in_charge_email: "admin@example.com".to_string(),
239            description: "Just a draft.".to_string(),
240            is_draft: false,
241            is_test_mode: false,
242            is_unlisted: false,
243            copy_user_permissions: false,
244            is_joinable_by_code_only: false,
245            join_code: None,
246            ask_marketing_consent: false,
247            flagged_answers_threshold: Some(3),
248            can_add_chatbot: false,
249        },
250        true,
251        teacher_user_id,
252    )
253    .await?;
254
255    let _preview_unopened_chapters = seed_sample_course(
256        Uuid::parse_str("dc276e05-6152-4a45-b31d-97a0c2700a68")?,
257        "Preview unopened chapters",
258        "preview-unopened-chapters",
259        uh_data.clone(),
260        false,
261        seed_users_result,
262    )
263    .await?;
264
265    let _reset_progress = seed_sample_course(
266        Uuid::parse_str("841ea3f5-0269-4146-a4c6-4fd2f51e4150")?,
267        "Reset progress",
268        "reset-progress",
269        uh_data.clone(),
270        false,
271        seed_users_result,
272    )
273    .await?;
274
275    let _change_path = seed_sample_course(
276        Uuid::parse_str("c783777b-426e-4cfd-9a5f-4a36b2da503a")?,
277        "Change path",
278        "change-path",
279        uh_data.clone(),
280        false,
281        seed_users_result,
282    )
283    .await?;
284
285    let _self_review = seed_sample_course(
286        Uuid::parse_str("3cbaac48-59c4-4e31-9d7e-1f51c017390d")?,
287        "Self review",
288        "self-review",
289        uh_data.clone(),
290        false,
291        seed_users_result,
292    )
293    .await?;
294
295    let _audio_course = seed_sample_course(
296        Uuid::parse_str("2b80a0cb-ae0c-4f4b-843e-0322a3d18aff")?,
297        "Audio course",
298        "audio-course",
299        uh_data.clone(),
300        false,
301        seed_users_result,
302    )
303    .await?;
304
305    let suspected_cheaters_course_id = seed_sample_course(
306        Uuid::parse_str("060c272f-8c68-4d90-946f-2d431114ed56")?,
307        "Course for Suspected Cheaters",
308        "course-for-suspected-cheaters",
309        uh_data.clone(),
310        false,
311        seed_users_result,
312    )
313    .await?;
314
315    // configure automatic completions
316    let automatic_default_module =
317        course_modules::get_default_by_course_id(&mut conn, suspected_cheaters_course_id).await?;
318    let automatic_default_module = course_modules::update_automatic_completion_status(
319        &mut conn,
320        automatic_default_module.id,
321        &CompletionPolicy::Automatic(AutomaticCompletionRequirements {
322            course_module_id: automatic_default_module.id,
323            number_of_exercises_attempted_treshold: Some(1),
324            number_of_points_treshold: Some(1),
325            requires_exam: false,
326        }),
327    )
328    .await?;
329    course_modules::update_uh_course_code(
330        &mut conn,
331        automatic_default_module.id,
332        Some("CHEATER123".to_string()),
333    )
334    .await?;
335
336    let chatbot_course_id = seed_sample_course(
337        Uuid::parse_str("c7753361-5b78-4307-aad6-f139ea3865d4")?,
338        "Chatbot",
339        "chatbot",
340        uh_data.clone(),
341        true,
342        seed_users_result,
343    )
344    .await?;
345
346    let llm = chatbot_configurations_models::insert(
347        &mut conn,
348        NewChatbotConfigurationModel {
349            id: Uuid::parse_str("f14d70bd-c228-4447-bddd-4f6f66705356")?,
350            model: "mock-gpt".to_string(),
351            thinking: false,
352            default_model: true,
353            deployment_name: "mock-gpt".to_string(),
354        },
355    )
356    .await?;
357
358    chatbot_configurations::insert(
359        &mut conn,
360        PKeyPolicy::Generate,
361        NewChatbotConf {
362            course_id: chatbot_course_id,
363            enabled_to_students: true,
364            chatbot_name: "Genetic Lifeform and Disk Operating System".to_string(),
365            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(),
366            initial_message: "Oh... It's you.".to_string(),
367            use_azure_search: true,
368            default_chatbot: true,
369            model_id: llm.id,
370            thinking_model: llm.thinking,
371            ..Default::default()
372        },
373    )
374    .await?;
375
376    let _giveaway_course_id = seed_sample_course(
377        Uuid::parse_str("f118ce1e-3511-4b5e-ba92-9ab91b81de22")?,
378        "Giveaway",
379        "giveaway",
380        uh_data.clone(),
381        false,
382        seed_users_result,
383    )
384    .await?;
385
386    let _custom_points_course_id = seed_sample_course(
387        Uuid::parse_str("db5cd9c7-1658-4214-896e-8213678d3534")?,
388        "Custom points",
389        "custom-points",
390        uh_data.clone(),
391        false,
392        seed_users_result,
393    )
394    .await?;
395
396    let _closed_course_id = seed_sample_course(
397        Uuid::parse_str("7622eb8e-15a5-40c8-8136-0956d9f25b16")?,
398        "Closed course",
399        "closed-course",
400        uh_data.clone(),
401        false,
402        seed_users_result,
403    )
404    .await?;
405
406    let _closed_course_id = seed_peer_review_course_without_submissions(
407        Uuid::parse_str("16159801-cf70-4f9c-9cba-2110c3bd4622")?,
408        "Accessibility course",
409        "accessibility-course",
410        uh_data.clone(),
411    )
412    .await?;
413
414    let _changing_course_instance_id = seed_switching_course_instances_course(
415        Uuid::parse_str("813ce3c6-acbc-47a5-9d95-47ade9d09a74")?,
416        "Changing course instance",
417        "changing-course-instance",
418        uh_data.clone(),
419        false,
420        seed_users_result,
421    )
422    .await?;
423
424    let _advanced_chatbot_id = seed_chatbot_course(
425        Uuid::parse_str("ced2f632-25ba-4e93-8e38-8df53ef7ab41")?,
426        "Advanced Chatbot course",
427        "advanced-chatbot-course",
428        uh_data.clone(),
429        seed_users_result,
430    )
431    .await?;
432
433    let _seed_reject_and_reset_submission_peer_review_course = seed_peer_review_course(
434        Uuid::parse_str("5158f2c6-98d9-4be9-b372-528f2c736dd7")?,
435        "Reject and reset submission with peer reviews course",
436        "reject-and-reset-submission-with-peer-reviews-course",
437        uh_data.clone(),
438        seed_users_result,
439    )
440    .await?;
441
442    Ok(uh_mathstat_id)
443}