Skip to content

Commit

Permalink
Ilias folder working
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr-Pine committed Oct 23, 2023
1 parent 966d634 commit 54b69b7
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 123 deletions.
3 changes: 2 additions & 1 deletion .ilias_upload
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
ilias_id = '1234567'
username = 'hier könnte ihr u-Kürzel stehen'
preselect_delete = SMART
preselect_delete = 'SMART'
upload_type = 'EXERCISE'
transform_regex = 'test(?<index>\d+)'
transform_format = 'my_test_${index}'
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ilias_uploader_utility"
version = "1.0.1"
version = "1.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
8 changes: 6 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use dialoguer::{theme::ColorfulTheme, Confirm, Password, Select};
use keyring::Entry;
use preselect_delete_setting::PreselectDeleteSetting;
use reqwest::blocking::Client;
use util::UploadType;

mod arguments;
mod authentication;
Expand Down Expand Up @@ -132,6 +133,7 @@ fn main() -> Result<()> {
&reqwest_client,
selected_excercise,
transformed_file_data,
upload_type,
preselect_delete_setting,
)
}
Expand All @@ -141,6 +143,7 @@ fn main() -> Result<()> {
&reqwest_client,
&target,
transformed_file_data,
upload_type,
preselect_delete_setting,
)
}
Expand All @@ -151,15 +154,16 @@ fn upload_files<T: UploadProvider, I: Iterator<Item = FileData>>(
client: &Client,
target: &T,
transformed_files: I,
upload_type: UploadType,
preselect_delete_setting: PreselectDeleteSetting,
) -> Result<()>
where
I: Clone,
{
let conflicting_files = target.get_conflicting_files(&client);
let conflicting_files = target.get_conflicting_files(&client, transformed_files.clone().map(|data| data.name));
if !conflicting_files.is_empty() {
let delete = Confirm::with_theme(&ColorfulTheme::default())
.with_prompt("This excercise already has uploaded files. Do you want to delete them?")
.with_prompt(upload_type.get_delete_message())
.default(true)
.interact()
.unwrap();
Expand Down
165 changes: 111 additions & 54 deletions src/uploaders/excercise.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
pub mod existing_file;
use std::fmt::Debug;

use anyhow::{Result, Context, anyhow, Ok};
use dialoguer::{MultiSelect, theme::ColorfulTheme};
use anyhow::{anyhow, Context, Ok, Result};
use dialoguer::{theme::ColorfulTheme, MultiSelect};
use reqwest::{
blocking::Client,
Url
blocking::{multipart::Form, Client},
Url,
};
use scraper::{ElementRef, Html, Selector};

use crate::{util::SetQuerypath, preselect_delete_setting::PreselectDeleteSetting};

use self::existing_file::ExistingFile;
use crate::{
preselect_delete_setting::PreselectDeleteSetting,
uploaders::file_with_filename::AddFileWithFilename, util::SetQuerypath,
};

use super::{upload_provider::UploadProvider, file_data::FileData, upload_utils::upload_files_to_url};
use super::{file_data::FileData, upload_provider::UploadProvider, existing_file::ExistingFile};

#[derive(Debug)]
pub struct Excercise {
Expand All @@ -26,11 +26,7 @@ pub struct Excercise {

impl Excercise {
#[allow(dead_code)]
pub fn new(
client: &Client,
excercise: ElementRef<'_>,
base_url: Url,
) -> Result<Excercise> {
pub fn new(client: &Client, excercise: ElementRef<'_>, base_url: Url) -> Result<Excercise> {
let mut raw = Self::parse_from(excercise, base_url)?;
let overview_page = raw.get_overview_page(client)?;
{
Expand All @@ -41,21 +37,27 @@ impl Excercise {
}

pub fn parse_from(excercise: ElementRef, base_url: Url) -> Result<Excercise> {
let name_selector = Selector::parse(r#".il_VAccordionHead span.ilAssignmentHeader"#).or_else(|err| Err(anyhow!("Could not parse scraper: {:?}", err)))?;
let name_selector = Selector::parse(r#".il_VAccordionHead span.ilAssignmentHeader"#)
.or_else(|err| Err(anyhow!("Could not parse scraper: {:?}", err)))?;
let name = excercise
.select(&name_selector)
.next().context("Did not find name for execise")?
.next()
.context("Did not find name for execise")?
.text()
.collect();

let submit_button_selector = Selector::parse(r#"a.btn.btn-default.btn-primary"#).or_else(|err| Err(anyhow!("Could not parse scraper: {:?}", err)))?;
let submit_button_selector = Selector::parse(r#"a.btn.btn-default.btn-primary"#)
.or_else(|err| Err(anyhow!("Could not parse scraper: {:?}", err)))?;
let button = excercise.select(&submit_button_selector).next();

let mut url = base_url.clone();

let (has_files, subit_url_option) = match button {
Some(submit_button) => {
let querypath = submit_button.value().attr("href").context("Did not find href")?;
let querypath = submit_button
.value()
.attr("href")
.context("Did not find href")?;
url.set_querypath(querypath);

(
Expand Down Expand Up @@ -88,13 +90,48 @@ impl Excercise {
Ok(Html::parse_document(response.text()?.as_str()))
}
}

fn parse_uploaded_files(page: &Html) -> Vec<ExistingFile> {
let file_row_selector = Selector::parse(r#"form tbody tr"#).unwrap();
let file_rows = page.select(&file_row_selector);

let id_selector = Selector::parse(r#"input[type="checkbox"][name="delivered[]"]"#).unwrap();
let name_selector = Selector::parse(r#"td:nth-child(2)"#).unwrap();

file_rows
.map(|file_row| {
let file_id = file_row
.select(&id_selector)
.next()
.unwrap()
.value()
.attr("value")
.unwrap();
let file_name = file_row
.select(&name_selector)
.next()
.unwrap()
.text()
.collect::<String>();

ExistingFile {
name: file_name,
id: file_id.to_string(),
}
})
.collect()
}
}

impl UploadProvider for Excercise {
type UploadedFile = ExistingFile;

fn upload_files<I: Iterator<Item = FileData>>(&self, client: &Client, file_data_iter: I) -> Result<()> {
let upload_button_selector = Selector::parse(r#"nav div.navbar-header button"#).unwrap();
fn upload_files<I: Iterator<Item = FileData>>(
&self,
client: &Client,
file_data_iter: I,
) -> Result<()> {
let upload_button_selector = Selector::parse(r#"nav div.navbar-header button"#).unwrap();
let page = self.get_overview_page(client)?;
let upload_querypath = page
.select(&upload_button_selector)
Expand All @@ -109,7 +146,7 @@ impl UploadProvider for Excercise {

let upload_page = client.post(url.clone()).send()?;
let form_selector = Selector::parse(r#"div#ilContentContainer form"#)
.or_else(|err| Err(anyhow!("Could not parse scraper: {:?}", err)))?;
.or_else(|err| Err(anyhow!("Could not parse scraper: {:?}", err)))?;
let page = Html::parse_document(upload_page.text()?.as_str());
let submit_querypath = page
.select(&form_selector)
Expand All @@ -121,25 +158,37 @@ impl UploadProvider for Excercise {

url.set_querypath(submit_querypath);

upload_files_to_url(&client, file_data_iter, url)
}

let mut form = Form::new();

for (index, file_data) in file_data_iter.enumerate() {
form = form.file_with_name(
format!("deliver[{}]", index),
file_data.path,
file_data.name,
)?;
}
client.post(url).multipart(form).send()?;
Ok(())
}

fn get_conflicting_files(self: &Self, client: &Client) -> Vec<ExistingFile> {
fn get_conflicting_files<I: IntoIterator<Item = String>>(self: &Self, client: &Client, _filenames: I) -> Vec<ExistingFile> {
if !self.has_files {
return vec![];
}
let page = self.get_overview_page(&client).unwrap();
let files = ExistingFile::parse_uploaded_files(&page);
let files = Excercise::parse_uploaded_files(&page);
return files;
}

fn delete_files<I: IntoIterator<Item = Self::UploadedFile>>(self: &Self, client: &Client, files: I) -> Result<()>{
fn delete_files<I: IntoIterator<Item = Self::UploadedFile>>(
self: &Self,
client: &Client,
files: I,
) -> Result<()> {
let page = self.get_overview_page(client)?;
let ids = files.into_iter().map(|file| file.id.clone());
let form_selector = Selector::parse(r#"div#ilContentContainer form"#)
.or_else(|err| Err(anyhow!("Could not parse scraper: {:?}", err)))?;
.or_else(|err| Err(anyhow!("Could not parse scraper: {:?}", err)))?;
let delete_querypath = page
.select(&form_selector)
.next()
Expand All @@ -158,30 +207,38 @@ impl UploadProvider for Excercise {
Ok(())
}

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 {
let mapped_files: Vec<(&str, bool)> = conflicting_files
.iter()
.map(|file| {
(
file.name.as_str(),
if preselect_setting == PreselectDeleteSetting::ALL {
true
} else if preselect_setting == PreselectDeleteSetting::NONE {
false
} else {
file_data
.clone()
.any(|file_data| file_data.name == file.name)
}
)
})
.collect();
let selection = MultiSelect::with_theme(&ColorfulTheme::default())
.with_prompt("Which files do you want to delete")
.items_checked(&mapped_files)
.interact()?
.into_iter()
.map(move |index| conflicting_files[index].clone());
return Ok(Box::new(selection));
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,
{
let mapped_files: Vec<(&str, bool)> = conflicting_files
.iter()
.map(|file| {
(
file.name.as_str(),
if preselect_setting == PreselectDeleteSetting::ALL {
true
} else if preselect_setting == PreselectDeleteSetting::NONE {
false
} else {
file_data
.clone()
.any(|file_data| file_data.name == file.name)
},
)
})
.collect();
let selection = MultiSelect::with_theme(&ColorfulTheme::default())
.with_prompt("Which files do you want to delete")
.items_checked(&mapped_files)
.interact()?
.into_iter()
.map(move |index| conflicting_files[index].clone());
return Ok(Box::new(selection));
}
}
}
24 changes: 0 additions & 24 deletions src/uploaders/excercise/existing_file.rs

This file was deleted.

5 changes: 5 additions & 0 deletions src/uploaders/existing_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#[derive(Debug, Clone)]
pub struct ExistingFile {
pub name: String,
pub id: String
}
Loading

0 comments on commit 54b69b7

Please sign in to comment.