Skip to main content

canvas_lms_api/resources/
course.rs

1use crate::{
2    error::Result,
3    http::Requester,
4    pagination::PageStream,
5    params::wrap_params,
6    resources::{
7        assignment::Assignment,
8        discussion_topic::DiscussionTopic,
9        enrollment::Enrollment,
10        file::File,
11        group::Group,
12        module::Module,
13        page::Page,
14        params::{
15            assignment_params::CreateAssignmentParams, course_params::UpdateCourseParams,
16            quiz_params::CreateQuizParams,
17        },
18        quiz::Quiz,
19        section::Section,
20        tab::Tab,
21        types::WorkflowState,
22        user::User,
23    },
24};
25use chrono::{DateTime, Utc};
26use serde::{Deserialize, Serialize};
27use std::sync::Arc;
28
29#[derive(Debug, Clone, Deserialize, Serialize)]
30pub struct Course {
31    pub id: u64,
32    pub name: Option<String>,
33    pub course_code: Option<String>,
34    pub workflow_state: Option<WorkflowState>,
35    pub account_id: Option<u64>,
36    pub root_account_id: Option<u64>,
37    pub enrollment_term_id: Option<u64>,
38    pub sis_course_id: Option<String>,
39    pub start_at: Option<DateTime<Utc>>,
40    pub end_at: Option<DateTime<Utc>>,
41    pub grading_standard_id: Option<u64>,
42    pub is_public: Option<bool>,
43    pub license: Option<String>,
44    pub locale: Option<String>,
45    pub time_zone: Option<String>,
46    pub total_students: Option<u64>,
47    pub default_view: Option<String>,
48    pub syllabus_body: Option<String>,
49    pub public_description: Option<String>,
50    pub hide_final_grades: Option<bool>,
51    pub apply_assignment_group_weights: Option<bool>,
52    pub restrict_enrollments_to_course_dates: Option<bool>,
53
54    #[serde(skip)]
55    pub(crate) requester: Option<Arc<Requester>>,
56}
57
58impl Course {
59    fn req(&self) -> &Arc<Requester> {
60        self.requester.as_ref().expect("requester not initialized")
61    }
62
63    /// Stream all assignments in this course.
64    ///
65    /// # Canvas API
66    /// `GET /api/v1/courses/:course_id/assignments`
67    pub fn get_assignments(&self) -> PageStream<Assignment> {
68        PageStream::new(
69            Arc::clone(self.req()),
70            &format!("courses/{}/assignments", self.id),
71            vec![],
72        )
73    }
74
75    /// Fetch a single assignment.
76    ///
77    /// # Canvas API
78    /// `GET /api/v1/courses/:course_id/assignments/:id`
79    pub async fn get_assignment(&self, assignment_id: u64) -> Result<Assignment> {
80        self.req()
81            .get(
82                &format!("courses/{}/assignments/{assignment_id}", self.id),
83                &[],
84            )
85            .await
86    }
87
88    /// Create a new assignment in this course.
89    ///
90    /// # Canvas API
91    /// `POST /api/v1/courses/:id/assignments`
92    pub async fn create_assignment(&self, params: CreateAssignmentParams) -> Result<Assignment> {
93        let form = wrap_params("assignment", &params);
94        self.req()
95            .post(&format!("courses/{}/assignments", self.id), &form)
96            .await
97    }
98
99    /// Stream all sections in this course.
100    ///
101    /// # Canvas API
102    /// `GET /api/v1/courses/:course_id/sections`
103    pub fn get_sections(&self) -> PageStream<Section> {
104        PageStream::new(
105            Arc::clone(self.req()),
106            &format!("courses/{}/sections", self.id),
107            vec![],
108        )
109    }
110
111    /// Fetch a single section by ID.
112    ///
113    /// # Canvas API
114    /// `GET /api/v1/courses/:id/sections/:section_id`
115    pub async fn get_section(&self, section_id: u64) -> Result<Section> {
116        self.req()
117            .get(&format!("courses/{}/sections/{section_id}", self.id), &[])
118            .await
119    }
120
121    /// Stream all enrollments in this course.
122    ///
123    /// # Canvas API
124    /// `GET /api/v1/courses/:course_id/enrollments`
125    pub fn get_enrollments(&self) -> PageStream<Enrollment> {
126        PageStream::new(
127            Arc::clone(self.req()),
128            &format!("courses/{}/enrollments", self.id),
129            vec![],
130        )
131    }
132
133    /// Stream all users in this course.
134    ///
135    /// # Canvas API
136    /// `GET /api/v1/courses/:course_id/users`
137    pub fn get_users(&self) -> PageStream<User> {
138        PageStream::new(
139            Arc::clone(self.req()),
140            &format!("courses/{}/users", self.id),
141            vec![],
142        )
143    }
144
145    /// Update this course.
146    ///
147    /// # Canvas API
148    /// `PUT /api/v1/courses/:id`
149    pub async fn update(&self, params: UpdateCourseParams) -> Result<Course> {
150        let form = wrap_params("course", &params);
151        let mut course: Course = self
152            .req()
153            .put(&format!("courses/{}", self.id), &form)
154            .await?;
155        course.requester = self.requester.clone();
156        Ok(course)
157    }
158
159    /// Delete this course. Canvas returns the deleted course object.
160    ///
161    /// # Canvas API
162    /// `DELETE /api/v1/courses/:id`
163    pub async fn delete(&self) -> Result<Course> {
164        let params = vec![("event".to_string(), "delete".to_string())];
165        let mut course: Course = self
166            .req()
167            .delete(&format!("courses/{}", self.id), &params)
168            .await?;
169        course.requester = self.requester.clone();
170        Ok(course)
171    }
172
173    /// Stream all quizzes in this course.
174    ///
175    /// # Canvas API
176    /// `GET /api/v1/courses/:id/quizzes`
177    pub fn get_quizzes(&self) -> PageStream<Quiz> {
178        PageStream::new(
179            Arc::clone(self.req()),
180            &format!("courses/{}/quizzes", self.id),
181            vec![],
182        )
183    }
184
185    /// Fetch a single quiz.
186    ///
187    /// # Canvas API
188    /// `GET /api/v1/courses/:id/quizzes/:quiz_id`
189    pub async fn get_quiz(&self, quiz_id: u64) -> Result<Quiz> {
190        self.req()
191            .get(&format!("courses/{}/quizzes/{quiz_id}", self.id), &[])
192            .await
193    }
194
195    /// Create a new quiz in this course.
196    ///
197    /// # Canvas API
198    /// `POST /api/v1/courses/:id/quizzes`
199    pub async fn create_quiz(&self, params: CreateQuizParams) -> Result<Quiz> {
200        let form = wrap_params("quiz", &params);
201        self.req()
202            .post(&format!("courses/{}/quizzes", self.id), &form)
203            .await
204    }
205
206    /// Stream all modules in this course.
207    ///
208    /// # Canvas API
209    /// `GET /api/v1/courses/:id/modules`
210    pub fn get_modules(&self) -> PageStream<Module> {
211        PageStream::new(
212            Arc::clone(self.req()),
213            &format!("courses/{}/modules", self.id),
214            vec![],
215        )
216    }
217
218    /// Fetch a single module.
219    ///
220    /// # Canvas API
221    /// `GET /api/v1/courses/:id/modules/:module_id`
222    pub async fn get_module(&self, module_id: u64) -> Result<Module> {
223        self.req()
224            .get(&format!("courses/{}/modules/{module_id}", self.id), &[])
225            .await
226    }
227
228    /// Stream all pages in this course.
229    ///
230    /// # Canvas API
231    /// `GET /api/v1/courses/:id/pages`
232    pub fn get_pages(&self) -> PageStream<Page> {
233        PageStream::new(
234            Arc::clone(self.req()),
235            &format!("courses/{}/pages", self.id),
236            vec![],
237        )
238    }
239
240    /// Fetch a single page by URL slug or ID.
241    ///
242    /// # Canvas API
243    /// `GET /api/v1/courses/:id/pages/:url_or_id`
244    pub async fn get_page(&self, url_or_id: &str) -> Result<Page> {
245        self.req()
246            .get(&format!("courses/{}/pages/{url_or_id}", self.id), &[])
247            .await
248    }
249
250    /// Stream all discussion topics in this course.
251    ///
252    /// # Canvas API
253    /// `GET /api/v1/courses/:id/discussion_topics`
254    pub fn get_discussion_topics(&self) -> PageStream<DiscussionTopic> {
255        PageStream::new(
256            Arc::clone(self.req()),
257            &format!("courses/{}/discussion_topics", self.id),
258            vec![],
259        )
260    }
261
262    /// Fetch a single discussion topic.
263    ///
264    /// # Canvas API
265    /// `GET /api/v1/courses/:id/discussion_topics/:topic_id`
266    pub async fn get_discussion_topic(&self, topic_id: u64) -> Result<DiscussionTopic> {
267        self.req()
268            .get(
269                &format!("courses/{}/discussion_topics/{topic_id}", self.id),
270                &[],
271            )
272            .await
273    }
274
275    /// Stream all files in this course.
276    ///
277    /// # Canvas API
278    /// `GET /api/v1/courses/:id/files`
279    pub fn get_files(&self) -> PageStream<File> {
280        PageStream::new(
281            Arc::clone(self.req()),
282            &format!("courses/{}/files", self.id),
283            vec![],
284        )
285    }
286
287    /// Stream all tabs in this course.
288    ///
289    /// # Canvas API
290    /// `GET /api/v1/courses/:id/tabs`
291    pub fn get_tabs(&self) -> PageStream<Tab> {
292        PageStream::new(
293            Arc::clone(self.req()),
294            &format!("courses/{}/tabs", self.id),
295            vec![],
296        )
297    }
298
299    /// Stream all groups in this course.
300    ///
301    /// # Canvas API
302    /// `GET /api/v1/courses/:id/groups`
303    pub fn get_groups(&self) -> PageStream<Group> {
304        PageStream::new(
305            Arc::clone(self.req()),
306            &format!("courses/{}/groups", self.id),
307            vec![],
308        )
309    }
310
311    /// Upload a file to this course.
312    ///
313    /// Canvas uses a two-step upload: first POSTing metadata to obtain an upload URL,
314    /// then POSTing the file as multipart form data to that URL.
315    ///
316    /// # Canvas API
317    /// `POST /api/v1/courses/:id/files`
318    pub async fn upload_file(
319        &self,
320        request: crate::upload::UploadRequest,
321        data: Vec<u8>,
322    ) -> crate::error::Result<crate::resources::file::File> {
323        crate::upload::initiate_and_upload(
324            self.req(),
325            &format!("courses/{}/files", self.id),
326            request,
327            data,
328        )
329        .await
330    }
331}