Skip to main content

headless_lms_server/programs/seed/seed_organizations/
uh_cs.rs

1use std::sync::Arc;
2
3use chrono::{Duration, TimeZone, Utc};
4use futures::try_join;
5use headless_lms_models::{
6    PKeyPolicy, course_exams,
7    course_instances::{self, NewCourseInstance},
8    course_modules::{self, AutomaticCompletionRequirements, CompletionPolicy},
9    courses::NewCourse,
10    library::{
11        self,
12        content_management::CreateNewCourseFixedIds,
13        progressing::{TeacherManualCompletion, TeacherManualCompletionRequest},
14    },
15    open_university_registration_links, organizations,
16    roles::{self, RoleDomain, UserRole},
17};
18use headless_lms_utils::futures::run_parallelly;
19use uuid::Uuid;
20
21use crate::{
22    domain::models_requests::{self, JwtKey},
23    programs::seed::{
24        seed_courses::{
25            CommonCourseData, seed_cs_course_material, seed_glossary, seed_graded_course,
26            seed_peer_review_course_without_submissions, seed_sample_course,
27        },
28        seed_file_storage::SeedFileStorageResult,
29        seed_helpers::{create_exam, get_seed_spec_fetcher},
30    },
31};
32
33use super::super::seed_users::SeedUsersResult;
34use sqlx::{Pool, Postgres};
35
36pub struct SeedOrganizationUhCsResult {
37    pub uh_cs_organization_id: Uuid,
38    pub cs_intro_course_id: Uuid,
39}
40
41pub async fn seed_organization_uh_cs(
42    db_pool: Pool<Postgres>,
43    seed_users_result: SeedUsersResult,
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<SeedOrganizationUhCsResult> {
49    info!("inserting organization uh-cs");
50    let SeedUsersResult {
51        admin_user_id: _,
52        teacher_user_id,
53        language_teacher_user_id,
54        material_viewer_user_id,
55        assistant_user_id: _,
56        course_or_exam_creator_user_id,
57        example_normal_user_ids,
58        teaching_and_learning_services_user_id: _,
59        student_without_research_consent: _,
60        student_without_country: _,
61        user_user_id: _,
62        student_1_user_id: _,
63        student_2_user_id: _,
64        student_3_user_id,
65        student_4_user_id: _,
66        student_5_user_id: _,
67        student_6_user_id: _,
68        student_7_user_id: _,
69        student_8_user_id: _,
70        langs_user_id,
71        sign_up_user: _,
72    } = seed_users_result;
73    let _ = seed_file_storage_result;
74
75    let mut conn = db_pool.acquire().await?;
76
77    let uh_cs_organization_id = organizations::insert(
78        &mut conn,
79        PKeyPolicy::Fixed(Uuid::parse_str("8bb12295-53ac-4099-9644-ac0ff5e34d92")?),
80        "University of Helsinki, Department of Computer Science",
81        "uh-cs",
82        Some("Organization for Computer Science students and the rest of the world who wish to learn the basics in Computer Science, programming and software development."),
83        false,
84    )
85    .await?;
86
87    roles::insert(
88        &mut conn,
89        material_viewer_user_id,
90        UserRole::MaterialViewer,
91        RoleDomain::Organization(uh_cs_organization_id),
92    )
93    .await?;
94
95    info!("inserting uh-cs courses");
96
97    // Seed courses in groups to improve performance. We cannot create a new task for each course because it is causing stack overflows in headless-lms entrypoint in seemingly unrelated code.
98    let cs_data = CommonCourseData {
99        db_pool: db_pool.clone(),
100        organization_id: uh_cs_organization_id,
101        teacher_user_id,
102        student_user_id: student_3_user_id,
103        langs_user_id,
104        example_normal_user_ids: Arc::new(example_normal_user_ids.to_vec()),
105        jwt_key: Arc::clone(&jwt_key),
106        base_url: base_url.clone(),
107    };
108    let (
109        cs_intro,
110        automatic_completions_id,
111        introduction_to_localizing,
112        manual_completions_id,
113        automatic_course_with_exam_id,
114        certificates_id,
115        ..,
116        _graded_demo_course_id,
117    ) = try_join!(
118        // using these ids
119        run_parallelly(seed_sample_course(
120            Uuid::parse_str("7f36cf71-c2d2-41fc-b2ae-bbbcafab0ea5")?,
121            "Introduction to everything",
122            "introduction-to-everything",
123            cs_data.clone(),
124            false,
125            seed_users_result,
126        )),
127        run_parallelly(seed_sample_course(
128            Uuid::parse_str("b39b64f3-7718-4556-ac2b-333f3ed4096f")?,
129            "Automatic Completions",
130            "automatic-completions",
131            cs_data.clone(),
132            false,
133            seed_users_result,
134        )),
135        run_parallelly(seed_sample_course(
136            Uuid::parse_str("639f4d25-9376-49b5-bcca-7cba18c38565")?,
137            "Introduction to localizing",
138            "introduction-to-localizing",
139            cs_data.clone(),
140            false,
141            seed_users_result,
142        )),
143        run_parallelly(seed_sample_course(
144            Uuid::parse_str("34f4e7b7-9f55-48a7-95d7-3fc3e89553b5")?,
145            "Manual Completions",
146            "manual-completions",
147            cs_data.clone(),
148            false,
149            seed_users_result,
150        )),
151        run_parallelly(seed_sample_course(
152            Uuid::parse_str("260b2157-94ad-4791-91c7-f236f203c338")?,
153            "Automatic Course with Exam",
154            "automatic-course-with-exam",
155            cs_data.clone(),
156            false,
157            seed_users_result,
158        )),
159        run_parallelly(seed_sample_course(
160            Uuid::parse_str("51ce5ea4-2587-407e-bea9-421309f77f69")?,
161            "Certificates",
162            "certificates",
163            cs_data.clone(),
164            false,
165            seed_users_result,
166        )),
167        // not using these ids
168        run_parallelly(seed_graded_course(
169            Uuid::parse_str("1c9f9ba8-aaaa-4aa4-bccb-123654abcabc")?,
170            "Graded Demo Course",
171            "graded-demo-course",
172            cs_data.clone(),
173            seed_users_result,
174        )),
175        run_parallelly(seed_sample_course(
176            Uuid::parse_str("4dde368a-5e5d-4001-b8aa-13079390f818")?,
177            "Model solutions",
178            "model-solutions",
179            cs_data.clone(),
180            false,
181            seed_users_result,
182        )),
183        run_parallelly(seed_sample_course(
184            Uuid::parse_str("edaa1c52-15cd-458d-8ce2-1e4010641244")?,
185            "Course Modules",
186            "course-modules",
187            cs_data.clone(),
188            false,
189            seed_users_result,
190        )),
191        run_parallelly(seed_sample_course(
192            Uuid::parse_str("d18b3780-563d-4326-b311-8d0e132901cd")?,
193            "Introduction to feedback",
194            "introduction-to-feedback",
195            cs_data.clone(),
196            false,
197            seed_users_result,
198        )),
199        run_parallelly(seed_sample_course(
200            Uuid::parse_str("0ab2c4c5-3aad-4daa-a8fe-c26e956fde35")?,
201            "Introduction to history",
202            "introduction-to-history",
203            cs_data.clone(),
204            false,
205            seed_users_result,
206        )),
207        run_parallelly(seed_sample_course(
208            Uuid::parse_str("cae7da38-9486-47da-9106-bff9b6a280f2")?,
209            "Introduction to edit proposals",
210            "introduction-to-edit-proposals",
211            cs_data.clone(),
212            false,
213            seed_users_result,
214        )),
215        run_parallelly(seed_sample_course(
216            Uuid::parse_str("b4cb334c-11d6-4e93-8f3d-849c4abfcd67")?,
217            "Point view for teachers",
218            "point-view-for-teachers",
219            cs_data.clone(),
220            false,
221            seed_users_result,
222        )),
223        run_parallelly(seed_sample_course(
224            Uuid::parse_str("1e0c52c7-8cb9-4089-b1c3-c24fc0dd5ae4")?,
225            "Advanced course instance management",
226            "advanced-course-instance-management",
227            cs_data.clone(),
228            false,
229            seed_users_result,
230        )),
231        run_parallelly(seed_sample_course(
232            Uuid::parse_str("0cf67777-0edb-480c-bdb6-13f90c136fc3")?,
233            "Advanced exercise states",
234            "advanced-exercise-states",
235            cs_data.clone(),
236            false,
237            seed_users_result,
238        )),
239        run_parallelly(seed_glossary::seed_glossary_course(
240            Uuid::parse_str("c218ca00-dbde-4b0c-ab98-4f075c49425a")?,
241            "Glossary course",
242            "glossary-course",
243            cs_data.clone(),
244            seed_users_result,
245        )),
246        run_parallelly(seed_sample_course(
247            Uuid::parse_str("a2002fc3-2c87-4aae-a5e5-9d14617aad2b")?,
248            "Permission management",
249            "permission-management",
250            cs_data.clone(),
251            false,
252            seed_users_result,
253        )),
254        run_parallelly(seed_sample_course(
255            Uuid::parse_str("f9579c00-d0bb-402b-affd-7db330dcb11f")?,
256            "Redirections",
257            "redirections",
258            cs_data.clone(),
259            false,
260            seed_users_result,
261        )),
262        run_parallelly(seed_sample_course(
263            Uuid::parse_str("9da60c66-9517-46e4-b351-07d0f7aa6cd4")?,
264            "Limited tries",
265            "limited-tries",
266            cs_data.clone(),
267            false,
268            seed_users_result,
269        )),
270        run_parallelly(seed_sample_course(
271            Uuid::parse_str("86cbc198-601c-42f4-8e0f-3e6cce49bbfc")?,
272            "Course Structure",
273            "course-structure",
274            cs_data.clone(),
275            false,
276            seed_users_result,
277        )),
278        run_parallelly(seed_peer_review_course_without_submissions(
279            Uuid::parse_str("c47e1cfd-a2da-4fd1-aca8-f2b2d906c4c0")?,
280            "Peer review Course",
281            "peer-review-course",
282            cs_data.clone(),
283        )),
284        run_parallelly(seed_sample_course(
285            Uuid::parse_str("557040ea-31bc-47ae-81bd-caeec45a08d0")?,
286            "TestMyCode",
287            "tmc-course",
288            cs_data.clone(),
289            false,
290            seed_users_result
291        )),
292    )?;
293
294    // configure automatic completions
295    let automatic_default_module =
296        course_modules::get_default_by_course_id(&mut conn, automatic_completions_id).await?;
297    let automatic_default_module = course_modules::update_automatic_completion_status(
298        &mut conn,
299        automatic_default_module.id,
300        &CompletionPolicy::Automatic(AutomaticCompletionRequirements {
301            course_module_id: automatic_default_module.id,
302            number_of_exercises_attempted_treshold: Some(1),
303            number_of_points_treshold: Some(1),
304            requires_exam: false,
305        }),
306    )
307    .await?;
308    course_modules::update_uh_course_code(
309        &mut conn,
310        automatic_default_module.id,
311        Some("EXAMPLE123".to_string()),
312    )
313    .await?;
314    let automatic_with_exam_default_module =
315        course_modules::get_default_by_course_id(&mut conn, automatic_course_with_exam_id).await?;
316    let automatic_with_exam_default_module = course_modules::update_automatic_completion_status(
317        &mut conn,
318        automatic_with_exam_default_module.id,
319        &CompletionPolicy::Automatic(AutomaticCompletionRequirements {
320            course_module_id: automatic_with_exam_default_module.id,
321            number_of_exercises_attempted_treshold: Some(1),
322            number_of_points_treshold: Some(1),
323            requires_exam: true,
324        }),
325    )
326    .await?;
327    course_modules::update_uh_course_code(
328        &mut conn,
329        automatic_with_exam_default_module.id,
330        Some("EXAMPLE123".to_string()),
331    )
332    .await?;
333    open_university_registration_links::upsert(&mut conn, "EXAMPLE123", "https://www.example.com")
334        .await?;
335
336    // configure manual completions
337    let manual_default_module =
338        course_modules::get_default_by_course_id(&mut conn, manual_completions_id).await?;
339    let manual_default_instance =
340        course_instances::get_default_by_course_id(&mut conn, manual_completions_id).await?;
341    library::progressing::add_manual_completions(
342        &mut conn,
343        teacher_user_id,
344        &manual_default_instance,
345        &TeacherManualCompletionRequest {
346            course_module_id: manual_default_module.id,
347            new_completions: vec![TeacherManualCompletion {
348                user_id: *example_normal_user_ids.first().unwrap(),
349                grade: Some(4),
350                passed: true,
351                completion_date: Some(Utc.with_ymd_and_hms(2022, 9, 1, 0, 0, 0).unwrap()),
352            }],
353            skip_duplicate_completions: true,
354        },
355    )
356    .await?;
357
358    // configure certification
359    let certificates_default_module =
360        course_modules::get_default_by_course_id(&mut conn, certificates_id).await?;
361    course_modules::update_automatic_completion_status(
362        &mut conn,
363        certificates_default_module.id,
364        &CompletionPolicy::Automatic(AutomaticCompletionRequirements {
365            course_module_id: certificates_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_certification_enabled(&mut conn, certificates_default_module.id, true)
373        .await?;
374
375    roles::insert(
376        &mut conn,
377        language_teacher_user_id,
378        UserRole::Teacher,
379        RoleDomain::Course(introduction_to_localizing),
380    )
381    .await?;
382    roles::insert(
383        &mut conn,
384        language_teacher_user_id,
385        UserRole::Teacher,
386        RoleDomain::Organization(uh_cs_organization_id),
387    )
388    .await?;
389    roles::insert(
390        &mut conn,
391        course_or_exam_creator_user_id,
392        UserRole::CourseOrExamCreator,
393        RoleDomain::Organization(uh_cs_organization_id),
394    )
395    .await?;
396
397    info!("inserting sample exams");
398    create_exam(
399        &mut conn,
400        "Ongoing ends soon".to_string(),
401        Some(Utc::now()),
402        Some(Utc::now() + Duration::minutes(1)),
403        120,
404        uh_cs_organization_id,
405        cs_intro,
406        Uuid::parse_str("7d6ed843-2a94-445b-8ced-ab3c67290ad0")?,
407        teacher_user_id,
408        0,
409        false,
410    )
411    .await?;
412    create_exam(
413        &mut conn,
414        "Ongoing short timer".to_string(),
415        Some(Utc::now()),
416        Some(Utc::now() + Duration::minutes(120)),
417        1,
418        uh_cs_organization_id,
419        cs_intro,
420        Uuid::parse_str("6959e7af-6b78-4d37-b381-eef5b7aaad6c")?,
421        teacher_user_id,
422        0,
423        false,
424    )
425    .await?;
426    create_exam(
427        &mut conn,
428        "Ongoing plenty of time".to_string(),
429        Some(Utc::now()),
430        Some(Utc::now() + Duration::minutes(120)),
431        730,
432        uh_cs_organization_id,
433        cs_intro,
434        Uuid::parse_str("8e202d37-3a26-4181-b9e4-0560b90c0ccb")?,
435        teacher_user_id,
436        0,
437        false,
438    )
439    .await?;
440
441    create_exam(
442        &mut conn,
443        "Exam for manual grading".to_string(),
444        Some(Utc::now()),
445        Some(Utc::now() + Duration::minutes(120)),
446        1,
447        uh_cs_organization_id,
448        cs_intro,
449        Uuid::parse_str("fee8bb0c-8629-477c-86eb-1785005143ae")?,
450        teacher_user_id,
451        0,
452        true,
453    )
454    .await?;
455    create_exam(
456        &mut conn,
457        "Starting soon".to_string(),
458        Some(Utc::now() + Duration::minutes(5)),
459        Some(Utc::now() + Duration::days(30)),
460        1,
461        uh_cs_organization_id,
462        cs_intro,
463        Uuid::parse_str("65f5c3f3-b5fd-478d-8858-a45cdcb16b86")?,
464        teacher_user_id,
465        0,
466        false,
467    )
468    .await?;
469    create_exam(
470        &mut conn,
471        "Over".to_string(),
472        Some(Utc::now() - Duration::days(7)),
473        Some(Utc::now() - Duration::minutes(30)),
474        1,
475        uh_cs_organization_id,
476        cs_intro,
477        Uuid::parse_str("5c4fca1f-f0d6-471f-a0fd-eac552f5fb84")?,
478        teacher_user_id,
479        0,
480        false,
481    )
482    .await?;
483    let automatic_course_exam = create_exam(
484        &mut conn,
485        "Automatic course exam".to_string(),
486        Some(Utc::now()),
487        Some(Utc::now() + Duration::minutes(120)),
488        1,
489        uh_cs_organization_id,
490        cs_intro,
491        Uuid::parse_str("b2168b2f-f721-4771-a35d-ca75ca0937b1")?,
492        teacher_user_id,
493        0,
494        false,
495    )
496    .await?;
497    course_exams::upsert(
498        &mut conn,
499        automatic_course_exam,
500        automatic_course_with_exam_id,
501    )
502    .await?;
503
504    info!("cs");
505    let _cs_design = seed_cs_course_material(
506        &db_pool,
507        uh_cs_organization_id,
508        teacher_user_id,
509        langs_user_id,
510        base_url,
511    )
512    .await?;
513    let new_course = NewCourse {
514        name: "Introduction to Computer Science".to_string(),
515        slug: "introduction-to-computer-science".to_string(),
516        organization_id: uh_cs_organization_id,
517        language_code: "en".to_string(),
518        teacher_in_charge_name: "admin".to_string(),
519        teacher_in_charge_email: "admin@example.com".to_string(),
520        description: "An example course.".to_string(),
521        is_draft: false,
522        is_test_mode: false,
523        is_unlisted: false,
524        copy_user_permissions: false,
525        is_joinable_by_code_only: false,
526        join_code: None,
527        ask_marketing_consent: false,
528        flagged_answers_threshold: Some(3),
529        can_add_chatbot: false,
530    };
531    let (cs_course, _cs_front_page, _cs_default_course_instance, _cs_default_course_module) =
532        library::content_management::create_new_course(
533            &mut conn,
534            PKeyPolicy::Fixed(CreateNewCourseFixedIds {
535                course_id: Uuid::parse_str("06a7ccbd-8958-4834-918f-ad7b24e583fd")?,
536                default_course_instance_id: Uuid::parse_str(
537                    "48399008-6523-43c5-8fd6-59ecc731a426",
538                )?,
539            }),
540            new_course,
541            teacher_user_id,
542            get_seed_spec_fetcher(),
543            models_requests::fetch_service_info,
544        )
545        .await?;
546    let _cs_course_instance = course_instances::insert(
547        &mut conn,
548        PKeyPolicy::Fixed(Uuid::parse_str("49c618d3-926d-4287-9159-b3af1f86082d")?),
549        NewCourseInstance {
550            course_id: cs_course.id,
551            name: Some("Non-default instance"),
552            description: Some("This is another non-default instance"),
553            support_email: Some("contact@example.com"),
554            teacher_in_charge_name: "admin",
555            teacher_in_charge_email: "admin@example.com",
556            opening_time: None,
557            closing_time: None,
558        },
559    )
560    .await?;
561    Ok(SeedOrganizationUhCsResult {
562        uh_cs_organization_id,
563        cs_intro_course_id: cs_intro,
564    })
565}