ema-dev / ui /selection_page.py
e-bug's picture
Move locale selection to first page and position dropdowns next to each other
f0ea1f2
Raw
History Blame Contribute Delete
9.52 kB
import os.path
import re
import gradio as gr
from functools import partial
from .main_page import sort_with_pyuca
def split_intro_text(intro_text):
lines = intro_text.strip().split("\n")
if lines and lines[0].startswith("#"):
title = lines[0]
body = "\n".join(lines[1:])
return title, body
return "", intro_text
# Basic, permissive email format check (local@domain.tld). Used only to gate the
# "Continue" button so users don't proceed with an obviously malformed address.
_EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
def _looks_like_email(value):
return bool(_EMAIL_RE.match((value or "").strip()))
def update_language_options(selected_country, metadata):
with open(metadata["USA"]["English"]["Intro"], "r", encoding="utf-8") as f:
INTRO_TEXT = f.read() # default
# Retrieve the list of languages available for the selected country.
if selected_country in metadata:
languages = sort_with_pyuca(list(metadata[selected_country].keys()))
if len(languages) == 0:
gr.Warning("No languages available for the selected country.")
t, b = split_intro_text(INTRO_TEXT)
return gr.update(choices=[], interactive=False, value=None), gr.update(value=t), gr.update(value=b)
# select the first language as default
fn = metadata[selected_country][languages[0]]["Intro"]
if os.path.exists(fn):
with open(fn, "r", encoding="utf-8") as f:
INTRO_TEXT = f.read()
# Use gr.Dropdown.update to change the choices of the language dropdown.
val = languages[0] if len(languages) == 1 else None
t, b = split_intro_text(INTRO_TEXT)
return gr.update(choices=languages, interactive=True, value=val, allow_custom_value=False), gr.update(value=t), gr.update(value=b)
else:
t, b = split_intro_text(INTRO_TEXT)
return gr.update(choices=[], interactive=False, value=None, allow_custom_value=False), gr.update(value=t), gr.update(value=b)
def enable_country():
return gr.update(interactive=True)
def update_profile_button_state(email):
email = (email or "").strip()
valid = _looks_like_email(email)
# Reveal the primary "Continue" button as soon as the user starts typing an
# email, but only enable it once the address looks valid. HF login auto-fills
# a valid email, so this enables Continue automatically in that path too.
return gr.update(interactive=valid, visible=bool(email))
def prefill_country_language(saved_country, saved_language, current_country, current_language, metadata):
"""
Build updates for country/language dropdowns from persisted values.
Country is set first so the existing country->language cascade remains valid.
"""
if current_country:
return (
gr.skip(),
gr.skip(),
gr.skip(),
gr.skip(),
)
countries = sort_with_pyuca(list(metadata.keys()))
if not saved_country or saved_country not in metadata:
return (
gr.skip(),
gr.skip(),
gr.skip(),
gr.skip(),
)
# Compute language options directly to avoid relying on component instance attrs.
valid_languages = sort_with_pyuca(list(metadata[saved_country].keys()))
final_language = saved_language if saved_language in valid_languages else (valid_languages[0] if len(valid_languages) == 1 else None)
fn = metadata[saved_country][valid_languages[0]]["Intro"] if valid_languages else metadata["USA"]["English"]["Intro"]
intro_text = ""
if os.path.exists(fn):
with open(fn, "r", encoding="utf-8") as f:
intro_text = f.read()
if intro_text:
t, b = split_intro_text(intro_text)
return (
gr.update(choices=countries, value=saved_country, interactive=True),
gr.update(choices=valid_languages, value=final_language, interactive=True),
gr.update(value=t),
gr.update(value=b),
)
return (
gr.update(choices=countries, value=saved_country, interactive=True),
gr.update(choices=valid_languages, value=final_language, interactive=True),
gr.skip(),
gr.skip(),
)
def build_selection_page(metadata_dict):
with gr.Column(visible=True, elem_classes=["compact-container"]) as selection_page:
with open(metadata_dict["USA"]["English"]["Intro"], "r", encoding="utf-8") as f:
INTRO_TEXT = f.read()
title, body = split_intro_text(INTRO_TEXT)
intro_title = gr.Markdown(title)
# Locale choice dropdowns next to each other
countries = sort_with_pyuca(list(metadata_dict.keys()))
with gr.Row():
country_choice = gr.Dropdown(
choices=countries,
label="Country",
allow_custom_value=False,
value=None,
interactive=True,
)
language_choice = gr.Dropdown(
choices=[],
label="Language",
allow_custom_value=False,
interactive=False, # Initially disabled.
)
intro_body = gr.Markdown(body)
with gr.Column(visible=True) as signin_section:
signin_title = gr.Markdown("## Please select your profile")
login_btn = gr.LoginButton("Sign in with Hugging Face (optional)") # TODO: Multilingual
user_name = gr.Markdown("", visible=False)
username = gr.Markdown("", visible=False)
email_inp = gr.Textbox(
label="Email",
type="email",
placeholder="you@example.com",
info="Add your email to save your work and review or edit it later.",
)
# Primary call-to-action. Hidden until the user starts entering an
# email and only enabled for a valid address (see
# update_profile_button_state). HF login auto-fills the email.
prof_btn = gr.Button("Continue", variant="primary", visible=False, interactive=False)
# Secondary, de-emphasized escape hatch with an explicit consequence
# so the trade-off of anonymous submissions is clear up front.
anon_note = gr.Markdown(
"<span style='opacity:0.7;font-size:0.85em;'>"
"Prefer not to log in? You can continue anonymously, but you won't be able to review, edit, or delete your submissions later. "
"Also, at the end of the data collection campaign, we will use account information to create a list of all contributors and provide rewards to the best ones. "
"For details, see our website [https://team-wonders.github.io/](https://team-wonders.github.io/)."
"</span>"
)
anon_btn = gr.Button("Proceed as Anonymous", variant="secondary", size="sm")
with gr.Column(visible=False) as selection_section:
selection_title = gr.Markdown("## Please select your country", visible=False)
proceed_btn = gr.Button("Annotate", variant="primary", visible=False)
# When the country selection changes, update the language dropdown.
country_choice.change(
fn=partial(update_language_options, metadata=metadata_dict),
inputs=country_choice,
outputs=[language_choice, intro_title, intro_body]
)
# Event listeners
# username.change(
# fn=enable_country,
# outputs=[country_choice],
# )
email_inp.change(
fn=update_profile_button_state,
inputs=[email_inp],
outputs=[prof_btn],
)
# with gr.Row():
# username = gr.Textbox(label="Email (optional)", type="email", elem_id="username_text")
# password = gr.Textbox(label="Password (optional)", type="password", elem_id="password_text")
# with gr.Row():
# login_btn = gr.Button("Login", elem_id="login_btn")
# sign_up_btn = gr.Button("Sign up", elem_id="sign_up_btn")
# reset_password_btn = gr.Button("Reset Password", elem_id="reset_password_btn")
# logout_btn = gr.Button("Logout", elem_id="logout_btn",visible=False)
# gr.Markdown("### Choose how you want to continue")
# with gr.Row():
# login_btn = gr.LoginButton("Sign in with Hugging Face") # TODO: Multilingual
# anon_btn = gr.Button("Proceed as Anonymous")
# username = gr.Markdown("", visible=False)
# login_status = gr.Markdown("")
# with gr.Row():
# change_password_field = gr.Textbox(
# label="Change Password", type="password", elem_id="change_password_field", visible=True
# )
# change_password_field_confirm = gr.Textbox(
# label="Confirm New Password", type="password", elem_id="change_password_field_confirm", visible=True
# )
# with gr.Row():
# change_password_btn = gr.Button("Change Password", elem_id="change_password_btn", visible=True)
# change_password_status = gr.Markdown("")
return (
selection_page, country_choice, language_choice, proceed_btn, anon_btn, login_btn, prof_btn,
username, user_name, email_inp, intro_title, intro_body, signin_title, selection_title,
signin_section, selection_section,
)