diff --git a/app/api/v1/endpoints/error_logs.py b/app/api/v1/endpoints/error_logs.py index b6a53a6..b6fd5f9 100644 --- a/app/api/v1/endpoints/error_logs.py +++ b/app/api/v1/endpoints/error_logs.py @@ -266,12 +266,7 @@ async def get_error_stats( # Get hourly trend for last 24 hours if within range hourly_trend = {} if (end_date - start_date).days <= 1: - # Dialect-aware date formatting - dialect_name = db.bind.dialect.name if db.bind else "sqlite" - if dialect_name == "postgresql": - hour_expr = func.to_char(ErrorLog.created_at, 'YYYY-MM-DD HH24:00').label('hour') - else: - hour_expr = func.strftime('%Y-%m-%d %H:00', ErrorLog.created_at).label('hour') + hour_expr = func.to_char(ErrorLog.created_at, 'YYYY-MM-DD HH24:00').label('hour') hourly_result = await db.execute( select( hour_expr, diff --git a/app/api/v1/endpoints/request_logs.py b/app/api/v1/endpoints/request_logs.py index 56a9b10..7cd878b 100644 --- a/app/api/v1/endpoints/request_logs.py +++ b/app/api/v1/endpoints/request_logs.py @@ -262,10 +262,9 @@ async def get_request_stats( # Get hourly trend for last 24 hours if within range hourly_trend = {} if (end_date - start_date).days <= 1: - # SQLite specific date formatting hourly_result = await db.execute( select( - func.strftime('%Y-%m-%d %H:00', RequestLog.created_at).label('hour'), + func.to_char(RequestLog.created_at, 'YYYY-MM-DD HH24:00').label('hour'), func.count().label('count') ).where(date_filter) .group_by('hour') diff --git a/app/core/config.py b/app/core/config.py index a94f552..159704d 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -37,8 +37,8 @@ class Settings(BaseSettings): # Database DATABASE_URL: str = os.getenv( - "DATABASE_URL", - "sqlite+aiosqlite:///./stock_oracle.db" + "DATABASE_URL", + "postgresql+asyncpg://stockoracle:stockoracle2024@localhost:15433/stock_oracle" ) DATABASE_ECHO: bool = False diff --git a/app/core/database.py b/app/core/database.py index 3949d56..0217c78 100644 --- a/app/core/database.py +++ b/app/core/database.py @@ -4,35 +4,24 @@ Database configuration and session management from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import sessionmaker, declarative_base -from sqlalchemy.pool import NullPool -from app.core.config import settings -# Conditional engine settings based on database type -_is_sqlite = settings.DATABASE_URL.startswith("sqlite") +from app.core.config import settings -if _is_sqlite: - engine = create_async_engine( - settings.DATABASE_URL, - echo=settings.DATABASE_ECHO, - future=True, - poolclass=NullPool, - ) -else: - engine = create_async_engine( - settings.DATABASE_URL, - echo=settings.DATABASE_ECHO, - future=True, - pool_size=10, - max_overflow=20, - pool_timeout=30, - pool_pre_ping=True, - pool_recycle=3600, - connect_args={ - "server_settings": { - "jit": "off" - } +engine = create_async_engine( + settings.DATABASE_URL, + echo=settings.DATABASE_ECHO, + future=True, + pool_size=10, + max_overflow=20, + pool_timeout=30, + pool_pre_ping=True, + pool_recycle=3600, + connect_args={ + "server_settings": { + "jit": "off" } - ) + } +) # Create async session factory AsyncSessionLocal = sessionmaker( diff --git a/app/services/overlay/feature_builder.py b/app/services/overlay/feature_builder.py index 65c7f71..dfef90b 100644 --- a/app/services/overlay/feature_builder.py +++ b/app/services/overlay/feature_builder.py @@ -29,14 +29,6 @@ def winsorize(value: float, lower: float = WINSOR_LOWER, upper: float = WINSOR_U return max(lower, min(upper, value)) -def _ensure_utc(dt) -> datetime: - """Ensure a datetime is UTC-aware (SQLite returns naive datetimes).""" - if dt is None: - return dt - if isinstance(dt, datetime) and dt.tzinfo is None: - return dt.replace(tzinfo=timezone.utc) - return dt - def compute_zscore(value: float, values: List[float]) -> Optional[float]: """Compute z-score of *value* within *values* (requires ≥2 data points).""" @@ -95,8 +87,8 @@ class FeatureBuilder: # Filter by symbol sym_rows_hist = [r for r in all_rows if symbol in (r.matched_symbols or [])] - sym_rows_24h = [r for r in sym_rows_hist if _ensure_utc(r.published_at) >= cutoff_24h] - sym_rows_6h = [r for r in sym_rows_24h if _ensure_utc(r.published_at) >= cutoff_6h] + sym_rows_24h = [r for r in sym_rows_hist if r.published_at >= cutoff_24h] + sym_rows_6h = [r for r in sym_rows_24h if r.published_at >= cutoff_6h] headline_count_24h = len(sym_rows_24h) headline_count_6h = len(sym_rows_6h) @@ -144,7 +136,7 @@ class FeatureBuilder: ) all_rows = result.fetchall() sym_rows = [r for r in all_rows if symbol in (r.matched_symbols or [])] - sym_rows_24h = [r for r in sym_rows if _ensure_utc(r.published_at) >= cutoff_24h] + sym_rows_24h = [r for r in sym_rows if r.published_at >= cutoff_24h] mentions_24h = len(sym_rows_24h) weighted_views_24h = sum(r.view_count * r.channel_weight for r in sym_rows_24h) @@ -195,7 +187,7 @@ class FeatureBuilder: views_1d = rows[0].views if rows else None # 7-day average - rows_7d = [r for r in rows if _ensure_utc(r.date) >= cutoff_7d] + rows_7d = [r for r in rows if r.date >= cutoff_7d] views_7d_avg = sum(r.views for r in rows_7d) / len(rows_7d) if rows_7d else None # Historical z-score diff --git a/app/services/overlay/overlay_pipeline.py b/app/services/overlay/overlay_pipeline.py index 6c9634f..bb5bcaa 100644 --- a/app/services/overlay/overlay_pipeline.py +++ b/app/services/overlay/overlay_pipeline.py @@ -181,11 +181,7 @@ class OverlayPipeline: """Return True if the record is missing or older than FEATURE_STALE_HOURS.""" if record is None: return True - # SQLite returns naive datetimes; ensure UTC-aware for comparison - as_of = record.as_of_ts - if as_of.tzinfo is None: - as_of = as_of.replace(tzinfo=timezone.utc) - age = datetime.now(timezone.utc) - as_of + age = datetime.now(timezone.utc) - record.as_of_ts return age.total_seconds() > FEATURE_STALE_HOURS * 3600 # ------------------------------------------------------------------ diff --git a/requirements-api.txt b/requirements-api.txt index 4127bd1..bad0073 100644 --- a/requirements-api.txt +++ b/requirements-api.txt @@ -3,7 +3,6 @@ uvicorn[standard]>=0.24.0 sqlalchemy>=2.0.0,<3.0.0 alembic>=1.12.0 asyncpg>=0.29.0 -aiosqlite>=0.19.0 greenlet>=2.0.0 pydantic>=2.0.0,<3.0.0 pydantic-settings>=2.0.0 diff --git a/requirements-test.txt b/requirements-test.txt index aa2c840..a1c01bf 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -4,5 +4,4 @@ pytest-asyncio>=0.21.0 pytest-cov>=4.1.0 httpx>=0.25.0 faker>=20.0.0 -pydantic-settings>=2.0.0 -aiosqlite>=0.19.0 \ No newline at end of file +pydantic-settings>=2.0.0 \ No newline at end of file