headless_lms_models/
peer_or_self_review_submissions.rs

1use std::collections::HashMap;
2
3use crate::prelude::*;
4
5#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Eq)]
6#[cfg_attr(feature = "ts_rs", derive(TS))]
7pub struct PeerOrSelfReviewSubmission {
8    pub id: Uuid,
9    pub created_at: DateTime<Utc>,
10    pub updated_at: DateTime<Utc>,
11    pub deleted_at: Option<DateTime<Utc>>,
12    pub user_id: Uuid,
13    pub exercise_id: Uuid,
14    pub course_instance_id: Uuid,
15    pub peer_or_self_review_config_id: Uuid,
16    pub exercise_slide_submission_id: Uuid,
17}
18
19pub async fn insert(
20    conn: &mut PgConnection,
21    pkey_policy: PKeyPolicy<Uuid>,
22    user_id: Uuid,
23    exercise_id: Uuid,
24    course_instance_id: Uuid,
25    peer_or_self_review_config_id: Uuid,
26    exercise_slide_submission_id: Uuid,
27) -> ModelResult<Uuid> {
28    let res = sqlx::query!(
29        "
30INSERT INTO peer_or_self_review_submissions (
31    id,
32    user_id,
33    exercise_id,
34    course_instance_id,
35    peer_or_self_review_config_id,
36    exercise_slide_submission_id
37  )
38VALUES ($1, $2, $3, $4, $5, $6)
39RETURNING id
40        ",
41        pkey_policy.into_uuid(),
42        user_id,
43        exercise_id,
44        course_instance_id,
45        peer_or_self_review_config_id,
46        exercise_slide_submission_id,
47    )
48    .fetch_one(conn)
49    .await?;
50    Ok(res.id)
51}
52
53pub async fn get_by_id(
54    conn: &mut PgConnection,
55    id: Uuid,
56) -> ModelResult<PeerOrSelfReviewSubmission> {
57    let res = sqlx::query_as!(
58        PeerOrSelfReviewSubmission,
59        "
60SELECT *
61FROM peer_or_self_review_submissions
62WHERE id = $1
63  AND deleted_at IS NULL
64        ",
65        id
66    )
67    .fetch_one(conn)
68    .await?;
69    Ok(res)
70}
71
72pub async fn get_by_ids(
73    conn: &mut PgConnection,
74    ids: &[Uuid],
75) -> ModelResult<Vec<PeerOrSelfReviewSubmission>> {
76    let res = sqlx::query_as!(
77        PeerOrSelfReviewSubmission,
78        "
79SELECT *
80FROM peer_or_self_review_submissions
81WHERE id = ANY($1)
82  AND deleted_at IS NULL
83        ",
84        ids
85    )
86    .fetch_all(conn)
87    .await?;
88    Ok(res)
89}
90
91pub async fn get_users_submission_ids_for_exercise_and_course_instance(
92    conn: &mut PgConnection,
93    user_id: Uuid,
94    exercise_id: Uuid,
95    course_instance_id: Uuid,
96) -> ModelResult<Vec<Uuid>> {
97    let res = sqlx::query!(
98        "
99SELECT exercise_slide_submission_id
100FROM peer_or_self_review_submissions
101WHERE user_id = $1
102  AND exercise_id = $2
103  AND course_instance_id = $3
104  AND deleted_at IS NULL
105    ",
106        user_id,
107        exercise_id,
108        course_instance_id
109    )
110    .fetch_all(conn)
111    .await?
112    .into_iter()
113    .map(|record| record.exercise_slide_submission_id)
114    .collect();
115    Ok(res)
116}
117
118pub async fn get_all_received_peer_or_self_review_submissions_for_user_and_course_instance(
119    conn: &mut PgConnection,
120    user_id: Uuid,
121    course_instance_id: Uuid,
122) -> ModelResult<Vec<PeerOrSelfReviewSubmission>> {
123    let res = sqlx::query_as!(
124        PeerOrSelfReviewSubmission,
125        "
126SELECT prs.*
127FROM exercise_slide_submissions ess
128INNER JOIN peer_or_self_review_submissions prs ON (ess.id = prs.exercise_slide_submission_id)
129WHERE ess.user_id = $1
130  AND ess.course_instance_id = $2
131  AND ess.deleted_at IS NULL
132  AND prs.deleted_at IS NULL
133    ",
134        user_id,
135        course_instance_id
136    )
137    .fetch_all(conn)
138    .await?;
139    Ok(res)
140}
141
142pub async fn get_all_given_peer_or_self_review_submissions_for_user_and_course_instance(
143    conn: &mut PgConnection,
144    user_id: Uuid,
145    course_instance_id: Uuid,
146) -> ModelResult<Vec<PeerOrSelfReviewSubmission>> {
147    let res = sqlx::query_as!(
148        PeerOrSelfReviewSubmission,
149        "
150SELECT *
151FROM peer_or_self_review_submissions
152WHERE user_id = $1
153  AND course_instance_id = $2
154  AND deleted_at IS NULL
155    ",
156        user_id,
157        course_instance_id
158    )
159    .fetch_all(conn)
160    .await?;
161    Ok(res)
162}
163
164pub async fn get_num_peer_reviews_given_by_user_and_course_instance_and_exercise(
165    conn: &mut PgConnection,
166    user_id: Uuid,
167    course_instance_id: Uuid,
168    exercise_id: Uuid,
169) -> ModelResult<i64> {
170    let res = sqlx::query!(
171        "
172SELECT COUNT(*)
173FROM peer_or_self_review_submissions
174WHERE user_id = $1
175  AND exercise_id = $3
176  AND course_instance_id = $2
177  AND deleted_at IS NULL
178    ",
179        user_id,
180        course_instance_id,
181        exercise_id
182    )
183    .fetch_one(conn)
184    .await?;
185    Ok(res.count.unwrap_or(0))
186}
187
188pub async fn get_peer_reviews_given_by_user_and_course_instance_and_exercise(
189    conn: &mut PgConnection,
190    user_id: Uuid,
191    course_instance_id: Uuid,
192    exercise_id: Uuid,
193) -> ModelResult<Vec<PeerOrSelfReviewSubmission>> {
194    let res = sqlx::query_as!(
195        PeerOrSelfReviewSubmission,
196        "
197SELECT *
198FROM peer_or_self_review_submissions
199WHERE user_id = $1
200  AND exercise_id = $3
201  AND course_instance_id = $2
202  AND deleted_at IS NULL
203    ",
204        user_id,
205        course_instance_id,
206        exercise_id
207    )
208    .fetch_all(conn)
209    .await?;
210    Ok(res)
211}
212
213pub async fn get_users_submission_count_for_exercise_and_course_instance(
214    conn: &mut PgConnection,
215    user_id: Uuid,
216    exercise_id: Uuid,
217    course_instance_id: Uuid,
218) -> ModelResult<u32> {
219    let res = sqlx::query!(
220        "
221SELECT COUNT(*) AS count
222FROM peer_or_self_review_submissions
223WHERE user_id = $1
224  AND exercise_id = $2
225  AND course_instance_id = $3
226  AND deleted_at IS NULL
227        ",
228        user_id,
229        exercise_id,
230        course_instance_id
231    )
232    .fetch_one(conn)
233    .await?;
234    Ok(res.count.unwrap_or(0).try_into()?)
235}
236
237pub async fn get_last_time_user_submitted_peer_review(
238    conn: &mut PgConnection,
239    user_id: Uuid,
240    exercise_id: Uuid,
241    course_instance_id: Uuid,
242) -> ModelResult<Option<DateTime<Utc>>> {
243    let res = sqlx::query!(
244        "
245SELECT MAX(created_at) as latest_submission_time
246FROM peer_or_self_review_submissions
247WHERE user_id = $1
248  AND exercise_id = $2
249  AND course_instance_id = $3
250  AND deleted_at IS NULL
251        ",
252        user_id,
253        exercise_id,
254        course_instance_id
255    )
256    .fetch_optional(conn)
257    .await?;
258    Ok(res.and_then(|o| o.latest_submission_time))
259}
260
261pub async fn count_peer_or_self_review_submissions_for_exercise_slide_submission(
262    conn: &mut PgConnection,
263    exercise_slide_submission_id: Uuid,
264    exclude_user_ids: &[Uuid],
265) -> ModelResult<u32> {
266    let res = sqlx::query!(
267        "
268SELECT COUNT(*) AS count
269FROM peer_or_self_review_submissions
270WHERE exercise_slide_submission_id = $1
271  AND user_id != ALL($2)
272  AND deleted_at IS NULL
273        ",
274        exercise_slide_submission_id,
275        exclude_user_ids
276    )
277    .fetch_one(conn)
278    .await?;
279    Ok(res.count.unwrap_or(0).try_into()?)
280}
281
282pub async fn get_self_review_submission_by_user_and_exercise(
283    conn: &mut PgConnection,
284    user_id: Uuid,
285    exercise_id: Uuid,
286    course_instance_id: Uuid,
287) -> ModelResult<Option<PeerOrSelfReviewSubmission>> {
288    let res = sqlx::query_as!(
289        PeerOrSelfReviewSubmission,
290        "
291SELECT prs.*
292FROM peer_or_self_review_submissions prs
293JOIN exercise_slide_submissions ess ON (ess.id = prs.exercise_slide_submission_id)
294WHERE ess.user_id = $1
295  AND prs.exercise_id = $2
296  AND prs.course_instance_id = $3
297  AND prs.deleted_at IS NULL
298  AND ess.deleted_at IS NULL
299        ",
300        user_id,
301        exercise_id,
302        course_instance_id
303    )
304    .fetch_optional(conn)
305    .await?;
306    Ok(res)
307}
308
309pub async fn get_received_peer_or_self_review_submissions_for_user_by_peer_or_self_review_config_id_and_exercise_slide_submission(
310    conn: &mut PgConnection,
311    user_id: Uuid,
312    exercise_slide_submission_id: Uuid,
313    peer_or_self_review_config_id: Uuid,
314) -> ModelResult<Vec<PeerOrSelfReviewSubmission>> {
315    let res = sqlx::query_as!(
316        PeerOrSelfReviewSubmission,
317        "
318SELECT prs.*
319FROM peer_or_self_review_submissions prs
320  JOIN exercise_slide_submissions ess ON (ess.id = prs.exercise_slide_submission_id)
321WHERE ess.user_id = $1
322  AND ess.id = $2
323  AND prs.peer_or_self_review_config_id = $3
324  AND prs.deleted_at IS NULL
325  AND ess.deleted_at IS NULL
326        ",
327        user_id,
328        exercise_slide_submission_id,
329        peer_or_self_review_config_id
330    )
331    .fetch_all(conn)
332    .await?;
333    Ok(res)
334}
335
336pub async fn get_mapping_from_peer_or_self_review_submission_ids_to_peer_review_giver_user_ids(
337    conn: &mut PgConnection,
338    peer_or_self_review_submission_ids: &[Uuid],
339) -> ModelResult<HashMap<Uuid, Uuid>> {
340    let full = get_by_ids(conn, peer_or_self_review_submission_ids).await?;
341    Ok(full
342        .into_iter()
343        .map(|submission: PeerOrSelfReviewSubmission| (submission.id, submission.user_id))
344        .collect())
345}