devaanand commited on
Commit
9fce59c
·
1 Parent(s): ed98758

Refine documentation and fix upload handling

Browse files

- Update MANUAL_TESTING.md to clarify parsing behavior for multi-column files.
- Correct title and formatting in guide.html and index.html for consistency.
- Enhance README.md to specify behavior for skipped duplicates and blank lines.
- Implement unique ID assignment for corpus uploads in main.py to prevent file clobbering.
- Add regression test in test_api.py to ensure distinct uploads do not overwrite each other.

MANUAL_TESTING.md CHANGED
@@ -63,7 +63,8 @@ with a "Sign in (top right)" message. Sign in and retry: accepted.
63
  2. Try a CSV with `item,reverse` columns (1/true/yes/R = reverse) or a
64
  single-column file with `(R)` markers.
65
  3. Expect: items fill the textarea ((R) appended where flagged), the filename
66
- becomes the suggested name, and parse notes list skipped blanks/duplicates.
 
67
  Nothing is saved until you review and press Save. Item files are never
68
  retained on the server.
69
 
 
63
  2. Try a CSV with `item,reverse` columns (1/true/yes/R = reverse) or a
64
  single-column file with `(R)` markers.
65
  3. Expect: items fill the textarea ((R) appended where flagged), the filename
66
+ becomes the suggested name, and parse notes list skipped duplicates (and
67
+ blank cells in multi-column files; fully blank lines are dropped at read).
68
  Nothing is saved until you review and press Save. Item files are never
69
  retained on the server.
70
 
backend/app/guide.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="utf-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>CCR Platform Testing Guide</title>
7
  <style>
