Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion api/db/repositories.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
return form

def get_form(session: Session, submission_id: int) -> FormSubmission | None:
return session.get(FormSubmission, submission_id)
17 changes: 16 additions & 1 deletion api/main.py
Original file line number Diff line number Diff line change
@@ -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)
68 changes: 62 additions & 6 deletions api/routes/forms.py
Original file line number Diff line number Diff line change
@@ -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)
)
6 changes: 4 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ pydantic
sqlmodel
pytest
httpx
numpy<2
ollama
numpy>=1.24.0,<3
ollama
pypdf
python-multipart