Skip to main content

headless_lms_models/
material_references.rs

1use crate::prelude::*;
2use chrono::{DateTime, Utc};
3use sqlx::PgConnection;
4use utoipa::ToSchema;
5use uuid::Uuid;
6
7#[derive(Debug, Serialize, Deserialize, FromRow, PartialEq, Clone, ToSchema)]
8
9pub struct MaterialReference {
10    pub id: Uuid,
11    pub course_id: Uuid,
12    pub citation_key: String,
13    pub reference: String,
14    pub created_at: DateTime<Utc>,
15    pub updated_at: DateTime<Utc>,
16    pub deleted_at: Option<DateTime<Utc>>,
17}
18
19#[derive(Debug, Serialize, Deserialize, FromRow, PartialEq, Clone, ToSchema)]
20
21pub struct NewMaterialReference {
22    pub citation_key: String,
23    pub reference: String,
24}
25
26pub async fn insert_reference(
27    conn: &mut PgConnection,
28    course_id: Uuid,
29    new_ref: Vec<NewMaterialReference>,
30) -> ModelResult<()> {
31    let mut tx = conn.begin().await?;
32    let new_ref_iter = new_ref.iter();
33
34    for new_ref in new_ref_iter {
35        sqlx::query!(
36            "
37    INSERT INTO material_references(course_id, citation_key, reference)
38    VALUES ($1, $2, $3)
39    ",
40            course_id,
41            new_ref.citation_key,
42            new_ref.reference
43        )
44        .execute(&mut *tx)
45        .await?;
46    }
47    tx.commit().await?;
48
49    Ok(())
50}
51
52pub async fn get_reference_by_id(
53    conn: &mut PgConnection,
54    reference_id: Uuid,
55) -> ModelResult<MaterialReference> {
56    let res = sqlx::query_as!(
57        MaterialReference,
58        "
59SELECT *
60FROM material_references
61WHERE id = $1;
62    ",
63        reference_id
64    )
65    .fetch_one(conn)
66    .await?;
67    Ok(res)
68}
69
70pub async fn get_non_deleted_by_id(
71    conn: &mut PgConnection,
72    reference_id: Uuid,
73) -> ModelResult<MaterialReference> {
74    let res = sqlx::query_as!(
75        MaterialReference,
76        "
77SELECT *
78FROM material_references
79WHERE id = $1
80  AND deleted_at IS NULL;
81    ",
82        reference_id
83    )
84    .fetch_one(conn)
85    .await?;
86    Ok(res)
87}
88
89pub async fn get_references_by_course_id(
90    conn: &mut PgConnection,
91    course_id: Uuid,
92) -> ModelResult<Vec<MaterialReference>> {
93    let res = sqlx::query_as!(
94        MaterialReference,
95        "
96SELECT *
97FROM material_references
98WHERE course_id = $1
99  AND deleted_at IS NULL;
100    ",
101        course_id
102    )
103    .fetch_all(conn)
104    .await?;
105    Ok(res)
106}
107
108/// The references with the ids that aren't deleted, in no particular order.
109pub async fn get_by_ids(
110    conn: &mut PgConnection,
111    ids: &[Uuid],
112) -> ModelResult<Vec<MaterialReference>> {
113    let res = sqlx::query_as!(
114        MaterialReference,
115        "
116SELECT *
117FROM material_references
118WHERE id = ANY($1)
119  AND deleted_at IS NULL
120    ",
121        ids
122    )
123    .fetch_all(conn)
124    .await?;
125    Ok(res)
126}
127
128pub async fn update_material_reference_by_id(
129    conn: &mut PgConnection,
130    material_reference_id: Uuid,
131    material_reference: NewMaterialReference,
132) -> ModelResult<()> {
133    sqlx::query!(
134        "
135UPDATE material_references
136SET reference = $1, citation_key = $2
137WHERE id = $3;
138",
139        material_reference.reference,
140        material_reference.citation_key,
141        material_reference_id
142    )
143    .execute(conn)
144    .await?;
145    Ok(())
146}
147
148pub async fn update_by_id_and_course_id(
149    conn: &mut PgConnection,
150    material_reference_id: Uuid,
151    course_id: Uuid,
152    material_reference: NewMaterialReference,
153) -> ModelResult<MaterialReference> {
154    let res = sqlx::query_as!(
155        MaterialReference,
156        "
157UPDATE material_references
158SET reference = $1,
159  citation_key = $2
160WHERE id = $3
161  AND course_id = $4
162  AND deleted_at IS NULL
163RETURNING *
164        ",
165        material_reference.reference,
166        material_reference.citation_key,
167        material_reference_id,
168        course_id
169    )
170    .fetch_one(conn)
171    .await?;
172    Ok(res)
173}
174
175pub async fn delete_reference(conn: &mut PgConnection, id: Uuid) -> ModelResult<()> {
176    sqlx::query!(
177        "
178UPDATE material_references
179SET deleted_at = now()
180WHERE material_references.id = $1
181AND deleted_at IS NULL;
182        ",
183        id
184    )
185    .execute(conn)
186    .await?;
187    Ok(())
188}
189
190pub async fn delete_by_id_and_course_id(
191    conn: &mut PgConnection,
192    id: Uuid,
193    course_id: Uuid,
194) -> ModelResult<MaterialReference> {
195    let res = sqlx::query_as!(
196        MaterialReference,
197        "
198UPDATE material_references
199SET deleted_at = now()
200WHERE id = $1
201  AND course_id = $2
202  AND deleted_at IS NULL
203RETURNING *
204        ",
205        id,
206        course_id
207    )
208    .fetch_one(conn)
209    .await?;
210    Ok(res)
211}
212
213/// Writes the snapshot to the reference, creating it with the snapshot's id if it does not exist
214/// and bringing it back if it is deleted.
215pub async fn upsert_from_snapshot(
216    conn: &mut PgConnection,
217    snapshot: &crate::library::entity_snapshots::ReferenceSnapshot,
218) -> ModelResult<()> {
219    sqlx::query!(
220        "
221INSERT INTO material_references (id, course_id, citation_key, reference)
222VALUES ($1, $2, $3, $4) ON CONFLICT (id) DO
223UPDATE
224SET citation_key = EXCLUDED.citation_key,
225  reference = EXCLUDED.reference,
226  deleted_at = NULL
227WHERE material_references.course_id = EXCLUDED.course_id
228",
229        snapshot.id,
230        snapshot.course_id,
231        snapshot.citation_key,
232        snapshot.reference,
233    )
234    .execute(conn)
235    .await?;
236    Ok(())
237}