- 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).
80 lines
2.3 KiB
TypeScript
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 };
|
|
}
|
|
|