Skip to content

Instantly share code, notes, and snippets.

@tcyrus
Last active March 24, 2020 22:38
Show Gist options
  • Save tcyrus/8977aa4d1b4d613e637372cb17ba2384 to your computer and use it in GitHub Desktop.
Save tcyrus/8977aa4d1b4d613e637372cb17ba2384 to your computer and use it in GitHub Desktop.
Rewrite of Submitty's calculate_extensions.cpp in Rust
#[macro_use]
extern crate clap;
extern crate chrono;
use std::collections::BTreeMap;
use std::fs::{self, DirEntry, File};
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::process;
use chrono::Utc;
// https://github.com/Submitty/Submitty/blob/master/bin/calculate_extensions.cpp
fn parse_results_grade(istr: File, testcases: Vec<i32>, cutoff: i32) -> i32 {
let mut answer: i32 = 0;
//println!("parse_results_grade");
let f = BufReader::new(istr);
for line in f.lines() {
let ss = line.split_whitespace().peekable();
if ss.next().contains("Testcase") {
let num = ss.next().and_then(|s| s.parse::<i32>()).unwrap();
let found = testcases.contains(num);
if (!found) continue;
//println!("line: {}", line);
assert_eq!(ss.next(), Some(":"));
/*
use itertools::Itertools;
let value: i32 = ss.tuple_windows::<(_, _)>()
.find(|(_, token)| token.contains("/"))
.and_then(|(token, _)| token.parse()).unwrap();
*/
let mut value: i32 = 0;
while let Some(token) = ss.next() {
if ss.peek().contains("/") {
value = token.parse().unwrap();
break;
}
}
//println!("testcases {} VALUE {}", num, value);
answer += value;
}
}
/*
println!(match answer {
a if a < cutoff => "not",
_ => "QUALIFIES",
});
*/
answer
}
// ------------------------------------------------------
fn parse_timestamp(istr: File, y: i32, m: i32, d: i32) -> i64 {
//println!("parse_timestamp");
let mut year: i32 = 0;
let mut month: i32 = 0;
let mut day: i32 = 0;
let f = BufReader::new(istr);
for line in f.lines() {
//println!("line: {}", line);
let ss = line.split_whitespace();
year = iter.next().and_then(|x| x.parse()).unwrap();
assert_eq!(iter.next(), Some("-"));
month = iter.next().and_then(|x| x.parse()).unwrap();
assert_eq!(iter.next(), Some("-"));
day = iter.next().and_then(|x| x.parse()).unwrap();
}
let cutoff = Utc.ymd(y, m, d);
let timestamp = Utc.ymd(year, month, day);
let diff = (cutoff - timestamp).days();
//println!("dates {:?} {:?} {:?}", cutoff, timestamp, diff);
/*
println!(match diff {
d if d < 0 => "later",
_ => "AVAILABLE",
});
*/
diff
}
fn main() {
let app = clap_app!(myapp =>
(@arg PATH: +required)
(@arg YEAR: +required)
(@arg MONTH: +required)
(@arg DAY: +required)
(@arg CUTOFF: +required)
(@arg TESTCASE: ... +required)
)
let matches = app.get_matches();
let submission_path = Path::new(matches.value_of("PATH").unwrap());
if (!submission_path.exists() || !submission_path.is_dir()) {
eprintln!("ERROR with directory {:?}", submission_path);
app.print_help();
process::exit(0);
}
let GRADEABLE = submission_path.ancestors().nth(2).to_str();
let year = value_t!(matches, "YEAR", u32).unwrap_or(2014);
let month = value_t!(matches, "MONTH", u32).unwrap_or(0);
let day = value_t!(matches, "DAY", u32).unwrap_or(0);
if (year < 2014 || !(1..13).contains(&month) || !(1..32).contains(&day)) {
app.print_help();
process::exit(0);
}
println!("Calculating extension earned by this date: {} {} {}", year, month, day);
let cutoff = value_t!(matches, "CUTOFF", u32).unwrap_or(0);
assert!((1..100).contains(&cutoff));
println!("Cutoff is: {}", cutoff);
let testcases = values_t!(matches, "TESTCASE", i32).unwrap();
println!("Testcases: {:?}", testcases);
let mut data: BTreeMap<&str, BTreeMap<i32, (i32, i32)>> = BTreeMap::new();
// LOOP OVER THE USERNAMES
for dir_entry in submission_path.read_dir().unwrap() {
if let Ok(dir_entry) = dir_entry {
let user_path = dir_entry.path();
if (!user_path.is_dir()) {
//println!("NOT A DIRECTORY {:?}", user_path);
continue;
}
let username = user_path.file_name().unwrap();
//println!("username {}", username);
// LOOP OVER THE SUBMISSION VERSIONS
for usr_entry in user_path.read_dir().unwrap() {
if let Ok(usr_entry) = usr_entry {
let version_path = usr_entry.path();
let version = version_path.file_name();
//println!("version {}", version);
if (!version_path.is_dir()) {
//println!("NOT A DIRECTORY {:?}", version_path);
continue;
}
let timestamp_file = format!("{}/{}/{}/.submit.timestamp", submission_path.to_str(), username, version);
let results_path = submission_path.to_str().replacen("submissions", "results", 1);
let results_file = format!("{}/{}/{}/grade.txt", results_path, username, version);
//println!("timestamp_file {}", timestamp_file);
//println!("results_file {}", results_file);
let points: i32 = File::open(results_file).map(|res_istr| {
//println!("user {} version {}", username, version);
let points = parse_results_grade(res_istr, testcases, cutoff);
//println!("points = {}", points);
points
}).unwrap_or(0);
let time_istr = File::open(timestamp_file).expect("timestamp file error!");
let days_diff: i64 = parse_timestamp(time_istr, year, month, day);
//println!("days_diff = {}", days_diff);
let version_int: i32 = version.parse().unwrap();
assert!(version_int >= 1);
data.entry(username).or_insert_with(BTreeMap::new).insert(version_int, (days_diff, points));
//println!("thing {} {} {}", username, days_diff, points);
}
}
}
}
for (user, map2) in &data {
println!("user: {}", user);
let mut earned = false;
let mut attempt = false;
for (version, (days, points)) in &map2 {
println!(" version: {}, days={} points={}", version, days, points);
attempt |= (days >= 0);
earned |= (attempt && points >= cutoff);
}
if (earned) {
println!("LATE DAY EARNED FOR {}", user);
eprintln!("{},{},1", user, GRADEABLE);
} else if (attempt) {
println!("ATTEMPT by {}", user);
} else {
println!("ONLY ON TIME {}" user);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment