Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added multiple flags to moodle #20

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
81 changes: 80 additions & 1 deletion src/moodle.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
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},
Expand Down Expand Up @@ -63,7 +65,8 @@ pub fn create_exam(
),
]
} 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)
Expand All @@ -82,3 +85,79 @@ 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<Flag>, separator: &str) -> Vec<Answer> {
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<Flag> =
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 crate::flag_generator::Algorithm;
use uuid::Uuid;

#[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);
}
}
}