@@ -3,6 +3,7 @@ use chrono::Datelike;
33use chrono:: Utc ;
44use scraper:: { Html , Selector } ;
55use std:: collections:: HashMap ;
6+ use crate :: cache:: Cache ;
67
78async fn fetch_page (
89 client : & Client ,
@@ -25,27 +26,129 @@ async fn fetch_page(
2526 Ok ( body)
2627}
2728
28- pub async fn fetch_info_page ( client : & Client , base_url : & str ) -> Result < String , String > {
29- fetch_page ( client, base_url, "Registration.aspx" ) . await
29+ pub async fn fetch_info_page (
30+ client : & Client ,
31+ base_url : & str ,
32+ cache : & Cache ,
33+ username : & str ,
34+ no_cache : bool ,
35+ ) -> Result < String , String > {
36+ if !no_cache {
37+ if let Some ( cached) = cache. get_page ( username, base_url, "Registration.aspx" , "" ) . await {
38+ return Ok ( cached) ;
39+ }
40+ }
41+
42+ let html = fetch_page ( client, base_url, "Registration.aspx" ) . await ?;
43+
44+ if !no_cache {
45+ cache. set_page ( username, base_url, "Registration.aspx" , "" , html. clone ( ) ) . await ;
46+ }
47+
48+ Ok ( html)
3049}
3150
32- pub async fn fetch_assignments_page ( client : & Client , base_url : & str ) -> Result < String , String > {
33- fetch_page ( client, base_url, "Assignments.aspx" ) . await
51+ pub async fn fetch_assignments_page (
52+ client : & Client ,
53+ base_url : & str ,
54+ cache : & Cache ,
55+ username : & str ,
56+ no_cache : bool ,
57+ ) -> Result < String , String > {
58+ if !no_cache {
59+ if let Some ( cached) = cache. get_page ( username, base_url, "Assignments.aspx" , "current" ) . await {
60+ return Ok ( cached) ;
61+ }
62+ }
63+
64+ let html = fetch_page ( client, base_url, "Assignments.aspx" ) . await ?;
65+
66+ if !no_cache {
67+ cache. set_page ( username, base_url, "Assignments.aspx" , "current" , html. clone ( ) ) . await ;
68+ }
69+
70+ Ok ( html)
3471}
3572
36- pub async fn fetch_report_page ( client : & Client , base_url : & str ) -> Result < String , String > {
37- fetch_page ( client, base_url, "ReportCards.aspx" ) . await
73+ pub async fn fetch_report_page (
74+ client : & Client ,
75+ base_url : & str ,
76+ cache : & Cache ,
77+ username : & str ,
78+ no_cache : bool ,
79+ ) -> Result < String , String > {
80+ if !no_cache {
81+ if let Some ( cached) = cache. get_page ( username, base_url, "ReportCards.aspx" , "" ) . await {
82+ return Ok ( cached) ;
83+ }
84+ }
85+
86+ let html = fetch_page ( client, base_url, "ReportCards.aspx" ) . await ?;
87+
88+ if !no_cache {
89+ cache. set_page ( username, base_url, "ReportCards.aspx" , "" , html. clone ( ) ) . await ;
90+ }
91+
92+ Ok ( html)
3893}
3994
40- pub async fn fetch_progress_page ( client : & Client , base_url : & str ) -> Result < String , String > {
41- fetch_page ( client, base_url, "InterimProgress.aspx" ) . await
95+ pub async fn fetch_progress_page (
96+ client : & Client ,
97+ base_url : & str ,
98+ cache : & Cache ,
99+ username : & str ,
100+ no_cache : bool ,
101+ ) -> Result < String , String > {
102+ if !no_cache {
103+ if let Some ( cached) = cache. get_page ( username, base_url, "InterimProgress.aspx" , "" ) . await {
104+ return Ok ( cached) ;
105+ }
106+ }
107+
108+ let html = fetch_page ( client, base_url, "InterimProgress.aspx" ) . await ?;
109+
110+ if !no_cache {
111+ cache. set_page ( username, base_url, "InterimProgress.aspx" , "" , html. clone ( ) ) . await ;
112+ }
113+
114+ Ok ( html)
42115}
43116
44- pub async fn fetch_transcript_page ( client : & Client , base_url : & str ) -> Result < String , String > {
45- fetch_page ( client, base_url, "Transcript.aspx" ) . await
117+ pub async fn fetch_transcript_page (
118+ client : & Client ,
119+ base_url : & str ,
120+ cache : & Cache ,
121+ username : & str ,
122+ no_cache : bool ,
123+ ) -> Result < String , String > {
124+ if !no_cache {
125+ if let Some ( cached) = cache. get_page ( username, base_url, "Transcript.aspx" , "" ) . await {
126+ return Ok ( cached) ;
127+ }
128+ }
129+
130+ let html = fetch_page ( client, base_url, "Transcript.aspx" ) . await ?;
131+
132+ if !no_cache {
133+ cache. set_page ( username, base_url, "Transcript.aspx" , "" , html. clone ( ) ) . await ;
134+ }
135+
136+ Ok ( html)
46137}
47138
48- pub async fn fetch_name_page ( client : & Client , base_url : & str ) -> Result < String , String > {
139+ pub async fn fetch_name_page (
140+ client : & Client ,
141+ base_url : & str ,
142+ cache : & Cache ,
143+ username : & str ,
144+ no_cache : bool ,
145+ ) -> Result < String , String > {
146+ if !no_cache {
147+ if let Some ( cached) = cache. get_page ( username, base_url, "Classwork" , "" ) . await {
148+ return Ok ( cached) ;
149+ }
150+ }
151+
49152 let url = format ! ( "{}/HomeAccess/Classes/Classwork" , base_url) ;
50153
51154 let response = client
@@ -54,12 +157,16 @@ pub async fn fetch_name_page(client: &Client, base_url: &str) -> Result<String,
54157 . await
55158 . map_err ( |_| "Failed to fetch classwork page" . to_string ( ) ) ?;
56159
57- let body = response
160+ let html = response
58161 . text ( )
59162 . await
60163 . map_err ( |_| "Failed to read classwork page body" . to_string ( ) ) ?;
61164
62- Ok ( body)
165+ if !no_cache {
166+ cache. set_page ( username, base_url, "Classwork" , "" , html. clone ( ) ) . await ;
167+ }
168+
169+ Ok ( html)
63170}
64171
65172fn format_six_weeks_param ( input : & str ) -> String {
@@ -102,44 +209,7 @@ pub async fn fetch_assignments_page_for_six_weeks(
102209 . await
103210 . map_err ( |_| "Failed to read assignments page" . to_string ( ) ) ?;
104211
105- let payload = {
106- let document = Html :: parse_document ( & body) ;
107-
108- let viewstate = document
109- . select ( & Selector :: parse ( "input[name='__VIEWSTATE']" ) . unwrap ( ) )
110- . next ( )
111- . and_then ( |el| el. value ( ) . attr ( "value" ) )
112- . unwrap_or ( "" )
113- . to_string ( ) ;
114-
115- let generator = document
116- . select ( & Selector :: parse ( "input[name='__VIEWSTATEGENERATOR']" ) . unwrap ( ) )
117- . next ( )
118- . and_then ( |el| el. value ( ) . attr ( "value" ) )
119- . unwrap_or ( "" )
120- . to_string ( ) ;
121-
122- let validation = document
123- . select ( & Selector :: parse ( "input[name='__EVENTVALIDATION']" ) . unwrap ( ) )
124- . next ( )
125- . and_then ( |el| el. value ( ) . attr ( "value" ) )
126- . unwrap_or ( "" )
127- . to_string ( ) ;
128-
129- let mut form_data: HashMap < & str , String > = HashMap :: new ( ) ;
130- form_data. insert ( "__EVENTTARGET" , "ctl00$plnMain$btnRefreshView" . to_string ( ) ) ;
131- form_data. insert ( "__EVENTARGUMENT" , "" . to_string ( ) ) ;
132- form_data. insert ( "__LASTFOCUS" , "" . to_string ( ) ) ;
133- form_data. insert ( "__VIEWSTATE" , viewstate) ;
134- form_data. insert ( "__VIEWSTATEGENERATOR" , generator) ;
135- form_data. insert ( "__EVENTVALIDATION" , validation) ;
136- form_data. insert ( "ctl00$plnMain$ddlReportCardRuns" , adjusted_six_weeks) ;
137- form_data. insert ( "ctl00$plnMain$ddlClasses" , "ALL" . to_string ( ) ) ;
138- form_data. insert ( "ctl00$plnMain$ddlCompetencies" , "ALL" . to_string ( ) ) ;
139- form_data. insert ( "ctl00$plnMain$ddlOrderBy" , "Class" . to_string ( ) ) ;
140-
141- form_data
142- } ;
212+ let payload = extract_form_data ( & body, & adjusted_six_weeks) ;
143213
144214 let post_resp = client
145215 . post ( & assignments_url)
@@ -155,3 +225,56 @@ pub async fn fetch_assignments_page_for_six_weeks(
155225
156226 Ok ( post_body)
157227}
228+
229+ fn extract_form_data ( body : & str , adjusted_six_weeks : & str ) -> HashMap < & ' static str , String > {
230+ let document = Html :: parse_document ( body) ;
231+
232+ static VIEWSTATE_SEL : std:: sync:: OnceLock < Selector > = std:: sync:: OnceLock :: new ( ) ;
233+ static GENERATOR_SEL : std:: sync:: OnceLock < Selector > = std:: sync:: OnceLock :: new ( ) ;
234+ static VALIDATION_SEL : std:: sync:: OnceLock < Selector > = std:: sync:: OnceLock :: new ( ) ;
235+
236+ let viewstate_sel = VIEWSTATE_SEL . get_or_init ( || {
237+ Selector :: parse ( "input[name='__VIEWSTATE']" ) . unwrap ( )
238+ } ) ;
239+ let generator_sel = GENERATOR_SEL . get_or_init ( || {
240+ Selector :: parse ( "input[name='__VIEWSTATEGENERATOR']" ) . unwrap ( )
241+ } ) ;
242+ let validation_sel = VALIDATION_SEL . get_or_init ( || {
243+ Selector :: parse ( "input[name='__EVENTVALIDATION']" ) . unwrap ( )
244+ } ) ;
245+
246+ let viewstate = document
247+ . select ( viewstate_sel)
248+ . next ( )
249+ . and_then ( |el| el. value ( ) . attr ( "value" ) )
250+ . unwrap_or ( "" )
251+ . to_string ( ) ;
252+
253+ let generator = document
254+ . select ( generator_sel)
255+ . next ( )
256+ . and_then ( |el| el. value ( ) . attr ( "value" ) )
257+ . unwrap_or ( "" )
258+ . to_string ( ) ;
259+
260+ let validation = document
261+ . select ( validation_sel)
262+ . next ( )
263+ . and_then ( |el| el. value ( ) . attr ( "value" ) )
264+ . unwrap_or ( "" )
265+ . to_string ( ) ;
266+
267+ let mut form_data: HashMap < & str , String > = HashMap :: new ( ) ;
268+ form_data. insert ( "__EVENTTARGET" , "ctl00$plnMain$btnRefreshView" . to_string ( ) ) ;
269+ form_data. insert ( "__EVENTARGUMENT" , "" . to_string ( ) ) ;
270+ form_data. insert ( "__LASTFOCUS" , "" . to_string ( ) ) ;
271+ form_data. insert ( "__VIEWSTATE" , viewstate) ;
272+ form_data. insert ( "__VIEWSTATEGENERATOR" , generator) ;
273+ form_data. insert ( "__EVENTVALIDATION" , validation) ;
274+ form_data. insert ( "ctl00$plnMain$ddlReportCardRuns" , adjusted_six_weeks. to_string ( ) ) ;
275+ form_data. insert ( "ctl00$plnMain$ddlClasses" , "ALL" . to_string ( ) ) ;
276+ form_data. insert ( "ctl00$plnMain$ddlCompetencies" , "ALL" . to_string ( ) ) ;
277+ form_data. insert ( "ctl00$plnMain$ddlOrderBy" , "Class" . to_string ( ) ) ;
278+
279+ form_data
280+ }
0 commit comments