From b8fa15f62ecab0c58a03de605b0732b18966f276 Mon Sep 17 00:00:00 2001 From: Jonah Lefkoff Date: Tue, 17 Dec 2024 17:57:30 -0700 Subject: [PATCH 1/2] add alias reference --- Cargo.lock | 1 + vzdv-site/Cargo.toml | 1 + vzdv-site/src/endpoints/facility.rs | 73 +++++++++++++++ vzdv-site/templates/_layout.jinja | 1 + vzdv-site/templates/facility/aliasref.jinja | 99 +++++++++++++++++++++ 5 files changed, 175 insertions(+) create mode 100644 vzdv-site/templates/facility/aliasref.jinja diff --git a/Cargo.lock b/Cargo.lock index 1d69ff1..3812cd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3999,6 +3999,7 @@ dependencies = [ "chrono-tz", "clap", "geo", + "indexmap", "itertools 0.13.0", "lettre", "log", diff --git a/vzdv-site/Cargo.toml b/vzdv-site/Cargo.toml index 407fa0f..40b87c8 100644 --- a/vzdv-site/Cargo.toml +++ b/vzdv-site/Cargo.toml @@ -14,6 +14,7 @@ publish = false [dependencies] vzdv = { path = "../vzdv" } +indexmap = "2.0" anyhow = "1.0.86" axum = { version = "0.7.4", features = ["multipart"] } axum-extra = "0.9.3" diff --git a/vzdv-site/src/endpoints/facility.rs b/vzdv-site/src/endpoints/facility.rs index b0e6ea8..6748d23 100644 --- a/vzdv-site/src/endpoints/facility.rs +++ b/vzdv-site/src/endpoints/facility.rs @@ -21,6 +21,7 @@ use std::{ sync::Arc, }; use tower_sessions::Session; +use indexmap::IndexMap; use vzdv::{ config::Config, determine_staff_positions, @@ -397,6 +398,77 @@ async fn page_resources( Ok(Html(rendered)) } + +pub async fn fetch_and_parse_alias_file() -> Result)>)>, reqwest::Error> { + let url = "https://data-api.vnas.vatsim.net/Files/Aliases/ZDV.txt"; + let response = reqwest::get(url).await?.text().await?; + + let mut parsed_data: IndexMap>> = IndexMap::new(); + let mut current_h1 = String::new(); + let mut current_h2 = String::new(); + + for line in response.lines() { + if line.starts_with(";;;;") { + // New Heading 1 + current_h1 = line[4..].trim().to_string(); + parsed_data.entry(current_h1.clone()).or_default(); + current_h2 = String::new(); // Reset H2 + } else if line.starts_with(";;;") { + // New Heading 2 + current_h2 = line[3..].trim().to_string(); + parsed_data + .entry(current_h1.clone()) + .or_default() + .entry(current_h2.clone()) + .or_default(); + } else if line.starts_with('.') { + // Command under current H1 or H2 + if !current_h1.is_empty() { + if !current_h2.is_empty() { + parsed_data + .entry(current_h1.clone()) + .or_default() + .entry(current_h2.clone()) + .or_default() + .push(line.trim().to_string()); + } else { + // Command directly under H1 + parsed_data + .entry(current_h1.clone()) + .or_default() + .entry("__root__".to_string()) + .or_default() + .push(line.trim().to_string()); + } + } + } + } + + // Convert IndexMap to Vec for Jinja compatibility + let parsed_vec: Vec<(String, Vec<(String, Vec)>)> = parsed_data + .into_iter() + .map(|(h1, h2_map)| { + let h2_vec = h2_map + .into_iter() + .map(|(h2, commands)| (h2, commands)) + .collect(); + (h1, h2_vec) + }) + .collect(); + + Ok(parsed_vec) +} + + +/// View Alias commands for the facility. (Polled from the vNAS API) +async fn alias_ref( + State(state): State>) -> Result, AppError> { + let template = state.templates.get_template("facility/aliasref.jinja")?; + let alias_ref = fetch_and_parse_alias_file().await?; + let rendered = template.render(context! { alias_ref })?; + Ok(Html(rendered)) +} + /// Check visitor requirements and submit an application. async fn page_visitor_application( State(state): State>, @@ -583,6 +655,7 @@ pub fn router() -> Router> { .route("/facility/staff", get(page_staff)) .route("/facility/activity", get(page_activity)) .route("/facility/resources", get(page_resources)) + .route("/facility/aliasref", get(alias_ref)) .route( "/facility/visitor_application", get(page_visitor_application), diff --git a/vzdv-site/templates/_layout.jinja b/vzdv-site/templates/_layout.jinja index 720e5d6..6d88d2d 100644 --- a/vzdv-site/templates/_layout.jinja +++ b/vzdv-site/templates/_layout.jinja @@ -81,6 +81,7 @@
  • Roster
  • Activity
  • Resources
  • +
  • Alias Reference
  • Visitor Application
  • diff --git a/vzdv-site/templates/facility/aliasref.jinja b/vzdv-site/templates/facility/aliasref.jinja new file mode 100644 index 0000000..4c28ff3 --- /dev/null +++ b/vzdv-site/templates/facility/aliasref.jinja @@ -0,0 +1,99 @@ +{% extends "_layout.jinja" %} + +{% block title %}Alias Reference | {{ super() }}{% endblock %} + +{% block body %} + +

    Alias Reference

    + + +
    + {% for h1, h2_list in alias_ref %} + + + {{ h1 }} + +
    + {% set parent_index = loop.index %} + {% for h2, commands in h2_list %} + {% if h2 == "__root__" %} + + {% for command in commands %} +
    + {{ command }} +
    + {% endfor %} + {% else %} + + + {{ h2 }} + +
    + {% for command in commands %} + +
    + {{ command }} +
    + {% endfor %} +
    + {% endif %} + {% endfor %} +
    + {% endfor %} +
    + + + +{% endblock %} From 90db8b0187ab793cbe186f5f5011c2719b33c61c Mon Sep 17 00:00:00 2001 From: Jonah Lefkoff Date: Wed, 18 Dec 2024 13:12:54 -0700 Subject: [PATCH 2/2] fix clippy and rustfmt errors --- vzdv-site/src/endpoints/facility.rs | 128 ++++++++++++++-------------- 1 file changed, 62 insertions(+), 66 deletions(-) diff --git a/vzdv-site/src/endpoints/facility.rs b/vzdv-site/src/endpoints/facility.rs index 6748d23..a16610f 100644 --- a/vzdv-site/src/endpoints/facility.rs +++ b/vzdv-site/src/endpoints/facility.rs @@ -11,6 +11,7 @@ use axum::{ Form, Router, }; use chrono::{DateTime, Months, Utc}; +use indexmap::IndexMap; use itertools::Itertools; use log::{error, warn}; use minijinja::context; @@ -21,7 +22,6 @@ use std::{ sync::Arc, }; use tower_sessions::Session; -use indexmap::IndexMap; use vzdv::{ config::Config, determine_staff_positions, @@ -39,6 +39,8 @@ struct StaffPosition { description: &'static str, } +type ParsedAlias = Vec<(String, Vec<(String, Vec)>)>; + fn generate_staff_outline(config: &Config) -> HashMap<&'static str, StaffPosition> { let email_domain = &config.staff.email_domain; HashMap::from([ @@ -398,75 +400,69 @@ async fn page_resources( Ok(Html(rendered)) } +pub async fn fetch_and_parse_alias_file() -> Result { + let url = "https://data-api.vnas.vatsim.net/Files/Aliases/ZDV.txt"; + let response = reqwest::get(url).await?.text().await?; + + let mut parsed_data: IndexMap>> = IndexMap::new(); + let mut current_h1 = String::new(); + let mut current_h2 = String::new(); + + for line in response.lines() { + if line.starts_with(";;;;") { + // New Heading 1 + current_h1 = line.strip_prefix(";;;;").unwrap_or(line).trim().to_string(); + parsed_data.entry(current_h1.clone()).or_default(); + current_h2 = String::new(); // Reset H2 + } else if line.starts_with(";;;") { + // New Heading 2 + current_h2 = line.strip_prefix(";;;").unwrap_or(line).trim().to_string(); + parsed_data + .entry(current_h1.clone()) + .or_default() + .entry(current_h2.clone()) + .or_default(); + } else if line.starts_with('.') { + // Command under current H1 or H2 + if !current_h1.is_empty() { + if !current_h2.is_empty() { + parsed_data + .entry(current_h1.clone()) + .or_default() + .entry(current_h2.clone()) + .or_default() + .push(line.trim().to_string()); + } else { + // Command directly under H1 + parsed_data + .entry(current_h1.clone()) + .or_default() + .entry("__root__".to_string()) + .or_default() + .push(line.trim().to_string()); + } + } + } + } + + // Convert IndexMap to Vec for Jinja compatibility + let parsed_vec: ParsedAlias = parsed_data + .into_iter() + .map(|(h1, h2_map)| { + let h2_vec = h2_map.into_iter().collect(); + (h1, h2_vec) + }) + .collect(); -pub async fn fetch_and_parse_alias_file() -> Result)>)>, reqwest::Error> { - let url = "https://data-api.vnas.vatsim.net/Files/Aliases/ZDV.txt"; - let response = reqwest::get(url).await?.text().await?; - - let mut parsed_data: IndexMap>> = IndexMap::new(); - let mut current_h1 = String::new(); - let mut current_h2 = String::new(); - - for line in response.lines() { - if line.starts_with(";;;;") { - // New Heading 1 - current_h1 = line[4..].trim().to_string(); - parsed_data.entry(current_h1.clone()).or_default(); - current_h2 = String::new(); // Reset H2 - } else if line.starts_with(";;;") { - // New Heading 2 - current_h2 = line[3..].trim().to_string(); - parsed_data - .entry(current_h1.clone()) - .or_default() - .entry(current_h2.clone()) - .or_default(); - } else if line.starts_with('.') { - // Command under current H1 or H2 - if !current_h1.is_empty() { - if !current_h2.is_empty() { - parsed_data - .entry(current_h1.clone()) - .or_default() - .entry(current_h2.clone()) - .or_default() - .push(line.trim().to_string()); - } else { - // Command directly under H1 - parsed_data - .entry(current_h1.clone()) - .or_default() - .entry("__root__".to_string()) - .or_default() - .push(line.trim().to_string()); - } - } - } - } - - // Convert IndexMap to Vec for Jinja compatibility - let parsed_vec: Vec<(String, Vec<(String, Vec)>)> = parsed_data - .into_iter() - .map(|(h1, h2_map)| { - let h2_vec = h2_map - .into_iter() - .map(|(h2, commands)| (h2, commands)) - .collect(); - (h1, h2_vec) - }) - .collect(); - - Ok(parsed_vec) + Ok(parsed_vec) } - /// View Alias commands for the facility. (Polled from the vNAS API) -async fn alias_ref( - State(state): State>) -> Result, AppError> { - let template = state.templates.get_template("facility/aliasref.jinja")?; - let alias_ref = fetch_and_parse_alias_file().await?; - let rendered = template.render(context! { alias_ref })?; - Ok(Html(rendered)) +async fn alias_ref(State(state): State>) -> Result, AppError> { + let template = state.templates.get_template("facility/aliasref.jinja")?; + let alias_ref = fetch_and_parse_alias_file().await?; + let rendered = template.render(context! { alias_ref })?; + Ok(Html(rendered)) } /// Check visitor requirements and submit an application.