1
- pub mod existing_file;
2
1
use std:: fmt:: Debug ;
3
2
4
- use anyhow:: { Result , Context , anyhow , Ok } ;
5
- use dialoguer:: { MultiSelect , theme:: ColorfulTheme } ;
3
+ use anyhow:: { anyhow , Context , Ok , Result } ;
4
+ use dialoguer:: { theme:: ColorfulTheme , MultiSelect } ;
6
5
use reqwest:: {
7
- blocking:: Client ,
8
- Url
6
+ blocking:: { multipart :: Form , Client } ,
7
+ Url ,
9
8
} ;
10
9
use scraper:: { ElementRef , Html , Selector } ;
11
10
12
- use crate :: { util:: SetQuerypath , preselect_delete_setting:: PreselectDeleteSetting } ;
13
-
14
- use self :: existing_file:: ExistingFile ;
11
+ use crate :: {
12
+ preselect_delete_setting:: PreselectDeleteSetting ,
13
+ uploaders:: file_with_filename:: AddFileWithFilename , util:: SetQuerypath ,
14
+ } ;
15
15
16
- use super :: { upload_provider :: UploadProvider , file_data :: FileData , upload_utils :: upload_files_to_url } ;
16
+ use super :: { file_data :: FileData , upload_provider :: UploadProvider , existing_file :: ExistingFile } ;
17
17
18
18
#[ derive( Debug ) ]
19
19
pub struct Excercise {
@@ -26,11 +26,7 @@ pub struct Excercise {
26
26
27
27
impl Excercise {
28
28
#[ allow( dead_code) ]
29
- pub fn new (
30
- client : & Client ,
31
- excercise : ElementRef < ' _ > ,
32
- base_url : Url ,
33
- ) -> Result < Excercise > {
29
+ pub fn new ( client : & Client , excercise : ElementRef < ' _ > , base_url : Url ) -> Result < Excercise > {
34
30
let mut raw = Self :: parse_from ( excercise, base_url) ?;
35
31
let overview_page = raw. get_overview_page ( client) ?;
36
32
{
@@ -41,21 +37,27 @@ impl Excercise {
41
37
}
42
38
43
39
pub fn parse_from ( excercise : ElementRef , base_url : Url ) -> Result < Excercise > {
44
- let name_selector = Selector :: parse ( r#".il_VAccordionHead span.ilAssignmentHeader"# ) . or_else ( |err| Err ( anyhow ! ( "Could not parse scraper: {:?}" , err) ) ) ?;
40
+ let name_selector = Selector :: parse ( r#".il_VAccordionHead span.ilAssignmentHeader"# )
41
+ . or_else ( |err| Err ( anyhow ! ( "Could not parse scraper: {:?}" , err) ) ) ?;
45
42
let name = excercise
46
43
. select ( & name_selector)
47
- . next ( ) . context ( "Did not find name for execise" ) ?
44
+ . next ( )
45
+ . context ( "Did not find name for execise" ) ?
48
46
. text ( )
49
47
. collect ( ) ;
50
48
51
- let submit_button_selector = Selector :: parse ( r#"a.btn.btn-default.btn-primary"# ) . or_else ( |err| Err ( anyhow ! ( "Could not parse scraper: {:?}" , err) ) ) ?;
49
+ let submit_button_selector = Selector :: parse ( r#"a.btn.btn-default.btn-primary"# )
50
+ . or_else ( |err| Err ( anyhow ! ( "Could not parse scraper: {:?}" , err) ) ) ?;
52
51
let button = excercise. select ( & submit_button_selector) . next ( ) ;
53
52
54
53
let mut url = base_url. clone ( ) ;
55
54
56
55
let ( has_files, subit_url_option) = match button {
57
56
Some ( submit_button) => {
58
- let querypath = submit_button. value ( ) . attr ( "href" ) . context ( "Did not find href" ) ?;
57
+ let querypath = submit_button
58
+ . value ( )
59
+ . attr ( "href" )
60
+ . context ( "Did not find href" ) ?;
59
61
url. set_querypath ( querypath) ;
60
62
61
63
(
@@ -88,13 +90,48 @@ impl Excercise {
88
90
Ok ( Html :: parse_document ( response. text ( ) ?. as_str ( ) ) )
89
91
}
90
92
}
93
+
94
+ fn parse_uploaded_files ( page : & Html ) -> Vec < ExistingFile > {
95
+ let file_row_selector = Selector :: parse ( r#"form tbody tr"# ) . unwrap ( ) ;
96
+ let file_rows = page. select ( & file_row_selector) ;
97
+
98
+ let id_selector = Selector :: parse ( r#"input[type="checkbox"][name="delivered[]"]"# ) . unwrap ( ) ;
99
+ let name_selector = Selector :: parse ( r#"td:nth-child(2)"# ) . unwrap ( ) ;
100
+
101
+ file_rows
102
+ . map ( |file_row| {
103
+ let file_id = file_row
104
+ . select ( & id_selector)
105
+ . next ( )
106
+ . unwrap ( )
107
+ . value ( )
108
+ . attr ( "value" )
109
+ . unwrap ( ) ;
110
+ let file_name = file_row
111
+ . select ( & name_selector)
112
+ . next ( )
113
+ . unwrap ( )
114
+ . text ( )
115
+ . collect :: < String > ( ) ;
116
+
117
+ ExistingFile {
118
+ name : file_name,
119
+ id : file_id. to_string ( ) ,
120
+ }
121
+ } )
122
+ . collect ( )
123
+ }
91
124
}
92
125
93
126
impl UploadProvider for Excercise {
94
127
type UploadedFile = ExistingFile ;
95
128
96
- fn upload_files < I : Iterator < Item = FileData > > ( & self , client : & Client , file_data_iter : I ) -> Result < ( ) > {
97
- let upload_button_selector = Selector :: parse ( r#"nav div.navbar-header button"# ) . unwrap ( ) ;
129
+ fn upload_files < I : Iterator < Item = FileData > > (
130
+ & self ,
131
+ client : & Client ,
132
+ file_data_iter : I ,
133
+ ) -> Result < ( ) > {
134
+ let upload_button_selector = Selector :: parse ( r#"nav div.navbar-header button"# ) . unwrap ( ) ;
98
135
let page = self . get_overview_page ( client) ?;
99
136
let upload_querypath = page
100
137
. select ( & upload_button_selector)
@@ -109,7 +146,7 @@ impl UploadProvider for Excercise {
109
146
110
147
let upload_page = client. post ( url. clone ( ) ) . send ( ) ?;
111
148
let form_selector = Selector :: parse ( r#"div#ilContentContainer form"# )
112
- . or_else ( |err| Err ( anyhow ! ( "Could not parse scraper: {:?}" , err) ) ) ?;
149
+ . or_else ( |err| Err ( anyhow ! ( "Could not parse scraper: {:?}" , err) ) ) ?;
113
150
let page = Html :: parse_document ( upload_page. text ( ) ?. as_str ( ) ) ;
114
151
let submit_querypath = page
115
152
. select ( & form_selector)
@@ -121,25 +158,37 @@ impl UploadProvider for Excercise {
121
158
122
159
url. set_querypath ( submit_querypath) ;
123
160
124
- upload_files_to_url ( & client, file_data_iter, url)
125
- }
126
-
161
+ let mut form = Form :: new ( ) ;
127
162
163
+ for ( index, file_data) in file_data_iter. enumerate ( ) {
164
+ form = form. file_with_name (
165
+ format ! ( "deliver[{}]" , index) ,
166
+ file_data. path ,
167
+ file_data. name ,
168
+ ) ?;
169
+ }
170
+ client. post ( url) . multipart ( form) . send ( ) ?;
171
+ Ok ( ( ) )
172
+ }
128
173
129
- fn get_conflicting_files ( self : & Self , client : & Client ) -> Vec < ExistingFile > {
174
+ fn get_conflicting_files < I : IntoIterator < Item = String > > ( self : & Self , client : & Client , _filenames : I ) -> Vec < ExistingFile > {
130
175
if !self . has_files {
131
176
return vec ! [ ] ;
132
177
}
133
178
let page = self . get_overview_page ( & client) . unwrap ( ) ;
134
- let files = ExistingFile :: parse_uploaded_files ( & page) ;
179
+ let files = Excercise :: parse_uploaded_files ( & page) ;
135
180
return files;
136
181
}
137
182
138
- fn delete_files < I : IntoIterator < Item = Self :: UploadedFile > > ( self : & Self , client : & Client , files : I ) -> Result < ( ) > {
183
+ fn delete_files < I : IntoIterator < Item = Self :: UploadedFile > > (
184
+ self : & Self ,
185
+ client : & Client ,
186
+ files : I ,
187
+ ) -> Result < ( ) > {
139
188
let page = self . get_overview_page ( client) ?;
140
189
let ids = files. into_iter ( ) . map ( |file| file. id . clone ( ) ) ;
141
190
let form_selector = Selector :: parse ( r#"div#ilContentContainer form"# )
142
- . or_else ( |err| Err ( anyhow ! ( "Could not parse scraper: {:?}" , err) ) ) ?;
191
+ . or_else ( |err| Err ( anyhow ! ( "Could not parse scraper: {:?}" , err) ) ) ?;
143
192
let delete_querypath = page
144
193
. select ( & form_selector)
145
194
. next ( )
@@ -158,30 +207,38 @@ impl UploadProvider for Excercise {
158
207
Ok ( ( ) )
159
208
}
160
209
161
- fn select_files_to_delete < ' a , I : Iterator < Item = FileData > > ( self : & ' a Self , preselect_setting : PreselectDeleteSetting , file_data : & I , conflicting_files : & ' a [ Self :: UploadedFile ] ) -> Result < Box < dyn Iterator < Item = ExistingFile > + ' _ > > where I : Clone {
162
- let mapped_files: Vec < ( & str , bool ) > = conflicting_files
163
- . iter ( )
164
- . map ( |file| {
165
- (
166
- file. name . as_str ( ) ,
167
- if preselect_setting == PreselectDeleteSetting :: ALL {
168
- true
169
- } else if preselect_setting == PreselectDeleteSetting :: NONE {
170
- false
171
- } else {
172
- file_data
173
- . clone ( )
174
- . any ( |file_data| file_data. name == file. name )
175
- }
176
- )
177
- } )
178
- . collect ( ) ;
179
- let selection = MultiSelect :: with_theme ( & ColorfulTheme :: default ( ) )
180
- . with_prompt ( "Which files do you want to delete" )
181
- . items_checked ( & mapped_files)
182
- . interact ( ) ?
183
- . into_iter ( )
184
- . map ( move |index| conflicting_files[ index] . clone ( ) ) ;
185
- return Ok ( Box :: new ( selection) ) ;
210
+ fn select_files_to_delete < ' a , I : Iterator < Item = FileData > > (
211
+ self : & ' a Self ,
212
+ preselect_setting : PreselectDeleteSetting ,
213
+ file_data : & I ,
214
+ conflicting_files : & ' a [ Self :: UploadedFile ] ,
215
+ ) -> Result < Box < dyn Iterator < Item = ExistingFile > + ' _ > >
216
+ where
217
+ I : Clone ,
218
+ {
219
+ let mapped_files: Vec < ( & str , bool ) > = conflicting_files
220
+ . iter ( )
221
+ . map ( |file| {
222
+ (
223
+ file. name . as_str ( ) ,
224
+ if preselect_setting == PreselectDeleteSetting :: ALL {
225
+ true
226
+ } else if preselect_setting == PreselectDeleteSetting :: NONE {
227
+ false
228
+ } else {
229
+ file_data
230
+ . clone ( )
231
+ . any ( |file_data| file_data. name == file. name )
232
+ } ,
233
+ )
234
+ } )
235
+ . collect ( ) ;
236
+ let selection = MultiSelect :: with_theme ( & ColorfulTheme :: default ( ) )
237
+ . with_prompt ( "Which files do you want to delete" )
238
+ . items_checked ( & mapped_files)
239
+ . interact ( ) ?
240
+ . into_iter ( )
241
+ . map ( move |index| conflicting_files[ index] . clone ( ) ) ;
242
+ return Ok ( Box :: new ( selection) ) ;
186
243
}
187
- }
244
+ }
0 commit comments