thbndi commited on
Commit
f32bb9c
·
verified ·
1 Parent(s): 132dd3a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -16
app.py CHANGED
@@ -13,6 +13,14 @@ Backing repos (private, read with the ``HF_TOKEN`` Space secret):
13
  Access control: the Space is public, but generation requires signing in with
14
  Hugging Face and being on the allowlist (the ``ALLOWLIST`` Space variable, a
15
  comma-separated list of usernames).
 
 
 
 
 
 
 
 
16
  """
17
 
18
  import os
@@ -20,7 +28,7 @@ import sys
20
  import tempfile
21
 
22
  import gradio as gr
23
- from huggingface_hub import snapshot_download
24
 
25
  TOKEN = os.environ.get("HF_TOKEN")
26
  CODE_REPO = os.environ.get("CODE_REPO", "thbndi/MoveTSA")
@@ -35,6 +43,12 @@ ALLOWLIST = {
35
  if u.strip()
36
  }
37
 
 
 
 
 
 
 
38
  # ----------------------------------------------------------------- startup
39
  # 1) Pipeline code → importable as the `MoveTSA` package (cwd on sys.path).
40
  snapshot_download(CODE_REPO, repo_type="dataset", token=TOKEN,
@@ -48,34 +62,73 @@ RAW_DIR = snapshot_download(RAW_REPO, repo_type="dataset", token=TOKEN,
48
  from MoveTSA.export_hf_dataset import build_windows # noqa: E402 (needs sys.path)
49
 
50
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  def generate(window_size, overlap, normalize, include_baselines,
52
- include_familiarization, profile: gr.OAuthProfile | None):
53
- """Run the pipeline with the chosen parameters and return a parquet file."""
54
- if profile is None:
 
 
 
 
 
 
 
 
55
  return None, "Sign in with your Hugging Face account to generate."
56
- if profile.username.lower() not in ALLOWLIST:
57
  return None, (
58
- f"Access not granted for **@{profile.username}**.\n\n"
59
  f"Request access from **@{OWNER}** (to be added to the allowlist)."
60
  )
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  df = build_windows(
63
  RAW_DIR,
64
- window_size=int(window_size),
65
- window_overlap=float(overlap),
66
  normalize=(None if normalize == "none" else normalize),
67
- include_baselines=bool(include_baselines),
68
- include_familiarization=bool(include_familiarization),
69
  verbose=False,
70
  )
71
 
72
- fname = (f"movetsa_w{int(window_size)}_ov{int(float(overlap) * 100)}"
73
- f"_{normalize}.parquet")
74
- out = os.path.join(tempfile.mkdtemp(), fname)
75
- df.to_parquet(out, index=False)
76
  msg = (f"**{len(df)} windows × {df.shape[1]} columns** "
77
  f"({df['subject'].nunique()} subjects) — `{fname}`")
78
- return out, msg
79
 
80
 
81
  with gr.Blocks(title="MoveTSA dataset builder") as demo:
@@ -108,7 +161,8 @@ with gr.Blocks(title="MoveTSA dataset builder") as demo:
108
  inputs=[window_size, overlap, normalize, include_baselines,
109
  include_familiarization],
110
  outputs=[out_file, status],
 
111
  )
112
 
113
  if __name__ == "__main__":
114
- demo.launch()
 
13
  Access control: the Space is public, but generation requires signing in with
14
  Hugging Face and being on the allowlist (the ``ALLOWLIST`` Space variable, a
15
  comma-separated list of usernames).
16
+
17
+ Programmatic access (gradio_client / the MovetsaDataset loading script):
18
+ - named API endpoint ``/generate`` with the 5 inputs below, in this order:
19
+ window_size, overlap, normalize, include_baselines, include_familiarization
20
+ - identity is injected by Gradio: in the browser via ``gr.OAuthProfile``;
21
+ over the API via ``gr.OAuthToken`` (resolved with ``whoami``). Callers just
22
+ pass ``hf_token=...`` to ``gradio_client.Client(...)``.
23
+ - requires ``hf_oauth: true`` in the Space README front-matter.
24
  """
25
 
26
  import os
 
28
  import tempfile
29
 
30
  import gradio as gr
31
+ from huggingface_hub import snapshot_download, whoami
32
 
33
  TOKEN = os.environ.get("HF_TOKEN")
