import uuid import json import csv import io from fastapi import APIRouter, Depends, UploadFile, File, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.auth.api_key import get_user_by_api_key from app.auth.deps import get_current_user from app.database import get_db from app.models.collection import Collection from app.models.schemas import TicketInput, TicketsUpload, TicketsUploadResponse from app.models.user import User from app.services.chunking import build_chunk from app.services.embedding import embed_texts from app.services.vector_store import upsert_chunks, get_existing_ticket_ids router = APIRouter(prefix="/api/collections", tags=["tickets"]) async def process_tickets( db: AsyncSession, collection_id: uuid.UUID, user: User, tickets: list[TicketInput], ) -> TicketsUploadResponse: result = await db.execute( select(Collection).where(Collection.id == collection_id, Collection.user_id == user.id) ) collection = result.scalar_one_or_none() if not collection: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Collection not found") existing_ids = get_existing_ticket_ids(str(user.id), str(collection_id)) processed = 0 skipped = 0 chunks = [] for t in tickets: if t.ticket_id in existing_ids: skipped += 1 continue ticket_dict = { "ticket_id": t.ticket_id, "category": t.category, "description": t.description, "client": t.client, "messages": [{"role": m.role, "text": m.text} for m in t.messages], } chunk = build_chunk(ticket_dict) if chunk: chunk["tenant_id"] = str(user.id) chunk["collection_id"] = str(collection_id) chunks.append(chunk) processed += 1 else: skipped += 1 if chunks: search_texts = [c["search_text"] for c in chunks] vectors = await embed_texts(search_texts) await upsert_chunks(str(user.id), chunks, vectors) collection.ticket_count += processed await db.commit() return TicketsUploadResponse( processed=processed, skipped=skipped, collection_ticket_count=collection.ticket_count, ) @router.post("/{collection_id}/tickets", response_model=TicketsUploadResponse) async def upload_tickets_json( collection_id: uuid.UUID, body: TicketsUpload, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): return await process_tickets(db, collection_id, user, body.tickets) @router.post("/{collection_id}/tickets/upload", response_model=TicketsUploadResponse) async def upload_tickets_file( collection_id: uuid.UUID, file: UploadFile = File(...), user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): content = await file.read() if file.filename.endswith(".json"): data = json.loads(content) tickets = [TicketInput(**t) for t in data] if isinstance(data, list) else [TicketInput(**data)] elif file.filename.endswith(".csv"): text = content.decode("utf-8-sig") reader = csv.DictReader(io.StringIO(text)) tickets = [] for row in reader: tickets.append( TicketInput( ticket_id=row.get("ticket_id", ""), category=row.get("category", ""), description=row.get("description", ""), client=row.get("client", ""), ) ) else: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Unsupported format. Use JSON or CSV.", ) return await process_tickets(db, collection_id, user, tickets)