3.7 KiB
Backend
FastAPI service that validates, parses, and stores XLSForm-compatible Excel files. Deployed on Render.
Local Setup
cd backend/
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
uvicorn main:app --reload --port 8000
Swagger UI: http://localhost:8000/docs
Environment Variables
| Variable | Default | Description |
|---|---|---|
MONGODB_URL |
— | Full MongoDB connection string |
DATABASE_NAME |
mform_bulk_upload |
Database name |
FRONTEND_URL |
* |
Allowed CORS origin(s), comma-separated |
API Reference
POST /api/validate
Validate an Excel file's structure without storing anything.
Request: multipart/form-data — field file (.xls / .xlsx)
Response:
{
"valid": true,
"message": "File format is valid.",
"sheets": [{ "name": "Forms", "exists": true, "missing_columns": [] }],
"form_metadata": { "language": "en", "title": "My Form" },
"questions_count": 5,
"options_count": 15,
"errors": [],
"warnings": []
}
POST /api/forms/parse
Parse an Excel file and return the full tempData JSON without saving to the database.
Request: multipart/form-data — field file
Response: Array — [questionnaireResponse, formDefinition]
POST /api/upload
Parse and store one or more Excel files concurrently.
Request: multipart/form-data — field files (multiple)
Response: Array of stored form objects in tempData format.
GET /api/forms
List all stored forms.
{ "forms": [{ "id": "...", "title": "...", "language": "en", "version": "1.0", "created_at": "..." }], "count": 1 }
GET /api/forms/{form_id}
Retrieve a single form in full tempData format.
PUT /api/forms/{form_id}/update
Replace a form's data with a new Excel file. multipart/form-data — field file.
DELETE /api/forms/{form_id}
Delete one form and all its questions and options.
DELETE /api/forms
Delete all forms (bulk).
File Format
Three sheets are required:
Forms
| Column | Required | Notes |
|---|---|---|
| Language | Yes | ISO 639-1 code (en, fr, es, …) |
| Title | Yes | ≤ 255 characters |
| Version | No | Arbitrary string |
| (others) | No | Extracted dynamically with defaults |
Questions Info
| Column | Required | Notes |
|---|---|---|
| Order | Yes | Positive integer, unique |
| Title | Yes | ≤ 1000 characters |
| View Sequence | Yes | Positive integer |
| Input Type | Yes | 1–10 (see below) |
Answer Options
| Column | Required | Notes |
|---|---|---|
| Order | Yes | References a question Order |
| Id | Yes | Positive integer, unique per Order |
| Label | Yes | ≤ 500 characters |
Question Types
| Code | Type |
|---|---|
| 1 | text |
| 2 | select_one |
| 3 | select_multiple |
| 4 | integer |
| 5 | decimal |
| 6 | date |
| 7 | time |
| 8 | datetime |
| 9 | note |
| 10 | calculate |
Choice questions (types 2, 3) must have matching Answer Options rows. Orphaned options (no matching question) are a validation error.
Database Schema
forms { _id, title, language, version, created_at }
questions { _id, form_id, order, title, view_sequence, input_type, created_at }
options { _id, form_id, order, option_id, label, created_at }
Indexes: forms.created_at desc, questions.form_id, options.{form_id, order}.
Testing
# Unit + integration (mocked MongoDB)
pytest tests/backend -q
# Against live deployment (saves perf_results.json)
BACKEND_URL=https://bulk-questionnaire-upload.onrender.com python scripts/measure_perf.py
Test fixtures: tests/test_xlsforms_valid/ (10 valid files) · tests/test_xlsforms_incorrect/ (error scenarios).