1use crate::{
2 error::Result,
3 http::Requester,
4 pagination::PageStream,
5 params::wrap_params,
6 resources::{
7 assignment::{Assignment, AssignmentGroup},
8 blueprint::{BlueprintSubscription, BlueprintTemplate},
9 collaboration::Collaboration,
10 content_export::{ContentExport, ContentExportParams},
11 content_migration::{ContentMigration, Migrator},
12 custom_gradebook_column::{CustomGradebookColumn, CustomGradebookColumnParams},
13 discussion_topic::DiscussionTopic,
14 enrollment::Enrollment,
15 external_tool::{ExternalTool, ExternalToolParams},
16 feature::{Feature, FeatureFlag},
17 file::File,
18 grade_change_log::GradeChangeEvent,
19 gradebook_history::{Day, Grader, SubmissionHistory, SubmissionVersion},
20 grading_period::GradingPeriod,
21 grading_standard::GradingStandard,
22 group::{Group, GroupCategory},
23 lti_resource_link::{CreateLtiResourceLinkParams, LtiResourceLink},
24 module::Module,
25 outcome::{OutcomeGroup, OutcomeImport, OutcomeLink, UpdateOutcomeGroupParams},
26 page::Page,
27 params::{
28 assignment_params::CreateAssignmentParams, course_params::UpdateCourseParams,
29 quiz_params::CreateQuizParams,
30 },
31 quiz::Quiz,
32 rubric::{Rubric, RubricAssociation, RubricParams},
33 section::Section,
34 tab::Tab,
35 types::WorkflowState,
36 user::User,
37 },
38};
39use chrono::{DateTime, Utc};
40use serde::{Deserialize, Serialize};
41use std::sync::Arc;
42
43#[derive(Debug, Clone, Deserialize, Serialize, canvas_lms_api_derive::CanvasResource)]
44pub struct Course {
45 pub id: u64,
46 pub name: Option<String>,
47 pub course_code: Option<String>,
48 pub workflow_state: Option<WorkflowState>,
49 pub account_id: Option<u64>,
50 pub root_account_id: Option<u64>,
51 pub enrollment_term_id: Option<u64>,
52 pub sis_course_id: Option<String>,
53 pub start_at: Option<DateTime<Utc>>,
54 pub end_at: Option<DateTime<Utc>>,
55 pub grading_standard_id: Option<u64>,
56 pub is_public: Option<bool>,
57 pub license: Option<String>,
58 pub locale: Option<String>,
59 pub time_zone: Option<String>,
60 pub total_students: Option<u64>,
61 pub default_view: Option<String>,
62 pub syllabus_body: Option<String>,
63 pub public_description: Option<String>,
64 pub hide_final_grades: Option<bool>,
65 pub apply_assignment_group_weights: Option<bool>,
66 pub restrict_enrollments_to_course_dates: Option<bool>,
67
68 #[serde(skip)]
69 pub(crate) requester: Option<Arc<Requester>>,
70}
71
72impl Course {
73 pub fn get_assignments(&self) -> PageStream<Assignment> {
78 let course_id = self.id;
79 PageStream::new_with_injector(
80 Arc::clone(self.req()),
81 &format!("courses/{}/assignments", self.id),
82 vec![],
83 move |mut a: Assignment, req| {
84 a.requester = Some(Arc::clone(&req));
85 a.course_id = Some(course_id);
86 a
87 },
88 )
89 }
90
91 pub async fn get_assignment(&self, assignment_id: u64) -> Result<Assignment> {
96 let mut a: Assignment = self
97 .req()
98 .get(
99 &format!("courses/{}/assignments/{assignment_id}", self.id),
100 &[],
101 )
102 .await?;
103 a.requester = Some(Arc::clone(self.req()));
104 a.course_id = Some(self.id);
105 Ok(a)
106 }
107
108 pub async fn create_assignment(&self, params: CreateAssignmentParams) -> Result<Assignment> {
113 let form = wrap_params("assignment", ¶ms);
114 let mut a: Assignment = self
115 .req()
116 .post(&format!("courses/{}/assignments", self.id), &form)
117 .await?;
118 a.requester = Some(Arc::clone(self.req()));
119 a.course_id = Some(self.id);
120 Ok(a)
121 }
122
123 pub fn get_assignment_groups(&self) -> PageStream<AssignmentGroup> {
128 let course_id = self.id;
129 PageStream::new_with_injector(
130 Arc::clone(self.req()),
131 &format!("courses/{}/assignment_groups", self.id),
132 vec![],
133 move |mut g: AssignmentGroup, req| {
134 g.requester = Some(Arc::clone(&req));
135 g.course_id = Some(course_id);
136 g
137 },
138 )
139 }
140
141 pub async fn create_assignment_group(
146 &self,
147 params: crate::resources::assignment::AssignmentGroupParams,
148 ) -> Result<AssignmentGroup> {
149 let form = wrap_params("assignment_group", ¶ms);
150 let mut g: AssignmentGroup = self
151 .req()
152 .post(&format!("courses/{}/assignment_groups", self.id), &form)
153 .await?;
154 g.requester = Some(Arc::clone(self.req()));
155 g.course_id = Some(self.id);
156 Ok(g)
157 }
158
159 pub fn get_sections(&self) -> PageStream<Section> {
164 PageStream::new_with_injector(
165 Arc::clone(self.req()),
166 &format!("courses/{}/sections", self.id),
167 vec![],
168 |mut s: Section, req| {
169 s.requester = Some(Arc::clone(&req));
170 s
171 },
172 )
173 }
174
175 pub async fn get_section(&self, section_id: u64) -> Result<Section> {
180 let mut s: Section = self
181 .req()
182 .get(&format!("courses/{}/sections/{section_id}", self.id), &[])
183 .await?;
184 s.requester = self.requester.clone();
185 Ok(s)
186 }
187
188 pub fn get_enrollments(&self) -> PageStream<Enrollment> {
193 PageStream::new_with_injector(
194 Arc::clone(self.req()),
195 &format!("courses/{}/enrollments", self.id),
196 vec![],
197 |mut e: Enrollment, req| {
198 e.requester = Some(Arc::clone(&req));
199 e
200 },
201 )
202 }
203
204 pub fn get_users(&self) -> PageStream<User> {
209 PageStream::new(
210 Arc::clone(self.req()),
211 &format!("courses/{}/users", self.id),
212 vec![],
213 )
214 }
215
216 pub async fn update(&self, params: UpdateCourseParams) -> Result<Course> {
221 let form = wrap_params("course", ¶ms);
222 let mut course: Course = self
223 .req()
224 .put(&format!("courses/{}", self.id), &form)
225 .await?;
226 course.requester = self.requester.clone();
227 Ok(course)
228 }
229
230 pub async fn delete(&self) -> Result<Course> {
235 let params = vec![("event".to_string(), "delete".to_string())];
236 let mut course: Course = self
237 .req()
238 .delete(&format!("courses/{}", self.id), ¶ms)
239 .await?;
240 course.requester = self.requester.clone();
241 Ok(course)
242 }
243
244 pub fn get_quizzes(&self) -> PageStream<Quiz> {
249 let course_id = self.id;
250 PageStream::new_with_injector(
251 Arc::clone(self.req()),
252 &format!("courses/{}/quizzes", self.id),
253 vec![],
254 move |mut q: Quiz, req| {
255 q.requester = Some(Arc::clone(&req));
256 q.course_id = Some(course_id);
257 q
258 },
259 )
260 }
261
262 pub async fn get_quiz(&self, quiz_id: u64) -> Result<Quiz> {
267 let mut q: Quiz = self
268 .req()
269 .get(&format!("courses/{}/quizzes/{quiz_id}", self.id), &[])
270 .await?;
271 q.requester = Some(Arc::clone(self.req()));
272 q.course_id = Some(self.id);
273 Ok(q)
274 }
275
276 pub async fn create_quiz(&self, params: CreateQuizParams) -> Result<Quiz> {
281 let form = wrap_params("quiz", ¶ms);
282 let mut q: Quiz = self
283 .req()
284 .post(&format!("courses/{}/quizzes", self.id), &form)
285 .await?;
286 q.requester = Some(Arc::clone(self.req()));
287 q.course_id = Some(self.id);
288 Ok(q)
289 }
290
291 pub fn get_modules(&self) -> PageStream<Module> {
296 let course_id = self.id;
297 PageStream::new_with_injector(
298 Arc::clone(self.req()),
299 &format!("courses/{}/modules", self.id),
300 vec![],
301 move |mut m: Module, req| {
302 m.requester = Some(Arc::clone(&req));
303 m.course_id = Some(course_id);
304 m
305 },
306 )
307 }
308
309 pub async fn get_module(&self, module_id: u64) -> Result<Module> {
314 let mut m: Module = self
315 .req()
316 .get(&format!("courses/{}/modules/{module_id}", self.id), &[])
317 .await?;
318 m.requester = Some(Arc::clone(self.req()));
319 m.course_id = Some(self.id);
320 Ok(m)
321 }
322
323 pub async fn create_module(
328 &self,
329 params: crate::resources::module::CreateModuleParams,
330 ) -> Result<Module> {
331 let form = wrap_params("module", ¶ms);
332 let mut m: Module = self
333 .req()
334 .post(&format!("courses/{}/modules", self.id), &form)
335 .await?;
336 m.requester = Some(Arc::clone(self.req()));
337 m.course_id = Some(self.id);
338 Ok(m)
339 }
340
341 pub fn get_pages(&self) -> PageStream<Page> {
346 let course_id = self.id;
347 PageStream::new_with_injector(
348 Arc::clone(self.req()),
349 &format!("courses/{}/pages", self.id),
350 vec![],
351 move |mut p: Page, req| {
352 p.requester = Some(Arc::clone(&req));
353 p.course_id = Some(course_id);
354 p
355 },
356 )
357 }
358
359 pub async fn get_page(&self, url_or_id: &str) -> Result<Page> {
364 let mut page: Page = self
365 .req()
366 .get(&format!("courses/{}/pages/{url_or_id}", self.id), &[])
367 .await?;
368 page.requester = self.requester.clone();
369 page.course_id = Some(self.id);
370 Ok(page)
371 }
372
373 pub fn get_discussion_topics(&self) -> PageStream<DiscussionTopic> {
378 let course_id = self.id;
379 PageStream::new_with_injector(
380 Arc::clone(self.req()),
381 &format!("courses/{}/discussion_topics", self.id),
382 vec![],
383 move |mut t: DiscussionTopic, req| {
384 t.requester = Some(Arc::clone(&req));
385 t.course_id_ctx = Some(course_id);
386 t
387 },
388 )
389 }
390
391 pub async fn get_discussion_topic(&self, topic_id: u64) -> Result<DiscussionTopic> {
396 let mut t: DiscussionTopic = self
397 .req()
398 .get(
399 &format!("courses/{}/discussion_topics/{topic_id}", self.id),
400 &[],
401 )
402 .await?;
403 t.requester = Some(Arc::clone(self.req()));
404 t.course_id_ctx = Some(self.id);
405 Ok(t)
406 }
407
408 pub async fn create_discussion_topic(
413 &self,
414 params: crate::resources::discussion_topic::UpdateDiscussionParams,
415 ) -> Result<DiscussionTopic> {
416 let form = wrap_params("discussion_topic", ¶ms);
417 let mut t: DiscussionTopic = self
418 .req()
419 .post(&format!("courses/{}/discussion_topics", self.id), &form)
420 .await?;
421 t.requester = Some(Arc::clone(self.req()));
422 t.course_id_ctx = Some(self.id);
423 Ok(t)
424 }
425
426 pub fn get_files(&self) -> PageStream<File> {
431 PageStream::new_with_injector(
432 Arc::clone(self.req()),
433 &format!("courses/{}/files", self.id),
434 vec![],
435 |mut f: File, req| {
436 f.requester = Some(Arc::clone(&req));
437 f
438 },
439 )
440 }
441
442 pub fn get_tabs(&self) -> PageStream<Tab> {
447 let course_id = self.id;
448 PageStream::new_with_injector(
449 Arc::clone(self.req()),
450 &format!("courses/{}/tabs", self.id),
451 vec![],
452 move |mut t: Tab, req| {
453 t.requester = Some(Arc::clone(&req));
454 t.course_id = Some(course_id);
455 t
456 },
457 )
458 }
459
460 pub fn get_collaborations(&self) -> PageStream<Collaboration> {
465 let course_id = self.id;
466 PageStream::new_with_injector(
467 Arc::clone(self.req()),
468 &format!("courses/{course_id}/collaborations"),
469 vec![],
470 {
471 let req = Arc::clone(self.req());
472 move |mut c: Collaboration, _| {
473 c.requester = Some(Arc::clone(&req));
474 c
475 }
476 },
477 )
478 }
479
480 pub fn get_groups(&self) -> PageStream<Group> {
485 let course_id = self.id;
486 PageStream::new_with_injector(
487 Arc::clone(self.req()),
488 &format!("courses/{course_id}/groups"),
489 vec![],
490 |mut g: Group, req| {
491 g.requester = Some(Arc::clone(&req));
492 g
493 },
494 )
495 }
496
497 pub fn get_group_categories(&self) -> PageStream<GroupCategory> {
502 let course_id = self.id;
503 PageStream::new_with_injector(
504 Arc::clone(self.req()),
505 &format!("courses/{course_id}/group_categories"),
506 vec![],
507 |mut gc: GroupCategory, req| {
508 gc.requester = Some(Arc::clone(&req));
509 gc
510 },
511 )
512 }
513
514 pub async fn create_group_category(
519 &self,
520 params: crate::resources::group::GroupCategoryParams,
521 ) -> Result<GroupCategory> {
522 let form = wrap_params("group_category", ¶ms);
523 let mut gc: GroupCategory = self
524 .req()
525 .post(&format!("courses/{}/group_categories", self.id), &form)
526 .await?;
527 gc.requester = Some(Arc::clone(self.req()));
528 Ok(gc)
529 }
530
531 pub async fn upload_file(
539 &self,
540 request: crate::upload::UploadRequest,
541 data: Vec<u8>,
542 ) -> crate::error::Result<crate::resources::file::File> {
543 crate::upload::initiate_and_upload(
544 self.req(),
545 &format!("courses/{}/files", self.id),
546 request,
547 data,
548 )
549 .await
550 }
551
552 pub async fn get_external_tool(&self, tool_id: u64) -> Result<ExternalTool> {
561 let mut tool: ExternalTool = self
562 .req()
563 .get(
564 &format!("courses/{}/external_tools/{tool_id}", self.id),
565 &[],
566 )
567 .await?;
568 tool.requester = self.requester.clone();
569 Ok(tool)
570 }
571
572 pub fn get_external_tools(&self) -> PageStream<ExternalTool> {
577 let course_id = self.id;
578 PageStream::new_with_injector(
579 Arc::clone(self.req()),
580 &format!("courses/{course_id}/external_tools"),
581 vec![],
582 |mut t: ExternalTool, req| {
583 t.requester = Some(Arc::clone(&req));
584 t
585 },
586 )
587 }
588
589 pub async fn create_external_tool(&self, params: ExternalToolParams) -> Result<ExternalTool> {
594 let form = wrap_params("external_tool", ¶ms);
595 let mut tool: ExternalTool = self
596 .req()
597 .post(&format!("courses/{}/external_tools", self.id), &form)
598 .await?;
599 tool.requester = self.requester.clone();
600 Ok(tool)
601 }
602
603 pub async fn get_rubric(&self, rubric_id: u64) -> Result<Rubric> {
612 let mut rubric: Rubric = self
613 .req()
614 .get(&format!("courses/{}/rubrics/{rubric_id}", self.id), &[])
615 .await?;
616 rubric.requester = self.requester.clone();
617 Ok(rubric)
618 }
619
620 pub fn get_rubrics(&self) -> PageStream<Rubric> {
625 let course_id = self.id;
626 PageStream::new_with_injector(
627 Arc::clone(self.req()),
628 &format!("courses/{course_id}/rubrics"),
629 vec![],
630 |mut r: Rubric, req| {
631 r.requester = Some(Arc::clone(&req));
632 r
633 },
634 )
635 }
636
637 pub async fn create_rubric(&self, params: RubricParams) -> Result<Rubric> {
642 let form = wrap_params("rubric", ¶ms);
643 let mut rubric: Rubric = self
644 .req()
645 .post(&format!("courses/{}/rubrics", self.id), &form)
646 .await?;
647 rubric.requester = self.requester.clone();
648 Ok(rubric)
649 }
650
651 pub async fn get_rubric_association(&self, association_id: u64) -> Result<RubricAssociation> {
656 let mut assoc: RubricAssociation = self
657 .req()
658 .get(
659 &format!("courses/{}/rubric_associations/{association_id}", self.id),
660 &[],
661 )
662 .await?;
663 assoc.requester = self.requester.clone();
664 Ok(assoc)
665 }
666
667 pub async fn create_rubric_association(
672 &self,
673 params: &[(String, String)],
674 ) -> Result<RubricAssociation> {
675 let mut assoc: RubricAssociation = self
676 .req()
677 .post(&format!("courses/{}/rubric_associations", self.id), params)
678 .await?;
679 assoc.requester = self.requester.clone();
680 assoc.course_id = Some(self.id);
681 Ok(assoc)
682 }
683
684 pub fn get_rubric_associations(&self) -> PageStream<RubricAssociation> {
689 let course_id = self.id;
690 PageStream::new_with_injector(
691 Arc::clone(self.req()),
692 &format!("courses/{course_id}/rubric_associations"),
693 vec![],
694 |mut a: RubricAssociation, req| {
695 a.requester = Some(Arc::clone(&req));
696 a
697 },
698 )
699 }
700
701 pub async fn get_blueprint(&self, template_id: &str) -> Result<BlueprintTemplate> {
712 let mut tmpl: BlueprintTemplate = self
713 .req()
714 .get(
715 &format!("courses/{}/blueprint_templates/{template_id}", self.id),
716 &[],
717 )
718 .await?;
719 tmpl.requester = self.requester.clone();
720 Ok(tmpl)
721 }
722
723 pub fn get_blueprint_subscriptions(&self) -> PageStream<BlueprintSubscription> {
728 let course_id = self.id;
729 PageStream::new_with_injector(
730 Arc::clone(self.req()),
731 &format!("courses/{course_id}/blueprint_subscriptions"),
732 vec![],
733 |mut s: BlueprintSubscription, req| {
734 s.requester = Some(Arc::clone(&req));
735 s
736 },
737 )
738 }
739
740 pub async fn get_content_migration(&self, migration_id: u64) -> Result<ContentMigration> {
749 let mut migration: ContentMigration = self
750 .req()
751 .get(
752 &format!("courses/{}/content_migrations/{migration_id}", self.id),
753 &[],
754 )
755 .await?;
756 migration.requester = self.requester.clone();
757 Ok(migration)
758 }
759
760 pub fn get_content_migrations(&self) -> PageStream<ContentMigration> {
765 let course_id = self.id;
766 PageStream::new_with_injector(
767 Arc::clone(self.req()),
768 &format!("courses/{course_id}/content_migrations"),
769 vec![],
770 |mut m: ContentMigration, req| {
771 m.requester = Some(Arc::clone(&req));
772 m
773 },
774 )
775 }
776
777 pub async fn create_content_migration(
782 &self,
783 migration_type: &str,
784 params: &[(String, String)],
785 ) -> Result<ContentMigration> {
786 let mut form = vec![("migration_type".to_string(), migration_type.to_string())];
787 form.extend_from_slice(params);
788 let mut migration: ContentMigration = self
789 .req()
790 .post(&format!("courses/{}/content_migrations", self.id), &form)
791 .await?;
792 migration.requester = self.requester.clone();
793 Ok(migration)
794 }
795
796 pub fn get_migrators(&self) -> PageStream<Migrator> {
801 PageStream::new(
802 Arc::clone(self.req()),
803 &format!("courses/{}/content_migrations/migrators", self.id),
804 vec![],
805 )
806 }
807
808 pub fn get_outcome_group_links(&self) -> PageStream<OutcomeLink> {
817 PageStream::new(
818 Arc::clone(self.req()),
819 &format!("courses/{}/outcome_group_links", self.id),
820 vec![],
821 )
822 }
823
824 pub async fn get_root_outcome_group(&self) -> Result<OutcomeGroup> {
829 let mut group: OutcomeGroup = self
830 .req()
831 .get(&format!("courses/{}/root_outcome_group", self.id), &[])
832 .await?;
833 group.requester = self.requester.clone();
834 Ok(group)
835 }
836
837 pub async fn get_outcome_group(&self, group_id: u64) -> Result<OutcomeGroup> {
842 let mut group: OutcomeGroup = self
843 .req()
844 .get(
845 &format!("courses/{}/outcome_groups/{group_id}", self.id),
846 &[],
847 )
848 .await?;
849 group.requester = self.requester.clone();
850 Ok(group)
851 }
852
853 pub async fn create_outcome_group(
858 &self,
859 params: UpdateOutcomeGroupParams,
860 ) -> Result<OutcomeGroup> {
861 let form = wrap_params("outcome_group", ¶ms);
862 let mut group: OutcomeGroup = self
863 .req()
864 .post(&format!("courses/{}/outcome_groups", self.id), &form)
865 .await?;
866 group.requester = self.requester.clone();
867 Ok(group)
868 }
869
870 pub fn get_custom_columns(&self) -> PageStream<CustomGradebookColumn> {
879 let course_id = self.id;
880 PageStream::new_with_injector(
881 Arc::clone(self.req()),
882 &format!("courses/{course_id}/custom_gradebook_columns"),
883 vec![],
884 move |mut col: CustomGradebookColumn, req| {
885 col.requester = Some(Arc::clone(&req));
886 col.course_id = Some(course_id);
887 col
888 },
889 )
890 }
891
892 pub async fn create_custom_column(
897 &self,
898 params: CustomGradebookColumnParams,
899 ) -> Result<CustomGradebookColumn> {
900 let form = wrap_params("column", ¶ms);
901 let mut col: CustomGradebookColumn = self
902 .req()
903 .post(
904 &format!("courses/{}/custom_gradebook_columns", self.id),
905 &form,
906 )
907 .await?;
908 col.requester = self.requester.clone();
909 col.course_id = Some(self.id);
910 Ok(col)
911 }
912
913 pub fn get_gradebook_history_dates(&self) -> PageStream<Day> {
922 PageStream::new(
923 Arc::clone(self.req()),
924 &format!("courses/{}/gradebook_history/days", self.id),
925 vec![],
926 )
927 }
928
929 pub fn get_gradebook_history_details(&self, date: &str) -> PageStream<Grader> {
936 PageStream::new(
937 Arc::clone(self.req()),
938 &format!("courses/{}/gradebook_history/{date}", self.id),
939 vec![],
940 )
941 }
942
943 pub fn get_submission_history(
948 &self,
949 date: &str,
950 grader_id: u64,
951 assignment_id: u64,
952 ) -> PageStream<SubmissionHistory> {
953 PageStream::new(
954 Arc::clone(self.req()),
955 &format!(
956 "courses/{}/gradebook_history/{date}/graders/{grader_id}/assignments/{assignment_id}/submissions",
957 self.id
958 ),
959 vec![],
960 )
961 }
962
963 pub fn get_uncollated_submissions(&self) -> PageStream<SubmissionVersion> {
968 PageStream::new(
969 Arc::clone(self.req()),
970 &format!("courses/{}/gradebook_history/feed", self.id),
971 vec![],
972 )
973 }
974
975 #[cfg(feature = "new-quizzes")]
984 pub async fn get_new_quiz(
985 &self,
986 assignment_id: &str,
987 ) -> Result<crate::resources::new_quiz::NewQuiz> {
988 let mut quiz: crate::resources::new_quiz::NewQuiz = self
989 .req()
990 .nq_get(&format!("courses/{}/quizzes/{assignment_id}", self.id), &[])
991 .await?;
992 quiz.requester = self.requester.clone();
993 quiz.course_id = Some(self.id);
994 Ok(quiz)
995 }
996
997 #[cfg(feature = "new-quizzes")]
1002 pub fn get_new_quizzes(&self) -> PageStream<crate::resources::new_quiz::NewQuiz> {
1003 let course_id = self.id;
1004 PageStream::new_with_injector_nq(
1005 Arc::clone(self.req()),
1006 &format!("courses/{course_id}/quizzes"),
1007 vec![],
1008 move |mut q: crate::resources::new_quiz::NewQuiz, req| {
1009 q.requester = Some(Arc::clone(&req));
1010 q.course_id = Some(course_id);
1011 q
1012 },
1013 )
1014 }
1015
1016 #[cfg(feature = "new-quizzes")]
1021 pub async fn create_new_quiz(
1022 &self,
1023 params: crate::resources::new_quiz::NewQuizParams,
1024 ) -> Result<crate::resources::new_quiz::NewQuiz> {
1025 let body = serde_json::to_value(¶ms).unwrap_or_default();
1026 let mut quiz: crate::resources::new_quiz::NewQuiz = self
1027 .req()
1028 .nq_post(&format!("courses/{}/quizzes", self.id), &body)
1029 .await?;
1030 quiz.requester = self.requester.clone();
1031 quiz.course_id = Some(self.id);
1032 Ok(quiz)
1033 }
1034
1035 pub fn get_grading_periods(&self) -> PageStream<GradingPeriod> {
1044 let course_id = self.id;
1045 PageStream::new_with_injector(
1046 Arc::clone(self.req()),
1047 &format!("courses/{course_id}/grading_periods"),
1048 vec![],
1049 move |mut gp: GradingPeriod, req| {
1050 gp.requester = Some(Arc::clone(&req));
1051 gp.course_id = Some(course_id);
1052 gp
1053 },
1054 )
1055 }
1056
1057 pub fn get_grading_standards(&self) -> PageStream<GradingStandard> {
1066 PageStream::new(
1067 Arc::clone(self.req()),
1068 &format!("courses/{}/grading_standards", self.id),
1069 vec![],
1070 )
1071 }
1072
1073 pub async fn create_grading_standard(
1078 &self,
1079 params: crate::resources::grading_standard::GradingStandardParams,
1080 ) -> Result<GradingStandard> {
1081 let form = wrap_params("grading_scheme_entry", ¶ms.grading_scheme_entry)
1082 .into_iter()
1083 .chain([("title".into(), params.title)])
1084 .collect::<Vec<_>>();
1085 self.req()
1086 .post(&format!("courses/{}/grading_standards", self.id), &form)
1087 .await
1088 }
1089
1090 pub async fn get_content_export(&self, export_id: u64) -> Result<ContentExport> {
1099 self.req()
1100 .get(
1101 &format!("courses/{}/content_exports/{export_id}", self.id),
1102 &[],
1103 )
1104 .await
1105 }
1106
1107 pub fn get_content_exports(&self) -> PageStream<ContentExport> {
1112 PageStream::new(
1113 Arc::clone(self.req()),
1114 &format!("courses/{}/content_exports", self.id),
1115 vec![],
1116 )
1117 }
1118
1119 pub async fn create_content_export(
1124 &self,
1125 params: ContentExportParams,
1126 ) -> Result<ContentExport> {
1127 let form = vec![
1128 ("export_type".into(), params.export_type),
1129 (
1130 "skip_notifications".into(),
1131 params.skip_notifications.unwrap_or(false).to_string(),
1132 ),
1133 ];
1134 self.req()
1135 .post(&format!("courses/{}/content_exports", self.id), &form)
1136 .await
1137 }
1138
1139 pub fn get_grade_change_events(&self) -> PageStream<GradeChangeEvent> {
1151 PageStream::new(
1152 Arc::clone(self.req()),
1153 &format!("audit/grade_change/courses/{}", self.id),
1154 vec![],
1155 )
1156 }
1157
1158 pub fn get_features(&self) -> PageStream<Feature> {
1167 PageStream::new(
1168 Arc::clone(self.req()),
1169 &format!("courses/{}/features", self.id),
1170 vec![],
1171 )
1172 }
1173
1174 pub async fn get_feature_flag(&self, feature: &str) -> Result<FeatureFlag> {
1179 let mut ff: FeatureFlag = self
1180 .req()
1181 .get(
1182 &format!("courses/{}/features/flags/{feature}", self.id),
1183 &[],
1184 )
1185 .await?;
1186 ff.requester = self.requester.clone();
1187 Ok(ff)
1188 }
1189
1190 pub async fn get_enabled_features(&self) -> Result<Vec<String>> {
1195 self.req()
1196 .get(&format!("courses/{}/features/enabled", self.id), &[])
1197 .await
1198 }
1199
1200 pub fn get_lti_resource_links(&self) -> PageStream<LtiResourceLink> {
1209 PageStream::new(
1210 Arc::clone(self.req()),
1211 &format!("courses/{}/lti_resource_links", self.id),
1212 vec![],
1213 )
1214 }
1215
1216 pub async fn get_lti_resource_link(&self, link_id: u64) -> Result<LtiResourceLink> {
1221 self.req()
1222 .get(
1223 &format!("courses/{}/lti_resource_links/{link_id}", self.id),
1224 &[],
1225 )
1226 .await
1227 }
1228
1229 pub async fn create_lti_resource_link(
1234 &self,
1235 params: CreateLtiResourceLinkParams,
1236 ) -> Result<LtiResourceLink> {
1237 let form = crate::params::flatten_params(&serde_json::to_value(¶ms).unwrap());
1238 self.req()
1239 .post(&format!("courses/{}/lti_resource_links", self.id), &form)
1240 .await
1241 }
1242
1243 pub async fn conclude(&self) -> Result<serde_json::Value> {
1248 self.req()
1249 .delete(
1250 &format!("courses/{}", self.id),
1251 &[("event".to_string(), "conclude".to_string())],
1252 )
1253 .await
1254 }
1255
1256 pub async fn reset(&self) -> Result<Course> {
1261 let mut c: Course = self
1262 .req()
1263 .post(&format!("courses/{}/reset_content", self.id), &[])
1264 .await?;
1265 c.requester = Some(Arc::clone(self.req()));
1266 Ok(c)
1267 }
1268
1269 pub async fn get_settings(&self) -> Result<serde_json::Value> {
1274 self.req()
1275 .get(&format!("courses/{}/settings", self.id), &[])
1276 .await
1277 }
1278
1279 pub async fn update_settings(&self, params: &[(String, String)]) -> Result<serde_json::Value> {
1284 self.req()
1285 .put(&format!("courses/{}/settings", self.id), params)
1286 .await
1287 }
1288
1289 pub async fn get_late_policy(&self) -> Result<serde_json::Value> {
1294 self.req()
1295 .get(&format!("courses/{}/late_policy", self.id), &[])
1296 .await
1297 }
1298
1299 pub fn get_multiple_submissions(&self) -> PageStream<crate::resources::submission::Submission> {
1304 let course_id = self.id;
1305 PageStream::new_with_injector(
1306 Arc::clone(self.req()),
1307 &format!("courses/{}/students/submissions", self.id),
1308 vec![],
1309 move |mut s: crate::resources::submission::Submission, req| {
1310 s.requester = Some(Arc::clone(&req));
1311 s.course_id = Some(course_id);
1312 s
1313 },
1314 )
1315 }
1316
1317 pub async fn enroll_user(
1322 &self,
1323 user_id: u64,
1324 enrollment_type: &str,
1325 ) -> Result<crate::resources::enrollment::Enrollment> {
1326 let params = vec![
1327 ("enrollment[user_id]".to_string(), user_id.to_string()),
1328 ("enrollment[type]".to_string(), enrollment_type.to_string()),
1329 ];
1330 let mut e: crate::resources::enrollment::Enrollment = self
1331 .req()
1332 .post(&format!("courses/{}/enrollments", self.id), ¶ms)
1333 .await?;
1334 e.requester = Some(Arc::clone(self.req()));
1335 Ok(e)
1336 }
1337
1338 pub async fn submissions_bulk_update(
1343 &self,
1344 params: &[(String, String)],
1345 ) -> Result<crate::resources::progress::Progress> {
1346 let mut p: crate::resources::progress::Progress = self
1347 .req()
1348 .post(
1349 &format!("courses/{}/submissions/update_grades", self.id),
1350 params,
1351 )
1352 .await?;
1353 p.requester = Some(Arc::clone(self.req()));
1354 Ok(p)
1355 }
1356
1357 pub async fn show_front_page(&self) -> Result<Page> {
1366 let mut p: Page = self
1367 .req()
1368 .get(&format!("courses/{}/front_page", self.id), &[])
1369 .await?;
1370 p.course_id = Some(self.id);
1371 p.requester = self.requester.clone();
1372 Ok(p)
1373 }
1374
1375 pub async fn edit_front_page(&self, params: &[(String, String)]) -> Result<Page> {
1380 let mut p: Page = self
1381 .req()
1382 .put(&format!("courses/{}/front_page", self.id), params)
1383 .await?;
1384 p.course_id = Some(self.id);
1385 p.requester = self.requester.clone();
1386 Ok(p)
1387 }
1388
1389 pub async fn export_content(&self, export_type: &str) -> Result<ContentExport> {
1398 let params = vec![("export_type".to_string(), export_type.to_string())];
1399 self.req()
1400 .post(&format!("courses/{}/content_exports", self.id), ¶ms)
1401 .await
1402 }
1403
1404 pub async fn get_full_discussion_topic(&self, topic_id: u64) -> Result<serde_json::Value> {
1409 self.req()
1410 .get(
1411 &format!("courses/{}/discussion_topics/{topic_id}/view", self.id),
1412 &[],
1413 )
1414 .await
1415 }
1416
1417 pub async fn preview_html(&self, html: &str) -> Result<String> {
1422 let params = vec![("html".to_string(), html.to_string())];
1423 let val: serde_json::Value = self
1424 .req()
1425 .post(&format!("courses/{}/preview_html", self.id), ¶ms)
1426 .await?;
1427 Ok(val
1428 .get("html")
1429 .and_then(|v| v.as_str())
1430 .unwrap_or("")
1431 .to_string())
1432 }
1433
1434 pub async fn reorder_pinned_topics(&self, order: &[u64]) -> Result<serde_json::Value> {
1439 let order_str = order
1440 .iter()
1441 .map(|id| id.to_string())
1442 .collect::<Vec<_>>()
1443 .join(",");
1444 let params = vec![("order".to_string(), order_str)];
1445 self.req()
1446 .post(
1447 &format!("courses/{}/discussion_topics/reorder", self.id),
1448 ¶ms,
1449 )
1450 .await
1451 }
1452
1453 pub async fn get_user(&self, user_id: u64) -> Result<User> {
1462 let mut u: User = self
1463 .req()
1464 .get(&format!("courses/{}/users/{user_id}", self.id), &[])
1465 .await?;
1466 u.requester = self.requester.clone();
1467 Ok(u)
1468 }
1469
1470 pub fn get_recent_students(&self) -> PageStream<User> {
1475 let req = Arc::clone(self.req());
1476 PageStream::new_with_injector(
1477 req,
1478 &format!("courses/{}/recent_students", self.id),
1479 vec![],
1480 |mut u: User, r| {
1481 u.requester = Some(Arc::clone(&r));
1482 u
1483 },
1484 )
1485 }
1486
1487 pub async fn set_usage_rights(&self, params: &[(String, String)]) -> Result<serde_json::Value> {
1496 self.req()
1497 .put(&format!("courses/{}/usage_rights", self.id), params)
1498 .await
1499 }
1500
1501 pub async fn remove_usage_rights(
1506 &self,
1507 params: &[(String, String)],
1508 ) -> Result<serde_json::Value> {
1509 self.req()
1510 .delete(&format!("courses/{}/usage_rights", self.id), params)
1511 .await
1512 }
1513
1514 pub fn get_licenses(&self) -> PageStream<serde_json::Value> {
1519 PageStream::new(
1520 Arc::clone(self.req()),
1521 &format!("courses/{}/content_licenses", self.id),
1522 vec![],
1523 )
1524 }
1525
1526 pub fn get_external_feeds(&self) -> PageStream<serde_json::Value> {
1535 PageStream::new(
1536 Arc::clone(self.req()),
1537 &format!("courses/{}/external_feeds", self.id),
1538 vec![],
1539 )
1540 }
1541
1542 pub async fn create_external_feed(&self, url: &str) -> Result<serde_json::Value> {
1547 let params = vec![("url".to_string(), url.to_string())];
1548 self.req()
1549 .post(&format!("courses/{}/external_feeds", self.id), ¶ms)
1550 .await
1551 }
1552
1553 pub async fn delete_external_feed(&self, feed_id: u64) -> Result<serde_json::Value> {
1558 self.req()
1559 .delete(
1560 &format!("courses/{}/external_feeds/{feed_id}", self.id),
1561 &[],
1562 )
1563 .await
1564 }
1565
1566 pub async fn create_course_section(&self, params: &[(String, String)]) -> Result<Section> {
1575 let mut s: Section = self
1576 .req()
1577 .post(&format!("courses/{}/sections", self.id), params)
1578 .await?;
1579 s.course_id = Some(self.id);
1580 s.requester = self.requester.clone();
1581 Ok(s)
1582 }
1583
1584 pub async fn import_outcomes(&self, params: &[(String, String)]) -> Result<OutcomeImport> {
1593 let context = format!("courses/{}", self.id);
1594 let mut import: OutcomeImport = self
1595 .req()
1596 .post(&format!("{}/outcome_imports", context), params)
1597 .await?;
1598 import.requester = self.requester.clone();
1599 import.course_id = Some(self.id);
1600 import.context_path = Some(context);
1601 Ok(import)
1602 }
1603
1604 pub async fn get_outcome_import_status(&self, import_id: u64) -> Result<OutcomeImport> {
1609 let context = format!("courses/{}", self.id);
1610 let mut import: OutcomeImport = self
1611 .req()
1612 .get(&format!("{}/outcome_imports/{import_id}", context), &[])
1613 .await?;
1614 import.requester = self.requester.clone();
1615 import.course_id = Some(self.id);
1616 import.context_path = Some(context);
1617 Ok(import)
1618 }
1619}