natalielamjohnson1 commited on
Commit
ce2e827
Β·
verified Β·
1 Parent(s): b7d7774

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +207 -131
app.py CHANGED
@@ -3,10 +3,21 @@ Streamlit front end for the STARS report redactor (student-facing).
3
 
4
  Privacy design: the uploaded PDF is redacted entirely IN MEMORY via
5
  `engine.redact_bytes` and is never written to the server's disk. Only the
6
- redacted copy is offered back to the student for download; nothing is stored.
 
7
 
8
  Deployment: Hugging Face Space (Streamlit SDK). Set a Space secret named
9
  APP_PASSPHRASE to the access phrase before going live.
 
 
 
 
 
 
 
 
 
 
10
  """
11
  import os
12
  import uuid
@@ -23,6 +34,22 @@ DELETE_FORM_URL = ( # "delete my report" request
23
  "https://docs.google.com/forms/d/e/"
24
  "1FAIpQLScSpM1ov0dMEXhT3hHhsWmIByu-j1myQYnNvRv7C0FNUW81eg/viewform"
25
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  # --- submission storage (a private HF Dataset in the org) -------------------
28
  # In-app submission switches on automatically once these Space secrets are set:
@@ -68,10 +95,12 @@ st.markdown(
68
  html, body, [class*="css"], .stMarkdown, p, li { font-family: 'Inter', system-ui, sans-serif; }
69
  h1, h2, h3 { font-family: 'Newsreader', Georgia, serif !important; color: #990000; letter-spacing: .2px; }
70
  h1 { border-bottom: 3px solid #FFCC00; padding-bottom: .35rem; }
71
- .stButton > button, .stDownloadButton > button {
72
  background-color: #990000; color: #ffffff; border: 0; font-weight: 600; border-radius: 8px;
73
  }
74
- .stButton > button:hover, .stDownloadButton > button:hover { background-color: #7a0000; color: #FFCC00; }
 
 
75
  div[data-testid="stExpander"] { border: 1px solid #ece2cc; border-radius: 8px; }
76
  div[data-testid="stExpander"] summary { font-weight: 600; color: #990000; }
77
  </style>
@@ -95,28 +124,68 @@ st.markdown(
95
  unsafe_allow_html=True,
96
  )
97
 
98
- # ---------------------------------------------------------------- the ask ----
99
- st.title("Help us build better tools for USC students")
 
 
100
 
101
  st.markdown(
102
- "BBH (the **BUAI Builder Hub**) is hard at work building tools to make "
103
- "**degree planning, course registration, and picking classes** way less of "
104
- "a headache. One thing we all agree on: actually **making sense of your "
105
- "STARS report** is a pain.\n\n"
106
- "**You can help.** We're putting together a set of STARS reports to build "
107
- "against. Upload yours below β€” we'll strip out the sensitive "
108
- "stuff (**your name, address, student ID, and grades**) right here, and only "
109
- "ever keep the **redacted** version."
110
  )
111
 
112
- # ------------------------------------------------------------------- Q & A ----
113
- st.subheader("Quick questions you might have")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
  with st.expander("Wait β€” are my grades going in a database?"):
116
  st.markdown(
117
  "Nope. Before anything gets saved, we swap **every grade for a plain "
118
  "\"passed / didn't pass\" marker** and **wipe your GPA**. The copy we keep "
119
- "literally doesn't have your letter grades in it β€” we can tell a class was "
120
  "finished, not what you got in it."
121
  )
122
 
@@ -126,27 +195,21 @@ with st.expander("Who actually sees my report?"):
126
  "the USC student experience. Your redacted report is kept somewhere "
127
  "**private and access-controlled** (not public, never for sale), and used "
128
  "only to build and test those tools. Whoever works with it agrees not to "
129
- "try to figure out who anyone is. (See the consent box below for the "
130
- "honest caveat on why we still can't *totally* promise anonymity.)"
131
  )
132
 
133
- with st.expander("I changed my mind β€” can you delete my report?"):
134
- st.markdown(
135
- "Totally fine β€” we get it! When you submit, we give you a **report ID**. "
136
- f"Pop it into [this quick form]({DELETE_FORM_URL}) and we'll remove your "
137
- "report.\n\n"
138
- "⚠️ **Save that ID somewhere safe.** Your report is stored with **no name "
139
- "attached**, so the ID is the *only* way we can find it. If you lose it, we "
140
- "won't be able to delete your report."
141
- )
142
 
143
- with st.expander("Why should I bother?"):
144
- st.markdown(
145
- "You'd be helping build tools that make planning your degree and getting "
146
- "into the classes you need way less painful β€” for you *and* everyone after "
147
- "you. It takes about two minutes, and your personal info and grades get "
148
- "stripped out before anything is stored. That's it."
149
- )
150
 
151
  with st.expander("How do I even get my STARS report?"):
152
  st.markdown(
@@ -158,81 +221,20 @@ with st.expander("How do I even get my STARS report?"):
158
  "or scan won't work.)"
159
  )
160
 
161
- st.markdown("### Ready? Let's do it πŸ‘‡")
162
-
163
- # --------------------------------------------------------------- pass gate ----
164
- if not PASSPHRASE:
165
- st.error("This app isn't set up yet (no passphrase configured). Ping the team.")
166
- footer()
167
- st.stop()
168
-
169
- entered = st.text_input("Passphrase", type="password")
170
- st.caption("If you're a USC student using this, whoever referred you should've "
171
- "shared a passphrase.")
172
- if entered.strip() != PASSPHRASE:
173
- if entered:
174
- st.warning("Hmm, that's not it β€” double-check with whoever sent you here.")
175
- footer()
176
- st.stop()
177
-
178
- # ------------------------------------------------------- consent + upload ----
179
- with st.expander("πŸ“‹ Please read this before you upload β€” what you're agreeing to",
180
- expanded=True):
181
- st.markdown(
182
- "- **This is totally voluntary.** You don't have to do it, and you can "
183
- "close this page anytime. No penalty, no hard feelings.\n"
184
- "- **What we keep:** only the **redacted** version of your STARS report β€” "
185
- "the file this tool hands back after removing your personal info.\n"
186
- "- **What's removed before anything is stored:** your **name, USC ID, "
187
- "mailing address, sport/team**, every **letter grade** (each becomes a "
188
- "simple pass/fail marker), and your **GPA**.\n"
189
- "- **What stays in the file:** your **major/minor, the courses and terms "
190
- "on your record, and your degree-progress details** β€” that's the part "
191
- "we're actually building with.\n"
192
- "- **Can people tell it's you?** We want to be upfront: possibly. We "
193
- "remove your name, ID, grades, and GPA, but we **keep your major(s), "
194
- "minor, and the list of classes you've taken** β€” that's the part we build "
195
- "with. If your combination is unusual (say you're the only person at USC "
196
- "double-majoring in BUAI and Anthropology), someone who sees the file "
197
- "could figure out it's yours. They still **won't** see your grades, GPA, "
198
- "or student ID β€” but they might know the report belongs to you. Only share "
199
- "if you're okay with that.\n"
200
- "- **Your original file never sticks around.** It's handled right here in "
201
- "the page's memory, **never written to disk**, and **automatically "
202
- "discarded the moment you close this tab**. If you hit **Submit**, we save "
203
- "**only the redacted copy** to a **private BBH archive**, tagged with a "
204
- "random ID we show you (keep it β€” that's how you can ask us to delete your "
205
- "report later). We never keep your original.\n"
206
- "- **How we use it:** to build and test tools that help USC students plan "
207
- "degrees and register for classes, and β€” because it's de-identified β€” "
208
- "possibly for research about the student experience. We won't sell it or "
209
- "try to identify you.\n"
210
- f"- **Changed your mind later?** It's your record and your call β€” you can "
211
- f"ask us to delete your redacted report anytime via our "
212
- f"[deletion form]({DELETE_FORM_URL}), using the report ID we give you at "
213
- "submission. Because we store it with **no name attached**, that ID is the "
214
- "only way to find your file, so please keep it β€” if it's lost, we can't "
215
- "identify your report to delete it."
216
- )
217
-
218
- consent = st.checkbox(
219
- "I've read the above and I'm good with sharing my **redacted** STARS report.")
220
-
221
  uploaded = st.file_uploader(
222
  "Upload your STARS report (the PDF you saved from experience.usc.edu)",
223
  type=["pdf"], accept_multiple_files=False)
224
  st.caption("πŸ”’ Your upload is handled in memory only and is discarded the moment "
225
- "you close this tab.")
226
-
227
- if uploaded is not None and not consent:
228
- st.info("Check the consent box above and you're good to go.")
229
 
230
- if uploaded is not None and consent:
231
  # Redact ONCE per uploaded file and cache the exact result. Streamlit reruns
232
- # the whole script on every click, so without caching, clicking Submit would
233
- # re-run redaction and upload a freshly-recomputed file β€” not the same bytes
234
- # the student downloaded and reviewed. One redaction, one UID, reused for the
235
- # download filename and the database submission so they always match.
 
236
  file_key = getattr(uploaded, "file_id", None) or f"{uploaded.name}:{uploaded.size}"
237
  if st.session_state.get("uid_key") != file_key:
238
  with st.spinner("Scrubbing your info…"):
@@ -244,6 +246,7 @@ if uploaded is not None and consent:
244
  st.session_state["uid"] = uuid.uuid4().hex[:8].upper()
245
  st.session_state["res"] = res
246
  st.session_state["out"] = out
 
247
 
248
  res = st.session_state["res"]
249
  out = st.session_state["out"]
@@ -265,33 +268,9 @@ if uploaded is not None and consent:
265
  st.download_button("⬇️ Download my redacted report", data=out,
266
  file_name=f"REDACTED_{uid}.pdf",
267
  mime="application/pdf")
268
- st.caption(f"Saved as `REDACTED_{uid}.pdf` β€” that code is your report ID. "
269
- "Open it to check it looks right, then submit below.")
270
-
271
- st.divider()
272
- if _submissions_enabled():
273
- st.markdown("**Share it with BBH**")
274
- if st.button("πŸ“€ Submit my redacted report"):
275
- try:
276
- _upload_submission(out, uid)
277
- st.success("Submitted β€” thank you so much! πŸŽ‰")
278
- st.warning(
279
- f"**Your report ID is `{uid}`.**\n\n"
280
- "**Save this now** (screenshot it, write it down β€” whatever "
281
- "works). Your report is stored with **no name attached**, so "
282
- "this ID is the **only** way to delete it later. If you lose "
283
- "it, we can't find your report to remove it."
284
- )
285
- st.markdown(
286
- f"Changed your mind? Fill out our "
287
- f"[deletion form]({DELETE_FORM_URL}) with this ID.")
288
- except Exception:
289
- st.error("Submission didn't go through β€” sorry! Your redacted "
290
- "file is still available above; please try submitting "
291
- "again in a moment.")
292
- else:
293
- st.caption("Direct submission is unavailable right now β€” you can still "
294
- "download your redacted file above.")
295
 
296
  elif status == "SKIPPED":
297
  st.error("This looks like a **scan or photo** (no text in it), so it can't "
@@ -310,4 +289,101 @@ if uploaded is not None and consent:
310
  f"better safe than sorry. Please email {CONTACT_EMAIL}, and "
311
  "don't share the original in the meantime.")
312
 
313
- footer()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
  Privacy design: the uploaded PDF is redacted entirely IN MEMORY via
5
  `engine.redact_bytes` and is never written to the server's disk. Only the
6
+ redacted copy is offered back to the student for download; nothing is stored
7
+ unless the student explicitly chooses to submit it to BBH.
8
 
9
  Deployment: Hugging Face Space (Streamlit SDK). Set a Space secret named
10
  APP_PASSPHRASE to the access phrase before going live.
11
+
12
+ Page layout (top to bottom):
13
+ 1) What is BBH, and why are we doing this
14
+ 2) Access code
15
+ 3) Screenshots - what we keep / don't keep
16
+ 4) Link to a full sample redacted STARS report
17
+ 5) Try it yourself - upload & see
18
+ 6) Consent form check (only required to SHARE your redacted copy)
19
+ 7) Your code (shown after you submit)
20
+ 8) Removal link
21
  """
