|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +microsetta-interface is the web UI for the Microsetta Initiative (primarily the American Gut Project). It's a Python Flask app (via Connexion/OpenAPI) that communicates with microsetta-private-api for data persistence and business logic. |
| 8 | + |
| 9 | +## Common Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +# Install (conda environment) |
| 13 | +conda create --yes -n microsetta-interface python=3.11 |
| 14 | +conda install --yes -n microsetta-interface --file ci/conda_requirements.txt |
| 15 | +conda activate microsetta-interface |
| 16 | +pip install -r ci/pip_requirements.txt |
| 17 | +pip install -e . --no-deps |
| 18 | + |
| 19 | +# Compile translation catalogs (required before running) |
| 20 | +pybabel compile -d microsetta_interface/translations --use-fuzzy |
| 21 | + |
| 22 | +# Run development server (localhost:8083) |
| 23 | +python microsetta_interface/server.py |
| 24 | + |
| 25 | +# Lint |
| 26 | +make lint # or: flake8 microsetta_interface |
| 27 | + |
| 28 | +# Run all tests |
| 29 | +make test # or: pytest |
| 30 | + |
| 31 | +# Run tests with coverage |
| 32 | +make test-cov # or: pytest --cov=microsetta_interface |
| 33 | + |
| 34 | +# Run a single test |
| 35 | +pytest microsetta_interface/tests/test_implementation.py::TestImplementation::test_get_source_redirect_on_source_prereqs_error |
| 36 | + |
| 37 | +# Verify package installs correctly |
| 38 | +make test-install |
| 39 | +``` |
| 40 | + |
| 41 | +## Integration Tests |
| 42 | + |
| 43 | +Integration tests (`test_integration.py`) require: |
| 44 | +- microsetta-private-api running with `disable_authentication: true` |
| 45 | +- PostgreSQL on port 5432 |
| 46 | +- Redis server running |
| 47 | +- JWT keys via `source keys_for_testing.sh` (sets `MICROSETTA_INTERFACE_DEBUG_JWT_PRIV`) |
| 48 | + |
| 49 | +Integration tests are run as a script (`python microsetta_interface/tests/test_integration.py`), not via pytest. |
| 50 | + |
| 51 | +## Architecture |
| 52 | + |
| 53 | +### Request Flow |
| 54 | + |
| 55 | +Connexion loads `routes.yaml` (OpenAPI 3.0 spec) which maps HTTP endpoints to functions in `implementation.py`. The Flask app is created in `server.py` via `build_app()`. |
| 56 | + |
| 57 | +### Prerequisite State Machine |
| 58 | + |
| 59 | +The `@prerequisite` decorator (`implementation.py:700`) enforces a workflow state machine on route handlers. Before a handler executes, the decorator checks the user's current state and redirects if prerequisites aren't met. States progress in order: |
| 60 | + |
| 61 | +`NeedsReroute` → `NeedsLogin` → `NeedsAccount` → `NeedsEmailCheck` → `NeedsPrimarySurveys` → `TokenPrereqsMet` → `AcctPrereqsMet` → `SourcePrereqsMet` → `BiospecimenPrereqsMet` |
| 62 | + |
| 63 | +Each route declares which states it accepts. Prerequisite checks are chained: `_check_home_prereqs` → `_check_acct_prereqs` → `_check_source_prereqs` → `_check_biospecimen_prereqs`. |
| 64 | + |
| 65 | +### Key Modules |
| 66 | + |
| 67 | +- **`implementation.py`** — All route handlers and business logic (~4000 lines). Manages JWT auth (Authrocket RS256), session state, and API calls to microsetta-private-api. |
| 68 | +- **`server.py`** — App factory (`build_app()`), Babel i18n setup, reverse proxy support. |
| 69 | +- **`routes.yaml`** — OpenAPI spec defining all endpoints and mapping to `implementation.py` functions. |
| 70 | +- **`config_manager.py`** — Loads `server_config.json` at import time into `SERVER_CONFIG`. |
| 71 | +- **`model_i18n.py`** — Translation utilities for source types, sample types, and survey labels. |
| 72 | +- **`redis_cache.py`** — Redis wrapper for system banner caching. |
| 73 | + |
| 74 | +### Internationalization (i18n) |
| 75 | + |
| 76 | +Four locales supported: `en_US`, `es_MX`, `es_ES`, `ja_JP`. Uses Flask-Babel with Jinja2 template extraction. |
| 77 | + |
| 78 | +To update translation files after changing translatable strings: |
| 79 | +```bash |
| 80 | +cd microsetta_interface |
| 81 | +pybabel extract -F ../babel.cfg -o translations/base.pot . |
| 82 | +pybabel update -i translations/base.pot -d translations |
| 83 | +``` |
| 84 | + |
| 85 | +### Frontend |
| 86 | + |
| 87 | +Server-side rendered Jinja2 templates with client-side Vue.js, jQuery, Bootstrap 5, and DataTables. Emperor is used for microbiome PCoA visualizations. All vendor JS/CSS is committed in `static/vendor/`. |
| 88 | + |
| 89 | +### Testing Patterns |
| 90 | + |
| 91 | +Unit tests (`test_implementation.py`) mock `requests.get`, `render_template`, and `session` via `unittest.mock.patch`. Tests use Flask's test client. The `TestResponse` helper class is used instead of MagicMock because tested code uses comparison operators on `status_code`. |
| 92 | + |
| 93 | +### Dependency Constraints |
| 94 | + |
| 95 | +connexion < 2.7.1 is the binding constraint for nearly all dependency ceilings. It uses private/removed APIs in Flask, Werkzeug, jsonschema, and openapi-spec-validator. Until connexion is upgraded to 3.x (a full rewrite), these ceilings apply: |
| 96 | + |
| 97 | +- Flask < 2.3.0 (2.3 removed `flask.json.JSONEncoder` used by connexion) |
| 98 | +- Werkzeug < 2.4.0 |
| 99 | +- jsonschema < 4.0.0 (connexion uses `jsonschema._utils.types_msg()` removed in 4.0) |
| 100 | +- openapi-spec-validator < 0.4.0 (0.4.0+ requires jsonschema >= 4) |
| 101 | +- setuptools < 82 (82+ removed `pkg_resources`, which connexion imports directly) |
| 102 | + |
| 103 | + |
| 104 | +### Configuration |
| 105 | + |
| 106 | +`server_config.json` holds runtime config: API endpoints, port, SSL, Authrocket URL, Flask secret key. The private API defaults to `localhost:8082/api`. |
0 commit comments