----------------------------------------------------------------------
This is the API documentation for the pyreporter library.
----------------------------------------------------------------------
## Classes
Main classes provided by the package
MetaRepository()
SessionCache()
## Exceptions
Exception classes
MetadataNotAvailableError
## Functions
Utility functions
create_plotlist(meta_list, snr, year, audience, report, data, ubb=False, export=True)
Export all plots for a report by calling export_plot for each item in meta_list.
Parameters
----------
meta_list : list[str]
List of plot names (e.g. ['A11', 'A12', ...]).
snr : str
School number.
year : str or int
School year.
audience : str
Audience type (e.g. 'sus', 'elt').
report : str
Report template name.
data : pd.DataFrame
Long-format survey response data.
ubb : bool
Whether this is a classroom observation report.
export : bool
If True, saves plots to PDF; if False, returns plot objects.
Returns
-------
list
List of return values from export_plot (None when export=True).
get_metadata(meta_templates: pandas.DataFrame, meta_reports: pandas.DataFrame, school: str, audience: str, ub: bool, gt: bool, data_avail: Iterable[str] | None = None) -> Dict[str, Any]
Get meta data for a report template.
limer_SIDs(snr, ubb)
Get metadata of surveys filtered by school number and UBB flag.
Parameters
----------
snr : str
School number (first 4 digits of survey title).
ubb : bool
Whether 'ubb' must appear in the survey title.
Returns
-------
pd.DataFrame
Surveys metadata with completed responses.
limer_call(method, params=None, **request_kwargs)
Make a call to the LimeSurvey API.
Parameters
----------
method : str
API method to call (e.g. "list_surveys").
params : dict, optional
Parameters to pass to the API method.
**request_kwargs
Additional keyword arguments passed to requests.post().
Returns
-------
Any
Result returned by the API (may be plain text, dict, list, etc.).
limer_connect()
Connect to LimeSurvey.
Returns
-------
str
Session ID
limer_list_surveys()
List surveys from LimeSurvey.
Returns
-------
pandas.DataFrame
A DataFrame with the list of surveys from LimeSurvey.
limer_n(id)
Get the number of completed responses of a LimeSurvey survey.
Parameters
----------
id : int
LimeSurvey survey ID.
Returns
-------
pd.DataFrame
DataFrame with columns: 'sid' and 'completed_responses'.
limer_release()
Release a LimeSurvey session key.
Clears the LimeSurvey API session key currently in use, effectively logging out.
limer_responses(iSurveyID, sDocumentType='csv', sLanguageCode='', sCompletionStatus='complete', sHeadingType='code', sResponseType='long', **kwargs)
Get LimeSurvey survey responses as a pandas DataFrame.
Uses an empty string for language code to retrieve data without checking language.
limer_sessionkey(username=None, password=None)
Get LimeSurvey API Session Key
Logs into the LimeSurvey API and returns an access session key.
Parameters
----------
username : str, optional
LimeSurvey username. Defaults to environment variable LIME_USERNAME.
password : str, optional
LimeSurvey password. Defaults to environment variable LIME_PASSWORD.
Returns
-------
str
API session key
create_ggplot(data: pandas.DataFrame, ubb: bool, labels: dict)
export_plot(meta, snr, audience, report, data, ubb=False, year=None, export=True)
Export a plot for the OES report.
Parameters
----------
meta : str
Plot name / ID (e.g., 'A12', 'A3a', etc.).
snr : str or int
School number.
audience : str
Audience type.
report : str
Report template name.
data : pd.DataFrame
Preprocessed long-format plot data with 'vars', 'vals', 'label_short', 'set'.
ubb : bool
Flag passed if UBB or not.
year : str or int
School year (used for directory).
export : bool
If True, saves the plot to PDF; else returns the plot.
get_directory(snr, syear)
Returns the main directory of the report results.
Parameters
----------
snr : str or int
School number.
syear : str or int
Recent school year from LS Title.
Returns
-------
str
Absolute path to the report directory.
get_plotdata(data: pandas.DataFrame, report_name: str, plot_name: str, audience: str)
get_data(id, surveyls_title, ubb)
Get response data from LimeSurvey in long format.
Parameters
----------
id : int or str
Survey ID
surveyls_title : str
Survey title
ubb : any
UBB (not used here but kept for compatibility)
Returns
-------
pandas.DataFrame
## Constants
Module-level constants and data
limer.session_cache
## Other
Additional exports
----------------------------------------------------------------------
This is the User Guide documentation for the package.
----------------------------------------------------------------------
## Getting Started
### Installation
This guide will help you install PyReporter and set up your development environment.
## Requirements
PyReporter requires:
- Python 3.14 or later
- Poetry (for dependency management)
- Quarto (for PDF generation)
## Install Poetry
If you don't have Poetry installed, follow the [official Poetry installation guide](https://python-poetry.org/docs/#installation):
```bash
curl -sSL https://install.python-poetry.org | python3 -
```
## Install Quarto
PyReporter uses Quarto to render PDF reports. Download and install Quarto from:
Or use a package manager:
::: {.panel-tabset}
## macOS
```bash
brew install quarto
```
## Linux
```bash
# Ubuntu/Debian
sudo apt-get install quarto
# Or download from https://quarto.org/docs/download/
```
## Windows
Download the installer from
:::
## Install PyReporter
### From Source
Clone the repository and install dependencies:
```bash
git clone
cd pyreporter
poetry install
```
This will install all required dependencies including pandas, plotnine, and quarto integration.
### Development Dependencies
To install development dependencies (including documentation tools):
```bash
poetry install --with dev,docs
```
## Configure LimeSurvey Credentials
PyReporter connects to LimeSurvey via its JSON-RPC API. You need to configure your credentials:
1. Copy the example environment file:
```bash
cp .env.example .env
```
2. Edit `.env` with your LimeSurvey credentials:
```bash
LIME_API_URL=https://your-limesurvey-instance.com/admin/remotecontrol
LIME_USERNAME=your_username
LIME_PASSWORD=your_password
```
:::{.callout-warning}
Never commit your `.env` file to version control. It contains sensitive credentials.
:::
## Verify Installation
Test that everything is installed correctly:
```bash
# Test the package imports
poetry run python -c "from pyreporter import MetaRepository; print('✓ Import successful')"
# Test Quarto is available
quarto --version
```
## Next Steps
Now that PyReporter is installed, continue to [Getting Started](getting-started.qmd) to learn how to generate your first report.
### Getting Started
This guide walks you through generating your first school evaluation report with PyReporter.
## Prerequisites
Before you begin, make sure you have:
1. [Installed PyReporter and its dependencies](installation.qmd)
2. Configured your LimeSurvey credentials in `.env`
3. Access to a LimeSurvey instance with survey data
## Understanding the Pipeline
PyReporter follows a multi-stage pipeline:
1. **Survey Discovery**: Connects to LimeSurvey and finds relevant surveys
2. **Data Export**: Downloads survey responses in CSV format
3. **Data Processing**: Transforms responses into normalized long-format data
4. **Metadata Joining**: Enriches data with labels and configuration from CSV metadata
5. **Visualization**: Generates plotnine charts as PDFs
6. **Report Assembly**: Renders final PDF using Quarto templates
## Running Your First Report
### Basic Usage
The simplest way to run the pipeline is with the default configuration:
```bash
poetry run python -m pyreporter.run
```
This will use environment variables to configure the report generation.
### Using the Makefile
For more convenient usage, PyReporter includes a Makefile with default values:
```bash
make run
```
This uses the following defaults:
- `SNR=0001` - School number
- `STYPE=gy` - School type (Gymnasium)
- `AUDIENCE=leh` - Target audience (teachers)
- `UBB=False` - UBB mode disabled
- `GANZTAG=False` - Full-day school mode disabled
- `HAS_N=sus,leh` - Available audience types
- `YEAR=2025` - Report year
### Customizing Parameters
Override any parameter when running the pipeline:
```bash
# Generate report for a different school
make run SNR=0042 STYPE=rs AUDIENCE=sus
# Generate UBB report with multiple audiences
make run SNR=0001 UBB=True HAS_N=sus,leh,elt AUDIENCE=all
# Generate full-day school report
make run SNR=0123 GANZTAG=True AUDIENCE=elt YEAR=2026
```
## Configuration Parameters
| Parameter | Description | Example Values |
|-----------|-------------|----------------|
| `SNR` | School number (zero-padded string) | `0001`, `0042`, `0123` |
| `STYPE` | School type | `gy` (Gymnasium), `rs` (Realschule) |
| `AUDIENCE` | Target audience | `sus` (students), `leh` (teachers), `elt` (parents), `ubb` (supervision), `all` |
| `UBB` | UBB supervision mode | `True`, `False` |
| `GANZTAG` | Full-day school mode | `True`, `False` |
| `HAS_N` | Available audience types (comma-separated) | `sus,leh`, `sus,leh,elt` |
| `YEAR` | Report year | `2024`, `2025`, `2026` |
## Output Structure
Reports are generated in the `res/` directory:
```
res/
└── 0001_2025/ # {SNR}_{YEAR}
├── template.qmd # Quarto template (copied from templates/)
├── params.yml # Report parameters
├── header_*.pdf # Header graphics
├── plots/ # Generated visualizations
│ ├── plot_*.pdf
│ └── ...
└── report.pdf # Final rendered report
```
## Exploring Test Scripts
PyReporter includes several test scripts for development and debugging:
```bash
# Print metadata sets from MetaRepository
poetry run python -m pyreporter.test.test
# Test metadata template selection
poetry run python -m pyreporter.test.test2
# Test PDF rendering from existing assets
poetry run python -m pyreporter.test.test_render
# Test plotnine visualization (opens a window)
poetry run python -m pyreporter.test.test_plot
# Test LimeSurvey API connection (requires .env)
poetry run python -m pyreporter.test.limer_test
```
:::{.callout-note}
The `test` module is for development checks, not pytest tests. These scripts help verify components work correctly.
:::
## Cleaning Generated Files
Remove all generated reports:
```bash
make clean
```
This deletes everything under `res/` while preserving the directory structure.
## Next Steps
- Explore the [API Reference](../../reference/index.qmd) to understand the core classes and functions
- Review the metadata CSV files in `pyreporter/data/` to customize report behavior
- Examine the Quarto templates in `pyreporter/templates/` to modify report layout
- Learn about the [MetaRepository](../../reference/MetaRepository.qmd) for managing metadata
## Common Issues
### LimeSurvey Connection Fails
Check your `.env` file has the correct credentials and API URL:
```bash
LIME_API_URL=https://your-instance.com/admin/remotecontrol
```
### Missing Metadata
Ensure all required CSV files exist in `pyreporter/data/`:
- `meta_templates.csv`
- `meta_reports.csv`
- `meta_sets.csv`
- `meta_headers.csv`
- `meta_mastertotemplate.csv`
- `meta_snames.csv`
### Quarto Rendering Errors
Verify Quarto is installed and accessible:
```bash
quarto --version
```
Check that `plots/` directory exists and contains the expected PDF files before rendering
# FastAPI Quick Reference
## Start Server
```bash
# Development (auto-reload)
make api-dev
# Production
make api
```
## Access Points
- **Interactive Docs**: http://localhost:8000/docs
- **Alternative Docs**: http://localhost:8000/redoc
- **Health Check**: http://localhost:8000/health
## Endpoints
### 1. List Available Plots
```bash
GET /api/v1/plots/list?snr=0001&stype=gy&audience=sus
```
### 2. Fetch Raw Data
```bash
POST /api/v1/raw-data
Body: {"snr": "0001", "stype": "gy", "audience": "sus"}
```
### 3. Prepare Data
```bash
POST /api/v1/prepared-data
Body: {"snr": "0001", "stype": "gy", "audience": "sus"}
```
### 4. Generate Plot
```bash
POST /api/v1/plot
Body: {"snr": "0001", "stype": "gy", "audience": "sus", "plot_name": "A12"}
Returns: PDF file
```
### 5. Create Report
```bash
POST /api/v1/report
Body: {"snr": "0001", "stype": "gy", "audience": "sus", "year": "2025"}
Returns: PDF file
```
## cURL Examples
```bash
# Health check
curl http://localhost:8000/health
# List plots
curl "http://localhost:8000/api/v1/plots/list?snr=0001&stype=gy&audience=sus"
# Fetch data
curl -X POST http://localhost:8000/api/v1/raw-data \
-H "Content-Type: application/json" \
-d '{"snr": "0001", "stype": "gy", "audience": "sus"}'
# Generate plot
curl -X POST http://localhost:8000/api/v1/plot \
-H "Content-Type: application/json" \
-d '{"snr": "0001", "stype": "gy", "audience": "sus", "plot_name": "A12"}' \
--output plot.pdf
# Create report
curl -X POST http://localhost:8000/api/v1/report \
-H "Content-Type: application/json" \
-d '{"snr": "0001", "stype": "gy", "audience": "sus", "year": "2025"}' \
--output report.pdf
```
## Python Example
```python
import requests
# Start with health check
response = requests.get("http://localhost:8000/health")
print(response.json())
# Fetch raw data
response = requests.post(
"http://localhost:8000/api/v1/raw-data",
json={
"snr": "0001",
"stype": "gy",
"audience": "sus"
}
)
data = response.json()
print(f"Fetched {data['rows']} rows")
# Generate report
response = requests.post(
"http://localhost:8000/api/v1/report",
json={
"snr": "0001",
"stype": "gy",
"audience": "sus",
"year": "2025"
}
)
# Save PDF
with open("report.pdf", "wb") as f:
f.write(response.content)
```
## Common Parameters
| Parameter | Type | Example | Description |
|-----------|------|---------|-------------|
| `snr` | string | "0001" | School number (zero-padded) |
| `stype` | string | "gy" | School type |
| `audience` | string | "sus" | Target: sus/elt/leh/ubb/aus/all |
| `ubb` | boolean | false | UBB flag |
| `ganztag` | boolean | false | Full-day flag |
| `has_N` | array | ["sus","leh"] | Available audiences |
| `use_cache` | boolean | true | Use cached data |
| `plot_name` | string | "A12" | Specific plot ID |
| `year` | string | "2025" | Report year |
## Testing
```bash
# Run test suite
poetry run python test_api_setup.py
# Run examples
poetry run python example_api_client.py
```
## Documentation
- **Full API Docs**: [API_README.md](API_README.md)
- **Implementation**: [FASTAPI_SUMMARY.md](FASTAPI_SUMMARY.md)
- **Main README**: [README.md](README.md)
# PyReporter API
FastAPI service layer for the pyreporter survey report generation pipeline.
## Quick Start
### Installation
```bash
# Install dependencies including FastAPI
poetry install
```
### Starting the API Server
```bash
# Development server with auto-reload
poetry run uvicorn pyreporter.api:app --reload --host 0.0.0.0 --port 8000
# Or using Python directly
poetry run python -m pyreporter.api
```
The API will be available at:
- **API Base**: http://localhost:8000
- **API Docs (Swagger)**: http://localhost:8000/docs
- **API Docs (ReDoc)**: http://localhost:8000/redoc
## API Endpoints
### 1. Get Raw Data
**POST** `/api/v1/raw-data`
Fetch raw survey data from LimeSurvey with caching support.
**Request Body:**
```json
{
"snr": "0001",
"stype": "gy",
"audience": "sus",
"ubb": false,
"ganztag": false,
"has_N": ["sus", "leh"],
"use_cache": true
}
```
**Response:**
```json
{
"status": "success",
"message": "Fetched raw data for school 0001",
"rows": 150,
"syear": "2025",
"result_n": "150",
"columns": ["sid", "surveyls_title", "id", "vars", "vals"],
"data_preview": [...]
}
```
**Example:**
```bash
curl -X POST http://localhost:8000/api/v1/raw-data \
-H "Content-Type: application/json" \
-d '{
"snr": "0001",
"stype": "gy",
"audience": "sus",
"ubb": false,
"ganztag": false,
"has_N": ["sus", "leh"]
}'
```
### 2. Get Prepared Data
**POST** `/api/v1/prepared-data`
Transform raw data into plot-ready format with metadata.
**Request Body:**
```json
{
"snr": "0001",
"stype": "gy",
"audience": "sus",
"ubb": false,
"ganztag": false,
"has_N": ["sus", "leh"],
"use_cache": true
}
```
**Response:**
```json
{
"status": "success",
"message": "Prepared data for school 0001",
"plots_count": 25,
"sname": "Example School",
"syear": "2025",
"report_name": "gy_report",
"plots_available": ["A12", "A3a", "B01", ...]
}
```
**Example:**
```bash
curl -X POST http://localhost:8000/api/v1/prepared-data \
-H "Content-Type: application/json" \
-d '{
"snr": "0001",
"stype": "gy",
"audience": "sus"
}'
```
### 3. Generate a Single Plot
**POST** `/api/v1/plot`
Generate a specific plot and return as PDF.
**Request Body:**
```json
{
"snr": "0001",
"stype": "gy",
"audience": "sus",
"plot_name": "A12",
"ubb": false,
"ganztag": false,
"has_N": ["sus", "leh"],
"use_cache": true
}
```
**Response:**
Returns PDF file download.
**Example:**
```bash
curl -X POST http://localhost:8000/api/v1/plot \
-H "Content-Type: application/json" \
-d '{
"snr": "0001",
"stype": "gy",
"audience": "sus",
"plot_name": "A12"
}' \
--output plot_A12.pdf
```
### 4. Create Complete Report
**POST** `/api/v1/report`
Generate complete PDF report with all plots.
**Request Body:**
```json
{
"snr": "0001",
"stype": "gy",
"audience": "sus",
"ubb": false,
"ganztag": false,
"has_N": ["sus", "leh"],
"year": "2025",
"duration": "2",
"use_cache": true
}
```
**Response:**
Returns complete PDF report.
**Example:**
```bash
curl -X POST http://localhost:8000/api/v1/report \
-H "Content-Type: application/json" \
-d '{
"snr": "0001",
"stype": "gy",
"audience": "sus",
"year": "2025"
}' \
--output report.pdf
```
### 5. List Available Plots
**GET** `/api/v1/plots/list`
List all available plots for a configuration (lightweight, no data fetching).
**Query Parameters:**
- `snr`: School number (required)
- `stype`: School type (required)
- `audience`: Target audience (required)
- `ubb`: UBB flag (default: false)
- `ganztag`: Full-day school flag (default: false)
**Example:**
```bash
curl "http://localhost:8000/api/v1/plots/list?snr=0001&stype=gy&audience=sus"
```
**Response:**
```json
{
"status": "success",
"snr": "0001",
"report": "gy_report",
"audience": "sus",
"plots": ["A12", "A3a", "B01", ...],
"count": 25
}
```
### 6. Health Check
**GET** `/health`
Simple health check endpoint.
**Example:**
```bash
curl http://localhost:8000/health
```
## Request Parameters
### Common Parameters
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `snr` | string | Yes | - | School number (zero-padded) |
| `stype` | string | Yes | - | School type (e.g., "gy") |
| `audience` | string | Yes | - | Target audience: sus, elt, leh, ubb, aus, all |
| `ubb` | boolean | No | false | UBB flag |
| `ganztag` | boolean | No | false | Full-day school flag |
| `has_N` | array[string] | No | ["sus", "leh"] | Available audiences |
| `use_cache` | boolean | No | true | Use cached data |
### Plot-Specific Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `plot_name` | string | Yes | Plot ID (e.g., "A12", "A3a") |
### Report-Specific Parameters
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `year` | string | No | "2025" | Report year |
| `duration` | string | No | "2" | Survey duration |
## Audience Codes
- `sus` - Students (Schülerinnen und Schüler)
- `elt` - Parents (Eltern)
- `leh` - Teachers (Lehrkräfte)
- `ubb` - Classroom observations (Unterrichtsbeobachtungen)
- `aus` - Trainers (Ausbilder)
- `all` - All groups
## School Types
- `gy` - Gymnasium
- (Other types as defined in your metadata)
## Error Handling
All endpoints return standard HTTP status codes:
- **200** - Success
- **404** - Resource not found (e.g., plot doesn't exist)
- **500** - Internal server error
Error response format:
```json
{
"detail": "Error message describing what went wrong"
}
```
## Caching
The API uses the same caching system as the CLI pipeline:
- **Raw data cache**: `.cache/raw_*.pkl` (cached by `snr`, `ubb`)
- **Prepared data cache**: `.cache/prepared_*.pkl` (cached by all params)
To bypass cache:
```bash
# Set use_cache to false in request
{
"snr": "0001",
"use_cache": false
}
# Or clear cache manually
make clean-cache
```
## Production Deployment
### Using Gunicorn
```bash
poetry run gunicorn pyreporter.api:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000
```
### Using Docker
```dockerfile
FROM python:3.14-slim
WORKDIR /app
COPY . /app
RUN pip install poetry && \
poetry config virtualenvs.create false && \
poetry install --no-dev
# Ensure Quarto is installed
RUN apt-get update && apt-get install -y quarto
EXPOSE 8000
CMD ["uvicorn", "pyreporter.api:app", "--host", "0.0.0.0", "--port", "8000"]
```
### Environment Variables
Required (same as CLI):
```bash
LIME_API_URL=https://your-limesurvey.com/admin/remotecontrol
LIME_USERNAME=your_username
LIME_PASSWORD=your_password
```
Optional:
```bash
NO_CACHE=true # Disable caching globally
```
## Testing the API
### Using the Interactive Docs
1. Start the server: `poetry run uvicorn pyreporter.api:app --reload`
2. Open http://localhost:8000/docs
3. Click "Try it out" on any endpoint
4. Fill in parameters and click "Execute"
### Using Python Requests
```python
import requests
# Fetch raw data
response = requests.post(
"http://localhost:8000/api/v1/raw-data",
json={
"snr": "0001",
"stype": "gy",
"audience": "sus"
}
)
data = response.json()
print(f"Fetched {data['rows']} rows")
# Generate report
response = requests.post(
"http://localhost:8000/api/v1/report",
json={
"snr": "0001",
"stype": "gy",
"audience": "sus",
"year": "2025"
}
)
# Save PDF
with open("report.pdf", "wb") as f:
f.write(response.content)
```
## Architecture
The API is a thin wrapper around the existing pyreporter pipeline:
```
API Request → FastAPI → pyreporter modules → Response
↓
┌─────────────────┐
│ fetch.py │ Fetches raw data
├─────────────────┤
│ prepare.py │ Prepares plot data
├─────────────────┤
│ plot.py │ Generates plots
├─────────────────┤
│ render_pdf.py │ Renders PDF
└─────────────────┘
```
Each endpoint reuses the existing pipeline functions with proper error handling and response formatting.
# API Endpoint Test Results
## Test Configuration
Using Makefile default values:
```json
{
"snr": "0001",
"stype": "gy",
"audience": "leh",
"ubb": false,
"ganztag": false,
"has_N": ["sus", "leh"],
"year": "2025"
}
```
## Test Results Summary
✅ **6/6 tests PASSED** - All endpoints working correctly
---
## 1. Health Check ✅
**Endpoint:** `GET /health`
**Status:** 200 OK
**Response:**
```json
{
"status": "healthy",
"service": "pyreporter"
}
```
---
## 2. List Available Plots ✅
**Endpoint:** `GET /api/v1/plots/list`
**Status:** 200 OK
**Key Results:**
- Report template: `rpt_leh_p1`
- Total plots: **37**
- Sample plots: A42, W24, A51, W13, W25, W14, A52, A53, B11, B12...
**Response Structure:**
```json
{
"status": "success",
"snr": "0001",
"report": "rpt_leh_p1",
"audience": "leh",
"plots": [...],
"count": 37
}
```
---
## 3. Fetch Raw Data ✅
**Endpoint:** `POST /api/v1/raw-data`
**Status:** 200 OK
**Key Results:**
- Data rows: **2,150**
- Survey year: **2024**
- Sample size: **10**
- Columns: sid, surveyls_title, id, vars, vals
**Response Structure:**
```json
{
"status": "success",
"message": "Fetched raw data for school 0001",
"rows": 2150,
"syear": "2024",
"result_n": "10",
"columns": [...],
"data_preview": [...]
}
```
---
## 4. Prepare Data ✅
**Endpoint:** `POST /api/v1/prepared-data`
**Status:** 200 OK
**Key Results:**
- School: **Leibniz-Gymnasium Altdorf**
- Survey year: **2024**
- Report template: **rpt_leh_p1**
- Plots prepared: **37**
**Response Structure:**
```json
{
"status": "success",
"message": "Prepared data for school 0001",
"plots_count": 37,
"sname": "Leibniz-Gymnasium Altdorf",
"syear": "2024",
"report_name": "rpt_leh_p1",
"plots_available": [...]
}
```
---
## 5. Generate Single Plot ✅
**Endpoint:** `POST /api/v1/plot`
**Status:** 200 OK
**Test Parameters:**
- Plot name: **A42**
- Audience: **leh** (teachers)
**Key Results:**
- Content-Type: **application/pdf**
- PDF size: **17,148 bytes** (~17 KB)
- File generated: `test_plot_A42.pdf`
**File saved successfully** ✓
---
## 6. Generate Complete Report ✅
**Endpoint:** `POST /api/v1/report`
**Status:** 200 OK
**Test Parameters:**
- School: **0001** (Leibniz-Gymnasium Altdorf)
- Audience: **leh** (teachers)
- Year: **2025**
- Duration: **2**
**Key Results:**
- Content-Type: **application/pdf**
- PDF size: **1,072,593 bytes** (~1.05 MB)
- File generated: `test_report_leh.pdf`
- Total plots in report: **37**
**Complete report generated successfully** ✓
---
## Issues Identified and Fixed
### Issue 1: `list_plots` endpoint error
**Problem:** Method `get_template()` didn't exist in `MetaRepository`
**Fix:** Updated endpoint to use direct DataFrame filtering with `_as_bool()` normalization, matching the approach used in `utils.py`
**Status:** ✅ Fixed
### Issue 2: Plot file not found
**Problem:** Generated plot files named `{plot}_plot.pdf` but API looked for `{plot}.pdf`
**Fix:** Updated API endpoint to look for correct filename format matching `export_plot()` function
**Status:** ✅ Fixed
### Issue 3: Report endpoint "definitely not working"
**Result:** Report endpoint was actually **working perfectly** - generated 1.05 MB PDF with all 37 plots
**Status:** ✅ Working (no fix needed)
---
## Performance Notes
- **Raw data fetch**: Fast (uses cache after first request)
- **Data preparation**: Fast (uses cache after first request)
- **Single plot generation**: Fast (~1-2 seconds)
- **Complete report generation**: Moderate (~30-60 seconds for 37 plots + PDF assembly)
---
## Caching Behavior
All endpoints use the existing caching system:
- Raw data cached by `(snr, ubb)`
- Prepared data cached by `(snr, stype, audience, ubb, ganztag, has_N)`
- Set `use_cache: false` to bypass cache
---
## Files Generated During Testing
1. `test_plot_A42.pdf` - Single plot (17 KB)
2. `test_report_leh.pdf` - Complete report (1.05 MB)
3. `res/0001_2024/plots/` - 37 individual plot PDFs
4. `res/0001_2024/0001_results_leh.pdf` - Final report
---
## Conclusion
✅ **All API endpoints are fully functional and tested**
The FastAPI service layer successfully wraps the pyreporter pipeline with proper error handling, validation, and response formatting. All endpoints work correctly with the Makefile default values.
**Ready for production use!**
# FastAPI Service Layer Implementation Summary
## Overview
Added a complete FastAPI service layer to pyreporter, providing REST API endpoints for all pipeline stages.
## Files Created
### 1. `/pyreporter/api.py` (13.6 KB)
Main FastAPI application with the following endpoints:
- **GET** `/` - API root with service information
- **GET** `/health` - Health check endpoint
- **POST** `/api/v1/raw-data` - Fetch raw survey data
- **POST** `/api/v1/prepared-data` - Prepare plot-ready data
- **POST** `/api/v1/plot` - Generate a single plot (returns PDF)
- **POST** `/api/v1/report` - Create complete PDF report
- **GET** `/api/v1/plots/list` - List available plots for a configuration
### 2. `/API_README.md` (8.2 KB)
Comprehensive API documentation including:
- Quick start guide
- Detailed endpoint documentation
- Request/response examples
- cURL and Python examples
- Production deployment instructions
- Docker configuration
- Error handling guide
### 3. `/test_api_setup.py` (3.0 KB)
Automated test script to verify:
- All required packages are installed
- API module imports correctly
- All endpoints are properly registered
### 4. `/example_api_client.py` (5.5 KB)
Example client demonstrating:
- Health check
- Listing available plots
- Fetching raw data
- Preparing data
- Generating plots
- Creating reports
### 5. Updated `/Makefile`
Added API-related commands:
```bash
make api # Start production server
make api-dev # Start development server with auto-reload
```
### 6. Updated `/README.md`
Added API documentation section with:
- Quick start for API usage
- Link to detailed API_README.md
- Common API workflows
- Testing instructions
### 7. Updated `/pyproject.toml`
Added dependencies:
- `fastapi (>=0.115.0,<0.116.0)`
- `uvicorn[standard] (>=0.32.0,<0.33.0)`
- `pydantic (>=2.10.0,<3.0.0)`
## API Architecture
```
Client Request
↓
FastAPI Layer (api.py)
↓
┌────────────────────────────────────┐
│ Existing pyreporter modules │
├────────────────────────────────────┤
│ fetch.py → Raw data │
│ prepare.py → Plot-ready data │
│ plot.py → Chart generation │
│ render_pdf.py → PDF assembly │
└────────────────────────────────────┘
↓
Response (JSON or PDF)
```
## Key Features
### 1. Request Validation
- Uses Pydantic models for type-safe request validation
- Automatic parameter validation and error messages
- Clear field descriptions and examples
### 2. Response Models
- Structured JSON responses for data endpoints
- File downloads for plot/report endpoints
- Consistent error handling
### 3. Caching Support
- Reuses existing cache infrastructure
- `use_cache` parameter on all endpoints
- Cache invalidation via `make clean-cache`
### 4. Error Handling
- Proper HTTP status codes (200, 404, 500)
- Detailed error messages
- Exception handling at all levels
### 5. Documentation
- Auto-generated OpenAPI/Swagger docs at `/docs`
- ReDoc alternative at `/redoc`
- Interactive API testing interface
## Usage Examples
### Start Server
```bash
# Development mode (auto-reload)
make api-dev
# Production mode
make api
```
### Access Documentation
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
### Example Requests
#### Fetch Raw Data
```bash
curl -X POST http://localhost:8000/api/v1/raw-data \
-H "Content-Type: application/json" \
-d '{"snr": "0001", "stype": "gy", "audience": "sus"}'
```
#### Generate Plot
```bash
curl -X POST http://localhost:8000/api/v1/plot \
-H "Content-Type: application/json" \
-d '{"snr": "0001", "stype": "gy", "audience": "sus", "plot_name": "A12"}' \
--output plot.pdf
```
#### Create Report
```bash
curl -X POST http://localhost:8000/api/v1/report \
-H "Content-Type: application/json" \
-d '{"snr": "0001", "stype": "gy", "audience": "sus", "year": "2025"}' \
--output report.pdf
```
## Testing
### Automated Tests
```bash
# Verify API setup
poetry run python test_api_setup.py
# Run example client
poetry run python example_api_client.py
```
### Manual Testing
1. Start server: `make api-dev`
2. Open: http://localhost:8000/docs
3. Click "Try it out" on any endpoint
4. Fill parameters and click "Execute"
## Integration Points
The API is a thin wrapper around existing pipeline functions:
- `fetch_raw_data()` from `fetch.py`
- `prepare_data()` from `prepare.py`
- `export_plot()` and `create_plotlist()` from `plot.py`
- `render_pdf()` from `render_pdf.py`
- `create_directories()` from `utils.py`
- `MetaRepository` from `meta_repository.py`
No changes were made to existing pipeline code - the API reuses everything as-is.
## Production Deployment
### Using Gunicorn
```bash
poetry run gunicorn pyreporter.api:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000
```
### Docker
See API_README.md for complete Dockerfile
### Environment
Requires same `.env` configuration as CLI:
```bash
LIME_API_URL=https://your-limesurvey.com/admin/remotecontrol
LIME_USERNAME=your_username
LIME_PASSWORD=your_password
```
## Benefits
1. **Programmatic Access**: Use pyreporter from any language
2. **Web Integration**: Embed in web applications
3. **Microservice Architecture**: Deploy as independent service
4. **API-First**: Build UIs on top of the API
5. **Testing**: Easier automated testing via HTTP
6. **Monitoring**: Standard HTTP metrics and logging
## Future Enhancements
Potential additions for future versions:
1. **Authentication**: JWT or API key authentication
2. **Rate Limiting**: Prevent abuse
3. **Async Processing**: Background job queue for long reports
4. **WebSockets**: Real-time progress updates
5. **Batch Operations**: Generate multiple reports in one request
6. **Caching Headers**: HTTP-level caching
7. **Metrics**: Prometheus endpoints
8. **Admin API**: Cache management, system status
## Compatibility
- ✅ Python 3.14+
- ✅ All existing CLI functionality preserved
- ✅ Same caching system
- ✅ Same metadata repository
- ✅ Same environment configuration
## Installation Verified
```bash
✓ FastAPI installed successfully
✓ Uvicorn installed successfully
✓ Pydantic installed successfully
✓ API module imported successfully
✓ All 7 endpoints registered
```
## Next Steps
1. Start the server: `make api-dev`
2. Visit: http://localhost:8000/docs
3. Try the interactive API documentation
4. Run example client: `poetry run python example_api_client.py`
5. Integrate with your applications!