Fundamental_Analysis/frontend/src/lib/financial-data-transformer.ts
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

80 lines
2.3 KiB
TypeScript

export interface TimeSeriesFinancialDto {
symbol: string;
metric_name: string;
period_date: string;
value: number;
source?: string;
}
export interface FinancialTableRow {
metric: string;
[year: string]: string | number | undefined;
}
export interface FinancialTableData {
headers: string[]; // Sorted years
rows: FinancialTableRow[];
}
/**
* Transforms a flat list of TimeSeriesFinancialDto into a pivoted table structure.
*
* Input:
* [
* { metric_name: "Revenue", period_date: "2023-12-31", value: 100, source: "tushare" },
* { metric_name: "Revenue", period_date: "2022-12-31", value: 90, source: "tushare" },
* ]
*
* Output:
* {
* headers: ["2023", "2022"],
* rows: [
* { metric: "Revenue", "2023": 100, "2022": 90 }
* ]
* }
*/
export function transformFinancialData(data: TimeSeriesFinancialDto[]): FinancialTableData {
if (!data || data.length === 0) {
return { headers: [], rows: [] };
}
// 1. Collect all unique years (from period_date)
const yearsSet = new Set<string>();
// 2. Group by metric name
const metricMap = new Map<string, Record<string, number | string>>();
data.forEach(item => {
if (!item.period_date) return;
// Extract year from "YYYY-MM-DD"
const year = item.period_date.substring(0, 4);
yearsSet.add(year);
if (!metricMap.has(item.metric_name)) {
metricMap.set(item.metric_name, { metric: item.metric_name });
}
const row = metricMap.get(item.metric_name)!;
// Handle potential conflicts:
// If multiple sources provide data, we currently just overwrite or keep the last one.
// A better approach might be to append source info or average, but for a table view, single value is cleaner.
// Maybe prioritize sources? Tushare > YFinance > AlphaVantage?
// For now, we just use the value.
// We could format it with source: "100 (Tushare)" but that breaks numeric sorting/formatting.
row[year] = item.value;
});
// Sort years descending (newest first)
const headers = Array.from(yearsSet).sort((a, b) => Number(b) - Number(a));
// Create rows
const rows = Array.from(metricMap.values()).map(row => {
// Ensure all header keys exist (fill with undefined/null if needed)
return row as FinancialTableRow;
});
return { headers, rows };
}