From 5a4cb5fb76656243f4ccc90174627bff80ec04ab Mon Sep 17 00:00:00 2001 From: Eetu Lassi Date: Fri, 11 Oct 2024 18:21:58 +0300 Subject: [PATCH 1/8] Added multiple flags to moodle --- Cargo.toml | 1 + src/moodle.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e5245ff..08af1a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ serde = { version = "1.0", default-features = false, features = ["derive"] } toml = "0.8" # Cli +itertools = "0.13.0" clap = { version = "4.5", features = ["derive", "cargo"] } thiserror = "1.0.63" tracing = { version = "0.1.40" } diff --git a/src/moodle.rs b/src/moodle.rs index dc5ebf2..d6ae58e 100644 --- a/src/moodle.rs +++ b/src/moodle.rs @@ -6,6 +6,8 @@ use moodle_xml::{ quiz::Quiz, }; use std::io::{self, BufRead, BufReader}; +use itertools::Itertools; +use crate::flag_generator::Flag; /// Create an exam from a list of task build process outputs, which includes the question as well pub fn create_exam( @@ -50,7 +52,7 @@ pub fn create_exam( let mut question = ShortAnswerQuestion::new(task_config.name.clone(), instructions_string, None); let answers = if item.flags.len() == 1 { - vec![ + vec![ Answer::new( 100, item.flags[0].encase_flag(), @@ -61,9 +63,10 @@ pub fn create_exam( item.flags[0].flag_string(), "Correct!".to_string().into(), ), - ] + ] } else { - todo!("Multiple flags in subtasks not supported yet") + // Adds 1-inf flags as answer with chosen separator + process_multiple_flags(item.flags.clone(), ";") }; question .add_answers(answers) @@ -82,3 +85,71 @@ pub fn create_exam( .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Error: {:?}", e)))?; Ok(()) } + +// Function to encase each flag with a specified separator +fn encase_each_flag(flags: &[Flag], separator: &str) -> String { + flags.iter().map(|f| f.encase_flag()).join(separator) +} + +// Function to join flags without encasing them +fn join_flags(flags: &[Flag], separator: &str) -> String { + flags.iter().map(|f| f.flag_string()).join(separator) +} + +// Function to process multiple flags and create answers +fn process_multiple_flags(flags: Vec, separator: &str) -> Vec { + let total_flags = flags.len(); + let mut answers = Vec::new(); + + for r in 1..=total_flags { + for combination in flags.iter().combinations(r) { + for perm in combination.iter().permutations(r) { + let perm_flags: Vec = perm.iter().cloned().map(|&flag| flag.clone()).collect(); + let encased_combined_answer = encase_each_flag(&perm_flags, separator); // Pass as a slice + let combined_answer = join_flags(&perm_flags, separator); // Pass as a slice + + // Calculate points based on the number of flags + let points = ((r as f64 / total_flags as f64) * 100.0).round() as u8; + + answers.push(Answer::new(points, encased_combined_answer.clone(), "Correct!".to_string().into())); + answers.push(Answer::new(points, combined_answer.clone(), "Correct!".to_string().into())); + } + } + } + + answers +} + +#[cfg(test)] +mod tests { + use super::*; + use uuid::Uuid; + use crate::flag_generator::Algorithm; + + #[test] + fn test_multiple_flags() { + let mut flags = Vec::new(); + + let id = Uuid::now_v7(); + let secret = "Work".to_string(); + let secret2 = "dslpl".to_string(); + let secret3 = "dslpl".to_string(); + let taskid = "task1".to_string(); + let taskid2 = "Wording mording".to_string(); + let taskid3 = "kdosogkdo".to_string(); + let prefix = "task_prefix".to_string(); + + let flag1 = Flag::new_random_flag(taskid2,32); + let flag2 = Flag::new_user_flag(taskid, &Algorithm::HMAC_SHA3_256, &secret, &secret3, &id); + let flag3 = Flag::new_user_flag(prefix, &Algorithm::HMAC_SHA3_256, &secret2, &taskid3, &id); + + flags.push(flag1); + flags.push(flag2); + flags.push(flag3); + + let answers = process_multiple_flags(flags, ";"); + for answer in answers { + println!("{:?}", answer); + } + } +} \ No newline at end of file From a29ca7e603889ad2485791bc0359336b9b3c16ed Mon Sep 17 00:00:00 2001 From: Eetu Lassi Date: Fri, 11 Oct 2024 18:43:06 +0300 Subject: [PATCH 2/8] clippy --- src/moodle.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/moodle.rs b/src/moodle.rs index d6ae58e..c67cee3 100644 --- a/src/moodle.rs +++ b/src/moodle.rs @@ -52,18 +52,18 @@ pub fn create_exam( let mut question = ShortAnswerQuestion::new(task_config.name.clone(), instructions_string, None); let answers = if item.flags.len() == 1 { - vec![ - Answer::new( - 100, - item.flags[0].encase_flag(), - "Correct!".to_string().into(), - ), - Answer::new( - 100, - item.flags[0].flag_string(), - "Correct!".to_string().into(), - ), - ] + vec![ + Answer::new( + 100, + item.flags[0].encase_flag(), + "Correct!".to_string().into(), + ), + Answer::new( + 100, + item.flags[0].flag_string(), + "Correct!".to_string().into(), + ), + ] } else { // Adds 1-inf flags as answer with chosen separator process_multiple_flags(item.flags.clone(), ";") From ac6c0ee51eb0bab56dfe71131218c5c6a378e22c Mon Sep 17 00:00:00 2001 From: Eetu Lassi Date: Fri, 11 Oct 2024 18:48:15 +0300 Subject: [PATCH 3/8] more clippy --- src/moodle.rs | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/moodle.rs b/src/moodle.rs index c67cee3..d1f1c71 100644 --- a/src/moodle.rs +++ b/src/moodle.rs @@ -53,16 +53,16 @@ pub fn create_exam( ShortAnswerQuestion::new(task_config.name.clone(), instructions_string, None); let answers = if item.flags.len() == 1 { vec![ - Answer::new( - 100, - item.flags[0].encase_flag(), - "Correct!".to_string().into(), - ), - Answer::new( - 100, - item.flags[0].flag_string(), - "Correct!".to_string().into(), - ), + Answer::new( + 100, + item.flags[0].encase_flag(), + "Correct!".to_string().into(), + ), + Answer::new( + 100, + item.flags[0].flag_string(), + "Correct!".to_string().into(), + ), ] } else { // Adds 1-inf flags as answer with chosen separator @@ -104,15 +104,24 @@ fn process_multiple_flags(flags: Vec, separator: &str) -> Vec { for r in 1..=total_flags { for combination in flags.iter().combinations(r) { for perm in combination.iter().permutations(r) { - let perm_flags: Vec = perm.iter().cloned().map(|&flag| flag.clone()).collect(); + let perm_flags: Vec = + perm.iter().cloned().map(|&flag| flag.clone()).collect(); let encased_combined_answer = encase_each_flag(&perm_flags, separator); // Pass as a slice let combined_answer = join_flags(&perm_flags, separator); // Pass as a slice // Calculate points based on the number of flags let points = ((r as f64 / total_flags as f64) * 100.0).round() as u8; - answers.push(Answer::new(points, encased_combined_answer.clone(), "Correct!".to_string().into())); - answers.push(Answer::new(points, combined_answer.clone(), "Correct!".to_string().into())); + answers.push(Answer::new( + points, + encased_combined_answer.clone(), + "Correct!".to_string().into() + )); + answers.push(Answer::new( + points, + combined_answer.clone(), + "Correct!".to_string().into() + )); } } } @@ -123,9 +132,9 @@ fn process_multiple_flags(flags: Vec, separator: &str) -> Vec { #[cfg(test)] mod tests { use super::*; - use uuid::Uuid; use crate::flag_generator::Algorithm; - + use uuid::Uuid; + #[test] fn test_multiple_flags() { let mut flags = Vec::new(); @@ -139,7 +148,7 @@ mod tests { let taskid3 = "kdosogkdo".to_string(); let prefix = "task_prefix".to_string(); - let flag1 = Flag::new_random_flag(taskid2,32); + let flag1 = Flag::new_random_flag(taskid2, 32); let flag2 = Flag::new_user_flag(taskid, &Algorithm::HMAC_SHA3_256, &secret, &secret3, &id); let flag3 = Flag::new_user_flag(prefix, &Algorithm::HMAC_SHA3_256, &secret2, &taskid3, &id); From 38447fe0768bbf4d93a2777ea182b768092927ce Mon Sep 17 00:00:00 2001 From: Eetu Lassi Date: Fri, 11 Oct 2024 18:52:49 +0300 Subject: [PATCH 4/8] lastclippy --- src/moodle.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/moodle.rs b/src/moodle.rs index d1f1c71..4389034 100644 --- a/src/moodle.rs +++ b/src/moodle.rs @@ -1,13 +1,13 @@ use crate::build_process::TaskBuildProcessOutput; use crate::config::{OutputKind, Task}; +use crate::flag_generator::Flag; +use itertools::Itertools; use moodle_xml::{ answer::Answer, question::{Question, QuestionType, ShortAnswerQuestion}, quiz::Quiz, }; use std::io::{self, BufRead, BufReader}; -use itertools::Itertools; -use crate::flag_generator::Flag; /// Create an exam from a list of task build process outputs, which includes the question as well pub fn create_exam( @@ -115,17 +115,16 @@ fn process_multiple_flags(flags: Vec, separator: &str) -> Vec { answers.push(Answer::new( points, encased_combined_answer.clone(), - "Correct!".to_string().into() + "Correct!".to_string().into(), )); answers.push(Answer::new( points, combined_answer.clone(), - "Correct!".to_string().into() + "Correct!".to_string().into(), )); } } } - answers } From 9dc3d136b7334f37a70ec53316f4376972ca1b2d Mon Sep 17 00:00:00 2001 From: Eetu Lassi Date: Sat, 12 Oct 2024 12:04:32 +0300 Subject: [PATCH 5/8] format --- src/moodle.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/moodle.rs b/src/moodle.rs index 4389034..51d9f5f 100644 --- a/src/moodle.rs +++ b/src/moodle.rs @@ -63,7 +63,7 @@ pub fn create_exam( item.flags[0].flag_string(), "Correct!".to_string().into(), ), - ] + ] } else { // Adds 1-inf flags as answer with chosen separator process_multiple_flags(item.flags.clone(), ";") @@ -104,7 +104,7 @@ fn process_multiple_flags(flags: Vec, separator: &str) -> Vec { for r in 1..=total_flags { for combination in flags.iter().combinations(r) { for perm in combination.iter().permutations(r) { - let perm_flags: Vec = + let perm_flags: Vec = perm.iter().cloned().map(|&flag| flag.clone()).collect(); let encased_combined_answer = encase_each_flag(&perm_flags, separator); // Pass as a slice let combined_answer = join_flags(&perm_flags, separator); // Pass as a slice @@ -160,4 +160,4 @@ mod tests { println!("{:?}", answer); } } -} \ No newline at end of file +} From 8987a66f77e365430ab55a7ccae1a98fd5a90624 Mon Sep 17 00:00:00 2001 From: Eetu Lassi Date: Wed, 23 Oct 2024 15:18:32 +0300 Subject: [PATCH 6/8] multiple flag review changes --- src/moodle.rs | 78 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/src/moodle.rs b/src/moodle.rs index 51d9f5f..48341e5 100644 --- a/src/moodle.rs +++ b/src/moodle.rs @@ -66,7 +66,7 @@ pub fn create_exam( ] } else { // Adds 1-inf flags as answer with chosen separator - process_multiple_flags(item.flags.clone(), ";") + process_multiple_flags(item.flags.clone(), " ") }; question .add_answers(answers) @@ -137,27 +137,77 @@ mod tests { #[test] fn test_multiple_flags() { let mut flags = Vec::new(); + let mut flags2 = Vec::new(); + let mut flags3 = Vec::new(); let id = Uuid::now_v7(); let secret = "Work".to_string(); let secret2 = "dslpl".to_string(); let secret3 = "dslpl".to_string(); let taskid = "task1".to_string(); - let taskid2 = "Wording mording".to_string(); - let taskid3 = "kdosogkdo".to_string(); - let prefix = "task_prefix".to_string(); + let taskid2 = "task2".to_string(); + let taskid3 = "task3".to_string(); + let taskid4 = "task4".to_string(); + let taskid5 = "task5".to_string(); + let taskid6 = "task6".to_string(); + let prefix4 = "task4_prefix".to_string(); + let prefix5 = "task5_prefix".to_string(); + let prefix6 = "task6_prefix".to_string(); + + let flag1 = Flag::new_random_flag(taskid, 32); + let flag2 = Flag::new_random_flag(taskid2, 32); + let flag3 = Flag::new_random_flag(taskid3, 32); + let flag4 = Flag::new_user_flag(prefix4, &Algorithm::HMAC_SHA3_256, &secret, &taskid4, &id); + let flag5 = Flag::new_user_flag(prefix5, &Algorithm::HMAC_SHA3_256, &secret2, &taskid5, &id); + let flag6 = Flag::new_user_flag(prefix6, &Algorithm::HMAC_SHA3_256, &secret3, &taskid6, &id); + + flags2.push(flag1); + + flags3.push(flag2); + flags3.push(flag3); + + flags.push(flag4); + flags.push(flag5); + flags.push(flag6); + + let answers = process_multiple_flags(flags, " "); + let answers2 = process_multiple_flags(flags2, " "); + let answers3 = process_multiple_flags(flags3, " "); - let flag1 = Flag::new_random_flag(taskid2, 32); - let flag2 = Flag::new_user_flag(taskid, &Algorithm::HMAC_SHA3_256, &secret, &secret3, &id); - let flag3 = Flag::new_user_flag(prefix, &Algorithm::HMAC_SHA3_256, &secret2, &taskid3, &id); - - flags.push(flag1); - flags.push(flag2); - flags.push(flag3); - - let answers = process_multiple_flags(flags, ";"); for answer in answers { - println!("{:?}", answer); + match answer.fraction{ + 33 => { + assert!(answer.text.contains("task4_prefix:") || answer.text.contains("task5_prefix:") || answer.text.contains("task6_prefix:")); + } + 67 => { + assert!((answer.text.contains("task4_prefix:") && answer.text.contains("task5_prefix:")) + || (answer.text.contains("task6_prefix:") && answer.text.contains("task5_prefix:")) + || (answer.text.contains("task6_prefix:") && answer.text.contains("task4_prefix:"))); + } + 100 => { + assert!((answer.text.contains("task4_prefix:") && answer.text.contains("task5_prefix:") && answer.text.contains("task6_prefix:"))); + } + _ => { + unreachable!("Unexpected fraction value encountered in test") + } + } + } + for answer in answers2 { + assert!(answer.fraction == 100); + assert!(answer.text.contains("task1:")); + } + for answer in answers3 { + match answer.fraction{ + 50 => { + assert!(answer.text.contains("task2:") || answer.text.contains("task3:")); + } + 100 => { + assert!(answer.text.contains("task2:") && answer.text.contains("task3:")); + } + _ => { + unreachable!("Unexpected fraction value encountered in test") + } + } } } } From 4d4b8719794e6841935dad8463a9e57232970cff Mon Sep 17 00:00:00 2001 From: Eetu Lassi Date: Wed, 23 Oct 2024 15:30:01 +0300 Subject: [PATCH 7/8] changes --- src/moodle.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/moodle.rs b/src/moodle.rs index 48341e5..64b2378 100644 --- a/src/moodle.rs +++ b/src/moodle.rs @@ -175,17 +175,30 @@ mod tests { let answers3 = process_multiple_flags(flags3, " "); for answer in answers { - match answer.fraction{ + match answer.fraction { 33 => { - assert!(answer.text.contains("task4_prefix:") || answer.text.contains("task5_prefix:") || answer.text.contains("task6_prefix:")); + assert!( + answer.text.contains("task4_prefix:") + || answer.text.contains("task5_prefix:") + || answer.text.contains("task6_prefix:") + ); } 67 => { - assert!((answer.text.contains("task4_prefix:") && answer.text.contains("task5_prefix:")) - || (answer.text.contains("task6_prefix:") && answer.text.contains("task5_prefix:")) - || (answer.text.contains("task6_prefix:") && answer.text.contains("task4_prefix:"))); + assert!( + (answer.text.contains("task4_prefix:") + && answer.text.contains("task5_prefix:")) + || (answer.text.contains("task6_prefix:") + && answer.text.contains("task5_prefix:")) + || (answer.text.contains("task6_prefix:") + && answer.text.contains("task4_prefix:")) + ); } 100 => { - assert!((answer.text.contains("task4_prefix:") && answer.text.contains("task5_prefix:") && answer.text.contains("task6_prefix:"))); + assert!( + (answer.text.contains("task4_prefix:") + && answer.text.contains("task5_prefix:") + && answer.text.contains("task6_prefix:")) + ); } _ => { unreachable!("Unexpected fraction value encountered in test") @@ -197,7 +210,7 @@ mod tests { assert!(answer.text.contains("task1:")); } for answer in answers3 { - match answer.fraction{ + match answer.fraction { 50 => { assert!(answer.text.contains("task2:") || answer.text.contains("task3:")); } From c2589ab548fb582d4d5afa878e3b3a12d3631920 Mon Sep 17 00:00:00 2001 From: Eetu Lassi Date: Wed, 23 Oct 2024 15:31:55 +0300 Subject: [PATCH 8/8] more changes --- src/moodle.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/moodle.rs b/src/moodle.rs index 64b2378..b31405a 100644 --- a/src/moodle.rs +++ b/src/moodle.rs @@ -158,8 +158,10 @@ mod tests { let flag2 = Flag::new_random_flag(taskid2, 32); let flag3 = Flag::new_random_flag(taskid3, 32); let flag4 = Flag::new_user_flag(prefix4, &Algorithm::HMAC_SHA3_256, &secret, &taskid4, &id); - let flag5 = Flag::new_user_flag(prefix5, &Algorithm::HMAC_SHA3_256, &secret2, &taskid5, &id); - let flag6 = Flag::new_user_flag(prefix6, &Algorithm::HMAC_SHA3_256, &secret3, &taskid6, &id); + let flag5 = + Flag::new_user_flag(prefix5, &Algorithm::HMAC_SHA3_256, &secret2, &taskid5, &id); + let flag6 = + Flag::new_user_flag(prefix6, &Algorithm::HMAC_SHA3_256, &secret3, &taskid6, &id); flags2.push(flag1);