ucas_iclass/
checkin.rs

1//! Check in to given class.
2
3use super::{IClass, IClassError, Response};
4use serde::Deserialize;
5use std::fmt;
6
7/// Check-in response structure.
8#[derive(Clone, Debug, Deserialize)]
9pub struct CheckInResult {
10    // Object {"stuSignId": String("41254913"), "stuSignStatus": String("1")}
11    /// Check-in ID. Maybe the serial number of the check-in record.
12    #[serde(rename = "stuSignId")]
13    pub id: String,
14    /// Check-in status.
15    #[serde(
16        rename = "stuSignStatus",
17        deserialize_with = "super::util::deserialize_str_to_bool"
18    )]
19    pub status: bool,
20}
21
22impl IClass {
23    /// Checks in the schedule with given uuid. This is equivalent to scanning the QR code on the smart device outside the classroom.
24    ///
25    /// # Errors
26    ///
27    /// See [`IClassError`].
28    ///
29    /// # Panics
30    ///
31    /// This function will panic if system time is before [`UNIX_EPOCH`].
32    pub async fn check_in_by_uuid(
33        &self,
34        schedule_uuid: &str,
35        timestamp: u128,
36    ) -> Result<CheckInResult, IClassError> {
37        // /app/course/stu_scan_sign.action?timeTableId={schedule_uuid}&timestamp={timestamp}
38        let user_session = self.get_user_session()?;
39        let url = self.api_root.join("app/course/stu_scan_sign.action")?;
40        let response: Response<CheckInResult> = self
41            .client
42            .get(url)? // TODO: Maybe post + form?
43            .header("sessionId", &user_session.session_id)?
44            .query(&[
45                ("timeTableId", schedule_uuid),
46                ("timestamp", &timestamp.to_string()),
47                ("id", user_session.id.as_str()),
48            ])?
49            .send()
50            .await?
51            .json()
52            .await?;
53        let check_in_result = response.into_result()?;
54
55        Ok(check_in_result)
56    }
57
58    /// Checks in the schedule with given id. This is equivalent to scanning the QR code on the computer inside the classroom.
59    ///
60    /// # Errors
61    ///
62    /// See [`IClassError`].
63    ///
64    /// # Panics
65    ///
66    /// This function will panic if system time is before [`UNIX_EPOCH`].
67    pub async fn check_in_by_id(
68        &self,
69        schedule_id: &str,
70        timestamp: u128,
71    ) -> Result<CheckInResult, IClassError> {
72        // /app/course/stu_sign_in.action?scheduleId={schedule_id}&timestamp={timestamp}
73        let user_session = self.get_user_session()?;
74        let url = self.api_root.join("app/course/stu_scan_sign.action")?;
75        let response: Response<CheckInResult> = self
76            .client
77            .get(url)? // TODO: Maybe post + form?
78            .header("sessionId", &user_session.session_id)?
79            .query(&[
80                ("courseSchedId", schedule_id),
81                ("timestamp", &timestamp.to_string()),
82                ("id", user_session.id.as_str()),
83            ])?
84            .send()
85            .await?
86            .json()
87            .await?;
88        let check_in_result = response.into_result()?;
89
90        Ok(check_in_result)
91    }
92}
93
94impl fmt::Display for CheckInResult {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        let Self { id, status } = self;
97        let status = if *status {
98            "🟢 Success"
99        } else {
100            "🔴 Failed"
101        };
102        write!(f, "{status} (#{id})")
103    }
104}