1use crate::{
2 error::Result,
3 http::Requester,
4 pagination::PageStream,
5 params::wrap_params,
6 resources::{
7 assignment::Assignment,
8 blueprint::{BlueprintSubscription, BlueprintTemplate},
9 content_export::{ContentExport, ContentExportParams},
10 content_migration::{ContentMigration, Migrator},
11 discussion_topic::DiscussionTopic,
12 enrollment::Enrollment,
13 external_tool::{ExternalTool, ExternalToolParams},
14 feature::{Feature, FeatureFlag},
15 file::File,
16 grade_change_log::GradeChangeEvent,
17 gradebook_history::{Day, Grader, SubmissionHistory, SubmissionVersion},
18 grading_period::GradingPeriod,
19 grading_standard::GradingStandard,
20 group::Group,
21 module::Module,
22 outcome::{OutcomeGroup, OutcomeLink, UpdateOutcomeGroupParams},
23 page::Page,
24 params::{
25 assignment_params::CreateAssignmentParams, course_params::UpdateCourseParams,
26 quiz_params::CreateQuizParams,
27 },
28 quiz::Quiz,
29 rubric::{Rubric, RubricAssociation, RubricParams},
30 section::Section,
31 tab::Tab,
32 types::WorkflowState,
33 user::User,
34 },
35};
36use chrono::{DateTime, Utc};
37use serde::{Deserialize, Serialize};
38use std::sync::Arc;
39
40#[derive(Debug, Clone, Deserialize, Serialize)]
41pub struct Course {
42 pub id: u64,
43 pub name: Option<String>,
44 pub course_code: Option<String>,
45 pub workflow_state: Option<WorkflowState>,
46 pub account_id: Option<u64>,
47 pub root_account_id: Option<u64>,
48 pub enrollment_term_id: Option<u64>,
49 pub sis_course_id: Option<String>,
50 pub start_at: Option<DateTime<Utc>>,
51 pub end_at: Option<DateTime<Utc>>,
52 pub grading_standard_id: Option<u64>,
53 pub is_public: Option<bool>,
54 pub license: Option<String>,
55 pub locale: Option<String>,
56 pub time_zone: Option<String>,
57 pub total_students: Option<u64>,
58 pub default_view: Option<String>,
59 pub syllabus_body: Option<String>,
60 pub public_description: Option<String>,
61 pub hide_final_grades: Option<bool>,
62 pub apply_assignment_group_weights: Option<bool>,
63 pub restrict_enrollments_to_course_dates: Option<bool>,
64
65 #[serde(skip)]
66 pub(crate) requester: Option<Arc<Requester>>,
67}
68
69impl Course {
70 fn req(&self) -> &Arc<Requester> {
71 self.requester.as_ref().expect("requester not initialized")
72 }
73
74 pub fn get_assignments(&self) -> PageStream<Assignment> {
79 PageStream::new(
80 Arc::clone(self.req()),
81 &format!("courses/{}/assignments", self.id),
82 vec![],
83 )
84 }
85
86 pub async fn get_assignment(&self, assignment_id: u64) -> Result<Assignment> {
91 self.req()
92 .get(
93 &format!("courses/{}/assignments/{assignment_id}", self.id),
94 &[],
95 )
96 .await
97 }
98
99 pub async fn create_assignment(&self, params: CreateAssignmentParams) -> Result<Assignment> {
104 let form = wrap_params("assignment", ¶ms);
105 self.req()
106 .post(&format!("courses/{}/assignments", self.id), &form)
107 .await
108 }
109
110 pub fn get_sections(&self) -> PageStream<Section> {
115 PageStream::new(
116 Arc::clone(self.req()),
117 &format!("courses/{}/sections", self.id),
118 vec![],
119 )
120 }
121
122 pub async fn get_section(&self, section_id: u64) -> Result<Section> {
127 self.req()
128 .get(&format!("courses/{}/sections/{section_id}", self.id), &[])
129 .await
130 }
131
132 pub fn get_enrollments(&self) -> PageStream<Enrollment> {
137 PageStream::new(
138 Arc::clone(self.req()),
139 &format!("courses/{}/enrollments", self.id),
140 vec![],
141 )
142 }
143
144 pub fn get_users(&self) -> PageStream<User> {
149 PageStream::new(
150 Arc::clone(self.req()),
151 &format!("courses/{}/users", self.id),
152 vec![],
153 )
154 }
155
156 pub async fn update(&self, params: UpdateCourseParams) -> Result<Course> {
161 let form = wrap_params("course", ¶ms);
162 let mut course: Course = self
163 .req()
164 .put(&format!("courses/{}", self.id), &form)
165 .await?;
166 course.requester = self.requester.clone();
167 Ok(course)
168 }
169
170 pub async fn delete(&self) -> Result<Course> {
175 let params = vec![("event".to_string(), "delete".to_string())];
176 let mut course: Course = self
177 .req()
178 .delete(&format!("courses/{}", self.id), ¶ms)
179 .await?;
180 course.requester = self.requester.clone();
181 Ok(course)
182 }
183
184 pub fn get_quizzes(&self) -> PageStream<Quiz> {
189 PageStream::new(
190 Arc::clone(self.req()),
191 &format!("courses/{}/quizzes", self.id),
192 vec![],
193 )
194 }
195
196 pub async fn get_quiz(&self, quiz_id: u64) -> Result<Quiz> {
201 self.req()
202 .get(&format!("courses/{}/quizzes/{quiz_id}", self.id), &[])
203 .await
204 }
205
206 pub async fn create_quiz(&self, params: CreateQuizParams) -> Result<Quiz> {
211 let form = wrap_params("quiz", ¶ms);
212 self.req()
213 .post(&format!("courses/{}/quizzes", self.id), &form)
214 .await
215 }
216
217 pub fn get_modules(&self) -> PageStream<Module> {
222 PageStream::new(
223 Arc::clone(self.req()),
224 &format!("courses/{}/modules", self.id),
225 vec![],
226 )
227 }
228
229 pub async fn get_module(&self, module_id: u64) -> Result<Module> {
234 self.req()
235 .get(&format!("courses/{}/modules/{module_id}", self.id), &[])
236 .await
237 }
238
239 pub fn get_pages(&self) -> PageStream<Page> {
244 PageStream::new(
245 Arc::clone(self.req()),
246 &format!("courses/{}/pages", self.id),
247 vec![],
248 )
249 }
250
251 pub async fn get_page(&self, url_or_id: &str) -> Result<Page> {
256 self.req()
257 .get(&format!("courses/{}/pages/{url_or_id}", self.id), &[])
258 .await
259 }
260
261 pub fn get_discussion_topics(&self) -> PageStream<DiscussionTopic> {
266 PageStream::new(
267 Arc::clone(self.req()),
268 &format!("courses/{}/discussion_topics", self.id),
269 vec![],
270 )
271 }
272
273 pub async fn get_discussion_topic(&self, topic_id: u64) -> Result<DiscussionTopic> {
278 self.req()
279 .get(
280 &format!("courses/{}/discussion_topics/{topic_id}", self.id),
281 &[],
282 )
283 .await
284 }
285
286 pub fn get_files(&self) -> PageStream<File> {
291 PageStream::new(
292 Arc::clone(self.req()),
293 &format!("courses/{}/files", self.id),
294 vec![],
295 )
296 }
297
298 pub fn get_tabs(&self) -> PageStream<Tab> {
303 PageStream::new(
304 Arc::clone(self.req()),
305 &format!("courses/{}/tabs", self.id),
306 vec![],
307 )
308 }
309
310 pub fn get_groups(&self) -> PageStream<Group> {
315 PageStream::new(
316 Arc::clone(self.req()),
317 &format!("courses/{}/groups", self.id),
318 vec![],
319 )
320 }
321
322 pub async fn upload_file(
330 &self,
331 request: crate::upload::UploadRequest,
332 data: Vec<u8>,
333 ) -> crate::error::Result<crate::resources::file::File> {
334 crate::upload::initiate_and_upload(
335 self.req(),
336 &format!("courses/{}/files", self.id),
337 request,
338 data,
339 )
340 .await
341 }
342
343 pub async fn get_external_tool(&self, tool_id: u64) -> Result<ExternalTool> {
352 let mut tool: ExternalTool = self
353 .req()
354 .get(
355 &format!("courses/{}/external_tools/{tool_id}", self.id),
356 &[],
357 )
358 .await?;
359 tool.requester = self.requester.clone();
360 Ok(tool)
361 }
362
363 pub fn get_external_tools(&self) -> PageStream<ExternalTool> {
368 let course_id = self.id;
369 PageStream::new_with_injector(
370 Arc::clone(self.req()),
371 &format!("courses/{course_id}/external_tools"),
372 vec![],
373 |mut t: ExternalTool, req| {
374 t.requester = Some(Arc::clone(&req));
375 t
376 },
377 )
378 }
379
380 pub async fn create_external_tool(&self, params: ExternalToolParams) -> Result<ExternalTool> {
385 let form = wrap_params("external_tool", ¶ms);
386 let mut tool: ExternalTool = self
387 .req()
388 .post(&format!("courses/{}/external_tools", self.id), &form)
389 .await?;
390 tool.requester = self.requester.clone();
391 Ok(tool)
392 }
393
394 pub async fn get_rubric(&self, rubric_id: u64) -> Result<Rubric> {
403 let mut rubric: Rubric = self
404 .req()
405 .get(&format!("courses/{}/rubrics/{rubric_id}", self.id), &[])
406 .await?;
407 rubric.requester = self.requester.clone();
408 Ok(rubric)
409 }
410
411 pub fn get_rubrics(&self) -> PageStream<Rubric> {
416 let course_id = self.id;
417 PageStream::new_with_injector(
418 Arc::clone(self.req()),
419 &format!("courses/{course_id}/rubrics"),
420 vec![],
421 |mut r: Rubric, req| {
422 r.requester = Some(Arc::clone(&req));
423 r
424 },
425 )
426 }
427
428 pub async fn create_rubric(&self, params: RubricParams) -> Result<Rubric> {
433 let form = wrap_params("rubric", ¶ms);
434 let mut rubric: Rubric = self
435 .req()
436 .post(&format!("courses/{}/rubrics", self.id), &form)
437 .await?;
438 rubric.requester = self.requester.clone();
439 Ok(rubric)
440 }
441
442 pub async fn get_rubric_association(&self, association_id: u64) -> Result<RubricAssociation> {
447 let mut assoc: RubricAssociation = self
448 .req()
449 .get(
450 &format!("courses/{}/rubric_associations/{association_id}", self.id),
451 &[],
452 )
453 .await?;
454 assoc.requester = self.requester.clone();
455 Ok(assoc)
456 }
457
458 pub fn get_rubric_associations(&self) -> PageStream<RubricAssociation> {
463 let course_id = self.id;
464 PageStream::new_with_injector(
465 Arc::clone(self.req()),
466 &format!("courses/{course_id}/rubric_associations"),
467 vec![],
468 |mut a: RubricAssociation, req| {
469 a.requester = Some(Arc::clone(&req));
470 a
471 },
472 )
473 }
474
475 pub async fn get_blueprint(&self, template_id: &str) -> Result<BlueprintTemplate> {
486 let mut tmpl: BlueprintTemplate = self
487 .req()
488 .get(
489 &format!("courses/{}/blueprint_templates/{template_id}", self.id),
490 &[],
491 )
492 .await?;
493 tmpl.requester = self.requester.clone();
494 Ok(tmpl)
495 }
496
497 pub fn get_blueprint_subscriptions(&self) -> PageStream<BlueprintSubscription> {
502 let course_id = self.id;
503 PageStream::new_with_injector(
504 Arc::clone(self.req()),
505 &format!("courses/{course_id}/blueprint_subscriptions"),
506 vec![],
507 |mut s: BlueprintSubscription, req| {
508 s.requester = Some(Arc::clone(&req));
509 s
510 },
511 )
512 }
513
514 pub async fn get_content_migration(&self, migration_id: u64) -> Result<ContentMigration> {
523 let mut migration: ContentMigration = self
524 .req()
525 .get(
526 &format!("courses/{}/content_migrations/{migration_id}", self.id),
527 &[],
528 )
529 .await?;
530 migration.requester = self.requester.clone();
531 Ok(migration)
532 }
533
534 pub fn get_content_migrations(&self) -> PageStream<ContentMigration> {
539 let course_id = self.id;
540 PageStream::new_with_injector(
541 Arc::clone(self.req()),
542 &format!("courses/{course_id}/content_migrations"),
543 vec![],
544 |mut m: ContentMigration, req| {
545 m.requester = Some(Arc::clone(&req));
546 m
547 },
548 )
549 }
550
551 pub async fn create_content_migration(
556 &self,
557 migration_type: &str,
558 params: &[(String, String)],
559 ) -> Result<ContentMigration> {
560 let mut form = vec![("migration_type".to_string(), migration_type.to_string())];
561 form.extend_from_slice(params);
562 let mut migration: ContentMigration = self
563 .req()
564 .post(&format!("courses/{}/content_migrations", self.id), &form)
565 .await?;
566 migration.requester = self.requester.clone();
567 Ok(migration)
568 }
569
570 pub fn get_migrators(&self) -> PageStream<Migrator> {
575 PageStream::new(
576 Arc::clone(self.req()),
577 &format!("courses/{}/content_migrations/migrators", self.id),
578 vec![],
579 )
580 }
581
582 pub fn get_outcome_group_links(&self) -> PageStream<OutcomeLink> {
591 PageStream::new(
592 Arc::clone(self.req()),
593 &format!("courses/{}/outcome_group_links", self.id),
594 vec![],
595 )
596 }
597
598 pub async fn get_outcome_group(&self, group_id: u64) -> Result<OutcomeGroup> {
603 let mut group: OutcomeGroup = self
604 .req()
605 .get(
606 &format!("courses/{}/outcome_groups/{group_id}", self.id),
607 &[],
608 )
609 .await?;
610 group.requester = self.requester.clone();
611 Ok(group)
612 }
613
614 pub async fn create_outcome_group(
619 &self,
620 params: UpdateOutcomeGroupParams,
621 ) -> Result<OutcomeGroup> {
622 let form = wrap_params("outcome_group", ¶ms);
623 let mut group: OutcomeGroup = self
624 .req()
625 .post(&format!("courses/{}/outcome_groups", self.id), &form)
626 .await?;
627 group.requester = self.requester.clone();
628 Ok(group)
629 }
630
631 pub fn get_gradebook_history_dates(&self) -> PageStream<Day> {
640 PageStream::new(
641 Arc::clone(self.req()),
642 &format!("courses/{}/gradebook_history/days", self.id),
643 vec![],
644 )
645 }
646
647 pub fn get_gradebook_history_details(&self, date: &str) -> PageStream<Grader> {
654 PageStream::new(
655 Arc::clone(self.req()),
656 &format!("courses/{}/gradebook_history/{date}", self.id),
657 vec![],
658 )
659 }
660
661 pub fn get_submission_history(
666 &self,
667 date: &str,
668 grader_id: u64,
669 assignment_id: u64,
670 ) -> PageStream<SubmissionHistory> {
671 PageStream::new(
672 Arc::clone(self.req()),
673 &format!(
674 "courses/{}/gradebook_history/{date}/graders/{grader_id}/assignments/{assignment_id}/submissions",
675 self.id
676 ),
677 vec![],
678 )
679 }
680
681 pub fn get_uncollated_submissions(&self) -> PageStream<SubmissionVersion> {
686 PageStream::new(
687 Arc::clone(self.req()),
688 &format!("courses/{}/gradebook_history/feed", self.id),
689 vec![],
690 )
691 }
692
693 #[cfg(feature = "new-quizzes")]
702 pub async fn get_new_quiz(
703 &self,
704 assignment_id: &str,
705 ) -> Result<crate::resources::new_quiz::NewQuiz> {
706 let mut quiz: crate::resources::new_quiz::NewQuiz = self
707 .req()
708 .nq_get(&format!("courses/{}/quizzes/{assignment_id}", self.id), &[])
709 .await?;
710 quiz.requester = self.requester.clone();
711 quiz.course_id = Some(self.id);
712 Ok(quiz)
713 }
714
715 #[cfg(feature = "new-quizzes")]
720 pub fn get_new_quizzes(&self) -> PageStream<crate::resources::new_quiz::NewQuiz> {
721 let course_id = self.id;
722 PageStream::new_with_injector_nq(
723 Arc::clone(self.req()),
724 &format!("courses/{course_id}/quizzes"),
725 vec![],
726 move |mut q: crate::resources::new_quiz::NewQuiz, req| {
727 q.requester = Some(Arc::clone(&req));
728 q.course_id = Some(course_id);
729 q
730 },
731 )
732 }
733
734 #[cfg(feature = "new-quizzes")]
739 pub async fn create_new_quiz(
740 &self,
741 params: crate::resources::new_quiz::NewQuizParams,
742 ) -> Result<crate::resources::new_quiz::NewQuiz> {
743 let body = serde_json::to_value(¶ms).unwrap_or_default();
744 let mut quiz: crate::resources::new_quiz::NewQuiz = self
745 .req()
746 .nq_post(&format!("courses/{}/quizzes", self.id), &body)
747 .await?;
748 quiz.requester = self.requester.clone();
749 quiz.course_id = Some(self.id);
750 Ok(quiz)
751 }
752
753 pub fn get_grading_periods(&self) -> PageStream<GradingPeriod> {
762 let course_id = self.id;
763 PageStream::new_with_injector(
764 Arc::clone(self.req()),
765 &format!("courses/{course_id}/grading_periods"),
766 vec![],
767 move |mut gp: GradingPeriod, req| {
768 gp.requester = Some(Arc::clone(&req));
769 gp.course_id = Some(course_id);
770 gp
771 },
772 )
773 }
774
775 pub fn get_grading_standards(&self) -> PageStream<GradingStandard> {
784 PageStream::new(
785 Arc::clone(self.req()),
786 &format!("courses/{}/grading_standards", self.id),
787 vec![],
788 )
789 }
790
791 pub async fn create_grading_standard(
796 &self,
797 params: crate::resources::grading_standard::GradingStandardParams,
798 ) -> Result<GradingStandard> {
799 let form = wrap_params("grading_scheme_entry", ¶ms.grading_scheme_entry)
800 .into_iter()
801 .chain([("title".into(), params.title)])
802 .collect::<Vec<_>>();
803 self.req()
804 .post(&format!("courses/{}/grading_standards", self.id), &form)
805 .await
806 }
807
808 pub async fn get_content_export(&self, export_id: u64) -> Result<ContentExport> {
817 self.req()
818 .get(
819 &format!("courses/{}/content_exports/{export_id}", self.id),
820 &[],
821 )
822 .await
823 }
824
825 pub fn get_content_exports(&self) -> PageStream<ContentExport> {
830 PageStream::new(
831 Arc::clone(self.req()),
832 &format!("courses/{}/content_exports", self.id),
833 vec![],
834 )
835 }
836
837 pub async fn create_content_export(
842 &self,
843 params: ContentExportParams,
844 ) -> Result<ContentExport> {
845 let form = vec![
846 ("export_type".into(), params.export_type),
847 (
848 "skip_notifications".into(),
849 params.skip_notifications.unwrap_or(false).to_string(),
850 ),
851 ];
852 self.req()
853 .post(&format!("courses/{}/content_exports", self.id), &form)
854 .await
855 }
856
857 pub fn get_grade_change_events(&self) -> PageStream<GradeChangeEvent> {
869 PageStream::new(
870 Arc::clone(self.req()),
871 &format!("audit/grade_change/courses/{}", self.id),
872 vec![],
873 )
874 }
875
876 pub fn get_features(&self) -> PageStream<Feature> {
885 PageStream::new(
886 Arc::clone(self.req()),
887 &format!("courses/{}/features", self.id),
888 vec![],
889 )
890 }
891
892 pub async fn get_feature_flag(&self, feature: &str) -> Result<FeatureFlag> {
897 self.req()
898 .get(
899 &format!("courses/{}/features/flags/{feature}", self.id),
900 &[],
901 )
902 .await
903 }
904
905 pub async fn get_enabled_features(&self) -> Result<Vec<String>> {
910 self.req()
911 .get(&format!("courses/{}/features/enabled", self.id), &[])
912 .await
913 }
914}