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}