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, 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 fn get_rubric_associations(&self) -> PageStream<RubricAssociation> {
672 let course_id = self.id;
673 PageStream::new_with_injector(
674 Arc::clone(self.req()),
675 &format!("courses/{course_id}/rubric_associations"),
676 vec![],
677 |mut a: RubricAssociation, req| {
678 a.requester = Some(Arc::clone(&req));
679 a
680 },
681 )
682 }
683
684 pub async fn get_blueprint(&self, template_id: &str) -> Result<BlueprintTemplate> {
695 let mut tmpl: BlueprintTemplate = self
696 .req()
697 .get(
698 &format!("courses/{}/blueprint_templates/{template_id}", self.id),
699 &[],
700 )
701 .await?;
702 tmpl.requester = self.requester.clone();
703 Ok(tmpl)
704 }
705
706 pub fn get_blueprint_subscriptions(&self) -> PageStream<BlueprintSubscription> {
711 let course_id = self.id;
712 PageStream::new_with_injector(
713 Arc::clone(self.req()),
714 &format!("courses/{course_id}/blueprint_subscriptions"),
715 vec![],
716 |mut s: BlueprintSubscription, req| {
717 s.requester = Some(Arc::clone(&req));
718 s
719 },
720 )
721 }
722
723 pub async fn get_content_migration(&self, migration_id: u64) -> Result<ContentMigration> {
732 let mut migration: ContentMigration = self
733 .req()
734 .get(
735 &format!("courses/{}/content_migrations/{migration_id}", self.id),
736 &[],
737 )
738 .await?;
739 migration.requester = self.requester.clone();
740 Ok(migration)
741 }
742
743 pub fn get_content_migrations(&self) -> PageStream<ContentMigration> {
748 let course_id = self.id;
749 PageStream::new_with_injector(
750 Arc::clone(self.req()),
751 &format!("courses/{course_id}/content_migrations"),
752 vec![],
753 |mut m: ContentMigration, req| {
754 m.requester = Some(Arc::clone(&req));
755 m
756 },
757 )
758 }
759
760 pub async fn create_content_migration(
765 &self,
766 migration_type: &str,
767 params: &[(String, String)],
768 ) -> Result<ContentMigration> {
769 let mut form = vec![("migration_type".to_string(), migration_type.to_string())];
770 form.extend_from_slice(params);
771 let mut migration: ContentMigration = self
772 .req()
773 .post(&format!("courses/{}/content_migrations", self.id), &form)
774 .await?;
775 migration.requester = self.requester.clone();
776 Ok(migration)
777 }
778
779 pub fn get_migrators(&self) -> PageStream<Migrator> {
784 PageStream::new(
785 Arc::clone(self.req()),
786 &format!("courses/{}/content_migrations/migrators", self.id),
787 vec![],
788 )
789 }
790
791 pub fn get_outcome_group_links(&self) -> PageStream<OutcomeLink> {
800 PageStream::new(
801 Arc::clone(self.req()),
802 &format!("courses/{}/outcome_group_links", self.id),
803 vec![],
804 )
805 }
806
807 pub async fn get_root_outcome_group(&self) -> Result<OutcomeGroup> {
812 let mut group: OutcomeGroup = self
813 .req()
814 .get(&format!("courses/{}/root_outcome_group", self.id), &[])
815 .await?;
816 group.requester = self.requester.clone();
817 Ok(group)
818 }
819
820 pub async fn get_outcome_group(&self, group_id: u64) -> Result<OutcomeGroup> {
825 let mut group: OutcomeGroup = self
826 .req()
827 .get(
828 &format!("courses/{}/outcome_groups/{group_id}", self.id),
829 &[],
830 )
831 .await?;
832 group.requester = self.requester.clone();
833 Ok(group)
834 }
835
836 pub async fn create_outcome_group(
841 &self,
842 params: UpdateOutcomeGroupParams,
843 ) -> Result<OutcomeGroup> {
844 let form = wrap_params("outcome_group", ¶ms);
845 let mut group: OutcomeGroup = self
846 .req()
847 .post(&format!("courses/{}/outcome_groups", self.id), &form)
848 .await?;
849 group.requester = self.requester.clone();
850 Ok(group)
851 }
852
853 pub fn get_custom_columns(&self) -> PageStream<CustomGradebookColumn> {
862 let course_id = self.id;
863 PageStream::new_with_injector(
864 Arc::clone(self.req()),
865 &format!("courses/{course_id}/custom_gradebook_columns"),
866 vec![],
867 move |mut col: CustomGradebookColumn, req| {
868 col.requester = Some(Arc::clone(&req));
869 col.course_id = Some(course_id);
870 col
871 },
872 )
873 }
874
875 pub async fn create_custom_column(
880 &self,
881 params: CustomGradebookColumnParams,
882 ) -> Result<CustomGradebookColumn> {
883 let form = wrap_params("column", ¶ms);
884 let mut col: CustomGradebookColumn = self
885 .req()
886 .post(
887 &format!("courses/{}/custom_gradebook_columns", self.id),
888 &form,
889 )
890 .await?;
891 col.requester = self.requester.clone();
892 col.course_id = Some(self.id);
893 Ok(col)
894 }
895
896 pub fn get_gradebook_history_dates(&self) -> PageStream<Day> {
905 PageStream::new(
906 Arc::clone(self.req()),
907 &format!("courses/{}/gradebook_history/days", self.id),
908 vec![],
909 )
910 }
911
912 pub fn get_gradebook_history_details(&self, date: &str) -> PageStream<Grader> {
919 PageStream::new(
920 Arc::clone(self.req()),
921 &format!("courses/{}/gradebook_history/{date}", self.id),
922 vec![],
923 )
924 }
925
926 pub fn get_submission_history(
931 &self,
932 date: &str,
933 grader_id: u64,
934 assignment_id: u64,
935 ) -> PageStream<SubmissionHistory> {
936 PageStream::new(
937 Arc::clone(self.req()),
938 &format!(
939 "courses/{}/gradebook_history/{date}/graders/{grader_id}/assignments/{assignment_id}/submissions",
940 self.id
941 ),
942 vec![],
943 )
944 }
945
946 pub fn get_uncollated_submissions(&self) -> PageStream<SubmissionVersion> {
951 PageStream::new(
952 Arc::clone(self.req()),
953 &format!("courses/{}/gradebook_history/feed", self.id),
954 vec![],
955 )
956 }
957
958 #[cfg(feature = "new-quizzes")]
967 pub async fn get_new_quiz(
968 &self,
969 assignment_id: &str,
970 ) -> Result<crate::resources::new_quiz::NewQuiz> {
971 let mut quiz: crate::resources::new_quiz::NewQuiz = self
972 .req()
973 .nq_get(&format!("courses/{}/quizzes/{assignment_id}", self.id), &[])
974 .await?;
975 quiz.requester = self.requester.clone();
976 quiz.course_id = Some(self.id);
977 Ok(quiz)
978 }
979
980 #[cfg(feature = "new-quizzes")]
985 pub fn get_new_quizzes(&self) -> PageStream<crate::resources::new_quiz::NewQuiz> {
986 let course_id = self.id;
987 PageStream::new_with_injector_nq(
988 Arc::clone(self.req()),
989 &format!("courses/{course_id}/quizzes"),
990 vec![],
991 move |mut q: crate::resources::new_quiz::NewQuiz, req| {
992 q.requester = Some(Arc::clone(&req));
993 q.course_id = Some(course_id);
994 q
995 },
996 )
997 }
998
999 #[cfg(feature = "new-quizzes")]
1004 pub async fn create_new_quiz(
1005 &self,
1006 params: crate::resources::new_quiz::NewQuizParams,
1007 ) -> Result<crate::resources::new_quiz::NewQuiz> {
1008 let body = serde_json::to_value(¶ms).unwrap_or_default();
1009 let mut quiz: crate::resources::new_quiz::NewQuiz = self
1010 .req()
1011 .nq_post(&format!("courses/{}/quizzes", self.id), &body)
1012 .await?;
1013 quiz.requester = self.requester.clone();
1014 quiz.course_id = Some(self.id);
1015 Ok(quiz)
1016 }
1017
1018 pub fn get_grading_periods(&self) -> PageStream<GradingPeriod> {
1027 let course_id = self.id;
1028 PageStream::new_with_injector(
1029 Arc::clone(self.req()),
1030 &format!("courses/{course_id}/grading_periods"),
1031 vec![],
1032 move |mut gp: GradingPeriod, req| {
1033 gp.requester = Some(Arc::clone(&req));
1034 gp.course_id = Some(course_id);
1035 gp
1036 },
1037 )
1038 }
1039
1040 pub fn get_grading_standards(&self) -> PageStream<GradingStandard> {
1049 PageStream::new(
1050 Arc::clone(self.req()),
1051 &format!("courses/{}/grading_standards", self.id),
1052 vec![],
1053 )
1054 }
1055
1056 pub async fn create_grading_standard(
1061 &self,
1062 params: crate::resources::grading_standard::GradingStandardParams,
1063 ) -> Result<GradingStandard> {
1064 let form = wrap_params("grading_scheme_entry", ¶ms.grading_scheme_entry)
1065 .into_iter()
1066 .chain([("title".into(), params.title)])
1067 .collect::<Vec<_>>();
1068 self.req()
1069 .post(&format!("courses/{}/grading_standards", self.id), &form)
1070 .await
1071 }
1072
1073 pub async fn get_content_export(&self, export_id: u64) -> Result<ContentExport> {
1082 self.req()
1083 .get(
1084 &format!("courses/{}/content_exports/{export_id}", self.id),
1085 &[],
1086 )
1087 .await
1088 }
1089
1090 pub fn get_content_exports(&self) -> PageStream<ContentExport> {
1095 PageStream::new(
1096 Arc::clone(self.req()),
1097 &format!("courses/{}/content_exports", self.id),
1098 vec![],
1099 )
1100 }
1101
1102 pub async fn create_content_export(
1107 &self,
1108 params: ContentExportParams,
1109 ) -> Result<ContentExport> {
1110 let form = vec![
1111 ("export_type".into(), params.export_type),
1112 (
1113 "skip_notifications".into(),
1114 params.skip_notifications.unwrap_or(false).to_string(),
1115 ),
1116 ];
1117 self.req()
1118 .post(&format!("courses/{}/content_exports", self.id), &form)
1119 .await
1120 }
1121
1122 pub fn get_grade_change_events(&self) -> PageStream<GradeChangeEvent> {
1134 PageStream::new(
1135 Arc::clone(self.req()),
1136 &format!("audit/grade_change/courses/{}", self.id),
1137 vec![],
1138 )
1139 }
1140
1141 pub fn get_features(&self) -> PageStream<Feature> {
1150 PageStream::new(
1151 Arc::clone(self.req()),
1152 &format!("courses/{}/features", self.id),
1153 vec![],
1154 )
1155 }
1156
1157 pub async fn get_feature_flag(&self, feature: &str) -> Result<FeatureFlag> {
1162 let mut ff: FeatureFlag = self
1163 .req()
1164 .get(
1165 &format!("courses/{}/features/flags/{feature}", self.id),
1166 &[],
1167 )
1168 .await?;
1169 ff.requester = self.requester.clone();
1170 Ok(ff)
1171 }
1172
1173 pub async fn get_enabled_features(&self) -> Result<Vec<String>> {
1178 self.req()
1179 .get(&format!("courses/{}/features/enabled", self.id), &[])
1180 .await
1181 }
1182
1183 pub fn get_lti_resource_links(&self) -> PageStream<LtiResourceLink> {
1192 PageStream::new(
1193 Arc::clone(self.req()),
1194 &format!("courses/{}/lti_resource_links", self.id),
1195 vec![],
1196 )
1197 }
1198
1199 pub async fn get_lti_resource_link(&self, link_id: u64) -> Result<LtiResourceLink> {
1204 self.req()
1205 .get(
1206 &format!("courses/{}/lti_resource_links/{link_id}", self.id),
1207 &[],
1208 )
1209 .await
1210 }
1211
1212 pub async fn create_lti_resource_link(
1217 &self,
1218 params: CreateLtiResourceLinkParams,
1219 ) -> Result<LtiResourceLink> {
1220 let form = crate::params::flatten_params(&serde_json::to_value(¶ms).unwrap());
1221 self.req()
1222 .post(&format!("courses/{}/lti_resource_links", self.id), &form)
1223 .await
1224 }
1225
1226 pub async fn conclude(&self) -> Result<serde_json::Value> {
1231 self.req()
1232 .delete(
1233 &format!("courses/{}", self.id),
1234 &[("event".to_string(), "conclude".to_string())],
1235 )
1236 .await
1237 }
1238
1239 pub async fn reset(&self) -> Result<Course> {
1244 let mut c: Course = self
1245 .req()
1246 .post(&format!("courses/{}/reset_content", self.id), &[])
1247 .await?;
1248 c.requester = Some(Arc::clone(self.req()));
1249 Ok(c)
1250 }
1251
1252 pub async fn get_settings(&self) -> Result<serde_json::Value> {
1257 self.req()
1258 .get(&format!("courses/{}/settings", self.id), &[])
1259 .await
1260 }
1261
1262 pub async fn update_settings(&self, params: &[(String, String)]) -> Result<serde_json::Value> {
1267 self.req()
1268 .put(&format!("courses/{}/settings", self.id), params)
1269 .await
1270 }
1271
1272 pub async fn get_late_policy(&self) -> Result<serde_json::Value> {
1277 self.req()
1278 .get(&format!("courses/{}/late_policy", self.id), &[])
1279 .await
1280 }
1281
1282 pub fn get_multiple_submissions(&self) -> PageStream<crate::resources::submission::Submission> {
1287 let course_id = self.id;
1288 PageStream::new_with_injector(
1289 Arc::clone(self.req()),
1290 &format!("courses/{}/students/submissions", self.id),
1291 vec![],
1292 move |mut s: crate::resources::submission::Submission, req| {
1293 s.requester = Some(Arc::clone(&req));
1294 s.course_id = Some(course_id);
1295 s
1296 },
1297 )
1298 }
1299
1300 pub async fn enroll_user(
1305 &self,
1306 user_id: u64,
1307 enrollment_type: &str,
1308 ) -> Result<crate::resources::enrollment::Enrollment> {
1309 let params = vec![
1310 ("enrollment[user_id]".to_string(), user_id.to_string()),
1311 ("enrollment[type]".to_string(), enrollment_type.to_string()),
1312 ];
1313 let mut e: crate::resources::enrollment::Enrollment = self
1314 .req()
1315 .post(&format!("courses/{}/enrollments", self.id), ¶ms)
1316 .await?;
1317 e.requester = Some(Arc::clone(self.req()));
1318 Ok(e)
1319 }
1320
1321 pub async fn submissions_bulk_update(
1326 &self,
1327 params: &[(String, String)],
1328 ) -> Result<crate::resources::progress::Progress> {
1329 let mut p: crate::resources::progress::Progress = self
1330 .req()
1331 .post(
1332 &format!("courses/{}/submissions/update_grades", self.id),
1333 params,
1334 )
1335 .await?;
1336 p.requester = Some(Arc::clone(self.req()));
1337 Ok(p)
1338 }
1339}