Fundamental_Analysis/services/report-generator-service/src/formatter.rs
Lv, Qi 0cb31e363e Refactor E2E tests and improve error handling in Orchestrator
- Fix `simple_test_analysis` template in E2E test setup to align with Orchestrator's data fetch logic.
- Implement and verify additional E2E scenarios:
    - Scenario C: Partial Provider Failure (verified error propagation fix in Orchestrator).
    - Scenario D: Invalid Symbol input.
    - Scenario E: Analysis Module failure.
- Update `WorkflowStateMachine::handle_report_failed` to correctly scope error broadcasting to the specific task instead of failing effectively silently or broadly.
- Update testing strategy documentation to reflect completed Phase 4 testing.
- Skip Scenario B (Orchestrator Restart) as persistence is not yet implemented (decision made to defer persistence).
2025-11-21 20:44:32 +08:00

73 lines
2.1 KiB
Rust

use std::collections::{BTreeMap, HashSet};
use common_contracts::dtos::TimeSeriesFinancialDto;
use chrono::Datelike;
/// Formats a list of TimeSeriesFinancialDto into a Markdown table.
/// The table columns are years (sorted descending), and rows are metrics.
pub fn format_financials_to_markdown(financials: &[TimeSeriesFinancialDto]) -> String {
if financials.is_empty() {
return "No financial data available.".to_string();
}
// 1. Group by Year and Metric
// Map<MetricName, Map<Year, Value>>
let mut data_map: BTreeMap<String, BTreeMap<i32, f64>> = BTreeMap::new();
let mut years_set: HashSet<i32> = HashSet::new();
for item in financials {
let year = item.period_date.year();
years_set.insert(year);
data_map
.entry(item.metric_name.clone())
.or_default()
.insert(year, item.value);
}
// 2. Sort years descending (recent first)
let mut sorted_years: Vec<i32> = years_set.into_iter().collect();
sorted_years.sort_by(|a, b| b.cmp(a));
// Limit to recent 5 years to keep table readable
let display_years = if sorted_years.len() > 5 {
&sorted_years[..5]
} else {
&sorted_years
};
// 3. Build Markdown Table
let mut markdown = String::new();
// Header
markdown.push_str("| Metric |");
for year in display_years {
markdown.push_str(&format!(" {} |", year));
}
markdown.push('\n');
// Separator
markdown.push_str("| :--- |");
for _ in display_years {
markdown.push_str(" ---: |");
}
markdown.push('\n');
// Rows
for (metric, year_values) in data_map {
markdown.push_str(&format!("| {} |", metric));
for year in display_years {
if let Some(value) = year_values.get(year) {
// Format large numbers or percentages intelligently if needed.
// For now, simple float formatting.
markdown.push_str(&format!(" {:.2} |", value));
} else {
markdown.push_str(" - |");
}
}
markdown.push('\n');
}
markdown
}