---------------------------------------------------------------------- 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!