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