diff --git a/api/db/repositories.py b/api/db/repositories.py index 6608718..9835fbd 100644 --- a/api/db/repositories.py +++ b/api/db/repositories.py @@ -11,9 +11,16 @@ def create_template(session: Session, template: Template) -> Template: def get_template(session: Session, template_id: int) -> Template | None: return session.get(Template, template_id) +def get_all_templates(session: Session, limit: int = 100, offset: int = 0) -> list[Template]: + statement = select(Template).offset(offset).limit(limit) + return session.exec(statement).all() + # Forms def create_form(session: Session, form: FormSubmission) -> FormSubmission: session.add(form) session.commit() session.refresh(form) - return form \ No newline at end of file + return form + +def get_form(session: Session, submission_id: int) -> FormSubmission | None: + return session.get(FormSubmission, submission_id) \ No newline at end of file diff --git a/api/main.py b/api/main.py index d0b8c79..6302d0d 100644 --- a/api/main.py +++ b/api/main.py @@ -1,7 +1,22 @@ from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware from api.routes import templates, forms +from api.errors.handlers import register_exception_handlers -app = FastAPI() +app = FastAPI( + title="FireForm API", + description="Report once, file everywhere.", + version="0.1.0" +) + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_methods=["*"], + allow_headers=["*"], +) + +register_exception_handlers(app) app.include_router(templates.router) app.include_router(forms.router) \ No newline at end of file diff --git a/api/routes/forms.py b/api/routes/forms.py index f3430ed..381583a 100644 --- a/api/routes/forms.py +++ b/api/routes/forms.py @@ -1,25 +1,81 @@ +import os from fastapi import APIRouter, Depends +from fastapi.responses import FileResponse from sqlmodel import Session from api.deps import get_db from api.schemas.forms import FormFill, FormFillResponse -from api.db.repositories import create_form, get_template +from api.db.repositories import create_form, get_template, get_form from api.db.models import FormSubmission from api.errors.base import AppError from src.controller import Controller router = APIRouter(prefix="/forms", tags=["forms"]) + @router.post("/fill", response_model=FormFillResponse) def fill_form(form: FormFill, db: Session = Depends(get_db)): - if not get_template(db, form.template_id): + # Single DB query (fixes issue #149 - redundant query) + template = get_template(db, form.template_id) + if not template: raise AppError("Template not found", status_code=404) - fetched_template = get_template(db, form.template_id) + try: + controller = Controller() + # FileManipulator.fill_form expects fields as a list of key strings + path = controller.fill_form( + user_input=form.input_text, + fields=template.fields, # Passes dict directly + pdf_form_path=template.pdf_path + ) + except ConnectionError: + raise AppError( + "Could not connect to Ollama. Make sure ollama serve is running.", + status_code=503 + ) + except Exception as e: + raise AppError(f"PDF filling failed: {str(e)}", status_code=500) + + # Guard: controller returned None instead of a file path + if not path: + raise AppError( + "PDF generation failed — no output file was produced. " + "Check that the PDF template is a valid fillable form and Ollama is running.", + status_code=500 + ) - controller = Controller() - path = controller.fill_form(user_input=form.input_text, fields=fetched_template.fields, pdf_form_path=fetched_template.pdf_path) + if not os.path.exists(path): + raise AppError( + f"PDF was generated but file not found at: {path}", + status_code=500 + ) - submission = FormSubmission(**form.model_dump(), output_pdf_path=path) + submission = FormSubmission( + **form.model_dump(), + output_pdf_path=path + ) return create_form(db, submission) +@router.get("/{submission_id}", response_model=FormFillResponse) +def get_submission(submission_id: int, db: Session = Depends(get_db)): + submission = get_form(db, submission_id) + if not submission: + raise AppError("Submission not found", status_code=404) + return submission + + +@router.get("/download/{submission_id}") +def download_filled_pdf(submission_id: int, db: Session = Depends(get_db)): + submission = get_form(db, submission_id) + if not submission: + raise AppError("Submission not found", status_code=404) + + file_path = submission.output_pdf_path + if not os.path.exists(file_path): + raise AppError("PDF file not found on server", status_code=404) + + return FileResponse( + path=file_path, + media_type="application/pdf", + filename=os.path.basename(file_path) + ) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index eaa6c81..5cf6c13 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,5 +8,7 @@ pydantic sqlmodel pytest httpx -numpy<2 -ollama \ No newline at end of file +numpy>=1.24.0,<3 +ollama +pypdf +python-multipart \ No newline at end of file