Processes invoice PDFs end-to-end in <3 s using Tesseract OCR + Django.
Upload any PDF or image invoice and get fully structured, editable JSON fields in seconds.
graph LR
Browser -->|"JWT REST /api"| Django
Django -->|"Celery task"| Worker
Worker --> Preprocess["Pillow / OpenCV\nPreprocess"]
Preprocess --> Tesseract["Tesseract OCR"]
Tesseract --> Extract["Regex Extract\n+ Normalize"]
Extract -->|"bulk_create"| DB[(PostgreSQL)]
Django -->|"read"| DB
Redis[(Redis)] <-->|"broker + results"| Worker
Nginx -->|"static"| Browser
Nginx -->|"proxy /api"| Django
| Directory | Role |
|---|---|
backend/ |
Django 4.2 + DRF REST API, Celery task runner |
frontend/ |
React 18 + Vite 5, Neumorphism design system |
sample_invoices/ |
Three synthetic demo invoices (PNG / JPG) |
.github/workflows/ |
CI: lint → pytest (75 % coverage gate) → Docker build |
Each upload triggers a Celery task that runs synchronously in eager mode during tests and asynchronously in production via Redis:
Upload
│
▼
Stage 1 — LOAD pdf2image / OpenCV imread
Stage 2 — PREPROCESS grayscale → adaptive threshold → deskew (Hough) → denoise
Stage 3 — OCR pytesseract --oem 3 --psm 6 (text + word-box confidence)
Stage 4 — EXTRACT 8 regex pattern groups, priority-ordered, first match wins
Stage 5 — NORMALIZE dates → ISO 8601 | amounts → float | symbols → ISO 4217
Stage 6 — SAVE bulk_create 9 ExtractedField rows; invoice.status → completed
Every stage writes a ProcessingLog row. On failure the invoice is marked failed and the task retries up to 3× with exponential backoff (60 s → 120 s → 240 s).
Prerequisites: Docker Desktop with Compose V2.
# 1. Clone
git clone https://github.com/your-org/ai-invoice-processor.git
cd ai-invoice-processor
# 2. Configure environment
cp backend/.env.example backend/.env
# Open backend/.env and set SECRET_KEY and any other required values
# 3. Start all services (first run builds images)
docker-compose up --build
# 4. Open the app
open http://localhost:3000The stack starts five services: PostgreSQL 15, Redis 7, Django API (port 8000), Celery worker, and the Vite dev server (port 3000).
To register your first account, open http://localhost:3000 and click Sign Up (or POST to /api/auth/register/).
All endpoints require Authorization: Bearer <access_token> except where noted.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/auth/register/ |
None | Create user account |
POST |
/api/auth/login/ |
None | Obtain JWT access + refresh tokens |
POST |
/api/auth/token/refresh/ |
None | Exchange refresh token for new access token |
GET |
/api/auth/me/ |
JWT | Retrieve / update current user profile |
GET |
/api/invoices/ |
JWT | Paginated invoice list; filter with ?status=pending|processing|completed|failed |
POST |
/api/invoices/upload/ |
JWT | Upload PDF / PNG / JPG (max 10 MB); triggers async processing |
GET |
/api/invoices/{id}/ |
JWT | Invoice detail including all extracted fields and processing logs |
GET |
/api/invoices/{id}/export/ |
JWT | Export fields as JSON (default) or CSV (?export_format=csv) |
PATCH |
/api/invoices/{id}/fields/{fid}/ |
JWT | Correct an extracted field (normalized_value, json_value); auto-sets is_verified=true |
GET |
/api/stats/ |
JWT | Aggregate counts, average confidence score, average processing time |
Backend
- Python 3.11, Django 4.2, Django REST Framework 3.15
- JWT authentication via
djangorestframework-simplejwt - Celery 5 + Redis — async task queue with retries
- PostgreSQL 15 — primary data store
- Tesseract OCR, pdf2image (Poppler), OpenCV, Pillow — document processing
Frontend
- React 18, Vite 5
- Tailwind CSS (layout / spacing utilities only)
- Neumorphism design system — inline styles with
#E7E5E4surface,6px 6px 12px #c8c6c4 / -6px -6px 12px #fffshadow pair - Space Mono (Google Fonts), react-dropzone, react-pdf, Axios with JWT interceptor
Infrastructure
- Docker Compose — five-service local stack
- Nginx — production static serving +
/apiproxy - GitHub Actions CI — lint → test (75 % coverage gate) → Docker build
cd backend
# Install runtime + test dependencies
pip install -r requirements.txt -r requirements-test.txt
# Apply migrations
python manage.py migrate
# Run tests (SQLite in-memory, Celery eager)
pytest
# Run tests with coverage report
pytest --cov=apps --cov-report=term-missing --cov-fail-under=75
# Lint
flake8 . --max-line-length=100 --exclude=migrationscd frontend
# Install dependencies
npm install
# Start Vite dev server (proxies /api to localhost:8000)
npm run dev
# Lint
npm run lint# Demo images → sample_invoices/*.png / *.jpg
python sample_invoices/generate.py
# Test fixture images → backend/tests/fixtures/*.png / *.jpg
python backend/tests/fixtures/generate.pyCopy backend/.env.example to backend/.env:
| Variable | Required | Default | Description |
|---|---|---|---|
SECRET_KEY |
Yes | — | Django secret key (generate with python -c "import secrets; print(secrets.token_urlsafe(50))") |
DATABASE_URL |
Yes | sqlite:///db.sqlite3 |
PostgreSQL connection string |
CELERY_BROKER_URL |
No | redis://redis:6379/0 |
Redis broker URL |
CELERY_RESULT_BACKEND |
No | redis://redis:6379/0 |
Redis result backend URL |
CORS_ALLOWED_ORIGINS |
No | http://localhost:3000 |
Comma-separated allowed origins |
DEBUG |
No | False |
Enable Django debug mode |
JWT_ACCESS_TOKEN_LIFETIME_MINUTES |
No | 60 |
Access token TTL in minutes |
JWT_REFRESH_TOKEN_LIFETIME_DAYS |
No | 7 |
Refresh token TTL in days |
GitHub Actions runs three jobs on every push to main and every pull request:
lint ──────────────────────────────► flake8 (backend) + ESLint (frontend)
│
test ──────────────────────────────► pytest + postgres service + coverage ≥ 75 %
│
build-docker (needs lint + test) ──► docker build backend + frontend images
Coverage is uploaded to Codecov. Set CODECOV_TOKEN in repository secrets to enable the integration.
ai-invoice-processor/
├── .github/workflows/ci.yml GitHub Actions CI
├── backend/
│ ├── apps/
│ │ ├── core/ User model, JWT auth endpoints
│ │ └── invoices/ Invoice model, OCR pipeline, REST views
│ ├── config/settings/ base / development / production / test / ci
│ ├── tests/
│ │ ├── fixtures/ Pillow-generated test images + generate.py
│ │ ├── test_pipeline_e2e.py Upload → process → export end-to-end
│ │ ├── test_confidence.py Confidence score range + model validators
│ │ └── test_field_correction.py PATCH semantics, access control
│ ├── conftest.py Shared pytest fixtures (images, auth clients)
│ ├── pytest.ini
│ └── requirements-test.txt
├── frontend/
│ ├── src/
│ │ ├── components/ Layout, StatCard, StatusBadge, ConfidenceBar
│ │ ├── pages/ AuthPage, DashboardPage, UploadPage,
│ │ │ InvoiceDetailPage, InvoiceListPage
│ │ └── styles/tokens.js Design token constants (T, S, typ)
│ └── eslint.config.js
├── sample_invoices/
│ └── generate.py Generates 3 demo invoice images with Pillow
└── docker-compose.yml
MIT — see LICENSE.