ysharma HF Staff commited on
Commit
5f5b418
Β·
verified Β·
1 Parent(s): 246efb5

Preview-vs-save flow, interactive recreate checkbox, top status message

Browse files
Files changed (1) hide show
  1. app.py +58 -41
app.py CHANGED
@@ -119,7 +119,7 @@ def lookup_participant(username: str):
119
  def on_load(profile: gr.OAuthProfile | None):
120
  """Runs on page load. Drives which panel is shown, pre-fills the form, and β€” if the user
121
  already generated a certificate β€” shows it and reveals the 'Recreate' control."""
122
- hidden_cb = gr.update(visible=False, value=False)
123
 
124
  if profile is None:
125
  return (
@@ -130,6 +130,7 @@ def on_load(profile: gr.OAuthProfile | None):
130
  gr.update(value="", visible=True), # project
131
  "", # data_status
132
  None, # certificate_image
 
133
  hidden_cb, # recreate_checkbox
134
  "", # generation_status
135
  )
@@ -144,7 +145,7 @@ def on_load(profile: gr.OAuthProfile | None):
144
  gr.update(visible=False),
145
  gr.update(visible=True),
146
  "", gr.update(value="", visible=True), "",
147
- None, hidden_cb, "",
148
  )
149
 
150
  name_value = full_name or profile_name
@@ -158,7 +159,7 @@ def on_load(profile: gr.OAuthProfile | None):
158
  )
159
  project_update = gr.update(value="", visible=True)
160
 
161
- # Already generated a certificate before? Fetch and show it.
162
  existing = None
163
  try:
164
  existing = get_certificate_image_path(username)
@@ -167,21 +168,25 @@ def on_load(profile: gr.OAuthProfile | None):
167
 
168
  if existing:
169
  gen_status = (
170
- "πŸ“œ You've **already generated** a certificate (shown below). To change it, edit your "
171
- "details, tick **Recreate certificate**, and click generate again β€” this **replaces** "
172
- "your existing one (one certificate per participant)."
 
 
 
 
173
  )
174
- cb = gr.update(visible=True, value=False)
175
  else:
176
  gen_status = ""
177
- cb = gr.update(visible=False, value=False)
178
 
179
  return (
180
  f"Signed in as **{username}**.",
181
  gr.update(visible=True),
182
  gr.update(visible=False),
183
  name_value, project_update, status,
184
- existing, cb, gen_status,
185
  )
186
 
187
 
@@ -202,8 +207,9 @@ def generate_linkedin_url(participant_name, project_name):
202
  # ----------------------------------------------------------------------------- generate
203
  def create_certificate(participant_name, project_name, recreate,
204
  oauth_token: gr.OAuthToken | None, profile: gr.OAuthProfile | None):
 
205
  if profile is None:
206
- return None, None, "❌ Please sign in first to generate your certificate.", "", gr.update(visible=False)
207
 
208
  username = profile.username
209
  state, _, _ = lookup_participant(username)
@@ -211,36 +217,26 @@ def create_certificate(participant_name, project_name, recreate,
211
  return None, None, (
212
  "❌ We couldn't find you in the Build Small Hackathon records, so we can't issue a "
213
  f"certificate. Please reach out on [Discord]({DISCORD_INVITE}) or email {CONTACT_EMAIL}."
214
- ), "", gr.update(visible=False)
215
  if state == "error":
216
- return None, None, "❌ Couldn't reach the hackathon records right now. Please try again shortly.", "", gr.update(visible=False)
217
 
218
- # If they already have one and didn't ask to recreate, show it instead of duplicating.
219
  existing_path = None
220
  try:
221
  existing_path = get_certificate_image_path(username)
222
  except Exception:
223
  existing_path = None
224
 
225
- if existing_path and not recreate:
226
- return (
227
- existing_path, existing_path,
228
- "ℹ️ You already have a certificate (shown above). To change it, edit your details, tick "
229
- "**Recreate certificate**, and click generate again to replace it.",
230
- generate_linkedin_url(participant_name or username, project_name or ""),
231
- gr.update(visible=True, value=False),
232
- )
233
-
234
  participant_name = (participant_name or "").strip() or (profile.name or username)
235
  project_name = (project_name or "").strip()
236
-
237
  project_section = (
238
  PROJECT_SECTION_WITH_NAME.replace("{project_name}", project_name)
239
  if project_name else PROJECT_SECTION_EMPTY
240
  )
241
  html = CERTIFICATE_HTML_TEMPLATE.replace("{participant_name}", participant_name)
242
  html = html.replace("{project_section}", project_section)
243
-
244
  with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".html", encoding="utf-8") as f:
