headless_lms_models/
feedback_categories.rs1use crate::prelude::*;
2use utoipa::ToSchema;
3
4#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
5pub struct FeedbackCategory {
6 pub id: Uuid,
7 pub created_at: DateTime<Utc>,
8 pub updated_at: DateTime<Utc>,
9 pub deleted_at: Option<DateTime<Utc>>,
10 pub name: String,
11}
12
13#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
14pub struct NewFeedbackCategory {
15 pub name: String,
16}
17
18fn normalize(input: &str) -> String {
23 input
24 .to_lowercase()
25 .replace(";", "")
26 .replace("*", "")
27 .trim_end_matches(|x: char| !x.is_alphanumeric())
28 .trim_start_matches(|x: char| !x.is_alphanumeric())
29 .to_string()
30}
31
32pub async fn insert_or_get_by_name(
33 conn: &mut PgConnection,
34 category: NewFeedbackCategory,
35) -> ModelResult<Uuid> {
36 let normalized_name = normalize(&category.name);
37 if normalized_name.is_empty() {
38 return Err(model_err!(
39 InvalidRequest,
40 format!(
41 "Cannot insert invalid or empty feedback_category name. Name: {} Name after normalization: {}",
42 &category.name, normalized_name
43 )
44 ));
45 }
46
47 if let Some(c) = get_by_name(conn, &normalized_name).await? {
49 return Ok(c.id);
50 };
51
52 let res: Option<FeedbackCategory> = sqlx::query_as!(
53 FeedbackCategory,
54 r#"
55INSERT INTO feedback_categories (name)
56VALUES ($1) ON CONFLICT DO NOTHING
57RETURNING *
58 "#,
59 &normalized_name,
60 )
61 .fetch_optional(&mut *conn)
62 .await?;
63
64 if let Some(c) = res {
65 Ok(c.id)
66 } else {
67 get_by_name(conn, &normalized_name)
68 .await?
69 .map(|x| x.id)
70 .ok_or(model_err!(RecordNotFound, "Category n"))
71 }
72}
73
74pub async fn get_by_name(
75 conn: &mut PgConnection,
76 name: &String,
78) -> ModelResult<Option<FeedbackCategory>> {
79 let res = sqlx::query_as!(
80 FeedbackCategory,
81 "
82SELECT *
83FROM feedback_categories
84WHERE name = $1
85 AND deleted_at IS NULL
86 ",
87 name
88 )
89 .fetch_optional(conn)
90 .await?;
91
92 Ok(res)
93}
94
95pub async fn get_all(
96 conn: &mut PgConnection,
97 course_id: Uuid,
98) -> ModelResult<Vec<FeedbackCategory>> {
99 let res = sqlx::query_as!(
100 FeedbackCategory,
101 "
102SELECT fc.*
103FROM feedback_categories AS fc
104WHERE EXISTS (
105 SELECT id
106 FROM feedback AS f
107 WHERE fc.id = f.category_id
108 AND f.deleted_at IS NULL
109 AND f.course_id = $1
110 )
111 AND fc.deleted_at IS NULL
112 ",
113 course_id
114 )
115 .fetch_all(conn)
116 .await?;
117
118 Ok(res)
119}
120
121pub async fn get_all_by_read_status(
122 conn: &mut PgConnection,
123 course_id: Uuid,
124 read: bool,
125) -> ModelResult<Vec<FeedbackCategory>> {
126 let res = sqlx::query_as!(
127 FeedbackCategory,
128 "
129SELECT fc.*
130FROM feedback_categories AS fc
131WHERE EXISTS (
132 SELECT id
133 FROM feedback AS f
134 WHERE fc.id = f.category_id
135 AND f.deleted_at IS NULL
136 AND f.course_id = $1
137 AND f.marked_as_read = $2
138 )
139 AND fc.deleted_at IS NULL
140 ",
141 course_id,
142 read
143 )
144 .fetch_all(conn)
145 .await?;
146
147 Ok(res)
148}