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_migration::{ContentMigration, Migrator},
10 discussion_topic::DiscussionTopic,
11 enrollment::Enrollment,
12 external_tool::{ExternalTool, ExternalToolParams},
13 file::File,
14 gradebook_history::{Day, Grader, SubmissionHistory, SubmissionVersion},
15 group::Group,
16 module::Module,
17 outcome::{OutcomeGroup, OutcomeLink, UpdateOutcomeGroupParams},
18 page::Page,
19 params::{
20 assignment_params::CreateAssignmentParams, course_params::UpdateCourseParams,
21 quiz_params::CreateQuizParams,
22 },
23 quiz::Quiz,
24 rubric::{Rubric, RubricAssociation, RubricParams},
25 section::Section,
26 tab::Tab,
27 types::WorkflowState,
28 user::User,
29 },
30};
31use chrono::{DateTime, Utc};
32use serde::{Deserialize, Serialize};
33use std::sync::Arc;
34
35#[derive(Debug, Clone, Deserialize, Serialize)]
36pub struct Course {
37 pub id: u64,
38 pub name: Option<String>,
39 pub course_code: Option<String>,
40 pub workflow_state: Option<WorkflowState>,
41 pub account_id: Option<u64>,
42 pub root_account_id: Option<u64>,
43 pub enrollment_term_id: Option<u64>,
44 pub sis_course_id: Option<String>,
45 pub start_at: Option<DateTime<Utc>>,
46 pub end_at: Option<DateTime<Utc>>,
47 pub grading_standard_id: Option<u64>,
48 pub is_public: Option<bool>,
49 pub license: Option<String>,
50 pub locale: Option<String>,
51 pub time_zone: Option<String>,
52 pub total_students: Option<u64>,
53 pub default_view: Option<String>,
54 pub syllabus_body: Option<String>,
55 pub public_description: Option<String>,
56 pub hide_final_grades: Option<bool>,
57 pub apply_assignment_group_weights: Option<bool>,
58 pub restrict_enrollments_to_course_dates: Option<bool>,
59
60 #[serde(skip)]
61 pub(crate) requester: Option<Arc<Requester>>,
62}
63
64impl Course {
65 fn req(&self) -> &Arc<Requester> {
66 self.requester.as_ref().expect("requester not initialized")
67 }
68
69 pub fn get_assignments(&self) -> PageStream<Assignment> {
74 PageStream::new(
75 Arc::clone(self.req()),
76 &format!("courses/{}/assignments", self.id),
77 vec![],
78 )
79 }
80
81 pub async fn get_assignment(&self, assignment_id: u64) -> Result<Assignment> {
86 self.req()
87 .get(
88 &format!("courses/{}/assignments/{assignment_id}", self.id),
89 &[],
90 )
91 .await
92 }
93
94 pub async fn create_assignment(&self, params: CreateAssignmentParams) -> Result<Assignment> {
99 let form = wrap_params("assignment", ¶ms);
100 self.req()
101 .post(&format!("courses/{}/assignments", self.id), &form)
102 .await
103 }
104
105 pub fn get_sections(&self) -> PageStream<Section> {
110 PageStream::new(
111 Arc::clone(self.req()),
112 &format!("courses/{}/sections", self.id),
113 vec![],
114 )
115 }
116
117 pub async fn get_section(&self, section_id: u64) -> Result<Section> {
122 self.req()
123 .get(&format!("courses/{}/sections/{section_id}", self.id), &[])
124 .await
125 }
126
127 pub fn get_enrollments(&self) -> PageStream<Enrollment> {
132 PageStream::new(
133 Arc::clone(self.req()),
134 &format!("courses/{}/enrollments", self.id),
135 vec![],
136 )
137 }
138
139 pub fn get_users(&self) -> PageStream<User> {
144 PageStream::new(
145 Arc::clone(self.req()),
146 &format!("courses/{}/users", self.id),
147 vec![],
148 )
149 }
150
151 pub async fn update(&self, params: UpdateCourseParams) -> Result<Course> {
156 let form = wrap_params("course", ¶ms);
157 let mut course: Course = self
158 .req()
159 .put(&format!("courses/{}", self.id), &form)
160 .await?;
161 course.requester = self.requester.clone();
162 Ok(course)
163 }
164
165 pub async fn delete(&self) -> Result<Course> {
170 let params = vec![("event".to_string(), "delete".to_string())];
171 let mut course: Course = self
172 .req()
173 .delete(&format!("courses/{}", self.id), ¶ms)
174 .await?;
175 course.requester = self.requester.clone();
176 Ok(course)
177 }
178
179 pub fn get_quizzes(&self) -> PageStream<Quiz> {
184 PageStream::new(
185 Arc::clone(self.req()),
186 &format!("courses/{}/quizzes", self.id),
187 vec![],
188 )
189 }
190
191 pub async fn get_quiz(&self, quiz_id: u64) -> Result<Quiz> {
196 self.req()
197 .get(&format!("courses/{}/quizzes/{quiz_id}", self.id), &[])
198 .await
199 }
200
201 pub async fn create_quiz(&self, params: CreateQuizParams) -> Result<Quiz> {
206 let form = wrap_params("quiz", ¶ms);
207 self.req()
208 .post(&format!("courses/{}/quizzes", self.id), &form)
209 .await
210 }
211
212 pub fn get_modules(&self) -> PageStream<Module> {
217 PageStream::new(
218 Arc::clone(self.req()),
219 &format!("courses/{}/modules", self.id),
220 vec![],
221 )
222 }
223
224 pub async fn get_module(&self, module_id: u64) -> Result<Module> {
229 self.req()
230 .get(&format!("courses/{}/modules/{module_id}", self.id), &[])
231 .await
232 }
233
234 pub fn get_pages(&self) -> PageStream<Page> {
239 PageStream::new(
240 Arc::clone(self.req()),
241 &format!("courses/{}/pages", self.id),
242 vec![],
243 )
244 }
245
246 pub async fn get_page(&self, url_or_id: &str) -> Result<Page> {
251 self.req()
252 .get(&format!("courses/{}/pages/{url_or_id}", self.id), &[])
253 .await
254 }
255
256 pub fn get_discussion_topics(&self) -> PageStream<DiscussionTopic> {
261 PageStream::new(
262 Arc::clone(self.req()),
263 &format!("courses/{}/discussion_topics", self.id),
264 vec![],
265 )
266 }
267
268 pub async fn get_discussion_topic(&self, topic_id: u64) -> Result<DiscussionTopic> {
273 self.req()
274 .get(
275 &format!("courses/{}/discussion_topics/{topic_id}", self.id),
276 &[],
277 )
278 .await
279 }
280
281 pub fn get_files(&self) -> PageStream<File> {
286 PageStream::new(
287 Arc::clone(self.req()),
288 &format!("courses/{}/files", self.id),
289 vec![],
290 )
291 }
292
293 pub fn get_tabs(&self) -> PageStream<Tab> {
298 PageStream::new(
299 Arc::clone(self.req()),
300 &format!("courses/{}/tabs", self.id),
301 vec![],
302 )
303 }
304
305 pub fn get_groups(&self) -> PageStream<Group> {
310 PageStream::new(
311 Arc::clone(self.req()),
312 &format!("courses/{}/groups", self.id),
313 vec![],
314 )
315 }
316
317 pub async fn upload_file(
325 &self,
326 request: crate::upload::UploadRequest,
327 data: Vec<u8>,
328 ) -> crate::error::Result<crate::resources::file::File> {
329 crate::upload::initiate_and_upload(
330 self.req(),
331 &format!("courses/{}/files", self.id),
332 request,
333 data,
334 )
335 .await
336 }
337
338 pub async fn get_external_tool(&self, tool_id: u64) -> Result<ExternalTool> {
347 let mut tool: ExternalTool = self
348 .req()
349 .get(
350 &format!("courses/{}/external_tools/{tool_id}", self.id),
351 &[],
352 )
353 .await?;
354 tool.requester = self.requester.clone();
355 Ok(tool)
356 }
357
358 pub fn get_external_tools(&self) -> PageStream<ExternalTool> {
363 let course_id = self.id;
364 PageStream::new_with_injector(
365 Arc::clone(self.req()),
366 &format!("courses/{course_id}/external_tools"),
367 vec![],
368 |mut t: ExternalTool, req| {
369 t.requester = Some(Arc::clone(&req));
370 t
371 },
372 )
373 }
374
375 pub async fn create_external_tool(&self, params: ExternalToolParams) -> Result<ExternalTool> {
380 let form = wrap_params("external_tool", ¶ms);
381 let mut tool: ExternalTool = self
382 .req()
383 .post(&format!("courses/{}/external_tools", self.id), &form)
384 .await?;
385 tool.requester = self.requester.clone();
386 Ok(tool)
387 }
388
389 pub async fn get_rubric(&self, rubric_id: u64) -> Result<Rubric> {
398 let mut rubric: Rubric = self
399 .req()
400 .get(&format!("courses/{}/rubrics/{rubric_id}", self.id), &[])
401 .await?;
402 rubric.requester = self.requester.clone();
403 Ok(rubric)
404 }
405
406 pub fn get_rubrics(&self) -> PageStream<Rubric> {
411 let course_id = self.id;
412 PageStream::new_with_injector(
413 Arc::clone(self.req()),
414 &format!("courses/{course_id}/rubrics"),
415 vec![],
416 |mut r: Rubric, req| {
417 r.requester = Some(Arc::clone(&req));
418 r
419 },
420 )
421 }
422
423 pub async fn create_rubric(&self, params: RubricParams) -> Result<Rubric> {
428 let form = wrap_params("rubric", ¶ms);
429 let mut rubric: Rubric = self
430 .req()
431 .post(&format!("courses/{}/rubrics", self.id), &form)
432 .await?;
433 rubric.requester = self.requester.clone();
434 Ok(rubric)
435 }
436
437 pub async fn get_rubric_association(&self, association_id: u64) -> Result<RubricAssociation> {
442 let mut assoc: RubricAssociation = self
443 .req()
444 .get(
445 &format!("courses/{}/rubric_associations/{association_id}", self.id),
446 &[],
447 )
448 .await?;
449 assoc.requester = self.requester.clone();
450 Ok(assoc)
451 }
452
453 pub fn get_rubric_associations(&self) -> PageStream<RubricAssociation> {
458 let course_id = self.id;
459 PageStream::new_with_injector(
460 Arc::clone(self.req()),
461 &format!("courses/{course_id}/rubric_associations"),
462 vec![],
463 |mut a: RubricAssociation, req| {
464 a.requester = Some(Arc::clone(&req));
465 a
466 },
467 )
468 }
469
470 pub async fn get_blueprint(&self, template_id: &str) -> Result<BlueprintTemplate> {
481 let mut tmpl: BlueprintTemplate = self
482 .req()
483 .get(
484 &format!("courses/{}/blueprint_templates/{template_id}", self.id),
485 &[],
486 )
487 .await?;
488 tmpl.requester = self.requester.clone();
489 Ok(tmpl)
490 }
491
492 pub fn get_blueprint_subscriptions(&self) -> PageStream<BlueprintSubscription> {
497 let course_id = self.id;
498 PageStream::new_with_injector(
499 Arc::clone(self.req()),
500 &format!("courses/{course_id}/blueprint_subscriptions"),
501 vec![],
502 |mut s: BlueprintSubscription, req| {
503 s.requester = Some(Arc::clone(&req));
504 s
505 },
506 )
507 }
508
509 pub async fn get_content_migration(&self, migration_id: u64) -> Result<ContentMigration> {
518 let mut migration: ContentMigration = self
519 .req()
520 .get(
521 &format!("courses/{}/content_migrations/{migration_id}", self.id),
522 &[],
523 )
524 .await?;
525 migration.requester = self.requester.clone();
526 Ok(migration)
527 }
528
529 pub fn get_content_migrations(&self) -> PageStream<ContentMigration> {
534 let course_id = self.id;
535 PageStream::new_with_injector(
536 Arc::clone(self.req()),
537 &format!("courses/{course_id}/content_migrations"),
538 vec![],
539 |mut m: ContentMigration, req| {
540 m.requester = Some(Arc::clone(&req));
541 m
542 },
543 )
544 }
545
546 pub async fn create_content_migration(
551 &self,
552 migration_type: &str,
553 params: &[(String, String)],
554 ) -> Result<ContentMigration> {
555 let mut form = vec![("migration_type".to_string(), migration_type.to_string())];
556 form.extend_from_slice(params);
557 let mut migration: ContentMigration = self
558 .req()
559 .post(&format!("courses/{}/content_migrations", self.id), &form)
560 .await?;
561 migration.requester = self.requester.clone();
562 Ok(migration)
563 }
564
565 pub fn get_migrators(&self) -> PageStream<Migrator> {
570 PageStream::new(
571 Arc::clone(self.req()),
572 &format!("courses/{}/content_migrations/migrators", self.id),
573 vec![],
574 )
575 }
576
577 pub fn get_outcome_group_links(&self) -> PageStream<OutcomeLink> {
586 PageStream::new(
587 Arc::clone(self.req()),
588 &format!("courses/{}/outcome_group_links", self.id),
589 vec![],
590 )
591 }
592
593 pub async fn get_outcome_group(&self, group_id: u64) -> Result<OutcomeGroup> {
598 let mut group: OutcomeGroup = self
599 .req()
600 .get(
601 &format!("courses/{}/outcome_groups/{group_id}", self.id),
602 &[],
603 )
604 .await?;
605 group.requester = self.requester.clone();
606 Ok(group)
607 }
608
609 pub async fn create_outcome_group(
614 &self,
615 params: UpdateOutcomeGroupParams,
616 ) -> Result<OutcomeGroup> {
617 let form = wrap_params("outcome_group", ¶ms);
618 let mut group: OutcomeGroup = self
619 .req()
620 .post(&format!("courses/{}/outcome_groups", self.id), &form)
621 .await?;
622 group.requester = self.requester.clone();
623 Ok(group)
624 }
625
626 pub fn get_gradebook_history_dates(&self) -> PageStream<Day> {
635 PageStream::new(
636 Arc::clone(self.req()),
637 &format!("courses/{}/gradebook_history/days", self.id),
638 vec![],
639 )
640 }
641
642 pub fn get_gradebook_history_details(&self, date: &str) -> PageStream<Grader> {
649 PageStream::new(
650 Arc::clone(self.req()),
651 &format!("courses/{}/gradebook_history/{date}", self.id),
652 vec![],
653 )
654 }
655
656 pub fn get_submission_history(
661 &self,
662 date: &str,
663 grader_id: u64,
664 assignment_id: u64,
665 ) -> PageStream<SubmissionHistory> {
666 PageStream::new(
667 Arc::clone(self.req()),
668 &format!(
669 "courses/{}/gradebook_history/{date}/graders/{grader_id}/assignments/{assignment_id}/submissions",
670 self.id
671 ),
672 vec![],
673 )
674 }
675
676 pub fn get_uncollated_submissions(&self) -> PageStream<SubmissionVersion> {
681 PageStream::new(
682 Arc::clone(self.req()),
683 &format!("courses/{}/gradebook_history/feed", self.id),
684 vec![],
685 )
686 }
687
688 #[cfg(feature = "new-quizzes")]
697 pub async fn get_new_quiz(
698 &self,
699 assignment_id: &str,
700 ) -> Result<crate::resources::new_quiz::NewQuiz> {
701 let mut quiz: crate::resources::new_quiz::NewQuiz = self
702 .req()
703 .nq_get(&format!("courses/{}/quizzes/{assignment_id}", self.id), &[])
704 .await?;
705 quiz.requester = self.requester.clone();
706 quiz.course_id = Some(self.id);
707 Ok(quiz)
708 }
709
710 #[cfg(feature = "new-quizzes")]
715 pub fn get_new_quizzes(&self) -> PageStream<crate::resources::new_quiz::NewQuiz> {
716 let course_id = self.id;
717 PageStream::new_with_injector_nq(
718 Arc::clone(self.req()),
719 &format!("courses/{course_id}/quizzes"),
720 vec![],
721 move |mut q: crate::resources::new_quiz::NewQuiz, req| {
722 q.requester = Some(Arc::clone(&req));
723 q.course_id = Some(course_id);
724 q
725 },
726 )
727 }
728
729 #[cfg(feature = "new-quizzes")]
734 pub async fn create_new_quiz(
735 &self,
736 params: crate::resources::new_quiz::NewQuizParams,
737 ) -> Result<crate::resources::new_quiz::NewQuiz> {
738 let body = serde_json::to_value(¶ms).unwrap_or_default();
739 let mut quiz: crate::resources::new_quiz::NewQuiz = self
740 .req()
741 .nq_post(&format!("courses/{}/quizzes", self.id), &body)
742 .await?;
743 quiz.requester = self.requester.clone();
744 quiz.course_id = Some(self.id);
745 Ok(quiz)
746 }
747}