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 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 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 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 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 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 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 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 course_or_exam_creator_user_id,
385 UserRole::CourseOrExamCreator,
386 RoleDomain::Organization(uh_cs_organization_id),
387 )
388 .await?;
389
390 info!("inserting sample exams");
391 create_exam(
392 &mut conn,
393 "Ongoing ends soon".to_string(),
394 Some(Utc::now()),
395 Some(Utc::now() + Duration::minutes(1)),
396 120,
397 uh_cs_organization_id,
398 cs_intro,
399 Uuid::parse_str("7d6ed843-2a94-445b-8ced-ab3c67290ad0")?,
400 teacher_user_id,
401 0,
402 false,
403 )
404 .await?;
405 create_exam(
406 &mut conn,
407 "Ongoing short timer".to_string(),
408 Some(Utc::now()),
409 Some(Utc::now() + Duration::minutes(120)),
410 1,
411 uh_cs_organization_id,
412 cs_intro,
413 Uuid::parse_str("6959e7af-6b78-4d37-b381-eef5b7aaad6c")?,
414 teacher_user_id,
415 0,
416 false,
417 )
418 .await?;
419 create_exam(
420 &mut conn,
421 "Ongoing plenty of time".to_string(),
422 Some(Utc::now()),
423 Some(Utc::now() + Duration::minutes(120)),
424 730,
425 uh_cs_organization_id,
426 cs_intro,
427 Uuid::parse_str("8e202d37-3a26-4181-b9e4-0560b90c0ccb")?,
428 teacher_user_id,
429 0,
430 false,
431 )
432 .await?;
433
434 create_exam(
435 &mut conn,
436 "Exam for manual grading".to_string(),
437 Some(Utc::now()),
438 Some(Utc::now() + Duration::minutes(120)),
439 1,
440 uh_cs_organization_id,
441 cs_intro,
442 Uuid::parse_str("fee8bb0c-8629-477c-86eb-1785005143ae")?,
443 teacher_user_id,
444 0,
445 true,
446 )
447 .await?;
448 create_exam(
449 &mut conn,
450 "Starting soon".to_string(),
451 Some(Utc::now() + Duration::minutes(5)),
452 Some(Utc::now() + Duration::days(30)),
453 1,
454 uh_cs_organization_id,
455 cs_intro,
456 Uuid::parse_str("65f5c3f3-b5fd-478d-8858-a45cdcb16b86")?,
457 teacher_user_id,
458 0,
459 false,
460 )
461 .await?;
462 create_exam(
463 &mut conn,
464 "Over".to_string(),
465 Some(Utc::now() - Duration::days(7)),
466 Some(Utc::now() - Duration::minutes(30)),
467 1,
468 uh_cs_organization_id,
469 cs_intro,
470 Uuid::parse_str("5c4fca1f-f0d6-471f-a0fd-eac552f5fb84")?,
471 teacher_user_id,
472 0,
473 false,
474 )
475 .await?;
476 let automatic_course_exam = create_exam(
477 &mut conn,
478 "Automatic course exam".to_string(),
479 Some(Utc::now()),
480 Some(Utc::now() + Duration::minutes(120)),
481 1,
482 uh_cs_organization_id,
483 cs_intro,
484 Uuid::parse_str("b2168b2f-f721-4771-a35d-ca75ca0937b1")?,
485 teacher_user_id,
486 0,
487 false,
488 )
489 .await?;
490 course_exams::upsert(
491 &mut conn,
492 automatic_course_exam,
493 automatic_course_with_exam_id,
494 )
495 .await?;
496
497 info!("cs");
498 let _cs_design = seed_cs_course_material(
499 &db_pool,
500 uh_cs_organization_id,
501 teacher_user_id,
502 langs_user_id,
503 base_url,
504 )
505 .await?;
506 let new_course = NewCourse {
507 name: "Introduction to Computer Science".to_string(),
508 slug: "introduction-to-computer-science".to_string(),
509 organization_id: uh_cs_organization_id,
510 language_code: "en".to_string(),
511 teacher_in_charge_name: "admin".to_string(),
512 teacher_in_charge_email: "admin@example.com".to_string(),
513 description: "An example course.".to_string(),
514 is_draft: false,
515 is_test_mode: false,
516 is_unlisted: false,
517 copy_user_permissions: false,
518 is_joinable_by_code_only: false,
519 join_code: None,
520 ask_marketing_consent: false,
521 flagged_answers_threshold: Some(3),
522 can_add_chatbot: false,
523 };
524 let (cs_course, _cs_front_page, _cs_default_course_instance, _cs_default_course_module) =
525 library::content_management::create_new_course(
526 &mut conn,
527 PKeyPolicy::Fixed(CreateNewCourseFixedIds {
528 course_id: Uuid::parse_str("06a7ccbd-8958-4834-918f-ad7b24e583fd")?,
529 default_course_instance_id: Uuid::parse_str(
530 "48399008-6523-43c5-8fd6-59ecc731a426",
531 )?,
532 }),
533 new_course,
534 teacher_user_id,
535 get_seed_spec_fetcher(),
536 models_requests::fetch_service_info,
537 )
538 .await?;
539 let _cs_course_instance = course_instances::insert(
540 &mut conn,
541 PKeyPolicy::Fixed(Uuid::parse_str("49c618d3-926d-4287-9159-b3af1f86082d")?),
542 NewCourseInstance {
543 course_id: cs_course.id,
544 name: Some("Non-default instance"),
545 description: Some("This is another non-default instance"),
546 support_email: Some("contact@example.com"),
547 teacher_in_charge_name: "admin",
548 teacher_in_charge_email: "admin@example.com",
549 opening_time: None,
550 closing_time: None,
551 },
552 )
553 .await?;
554 Ok(SeedOrganizationUhCsResult {
555 uh_cs_organization_id,
556 cs_intro_course_id: cs_intro,
557 })
558}