245
  f.write(html)
246
  html_path = f.name
@@ -249,22 +245,41 @@ def create_certificate(participant_name, project_name, recreate,
249
  client = get_gradio_client()
250
  image_path = client.predict(html_file=handle_file(html_path), api_name="/predict")[0]
251
  except Exception as e:
252
- return None, None, f"❌ Error generating certificate image: {e}", "", gr.update(visible=bool(existing_path))
253
-
254
- # Save (or replace) in the private gallery dataset
255
- try:
256
- cert_image = Image.open(image_path)
257
- ok, msg = upload_user_certificate(cert_image, username, overwrite=bool(existing_path))
258
- if existing_path:
259
- save_note = "πŸ” This replaces your previous certificate in our records."
260
- else:
261
- save_note = msg if ok else f"(generated β€” gallery note: {msg})"
262
- except Exception as e:
263
- save_note = f"(generated β€” gallery save skipped: {e})"
264
 
 
265
  linkedin_url = generate_linkedin_url(participant_name, project_name)
266
- status = f"πŸŽ‰ Your certificate is ready, {participant_name}! {save_note}"
267
- return image_path, image_path, status, linkedin_url, gr.update(visible=True, value=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
 
270
  def render_linkedin_button(url):
@@ -352,6 +367,8 @@ with gr.Blocks(title="Build Small β€” Certificate Generator") as demo:
352
  with gr.Column(visible=False) as main_interface:
353
  gr.HTML('<div class="bs-section">Your details</div>')
354
  data_status = gr.Markdown("")
 
 
355
  gr.Markdown(
356
  "✏️ **These details were auto-filled from the Build Small Hackathon records β€” "
357
  "you can edit both your name and your project/Space name** below before generating "
@@ -366,7 +383,8 @@ with gr.Blocks(title="Build Small β€” Certificate Generator") as demo:
366
  info="Auto-filled from your Build Small submission. Edit it, or leave blank for a certificate without a project.",
367
  )
368
  recreate_checkbox = gr.Checkbox(
369
- label="Recreate certificate (replaces my existing one)", value=False, visible=False,
 
370
  )
371
  with gr.Row(elem_classes=["bs-generate"]):
372
  generate_btn = gr.Button("Generate my certificate", variant="primary", size="lg")
@@ -374,7 +392,6 @@ with gr.Blocks(title="Build Small β€” Certificate Generator") as demo:
374
  gr.HTML('<div class="bs-section">Your certificate</div>')
375
  certificate_image = gr.Image(label="Preview", type="filepath", interactive=False)
376
  certificate_file = gr.File(label="Download (PNG)", interactive=False)
377
- generation_status = gr.Markdown("")
378
 
379
  gr.HTML('<div class="bs-section">Share</div>')
380
  linkedin_html = gr.HTML(render_linkedin_button(""))
@@ -385,7 +402,7 @@ with gr.Blocks(title="Build Small β€” Certificate Generator") as demo:
385
  fn=on_load,
386
  inputs=None,
387
  outputs=[login_status, main_interface, contact_box, participant_name, project_name,
388
- data_status, certificate_image, recreate_checkbox, generation_status],
389
  )
390
 
391
  generate_btn.click(
 
119
  def on_load(profile: gr.OAuthProfile | None):
120
  """Runs on page load. Drives which panel is shown, pre-fills the form, and β€” if the user
121
  already generated a certificate β€” shows it and reveals the 'Recreate' control."""
122
+ hidden_cb = gr.update(visible=False, value=False, interactive=True)
123
 
124
  if profile is None:
125
  return (
 
130
  gr.update(value="", visible=True), # project
131
  "", # data_status
132
  None, # certificate_image
133
+ None, # certificate_file
134
  hidden_cb, # recreate_checkbox
135
  "", # generation_status
136
  )
 
145
  gr.update(visible=False),
146
  gr.update(visible=True),
147
  "", gr.update(value="", visible=True), "",
148
+ None, None, hidden_cb, "",
149
  )
150
 
151
  name_value = full_name or profile_name
 
159
  )
