headless_lms_models/course_module_suotar_configurations/
listing.rs1use crate::credit_registrations::CreditRegistrationErrorCode;
5use crate::prelude::*;
6
7#[derive(Debug, Clone, PartialEq, Default)]
10pub struct ModuleListingOutcome {
11 pub listed_person_count: i32,
12 pub already_linked_count: i32,
13 pub mailed_count: i32,
14 pub suppressed_by_dedup_count: i32,
15 pub suppressed_by_rate_cap_count: i32,
16 pub no_address_count: i32,
17}
18
19#[derive(Debug, Clone, PartialEq)]
21pub struct ModuleToList {
22 pub course_module_id: Uuid,
23 pub course_id: Uuid,
24 pub uh_course_code: String,
25 pub course_language_code: String,
27}
28
29pub async fn get_active_modules_for_course(
31 conn: &mut PgConnection,
32 course_id: Uuid,
33) -> ModelResult<Vec<ModuleToList>> {
34 let res = sqlx::query_as!(
35 ModuleToList,
36 r#"
37SELECT acm.course_module_id AS "course_module_id!",
38 acm.course_id AS "course_id!",
39 TRIM(cm.uh_course_code) AS "uh_course_code!",
40 co.language_code AS "course_language_code!"
41FROM credit_registration_active_course_modules acm
42 JOIN course_modules cm ON cm.id = acm.course_module_id AND cm.deleted_at IS NULL
43 JOIN courses co ON co.id = acm.course_id AND co.deleted_at IS NULL
44WHERE TRIM(COALESCE(cm.uh_course_code, '')) <> ''
45 AND acm.course_id = $1
46ORDER BY cm.order_number,
47 cm.id
48 "#,
49 course_id,
50 )
51 .fetch_all(conn)
52 .await?;
53 Ok(res)
54}
55
56#[derive(Debug, Clone, PartialEq)]
60pub struct ModuleDiscoveryReport {
61 pub course_id: Uuid,
62 pub course_name: String,
63 pub course_module_id: Uuid,
64 pub course_module_name: Option<String>,
65 pub uh_course_code: Option<String>,
66 pub last_listed_at: Option<DateTime<Utc>>,
67 pub last_listed_person_count: Option<i32>,
68 pub last_already_linked_count: Option<i32>,
69 pub last_mailed_count: Option<i32>,
70 pub last_suppressed_by_dedup_count: Option<i32>,
71 pub last_suppressed_by_rate_cap_count: Option<i32>,
72 pub last_no_address_count: Option<i32>,
73 pub last_listing_attempted_at: Option<DateTime<Utc>>,
74 pub last_listing_error: Option<CreditRegistrationErrorCode>,
75 pub consecutive_listing_failures: i32,
76}
77
78pub async fn get_active_discovery_reports(
80 conn: &mut PgConnection,
81) -> ModelResult<Vec<ModuleDiscoveryReport>> {
82 let res = sqlx::query_as!(
83 ModuleDiscoveryReport,
84 r#"
85SELECT cm.course_id,
86 c.name AS course_name,
87 conf.course_module_id,
88 cm.name AS course_module_name,
89 cm.uh_course_code,
90 conf.last_listed_at,
91 conf.last_listed_person_count,
92 conf.last_already_linked_count,
93 conf.last_mailed_count,
94 conf.last_suppressed_by_dedup_count,
95 conf.last_suppressed_by_rate_cap_count,
96 conf.last_no_address_count,
97 conf.last_listing_attempted_at,
98 conf.last_listing_error AS "last_listing_error?",
99 conf.consecutive_listing_failures
100FROM course_module_suotar_configurations conf
101 JOIN credit_registration_active_course_modules acm ON acm.course_module_id = conf.course_module_id
102 JOIN course_modules cm ON cm.id = conf.course_module_id AND cm.deleted_at IS NULL
103 JOIN courses c ON c.id = cm.course_id AND c.deleted_at IS NULL
104WHERE conf.deleted_at IS NULL
105 AND c.deleted_at IS NULL
106ORDER BY c.name,
107 cm.order_number
108 "#,
109 )
110 .fetch_all(conn)
111 .await?;
112 Ok(res)
113}
114
115pub async fn mark_listing_failed(
119 conn: &mut PgConnection,
120 course_module_id: Uuid,
121 error: CreditRegistrationErrorCode,
122) -> ModelResult<()> {
123 sqlx::query!(
124 r#"
125UPDATE course_module_suotar_configurations
126SET last_listing_attempted_at = now(),
127 last_listing_error = $2,
128 consecutive_listing_failures = consecutive_listing_failures + 1
129WHERE course_module_id = $1
130 AND deleted_at IS NULL
131 "#,
132 course_module_id,
133 error as CreditRegistrationErrorCode,
134 )
135 .execute(conn)
136 .await?;
137 Ok(())
138}
139
140pub async fn mark_listing_succeeded_without_linking(
144 conn: &mut PgConnection,
145 course_module_id: Uuid,
146) -> ModelResult<()> {
147 sqlx::query!(
148 r#"
149UPDATE course_module_suotar_configurations
150SET last_listing_attempted_at = now(),
151 last_listing_error = NULL,
152 consecutive_listing_failures = 0
153WHERE course_module_id = $1
154 AND deleted_at IS NULL
155 "#,
156 course_module_id,
157 )
158 .execute(conn)
159 .await?;
160 Ok(())
161}
162
163pub async fn record_listing_outcome(
166 conn: &mut PgConnection,
167 course_module_id: Uuid,
168 outcome: &ModuleListingOutcome,
169) -> ModelResult<()> {
170 sqlx::query!(
171 r#"
172UPDATE course_module_suotar_configurations
173SET last_listed_at = now(),
174 last_listing_attempted_at = now(),
175 last_listing_error = NULL,
176 consecutive_listing_failures = 0,
177 last_listed_person_count = $2,
178 last_already_linked_count = $3,
179 last_mailed_count = $4,
180 last_suppressed_by_dedup_count = $5,
181 last_suppressed_by_rate_cap_count = $6,
182 last_no_address_count = $7
183WHERE course_module_id = $1
184 AND deleted_at IS NULL
185 "#,
186 course_module_id,
187 outcome.listed_person_count,
188 outcome.already_linked_count,
189 outcome.mailed_count,
190 outcome.suppressed_by_dedup_count,
191 outcome.suppressed_by_rate_cap_count,
192 outcome.no_address_count,
193 )
194 .execute(conn)
195 .await?;
196 Ok(())
197}