Favour Nerrise commited on
Commit
37ffe53
·
1 Parent(s): d79a495

Bring up FastAPI leaderboard backend

Browse files
Files changed (5) hide show
  1. Dockerfile +18 -0
  2. README.md +59 -5
  3. app.py +62 -0
  4. leaderboard.json +45 -0
  5. requirements.txt +2 -0
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MyHeartCounts-Bench leaderboard — HF Docker Space
2
+ # Listens on $PORT (HF Spaces default 7860). Serves /api/data with CORS for the
3
+ # public site at myheartcounts.stanford.edu.
4
+
5
+ FROM python:3.11-slim
6
+
7
+ WORKDIR /app
8
+
9
+ COPY requirements.txt .
10
+ RUN pip install --no-cache-dir -r requirements.txt
11
+
12
+ COPY app.py leaderboard.json ./
13
+
14
+ # HF Spaces sets $PORT, default to 7860 for local runs
15
+ ENV PORT=7860
16
+ EXPOSE 7860
17
+
18
+ CMD ["sh", "-c", "uvicorn app:app --host 0.0.0.0 --port ${PORT}"]
README.md CHANGED
@@ -1,11 +1,65 @@
1
  ---
2
- title: MyHeartCounts
3
- emoji: 👀
4
  colorFrom: red
5
- colorTo: purple
6
  sdk: docker
 
7
  pinned: false
8
- license: openrail
9
  ---
10
 
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: MyHeartCounts-Bench Leaderboard
3
+ emoji: 🫀
4
  colorFrom: red
5
+ colorTo: gray
6
  sdk: docker
7
+ app_port: 7860
8
  pinned: false
9
+ license: mit
10
  ---
11
 