160
  project_update = gr.update(value="", visible=True)
161
 
162
+ # Already generated a certificate before? Fetch and show it, with guidance up top.
163
  existing = None
164
  try:
165
  existing = get_certificate_image_path(username)
 
168
 
169
  if existing:
170
  gen_status = (
171
+ "### πŸ“œ You already have a certificate\n"
172
+ "It's shown below β€” **download it** and use it however you like (LinkedIn, socials, etc.).\n\n"
173
+ "- Want a version with **different details**? Edit your name/project and click "
174
+ "**Generate my certificate** β€” that's just a fresh preview to download and **won't change** "
175
+ "the copy saved in our records.\n"
176
+ "- Want to **replace** your saved copy? Tick **Recreate certificate** below, then generate. "
177
+ "There's one certificate per participant, so the new one overwrites the old."
178
  )
179
+ cb = gr.update(visible=True, value=False, interactive=True)
180
  else:
181
  gen_status = ""
182
+ cb = gr.update(visible=False, value=False, interactive=True)
183
 
184
  return (
185
  f"Signed in as **{username}**.",
186
  gr.update(visible=True),
187
  gr.update(visible=False),
188
  name_value, project_update, status,
189
+ existing, existing, cb, gen_status,
190
  )
191
 
192
 
 
207
  # ----------------------------------------------------------------------------- generate
208
  def create_certificate(participant_name, project_name, recreate,
209
  oauth_token: gr.OAuthToken | None, profile: gr.OAuthProfile | None):
210
+ hide_cb = gr.update(visible=False, interactive=True)
211
  if profile is None:
212
+ return None, None, "❌ Please sign in first to generate your certificate.", "", hide_cb
213
 
214
  username = profile.username
215
  state, _, _ = lookup_participant(username)
 
217
  return None, None, (
218
  "❌ We couldn't find you in the Build Small Hackathon records, so we can't issue a "
219
  f"certificate. Please reach out on [Discord]({DISCORD_INVITE}) or email {CONTACT_EMAIL}."
220
+ ), "", hide_cb
221
  if state == "error":
222
+ return None, None, "❌ Couldn't reach the hackathon records right now. Please try again shortly.", "", hide_cb
223
 
224
+ # Does a saved certificate already exist for this user?
225
  existing_path = None
226
  try:
227
  existing_path = get_certificate_image_path(username)
228
  except Exception:
229
  existing_path = None
230
 
231
+ # Always render a fresh certificate from the CURRENT field values.
 
 
 
 
 
 
 
 
232
  participant_name = (participant_name or "").strip() or (profile.name or username)
233
  project_name = (project_name or "").strip()
 
234
  project_section = (
235
  PROJECT_SECTION_WITH_NAME.replace("{project_name}", project_name)
236
  if project_name else PROJECT_SECTION_EMPTY
237
  )
238
  html = CERTIFICATE_HTML_TEMPLATE.replace("{participant_name}", participant_name)
239
  html = html.replace("{project_section}", project_section)
 
240
  with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".html", encoding="utf-8") as f:
241
  f.write(html)
242
  html_path = f.name
 
245
  client = get_gradio_client()
