llm-ready-data / app /api /v1 /qr_generator.py
validops-east-1's picture
feat: add tidb
5a01a63
Raw
History Blame Contribute Delete
3.01 kB
from __future__ import annotations
import threading
from typing import List, Optional
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field, field_validator
from app.services.qr_generator_service import QRGeneratorError, QRGeneratorService
router = APIRouter()
_service_lock = threading.Lock()
_shared_service: Optional[QRGeneratorService] = None
def _get_service() -> QRGeneratorService:
global _shared_service
if _shared_service is None:
with _service_lock:
if _shared_service is None:
_shared_service = QRGeneratorService()
return _shared_service
class QRGenerateRequest(BaseModel):
data: List[str] = Field(
...,
min_length=1,
max_length=500,
description="List of data strings to generate QR codes for (max 500)",
)
size: str = Field(
"300x300",
description="QR code image size in WIDTHxHEIGHT format (e.g., 300x300)",
)
@field_validator("data")
@classmethod
def validate_data_items(cls, v: List[str]) -> List[str]:
for i, item in enumerate(v):
if not item or not item.strip():
raise ValueError(f"Data item at index {i} must be a non-empty string")
if len(item) > 2048:
raise ValueError(
f"Data item at index {i} exceeds max length of 2048 characters"
)
return v
@field_validator("size")
@classmethod
def validate_size(cls, v: str) -> str:
parts = v.lower().split("x")
if len(parts) != 2:
raise ValueError("Size must be in format WIDTHxHEIGHT (e.g., 300x300)")
for part in parts:
if not part.isdigit() or int(part) < 1 or int(part) > 1000:
raise ValueError("Width and height must be between 1 and 1000")
return v
class QRItemResult(BaseModel):
index: int
data: str
qr_image_url: Optional[str] = None
error: Optional[str] = None
class QRGenerateResponse(BaseModel):
success: bool
total: int
succeeded: int
failed: int
results: List[QRItemResult]
@router.post(
"/qr-generator",
response_model=QRGenerateResponse,
summary="Generate QR codes for a batch of data strings",
description="Accepts up to 500 data strings and returns QR code image URLs for each. "
"The actual QR image is hosted by an external service.",
)
def generate_qr_codes(body: QRGenerateRequest):
service = _get_service()
try:
results = service.generate_bulk(body.data, body.size)
except QRGeneratorError as e:
raise HTTPException(status_code=400, detail={"success": False, "error": str(e)})
succeeded = sum(1 for r in results if r["error"] is None)
failed = len(results) - succeeded
return QRGenerateResponse(
success=failed == 0,
total=len(results),
succeeded=succeeded,
failed=failed,
results=[QRItemResult(**r) for r in results],
)