12
+ # MyHeartCounts-Bench leaderboard Space
13
+
14
+ This is the API backend behind the leaderboard on the public site at
15
+ [myheartcounts.stanford.edu/benchmark](https://myheartcounts.stanford.edu/benchmark).
16
+
17
+ ## What this Space serves
18
+
19
+ A single endpoint:
20
+
21
+ - `GET /api/data` — returns the JSON shape documented in
22
+ [data/leaderboard/README.md of the website repo](https://github.com/NarayanSchuetz/myheartcounts.stanford.edu/blob/main/data/leaderboard/README.md).
23
+
24
+ Plus health/info:
25
+
26
+ - `GET /` — service info
27
+ - `GET /health` — liveness check
28
+
29
+ ## Files
30
+
31
+ | file | role |
32
+ |---|---|
33
+ | `Dockerfile` | Builds the container HF Spaces runs |
34
+ | `app.py` | FastAPI app, ~50 LOC |
35
+ | `requirements.txt` | `fastapi` + `uvicorn` |
36
+ | `leaderboard.json` | The canonical leaderboard data |
37
+
38
+ ## Updating the leaderboard
39
+
40
+ 1. Edit `leaderboard.json`. Keep the schema (see linked README); JSON-validate.
41
+ 2. Commit + push to this Space (HF git remote).
42
+ 3. HF rebuilds the container; the new JSON is live on the next `/api/data` call.
43
+
44
+ The website's 5-second fetch on page mount picks it up — no website redeploy.
45
+
46
+ ## Local dev
47
+
48
+ ```bash
49
+ pip install -r requirements.txt
50
+ uvicorn app:app --reload --port 7860
51
+ curl http://localhost:7860/api/data | jq .
52
+ ```
53
+
54
+ ## Cross-origin
55
+
56
+ `app.py` allows the public site origin and localhost dev. If the site moves to
57
+ a different origin, add it to `ALLOWED_ORIGINS` in `app.py`.
58
+
59
+ ## Submission ingestion
60
+
61
+ GitHub issues filed via the [submission template](https://github.com/kellywzhang/MHC-benchmark/blob/master/.github/ISSUE_TEMPLATE/submission.yml)
62
+ contain `method_name`, `submitter_team`, metrics, etc. A future workflow
63
+ (or a manual edit) appends them as new rows under `downstream` /
64
+ `imputation` / `forecasting`. Until that workflow exists, submission
65
+ ingestion is manual: edit `leaderboard.json`, commit, push.
app.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FastAPI app for the MyHeartCounts-Bench leaderboard HF Space.
2
+
3
+ Serves a single JSON blob at /api/data — the canonical leaderboard, matching
4
+ the schema documented in data/leaderboard/README.md of the public website
5
+ repo. Updates flow as `git push` to this Space (or programmatic commits).
6
+
7
+ Cross-origin: the public website at https://myheartcounts.stanford.edu
8
+ fetches from https://myheartcounts-myheartcounts-bench.hf.space/api/data.
9
+ """
10
+
11
+ import json
12
+ import os
13
+ from pathlib import Path
14
+
15
+ from fastapi import FastAPI, HTTPException
16
+ from fastapi.middleware.cors import CORSMiddleware
17
+
18
+ DATA_PATH = Path(__file__).parent / "leaderboard.json"
19
+ ALLOWED_ORIGINS = [
20
+ "https://myheartcounts.stanford.edu",
21
+ "https://www.myheartcounts.stanford.edu",
22
+ "http://localhost:3000",
23
+ "http://localhost:5173",
24
+ ]
25
+
26
+ app = FastAPI(
27
+ title="MyHeartCounts-Bench leaderboard",
28
+ description="Read-only leaderboard JSON for the /benchmark page on the public site.",
29
+ version="1.0.0",
30
+ )
31
+
32
+ app.add_middleware(
33
+ CORSMiddleware,
34
+ allow_origins=ALLOWED_ORIGINS,
35
+ allow_methods=["GET", "OPTIONS"],
36
+ allow_headers=["*"],
37
+ max_age=3600,
38
+ )
39
+
40
+
41
+ @app.get("/")
42
+ def root():
43
+ return {
44
+ "name": "MyHeartCounts-Bench leaderboard",
45
+ "endpoints": ["/api/data", "/health"],
46
+ "schema_doc": "https://github.com/NarayanSchuetz/myheartcounts.stanford.edu/blob/main/data/leaderboard/README.md",
47
+ }
48
+
49
+
50
+ @app.get("/api/data")
51
+ def get_data():
52
+ if not DATA_PATH.exists():
53
+ raise HTTPException(status_code=503, detail="leaderboard.json missing on disk")
54
+ try:
55
+ return json.loads(DATA_PATH.read_text())
56
+ except json.JSONDecodeError as e:
57
+ raise HTTPException(status_code=500, detail=f"leaderboard.json invalid: {e}") from e
58
+
59
+
60
+ @app.get("/health")
61
+ def health():
62
+ return {"status": "ok", "data_present": DATA_PATH.exists()}
leaderboard.json ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema_version": 1,
3
+ "generated_at": "2026-04-15",
4
+ "downstream": [
5
+ { "method": "LSM-2", "type": "Self-Supervised", "skill": "+14.4", "fair_skill": "+7.6", "rank": "2.76", "demographics": "1.87", "medical": "3.40", "body": "3.74", "mental": "2.86", "sleep": "1.95", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
6
+ { "method": "XGBoost", "type": "Tree Boosting", "skill": "+12.7", "fair_skill": "+5.4", "rank": "3.41", "demographics": "1.14", "medical": "4.38", "body": "2.59", "mental": "6.02", "sleep": "2.92", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
7
+ { "method": "Mamba-CON", "type": "Self-Supervised", "skill": "+4.2", "fair_skill": "-1.4", "rank": "4.27", "demographics": "3.63", "medical": "4.65", "body": "4.02", "mental": "4.45", "sleep": "4.58", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
8
+ { "method": "GRU-D", "type": "Deep Learning", "skill": "+2.2", "fair_skill": "-4.7", "rank": "4.89", "demographics": "4.69", "medical": "3.93", "body": "3.62", "mental": "6.22", "sleep": "5.98", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
9
+ { "method": "Linear (baseline)", "type": "Statistical", "skill": "0.0", "fair_skill": "-6.2", "rank": "4.59", "demographics": "6.98", "medical": "3.99", "body": "4.28", "mental": "3.19", "sleep": "4.52", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
10
+ { "method": "MultiRocket", "type": "Deep Learning", "skill": "-0.4", "fair_skill": "-7.2", "rank": "5.18", "demographics": "4.22", "medical": "4.83", "body": "5.90", "mental": "5.52", "sleep": "5.43", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
11
+ { "method": "Chronos-2", "type": "Foundation Model","skill": "-3.8", "fair_skill": "-9.9", "rank": "5.13", "demographics": "6.50", "medical": "4.83", "body": "5.87", "mental": "2.70", "sleep": "5.77", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
12
+ { "method": "Toto 1.0", "type": "Foundation Model","skill": "-4.8", "fair_skill": "-12.7", "rank": "5.76", "demographics": "6.98", "medical": "5.99", "body": "5.98", "mental": "5.03", "sleep": "4.85", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" }
13
+ ],
14
+ "imputation": [
15
+ { "type": "h1", "text": "Daily (single-day) methods" },
16
+ { "type": "method", "method": "LSM-2 (daily)", "mtype": "Self-Supervised", "skill": "+80.9", "fair_skill": "+79.0", "rank": "2.5", "activity": "+40.4", "physiology": "+33.5", "sleep": "+91.9", "workout": "+93.1", "semantic": "+47.6", "sectionRank": 1, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
17
+ { "type": "method", "method": "Linear", "mtype": "Statistical", "skill": "+29.7", "fair_skill": "+18.5", "rank": "7.8", "activity": "+11.7", "physiology": "+14.0", "sleep": "+66.4", "workout": "+36.7", "semantic": "+7.8", "sectionRank": 2, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
18
+ { "type": "method", "method": "DLinear", "mtype": "Deep Learning", "skill": "+18.5", "fair_skill": "+8.2", "rank": "7.9", "activity": "+20.6", "physiology": "+1.4", "sleep": "-12.3", "workout": "+21.6", "semantic": "+29.0", "sectionRank": 3, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
19
+ { "type": "method", "method": "Temporal mean", "mtype": "Statistical", "skill": "+0.2", "fair_skill": "-6.4", "rank": "8.6", "activity": "+12.9", "physiology": "-1.5", "sleep": "+2.4", "workout": "-17.1", "semantic": "+21.7", "sectionRank": 4, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
20
+ { "type": "method", "method": "LOCF (baseline)", "mtype": "Statistical", "skill": "0.0", "fair_skill": "-9.1", "rank": "10.4", "activity": "0.0", "physiology": "0.0", "sleep": "0.0", "workout": "0.0", "semantic": "0.0", "sectionRank": 5, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
21
+ { "type": "method", "method": "BRITS", "mtype": "Deep Learning", "skill": "-11.4", "fair_skill": "-15.4", "rank": "10.4", "activity": "-17.4", "physiology": "-32.0", "sleep": "+27.8", "workout": "-13.8", "semantic": "-12.8", "sectionRank": 6, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
22
+ { "type": "method", "method": "TimesNet", "mtype": "Deep Learning", "skill": "-17.5", "fair_skill": "-22.1", "rank": "11.0", "activity": "+19.9", "physiology": "-5.0", "sleep": "-170.7", "workout": "-41.2", "semantic": "+19.1", "sectionRank": 7, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
23
+ { "type": "method", "method": "Temporal mode", "mtype": "Statistical", "skill": "-22.9", "fair_skill": "-28.1", "rank": "11.9", "activity": "+9.2", "physiology": "-12.2", "sleep": "-71.0", "workout": "-61.8", "semantic": "+23.0", "sectionRank": 8, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
24
+ { "type": "method", "method": "FEDformer", "mtype": "Deep Learning", "skill": "-29.1", "fair_skill": "-34.6", "rank": "12.7", "activity": "+10.5", "physiology": "-9.2", "sleep": "-217.9", "workout": "-61.0", "semantic": "+22.0", "sectionRank": 9, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
25
+ { "type": "method", "method": "Mode", "mtype": "Statistical", "skill": "-32.6", "fair_skill": "-37.9", "rank": "12.1", "activity": "+9.2", "physiology": "-11.2", "sleep": "-305.7", "workout": "-61.8", "semantic": "+22.6", "sectionRank": 10, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
26
+ { "type": "method", "method": "Mean", "mtype": "Statistical", "skill": "-34.1", "fair_skill": "-38.7", "rank": "11.7", "activity": "+11.9", "physiology": "-4.9", "sleep": "-305.7", "workout": "-61.8", "semantic": "+10.7", "sectionRank": 11, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
27
+ { "type": "h1", "text": "Personalized-context (extended-history) methods" },
28
+ { "type": "method", "method": "LSM-2-Sparse (7-day)", "mtype": "Self-Supervised", "skill": "+86.3", "fair_skill": "+85.1", "rank": "1.1", "activity": "+42.2", "physiology": "+38.5", "sleep": "+94.2", "workout": "+96.3", "semantic": "+49.7", "sectionRank": 1, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
29
+ { "type": "method", "method": "Pers. temp. mean", "mtype": "Statistical", "skill": "+68.9", "fair_skill": "+61.3", "rank": "4.6", "activity": "+16.7", "physiology": "+13.1", "sleep": "+66.4", "workout": "+88.7", "semantic": "+29.3", "sectionRank": 2, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
30
+ { "type": "method", "method": "Pers. mean", "mtype": "Statistical", "skill": "+31.0", "fair_skill": "+15.9", "rank": "7.5", "activity": "+12.7", "physiology": "+2.1", "sleep": "-175.6", "workout": "+60.3", "semantic": "+12.3", "sectionRank": 3, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
31
+ { "type": "method", "method": "DLinear (7-day)", "mtype": "Deep Learning", "skill": "+5.3", "fair_skill": "-2.6", "rank": "8.8", "activity": "+20.1", "physiology": "+0.3", "sleep": "-39.2", "workout": "-3.8", "semantic": "+26.6", "sectionRank": 4, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
32
+ { "type": "method", "method": "Pers. mode", "mtype": "Statistical", "skill": "-32.1", "fair_skill": "-37.2", "rank": "11.6", "activity": "+9.2", "physiology": "-10.0", "sleep": "-301.4", "workout": "-61.7", "semantic": "+23.5", "sectionRank": 5, "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" }
33
+ ],
34
+ "forecasting": [
35
+ { "method": "Chronos-2", "mtype": "Foundation Model", "skill": "+25.8", "fair_skill": "+21.1", "rank": "2.4", "step": "+25.2", "flights": "+34.4", "hr": "+32.2", "energy": "+24.3", "sleep": "+59.2", "workout": "+14.6", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
36
+ { "method": "DLinear", "mtype": "Deep Learning", "skill": "+23.9", "fair_skill": "+18.4", "rank": "3.2", "step": "+24.5", "flights": "+24.3", "hr": "+23.9", "energy": "+24.2", "sleep": "+61.7", "workout": "+11.9", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
37
+ { "method": "MixLinear", "mtype": "Deep Learning", "skill": "+21.0", "fair_skill": "+15.0", "rank": "4.1", "step": "+21.5", "flights": "+18.6", "hr": "+21.5", "energy": "+20.0", "sleep": "+55.8", "workout": "+11.0", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
38
+ { "method": "SegRNN", "mtype": "Deep Learning", "skill": "+20.7", "fair_skill": "+16.7", "rank": "3.8", "step": "+25.9", "flights": "+19.1", "hr": "+26.4", "energy": "+26.2", "sleep": "+59.2", "workout": "+6.7", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
39
+ { "method": "Toto 1.0 (FT)", "mtype": "Foundation Model", "skill": "+19.0", "fair_skill": "+14.8", "rank": "4.2", "step": "+23.6", "flights": "+34.6", "hr": "+31.4", "energy": "+25.3", "sleep": "+47.7", "workout": "+6.1", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
40
+ { "method": "Toto 1.0", "mtype": "Foundation Model", "skill": "+17.2", "fair_skill": "+13.0", "rank": "4.7", "step": "+22.5", "flights": "+35.1", "hr": "-1.9", "energy": "+20.3", "sleep": "+49.4", "workout": "+6.5", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
41
+ { "method": "Seasonal Naive (baseline)", "mtype": "Statistical", "skill": "0.0", "fair_skill": "-4.8", "rank": "7.2", "step": "0.0", "flights": "0.0", "hr": "0.0", "energy": "0.0", "sleep": "0.0", "workout": "0.0", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
42
+ { "method": "AutoARIMA", "mtype": "Statistical", "skill": "-4.2", "fair_skill": "-9.2", "rank": "8.2", "step": "0.0", "flights": "-3.8", "hr": "-2.4", "energy": "-5.1", "sleep": "+20.2", "workout": "-11.0", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" },
43
+ { "method": "AutoETS", "mtype": "Statistical", "skill": "-21.4", "fair_skill": "-33.4", "rank": "7.2", "step": "+1.4", "flights": "-0.7", "hr": "-11.3", "energy": "-3078.9", "sleep": "+38.9", "workout": "+1.1", "submitter": "MHC-Bench team", "submitted_on": "2026-04", "paper_url": "", "code_url": "" }
44
+ ]
45
+ }
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastapi==0.110.3
2
+ uvicorn[standard]==0.29.0