- **Common Contracts**: Updated DTOs and models to support workflow history; removed legacy analysis result DTOs.
- **Data Persistence Service**:
- Removed `analysis_results` table logic and API endpoints.
- Implemented `workflow_history` API and DB access (`history.rs`).
- Fixed compilation errors and updated tests.
- Exposed Postgres port in `docker-compose.yml` for easier debugging/offline checks.
- **API Gateway**:
- Implemented `history` endpoints (get history list, get by ID).
- Removed legacy `analysis-results` endpoints.
- Fixed routing and handler logic in `api.rs`.
- **Report Generator Service**:
- Removed dependency on legacy `analysis-results` persistence calls.
- Fixed compilation errors.
- **Workflow Orchestrator**: Fixed warnings and minor logic issues.
- **Providers**: Updated provider services (alphavantage, tushare, finnhub, yfinance, mock) to align with contract changes.
- **Frontend**:
- Updated `ReportPage` and stores to use new workflow history.
- Added `RecentReportsDropdown` component.
- Cleaned up `RealtimeLogs` component.
- **Documentation**: Moved completed design tasks to `completed/` and added refactoring context docs.
Confirmed all services pass `cargo check`.
74 lines
2.2 KiB
Rust
74 lines
2.2 KiB
Rust
use common_contracts::messages::TaskType;
|
|
|
|
pub struct IOBinder;
|
|
|
|
impl IOBinder {
|
|
pub fn new() -> Self {
|
|
Self
|
|
}
|
|
|
|
pub fn allocate_output_path(
|
|
&self,
|
|
task_type: TaskType,
|
|
symbol: &str,
|
|
task_id: &str,
|
|
display_name: Option<&str>,
|
|
) -> String {
|
|
// Convention based paths:
|
|
// DataFetch: raw/{provider_id}/{symbol}
|
|
// DataProcessing: processed/{processor_id}/{symbol}
|
|
// Analysis: analysis/{module_name_or_id}/{symbol}.md
|
|
|
|
let clean_task_id = task_id.split(':').last().unwrap_or(task_id);
|
|
|
|
match task_type {
|
|
TaskType::DataFetch => format!("raw/{}/{}", clean_task_id, symbol),
|
|
TaskType::DataProcessing => format!("processed/{}/{}", clean_task_id, symbol),
|
|
TaskType::Analysis => {
|
|
let folder_name = if let Some(name) = display_name {
|
|
self.sanitize_path_segment(name)
|
|
} else {
|
|
clean_task_id.to_string()
|
|
};
|
|
format!("analysis/{}/{}.md", folder_name, symbol)
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn allocate_trace_path(
|
|
&self,
|
|
task_type: TaskType,
|
|
symbol: &str,
|
|
task_id: &str,
|
|
display_name: Option<&str>,
|
|
) -> String {
|
|
let clean_task_id = task_id.split(':').last().unwrap_or(task_id);
|
|
match task_type {
|
|
TaskType::Analysis => {
|
|
let folder_name = if let Some(name) = display_name {
|
|
self.sanitize_path_segment(name)
|
|
} else {
|
|
clean_task_id.to_string()
|
|
};
|
|
format!("analysis/{}/{}_trace.md", folder_name, symbol)
|
|
},
|
|
_ => format!("debug/{}/{}_trace.md", clean_task_id, symbol),
|
|
}
|
|
}
|
|
|
|
fn sanitize_path_segment(&self, name: &str) -> String {
|
|
name.replace('/', "_")
|
|
.replace('\\', "_")
|
|
.replace(':', "_")
|
|
.replace('"', "_")
|
|
.replace('<', "_")
|
|
.replace('>', "_")
|
|
.replace('|', "_")
|
|
.replace('?', "_")
|
|
.replace('*', "_")
|
|
.trim()
|
|
.to_string()
|
|
}
|
|
}
|
|
|