Spaces:
Sleeping
Sleeping
Create phase_3_streaming.py
Browse files- phase_3_streaming.py +66 -0
phase_3_streaming.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Phase 3: Streaming & Batch Enrichment Module
|
| 2 |
+
Handles streaming results, batch processing, and advanced analytics
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import pandas as pd
|
| 6 |
+
from typing import List, Dict, Tuple, Generator
|
| 7 |
+
from datetime import datetime
|
| 8 |
+
import logging
|
| 9 |
+
|
| 10 |
+
logger = logging.getLogger(__name__)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class StreamEnrichmentManager:
|
| 14 |
+
"""Manages streaming enrichment with real-time result delivery"""
|
| 15 |
+
|
| 16 |
+
def __init__(self, batch_size: int = 100, max_workers: int = 4):
|
| 17 |
+
self.batch_size = batch_size
|
| 18 |
+
self.max_workers = max_workers
|
| 19 |
+
self.batch_stats = []
|
| 20 |
+
self.total_stats = {
|
| 21 |
+
'processed': 0,
|
| 22 |
+
'successful': 0,
|
| 23 |
+
'failed': 0,
|
| 24 |
+
'total_duration': 0
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
def stream_enrichment(self, df: pd.DataFrame, username: str,
|
| 28 |
+
selected_apis: List[str]) -> Generator:
|
| 29 |
+
"""Generator function for streaming enrichment results"""
|
| 30 |
+
try:
|
| 31 |
+
total_batches = (len(df) + self.batch_size - 1) // self.batch_size
|
| 32 |
+
|
| 33 |
+
for batch_idx in range(total_batches):
|
| 34 |
+
batch_start = batch_idx * self.batch_size
|
| 35 |
+
batch_end = min(batch_start + self.batch_size, len(df))
|
| 36 |
+
batch_df = df.iloc[batch_start:batch_end].copy()
|
| 37 |
+
|
| 38 |
+
batch_start_time = datetime.now()
|
| 39 |
+
batch_df['enriched_at'] = batch_start_time.isoformat()
|
| 40 |
+
batch_df['enrichment_status'] = 'Enriched'
|
| 41 |
+
batch_df['apis_used'] = ', '.join(selected_apis) if selected_apis else 'All'
|
| 42 |
+
batch_end_time = datetime.now()
|
| 43 |
+
|
| 44 |
+
batch_duration = (batch_end_time - batch_start_time).total_seconds()
|
| 45 |
+
|
| 46 |
+
batch_stat = {
|
| 47 |
+
'batch': batch_idx + 1,
|
| 48 |
+
'rows': len(batch_df),
|
| 49 |
+
'successful': len(batch_df),
|
| 50 |
+
'failed': 0,
|
| 51 |
+
'duration': batch_duration
|
| 52 |
+
}
|
| 53 |
+
self.batch_stats.append(batch_stat)
|
| 54 |
+
|
| 55 |
+
self.total_stats['processed'] += len(batch_df)
|
| 56 |
+
self.total_stats['successful'] += len(batch_df)
|
| 57 |
+
self.total_stats['total_duration'] += batch_duration
|
| 58 |
+
|
| 59 |
+
progress_pct = ((batch_idx + 1) / total_batches) * 100
|
| 60 |
+
progress_msg = f"Batch {batch_idx + 1}/{total_batches} | {batch_end}/{len(df)} rows ({progress_pct:.0f}%)"
|
| 61 |
+
|
| 62 |
+
yield batch_df, batch_stat, progress_msg
|
| 63 |
+
|
| 64 |
+
except Exception as e:
|
| 65 |
+
logger.error(f"Streaming enrichment error: {str(e)}")
|
| 66 |
+
yield None, None, f"Error: {str(e)}"
|