Lots of stuff
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
from typing import List, Optional, Tuple, Any
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, HTTPException, Path, Response
|
||||
from sqlalchemy import text, select
|
||||
from fastapi import APIRouter, Depends, Query, HTTPException, Path, Response, UploadFile, File
|
||||
from sqlalchemy import text, select, insert
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -14,6 +14,52 @@ router = APIRouter()
|
||||
# ---------------------------------------------------------------------------
|
||||
# Crafting Recipes endpoints
|
||||
|
||||
@router.post("/inventory/import")
|
||||
async def import_inventory_csv(
|
||||
file: UploadFile = File(...),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
):
|
||||
"""Replace the entire inventory table with contents from an uploaded CSV.
|
||||
|
||||
The CSV must use the delimiter ``;`` and column headers: ``char;storage;item;quantity``.
|
||||
"""
|
||||
import csv
|
||||
import io
|
||||
# Read file bytes and decode
|
||||
contents = await file.read()
|
||||
try:
|
||||
text_data = contents.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
raise HTTPException(status_code=400, detail="CSV must be UTF-8 encoded")
|
||||
|
||||
reader = csv.DictReader(io.StringIO(text_data), delimiter=";", quotechar='"')
|
||||
rows = []
|
||||
for r in reader:
|
||||
try:
|
||||
qty = int(r["quantity"].strip()) if r["quantity"].strip() else 0
|
||||
except (KeyError, ValueError):
|
||||
raise HTTPException(status_code=400, detail="Invalid CSV schema or quantity value")
|
||||
rows.append(
|
||||
{
|
||||
"character_name": r["char"].strip(),
|
||||
"storage_type": r["storage"].strip(),
|
||||
"item_name": r["item"].strip(),
|
||||
"quantity": qty,
|
||||
}
|
||||
)
|
||||
|
||||
# Replace table contents inside a transaction
|
||||
try:
|
||||
await session.execute(text("TRUNCATE TABLE inventory;"))
|
||||
if rows:
|
||||
await session.execute(insert(Inventory), rows)
|
||||
await session.commit()
|
||||
except Exception as e:
|
||||
await session.rollback()
|
||||
raise HTTPException(status_code=500, detail=f"Failed to import CSV: {e}")
|
||||
|
||||
return {"imported": len(rows)}
|
||||
|
||||
ALLOWED_CRAFTS = {
|
||||
"woodworking": "recipes_woodworking",
|
||||
# Future crafts can be added here, e.g. "smithing": "recipes_smithing"
|
||||
@@ -102,6 +148,7 @@ class ItemSummary(BaseModel):
|
||||
name: str
|
||||
icon_id: Optional[str]
|
||||
type_description: Optional[str]
|
||||
jobs_description: Optional[List[str]]
|
||||
|
||||
|
||||
@router.get("/items", response_model=List[ItemSummary])
|
||||
@@ -139,12 +186,14 @@ async def items(
|
||||
total_count = total_res.scalar() or 0
|
||||
response.headers["X-Total-Count"] = str(total_count)
|
||||
|
||||
# Use LEFT JOIN to fetch jobs_description from armor_items table; it will be NULL for non-armor.
|
||||
join_sql = "FROM all_items a LEFT JOIN armor_items ai ON ai.id = a.id"
|
||||
q = text(
|
||||
f"SELECT id, name, icon_id, type_description FROM all_items {where_sql} ORDER BY id LIMIT :limit OFFSET :offset"
|
||||
f"SELECT a.id, a.name, a.icon_id, a.type_description, ai.jobs_description {join_sql} {where_sql} ORDER BY a.id LIMIT :limit OFFSET :offset"
|
||||
)
|
||||
result = await session.execute(q, params)
|
||||
rows = result.fetchall()
|
||||
return [ItemSummary(id=r.id, name=r.name, icon_id=r.icon_id, type_description=r.type_description) for r in rows]
|
||||
return [ItemSummary(id=r.id, name=r.name, icon_id=r.icon_id, type_description=r.type_description, jobs_description=r.jobs_description) for r in rows]
|
||||
|
||||
|
||||
class ItemDetail(BaseModel):
|
||||
|
||||
@@ -4,3 +4,4 @@ SQLAlchemy[asyncio]==2.0.27
|
||||
asyncpg==0.29.0
|
||||
pydantic==2.7.1
|
||||
python-dotenv==1.0.1
|
||||
python-multipart==0.0.9
|
||||
|
||||
Reference in New Issue
Block a user