22
  import os
23
  import uuid
 
34
  "https://docs.google.com/forms/d/e/"
35
  "1FAIpQLScSpM1ov0dMEXhT3hHhsWmIByu-j1myQYnNvRv7C0FNUW81eg/viewform"
36
  )
37
+ # Points at exampleRedactedStars.pdf in this Space repo. st.link_button below
38
+ # opens it in a new tab automatically.
39
+ SAMPLE_REPORT_URL = (
40
+ "https://huggingface.co/spaces/buai-builder-hub/STARSRedacter/blob/main/"
41
+ "exampleRedactedStars.pdf"
42
+ )
43
+
44
+ # Screenshots live at the repo root (next to app.py). Shown in this list
45
+ # order, top to bottom, as the student scrolls.
46
+ # TODO: these captions are placeholders β€” edit them to describe what each
47
+ # screenshot actually shows.
48
+ KEEP_DONT_KEEP_IMAGES = [
49
+ ("explanationSS1.png", "TODO: caption for screenshot 1"),
50
+ ("explanationSS2.png", "TODO: caption for screenshot 2"),
51
+ ("explanationSS3.png", "TODO: caption for screenshot 3"),
52
+ ]
53
 
54
  # --- submission storage (a private HF Dataset in the org) -------------------
55
  # In-app submission switches on automatically once these Space secrets are set:
 
