headless_lms_server/domain/credit_registration/
enrolment_recheck.rs1use headless_lms_models::credit_registration_enrolment_check_signals;
4use headless_lms_models::credit_registration_events::{
5 CreditRegistrationEventKind, NewCreditRegistrationEvent,
6};
7use headless_lms_models::credit_registrations::CreditRegistrationState;
8use headless_lms_models::library::credit_registration::enrolment_check_schedule::EnrolmentCheckSource;
9use headless_lms_models::library::credit_registration::enrolment_checks::{
10 self, CheckRequestOutcome,
11};
12
13use crate::prelude::*;
14
15pub fn can_request_enrolment_recheck(
18 state: CreditRegistrationState,
19 enrolment_check_requested_at: Option<DateTime<Utc>>,
20 enrolment_checked_at: Option<DateTime<Utc>>,
21) -> bool {
22 state == CreditRegistrationState::NoUsableEnrolment
23 && !enrolment_checks::is_check_request_limited(
24 enrolment_check_requested_at,
25 enrolment_checked_at,
26 Utc::now(),
27 )
28}
29
30pub fn can_student_request_enrolment_recheck(
33 state: CreditRegistrationState,
34 row_created_at: DateTime<Utc>,
35 enrolment_check_requested_at: Option<DateTime<Utc>>,
36 enrolment_checked_at: Option<DateTime<Utc>>,
37) -> bool {
38 !enrolment_checks::is_too_new_for_student_check_request(row_created_at, Utc::now())
39 && can_request_enrolment_recheck(state, enrolment_check_requested_at, enrolment_checked_at)
40}
41
42#[derive(Debug, Clone, Copy)]
44pub struct RecheckTarget {
45 pub registration_id: Uuid,
46 pub course_module_completion_id: Uuid,
47}
48
49pub async fn start_enrolment_recheck(
57 conn: &mut PgConnection,
58 actor_user_id: Uuid,
59 target: RecheckTarget,
60 source: EnrolmentCheckSource,
61 event_kind: CreditRegistrationEventKind,
62 message: &str,
63) -> Result<CheckRequestOutcome, ControllerError> {
64 start_recheck(
65 conn,
66 actor_user_id,
67 target,
68 source,
69 event_kind,
70 message,
71 false,
72 )
73 .await
74}
75
76pub async fn start_student_enrolment_recheck(
79 conn: &mut PgConnection,
80 student_user_id: Uuid,
81 target: RecheckTarget,
82 registration_created_at: DateTime<Utc>,
83 message: &str,
84) -> Result<CheckRequestOutcome, ControllerError> {
85 let is_too_new =
86 enrolment_checks::is_too_new_for_student_check_request(registration_created_at, Utc::now());
87 start_recheck(
88 conn,
89 student_user_id,
90 target,
91 EnrolmentCheckSource::StudentRequest,
92 CreditRegistrationEventKind::StudentAction,
93 message,
94 is_too_new,
95 )
96 .await
97}
98
99async fn start_recheck(
100 conn: &mut PgConnection,
101 actor_user_id: Uuid,
102 target: RecheckTarget,
103 source: EnrolmentCheckSource,
104 event_kind: CreditRegistrationEventKind,
105 message: &str,
106 is_refused_as_too_soon: bool,
107) -> Result<CheckRequestOutcome, ControllerError> {
108 let registration_id = target.registration_id;
109 let mut tx = conn.begin().await?;
110 let outcome = if is_refused_as_too_soon {
111 CheckRequestOutcome::TooSoon
112 } else {
113 enrolment_checks::request_check(&mut tx, registration_id, source, Utc::now()).await?
114 };
115 if outcome == CheckRequestOutcome::NotWaiting {
116 tx.commit().await?;
117 return Ok(outcome);
118 }
119 credit_registration_enrolment_check_signals::record_check_request(
120 &mut tx,
121 target.course_module_completion_id,
122 source,
123 )
124 .await?;
125 if outcome == CheckRequestOutcome::TooSoon {
126 tx.commit().await?;
127 return Ok(outcome);
128 }
129 models::credit_registration_events::insert(
130 &mut tx,
131 &NewCreditRegistrationEvent {
132 actor_user_id: Some(actor_user_id),
133 message: Some(message.to_string()),
134 ..NewCreditRegistrationEvent::new(registration_id, event_kind)
135 },
136 )
137 .await?;
138 tx.commit().await?;
139 Ok(outcome)
140}