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