8
  :root {
9
  --bg: #ffffff; --fg: #1a1a1a; --muted: #666; --border: #ddd;
@@ -49,16 +49,16 @@
49
  <body>
50
  <main>
51
 
52
- <h1>CCR Platform Testing Guide</h1>
53
  <p class="sub">Everything built so far, as click-through scenarios: what to do and exactly what
54
- you should see. Every dataset referenced is downloadable right here all texts are
55
  synthetic, written for this kit; no real participant data anywhere.</p>
56
 
57
  <div class="note">
58
- <b>⚠ This is the free dev instance two caveats before you start:</b>
59
  <ul>
60
  <li><b>Storage is ephemeral.</b> Accounts, projects, and saved runs are wiped whenever
61
- the instance rebuilds or restarts. If your account disappears, that's expected
62
  just re-register. (Google sign-in recreates itself automatically.)</li>
63
  <li><b>It sleeps when idle.</b> After ~48&nbsp;h without visitors, the first page load
64
  takes about a minute to wake it up.</li>
@@ -138,12 +138,12 @@ are deleted after analysis. Upload
138
  <ol>
139
  <li>"+ Custom construct" &gt; "Upload items from CSV/XLSX".</li>
140
  <li>Try <a href="/samples/construct_items_demo.csv" download>construct_items_demo.csv</a>
141
- (<code>item,reverse</code> columns 1/true/yes/R = reverse),
142
  <a href="/samples/construct_items_marker_demo.csv" download>construct_items_marker_demo.csv</a>
143
  (single column with <code>(R)</code> markers), or
144
  <a href="/samples/construct_items_demo.xlsx" download>construct_items_demo.xlsx</a> (Excel).</li>
145
  <li>Expect: items fill the textarea ((R) appended where flagged), the filename becomes
146
- the suggested name, and parse notes list skipped blanks/duplicates. Nothing is saved
147
  until you review and press Save. Item files are never retained on the server.</li>
148
  </ol>
149
 
@@ -159,7 +159,7 @@ are deleted after analysis. Upload
159
  <tr><td>french_demo.csv</td><td>fr + Multilingual E5</td><td>no language warnings</td></tr>
160
  <tr><td><a href="/samples/mixed_language_demo.csv" download>mixed_language_demo.csv</a></td><td>en + MiniLM</td><td>LANGUAGE_UNCERTAIN (majority 50%)</td></tr>
161
  <tr><td><a href="/samples/long_documents_demo.csv" download>long_documents_demo.csv</a></td><td>en + MiniLM</td>
162
- <td>TEXTS_MAYBE_TRUNCATED (4) + LANGUAGE_UNCERTAIN (only 10 rows, below the 20-row minimum by design)</td></tr>
163
  </table>
164
  </div>
165
  <p>Warnings are per-run snapshots: changing language/model requires a NEW run; old result
@@ -181,12 +181,12 @@ pages don't update.</p>
181
 
182
  <h2 id="accounts">6. Accounts</h2>
183
  <ol>
184
- <li>Sign in (top right) &gt; "Create a free account" &gt; email + password (min 8 chars)
185
  or use "Continue with Google".</li>
186
  <li>You're signed in immediately; the header shows your name.</li>
187
  <li>Sign out, sign back in; wrong password gives "Incorrect email or password";
188
  registering the same email again gives "already exists".</li>
189
- <li>Email is case-insensitive. No self-service password reset yet reset = admin action.
190
  (And remember: password accounts vanish when this dev instance rebuilds.)</li>
191
  </ol>
192
 
@@ -223,8 +223,8 @@ pages don't update.</p>
223
  </ol>
224
 
225
  <h2 id="feedback">10. Found something off?</h2>
226
- <p>Anything that doesn't match what this guide says it should do or anything confusing,
227
- slow, or missing please note the section number and what you saw, and send it to
228
  Deva (<a href="mailto:devaanand@umass.edu">devaanand@umass.edu</a>). Screenshots welcome.</p>
229
 
230
  </main>
 
3
  <head>
4
  <meta charset="utf-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>CCR Platform - Testing Guide</title>
7
  <style>
8
  :root {
9
  --bg: #ffffff; --fg: #1a1a1a; --muted: #666; --border: #ddd;
 
49
  <body>
50
  <main>
51
 
52
+ <h1>CCR Platform - Testing Guide</h1>
53
  <p class="sub">Everything built so far, as click-through scenarios: what to do and exactly what
54
+ you should see. Every dataset referenced is downloadable right here - all texts are
55
  synthetic, written for this kit; no real participant data anywhere.</p>
56
 
57
  <div class="note">
58
+ <b>⚠ This is the free dev instance - two caveats before you start:</b>
59
  <ul>
60
  <li><b>Storage is ephemeral.</b> Accounts, projects, and saved runs are wiped whenever
61
+ the instance rebuilds or restarts. If your account disappears, that's expected -
62
  just re-register. (Google sign-in recreates itself automatically.)</li>
63
  <li><b>It sleeps when idle.</b> After ~48&nbsp;h without visitors, the first page load
64
  takes about a minute to wake it up.</li>
 
138
  <ol>
139
  <li>"+ Custom construct" &gt; "Upload items from CSV/XLSX".</li>
140
  <li>Try <a href="/samples/construct_items_demo.csv" download>construct_items_demo.csv</a>
141
+ (<code>item,reverse</code> columns - 1/true/yes/R = reverse),
142
  <a href="/samples/construct_items_marker_demo.csv" download>construct_items_marker_demo.csv</a>
143
  (single column with <code>(R)</code> markers), or
144
  <a href="/samples/construct_items_demo.xlsx" download>construct_items_demo.xlsx</a> (Excel).</li>
145
  <li>Expect: items fill the textarea ((R) appended where flagged), the filename becomes
146
+ the suggested name, and parse notes list skipped duplicates. Nothing is saved
147
  until you review and press Save. Item files are never retained on the server.</li>
148
  </ol>
149
 
 
159
  <tr><td>french_demo.csv</td><td>fr + Multilingual E5</td><td>no language warnings</td></tr>
160
  <tr><td><a href="/samples/mixed_language_demo.csv" download>mixed_language_demo.csv</a></td><td>en + MiniLM</td><td>LANGUAGE_UNCERTAIN (majority 50%)</td></tr>
161
  <tr><td><a href="/samples/long_documents_demo.csv" download>long_documents_demo.csv</a></td><td>en + MiniLM</td>
162
+ <td>TEXTS_MAYBE_TRUNCATED (4) + LANGUAGE_UNCERTAIN (only 10 rows, below the 20-row minimum - by design)</td></tr>
163
  </table>
164
  </div>
165
  <p>Warnings are per-run snapshots: changing language/model requires a NEW run; old result
 
181
 
182
  <h2 id="accounts">6. Accounts</h2>
183
  <ol>
184
+ <li>Sign in (top right) &gt; "Create a free account" &gt; email + password (min 8 chars) -
185
  or use "Continue with Google".</li>
186
  <li>You're signed in immediately; the header shows your name.</li>
187
  <li>Sign out, sign back in; wrong password gives "Incorrect email or password";
188
  registering the same email again gives "already exists".</li>
189
+ <li>Email is case-insensitive. No self-service password reset yet - reset = admin action.
190
  (And remember: password accounts vanish when this dev instance rebuilds.)</li>
191
  </ol>
192
 
 
223
  </ol>
224
 
225
  <h2 id="feedback">10. Found something off?</h2>
226
+ <p>Anything that doesn't match what this guide says it should do - or anything confusing,
227
+ slow, or missing - please note the section number and what you saw, and send it to
228
  Deva (<a href="mailto:devaanand@umass.edu">devaanand@umass.edu</a>). Screenshots welcome.</p>
229
 
230
  </main>
backend/app/main.py CHANGED
@@ -11,6 +11,7 @@ from __future__ import annotations
11
 
12
  import json
13
  import os
 
14
  from contextlib import asynccontextmanager
15
  from pathlib import Path
16
 
@@ -483,7 +484,11 @@ async def upload_corpus(
483
  f"Anonymous uploads are limited to {mb} MB. Sign in (top right) to upload larger files.",
484
  )
485
 
 
 
 
486
  corpus = Corpus(
 
487
  project_id=project_id, filename=file.filename, path="", n_rows=0, columns_json="[]"
488
  )
489
  # Parse from a local temp file, then hand the bytes to the storage backend
 
11
 
12
  import json
13
  import os
14
+ import uuid
15
  from contextlib import asynccontextmanager
16
  from pathlib import Path
17
 
 
484
  f"Anonymous uploads are limited to {mb} MB. Sign in (top right) to upload larger files.",
485
  )
486
 
487
+ # Assign the id NOW: the model's default fires at INSERT flush, so reading
488
+ # corpus.id before commit yields None - every upload would then share one
489
+ # "None.csv" on disk, each new upload silently clobbering the previous one.
490
  corpus = Corpus(
491
+ id=uuid.uuid4().hex,
492
  project_id=project_id, filename=file.filename, path="", n_rows=0, columns_json="[]"
493
  )
494
  # Parse from a local temp file, then hand the bytes to the storage backend
backend/tests/test_api.py CHANGED
@@ -269,3 +269,41 @@ def test_script_export_is_valid_offline_python(client, flow):
269
  reqs = client.get(f"/api/jobs/{job_id}/script-requirements")
270
  assert reqs.status_code == 200
271
  assert "==" in reqs.text # pinned versions, not ranges
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
  reqs = client.get(f"/api/jobs/{job_id}/script-requirements")
270
  assert reqs.status_code == 200
271
  assert "==" in reqs.text # pinned versions, not ranges
272
+
273
+
274
+ # ------------------------------------------- uploads must not clobber each other
275
+ def test_second_upload_does_not_clobber_first(client, flow):
276
+ """Regression: corpus ids used to be read before INSERT (still None), so
277
+ every upload stored to the same "None.csv" - a later upload silently
278
+ replaced an earlier corpus's file, and runs analyzed the wrong dataset."""
279
+ project = client.post("/api/projects", json={"name": "clobber-check"}).json()
280
+
281
+ first = upload(client, project["id"], "first.csv",
282
+ b"text\nalpha one\nalpha two\nalpha three\n").json()
283
+ second = upload(client, project["id"], "second.csv",
284
+ b"text\nbeta one\nbeta two\nbeta three\nbeta four\n").json()
285
+
286
+ # Distinct stored files, both still present after the second upload.
287
+ from pathlib import Path
288
+ assert first["id"] and second["id"] and first["id"] != second["id"]
289
+ paths = {
290
+ c["filename"]: c for c in
291
+ client.get(f"/api/projects/{project['id']}/corpora").json()
292
+ }
293
+ assert paths["first.csv"]["n_rows"] == 3 and paths["second.csv"]["n_rows"] == 4
294
+
295
+ # Running the FIRST corpus must analyze the first corpus's rows.
296
+ job = client.post("/api/jobs", json={
297
+ "project_id": project["id"], "corpus_id": first["id"],
298
+ "construct_id": flow["construct"]["id"], "text_column": "text",
299
+ "model_name": "fake-deterministic",
300
+ }).json()
301
+ job = wait_for_job(client, job["id"])
302
+ assert job["status"] == "completed", job.get("error")
303
+ # Assert on the EXPORTED rows (what was actually analyzed), not on DB
304
+ # metadata - the broken path kept correct metadata while scoring the
305
+ # other corpus's texts.
306
+ export = client.get(f"/api/jobs/{job['id']}/export").text
307
+ data_lines = [l for l in export.splitlines()[1:] if l.strip()]
308
+ assert len(data_lines) == 3, export
309
+ assert "alpha" in export and "beta" not in export
frontend/index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>CCR Platform Contextualized Construct Representations</title>
7
  </head>
8
  <body>
9
  <div id="root"></div>
 
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>CCR Platform - Contextualized Construct Representations</title>
7
  </head>
8
  <body>
9
  <div id="root"></div>
sample_data/README.md CHANGED
@@ -19,7 +19,7 @@ select language English and model MiniLM.
19
  | `xlsx_upload_demo.xlsx` | Excel ingestion path | Uploads and parses like a CSV |
20
  | `large_demo.csv` | Anonymous upload caps (800 rows) | Signed out: blocked with the sign-in hint. Signed in: runs |
21
  | `construct_items_demo.csv` | Construct upload: item + reverse columns | + Custom construct > upload: 6 items parse, last one flagged reverse-scored |
22
- | `construct_items_marker_demo.csv` | Construct upload: (R) markers, blank + duplicate rows | 4 items parse (blank and duplicate skipped, notes say so); "(R)" line flagged |
23
  | `construct_items_demo.xlsx` | Construct upload from Excel | 4 items, two flagged reverse via (R) markers |
24
 
25
  Notes:
 
19
  | `xlsx_upload_demo.xlsx` | Excel ingestion path | Uploads and parses like a CSV |
20
  | `large_demo.csv` | Anonymous upload caps (800 rows) | Signed out: blocked with the sign-in hint. Signed in: runs |
21
  | `construct_items_demo.csv` | Construct upload: item + reverse columns | + Custom construct > upload: 6 items parse, last one flagged reverse-scored |
22
+ | `construct_items_marker_demo.csv` | Construct upload: (R) markers, blank + duplicate rows | 4 items parse; notes report the skipped duplicate (the blank line is dropped silently at read); "(R)" line flagged |
23
  | `construct_items_demo.xlsx` | Construct upload from Excel | 4 items, two flagged reverse via (R) markers |
24
 
25
  Notes: