diff --git a/src/server/domain/types.py b/src/server/domain/types.py index 8710da1..8683a09 100644 --- a/src/server/domain/types.py +++ b/src/server/domain/types.py @@ -164,15 +164,18 @@ class AssetPrice: def to_dict(self) -> Dict[str, Any]: """Convert to dictionary representation.""" + # Handle both datetime and date objects for timestamp + ts = self.timestamp + if isinstance(ts, datetime): + ts = ts.isoformat() + elif hasattr(ts, 'isoformat'): # date object + ts = ts.isoformat() + return { "ticker": self.ticker, "price": float(self.price) if self.price else None, "currency": self.currency, - "timestamp": ( - self.timestamp.isoformat() - if isinstance(self.timestamp, datetime) - else self.timestamp - ), + "timestamp": ts, "volume": float(self.volume) if self.volume else None, "open_price": float(self.open_price) if self.open_price else None, "high_price": float(self.high_price) if self.high_price else None, diff --git a/src/server/infrastructure/cache/redis_cache.py b/src/server/infrastructure/cache/redis_cache.py index 1e255cc..6f35fd3 100644 --- a/src/server/infrastructure/cache/redis_cache.py +++ b/src/server/infrastructure/cache/redis_cache.py @@ -36,12 +36,52 @@ async def get(self, key: str) -> Optional[Any]: async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool: try: - await self._cache.set(key, value, ttl=ttl or self._ttl_default) + # Serialize value for JSON storage (handles datetime objects) + serialized = self._serialize_value(value) + await self._cache.set(key, serialized, ttl=ttl or self._ttl_default) return True except Exception as e: logger.error(f"❌ Cache set error for {key}: {e}") return False + def _serialize_value(self, value: Any) -> Any: + """Serialize value for JSON storage, handling datetime objects.""" + # Pydantic model: use mode='json' to convert datetime to ISO strings + if hasattr(value, 'model_dump'): + return value.model_dump(mode='json') + + # Dataclass with to_dict method + if hasattr(value, 'to_dict'): + result = value.to_dict() + # Recursively handle nested datetime objects + return self._serialize_dict(result) + + # List of items + if isinstance(value, list): + return [self._serialize_value(item) for item in value] + + # Dict with potential datetime values + if isinstance(value, dict): + return self._serialize_dict(value) + + return value + + def _serialize_dict(self, data: dict) -> dict: + """Recursively serialize dict values, converting datetime to ISO strings.""" + from datetime import date, datetime + + result = {} + for k, v in data.items(): + if isinstance(v, (datetime, date)): + result[k] = v.isoformat() + elif isinstance(v, dict): + result[k] = self._serialize_dict(v) + elif isinstance(v, list): + result[k] = [self._serialize_value(item) for item in v] + else: + result[k] = v + return result + async def delete(self, key: str) -> bool: try: await self._cache.delete(key)