Last active
May 14, 2020 18:23
-
-
Save qryxip/d154944658df7d31e926993d675378a3 to your computer and use it in GitHub Desktop.
Scrape submitted code from AtCoder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //! This code is licensed under [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0). | |
| //! | |
| //! ```cargo | |
| //! [package] | |
| //! name = "atcoder-submissions" | |
| //! version = "0.0.0" | |
| //! authors = ["Ryo Yamashita <qryxip@gmail.com>"] | |
| //! edition = "2018" | |
| //! publish = false | |
| //! license = "CC0-1.0" | |
| //! description = "Scrape submitted code from AtCoder" | |
| //! | |
| //! [dependencies] | |
| //! anyhow = "1.0.30" | |
| //! chrono = "0.4.11" | |
| //! env_logger = "0.7.1" | |
| //! itertools = "0.9.0" | |
| //! log = "0.4.8" | |
| //! once_cell = "1.4.0" | |
| //! regex = "1.3.7" | |
| //! scraper = "0.12.0" | |
| //! structopt = "0.3.14" | |
| //! strum = { version = "0.18.0", features = ["derive"] } | |
| //! ureq = "1.0.0" | |
| //! url = "2.1.1" | |
| //! ``` | |
| use anyhow::{anyhow, ensure, Context as _}; | |
| use chrono::{DateTime, FixedOffset}; | |
| use env_logger::fmt::Color; | |
| use itertools::Itertools as _; | |
| use log::{info, Level, LevelFilter}; | |
| use once_cell::sync::Lazy; | |
| use regex::Regex; | |
| use scraper::{Html, Selector}; | |
| use structopt::clap::AppSettings; | |
| use structopt::StructOpt; | |
| use strum::{EnumString, EnumVariantNames, IntoStaticStr, VariantNames as _}; | |
| use ureq::Response; | |
| use url::Url; | |
| use std::fs; | |
| use std::io::Write as _; | |
| use std::path::{Path, PathBuf}; | |
| macro_rules! static_regex { | |
| ($s:literal $(,)?) => {{ | |
| static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new($s).unwrap()); | |
| &*REGEX | |
| }}; | |
| } | |
| macro_rules! static_selector { | |
| ($s:literal $(,)?) => {{ | |
| static SELECTOR: Lazy<Selector> = Lazy::new(|| Selector::parse($s).unwrap()); | |
| &*SELECTOR | |
| }}; | |
| } | |
| #[derive(StructOpt, Debug)] | |
| #[structopt(author, about, setting(AppSettings::DeriveDisplayOrder))] | |
| struct Opt { | |
| #[structopt(long, help("Scrapes only AC submissions"))] | |
| ac: bool, | |
| #[structopt( | |
| short, | |
| long, | |
| value_name("DIR"), | |
| default_value("./submissions"), | |
| help("Saves scraped code to the directory") | |
| )] | |
| out: PathBuf, | |
| #[structopt( | |
| short, | |
| long, | |
| value_name("LANG"), | |
| possible_values(Lang::VARIANTS), | |
| required(true), | |
| min_values(1), | |
| help("Languages") | |
| )] | |
| langs: Vec<Lang>, | |
| #[structopt( | |
| short, | |
| long, | |
| value_name("SCREEN_NAME"), | |
| required(true), | |
| min_values(1), | |
| help("Screen names of the contests (https://atcoder.jp/contests/{}/submissions)") | |
| )] | |
| contests: Vec<String>, | |
| } | |
| fn main() -> anyhow::Result<()> { | |
| let Opt { | |
| ac, | |
| out, | |
| langs, | |
| contests, | |
| } = Opt::from_args(); | |
| env_logger::builder() | |
| .format(|buf, record| { | |
| let mut style = buf.style(); | |
| let mut write_with_style = |color, bold, intense, value| -> _ { | |
| let value = style | |
| .set_color(color) | |
| .set_bold(bold) | |
| .set_intense(intense) | |
| .value(value); | |
| write!(buf, "{}", value) | |
| }; | |
| write_with_style(Color::Black, false, true, "[")?; | |
| match record.level() { | |
| Level::Info => write_with_style(Color::Cyan, true, false, "INFO"), | |
| Level::Warn => write_with_style(Color::Yellow, true, false, "WARN"), | |
| Level::Error => write_with_style(Color::Red, true, false, "ERROR"), | |
| _ => unreachable!(), | |
| }?; | |
| write_with_style(Color::Black, false, true, "]")?; | |
| writeln!(buf, " {}", record.args()) | |
| }) | |
| .filter_module("atcoder_submissions", LevelFilter::Info) | |
| .init(); | |
| get("/robots.txt", 404)?; | |
| for &lang in &langs { | |
| for contest in &contests { | |
| for (path, code) in retrieve_codes(contest, &out, lang, ac)? { | |
| if let Some(parent) = path.parent() { | |
| create_dir_all(parent)?; | |
| } | |
| write(&path, &code)?; | |
| } | |
| } | |
| } | |
| Ok(()) | |
| } | |
| fn retrieve_codes( | |
| contest: &str, | |
| out: &Path, | |
| lang: Lang, | |
| only_ac: bool, | |
| ) -> anyhow::Result<Vec<(PathBuf, String)>> { | |
| let path = |lang_id: u32| -> _ { | |
| let mut acc = format!( | |
| "/contests/{}/submissions?desc=true&orderBy=created&f.Language={}", | |
| contest, lang_id, | |
| ); | |
| if only_ac { | |
| acc += "&f.Status=AC"; | |
| } | |
| acc | |
| }; | |
| let mut acc = vec![]; | |
| let mut retrieve_codes = |summaries: &[Summary]| -> anyhow::Result<_> { | |
| for summary in summaries { | |
| let path = out | |
| .join(<&str>::from(lang)) | |
| .join(contest) | |
| .join(&summary.user) | |
| .join(summary.file_stem()) | |
| .with_extension(<&str>::from(lang)); | |
| let code = get_html(&summary.detail)?.extract_code()?; | |
| acc.push((path, code)) | |
| } | |
| Ok(()) | |
| }; | |
| for &lang_id in lang.ids() { | |
| let path = path(lang_id); | |
| let (summaries, num_pages) = get_html(&path)?.extract_summaries()?; | |
| retrieve_codes(&summaries)?; | |
| for i in 1..num_pages { | |
| let (summaries, _) = | |
| get_html(&format!("{}&page={}", path, i + 1))?.extract_summaries()?; | |
| retrieve_codes(&summaries)?; | |
| } | |
| } | |
| Ok(acc) | |
| } | |
| fn get(path: &str, expected: u16) -> anyhow::Result<Response> { | |
| static BASE: Lazy<Url> = Lazy::new(|| "https://atcoder.jp".parse().unwrap()); | |
| let url = BASE.join(path)?; | |
| info!("GET: {}", url); | |
| let res = ureq::get(url.as_ref()) | |
| .set( | |
| "User-Agent", | |
| "atcoder-submissions <https://gist.github.com/qryxip/d154944658df7d31e926993d675378a3>", | |
| ) | |
| .call(); | |
| if let Some(err) = res.synthetic_error() { | |
| let mut err = err as &dyn std::error::Error; | |
| let mut displays = vec![err.to_string()]; | |
| while let Some(source) = err.source() { | |
| displays.push(source.to_string()); | |
| err = source; | |
| } | |
| let mut displays = displays.into_iter().rev(); | |
| let cause = anyhow!("{}", displays.next().unwrap()); | |
| return Err(displays.fold(cause, |err, display| err.context(display))); | |
| } | |
| info!("{} {}", res.status(), res.status_text()); | |
| ensure!(res.status() == expected, "expected {}", expected); | |
| Ok(res) | |
| } | |
| fn get_html(path: &str) -> anyhow::Result<Html> { | |
| let text = get(path, 200)?.into_string()?; | |
| Ok(Html::parse_document(&text)) | |
| } | |
| trait HtmlExt { | |
| fn extract_summaries(&self) -> anyhow::Result<(Vec<Summary>, u32)>; | |
| fn extract_code(&self) -> anyhow::Result<String>; | |
| } | |
| impl HtmlExt for Html { | |
| fn extract_summaries(&self) -> anyhow::Result<(Vec<Summary>, u32)> { | |
| (|| { | |
| let summaries = self | |
| .select(static_selector!( | |
| "#main-container > div.row > div.col-sm-12 > div.panel-submission > \ | |
| div.table-responsive > table.table > tbody > tr", | |
| )) | |
| .map(|tr| { | |
| let submission_time = tr | |
| .select(static_selector!("td > time")) | |
| .exactly_one() | |
| .ok()? | |
| .text() | |
| .exactly_one() | |
| .ok()? | |
| .to_owned(); | |
| let submission_time = | |
| DateTime::parse_from_str(&submission_time, "%F %T%z").ok()?; | |
| let a = tr.select(static_selector!("td > a")).next()?; | |
| let task_display = a.text().exactly_one().ok()?.to_owned(); | |
| let task_path = a.value().attr("href")?.to_owned(); | |
| let user = tr | |
| .select(static_selector!("td")) | |
| .nth(2)? | |
| .text() | |
| .next()? | |
| .to_owned(); | |
| let language = tr | |
| .select(static_selector!("td")) | |
| .nth(3)? | |
| .text() | |
| .exactly_one() | |
| .ok()? | |
| .to_owned(); | |
| let score = tr | |
| .select(static_selector!("td")) | |
| .nth(4)? | |
| .text() | |
| .exactly_one() | |
| .ok()? | |
| .parse() | |
| .ok()?; | |
| let code_size = tr | |
| .select(static_selector!("td")) | |
| .nth(5)? | |
| .text() | |
| .exactly_one() | |
| .ok()?; | |
| let code_size = static_regex!(r"\A([0-9]+) Byte\z").captures(code_size)?[1] | |
| .parse() | |
| .ok()?; | |
| let status = tr | |
| .select(static_selector!("td > span")) | |
| .next()? | |
| .text() | |
| .exactly_one() | |
| .ok()? | |
| .to_owned(); | |
| let exec_time = tr | |
| .select(static_selector!("td")) | |
| .nth(7)? | |
| .text() | |
| .exactly_one() | |
| .ok()?; | |
| let exec_time_millis = static_regex!(r"\A([0-9]+) ms\z").captures(exec_time)? | |
| [1] | |
| .parse() | |
| .ok()?; | |
| let memory = tr | |
| .select(static_selector!("td")) | |
| .nth(8)? | |
| .text() | |
| .exactly_one() | |
| .ok()? | |
| .to_owned(); | |
| let detail = tr | |
| .select(static_selector!("td.text-center > a")) | |
| .flat_map(|a| { | |
| let text = a.text().next()?; | |
| if !["詳細", "Detail"].contains(&text) { | |
| return None; | |
| } | |
| a.value().attr("href").map(ToOwned::to_owned) | |
| }) | |
| .next()?; | |
| Some(Summary { | |
| submission_time, | |
| task_display, | |
| task_path, | |
| user, | |
| language, | |
| score, | |
| code_size, | |
| status, | |
| exec_time_millis, | |
| memory, | |
| detail, | |
| }) | |
| }) | |
| .collect::<Option<Vec<_>>>()?; | |
| let num_pages = self | |
| .select(static_selector!( | |
| "#main-container > div.row > div.col-sm-12 > div.text-center > ul > li > a", | |
| )) | |
| .flat_map(|r| r.text()) | |
| .flat_map(|t| t.parse::<u32>().ok()) | |
| .max() | |
| .unwrap_or(1); | |
| Some((summaries, num_pages)) | |
| })() | |
| .ok_or_else(|| anyhow!("Failed to scrape (note: try `--ac`)")) | |
| } | |
| fn extract_code(&self) -> anyhow::Result<String> { | |
| (|| { | |
| self.select(static_selector!("#submission-code")) | |
| .exactly_one() | |
| .ok()? | |
| .text() | |
| .exactly_one() | |
| .ok() | |
| .map(ToOwned::to_owned) | |
| })() | |
| .ok_or_else(|| anyhow!("Failed to scrape")) | |
| } | |
| } | |
| #[derive(Debug, Clone, Copy, EnumString, EnumVariantNames, IntoStaticStr)] | |
| #[strum(serialize_all = "lowercase")] | |
| enum Lang { | |
| Cpp, | |
| Bash, | |
| C, | |
| Cs, | |
| Clj, | |
| Lisp, | |
| D, | |
| F08, | |
| Go, | |
| Hs, | |
| Java, | |
| Js, | |
| Ml, | |
| Pas, | |
| Pl, | |
| Php, | |
| Py, | |
| Rb, | |
| Scala, | |
| Scm, | |
| Txt, | |
| Vb, | |
| Swift, | |
| Rs, | |
| Sed, | |
| Awk, | |
| Bf, | |
| Sml, | |
| Cr, | |
| Fs, | |
| Unl, | |
| Lua, | |
| Moon, | |
| Ceylon, | |
| Jl, | |
| Nim, | |
| Ts, | |
| P6, | |
| Kt, | |
| Cob, | |
| Bc, | |
| Dart, | |
| Dc, | |
| Erl, | |
| Ex, | |
| Forth, | |
| F18, // ??? | |
| Hx, | |
| Sh, // dash | |
| Raku, | |
| Pro, | |
| Rkt, | |
| } | |
| impl Lang { | |
| fn ids(self) -> &'static [u32] { | |
| // 2016: | |
| // ┌─────────────────────────────────┬──────┐ | |
| // │ Name │ ID │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ C++14 (GCC 5.4.1) │ 3003 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Bash (GNU bash v4.3.11) │ 3001 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ C (GCC 5.4.1) │ 3002 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ C (Clang 3.8.0) │ 3004 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ C++14 (Clang 3.8.0) │ 3005 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ C# (Mono 4.6.2.0) │ 3006 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Clojure (1.8.0) │ 3007 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Common Lisp (SBCL 1.1.14) │ 3008 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ D (DMD64 v2.070.1) │ 3009 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ D (LDC 0.17.0) │ 3010 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ D (GDC 4.9.4) │ 3011 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Fortran (gfortran v4.8.4) │ 3012 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Go (1.6) │ 3013 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Haskell (GHC 7.10.3) │ 3014 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Java7 (OpenJDK 1.7.0) │ 3015 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Java8 (OpenJDK 1.8.0) │ 3016 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ JavaScript (node.js v5.12) │ 3017 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ OCaml (4.02.3) │ 3018 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Pascal (FPC 2.6.2) │ 3019 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Perl (v5.18.2) │ 3020 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ PHP (5.6.30) │ 3021 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Python2 (2.7.6) │ 3022 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Python3 (3.4.3) │ 3023 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Ruby (2.3.3) │ 3024 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Scala (2.11.7) │ 3025 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Scheme (Gauche 0.9.3.3) │ 3026 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Text (cat) │ 3027 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Visual Basic (Mono 4.0.1) │ 3028 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ C++ (GCC 5.4.1) │ 3029 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ C++ (Clang 3.8.0) │ 3030 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Objective-C (GCC 5.3.0) │ 3501 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Objective-C (Clang3.8.0) │ 3502 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Swift (swift-2.2-RELEASE) │ 3503 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Rust (1.15.1) │ 3504 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Sed (GNU sed 4.2.2) │ 3505 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Awk (mawk 1.3.3) │ 3506 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Brainfuck (bf 20041219) │ 3507 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Standard ML (MLton 20100608) │ 3508 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ PyPy2 (5.6.0) │ 3509 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ PyPy3 (2.4.0) │ 3510 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Crystal (0.20.5) │ 3511 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ F# (Mono 4.0) │ 3512 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Unlambda (0.1.3) │ 3513 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Lua (5.3.2) │ 3514 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ LuaJIT (2.0.4) │ 3515 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ MoonScript (0.5.0) │ 3516 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Ceylon (1.2.1) │ 3517 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Julia (0.5.0) │ 3518 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Octave (4.0.2) │ 3519 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Nim (0.13.0) │ 3520 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ TypeScript (2.1.6) │ 3521 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Perl6 (rakudo-star 2016.01) │ 3522 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ Kotlin (1.0.0) │ 3523 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ PHP7 (7.0.15) │ 3524 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ COBOL - Fixed (OpenCOBOL 1.1.0) │ 3525 │ | |
| // ├─────────────────────────────────┼──────┤ | |
| // │ COBOL - Free (OpenCOBOL 1.1.0) │ 3526 │ | |
| // └─────────────────────────────────┴──────┘ | |
| // | |
| // 2020: | |
| // ┌──────────────────────────────────┬──────┐ | |
| // │ Name │ ID │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ C (GCC 9.2.1) │ 4001 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ C (Clang 10.0.0) │ 4002 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ C++ (GCC 9.2.1) │ 4003 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ C++ (Clang 10.0.0) │ 4004 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Java (OpenJDK 11.0.6) │ 4005 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Python (3.8.2) │ 4006 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Bash (5.0.11) │ 4007 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ bc (1.07.1) │ 4008 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Awk (GNU Awk 4.1.4) │ 4009 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ C# (.NET Core 3.1.201) │ 4010 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ C# (Mono-mcs 6.8.0.105) │ 4011 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ C# (Mono-csc 3.5.0) │ 4012 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Clojure (1.10.1.536) │ 4013 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Crystal (0.33.0) │ 4014 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ D (DMD 2.091.0) │ 4015 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ D (GDC 9.2.1) │ 4016 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ D (LDC 1.20.1) │ 4017 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Dart (2.7.2) │ 4018 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ dc (1.4.1) │ 4019 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Erlang (22.3) │ 4020 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Elixir (1.10.2) │ 4021 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ F# (.NET Core 3.1.201) │ 4022 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ F# (Mono 10.2.3) │ 4023 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Forth (gforth 0.7.3) │ 4024 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Fortran(GNU Fortran 9.2.1) │ 4025 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Go (1.14.1) │ 4026 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Haskell (GHC 8.8.3) │ 4027 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Haxe (4.0.3); js │ 4028 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Haxe (4.0.3); Java │ 4029 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ JavaScript (Node.js 12.16.1) │ 4030 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Julia (1.4.0) │ 4031 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Kotlin (1.3.71) │ 4032 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Lua (Lua 5.3.5) │ 4033 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Lua (LuaJIT 2.1.0) │ 4034 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Dash (0.5.8) │ 4035 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Nim (1.0.6) │ 4036 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Objective-C (Clang 10.0.0) │ 4037 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Common Lisp (SBCL 2.0.3) │ 4038 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ OCaml (4.10.0) │ 4039 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Octave (5.2.0) │ 4040 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Pascal (FPC 3.0.4) │ 4041 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Perl (5.26.1) │ 4042 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Raku (Rakudo 2020.02.1) │ 4043 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ PHP (7.4.4) │ 4044 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Prolog (SWI-Prolog 8.0.3) │ 4045 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ PyPy2 (7.3.0) │ 4046 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ PyPy3 (7.3.0) │ 4047 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Racket (7.6) │ 4048 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Ruby (2.7.1) │ 4049 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Rust (1.42.0) │ 4050 │ | |
| // ├──────────────────────────────────┼──────┤ // ← TODO | |
| // │ Scala (2.13.1) │ 4051 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Java (OpenJDK 1.8.0) │ 4052 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Scheme (Gauche 0.9.9) │ 4053 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Standard ML (MLton 20130715) │ 4054 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Swift (5.2.1) │ 4055 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Text (cat 8.28) │ 4056 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ TypeScript (3.8) │ 4057 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Visual Basic (.NET Core 3.1.101) │ 4058 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Zsh (5.4.2) │ 4059 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ COBOL - Fixed (OpenCOBOL 1.1.0) │ 4060 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ COBOL - Free (OpenCOBOL 1.1.0) │ 4061 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Brainfuck (bf 20041219) │ 4062 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Ada2012 (GNAT 9.2.1) │ 4063 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Unlambda (2.0.0) │ 4064 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Cython (0.29.16) │ 4065 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Sed (4.4) │ 4066 │ | |
| // ├──────────────────────────────────┼──────┤ | |
| // │ Vim (8.2.0460) │ 4067 │ | |
| // └──────────────────────────────────┴──────┘ | |
| match self { | |
| Self::Cpp => &[3003, 3005, 3029, 3030, 4003, 4004], | |
| Self::Bash => &[3001, 4007], | |
| Self::C => &[3002, 3004, 4001, 4002], | |
| Self::Cs => &[3006, 4007, 4008, 4009], | |
| Self::Clj => &[3007, 4013], | |
| Self::Lisp => &[3008, 4038], | |
| Self::D => &[3009, 3010, 3011, 4015, 4016, 4017], | |
| Self::F08 => &[3012], | |
| Self::Go => &[3013, 4026], | |
| Self::Hs => &[3014, 4027], | |
| Self::Java => &[3015, 3016, 4006], | |
| Self::Js => &[3017, 4030], | |
| Self::Ml => &[3018, 4039], | |
| Self::Pas => &[3019, 4041], | |
| Self::Pl => &[3020, 4042], | |
| Self::Php => &[3021, 4044], | |
| Self::Py => &[3022, 3023, 3509, 3510, 4006, 4046, 4047], | |
| Self::Rb => &[3024, 4049], | |
| Self::Scala => &[3025], | |
| Self::Scm => &[3026], | |
| Self::Txt => &[3027], | |
| Self::Vb => &[3028], | |
| Self::Swift => &[3503], | |
| Self::Rs => &[3504, 4050], | |
| Self::Sed => &[3505], | |
| Self::Awk => &[3506, 4009], | |
| Self::Bf => &[3507], | |
| Self::Sml => &[3508], | |
| Self::Cr => &[3511, 4014], | |
| Self::Fs => &[3512, 4022, 4023], | |
| Self::Unl => &[3513], | |
| Self::Lua => &[3514, 3515, 4033, 4034], | |
| Self::Moon => &[3516], | |
| Self::Ceylon => &[3517], | |
| Self::Jl => &[3518, 4031], | |
| Self::Nim => &[3520, 4036], | |
| Self::Ts => &[3512], | |
| Self::P6 => &[3522], | |
| Self::Kt => &[3523, 4032], | |
| Self::Cob => &[3525, 3526], | |
| Self::Bc => &[4008], | |
| Self::Dart => &[4018], | |
| Self::Dc => &[4019], | |
| Self::Erl => &[4020], | |
| Self::Ex => &[4021], | |
| Self::Forth => &[4024], | |
| Self::F18 => &[4025], | |
| Self::Hx => &[4028, 4029], | |
| Self::Sh => &[4035], | |
| Self::Raku => &[4043], | |
| Self::Pro => &[4045], | |
| Self::Rkt => &[4048], | |
| } | |
| } | |
| } | |
| #[derive(Debug)] | |
| struct Summary { | |
| submission_time: DateTime<FixedOffset>, | |
| task_display: String, | |
| task_path: String, | |
| user: String, | |
| language: String, | |
| score: u32, | |
| code_size: u32, | |
| status: String, | |
| exec_time_millis: u64, | |
| memory: String, | |
| detail: String, | |
| } | |
| impl Summary { | |
| fn file_stem(&self) -> impl AsRef<Path> { | |
| format!( | |
| "{}-{}Z-{}", | |
| self.task_display | |
| .split_whitespace() | |
| .next() | |
| .unwrap_or_default() | |
| .to_lowercase(), | |
| self.submission_time.naive_utc().format("%Y-%m-%d-%H-%M-%S"), | |
| self.status, | |
| ) | |
| } | |
| } | |
| fn create_dir_all(path: &Path) -> anyhow::Result<()> { | |
| fs::create_dir_all(path).with_context(|| format!("Failed to crate {}", path.display())) | |
| } | |
| fn write(path: &Path, contents: &str) -> anyhow::Result<()> { | |
| fs::write(path, contents).with_context(|| format!("Failed to write {}", path.display()))?; | |
| info!("Wrote {}", path.display()); | |
| Ok(()) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment