diff --git a/README.md b/README.md index e4ab625..3a8c7ef 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,9 @@ giant-squid l ### List ASVO jobs in JSON +the following commands are equivalent: + ```bash -# Any of the following work. giant-squid list --json giant-squid list -j giant-squid l -j @@ -114,6 +115,30 @@ In [3]: q.keys() Out[3]: dict_keys(['216087', '216241', '217628']) ``` +### Filter ASVO job listing + +`giant-squid list` takes an optional list of identifiers that can be used to filter the job listing, +these identifiers can either be a list of jobIDs or a list of obsIDs, but not both. + +Additionally, the `--states` and `--types` options can be used to further filter the output. + +These both taks a comma-separated, case-insensitive list of values from the `jobType` and +`jobState` lists above. These can be provided in `TitleCase`, `UPPERCASE`, `lowercase`, +`kebab-case`, `snake_case`, or even `SPoNgeBOb-CAse` + +example: show only jobs that match both of the following conditions: + +- obsid is `1234567890` or `1234567891` +- jobType is `DownloadVisibilities`, `DownloadMetadata` or `CancelJob` +- jobState is `Processing` or `Queued` + +```bash +giant-squid list \ + --types dOwNlOaD__vIsIbIlItIeS,download-metadata,CANCELJOB \ + --states pRoCeSsInG,__Q_u_e_u_e_D__ \ + 1234567890 1234567891 +``` + ### Download ASVO jobs To download job ID 12345: diff --git a/src/asvo/mod.rs b/src/asvo/mod.rs index 22d7e1a..12b0ee4 100644 --- a/src/asvo/mod.rs +++ b/src/asvo/mod.rs @@ -190,9 +190,6 @@ impl AsvoClient { Delivery::Acacia => { match f.url.as_deref() { Some(url) => { - debug!("Downloading file {:?}", f.url); - let url = url; - debug!("Downloading file {:?}", &url); // parse out path from url @@ -205,14 +202,14 @@ impl AsvoClient { if keep_tar { // Simply dump the response to the appropriate file name. Use a // buffer to avoid doing frequent writes. - + let mut out_file = File::create(out_path)?; let mut file_buf = BufReader::with_capacity(buffer_size, tee.by_ref()); - + loop { let buffer = file_buf.fill_buf()?; out_file.write_all(buffer)?; - + let length = buffer.len(); file_buf.consume(length); if length == 0 { @@ -225,7 +222,7 @@ impl AsvoClient { let mut tar = Archive::new(&mut tee); tar.unpack(".")?; } - + // If we were told to hash the download, compare our hash against // the upstream hash. Stream untarring may not read all of the // bytes; read the tee to the end. diff --git a/src/asvo/types.rs b/src/asvo/types.rs index b57ce2e..fd25c1d 100644 --- a/src/asvo/types.rs +++ b/src/asvo/types.rs @@ -12,6 +12,15 @@ use serde::Serialize; use crate::{obsid::Obsid, AsvoError}; +/// Sanitize a string to lowercase, and ascii 'a'-'z' only. +/// +/// Used to sanitize user input for ASVO identifiers. +fn _sanitize_identifier(s: &str) -> String { + let mut sanitized = s.to_lowercase(); + sanitized.retain(|c| 'a' <= c && c <= 'z'); + sanitized +} + /// All of the available types of ASVO jobs. #[derive(Serialize, PartialEq, Debug, Clone)] pub enum AsvoJobType { @@ -26,12 +35,12 @@ impl FromStr for AsvoJobType { type Err = AsvoError; fn from_str(s: &str) -> Result { - match s { + match _sanitize_identifier(s).as_str() { "conversion" => Ok(AsvoJobType::Conversion), - "download_visibilities" => Ok(AsvoJobType::DownloadVisibilities), - "download_metadata" => Ok(AsvoJobType::DownloadMetadata), - "download_voltage" => Ok(AsvoJobType::DownloadVoltage), - "cancel_job" => Ok(AsvoJobType::CancelJob), + "downloadvisibilities" => Ok(AsvoJobType::DownloadVisibilities), + "downloadmetadata" => Ok(AsvoJobType::DownloadMetadata), + "downloadvoltage" => Ok(AsvoJobType::DownloadVoltage), + "canceljob" => Ok(AsvoJobType::CancelJob), _ => Err(AsvoError::InvalidJobType { str: s.to_string() }), } } @@ -52,7 +61,7 @@ impl FromStr for AsvoJobState { type Err = AsvoError; fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { + match _sanitize_identifier(s).as_str() { "queued" => Ok(AsvoJobState::Queued), "processing" => Ok(AsvoJobState::Processing), "ready" => Ok(AsvoJobState::Ready), @@ -293,3 +302,22 @@ impl std::fmt::Display for Delivery { ) } } + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_asvo_job_state_fromstr() { + assert!(matches!(AsvoJobState::from_str("__Q_u_e_u_e_D__"), Ok(AsvoJobState::Queued))); + assert!(matches!(AsvoJobState::from_str("invalid job state"), Err(AsvoError::InvalidJobState{..}))); + } + + #[test] + fn test_asvo_job_type_fromstr() { + assert!(matches!(AsvoJobType::from_str("DownloadVisibilities"), Ok(AsvoJobType::DownloadVisibilities))); + assert!(matches!(AsvoJobType::from_str("download_visibilities"), Ok(AsvoJobType::DownloadVisibilities))); + assert!(matches!(AsvoJobType::from_str("invalid job type"), Err(AsvoError::InvalidJobType{..}))); + } +} \ No newline at end of file diff --git a/src/bin/giant-squid.rs b/src/bin/giant-squid.rs index 08cae6d..c806a3a 100644 --- a/src/bin/giant-squid.rs +++ b/src/bin/giant-squid.rs @@ -50,13 +50,13 @@ enum Args { /// show only jobs matching the provided states, case insensitive. /// Options: queued, processing, ready, error, expired, cancelled. - #[clap(long, name = "STATE")] + #[clap(long, name = "STATE", value_delimiter = ',')] states: Vec, /// filter job list by type, case insensitive with underscores. Options: /// conversion, download_visibilities, download_metadata, /// download_voltage or cancel_job - #[clap(long, name = "TYPE")] + #[clap(long, name = "TYPE", value_delimiter = ',')] types: Vec, /// job IDs or obsids to filter by. Files containing job IDs or