Skip to main content

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