Skip to content

Commit ef982a0

Browse files
authored
Merge pull request #354 from biocore/2026.03.16-techdebt
2026.03.16 techdebt
2 parents 6767798 + 4921881 commit ef982a0

21 files changed

Lines changed: 250 additions & 2546 deletions

.flake8

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[flake8]
2+
max-line-length = 79

.github/workflows/main.yml

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ jobs:
1010
lint:
1111
runs-on: ubuntu-22.04
1212
steps:
13-
- uses: actions/checkout@v2
14-
- name: set up python 3.7
15-
uses: actions/setup-python@v2
13+
- uses: actions/checkout@v6
1614
with:
17-
python-version: 3.7
15+
fetch-depth: 0
16+
- name: set up python 3.11
17+
uses: actions/setup-python@v5
18+
with:
19+
python-version: 3.11
1820
- name: lint it
1921
run: python -m pip install --upgrade pip
2022
- name: lint
@@ -24,18 +26,20 @@ jobs:
2426
test:
2527
runs-on: ubuntu-22.04
2628
steps:
27-
- uses: actions/checkout@v2
29+
- uses: actions/checkout@v6
30+
with:
31+
fetch-depth: 0
2832

2933
- name: Setup for conda
30-
uses: conda-incubator/setup-miniconda@v2
31-
with:
34+
uses: conda-incubator/setup-miniconda@v3
35+
with:
3236
auto-update-conda: true
33-
python-version: 3.7
34-
37+
python-version: 3.11
38+
3539
- name: Install
3640
shell: bash -l {0}
3741
run: |
38-
conda create --yes -n test-microsetta-interface python=3.7
42+
conda create --yes -n test-microsetta-interface python=3.11
3943
conda activate test-microsetta-interface
4044
conda install --yes --file ci/conda_requirements.txt
4145
pip install -r ci/pip_requirements.txt
@@ -69,29 +73,40 @@ jobs:
6973
ports:
7074
# based on https://github.com/actions/example-services/blob/master/.github/workflows/postgres-service.yml#L44-L72
7175
- 5432/tcp
76+
redis:
77+
image: redis
78+
ports:
79+
- 6379:6379
80+
options: >-
81+
--health-cmd "redis-cli ping"
82+
--health-interval 10s
83+
--health-timeout 5s
84+
--health-retries 5
7285
7386
steps:
7487
# Downloads a copy of the code in your repository before running CI tests
7588
- name: Check out repository code
76-
uses: actions/checkout@v2
89+
uses: actions/checkout@v6
90+
with:
91+
fetch-depth: 0
7792

7893
- name: Setup for conda
79-
uses: conda-incubator/setup-miniconda@v2
80-
with:
94+
uses: conda-incubator/setup-miniconda@v3
95+
with:
8196
auto-update-conda: true
82-
python-version: 3.7
97+
python-version: 3.11
8398

8499
- name: Install
85100
env:
86101
BRANCH: ${{ github.base_ref == 'master-overhaul' && 'master-overhaul' || 'master' }}
87102
shell: bash -l {0}
88103
run: |
89104
echo Branch: $BRANCH
90-
conda create --yes -n microsetta-interface python=3.7
105+
conda create --yes -n microsetta-interface python=3.11
91106
conda activate microsetta-interface
92107
conda install --yes --file ci/conda_requirements.txt
93108
pip install -r ci/pip_requirements.txt
94-
python setup.py compile_catalog
109+
pybabel compile -d microsetta_interface/translations --use-fuzzy
95110
pip install -e . --no-deps
96111
97112
git clone -b $BRANCH --single-branch https://github.com/biocore/microsetta-private-api.git
@@ -102,7 +117,7 @@ jobs:
102117
# PGPASSWORD is read by pg_restore, which is called by the build_db process.
103118
export PGPASSWORD=postgres
104119
105-
conda create --yes -n microsetta-private-api python=3.7
120+
conda create --yes -n microsetta-private-api python=3.11
106121
conda activate microsetta-private-api
107122
conda install --yes --file ci/conda_requirements.txt
108123
pip install -r ci/pip_requirements.txt
@@ -122,7 +137,6 @@ jobs:
122137
popd
123138
124139
conda activate microsetta-interface
125-
redis-server --daemonize yes
126140
source keys_for_testing.sh
127141
python microsetta_interface/tests/test_integration.py 2> >(tee -a stderr.log >&2)
128142

.travis.yml

Lines changed: 0 additions & 28 deletions
This file was deleted.

CLAUDE.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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`.

MANIFEST.in

Lines changed: 0 additions & 4 deletions
This file was deleted.

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test-cov: all
2222
py.test --cov=microsetta_interface
2323

2424
install: all
25-
$(PYTHON) setup.py install
25+
pip install .
2626

2727
dev: all
2828
pip install -e .

ci/conda_requirements.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
flake8
2-
flask < 3.0.0
2+
flask >= 2.2.0, < 2.3.0
33
natsort
44
pycryptodome
55
redis-py
6-
redis
76
pytest
87
flask-babel=2.0.0
98
pandas
9+
pytz
10+
werkzeug >= 2.2.2, < 2.4.0
11+
jsonschema >= 3.2.0, < 4.0.0

ci/pip_requirements.txt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
openapi-spec-validator < 0.2.10
2-
swagger-ui-bundle==0.0.9
2+
swagger-ui-bundle >= 0.0.2, < 0.1
33
connexion[swagger-ui] < 2.7.1
4-
pyjwt[crypto] < 2.2.0
5-
pytest < 5.3.4
4+
pyjwt[crypto] >= 2.12.0, < 3.0.0
5+
pytest
66
pytest-cov
77
coveralls
88
pycountry
99
python-dateutil
10+
redis
11+
setuptools-scm >= 8
12+
setuptools < 82

microsetta_interface/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11

2-
from ._version import get_versions
3-
__version__ = get_versions()['version']
4-
del get_versions
2+
from importlib.metadata import version, PackageNotFoundError
3+
try:
4+
__version__ = version("microsetta-interface")
5+
except PackageNotFoundError:
6+
__version__ = "0.0.0"

0 commit comments

Comments
 (0)