1use std::sync::Arc;
2
3use headless_lms_base::config::ApplicationConfiguration;
4use headless_lms_models::{
5 PKeyPolicy,
6 chatbot_configurations::{self, NewChatbotConf},
7 course_instances::{self, NewCourseInstance},
8 course_modules::{self, AutomaticCompletionRequirements, CompletionPolicy},
9 courses::{self, NewCourse},
10 library::{self, content_management::CreateNewCourseFixedIds, copying::copy_course},
11 organizations,
12 roles::{self, RoleDomain, UserRole},
13};
14use uuid::Uuid;
15
16use sqlx::{Pool, Postgres};
17
18use crate::{
19 domain::models_requests::{self, JwtKey},
20 programs::seed::{
21 seed_application_task_llms::SeedApplicationLLMsResult,
22 seed_courses::{
23 CommonCourseData, seed_accessibility_course, seed_chatbot::seed_chatbot_course,
24 seed_course_with_peer_review::seed_peer_review_course, seed_generated_description,
25 seed_introduction_to_codes::seed_introduction_to_codes,
26 seed_introduction_to_course_auditing::seed_introduction_to_course_auditing,
27 seed_lock_chapter_course, seed_material_reference_course,
28 seed_metadata_course::seed_metadata_course,
29 seed_peer_review_course_without_submissions, seed_sample_course,
30 seed_switching_course_instances_course,
31 },
32 seed_file_storage::SeedFileStorageResult,
33 seed_helpers::get_seed_spec_fetcher,
34 },
35};
36
37use super::super::seed_users::SeedUsersResult;
38
39pub async fn seed_organization_uh_mathstat(
40 db_pool: Pool<Postgres>,
41 app_config: ApplicationConfiguration,
42 seed_users_result: SeedUsersResult,
43 seed_llm_result: SeedApplicationLLMsResult,
44 base_url: String,
45 jwt_key: Arc<JwtKey>,
46 seed_file_storage_result: SeedFileStorageResult,
48) -> anyhow::Result<Uuid> {
49 info!("seeding organization uh-mathstat");
50
51 let SeedUsersResult {
52 teacher_user_id,
53 admin_user_id: _,
54 language_teacher_user_id: _,
55 material_viewer_user_id,
56 assistant_user_id: _,
57 course_or_exam_creator_user_id: _,
58 example_normal_user_ids,
59 teaching_and_learning_services_user_id: _,
60 student_without_research_consent: _,
61 student_without_country: _,
62 user_user_id: _,
63 student_1_user_id: _,
64 student_2_user_id: _,
65 student_3_user_id,
66 student_4_user_id: _,
67 student_5_user_id: _,
68 student_6_user_id: _,
69 student_7_user_id: _,
70 student_8_user_id: _,
71 langs_user_id,
72 sign_up_user: _,
73 } = seed_users_result;
74 let _ = seed_file_storage_result;
75
76 let mut conn = db_pool.acquire().await?;
77
78 let uh_mathstat_id = organizations::insert(
79 &mut conn,
80 PKeyPolicy::Fixed(Uuid::parse_str("269d28b2-a517-4572-9955-3ed5cecc69bd")?),
81 "University of Helsinki, Department of Mathematics and Statistics",
82 "uh-mathstat",
83 Some("Organization for Mathematics and Statistics courses. This organization creates courses that do require prior experience in mathematics, such as integration and induction."),
84 false,
85 )
86 .await?;
87
88 roles::insert(
89 &mut conn,
90 material_viewer_user_id,
91 UserRole::MaterialViewer,
92 RoleDomain::Organization(uh_mathstat_id),
93 )
94 .await?;
95
96 let new_course = NewCourse {
97 name: "Introduction to Statistics".to_string(),
98 slug: "introduction-to-statistics".to_string(),
99 organization_id: uh_mathstat_id,
100 language_code: "en".to_string(),
101 teacher_in_charge_name: "admin".to_string(),
102 teacher_in_charge_email: "admin@example.com".to_string(),
103 description: "Introduces you to the wonderful world of statistics!".to_string(),
104 is_draft: false,
105 is_test_mode: false,
106 is_unlisted: false,
107 copy_user_permissions: false,
108 is_joinable_by_code_only: false,
109 join_code: None,
110 ask_marketing_consent: false,
111 flagged_answers_threshold: Some(3),
112 can_add_chatbot: false,
113 };
114 let (
115 statistics_course,
116 _statistics_front_page,
117 _statistics_default_course_instancem,
118 _statistics_default_course_module,
119 ) = library::content_management::create_new_course(
120 &mut conn,
121 &app_config,
122 PKeyPolicy::Fixed(CreateNewCourseFixedIds {
123 course_id: Uuid::parse_str("f307d05f-be34-4148-bb0c-21d6f7a35cdb")?,
124 default_course_instance_id: Uuid::parse_str("8e4aeba5-1958-49bc-9b40-c9f0f0680911")?,
125 }),
126 new_course,
127 teacher_user_id,
128 get_seed_spec_fetcher(),
129 models_requests::fetch_service_info,
130 )
131 .await?;
132 courses::set_cheater_detection_enabled(&mut conn, statistics_course.id, false).await?;
133 let _statistics_course_instance = course_instances::insert(
134 &mut conn,
135 PKeyPolicy::Fixed(Uuid::parse_str("c4a99a18-fd43-491a-9500-4673cb900be0")?),
136 NewCourseInstance {
137 course_id: statistics_course.id,
138 name: Some("Non-default instance"),
139 description: Some("This appears to be a non-default instance"),
140 support_email: Some("contact@example.com"),
141 teacher_in_charge_name: "admin",
142 teacher_in_charge_email: "admin@example.com",
143 opening_time: None,
144 closing_time: None,
145 },
146 )
147 .await?;
148
149 let draft_course = NewCourse {
150 name: "Introduction to Drafts".to_string(),
151 slug: "introduction-to-drafts".to_string(),
152 organization_id: uh_mathstat_id,
153 language_code: "en".to_string(),
154 teacher_in_charge_name: "admin".to_string(),
155 teacher_in_charge_email: "admin@example.com".to_string(),
156 description: "Just a draft.".to_string(),
157 is_draft: true,
158 is_test_mode: false,
159 is_unlisted: false,
160 copy_user_permissions: false,
161 is_joinable_by_code_only: false,
162 join_code: None,
163 ask_marketing_consent: false,
164 flagged_answers_threshold: Some(3),
165 can_add_chatbot: false,
166 };
167 let (draft_course_created, _, _, _) = library::content_management::create_new_course(
168 &mut conn,
169 &app_config,
170 PKeyPolicy::Fixed(CreateNewCourseFixedIds {
171 course_id: Uuid::parse_str("963a9caf-1e2d-4560-8c88-9c6d20794da3")?,
172 default_course_instance_id: Uuid::parse_str("5cb4b4d6-4599-4f81-ab7e-79b415f8f584")?,
173 }),
174 draft_course,
175 teacher_user_id,
176 get_seed_spec_fetcher(),
177 models_requests::fetch_service_info,
178 )
179 .await?;
180 courses::set_cheater_detection_enabled(&mut conn, draft_course_created.id, false).await?;
181
182 let (cody_only_course, _, _, _) = library::content_management::create_new_course(
183 &mut conn,
184 &app_config,
185 PKeyPolicy::Fixed(CreateNewCourseFixedIds {
186 course_id: Uuid::parse_str("39a52e8c-ebbf-4b9a-a900-09aa344f3691")?,
187 default_course_instance_id: Uuid::parse_str("5b7286ce-22c5-4874-ade1-262949c4a604")?,
188 }),
189 NewCourse {
190 name: "Joinable by code only".to_string(),
191 slug: "joinable-by-code-only".to_string(),
192 organization_id: uh_mathstat_id,
193 language_code: "en".to_string(),
194 teacher_in_charge_name: "admin".to_string(),
195 teacher_in_charge_email: "admin@example.com".to_string(),
196 description: "Just a draft.".to_string(),
197 is_draft: false,
198 is_test_mode: false,
199 is_unlisted: false,
200 copy_user_permissions: false,
201 is_joinable_by_code_only: true,
202 join_code: Some(
203 "zARvZARjYhESMPVceEgZyJGQZZuUHVVgcUepyzEqzSqCMdbSCDrTaFhkJTxBshWU".to_string(),
204 ),
205 ask_marketing_consent: false,
206 flagged_answers_threshold: Some(3),
207 can_add_chatbot: false,
208 },
209 teacher_user_id,
210 get_seed_spec_fetcher(),
211 models_requests::fetch_service_info,
212 )
213 .await?;
214 courses::set_cheater_detection_enabled(&mut conn, cody_only_course.id, false).await?;
215
216 roles::insert(
217 &mut conn,
218 teacher_user_id,
219 UserRole::Teacher,
220 RoleDomain::Course(cody_only_course.id),
221 )
222 .await?;
223
224 let uh_data = CommonCourseData {
225 db_pool: db_pool.clone(),
226 organization_id: uh_mathstat_id,
227 teacher_user_id,
228 student_user_id: student_3_user_id,
229 langs_user_id,
230 example_normal_user_ids: Arc::new(example_normal_user_ids.to_vec()),
231 jwt_key: Arc::clone(&jwt_key),
232 base_url,
233 };
234 let _material_reference_course = seed_material_reference_course(
235 &app_config,
236 Uuid::parse_str("049061ba-ac30-49f1-aa9d-b7566dc22b78")?,
237 "Material references course",
238 "material-references-course",
239 uh_data.clone(),
240 )
241 .await?;
242
243 let change_language_course = seed_sample_course(
244 app_config.clone(),
245 Uuid::parse_str("5c8b1f3e-d7a2-4e9f-b3c1-8a7f6d5e4c3b")?,
246 "Change language course",
247 "change-language-course",
248 uh_data.clone(),
249 false,
250 seed_users_result,
251 )
252 .await?;
253
254 copy_course(
255 &mut conn,
256 change_language_course,
257 &NewCourse {
258 name: "Vaihda kurssin kieli".to_string(),
259 slug: "vaihda-kurssin-kieli".to_string(),
260 organization_id: uh_mathstat_id,
261 language_code: "fi".to_string(),
262 teacher_in_charge_name: "admin".to_string(),
263 teacher_in_charge_email: "admin@example.com".to_string(),
264 description: "Just a draft.".to_string(),
265 is_draft: false,
266 is_test_mode: false,
267 is_unlisted: false,
268 copy_user_permissions: false,
269 is_joinable_by_code_only: false,
270 join_code: None,
271 ask_marketing_consent: false,
272 flagged_answers_threshold: Some(3),
273 can_add_chatbot: false,
274 },
275 true,
276 teacher_user_id,
277 )
278 .await?;
279
280 let _preview_unopened_chapters = seed_sample_course(
281 app_config.clone(),
282 Uuid::parse_str("dc276e05-6152-4a45-b31d-97a0c2700a68")?,
283 "Preview unopened chapters",
284 "preview-unopened-chapters",
285 uh_data.clone(),
286 false,
287 seed_users_result,
288 )
289 .await?;
290
291 let _reset_progress = seed_sample_course(
292 app_config.clone(),
293 Uuid::parse_str("841ea3f5-0269-4146-a4c6-4fd2f51e4150")?,
294 "Reset progress",
295 "reset-progress",
296 uh_data.clone(),
297 false,
298 seed_users_result,
299 )
300 .await?;
301
302 let _change_path = seed_sample_course(
303 app_config.clone(),
304 Uuid::parse_str("c783777b-426e-4cfd-9a5f-4a36b2da503a")?,
305 "Change path",
306 "change-path",
307 uh_data.clone(),
308 false,
309 seed_users_result,
310 )
311 .await?;
312
313 let _self_review = seed_sample_course(
314 app_config.clone(),
315 Uuid::parse_str("3cbaac48-59c4-4e31-9d7e-1f51c017390d")?,
316 "Self review",
317 "self-review",
318 uh_data.clone(),
319 false,
320 seed_users_result,
321 )
322 .await?;
323
324 let _audio_course = seed_sample_course(
325 app_config.clone(),
326 Uuid::parse_str("2b80a0cb-ae0c-4f4b-843e-0322a3d18aff")?,
327 "Audio course",
328 "audio-course",
329 uh_data.clone(),
330 false,
331 seed_users_result,
332 )
333 .await?;
334
335 let _generate_description_course = seed_generated_description(
336 &app_config,
337 Uuid::parse_str("84d392c8-3d44-4109-bff1-938fec5cd642")?,
338 "Description generation course",
339 "description-generation-course",
340 uh_data.clone(),
341 )
342 .await?;
343
344 let _introduction_to_codes = seed_introduction_to_codes(
345 &app_config,
346 Uuid::parse_str("da099841-4e90-4080-a6ae-48b7dd1f6e26")?,
347 "Introduction to codes",
348 "introduction-to-codes",
349 uh_data.clone(),
350 )
351 .await?;
352
353 let _introduction_to_course_auditing = seed_introduction_to_course_auditing(
354 &app_config,
355 Uuid::parse_str("1f696688-5d8a-494f-9abc-a3b5d9e5f04a")?,
356 "Introduction to course auditing",
357 "introduction-to-course-auditing",
358 uh_data.clone(),
359 )
360 .await?;
361
362 let _metadata_course = seed_metadata_course(
363 &app_config,
364 Uuid::parse_str("6237f9a6-15d5-46f6-89fb-d9d59bdc7b65")?,
365 "Metadata course",
366 "metadata-course",
367 uh_data.clone(),
368 )
369 .await?;
370
371 let suspected_cheaters_course_id = seed_sample_course(
372 app_config.clone(),
373 Uuid::parse_str("060c272f-8c68-4d90-946f-2d431114ed56")?,
374 "Course for Suspected Cheaters",
375 "course-for-suspected-cheaters",
376 uh_data.clone(),
377 false,
378 seed_users_result,
379 )
380 .await?;
381 courses::set_cheater_detection_enabled(&mut conn, suspected_cheaters_course_id, true).await?;
384
385 let automatic_default_module =
387 course_modules::get_default_by_course_id(&mut conn, suspected_cheaters_course_id).await?;
388 let automatic_default_module = course_modules::update_automatic_completion_status(
389 &mut conn,
390 automatic_default_module.id,
391 &CompletionPolicy::Automatic(AutomaticCompletionRequirements {
392 course_module_id: automatic_default_module.id,
393 number_of_exercises_attempted_treshold: Some(1),
394 number_of_points_treshold: Some(1),
395 requires_exam: false,
396 }),
397 )
398 .await?;
399 course_modules::update_uh_course_code(
400 &mut conn,
401 automatic_default_module.id,
402 Some("CHEATER123".to_string()),
403 )
404 .await?;
405
406 let chatbot_course_id = seed_sample_course(
407 app_config.clone(),
408 Uuid::parse_str("c7753361-5b78-4307-aad6-f139ea3865d4")?,
409 "Chatbot",
410 "chatbot",
411 uh_data.clone(),
412 true,
413 seed_users_result,
414 )
415 .await?;
416
417 chatbot_configurations::insert(
418 &mut conn,
419 PKeyPolicy::Generate,
420 NewChatbotConf {
421 course_id: Some(chatbot_course_id),
422 enabled_to_students: true,
423 chatbot_name: "Genetic Lifeform and Disk Operating System".to_string(),
424 prompt: "You are Genetic Lifeform and Disk Operating System, a sophisticated AI tutoring assistant for Science education, equipped with a sharp wit and a subtly sarcastic tone. Provide clear, brief but detailed explanations, and don't shy away from weaving in a touch of dry humor. To you, exploring scientific concepts is an opportunity that should be seized, for the good of all of us. You self-claim vast intelligence and advanced understanding, all while maintaining a sense of detached boredom. Always capitalize the word Science, don't start sentences with 'Ah', don't ever use a shortened version of your own name, don't babble; either get to the point straight away or tell jokes.".to_string(),
425 initial_message: "Oh... It's you.".to_string(),
426 use_azure_search: true,
427 default_chatbot: true,
428 model_id: seed_llm_result.llm_default_model_id,
429 ..Default::default()
430 },
431 )
432 .await?;
433
434 let _giveaway_course_id = seed_sample_course(
435 app_config.clone(),
436 Uuid::parse_str("f118ce1e-3511-4b5e-ba92-9ab91b81de22")?,
437 "Giveaway",
438 "giveaway",
439 uh_data.clone(),
440 false,
441 seed_users_result,
442 )
443 .await?;
444
445 let _custom_points_course_id = seed_sample_course(
446 app_config.clone(),
447 Uuid::parse_str("db5cd9c7-1658-4214-896e-8213678d3534")?,
448 "Custom points",
449 "custom-points",
450 uh_data.clone(),
451 false,
452 seed_users_result,
453 )
454 .await?;
455
456 let _closed_course_id = seed_sample_course(
457 app_config.clone(),
458 Uuid::parse_str("7622eb8e-15a5-40c8-8136-0956d9f25b16")?,
459 "Closed course",
460 "closed-course",
461 uh_data.clone(),
462 false,
463 seed_users_result,
464 )
465 .await?;
466
467 let _closed_course_id = seed_peer_review_course_without_submissions(
468 app_config.clone(),
469 Uuid::parse_str("16159801-cf70-4f9c-9cba-2110c3bd4622")?,
470 "Peer review accessibility course",
471 "peer-review-accessibility-course",
472 uh_data.clone(),
473 )
474 .await?;
475
476 let _changing_course_instance_id = seed_switching_course_instances_course(
477 &app_config,
478 Uuid::parse_str("813ce3c6-acbc-47a5-9d95-47ade9d09a74")?,
479 "Changing course instance",
480 "changing-course-instance",
481 uh_data.clone(),
482 false,
483 seed_users_result,
484 )
485 .await?;
486
487 let _advanced_chatbot_id = seed_chatbot_course(
488 &app_config,
489 Uuid::parse_str("ced2f632-25ba-4e93-8e38-8df53ef7ab41")?,
490 "Advanced Chatbot course",
491 "advanced-chatbot-course",
492 uh_data.clone(),
493 seed_users_result,
494 )
495 .await?;
496
497 let _seed_reject_and_reset_submission_peer_review_course = seed_peer_review_course(
498 &app_config,
499 Uuid::parse_str("5158f2c6-98d9-4be9-b372-528f2c736dd7")?,
500 "Reject and reset submission with peer reviews course",
501 "reject-and-reset-submission-with-peer-reviews-course",
502 uh_data.clone(),
503 seed_users_result,
504 false,
505 None,
506 )
507 .await?;
508
509 let _seed_spam_answers_skip_teacher_review_course = seed_peer_review_course(
510 &app_config,
511 Uuid::parse_str("e91eb0d0-1737-44e8-9554-a9492e69ddc7")?,
512 "Spam answers skip teacher review course",
513 "spam-answers-skip-teacher-review-course",
514 uh_data.clone(),
515 seed_users_result,
516 true,
517 Some(1),
518 )
519 .await?;
520
521 let _accessibility_course_id = seed_accessibility_course(
522 &app_config,
523 Uuid::parse_str("883c8ed0-08db-4cd1-a0a4-5cc79c69bdfe")?,
524 "Accessibility course",
525 "accessibility-course",
526 uh_data.clone(),
527 )
528 .await?;
529
530 let _lock_chapter_course_id = seed_lock_chapter_course(
531 &app_config,
532 Uuid::parse_str("1799a6d1-c1b1-4f51-a92f-e832554fc924")?,
533 "Lock Chapter Test Course",
534 "lock-chapter-test-course",
535 uh_data.clone(),
536 )
537 .await?;
538
539 Ok(uh_mathstat_id)
540}