Move slot expansion logic to ExchangeService
- Add get_available_slots() and _expand_availability_to_slots() to ExchangeService - Update routes/exchange.py to use ExchangeService.get_available_slots() - Remove all business logic from get_available_slots endpoint - Add AvailabilityRepository to ExchangeService dependencies - Add Availability and BookableSlot imports to ExchangeService - Fix import path for validate_date_in_range (use date_validation module) - Remove unused user_repo variable and import from routes/invites.py - Fix mypy error in ValidationError by adding proper type annotation
This commit is contained in:
parent
c3a501e3b2
commit
280c1e5687
12 changed files with 571 additions and 303 deletions
|
|
@ -14,6 +14,7 @@ from exceptions import (
|
|||
ServiceUnavailableError,
|
||||
)
|
||||
from models import (
|
||||
Availability,
|
||||
BitcoinTransferMethod,
|
||||
Exchange,
|
||||
ExchangeStatus,
|
||||
|
|
@ -21,8 +22,10 @@ from models import (
|
|||
TradeDirection,
|
||||
User,
|
||||
)
|
||||
from repositories.availability import AvailabilityRepository
|
||||
from repositories.exchange import ExchangeRepository
|
||||
from repositories.price import PriceRepository
|
||||
from schemas import AvailableSlotsResponse, BookableSlot
|
||||
from shared_constants import (
|
||||
EUR_TRADE_INCREMENT,
|
||||
EUR_TRADE_MAX,
|
||||
|
|
@ -44,6 +47,7 @@ class ExchangeService:
|
|||
self.db = db
|
||||
self.price_repo = PriceRepository(db)
|
||||
self.exchange_repo = ExchangeRepository(db)
|
||||
self.availability_repo = AvailabilityRepository(db)
|
||||
|
||||
def apply_premium_for_direction(
|
||||
self,
|
||||
|
|
@ -379,3 +383,73 @@ class ExchangeService:
|
|||
await self.db.refresh(exchange)
|
||||
|
||||
return exchange
|
||||
|
||||
def _expand_availability_to_slots(
|
||||
self, avail: Availability, slot_date: date, booked_starts: set[datetime]
|
||||
) -> list[BookableSlot]:
|
||||
"""
|
||||
Expand an availability block into individual slots, filtering out booked ones.
|
||||
|
||||
Args:
|
||||
avail: Availability record
|
||||
slot_date: Date for the slots
|
||||
booked_starts: Set of already-booked slot start times
|
||||
|
||||
Returns:
|
||||
List of available BookableSlot records
|
||||
"""
|
||||
slots: list[BookableSlot] = []
|
||||
|
||||
# Start from the availability's start time
|
||||
current_start = datetime.combine(slot_date, avail.start_time, tzinfo=UTC)
|
||||
avail_end = datetime.combine(slot_date, avail.end_time, tzinfo=UTC)
|
||||
|
||||
while current_start + timedelta(minutes=SLOT_DURATION_MINUTES) <= avail_end:
|
||||
slot_end = current_start + timedelta(minutes=SLOT_DURATION_MINUTES)
|
||||
|
||||
# Only include if not already booked
|
||||
if current_start not in booked_starts:
|
||||
slots.append(BookableSlot(start_time=current_start, end_time=slot_end))
|
||||
|
||||
current_start = slot_end
|
||||
|
||||
return slots
|
||||
|
||||
async def get_available_slots(self, date_param: date) -> AvailableSlotsResponse:
|
||||
"""
|
||||
Get available booking slots for a specific date.
|
||||
|
||||
Returns all slots that:
|
||||
- Fall within admin-defined availability windows
|
||||
- Are not already booked by another user
|
||||
|
||||
Args:
|
||||
date_param: Date to get slots for
|
||||
|
||||
Returns:
|
||||
AvailableSlotsResponse with date and list of available slots
|
||||
|
||||
Raises:
|
||||
BadRequestError: If date is out of range
|
||||
"""
|
||||
validate_date_in_range(date_param, context="book")
|
||||
|
||||
# Get availability for the date
|
||||
availabilities = await self.availability_repo.get_by_date(date_param)
|
||||
|
||||
if not availabilities:
|
||||
return AvailableSlotsResponse(date=date_param, slots=[])
|
||||
|
||||
# Get already booked slots for the date
|
||||
booked_starts = await self.exchange_repo.get_booked_slots_for_date(date_param)
|
||||
|
||||
# Expand each availability into slots
|
||||
all_slots: list[BookableSlot] = []
|
||||
for avail in availabilities:
|
||||
slots = self._expand_availability_to_slots(avail, date_param, booked_starts)
|
||||
all_slots.extend(slots)
|
||||
|
||||
# Sort by start time
|
||||
all_slots.sort(key=lambda s: s.start_time)
|
||||
|
||||
return AvailableSlotsResponse(date=date_param, slots=all_slots)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue