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