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