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 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 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 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 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 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 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 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}