refactors

This commit is contained in:
counterweight 2025-12-25 18:27:59 +01:00
parent f46d2ae8b3
commit 168b67acee
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
12 changed files with 471 additions and 126 deletions

View file

@ -1,9 +1,8 @@
"""Exchange service for business logic related to Bitcoin trading."""
import uuid
from datetime import UTC, date, datetime, time, timedelta
from datetime import UTC, date, datetime, timedelta
from sqlalchemy import and_, select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
@ -15,7 +14,6 @@ from exceptions import (
ServiceUnavailableError,
)
from models import (
Availability,
BitcoinTransferMethod,
Exchange,
ExchangeStatus,
@ -23,6 +21,7 @@ from models import (
TradeDirection,
User,
)
from repositories.exchange import ExchangeRepository
from repositories.price import PriceRepository
from shared_constants import (
EUR_TRADE_INCREMENT,
@ -44,6 +43,7 @@ class ExchangeService:
def __init__(self, db: AsyncSession):
self.db = db
self.price_repo = PriceRepository(db)
self.exchange_repo = ExchangeRepository(db)
def apply_premium_for_direction(
self,
@ -107,20 +107,21 @@ class ExchangeService:
self, slot_start: datetime, slot_date: date
) -> None:
"""Verify slot falls within availability."""
from repositories.availability import AvailabilityRepository
slot_start_time = slot_start.time()
slot_end_dt = slot_start + timedelta(minutes=SLOT_DURATION_MINUTES)
slot_end_time = slot_end_dt.time()
result = await self.db.execute(
select(Availability).where(
and_(
Availability.date == slot_date,
Availability.start_time <= slot_start_time,
Availability.end_time >= slot_end_time,
)
)
)
matching_availability = result.scalar_one_or_none()
availability_repo = AvailabilityRepository(self.db)
availabilities = await availability_repo.get_by_date(slot_date)
# Check if any availability block contains this slot
matching_availability = None
for avail in availabilities:
if avail.start_time <= slot_start_time and avail.end_time >= slot_end_time:
matching_availability = avail
break
if not matching_availability:
slot_str = slot_start.strftime("%Y-%m-%d %H:%M")
@ -171,29 +172,19 @@ class ExchangeService:
self, user: User, slot_date: date
) -> Exchange | None:
"""Check if user already has a trade on this date."""
existing_trade_query = select(Exchange).where(
and_(
Exchange.user_id == user.id,
Exchange.slot_start
>= datetime.combine(slot_date, time.min, tzinfo=UTC),
Exchange.slot_start
< datetime.combine(slot_date, time.max, tzinfo=UTC) + timedelta(days=1),
Exchange.status == ExchangeStatus.BOOKED,
)
exchanges = await self.exchange_repo.get_by_user_and_date_range(
user_id=user.id,
start_date=slot_date,
end_date=slot_date,
status=ExchangeStatus.BOOKED,
)
result = await self.db.execute(existing_trade_query)
return result.scalar_one_or_none()
return exchanges[0] if exchanges else None
async def check_slot_already_booked(self, slot_start: datetime) -> Exchange | None:
"""Check if slot is already booked (only consider BOOKED status)."""
slot_booked_query = select(Exchange).where(
and_(
Exchange.slot_start == slot_start,
Exchange.status == ExchangeStatus.BOOKED,
)
return await self.exchange_repo.get_by_slot_start(
slot_start, status=ExchangeStatus.BOOKED
)
result = await self.db.execute(slot_booked_query)
return result.scalar_one_or_none()
async def create_exchange(
self,
@ -297,9 +288,7 @@ class ExchangeService:
NotFoundError: If exchange not found or user doesn't own it
(for security, returns 404)
"""
query = select(Exchange).where(Exchange.public_id == public_id)
result = await self.db.execute(query)
exchange = result.scalar_one_or_none()
exchange = await self.exchange_repo.get_by_public_id(public_id)
if not exchange:
raise NotFoundError("Trade")