95
  html, body, [class*="css"], .stMarkdown, p, li { font-family: 'Inter', system-ui, sans-serif; }
96
  h1, h2, h3 { font-family: 'Newsreader', Georgia, serif !important; color: #990000; letter-spacing: .2px; }
97
  h1 { border-bottom: 3px solid #FFCC00; padding-bottom: .35rem; }
98
+ .stButton > button, .stDownloadButton > button, .stLinkButton > a {
99
  background-color: #990000; color: #ffffff; border: 0; font-weight: 600; border-radius: 8px;
100
  }
101
+ .stButton > button:hover, .stDownloadButton > button:hover, .stLinkButton > a:hover {
102
+ background-color: #7a0000; color: #FFCC00;
103
+ }
104
  div[data-testid="stExpander"] { border: 1px solid #ece2cc; border-radius: 8px; }
105
  div[data-testid="stExpander"] summary { font-weight: 600; color: #990000; }
106
  </style>
 
124
  unsafe_allow_html=True,
125
  )
126
 
127
+ # ============================================================================
128
+ # 1) WHAT IS BBH, AND WHY ARE WE DOING THIS
129
+ # ============================================================================
130
+ st.title("Please Share Your STARS Report to Support Our Student Tool!")
131
 
132
  st.markdown(
133
+ "**BUAI Builder Hub** is working on creating tools to support students in "
134
+ "**degree planning, course registration, and picking classes**. We need your "
135
+ "help sourcing STARS reports to test against our tool!\n\n"
136
+ "It takes 2 minutes. Upload your STARS report and we'll strip out all "
137
+ "sensitive info."
 
 
 
138
  )
139
 
140
+ # A skimmable recap for anyone who won't read every section below.
141
+ with st.container(border=True):
142
+ st.markdown("**πŸ“Œ TL;DR**")
143
+ st.markdown(
144
+ "- Your **name, USC ID, address, grades, and GPA** are never stored β€” "
145
+ "only a de-identified copy, and only if you choose to share it.\n"
146
+ "- Only **BBH student developer teams** can ever access it. Private, "
147
+ "access-controlled, never sold or made public.\n"
148
+ "- You can **try the tool and see your redacted file first** β€” sharing "
149
+ "it with BBH is a separate, optional step.\n"
150
+ "- Changed your mind later? You can request that we **delete your report** "
151
+ "anytime using the code we give you.\n"
152
+ "- Takes about **two minutes**, start to finish."
153
+ )
154
+
155
+ # ============================================================================
156
+ # 2) ACCESS CODE
157
+ # ============================================================================
158
+ if not PASSPHRASE:
159
+ st.error("This app isn't set up yet (no passphrase configured). Ping the team.")
160
+ footer()
161
+ st.stop()
162
+
163
+ st.markdown("### Enter your access code")
164
+ entered = st.text_input("Passphrase", type="password")
165
+ st.caption("If you're a USC student using this, whoever referred you should've "
166
+ "shared a passphrase.")
167
+ if entered.strip() != PASSPHRASE:
168
+ if entered:
169
+ st.warning("Hmm, that's not it β€” double-check with whoever sent you here.")
170
+ footer()
171
+ st.stop()
172
+
173
+ # ============================================================================
174
+ # 3) SCREENSHOTS β€” WHAT WE KEEP & DON'T KEEP
175
+ # ============================================================================
176
+ st.markdown("### What we keep β€” and what we don't")
177
+
178
+ for path, caption in KEEP_DONT_KEEP_IMAGES:
179
+ try:
180
+ st.image(path, caption=caption, use_container_width=True)
181
+ except Exception:
182
+ st.info(f"(Add `{path}` to the Space repo to show this screenshot.)")
183
 
184
  with st.expander("Wait β€” are my grades going in a database?"):
185
  st.markdown(
186
  "Nope. Before anything gets saved, we swap **every grade for a plain "
187
  "\"passed / didn't pass\" marker** and **wipe your GPA**. The copy we keep "
188
+ " doesn't have your letter grades in it β€” we can tell a class was "
189
  "finished, not what you got in it."
190
  )
191
 
 
195
  "the USC student experience. Your redacted report is kept somewhere "
196
  "**private and access-controlled** (not public, never for sale), and used "
197
  "only to build and test those tools. Whoever works with it agrees not to "
198
+ "try to figure out who anyone is. (See the consent section further down "
199
+ "for the honest caveat on why we still can't *totally* promise anonymity.)"
200
  )
201
 
202
+ # ============================================================================
203
+ # 4) LINK TO A FULL SAMPLE REDACTED STARS REPORT
204
+ # ============================================================================
205
+ st.markdown("### See a full example of Redacted STARS Report")
206
+ st.link_button("πŸ“„ View here", SAMPLE_REPORT_URL)
207
+ st.caption("Opens in a new tab.")
 
 
 
208
 
209
+ # ============================================================================
210
+ # 5) TRY IT YOURSELF β€” UPLOAD & SEE
211
+ # ============================================================================
212
+ st.markdown("### Try it yourself")
 
 
 
213
 
214
  with st.expander("How do I even get my STARS report?"):
215
  st.markdown(
 
221
  "or scan won't work.)"
222
  )
223
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  uploaded = st.file_uploader(
225
  "Upload your STARS report (the PDF you saved from experience.usc.edu)",
226
  type=["pdf"], accept_multiple_files=False)
227
  st.caption("πŸ”’ Your upload is handled in memory only and is discarded the moment "
228
+ "you close this tab. You can preview and download your redacted file "
229
+ "below β€” to share it, you must click submit further down the page.")
 
 
230
 
231
+ if uploaded is not None:
232
  # Redact ONCE per uploaded file and cache the exact result. Streamlit reruns
233
+ # the whole script on every click, so without caching, checking the consent
234
+ # box or clicking Submit would re-run redaction and upload a freshly
235
+ # -recomputed file β€” not the same bytes the student downloaded and reviewed.
236
+ # One redaction, one UID, reused for the download filename and the database
237
+ # submission so they always match.
238
  file_key = getattr(uploaded, "file_id", None) or f"{uploaded.name}:{uploaded.size}"
239
  if st.session_state.get("uid_key") != file_key:
240
  with st.spinner("Scrubbing your info…"):
 
246
  st.session_state["uid"] = uuid.uuid4().hex[:8].upper()
247
  st.session_state["res"] = res
248
  st.session_state["out"] = out
249
+ st.session_state["submitted"] = False
250
 
251
  res = st.session_state["res"]
252
  out = st.session_state["out"]
 
268
  st.download_button("⬇️ Download my redacted report", data=out,
269
  file_name=f"REDACTED_{uid}.pdf",
270
  mime="application/pdf")
271
+ st.caption(f"Saved as `REDACTED_{uid}.pdf` β€” take a look and make sure it "
272
+ "looks right. Sharing it with BBH is a separate, optional step "
273
+ "below.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
 
275
  elif status == "SKIPPED":
276
  st.error("This looks like a **scan or photo** (no text in it), so it can't "
 
289
  f"better safe than sorry. Please email {CONTACT_EMAIL}, and "
290
  "don't share the original in the meantime.")
291
 
292
+ # ============================================================================
293
+ # 6) CONSENT FORM CHECK β€” only needed to SHARE your redacted copy with BBH
294
+ # ============================================================================
295
+ have_valid_redaction = bool(
296
+ uploaded is not None
297
+ and st.session_state.get("out")
298
+ and st.session_state.get("res", {}).get("status") in ("REDACTED", "REVIEW")
299
+ )
300
+
301
+ if have_valid_redaction:
302
+ st.markdown("### Want to share it with BBH?")
303
+ st.caption("Totally optional β€” you've already got your redacted file above. "
304
+ "This part is only about whether you'd like to contribute a copy "
305
+ "to BBH's project.")
306
+
307
+ with st.expander("Sharing Consent Acknowledgement", expanded=True):
308
+ st.markdown(
309
+ "- **Submitting is voluntary.** You don't have to do it, and you can "
310
+ "close this page anytime.\n"
311
+ "- **What we'd keep:** only the **redacted** version of your STARS "
312
+ "report β€” the file this tool just handed you after removing your "
313
+ "personal info.\n"
314
+ "- **What's removed before anything is stored:** your **name, USC ID, "
315
+ "mailing address, sport/team**, every **letter grade** (each becomes a "
316
+ "simple pass/fail marker), and your **GPA**.\n"
317
+ "- **What stays in the file:** your **major/minor, the courses and "
318
+ "terms on your record, and your degree-progress details**.\n"
319
+ "- **Identifiability:** We remove your name, ID, grades, and GPA, but "
320
+ "we **keep your major(s), minor, and the list of classes you've "
321
+ "taken**. If your combination is unusual (e.g. the only USC student "
322
+ "double-majoring in BUAI and Anthropology), someone who sees the file "
323
+ "could figure out it's yours. They still **won't** see your grades, "
324
+ "GPA, or student ID.\n"
325
+ "- **Your original file is never saved.** It's handled right here in "
326
+ "the page's memory, **never written to disk**, and **automatically "
327
+ "discarded the moment you close this tab**. If you submit, we save "
328
+ "**only the redacted copy** to a **private BBH archive**, tagged with "
329
+ "a random code we show you. You can use this code to request that "
330
+ "your report be removed anytime.\n"
331
+ "- **How we use it:** to build and test tools that help USC students "
332
+ "plan degrees and register for classes, and β€” because it's "
333
+ "de-identified β€” possibly for research about the student experience. "
334
+ "We won't sell it or try to identify you.\n"
335
+ "- **Want to remove your report?** You can ask us to delete your "
336
+ "redacted report anytime, using the code we give you at submission."
337
+ )
338
+
339
+ consent = st.checkbox(
340
+ "I've read the above and I consent to sharing my **redacted** STARS "
341
+ "report with BBH.")
342
+
343
+ if not consent:
344
+ st.info("Check the box above if you'd like to submit your redacted copy.")
345
+ else:
346
+ st.markdown("**Share it with BBH**")
347
+ if _submissions_enabled():
348
+ if st.button("πŸ“€ Submit my redacted report"):
349
+ try:
350
+ _upload_submission(st.session_state["out"], st.session_state["uid"])
351
+ st.session_state["submitted"] = True
352
+ st.success("Submitted β€” thank you so much! πŸŽ‰")
353
+ except Exception:
354
+ st.error("Submission didn't go through β€” sorry! Your redacted "
355
+ "file is still available above; please try submitting "
356
+ "again in a moment.")
357
+ else:
358
+ st.caption("Direct submission is unavailable right now β€” you can still "
359
+ "download your redacted file above.")
360
+
361
+ # ============================================================================
362
+ # 7) YOUR CODE
363
+ # ============================================================================
364
+ if st.session_state.get("submitted"):
365
+ st.markdown("### Your code")
366
+ st.warning(
367
+ f"**Your report ID is `{st.session_state['uid']}`.**\n\n"
368
+ "**Save this code now** (screenshot it, write it down β€” whatever works). "
369
+ "Your report is stored with **no name attached**, so this ID is the "
370
+ "**only** way to delete it later. If you lose it, we can't find your "
371
+ "report to remove it."
372
+ )
373
+
374
+ # ============================================================================
375
+ # 8) REMOVAL LINK
376
+ # ============================================================================
377
+ st.markdown("### Requesting removal of your report?")
378
+ st.markdown(
379
+ "When you submit, we give you a **report ID code** (see above). Fill out the "
380
+ "form below with your code, and we'll remove your report."
381
+ )
382
+ st.link_button("πŸ—‘οΈ Request removal", DELETE_FORM_URL)
383
+ st.caption(
384
+ "⚠️ Save your report ID somewhere safe. Your report is stored with **no name "
385
+ "attached**, so the ID is the *only* way we can find it. If you lose it, we "
386
+ "won't be able to delete your report."
387
+ )
388
+
389
+ footer()