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