manaba_sdk/
report.rs

1use crate::{Client, course::Course};
2use chrono::NaiveDateTime;
3use color_eyre::Result;
4use scraper::{Html, Selector};
5
6#[derive(Debug)]
7pub struct Report {
8    pub title: String,
9    pub state: ReportState,
10    pub start_date: Option<ReportDate>,
11    pub due_date: Option<ReportDate>,
12}
13
14#[derive(Debug)]
15pub struct ReportDate {
16    pub date: NaiveDateTime,
17    pub importance_level: ReportImportanceLevel,
18}
19
20impl ReportDate {
21    fn new(date: &str) -> Self {
22        const DATE_FORMAT: &str = "%Y-%m-%d %H:%M";
23        let date = NaiveDateTime::parse_from_str(date, DATE_FORMAT).unwrap();
24        let importance_level = ReportImportanceLevel::from(date);
25
26        ReportDate {
27            date,
28            importance_level,
29        }
30    }
31}
32
33#[derive(Debug, PartialEq, Eq)]
34pub enum ReportState {
35    Todo,
36    Done,
37}
38
39#[derive(Debug)]
40pub enum ReportImportanceLevel {
41    None,
42    Low,
43    Medium,
44    High,
45}
46
47impl From<NaiveDateTime> for ReportImportanceLevel {
48    fn from(date: NaiveDateTime) -> Self {
49        let today: chrono::NaiveDate = chrono::Local::now().date_naive();
50        let due_date: chrono::NaiveDate = date.date();
51
52        if due_date < today {
53            ReportImportanceLevel::None
54        } else {
55            let tomorrow = today + chrono::Duration::days(1);
56            let one_week_later = today + chrono::Duration::days(7);
57
58            if due_date <= tomorrow {
59                ReportImportanceLevel::High
60            } else if due_date <= one_week_later {
61                ReportImportanceLevel::Medium
62            } else {
63                ReportImportanceLevel::Low
64            }
65        }
66    }
67}
68
69impl Client {
70    pub async fn get_reports(&self, course: &Course) -> Result<Vec<Report>> {
71        let url = format!("{}/{}_report", self.base_url, course.id);
72
73        let request = self.request(reqwest::Method::GET, url);
74
75        let response = request.send().await?;
76        let body = response.text().await?;
77
78        let document = Html::parse_document(&body);
79
80        let selector = Selector::parse("table.stdlist tr:not(.title)").unwrap();
81        let reports_element = document.select(&selector);
82
83        let reports = reports_element
84            .map(|report_element| {
85                let selector = Selector::parse("td").unwrap();
86                let mut rows = report_element.select(&selector);
87
88                let title = {
89                    let row = rows.next().unwrap();
90                    let selector = Selector::parse("a").unwrap();
91                    let report_title_element = row.select(&selector).next().unwrap();
92                    report_title_element.inner_html()
93                };
94
95                let state = {
96                    let row = rows.next().unwrap();
97                    let selector = Selector::parse("strong").unwrap();
98
99                    if row.select(&selector).next().is_some() {
100                        ReportState::Done
101                    } else {
102                        ReportState::Todo
103                    }
104                };
105
106                let start_date = {
107                    let row = rows.next().unwrap();
108                    let date = row.inner_html();
109                    if date.is_empty() {
110                        None
111                    } else {
112                        Some(ReportDate::new(&date))
113                    }
114                };
115
116                let due_date = {
117                    let row = rows.next().unwrap();
118                    let date = row.inner_html();
119                    if date.is_empty() {
120                        None
121                    } else {
122                        Some(ReportDate::new(&date))
123                    }
124                };
125
126                Report {
127                    title,
128                    state,
129                    start_date,
130                    due_date,
131                }
132            })
133            .collect::<Vec<Report>>();
134
135        Ok(reports)
136    }
137}