1use chrono::{DateTime, FixedOffset, NaiveDate};
4use std::fmt;
5
6use super::{IClass, IClassError, Response};
7use serde::Deserialize;
8
9#[derive(Clone, Debug, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct Semester {
13 pub code: String,
15 pub name: String,
17 #[serde(deserialize_with = "super::util::deserialize_str_to_date_hyphen")]
19 pub begin_date: NaiveDate,
20 #[serde(deserialize_with = "super::util::deserialize_str_to_date_hyphen")]
22 pub end_date: NaiveDate,
23 #[serde(
25 rename = "yearStatus",
26 deserialize_with = "super::util::deserialize_str_to_bool"
27 )]
28 pub is_current: bool,
29}
30
31#[derive(Clone, Debug, Deserialize)]
33#[serde(rename_all = "camelCase")]
34pub struct Course {
35 #[serde(rename = "courseNum")]
39 pub course_id: String,
40 pub course_name: String,
42 pub classroom_name: String,
44 pub teacher_name: String,
46}
47
48#[derive(Clone, Debug, Deserialize)]
50#[serde(rename_all = "camelCase")]
51pub struct DailySchedule {
52 #[serde(
54 rename = "dateStr",
55 deserialize_with = "super::util::deserialize_str_to_date"
56 )]
57 pub date: NaiveDate,
58 #[serde(rename = "schedData")]
60 pub schedules: Vec<Schedule>,
61}
62
63#[derive(Clone, Debug, Deserialize)]
65pub struct Schedule {
66 #[serde(flatten)]
68 pub course: Course,
69 pub id: String,
71 pub uuid: String,
73 #[serde(
75 rename = "signStatus",
76 deserialize_with = "super::util::deserialize_str_to_bool"
77 )]
78 pub checked_in: bool,
79 #[serde(
81 rename = "classBeginTime",
82 deserialize_with = "super::util::deserialize_str_to_datetime"
83 )]
84 pub begin_time: DateTime<FixedOffset>,
85 #[serde(
87 rename = "classEndTime",
88 deserialize_with = "super::util::deserialize_str_to_datetime"
89 )]
90 pub end_time: DateTime<FixedOffset>,
91}
92
93impl IClass {
94 pub async fn query_semester(&self) -> Result<Vec<Semester>, IClassError> {
100 let url = self
101 .api_root
102 .join("app/course/get_base_school_year.action")?;
103 let response: Response<Vec<Semester>> = self
104 .client
105 .post(url)?
106 .form(&[("userId", &self.get_user_session()?.id)])?
107 .send()
108 .await?
109 .json()
110 .await?;
111 let semesters = response.into_result()?;
112 Ok(semesters)
113 }
114
115 pub async fn query_courses(&self) -> Result<Vec<Course>, IClassError> {
123 let user_session = self.get_user_session()?;
124 let url = self.api_root.join("app/my/get_my_course.action")?;
125 let response: Response<Vec<Course>> = self
126 .client
127 .post(url)?
128 .header("sessionId", &user_session.session_id)?
129 .form(&[("id", &user_session.id)])?
130 .send()
131 .await?
132 .json()
133 .await?;
134 let courses = response.into_result()?;
135
136 Ok(courses)
137 }
138
139 pub async fn query_daily_schedule(
149 &self,
150 date: &NaiveDate,
151 ) -> Result<Vec<Schedule>, IClassError> {
152 let user_session = self.get_user_session()?;
153 let url = self
154 .api_root
155 .join("app/course/get_stu_course_sched.action")?;
156 let date_str = super::util::format_date_to_str(date);
157 let response: Response<Vec<Schedule>> = self
158 .client
159 .post(url)?
160 .header("sessionId", &user_session.session_id)?
161 .form(&[("id", &user_session.id), ("dateStr", &date_str)])?
162 .send()
163 .await?
164 .json()
165 .await?;
166 let daily_schedule = response.into_result()?;
167
168 Ok(daily_schedule)
169 }
170
171 pub async fn query_weekly_schedule(
181 &self,
182 date: &NaiveDate,
183 ) -> Result<Vec<DailySchedule>, IClassError> {
184 let user_session = self.get_user_session()?;
185 let url = self
186 .api_root
187 .join("app/course/get_stu_course_sched_week.action")?;
188 let date_str = super::util::format_date_to_str(date);
189 let response: Response<Vec<DailySchedule>> = self
190 .client
191 .post(url)?
192 .header("sessionId", &user_session.session_id)?
193 .form(&[("id", &user_session.id), ("dateStr", &date_str)])?
194 .send()
195 .await?
196 .json()
197 .await?;
198 let week_schedule = response.into_result()?;
199
200 Ok(week_schedule)
201 }
202}
203
204impl fmt::Display for Semester {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 let Self {
207 code,
208 name,
209 begin_date,
210 end_date,
211 is_current,
212 } = self;
213 let current_indicator = if *is_current { " (current)" } else { "" };
214 write!(
215 f,
216 "{name} ({code}): {begin_date} ~ {end_date}{current_indicator}"
217 )
218 }
219}
220
221impl fmt::Display for Course {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 let Self {
224 course_name,
225 course_id,
226 teacher_name,
227 classroom_name,
228 ..
229 } = self;
230 write!(
231 f,
232 "{course_name} ({course_id}) - {teacher_name} @ {classroom_name}"
233 )
234 }
235}
236
237impl fmt::Display for Schedule {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 let Self {
240 course,
241 id,
242 uuid,
243 checked_in,
244 begin_time,
245 end_time,
246 ..
247 } = self;
248 let indicator = if *checked_in { "[✓]" } else { "[ ]" };
249 let (begin_time, end_time) = (
250 super::util::format_datetime_to_str(begin_time),
251 super::util::format_datetime_to_str(end_time),
252 );
253 write!(
254 f,
255 "{indicator} [{begin_time} ~ {end_time}] id={id} uuid={uuid} {}",
256 course.course_name
257 )
258 }
259}
260
261impl fmt::Display for DailySchedule {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 let Self { date, schedules } = self;
264 writeln!(f, "Schedule on {date}:")?;
265 for schedule in schedules {
266 let Schedule {
267 id,
268 uuid,
269 begin_time,
270 end_time,
271 ..
272 } = schedule;
273 let (begin_time, end_time) = (
274 super::util::format_datetime_to_str(begin_time),
275 super::util::format_datetime_to_str(end_time),
276 );
277 writeln!(
278 f,
279 " [{begin_time} ~ {end_time}] id={id} uuid={uuid} {}",
280 schedule.course.course_name
281 )?;
282 }
283 Ok(())
284 }
285}