Skip to main content

headless_lms_server/programs/seed/seed_organizations/
uh_mathstat.rs

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