feat(#19): Add filamentcolors.xyz color lookup integration
Some checks failed
CI / style (push) Has been cancelled
CI / build-client (push) Has been cancelled
CI / build-amd64 (push) Has been cancelled
CI / build-tester (push) Has been cancelled
CI / test (cockroachdb) (push) Has been cancelled
CI / test (mariadb) (push) Has been cancelled
CI / test (postgres) (push) Has been cancelled
CI / test (sqlite) (push) Has been cancelled
CI / build-arm64 (push) Has been cancelled
CI / build-armv7 (push) Has been cancelled
CI / publish-images (push) Has been cancelled
CI / publish-release (push) Has been cancelled
Some checks failed
CI / style (push) Has been cancelled
CI / build-client (push) Has been cancelled
CI / build-amd64 (push) Has been cancelled
CI / build-tester (push) Has been cancelled
CI / test (cockroachdb) (push) Has been cancelled
CI / test (mariadb) (push) Has been cancelled
CI / test (postgres) (push) Has been cancelled
CI / test (sqlite) (push) Has been cancelled
CI / build-arm64 (push) Has been cancelled
CI / build-armv7 (push) Has been cancelled
CI / publish-images (push) Has been cancelled
CI / publish-release (push) Has been cancelled
- Backend proxy endpoints: GET /filamentcolors/manufacturers, GET /filamentcolors/search - Frontend: FilamentColorLookup modal with manufacturer filter + color search - Color grid shows swatches with hex codes, click to apply - Added to both filament create and edit forms next to ColorPicker Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Annotated, Optional
|
||||
|
||||
import httpx
|
||||
import sqlalchemy
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from pydantic import BaseModel, Field, RootModel
|
||||
@@ -371,3 +372,104 @@ async def get_cost_stats(
|
||||
avg_cost_per_kg=avg_cost_per_kg,
|
||||
cost_by_material=cost_by_material,
|
||||
)
|
||||
|
||||
|
||||
# --- filamentcolors.xyz integration ---
|
||||
|
||||
FILAMENTCOLORS_API = "https://filamentcolors.xyz/api"
|
||||
|
||||
|
||||
class FilamentColorSwatch(BaseModel):
|
||||
"""A color swatch from filamentcolors.xyz."""
|
||||
|
||||
id: int
|
||||
color_name: str
|
||||
hex_color: Optional[str] = None
|
||||
manufacturer_name: str
|
||||
filament_type: str
|
||||
image_front: Optional[str] = None
|
||||
|
||||
|
||||
class FilamentColorManufacturer(BaseModel):
|
||||
"""A manufacturer from filamentcolors.xyz."""
|
||||
|
||||
id: int
|
||||
name: str
|
||||
|
||||
|
||||
class FilamentColorSearchResult(BaseModel):
|
||||
"""Search results from filamentcolors.xyz."""
|
||||
|
||||
count: int
|
||||
swatches: list[FilamentColorSwatch]
|
||||
|
||||
|
||||
@router.get(
|
||||
"/filamentcolors/manufacturers",
|
||||
name="Get filamentcolors.xyz manufacturers",
|
||||
description="List all manufacturers from filamentcolors.xyz.",
|
||||
response_model=list[FilamentColorManufacturer],
|
||||
)
|
||||
async def get_filamentcolors_manufacturers() -> list[FilamentColorManufacturer]:
|
||||
"""Proxy manufacturer list from filamentcolors.xyz."""
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
resp = await client.get(f"{FILAMENTCOLORS_API}/manufacturer/", params={"format": "json", "limit": 500})
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
|
||||
return [
|
||||
FilamentColorManufacturer(id=m["id"], name=m["name"])
|
||||
for m in data.get("results", [])
|
||||
]
|
||||
|
||||
|
||||
@router.get(
|
||||
"/filamentcolors/search",
|
||||
name="Search filamentcolors.xyz swatches",
|
||||
description="Search color swatches from filamentcolors.xyz by manufacturer and/or color name.",
|
||||
response_model=FilamentColorSearchResult,
|
||||
)
|
||||
async def search_filamentcolors(
|
||||
*,
|
||||
manufacturer: Annotated[
|
||||
Optional[str],
|
||||
Query(description="Manufacturer name to filter by."),
|
||||
] = None,
|
||||
color: Annotated[
|
||||
Optional[str],
|
||||
Query(description="Color name to search for."),
|
||||
] = None,
|
||||
filament_type: Annotated[
|
||||
Optional[str],
|
||||
Query(description="Filament type to filter by (e.g., PLA, PETG)."),
|
||||
] = None,
|
||||
limit: Annotated[int, Query(ge=1, le=100)] = 50,
|
||||
page: Annotated[int, Query(ge=1)] = 1,
|
||||
) -> FilamentColorSearchResult:
|
||||
"""Proxy swatch search from filamentcolors.xyz."""
|
||||
params: dict = {"format": "json", "limit": limit, "page": page}
|
||||
if manufacturer:
|
||||
params["manufacturer__name"] = manufacturer
|
||||
if color:
|
||||
params["color_name__icontains"] = color
|
||||
if filament_type:
|
||||
params["filament_type__name__icontains"] = filament_type
|
||||
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
resp = await client.get(f"{FILAMENTCOLORS_API}/swatch/", params=params)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
|
||||
swatches = [
|
||||
FilamentColorSwatch(
|
||||
id=s["id"],
|
||||
color_name=s.get("color_name", ""),
|
||||
hex_color=s.get("hex_color"),
|
||||
manufacturer_name=s.get("manufacturer", {}).get("name", ""),
|
||||
filament_type=s.get("filament_type", {}).get("name", ""),
|
||||
image_front=s.get("image_front"),
|
||||
)
|
||||
for s in data.get("results", [])
|
||||
]
|
||||
|
||||
return FilamentColorSearchResult(count=data.get("count", 0), swatches=swatches)
|
||||
|
||||
Reference in New Issue
Block a user