- Delete routes: counter.py, sum.py - Delete jobs.py and worker.py - Delete tests: test_counter.py, test_jobs.py - Update audit.py: keep only price-history endpoints - Update models.py: remove VIEW_COUNTER, INCREMENT_COUNTER, USE_SUM permissions - Update models.py: remove Counter, SumRecord, CounterRecord, RandomNumberOutcome models - Update schemas.py: remove sum/counter related schemas - Update main.py: remove deleted router imports - Update test_permissions.py: remove tests for deprecated features - Update test_price_history.py: remove worker-related tests - Update conftest.py: remove mock_enqueue_job fixture - Update auth.py: fix example in docstring
82 lines
2.6 KiB
Python
82 lines
2.6 KiB
Python
"""Audit routes for price history."""
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from sqlalchemy import desc, select
|
|
from sqlalchemy.exc import IntegrityError
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from auth import require_permission
|
|
from database import get_db
|
|
from models import Permission, PriceHistory, User
|
|
from price_fetcher import PAIR_BTC_EUR, SOURCE_BITFINEX, fetch_btc_eur_price
|
|
from schemas import PriceHistoryResponse
|
|
|
|
router = APIRouter(prefix="/api/audit", tags=["audit"])
|
|
|
|
|
|
def _to_price_history_response(record: PriceHistory) -> PriceHistoryResponse:
|
|
return PriceHistoryResponse(
|
|
id=record.id,
|
|
source=record.source,
|
|
pair=record.pair,
|
|
price=record.price,
|
|
timestamp=record.timestamp,
|
|
created_at=record.created_at,
|
|
)
|
|
|
|
|
|
# =============================================================================
|
|
# Price History Endpoints
|
|
# =============================================================================
|
|
|
|
PRICE_HISTORY_LIMIT = 20
|
|
|
|
|
|
@router.get("/price-history", response_model=list[PriceHistoryResponse])
|
|
async def get_price_history(
|
|
db: AsyncSession = Depends(get_db),
|
|
_current_user: User = Depends(require_permission(Permission.VIEW_AUDIT)),
|
|
) -> list[PriceHistoryResponse]:
|
|
"""Get the 20 most recent price history records."""
|
|
query = (
|
|
select(PriceHistory)
|
|
.order_by(desc(PriceHistory.timestamp))
|
|
.limit(PRICE_HISTORY_LIMIT)
|
|
)
|
|
result = await db.execute(query)
|
|
records = result.scalars().all()
|
|
|
|
return [_to_price_history_response(record) for record in records]
|
|
|
|
|
|
@router.post("/price-history/fetch", response_model=PriceHistoryResponse)
|
|
async def fetch_price_now(
|
|
db: AsyncSession = Depends(get_db),
|
|
_current_user: User = Depends(require_permission(Permission.FETCH_PRICE)),
|
|
) -> PriceHistoryResponse:
|
|
"""Manually trigger a price fetch from Bitfinex."""
|
|
price, timestamp = await fetch_btc_eur_price()
|
|
|
|
record = PriceHistory(
|
|
source=SOURCE_BITFINEX,
|
|
pair=PAIR_BTC_EUR,
|
|
price=price,
|
|
timestamp=timestamp,
|
|
)
|
|
db.add(record)
|
|
|
|
try:
|
|
await db.commit()
|
|
await db.refresh(record)
|
|
except IntegrityError:
|
|
# Duplicate timestamp - return the existing record
|
|
await db.rollback()
|
|
query = select(PriceHistory).where(
|
|
PriceHistory.source == SOURCE_BITFINEX,
|
|
PriceHistory.pair == PAIR_BTC_EUR,
|
|
PriceHistory.timestamp == timestamp,
|
|
)
|
|
result = await db.execute(query)
|
|
record = result.scalar_one()
|
|
|
|
return _to_price_history_response(record)
|