1use crate::course_modules;
2use crate::prelude::*;
3
4#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
5#[cfg_attr(feature = "ts_rs", derive(TS))]
6pub struct SuspectedCheaters {
7 pub id: Uuid,
8 pub user_id: Uuid,
9 pub course_id: Uuid,
10 pub created_at: DateTime<Utc>,
11 pub deleted_at: Option<DateTime<Utc>>,
12 pub updated_at: Option<DateTime<Utc>>,
13 pub total_duration_seconds: Option<i32>,
14 pub total_points: i32,
15 pub is_archived: Option<bool>,
16}
17
18#[derive(Debug, Serialize, Deserialize)]
19#[cfg_attr(feature = "ts_rs", derive(TS))]
20pub struct ThresholdData {
21 pub duration_seconds: i32,
22}
23
24#[derive(Debug, Serialize, Deserialize)]
25#[cfg_attr(feature = "ts_rs", derive(TS))]
26pub struct DeletedSuspectedCheater {
27 pub id: i32,
28 pub count: i32,
29}
30
31#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
32#[cfg_attr(feature = "ts_rs", derive(TS))]
33pub struct Threshold {
34 pub id: Uuid,
35 pub course_module_id: Uuid,
36 pub created_at: DateTime<Utc>,
37 pub updated_at: DateTime<Utc>,
38 pub deleted_at: Option<DateTime<Utc>>,
39 pub duration_seconds: i32,
40}
41
42pub async fn insert(
43 conn: &mut PgConnection,
44 user_id: Uuid,
45 course_id: Uuid,
46 total_duration_seconds: Option<i32>,
47 total_points: i32,
48) -> ModelResult<()> {
49 sqlx::query!(
50 "
51 INSERT INTO suspected_cheaters (
52 user_id,
53 total_duration_seconds,
54 total_points,
55 course_id
56 )
57 VALUES ($1, $2, $3, $4)
58 ",
59 user_id,
60 total_duration_seconds,
61 total_points,
62 course_id
63 )
64 .execute(conn)
65 .await?;
66 Ok(())
67}
68
69pub async fn insert_thresholds(
70 conn: &mut PgConnection,
71 course_id: Uuid,
72 duration_seconds: i32,
73) -> ModelResult<Threshold> {
74 let default_module = course_modules::get_default_by_course_id(conn, course_id).await?;
75
76 let threshold = sqlx::query_as!(
77 Threshold,
78 "
79 INSERT INTO cheater_thresholds (
80 course_module_id,
81 duration_seconds
82 )
83 VALUES ($1, $2)
84 ON CONFLICT (course_module_id)
85 DO UPDATE SET
86 duration_seconds = EXCLUDED.duration_seconds,
87 deleted_at = NULL
88 RETURNING *
89 ",
90 default_module.id,
91 duration_seconds,
92 )
93 .fetch_one(conn)
94 .await?;
95
96 Ok(threshold)
97}
98
99pub async fn get_thresholds_by_id(
100 conn: &mut PgConnection,
101 course_id: Uuid,
102) -> ModelResult<Threshold> {
103 let default_module = course_modules::get_default_by_course_id(conn, course_id).await?;
104
105 let thresholds = sqlx::query_as!(
106 Threshold,
107 "
108 SELECT id,
109 course_module_id,
110 duration_seconds,
111 created_at,
112 updated_at,
113 deleted_at
114 FROM cheater_thresholds
115 WHERE course_module_id = $1
116 AND deleted_at IS NULL;
117 ",
118 default_module.id
119 )
120 .fetch_one(conn)
121 .await?;
122 Ok(thresholds)
123}
124
125pub async fn archive_suspected_cheater(conn: &mut PgConnection, id: Uuid) -> ModelResult<()> {
126 sqlx::query!(
127 "
128 UPDATE suspected_cheaters
129 SET is_archived = TRUE
130 WHERE user_id = $1
131 ",
132 id
133 )
134 .execute(conn)
135 .await?;
136 Ok(())
137}
138
139pub async fn approve_suspected_cheater(conn: &mut PgConnection, id: Uuid) -> ModelResult<()> {
140 sqlx::query!(
141 "
142 UPDATE suspected_cheaters
143 SET is_archived = FALSE
144 WHERE user_id = $1
145 ",
146 id
147 )
148 .execute(conn)
149 .await?;
150 Ok(())
151}
152
153pub async fn get_suspected_cheaters_by_id(
154 conn: &mut PgConnection,
155 id: Uuid,
156) -> ModelResult<SuspectedCheaters> {
157 let cheaters = sqlx::query_as!(
158 SuspectedCheaters,
159 "
160 SELECT *
161 FROM suspected_cheaters
162 WHERE user_id = $1
163 AND deleted_at IS NULL;
164 ",
165 id
166 )
167 .fetch_one(conn)
168 .await?;
169 Ok(cheaters)
170}
171
172pub async fn get_all_suspected_cheaters_in_course(
173 conn: &mut PgConnection,
174 course_id: Uuid,
175 archive: bool,
176) -> ModelResult<Vec<SuspectedCheaters>> {
177 let cheaters = sqlx::query_as!(
178 SuspectedCheaters,
179 "
180SELECT *
181FROM suspected_cheaters
182WHERE course_id = $1
183 AND is_archived = $2
184 AND deleted_at IS NULL;
185 ",
186 course_id,
187 archive
188 )
189 .fetch_all(conn)
190 .await?;
191 Ok(cheaters)
192}
193
194pub async fn insert_thresholds_by_module_id(
195 conn: &mut PgConnection,
196 course_module_id: Uuid,
197 duration_seconds: i32,
198) -> ModelResult<Threshold> {
199 let threshold = sqlx::query_as!(
200 Threshold,
201 "
202 INSERT INTO cheater_thresholds (
203 course_module_id,
204 duration_seconds
205 )
206 VALUES ($1, $2)
207 ON CONFLICT (course_module_id)
208 DO UPDATE SET
209 duration_seconds = EXCLUDED.duration_seconds,
210 deleted_at = NULL
211 RETURNING *
212 ",
213 course_module_id,
214 duration_seconds,
215 )
216 .fetch_one(conn)
217 .await?;
218
219 Ok(threshold)
220}
221
222pub async fn get_thresholds_by_module_id(
223 conn: &mut PgConnection,
224 course_module_id: Uuid,
225) -> ModelResult<Option<Threshold>> {
226 let threshold = sqlx::query_as!(
227 Threshold,
228 "
229 SELECT id,
230 course_module_id,
231 duration_seconds,
232 created_at,
233 updated_at,
234 deleted_at
235 FROM cheater_thresholds
236 WHERE course_module_id = $1
237 AND deleted_at IS NULL;
238 ",
239 course_module_id
240 )
241 .fetch_optional(conn)
242 .await?;
243 Ok(threshold)
244}
245
246pub async fn get_all_thresholds_for_course(
247 conn: &mut PgConnection,
248 course_id: Uuid,
249) -> ModelResult<Vec<Threshold>> {
250 let thresholds = sqlx::query_as!(
251 Threshold,
252 "
253 SELECT ct.id,
254 ct.course_module_id,
255 ct.duration_seconds,
256 ct.created_at,
257 ct.updated_at,
258 ct.deleted_at
259 FROM cheater_thresholds ct
260 JOIN course_modules cm ON ct.course_module_id = cm.id
261 WHERE cm.course_id = $1
262 AND ct.deleted_at IS NULL
263 AND cm.deleted_at IS NULL;
264 ",
265 course_id
266 )
267 .fetch_all(conn)
268 .await?;
269 Ok(thresholds)
270}
271
272pub async fn delete_threshold_for_module(
273 conn: &mut PgConnection,
274 course_module_id: Uuid,
275) -> ModelResult<()> {
276 sqlx::query!(
277 "
278 UPDATE cheater_thresholds
279 SET deleted_at = NOW()
280 WHERE course_module_id = $1
281 AND deleted_at IS NULL
282 ",
283 course_module_id
284 )
285 .execute(conn)
286 .await?;
287 Ok(())
288}