@@ -10,8 +10,9 @@ pub use self::exercise::{
10
10
} ;
11
11
use crate :: error:: { MoocClientError , MoocClientResult } ;
12
12
use bytes:: Bytes ;
13
+ use chrono:: { DateTime , Utc } ;
13
14
use exercise:: UserAnswer ;
14
- pub use mooc_langs_api:: * ;
15
+ pub use mooc_langs_api as api ;
15
16
use oauth2:: TokenResponse ;
16
17
use reqwest:: {
17
18
blocking:: {
@@ -23,6 +24,8 @@ use reqwest::{
23
24
use serde:: { de:: DeserializeOwned , Serialize } ;
24
25
use std:: { borrow:: Cow , path:: Path , sync:: Arc } ;
25
26
use tmc_langs_util:: { serialize, JsonError } ;
27
+ #[ cfg( feature = "ts-rs" ) ]
28
+ use ts_rs:: TS ;
26
29
use uuid:: Uuid ;
27
30
28
31
/// Client for accessing the Courses MOOC API.
@@ -33,7 +36,7 @@ pub struct MoocClient(Arc<MoocClientInner>);
33
36
struct MoocClientInner {
34
37
client : Client ,
35
38
root_addr : Cow < ' static , str > ,
36
- token : Option < Token > ,
39
+ token : Option < api :: Token > ,
37
40
}
38
41
39
42
/// Non-API methods.
@@ -78,7 +81,7 @@ impl MoocClient {
78
81
}
79
82
}
80
83
81
- pub fn set_token ( & mut self , token : Token ) {
84
+ pub fn set_token ( & mut self , token : api :: Token ) {
82
85
Arc :: get_mut ( & mut self . 0 )
83
86
. expect ( "called when multiple clones exist" )
84
87
. token = Some ( token) ;
@@ -90,8 +93,8 @@ impl MoocClient {
90
93
pub fn course_instances ( & self ) -> MoocClientResult < Vec < CourseInstance > > {
91
94
let res = self
92
95
. request ( Method :: GET , "course-instances" )
93
- . send_expect_json ( ) ?;
94
- Ok ( res)
96
+ . send_expect_json :: < Vec < api :: CourseInstance > > ( ) ?;
97
+ Ok ( res. into_iter ( ) . map ( Into :: into ) . collect ( ) )
95
98
}
96
99
97
100
pub fn course_instance_exercise_slides (
@@ -101,7 +104,7 @@ impl MoocClient {
101
104
let url = format ! ( "course-instances/{course_instance}/exercises" ) ;
102
105
let res = self
103
106
. request ( Method :: GET , & url)
104
- . send_expect_json :: < Vec < ExerciseSlide > > ( ) ?
107
+ . send_expect_json :: < Vec < api :: ExerciseSlide > > ( ) ?
105
108
. into_iter ( )
106
109
. map ( TryFrom :: try_from)
107
110
. collect :: < Result < _ , JsonError > > ( )
@@ -116,7 +119,7 @@ impl MoocClient {
116
119
let url = format ! ( "exercises/{exercise}" ) ;
117
120
let res = self
118
121
. request ( Method :: GET , & url)
119
- . send_expect_json :: < ExerciseSlide > ( ) ?
122
+ . send_expect_json :: < api :: ExerciseSlide > ( ) ?
120
123
. try_into ( )
121
124
. map_err ( |err : JsonError | MoocClientError :: DeserializingResponse {
122
125
url,
@@ -145,7 +148,7 @@ impl MoocClient {
145
148
archive : & Path ,
146
149
) -> MoocClientResult < ExerciseTaskSubmissionResult > {
147
150
// upload archive
148
- let metadata = UploadMetadata { slide_id, task_id } ;
151
+ let metadata = api :: UploadMetadata { slide_id, task_id } ;
149
152
let metadata = serialize:: to_json_vec ( & metadata) ?;
150
153
let form = Form :: new ( )
151
154
. part (
@@ -159,23 +162,23 @@ impl MoocClient {
159
162
let res = self
160
163
. request ( Method :: POST , & format ! ( "exercises/{exercise_id}/upload" ) )
161
164
. multipart ( form)
162
- . send_expect_json :: < UploadResult > ( ) ?;
165
+ . send_expect_json :: < api :: UploadResult > ( ) ?;
163
166
164
167
// send submission
165
168
let user_answer = UserAnswer :: Editor {
166
169
download_url : res. download_url ,
167
170
} ;
168
171
let data_json = serialize:: to_json_value ( & user_answer) ?;
169
- let exercise_slide_submission = ExerciseSlideSubmission {
172
+ let exercise_slide_submission = api :: ExerciseSlideSubmission {
170
173
exercise_slide_id : slide_id,
171
174
exercise_task_id : task_id,
172
175
data_json,
173
176
} ;
174
177
let res = self
175
178
. request ( Method :: POST , & format ! ( "exercises/{exercise_id}/submit" ) )
176
179
. json ( & exercise_slide_submission)
177
- . send_expect_json ( ) ?;
178
- Ok ( res)
180
+ . send_expect_json :: < api :: ExerciseTaskSubmissionResult > ( ) ?;
181
+ Ok ( res. into ( ) )
179
182
}
180
183
181
184
pub fn get_submission_grading (
@@ -184,8 +187,8 @@ impl MoocClient {
184
187
) -> MoocClientResult < ExerciseTaskSubmissionStatus > {
185
188
let res = self
186
189
. request ( Method :: GET , & format ! ( "submissions/{submission_id}/grading" ) )
187
- . send_expect_json ( ) ?;
188
- Ok ( res)
190
+ . send_expect_json :: < api :: ExerciseTaskSubmissionStatus > ( ) ?;
191
+ Ok ( res. into ( ) )
189
192
}
190
193
}
191
194
@@ -272,3 +275,107 @@ impl MoocRequest {
272
275
Ok ( json)
273
276
}
274
277
}
278
+
279
+ #[ derive( Debug , Serialize ) ]
280
+ #[ cfg_attr( feature = "ts-rs" , derive( TS ) ) ]
281
+ pub struct CourseInstance {
282
+ pub id : Uuid ,
283
+ pub course_id : Uuid ,
284
+ pub course_slug : String ,
285
+ pub course_name : String ,
286
+ pub course_description : Option < String > ,
287
+ pub instance_name : Option < String > ,
288
+ pub instance_description : Option < String > ,
289
+ }
290
+
291
+ impl From < api:: CourseInstance > for CourseInstance {
292
+ fn from ( value : api:: CourseInstance ) -> Self {
293
+ Self {
294
+ id : value. id ,
295
+ course_id : value. course_id ,
296
+ course_slug : value. course_slug ,
297
+ course_name : value. course_name ,
298
+ course_description : value. course_description ,
299
+ instance_name : value. instance_name ,
300
+ instance_description : value. instance_description ,
301
+ }
302
+ }
303
+ }
304
+
305
+ #[ derive( Debug , Serialize ) ]
306
+ #[ cfg_attr( feature = "ts-rs" , derive( TS ) ) ]
307
+ pub struct ExerciseTaskSubmissionResult {
308
+ pub submission_id : Uuid ,
309
+ }
310
+
311
+ impl From < api:: ExerciseTaskSubmissionResult > for ExerciseTaskSubmissionResult {
312
+ fn from ( value : api:: ExerciseTaskSubmissionResult ) -> Self {
313
+ Self {
314
+ submission_id : value. submission_id ,
315
+ }
316
+ }
317
+ }
318
+
319
+ #[ derive( Debug , Serialize ) ]
320
+ #[ cfg_attr( feature = "ts-rs" , derive( TS ) ) ]
321
+ pub enum ExerciseTaskSubmissionStatus {
322
+ NoGradingYet ,
323
+ Grading {
324
+ grading_progress : GradingProgress ,
325
+ score_given : Option < f32 > ,
326
+ grading_started_at : Option < DateTime < Utc > > ,
327
+ grading_completed_at : Option < DateTime < Utc > > ,
328
+ feedback_json : Option < serde_json:: Value > ,
329
+ feedback_text : Option < String > ,
330
+ } ,
331
+ }
332
+
333
+ impl From < api:: ExerciseTaskSubmissionStatus > for ExerciseTaskSubmissionStatus {
334
+ fn from ( value : api:: ExerciseTaskSubmissionStatus ) -> Self {
335
+ match value {
336
+ api:: ExerciseTaskSubmissionStatus :: NoGradingYet => Self :: NoGradingYet ,
337
+ api:: ExerciseTaskSubmissionStatus :: Grading {
338
+ grading_progress,
339
+ score_given,
340
+ grading_started_at,
341
+ grading_completed_at,
342
+ feedback_json,
343
+ feedback_text,
344
+ } => Self :: Grading {
345
+ grading_progress : grading_progress. into ( ) ,
346
+ score_given,
347
+ grading_started_at,
348
+ grading_completed_at,
349
+ feedback_json,
350
+ feedback_text,
351
+ } ,
352
+ }
353
+ }
354
+ }
355
+
356
+ #[ derive( Debug , Clone , Copy , Serialize ) ]
357
+ #[ cfg_attr( feature = "ts-rs" , derive( TS ) ) ]
358
+ pub enum GradingProgress {
359
+ /// The grading could not complete.
360
+ Failed ,
361
+ /// There is no grading process occurring; for example, the student has not yet made any submission.
362
+ NotReady ,
363
+ /// Final Grade is pending, and it does require human intervention; if a Score value is present, it indicates the current value is partial and may be updated during the manual grading.
364
+ PendingManual ,
365
+ /// Final Grade is pending, but does not require manual intervention; if a Score value is present, it indicates the current value is partial and may be updated.
366
+ Pending ,
367
+ /// The grading process is completed; the score value, if any, represents the current Final Grade;
368
+ FullyGraded ,
369
+ }
370
+
371
+ impl From < api:: GradingProgress > for GradingProgress {
372
+ fn from ( value : api:: GradingProgress ) -> Self {
373
+ match value {
374
+ api:: GradingProgress :: Failed => Self :: Failed ,
375
+ api:: GradingProgress :: NotReady => Self :: NotReady ,
376
+ api:: GradingProgress :: PendingManual => Self :: PendingManual ,
377
+ api:: GradingProgress :: Pending => Self :: Pending ,
378
+ api:: GradingProgress :: FullyGraded => Self :: FullyGraded ,
379
+ }
380
+ }
381
+ }
0 commit comments