Sheetly / app /main.py
nvikou's picture
initial commit
da6ee5e verified
Raw History Blame Contribute Delete
2.94 kB
"""Sheetly FastAPI application entrypoint."""
from __future__ import annotations
import logging
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from app.api import batches, documents, export
from app.config import get_settings
from app.db import get_session_factory, init_db
from app.schemas import HealthResponse
from app.services.seed import seed_demo_if_empty
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
logger = logging.getLogger("sheetly")
@asynccontextmanager
async def lifespan(_: FastAPI):
"""Create folders, database, and demo seed on startup."""
settings = get_settings()
Path("data/db").mkdir(parents=True, exist_ok=True)
Path(settings.upload_path).mkdir(parents=True, exist_ok=True)
Path(settings.export_path).mkdir(parents=True, exist_ok=True)
await init_db()
factory = get_session_factory()
async with factory() as session:
seeded = await seed_demo_if_empty(session)
if seeded:
logger.info("Demo seed: %s files queued", seeded)
logger.info(
"Sheetly ready (openai=%s)",
settings.has_openai,
)
yield
settings = get_settings()
app = FastAPI(
title=settings.app_name,
version="1.0.0",
description=(
"Sheetly — senior document extraction to Excel / JSON. "
"Batch upload, vision+text routing, schema-safe fields, "
"confidence review, and structured exports."
),
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(batches.router)
app.include_router(documents.router)
app.include_router(export.router)
static_dir = Path(__file__).parent / "static"
app.mount(
"/static",
StaticFiles(directory=str(static_dir)),
name="static",
)
@app.get("/api/health", response_model=HealthResponse)
async def health() -> HealthResponse:
"""Health check."""
current = get_settings()
return HealthResponse(
status="ok",
app=current.app_name,
openai=current.has_openai,
confidence_threshold=current.confidence_threshold,
text_model=current.openai_model,
vision_model=current.openai_vision_model,
)
@app.get("/")
async def home() -> FileResponse:
"""Landing / upload page."""
return FileResponse(static_dir / "index.html")
@app.get("/validate")
async def validate_page() -> FileResponse:
"""Validation review screen."""
return FileResponse(static_dir / "validate.html")
@app.get("/dashboard")
async def dashboard_page() -> FileResponse:
"""Operator dashboard."""
return FileResponse(static_dir / "dashboard.html")