| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- import os
- import uuid
- import json
- from datetime import datetime
- from fastapi import FastAPI, UploadFile, File, Header, HTTPException, status, Form, Depends
- from fastapi.middleware.cors import CORSMiddleware
- from typing import Optional, List
- from sqlalchemy.orm import Session
- from backend.services.openai_service import extract_receipt_data, fill_form_with_template_v2
- from backend.schemas import ExtractionResponse, ClaimRecord, UserAccount, ClaimSubmission, V2TemplateResponse
- from . import crud, models, schemas
- from .database import SessionLocal, engine
- from dotenv import load_dotenv
- load_dotenv()
- # Create tables
- models.Base.metadata.create_all(bind=engine)
- app = FastAPI(title="AI-Assisted Data Entry API")
- # Configure CORS
- app.add_middleware(
- CORSMiddleware,
- allow_origins=["*"],
- allow_credentials=True,
- allow_methods=["*"],
- allow_headers=["*"],
- )
- # Dependency
- def get_db():
- db = SessionLocal()
- try:
- yield db
- finally:
- db.close()
- # Startup event for seeding
- @app.on_event("startup")
- def startup_event():
- db = SessionLocal()
- try:
- crud.seed_demo_user(db)
- finally:
- db.close()
- @app.get("/health")
- async def health_check():
- return {"status": "healthy"}
- @app.get("/api/v1/users", response_model=List[UserAccount])
- async def get_users(db: Session = Depends(get_db)):
- db_users = crud.get_users(db)
- return [
- UserAccount(
- id=u.id,
- name=u.name,
- department=u.department,
- medical_allowance=u.medical_allowance
- ) for u in db_users
- ]
- @app.post("/api/v1/users", response_model=UserAccount)
- async def create_user(user: UserAccount, db: Session = Depends(get_db)):
- db_user = crud.create_user(db, user)
- return UserAccount(
- id=db_user.id,
- name=db_user.name,
- department=db_user.department,
- medical_allowance=db_user.medical_allowance
- )
- @app.post("/api/v1/extract", response_model=ExtractionResponse)
- async def extract_receipt(
- file: UploadFile = File(...),
- user_name: str = Form("Demo User"),
- department: str = Form("Operations")
- ):
- if not file.content_type.startswith("image/"):
- raise HTTPException(
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
- detail="File provided is not an image."
- )
-
- try:
- content = await file.read()
- extraction_result = await extract_receipt_data(content, user_name, department)
-
- if extraction_result is None:
- raise HTTPException(
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
- detail="Could not extract data from the provided image."
- )
-
- return extraction_result
- except Exception as e:
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail=f"An error occurred during extraction: {str(e)}"
- )
- @app.post("/api/v2/fill-template", response_model=V2TemplateResponse)
- async def fill_template(
- file: UploadFile = File(...),
- template_fields: str = Form(...),
- user_name: str = Form("Demo User"),
- department: str = Form("Operations")
- ):
- if not file.content_type.startswith("image/"):
- raise HTTPException(
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
- detail="File provided is not an image."
- )
-
- try:
- content = await file.read()
- template_dict = json.loads(template_fields)
-
- result = await fill_form_with_template_v2(
- content,
- template_dict,
- user_name,
- department
- )
-
- return result
- except Exception as e:
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail=f"An error occurred during template filling: {str(e)}"
- )
- @app.post("/api/v1/claims", response_model=ClaimRecord)
- async def submit_claim(
- submission_data: ClaimSubmission,
- user_id: str = Header(...),
- db: Session = Depends(get_db)
- ):
- # crud operation handles user lookup and allowance update
- db_claim = crud.create_claim(db, submission_data, user_id)
- if not db_claim:
- raise HTTPException(status_code=404, detail="User not found")
-
- return crud.to_pydantic_claim(db_claim)
- @app.delete("/api/v1/claims/{claim_id}")
- async def delete_claim(claim_id: str, db: Session = Depends(get_db)):
- success = crud.delete_claim(db, claim_id)
- if not success:
- raise HTTPException(status_code=404, detail="Claim not found")
-
- return {"status": "success", "message": "Claim deleted and allowance refunded"}
- @app.get("/api/v1/claims", response_model=List[ClaimRecord])
- async def get_claims(db: Session = Depends(get_db)):
- db_claims = crud.get_claims(db)
- return [crud.to_pydantic_claim(c) for c in db_claims]
- if __name__ == "__main__":
- import uvicorn
- uvicorn.run(app, host="0.0.0.0", port=8000)
|