KeenWoo's picture
Update app_AD2class.py
4fe2d98 verified
raw
history blame
6.36 kB
# app_ADclass.py
# Main application file. Focuses on building the Gradio UI and handling
# user interactions. It imports assessment logic from MMSE.py and utilities from utils.py.
import gradio as gr
# Import the separated modules
import MMSE
import utils
# --- UI Interaction Handlers ---
def update_view(new_index, all_answers):
"""Updates the UI elements when navigating to a new question."""
q_data = MMSE.STRUCTURED_QUESTIONS[new_index]
progress = f"## {q_data['main_cat']} - Question {q_data['main_num']}{q_data['sub_letter']} ({new_index + 1} of {MMSE.TOTAL_QUESTIONS})"
drawing_q_visible = (new_index == MMSE.DRAWING_Q_INDEX)
return (
f"Say πŸ”Š {q_data['question']}",
all_answers[new_index],
new_index,
progress,
q_data["instruction"],
MMSE.QUESTION_CHOICES[new_index],
gr.update(visible=drawing_q_visible),
None # Clear the audio_input component
)
def save_and_navigate(direction, current_index, current_answer, all_answers):
"""Saves the current answer and moves to the next/previous question."""
all_answers[current_index] = current_answer
if direction == "next":
new_index = min(current_index + 1, MMSE.TOTAL_QUESTIONS - 1)
else: # prev
new_index = max(current_index - 1, 0)
return update_view(new_index, all_answers) + (all_answers,)
def jump_to_question(selected_choice, current_index, current_answer, all_answers):
"""Saves the current answer and jumps to the selected question."""
if not selected_choice:
return update_view(current_index, all_answers) + (all_answers,)
all_answers[current_index] = current_answer
new_index = MMSE.QUESTION_CHOICES.index(selected_choice)
return update_view(new_index, all_answers) + (all_answers,)
def save_final_answer(current_index, current_answer, all_answers):
"""A dedicated function to save the last answer before submitting."""
all_answers[current_index] = current_answer
return all_answers
def process_evaluation_MMSE(answers, image_upload):
"""Calls the evaluation function from the MMSE module and prepares UI updates."""
score_lines, total = MMSE.evaluate_MMSE(answers, image_upload)
# This function now returns the data AND the UI updates
return score_lines, total, gr.update(visible=False), gr.update(visible=True)
def reset_app():
"""Resets the entire application state to its initial view."""
initial_q = MMSE.STRUCTURED_QUESTIONS[0]
return (
0, # question_index
[""] * MMSE.TOTAL_QUESTIONS, # answers
"", # score_lines
"", # total
f"Say πŸ”Š {initial_q['question']}", # question_button
f"## {initial_q['main_cat']} - Question {initial_q['main_num']}{initial_q['sub_letter']} (1 of {MMSE.TOTAL_QUESTIONS})", # progress_text
initial_q["instruction"], # instruction_display
"", # answer_text
MMSE.QUESTION_CHOICES[0], # jump_nav
None, # audio_input
None, # image_upload
gr.update(visible=False), # start_over_btn
gr.update(visible=True), # submit_btn
None # tts_audio
)
def build_ui():
"""Builds and returns the Gradio interface."""
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# Mini Mental State Exam (MMSE) with Multimodal Inputs")
question_index = gr.State(0)
answers = gr.State([""] * MMSE.TOTAL_QUESTIONS)
jump_nav = gr.Dropdown(choices=MMSE.QUESTION_CHOICES, value=MMSE.QUESTION_CHOICES[0], label="Jump to Question")
initial_q = MMSE.STRUCTURED_QUESTIONS[0]
progress_text = gr.Markdown(f"## {initial_q['main_cat']} - Question {initial_q['main_num']}{initial_q['sub_letter']} (1 of {MMSE.TOTAL_QUESTIONS})")
instruction_display = gr.Markdown(initial_q["instruction"])
question_button = gr.Button(f"Say πŸ”Š {initial_q['question']}", variant="secondary")
with gr.Group():
answer_text = gr.Textbox(label="Your Answer (type or record below)")
audio_input = gr.Audio(sources=["microphone"], label="🎀 Record Your Answer Here")
image_upload = gr.Image(type="filepath", label="Upload Drawing for Drawing Question", visible=False)
with gr.Row():
prev_btn = gr.Button("⬅️ Previous Question")
next_btn = gr.Button("Next Question ➑️")
tts_audio = gr.Audio(autoplay=True, visible=False)
score_lines = gr.Textbox(label="Score by Question", lines=MMSE.TOTAL_QUESTIONS)
total = gr.Textbox(label="Total Score")
submit_btn = gr.Button("βœ… Submit All Answers")
start_over_btn = gr.Button("πŸ”„ Start Over", visible=False)
# --- Event Listeners ---
audio_input.change(utils.transcribe, inputs=audio_input, outputs=answer_text)
question_button.click(MMSE.speak_question, inputs=question_index, outputs=tts_audio)
outputs_list = [question_button, answer_text, question_index, progress_text,
instruction_display, jump_nav, image_upload, audio_input, answers]
next_btn.click(save_and_navigate, inputs=[gr.Textbox("next", visible=False), question_index, answer_text, answers], outputs=outputs_list)
prev_btn.click(save_and_navigate, inputs=[gr.Textbox("prev", visible=False), question_index, answer_text, answers], outputs=outputs_list)
jump_nav.change(jump_to_question, inputs=[jump_nav, question_index, answer_text, answers], outputs=outputs_list)
submit_btn.click(
save_final_answer, inputs=[question_index, answer_text, answers], outputs=answers
).then(
fn=process_evaluation_MMSE, inputs=[answers, image_upload], outputs=[score_lines, total, submit_btn, start_over_btn]
)
reset_outputs = [question_index, answers, score_lines, total, question_button, progress_text,
instruction_display, answer_text, jump_nav, audio_input, image_upload,
start_over_btn, submit_btn, tts_audio]
start_over_btn.click(reset_app, inputs=None, outputs=reset_outputs)
return demo
if __name__ == "__main__":
# Perform one-time setup before launching the UI
MMSE.pregenerate_audio()
# Build and launch the UI
app = build_ui()
app.launch()