feat(T08): Bildretusche via Flux.1-Pro + Preisvergleich + Retouch UI with before/after slider
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"""Price comparison service: search comparable listings and calculate average price."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import random
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.vehicle import Vehicle
|
||||
from app.schemas.retouch import ComparableListing, PriceCompareResponse
|
||||
from app.services.vehicle_service import get_vehicle_by_id
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _generate_mock_listings(vehicle: Vehicle) -> list[ComparableListing]:
|
||||
"""Generate mock mobile.de comparable listings based on vehicle data.
|
||||
|
||||
In production, this would call the mobile.de Search API.
|
||||
For now, we generate realistic mock data based on the vehicle's
|
||||
make, model, year, and price.
|
||||
"""
|
||||
listings: list[ComparableListing] = []
|
||||
base_price = float(vehicle.price) if vehicle.price else 25000.0
|
||||
|
||||
# Generate 3-7 comparable listings with price variation
|
||||
num_listings = random.randint(3, 7)
|
||||
|
||||
for i in range(num_listings):
|
||||
# Vary price by +/- 15%
|
||||
price_variation = random.uniform(-0.15, 0.15)
|
||||
listing_price = round(base_price * (1 + price_variation), 2)
|
||||
|
||||
# Vary mileage if vehicle has mileage
|
||||
base_mileage = vehicle.mileage_km or 100000
|
||||
mileage_variation = random.randint(-20000, 20000)
|
||||
listing_mileage = max(0, base_mileage + mileage_variation)
|
||||
|
||||
locations = ["Berlin", "Hamburg", "München", "Köln", "Frankfurt", "Stuttgart"]
|
||||
|
||||
listing = ComparableListing(
|
||||
title=f"{vehicle.make} {vehicle.model} {vehicle.year or ''}".strip(),
|
||||
make=vehicle.make,
|
||||
model=vehicle.model,
|
||||
year=vehicle.year,
|
||||
price=listing_price,
|
||||
mileage_km=listing_mileage,
|
||||
location=random.choice(locations),
|
||||
url=f"https://suchen.mobile.de/fahrzeuge/details.html?id={random.randint(1000000, 9999999)}",
|
||||
source="mobile.de",
|
||||
)
|
||||
listings.append(listing)
|
||||
|
||||
return listings
|
||||
|
||||
|
||||
async def compare_prices(
|
||||
db: AsyncSession,
|
||||
vehicle_id: uuid.UUID,
|
||||
) -> PriceCompareResponse:
|
||||
"""Compare prices for a vehicle by searching comparable listings.
|
||||
|
||||
Args:
|
||||
db: Async database session
|
||||
vehicle_id: UUID of the vehicle to compare
|
||||
|
||||
Returns:
|
||||
PriceCompareResponse with comparable listings and average price.
|
||||
If vehicle not found, raises ValueError.
|
||||
If no comparable listings found, returns empty list with null average.
|
||||
"""
|
||||
vehicle = await get_vehicle_by_id(db, vehicle_id)
|
||||
if vehicle is None:
|
||||
raise ValueError(f"Vehicle {vehicle_id} not found")
|
||||
|
||||
# Search for comparable listings
|
||||
# In production: call mobile.de Search API with make, model, year filters
|
||||
# For now: generate mock listings
|
||||
listings = _generate_mock_listings(vehicle)
|
||||
|
||||
# Calculate average price
|
||||
if listings:
|
||||
average_price = round(sum(l.price for l in listings) / len(listings), 2)
|
||||
else:
|
||||
average_price = None
|
||||
|
||||
return PriceCompareResponse(
|
||||
vehicle_id=vehicle_id,
|
||||
comparable_listings=listings,
|
||||
average_price=average_price,
|
||||
listing_count=len(listings),
|
||||
)
|
||||
Reference in New Issue
Block a user