lanczos commited on
Commit
ea27f41
·
verified ·
1 Parent(s): 083fb75

deploy: labeling server

Browse files
labeling/static/app.js CHANGED
@@ -26,11 +26,19 @@ function initThemeToggle() {
26
  });
27
  }
28
 
 
 
 
 
 
 
 
 
29
  async function fetchJSON(path, init) {
30
  const resp = await fetch(path, init);
31
  if (!resp.ok) {
32
  const body = await resp.text();
33
- throw new Error(`${resp.status}: ${body}`);
34
  }
35
  return resp.json();
36
  }
@@ -45,15 +53,7 @@ function nextSessionCap() {
45
  return rounds === 0 ? FIRST_SESSION_CAP : REPEAT_SESSION_CAP;
46
  }
47
 
48
- async function ensureToken() {
49
- const urlToken = new URL(window.location.href).searchParams.get("token");
50
- if (urlToken) {
51
- localStorage.setItem(TOKEN_STORAGE_KEY, urlToken);
52
- return urlToken;
53
- }
54
- const stored = localStorage.getItem(TOKEN_STORAGE_KEY);
55
- if (stored) return stored;
56
- const cap = nextSessionCap();
57
  const resp = await fetch(`/api/register?cap=${cap}`, { method: "POST" });
58
  if (!resp.ok) {
59
  throw new Error(
@@ -65,6 +65,17 @@ async function ensureToken() {
65
  return token;
66
  }
67
 
 
 
 
 
 
 
 
 
 
 
 
68
  function renderProfileCard(idx, profile) {
69
  const ul = document.createElement("ul");
70
  ul.className = "profile";
@@ -162,22 +173,26 @@ async function submitLabel(token) {
162
  const chosen = document.querySelector("input[name=choice]:checked");
163
  if (!chosen || !currentItem) return;
164
  const elapsed = (performance.now() - shownAt) / 1000;
165
- try {
166
- await fetchJSON("/api/label", {
167
- method: "POST",
168
- headers: { "content-type": "application/json" },
169
- body: JSON.stringify({
170
- token,
171
- item_id: currentItem.item_id,
172
- chosen_index: Number(chosen.value),
173
- seconds: elapsed,
174
- confidence: null,
175
- }),
176
- });
177
- await loadNext(token);
178
- } catch (e) {
179
- err.textContent = `Submit failed: ${e.message}`;
180
- }
 
 
 
 
181
  }
182
 
183
  async function main() {
@@ -189,11 +204,31 @@ async function main() {
189
  document.getElementById("error").textContent = e.message;
190
  return;
191
  }
192
- document.getElementById("submit").addEventListener("click", () => submitLabel(token));
 
 
 
 
 
 
 
 
 
 
 
193
  try {
194
  await loadNext(token);
195
  } catch (e) {
196
- document.getElementById("error").textContent = `Load failed: ${e.message}`;
 
 
 
 
 
 
 
 
 
197
  }
198
  }
199
 
 
26
  });
27
  }
28
 
29
+ class HttpError extends Error {
30
+ constructor(status, body) {
31
+ super(`${status}: ${body}`);
32
+ this.status = status;
33
+ this.body = body;
34
+ }
35
+ }
36
+
37
  async function fetchJSON(path, init) {
38
  const resp = await fetch(path, init);
39
  if (!resp.ok) {
40
  const body = await resp.text();
41
+ throw new HttpError(resp.status, body);
42
  }
43
  return resp.json();
44
  }
 
53
  return rounds === 0 ? FIRST_SESSION_CAP : REPEAT_SESSION_CAP;
54
  }
55
 
56
+ async function registerFresh(cap) {
 
 
 
 
 
 
 
 
57
  const resp = await fetch(`/api/register?cap=${cap}`, { method: "POST" });
58
  if (!resp.ok) {
59
  throw new Error(
 
65
  return token;
66
  }
67
 
68
+ async function ensureToken() {
69
+ const urlToken = new URL(window.location.href).searchParams.get("token");
70
+ if (urlToken) {
71
+ localStorage.setItem(TOKEN_STORAGE_KEY, urlToken);
72
+ return urlToken;
73
+ }
74
+ const stored = localStorage.getItem(TOKEN_STORAGE_KEY);
75
+ if (stored) return stored;
76
+ return registerFresh(nextSessionCap());
77
+ }
78
+
79
  function renderProfileCard(idx, profile) {
80
  const ul = document.createElement("ul");
81
  ul.className = "profile";
 
173
  const chosen = document.querySelector("input[name=choice]:checked");
174
  if (!chosen || !currentItem) return;
175
  const elapsed = (performance.now() - shownAt) / 1000;
176
+ await fetchJSON("/api/label", {
177
+ method: "POST",
178
+ headers: { "content-type": "application/json" },
179
+ body: JSON.stringify({
180
+ token,
181
+ item_id: currentItem.item_id,
182
+ chosen_index: Number(chosen.value),
183
+ seconds: elapsed,
184
+ confidence: null,
185
+ }),
186
+ });
187
+ await loadNext(token);
188
+ }
189
+
190
+ async function recoverFromInvalidToken() {
191
+ // Server doesn't know this token (DB was reset, Space rebuild lost state,
192
+ // etc.). Wipe client state and start a fresh session.
193
+ localStorage.removeItem(TOKEN_STORAGE_KEY);
194
+ localStorage.removeItem(ROUNDS_KEY);
195
+ return registerFresh(FIRST_SESSION_CAP);
196
  }
197
 
198
  async function main() {
 
204
  document.getElementById("error").textContent = e.message;
205
  return;
206
  }
207
+ document.getElementById("submit").addEventListener("click", async () => {
208
+ try {
209
+ await submitLabel(token);
210
+ } catch (e) {
211
+ if (e instanceof HttpError && e.status === 401) {
212
+ token = await recoverFromInvalidToken();
213
+ await loadNext(token);
214
+ } else {
215
+ document.getElementById("error").textContent = `Submit failed: ${e.message}`;
216
+ }
217
+ }
218
+ });
219
  try {
220
  await loadNext(token);
221
  } catch (e) {
222
+ if (e instanceof HttpError && e.status === 401) {
223
+ try {
224
+ token = await recoverFromInvalidToken();
225
+ await loadNext(token);
226
+ } catch (e2) {
227
+ document.getElementById("error").textContent = `Load failed: ${e2.message}`;
228
+ }
229
+ } else {
230
+ document.getElementById("error").textContent = `Load failed: ${e.message}`;
231
+ }
232
  }
233
  }
234
 
labeling/static/index.html CHANGED
@@ -41,6 +41,6 @@
41
  <span id="error"></span>
42
  </footer>
43
  </main>
44
- <script src="/app.js?v=9"></script>
45
  </body>
46
  </html>
 
41
  <span id="error"></span>
42
  </footer>
43
  </main>
44
+ <script src="/app.js?v=10"></script>
45
  </body>
46
  </html>