Skip to content

Commit 54b69b7

Browse files
committed
Ilias folder working
1 parent 966d634 commit 54b69b7

File tree

12 files changed

+210
-123
lines changed

12 files changed

+210
-123
lines changed

.ilias_upload

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
ilias_id = '1234567'
22
username = 'hier könnte ihr u-Kürzel stehen'
3-
preselect_delete = SMART
3+
preselect_delete = 'SMART'
4+
upload_type = 'EXERCISE'
45
transform_regex = 'test(?<index>\d+)'
56
transform_format = 'my_test_${index}'

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ilias_uploader_utility"
3-
version = "1.0.1"
3+
version = "1.1.0"
44
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

src/main.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use dialoguer::{theme::ColorfulTheme, Confirm, Password, Select};
66
use keyring::Entry;
77
use preselect_delete_setting::PreselectDeleteSetting;
88
use reqwest::blocking::Client;
9+
use util::UploadType;
910

1011
mod arguments;
1112
mod authentication;
@@ -132,6 +133,7 @@ fn main() -> Result<()> {
132133
&reqwest_client,
133134
selected_excercise,
134135
transformed_file_data,
136+
upload_type,
135137
preselect_delete_setting,
136138
)
137139
}
@@ -141,6 +143,7 @@ fn main() -> Result<()> {
141143
&reqwest_client,
142144
&target,
143145
transformed_file_data,
146+
upload_type,
144147
preselect_delete_setting,
145148
)
146149
}
@@ -151,15 +154,16 @@ fn upload_files<T: UploadProvider, I: Iterator<Item = FileData>>(
151154
client: &Client,
152155
target: &T,
153156
transformed_files: I,
157+
upload_type: UploadType,
154158
preselect_delete_setting: PreselectDeleteSetting,
155159
) -> Result<()>
156160
where
157161
I: Clone,
158162
{
159-
let conflicting_files = target.get_conflicting_files(&client);
163+
let conflicting_files = target.get_conflicting_files(&client, transformed_files.clone().map(|data| data.name));
160164
if !conflicting_files.is_empty() {
161165
let delete = Confirm::with_theme(&ColorfulTheme::default())
162-
.with_prompt("This excercise already has uploaded files. Do you want to delete them?")
166+
.with_prompt(upload_type.get_delete_message())
163167
.default(true)
164168
.interact()
165169
.unwrap();

src/uploaders/excercise.rs

Lines changed: 111 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
pub mod existing_file;
21
use std::fmt::Debug;
32

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};
65
use reqwest::{
7-
blocking::Client,
8-
Url
6+
blocking::{multipart::Form, Client},
7+
Url,
98
};
109
use scraper::{ElementRef, Html, Selector};
1110

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+
};
1515

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};
1717

1818
#[derive(Debug)]
1919
pub struct Excercise {
@@ -26,11 +26,7 @@ pub struct Excercise {
2626

2727
impl Excercise {
2828
#[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> {
3430
let mut raw = Self::parse_from(excercise, base_url)?;
3531
let overview_page = raw.get_overview_page(client)?;
3632
{
@@ -41,21 +37,27 @@ impl Excercise {
4137
}
4238

4339
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)))?;
4542
let name = excercise
4643
.select(&name_selector)
47-
.next().context("Did not find name for execise")?
44+
.next()
45+
.context("Did not find name for execise")?
4846
.text()
4947
.collect();
5048

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)))?;
5251
let button = excercise.select(&submit_button_selector).next();
5352

5453
let mut url = base_url.clone();
5554

5655
let (has_files, subit_url_option) = match button {
5756
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")?;
5961
url.set_querypath(querypath);
6062

6163
(
@@ -88,13 +90,48 @@ impl Excercise {
8890
Ok(Html::parse_document(response.text()?.as_str()))
8991
}
9092
}
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+
}
91124
}
92125

93126
impl UploadProvider for Excercise {
94127
type UploadedFile = ExistingFile;
95128

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();
98135
let page = self.get_overview_page(client)?;
99136
let upload_querypath = page
100137
.select(&upload_button_selector)
@@ -109,7 +146,7 @@ impl UploadProvider for Excercise {
109146

110147
let upload_page = client.post(url.clone()).send()?;
111148
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)))?;
113150
let page = Html::parse_document(upload_page.text()?.as_str());
114151
let submit_querypath = page
115152
.select(&form_selector)
@@ -121,25 +158,37 @@ impl UploadProvider for Excercise {
121158

122159
url.set_querypath(submit_querypath);
123160

124-
upload_files_to_url(&client, file_data_iter, url)
125-
}
126-
161+
let mut form = Form::new();
127162

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+
}
128173

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> {
130175
if !self.has_files {
131176
return vec![];
132177
}
133178
let page = self.get_overview_page(&client).unwrap();
134-
let files = ExistingFile::parse_uploaded_files(&page);
179+
let files = Excercise::parse_uploaded_files(&page);
135180
return files;
136181
}
137182

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<()> {
139188
let page = self.get_overview_page(client)?;
140189
let ids = files.into_iter().map(|file| file.id.clone());
141190
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)))?;
143192
let delete_querypath = page
144193
.select(&form_selector)
145194
.next()
@@ -158,30 +207,38 @@ impl UploadProvider for Excercise {
158207
Ok(())
159208
}
160209

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));
186243
}
187-
}
244+
}

src/uploaders/excercise/existing_file.rs

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/uploaders/existing_file.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#[derive(Debug, Clone)]
2+
pub struct ExistingFile {
3+
pub name: String,
4+
pub id: String
5+
}

0 commit comments

Comments
 (0)