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