34
  CODE_REPO = os.environ.get("CODE_REPO", "thbndi/MoveTSA")
 
43
  if u.strip()
44
  }
45
 
46
+ # Optional server-side cache: identical parameters reuse the same parquet
47
+ # instead of re-running the (heavy) pipeline. Keyed on ALL five parameters.
48
+ CACHE_DIR = os.environ.get("MOVETSA_CACHE",
49
+ os.path.join(tempfile.gettempdir(), "movetsa_out"))
50
+ os.makedirs(CACHE_DIR, exist_ok=True)
51
+
52
  # ----------------------------------------------------------------- startup
53
  # 1) Pipeline code → importable as the `MoveTSA` package (cwd on sys.path).
54
  snapshot_download(CODE_REPO, repo_type="dataset", token=TOKEN,
 
62
  from MoveTSA.export_hf_dataset import build_windows # noqa: E402 (needs sys.path)
63
 
64
 
65
+ def _resolve_username(profile, oauth_token):
66
+ """Identity from the browser OAuth profile, or from an API token."""
67
+ if profile is not None:
68
+ return profile.username
69
+ if oauth_token is not None and getattr(oauth_token, "token", None):
70
+ try:
71
+ return whoami(token=oauth_token.token).get("name")
72
+ except Exception: # noqa: BLE001
73
+ return None
74
+ return None
75
+
76
+
77
  def generate(window_size, overlap, normalize, include_baselines,
78
+ include_familiarization,
79
+ profile: gr.OAuthProfile | None = None,
80
+ oauth_token: gr.OAuthToken | None = None):
81
+ """Run the pipeline with the chosen parameters and return a parquet file.
82
+
83
+ ``profile`` (browser) and ``oauth_token`` (API) are injected by Gradio and
84
+ are NOT part of the API signature — the named endpoint only exposes the 5
85
+ parameters above.
86
+ """
87
+ username = _resolve_username(profile, oauth_token)
88
+ if username is None:
89
  return None, "Sign in with your Hugging Face account to generate."
90
+ if username.lower() not in ALLOWLIST:
91
  return None, (
92
+ f"Access not granted for **@{username}**.\n\n"
93
  f"Request access from **@{OWNER}** (to be added to the allowlist)."
94
  )
95
 
96
+ # --- validate parameters (API callers are not bound by the sliders) ---
97
+ try:
98
+ ws = int(window_size)
99
+ ov = float(overlap)
100
+ except (TypeError, ValueError):
101
+ return None, "Invalid parameters."
102
+ if not (15 <= ws <= 180):
103
+ return None, "window_size must be between 15 and 180 s."
104
+ if not (0.0 <= ov <= 0.9):
105
+ return None, "overlap must be between 0 and 0.9."
106
+ if normalize not in ("zscore", "center", "none"):
107
+ return None, "normalize must be 'zscore', 'center' or 'none'."
108
+ include_baselines = bool(include_baselines)
109
+ include_familiarization = bool(include_familiarization)
110
+
111
+ # Stable filename = also the cache key (now includes the B/F flags).
112
+ fname = (f"movetsa_w{ws}_ov{int(ov * 100)}_{normalize}"
113
+ f"_b{int(include_baselines)}_f{int(include_familiarization)}.parquet")
114
+ cached = os.path.join(CACHE_DIR, fname)
115
+ if os.path.exists(cached):
116
+ return cached, f"Served from cache — `{fname}`"
117
+
118
  df = build_windows(
119
  RAW_DIR,
120
+ window_size=ws,
121
+ window_overlap=ov,
122
  normalize=(None if normalize == "none" else normalize),
123
+ include_baselines=include_baselines,
124
+ include_familiarization=include_familiarization,
125
  verbose=False,
126
  )
127
 
128
+ df.to_parquet(cached, index=False)
 
 
 
129
  msg = (f"**{len(df)} windows × {df.shape[1]} columns** "
130
  f"({df['subject'].nunique()} subjects) — `{fname}`")
131
+ return cached, msg
132
 
133
 
134
  with gr.Blocks(title="MoveTSA dataset builder") as demo:
 
161
  inputs=[window_size, overlap, normalize, include_baselines,
162
  include_familiarization],
163
  outputs=[out_file, status],
164
+ api_name="generate", # => client.predict(..., api_name="/generate")
165
  )
166
 
167
  if __name__ == "__main__":
168
+ demo.launch()