headless_lms_models/
page_language_groups.rs1use std::collections::HashMap;
2
3use crate::prelude::*;
4
5#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
6pub struct PageLanguageGroup {
7 pub id: Uuid,
8 pub created_at: DateTime<Utc>,
9 pub updated_at: DateTime<Utc>,
10 pub deleted_at: Option<DateTime<Utc>>,
11 pub course_language_group_id: Uuid,
12}
13
14pub async fn insert(
15 conn: &mut PgConnection,
16 pkey_policy: PKeyPolicy<Uuid>,
17 course_language_group_id: Uuid,
18) -> ModelResult<Uuid> {
19 let res = sqlx::query!(
20 "
21INSERT INTO page_language_groups (
22 id,
23 course_language_group_id
24 )
25VALUES ($1, $2)
26RETURNING *
27 ",
28 pkey_policy.into_uuid(),
29 course_language_group_id
30 )
31 .fetch_one(conn)
32 .await?;
33 Ok(res.id)
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum PairingRefusal {
39 NotPublished,
41 NotInCourseLanguageGroup,
43}
44
45pub async fn resolve_pairing(
50 conn: &mut PgConnection,
51 source_page_id: Uuid,
52 course_language_group_id: Uuid,
53) -> ModelResult<Result<Uuid, PairingRefusal>> {
54 let source = sqlx::query!(
55 r#"
56SELECT p.page_language_group_id,
57 c.course_language_group_id AS "course_language_group_id?"
58FROM pages p
59 LEFT JOIN courses c ON c.id = p.course_id
60 AND c.deleted_at IS NULL
61WHERE p.id = $1
62 AND p.deleted_at IS NULL
63 "#,
64 source_page_id
65 )
66 .fetch_optional(conn)
67 .await?;
68 let Some(source) = source else {
69 return Ok(Err(PairingRefusal::NotPublished));
70 };
71 Ok(source
72 .page_language_group_id
73 .filter(|_| source.course_language_group_id == Some(course_language_group_id))
74 .ok_or(PairingRefusal::NotInCourseLanguageGroup))
75}
76
77pub async fn course_has_other_page(
82 conn: &mut PgConnection,
83 id: Uuid,
84 course_id: Uuid,
85 page_id: Uuid,
86) -> ModelResult<bool> {
87 sqlx::query!(
90 "SELECT id FROM page_language_groups WHERE id = $1 FOR NO KEY UPDATE",
91 id
92 )
93 .fetch_optional(&mut *conn)
94 .await?;
95 let has_other = sqlx::query_scalar!(
96 r#"
97SELECT EXISTS (
98 SELECT 1
99 FROM pages
100 WHERE page_language_group_id = $1
101 AND course_id = $2
102 AND id <> $3
103 AND deleted_at IS NULL
104 ) AS "has_other!"
105 "#,
106 id,
107 course_id,
108 page_id
109 )
110 .fetch_one(conn)
111 .await?;
112 Ok(has_other)
113}
114
115pub struct PageLanguageGroupNavigationInfo {
116 pub page_language_group_id: Uuid,
117 pub page_id: Uuid,
118 pub course_id: Option<Uuid>,
119 pub exam_id: Option<Uuid>,
120 pub page_path: String,
121}
122
123pub async fn get_all_pages_in_page_language_group_mapping(
125 conn: &mut PgConnection,
126 page_id: Uuid,
127) -> ModelResult<HashMap<CourseOrExamId, PageLanguageGroupNavigationInfo>> {
128 let res = sqlx::query_as!(
129 PageLanguageGroupNavigationInfo,
130 "
131SELECT plg.id AS page_language_group_id,
132 p.id AS page_id,
133 p.url_path AS page_path,
134 p.course_id AS course_id,
135 p.exam_id AS exam_id
136FROM page_language_groups plg
137 JOIN pages p ON plg.id = p.page_language_group_id
138WHERE plg.id = (SELECT page_language_group_id FROM pages WHERE id = $1)
139 AND plg.deleted_at IS NULL
140 AND p.deleted_at IS NULL
141 ",
142 page_id,
143 )
144 .fetch_all(conn)
145 .await?;
146 let mut result = HashMap::new();
147 for x in res {
148 let key = CourseOrExamId::from_course_and_exam_ids(x.course_id, x.exam_id)?;
149 result.insert(key, x);
150 }
151 Ok(result)
152}
153
154pub async fn get_by_course_id(
156 conn: &mut PgConnection,
157 course_id: Uuid,
158) -> ModelResult<HashMap<Uuid, Uuid>> {
159 let pages = crate::pages::get_all_by_course_id_and_visibility(
160 conn,
161 course_id,
162 crate::pages::PageVisibility::Any,
163 )
164 .await?;
165 Ok(pages
166 .into_iter()
167 .filter_map(|page| Some((page.id, page.page_language_group_id?)))
168 .collect())
169}