- veins → shunts rename - add cfg/standalone/ and cfg/<room>/ structure - remove old data/*.json (moved to cfg/<room>/data/) - update build.py and ctrl scripts
303 lines
7.3 KiB
Python
303 lines
7.3 KiB
Python
"""API routes for Amar (MOCK) vein - Mock Amar API for testing."""
|
|
|
|
import asyncio
|
|
import random
|
|
from fastapi import APIRouter, HTTPException, Query
|
|
from fastapi.responses import JSONResponse
|
|
from typing import Optional, List, Dict, Any
|
|
from pydantic import BaseModel
|
|
|
|
# Import datagen from ward/tools
|
|
import sys
|
|
from pathlib import Path
|
|
ward_tools_path = Path(__file__).parent.parent.parent.parent.parent / "ward" / "tools"
|
|
sys.path.insert(0, str(ward_tools_path))
|
|
|
|
from datagen.amar import AmarDataGenerator
|
|
|
|
from ..core.config import settings
|
|
|
|
router = APIRouter()
|
|
|
|
# In-memory storage for mock data (reset on restart)
|
|
MOCK_DB = {
|
|
"petowners": {},
|
|
"pets": {},
|
|
"carts": {},
|
|
"service_requests": {},
|
|
}
|
|
|
|
|
|
# Request/Response Models
|
|
class CreatePetOwnerRequest(BaseModel):
|
|
address: str
|
|
email: Optional[str] = None
|
|
phone: Optional[str] = None
|
|
|
|
|
|
class CreatePetRequest(BaseModel):
|
|
owner_id: int
|
|
name: str
|
|
species: str # DOG, CAT
|
|
age: Optional[int] = None
|
|
age_unit: str = "years" # years, months
|
|
|
|
|
|
class CreateCartRequest(BaseModel):
|
|
owner_id: int
|
|
|
|
|
|
class AddCartItemRequest(BaseModel):
|
|
service_id: int
|
|
pet_id: int
|
|
quantity: int = 1
|
|
|
|
|
|
async def _mock_delay():
|
|
"""Add realistic delay if enabled."""
|
|
if settings.enable_random_delays:
|
|
delay_ms = random.randint(settings.min_delay_ms, settings.max_delay_ms)
|
|
await asyncio.sleep(delay_ms / 1000)
|
|
|
|
|
|
def _maybe_error():
|
|
"""Randomly raise an error based on error_rate."""
|
|
if random.random() < settings.error_rate:
|
|
raise HTTPException(500, "Mock error: Simulated failure")
|
|
|
|
|
|
@router.get("/health")
|
|
async def health():
|
|
"""Health check endpoint."""
|
|
return {
|
|
"status": "ok",
|
|
"vein": "Amar\n(MOCK)",
|
|
"message": "Mock Amar API for testing",
|
|
"_mock": True,
|
|
}
|
|
|
|
|
|
@router.post("/api/v1/pet-owners/")
|
|
async def create_petowner(request: CreatePetOwnerRequest):
|
|
"""Create a guest petowner (VET-536)."""
|
|
await _mock_delay()
|
|
_maybe_error()
|
|
|
|
owner = AmarDataGenerator.petowner(
|
|
address=request.address,
|
|
is_guest=True,
|
|
email=request.email,
|
|
phone=request.phone,
|
|
)
|
|
|
|
# Store in mock DB
|
|
MOCK_DB["petowners"][owner["id"]] = owner
|
|
|
|
owner["_mock"] = True
|
|
return owner
|
|
|
|
|
|
@router.get("/api/v1/pet-owners/{owner_id}")
|
|
async def get_petowner(owner_id: int):
|
|
"""Get petowner by ID."""
|
|
await _mock_delay()
|
|
_maybe_error()
|
|
|
|
owner = MOCK_DB["petowners"].get(owner_id)
|
|
if not owner:
|
|
raise HTTPException(404, f"PetOwner {owner_id} not found")
|
|
|
|
return owner
|
|
|
|
|
|
@router.post("/api/v1/pets/")
|
|
async def create_pet(request: CreatePetRequest):
|
|
"""Create a pet (VET-537)."""
|
|
await _mock_delay()
|
|
_maybe_error()
|
|
|
|
# Verify owner exists
|
|
if request.owner_id not in MOCK_DB["petowners"]:
|
|
raise HTTPException(404, f"Owner {request.owner_id} not found")
|
|
|
|
pet = AmarDataGenerator.pet(
|
|
owner_id=request.owner_id,
|
|
name=request.name,
|
|
species=request.species,
|
|
age_value=request.age,
|
|
age_unit=request.age_unit,
|
|
)
|
|
|
|
# Store in mock DB
|
|
MOCK_DB["pets"][pet["id"]] = pet
|
|
|
|
pet["_mock"] = True
|
|
return pet
|
|
|
|
|
|
@router.get("/api/v1/pets/{pet_id}")
|
|
async def get_pet(pet_id: int):
|
|
"""Get pet by ID."""
|
|
await _mock_delay()
|
|
_maybe_error()
|
|
|
|
pet = MOCK_DB["pets"].get(pet_id)
|
|
if not pet:
|
|
raise HTTPException(404, f"Pet {pet_id} not found")
|
|
|
|
return pet
|
|
|
|
|
|
@router.post("/api/v1/cart/")
|
|
async def create_cart(request: CreateCartRequest):
|
|
"""Create a cart (VET-538)."""
|
|
await _mock_delay()
|
|
_maybe_error()
|
|
|
|
# Verify owner exists
|
|
if request.owner_id not in MOCK_DB["petowners"]:
|
|
raise HTTPException(404, f"Owner {request.owner_id} not found")
|
|
|
|
cart = AmarDataGenerator.cart(owner_id=request.owner_id)
|
|
|
|
# Store in mock DB
|
|
MOCK_DB["carts"][cart["id"]] = cart
|
|
|
|
cart["_mock"] = True
|
|
return cart
|
|
|
|
|
|
@router.post("/api/v1/cart/{cart_id}/items/")
|
|
async def add_cart_item(cart_id: int, request: AddCartItemRequest):
|
|
"""Add item to cart and recalculate summary."""
|
|
await _mock_delay()
|
|
_maybe_error()
|
|
|
|
cart = MOCK_DB["carts"].get(cart_id)
|
|
if not cart:
|
|
raise HTTPException(404, f"Cart {cart_id} not found")
|
|
|
|
# Get service price
|
|
services = AmarDataGenerator.SERVICES
|
|
service = next((s for s in services if s["id"] == request.service_id), None)
|
|
if not service:
|
|
raise HTTPException(404, f"Service {request.service_id} not found")
|
|
|
|
# Add item
|
|
item = {
|
|
"service_id": request.service_id,
|
|
"service_name": service["name"],
|
|
"pet_id": request.pet_id,
|
|
"quantity": request.quantity,
|
|
"price": service["price"],
|
|
}
|
|
|
|
cart["items"].append(item)
|
|
|
|
# Recalculate summary
|
|
cart = AmarDataGenerator.calculate_cart_summary(cart, cart["items"])
|
|
MOCK_DB["carts"][cart_id] = cart
|
|
|
|
cart["_mock"] = True
|
|
return cart
|
|
|
|
|
|
@router.get("/api/v1/cart/{cart_id}")
|
|
async def get_cart(cart_id: int):
|
|
"""Get cart by ID."""
|
|
await _mock_delay()
|
|
_maybe_error()
|
|
|
|
cart = MOCK_DB["carts"].get(cart_id)
|
|
if not cart:
|
|
raise HTTPException(404, f"Cart {cart_id} not found")
|
|
|
|
return cart
|
|
|
|
|
|
@router.get("/api/v1/services/")
|
|
async def list_services(
|
|
species: Optional[str] = Query(None),
|
|
neighborhood_id: Optional[int] = Query(None),
|
|
):
|
|
"""List available services filtered by species and neighborhood (VET-540)."""
|
|
await _mock_delay()
|
|
_maybe_error()
|
|
|
|
services = AmarDataGenerator.filter_services(
|
|
species=species,
|
|
neighborhood_id=neighborhood_id,
|
|
)
|
|
|
|
for service in services:
|
|
service["_mock"] = True
|
|
|
|
return services
|
|
|
|
|
|
@router.get("/api/v1/categories/")
|
|
async def list_categories(
|
|
species: Optional[str] = Query(None),
|
|
neighborhood_id: Optional[int] = Query(None),
|
|
):
|
|
"""List categories with available services (VET-539)."""
|
|
await _mock_delay()
|
|
_maybe_error()
|
|
|
|
categories = AmarDataGenerator.filter_categories(
|
|
species=species,
|
|
neighborhood_id=neighborhood_id,
|
|
)
|
|
|
|
for category in categories:
|
|
category["_mock"] = True
|
|
|
|
return categories
|
|
|
|
|
|
@router.post("/solicitudes/service-requests/")
|
|
async def create_service_request(cart_id: int, requested_date: Optional[str] = None):
|
|
"""Create a service request."""
|
|
await _mock_delay()
|
|
_maybe_error()
|
|
|
|
cart = MOCK_DB["carts"].get(cart_id)
|
|
if not cart:
|
|
raise HTTPException(404, f"Cart {cart_id} not found")
|
|
|
|
request = AmarDataGenerator.service_request(
|
|
cart_id=cart_id,
|
|
requested_date=requested_date,
|
|
)
|
|
|
|
MOCK_DB["service_requests"][request["id"]] = request
|
|
|
|
request["_mock"] = True
|
|
return request
|
|
|
|
|
|
@router.get("/mock/reset")
|
|
async def reset_mock_db():
|
|
"""Reset the mock database (useful for testing)."""
|
|
MOCK_DB["petowners"].clear()
|
|
MOCK_DB["pets"].clear()
|
|
MOCK_DB["carts"].clear()
|
|
MOCK_DB["service_requests"].clear()
|
|
|
|
return {
|
|
"message": "Mock database reset",
|
|
"_mock": True,
|
|
}
|
|
|
|
|
|
@router.get("/mock/stats")
|
|
async def mock_stats():
|
|
"""Get mock database statistics."""
|
|
return {
|
|
"petowners": len(MOCK_DB["petowners"]),
|
|
"pets": len(MOCK_DB["pets"]),
|
|
"carts": len(MOCK_DB["carts"]),
|
|
"service_requests": len(MOCK_DB["service_requests"]),
|
|
"_mock": True,
|
|
}
|