246
  image_path = client.predict(html_file=handle_file(html_path), api_name="/predict")[0]
247
  except Exception as e:
248
+ return None, None, f"❌ Error generating certificate image: {e}", "", gr.update(visible=bool(existing_path), interactive=True)
 
 
 
 
 
 
 
 
 
 
 
249
 
250
+ show_cb = gr.update(visible=True, value=False, interactive=True)
251
  linkedin_url = generate_linkedin_url(participant_name, project_name)
252
+
253
+ # Decide what happens to the SAVED copy in the dataset.
254
+ if existing_path is None:
255
+ # First time β€” save it.
256
+ try:
257
+ ok, msg = upload_user_certificate(Image.open(image_path), username, overwrite=False)
258
+ note = ("πŸŽ‰ Your certificate is ready and has been **saved to our records**. Download it "
259
+ "below β€” you can edit your details and regenerate anytime.") if ok else \
260
+ f"⚠️ Certificate generated, but saving hit an issue: {msg}"
261
+ except Exception as e:
262
+ note = f"⚠️ Certificate generated, but saving was skipped: {e}"
263
+ return image_path, image_path, note, linkedin_url, show_cb
264
+
265
+ if recreate:
266
+ # Replace the saved copy.
267
+ try:
268
+ ok, msg = upload_user_certificate(Image.open(image_path), username, overwrite=True)
269
+ note = ("πŸ” **Done β€” your new certificate has replaced the previous one saved in our "
270
+ "records.** Download the updated copy below.") if ok else \
271
+ f"⚠️ Generated, but replacing the saved copy hit an issue: {msg}"
272
+ except Exception as e:
273
+ note = f"⚠️ Generated, but replacing the saved copy was skipped: {e}"
274
+ return image_path, image_path, note, linkedin_url, show_cb
275
+
276
+ # Existing copy + recreate NOT ticked β†’ fresh preview only, dataset untouched.
277
+ note = (
278
+ "πŸ‘€ **Here's a new preview with your latest details** β€” download it below. "
279
+ "Your **saved** certificate in our records is **unchanged**. "
280
+ "To make this version the official one, tick **Recreate certificate** above and generate again."
281
+ )
282
+ return image_path, image_path, note, linkedin_url, show_cb
283
 
284
 
285
  def render_linkedin_button(url):
 
367
  with gr.Column(visible=False) as main_interface:
368
  gr.HTML('<div class="bs-section">Your details</div>')
369
  data_status = gr.Markdown("")
370
+ # status / "you already have a certificate" notice β€” kept at the TOP, above the form
371
+ generation_status = gr.Markdown("")
372
  gr.Markdown(
373
  "✏️ **These details were auto-filled from the Build Small Hackathon records β€” "
374
  "you can edit both your name and your project/Space name** below before generating "
 
383
  info="Auto-filled from your Build Small submission. Edit it, or leave blank for a certificate without a project.",
384
  )
385
  recreate_checkbox = gr.Checkbox(
386
+ label="Recreate certificate β€” replace the copy saved in our records",
387
+ value=False, visible=False, interactive=True,
388
  )
389
  with gr.Row(elem_classes=["bs-generate"]):
390
  generate_btn = gr.Button("Generate my certificate", variant="primary", size="lg")
 
392
  gr.HTML('<div class="bs-section">Your certificate</div>')
393
  certificate_image = gr.Image(label="Preview", type="filepath", interactive=False)
394
  certificate_file = gr.File(label="Download (PNG)", interactive=False)
 
395
 
396
  gr.HTML('<div class="bs-section">Share</div>')
397
  linkedin_html = gr.HTML(render_linkedin_button(""))
 
402
  fn=on_load,
403
  inputs=None,
404
  outputs=[login_status, main_interface, contact_box, participant_name, project_name,
405
+ data_status, certificate_image, certificate_file, recreate_checkbox, generation_status],
406
  )
407
 
408
  generate_btn.click(