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