merterm/Learning-Games-Experiment-2
0
1from pathlib import Path2import zipfile3from typing import List, Tuple, Optional, Set4import json5import dataclasses6import gradio as gr7import asyncio8from openai import AsyncOpenAI9import tempfile10import os11import argparse12import gradio as gr13import random14import os15from pathlib import Path16import time17import matplotlib.pyplot as plt18import io19 20# BASE_URL = os.getenv("BASE_URL")21API_KEY = os.getenv("API_KEY")22 23BASE_URL = "https://api.openai.com"24print(f"BASE_URL: {BASE_URL}")25print(f"API_KEY: {API_KEY}")26if not BASE_URL or not API_KEY:27 raise ValueError("BASE_URL or API_KEY environment variables are not set")28 29client = AsyncOpenAI(api_key=API_KEY)30 31 32##########################################################################################################33# HELPER FUNCTIONS #34##########################################################################################################35async def run_command(cmd, timeout=5):36 process = await asyncio.create_subprocess_exec(37 *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE38 )39 try:40 stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=timeout)41 return (42 stdout.decode("utf-8", errors="ignore"),43 stderr.decode("utf-8", errors="ignore"),44 process.returncode,45 )46 except asyncio.TimeoutError:47 process.kill()48 return None, None, None49 50# def echo(message, history):51# return random.choice(["Yes", "No"])52 53# Prompt chatgpt with a message54async def chatgpt(prompt, history):55 messages = [56 {"role": "system", "content": ""}57 ]58 print(history)59 if history:60 messages += history61 messages += [{"role": "user", "content": prompt}]62 try:63 response = await client.chat.completions.create(64 model="gpt-4o",65 messages=messages66 )67 except Exception as e:68 print(e)69 return "I'm sorry, I'm having trouble. Could you please try again?"70 return response.choices[0].message.content71 72async def process_submission(finished_code, user_state):73 # Compile and execute user code, generate plot74 print("Compiling and plotting code")75 print(f"Code: {finished_code}")76 with tempfile.NamedTemporaryFile(delete=True, suffix=".py") as f:77 f.write(finished_code.encode("utf-8"))78 f.flush()79 stdout, stderr, exit_code = await run_command(["python", f.name], timeout=5)80 81 # result = await run_python_code(finished_code)82 print(f"Result: {stdout}")83 84 # Check if plot was created85 if f"temp_plot_{user_state}.png" in os.listdir():86 return f"temp_plot_{user_state}.png", stdout, stderr87 else:88 return "No plot generated", stdout, stderr89 # return gr.update(value="No plot generated", visible=True), None90 91# Function to create a zip file92def create_zip_file(jsonl_path, image_path, zip_path):93 with zipfile.ZipFile(zip_path, 'w') as zipf:94 zipf.write(jsonl_path, arcname=Path(jsonl_path).name)95 zipf.write(image_path, arcname=Path(image_path).name)96 97# Function to assign plots to users randomly98def pick_random_image_for_user(users, images):99 assigned_images = {}100 for user in users:101 assigned_images[user] = random.sample(images, 5)102 # print(assigned_images)103 return assigned_images104 105##########################################################################################################106# GRADIO INTERFACE SETUP #107##########################################################################################################108# Define each page as a separate function109def create_interface(users):110 max_num_submissions = 5111 plot_time_limit = 130112 # plot_time_limit = 10113 dialogue_time_limit = 600114 # dialogue_time_limit = 10115 116 with gr.Blocks() as demo:117 user_state = gr.State()118 notes_state = gr.State([])119 dialogue_state = gr.State([]) # Store the conversation with the LLM120 submission_count = gr.State(0) # Track number of code submissions121 produced_codes = gr.State([])122 previous_text = gr.State("") # Track previous text in notepad123 random.seed(time.time())124 125 folder_path = "ChartMimic/dataset/ori_500"126 images = [f for f in os.listdir(folder_path) if f.endswith(('png', 'jpg', 'jpeg'))]127 chosen_image = os.path.join(folder_path, random.choice(images))128 assigned_images = pick_random_image_for_user(users, images)129 130 reference_code = chosen_image.replace(".png", ".py")131 chosen_image_state = gr.State(chosen_image)132 reference_code_state = gr.State(reference_code)133 134 expertise_survey_responses = gr.State({})135 uncertainty_survey_part_1_responses = gr.State({}) # Store responses to the uncertainty survey136 uncertainty_survey_part_2_responses = gr.State({}) # Store responses to the uncertainty survey137 uncertainty_survey_part_3_responses = gr.State({}) # Store responses to the uncertainty survey138 demographic_survey_responses = gr.State({}) # Store responses to the demographic survey139 140 ##########################################################################################################141 # UI SETUP FOR EACH PAGE #142 ##########################################################################################################143 # Page 1: Login, Add login components144 with gr.Column(visible=True) as login_row:145 instructions_text = gr.Markdown(f"## Instructions\n\nWelcome to Learning Games! PLEASE READ THE FOLLOWING INSTRUCTIONS CAREFULLY. \146 \n\nThis game consists of three parts:\n\n**Part 1: Inspection of the Chart**\n\nYou will be given \147 an image of a scientific chart. Please inspect it carefully and think about ways to reproduce it in \148 Python. You will have access to this plot throughout the experiment. You can take notes while \149 inspecting, a notepad will be given to you. At the end of the game, you will be asked to write \150 the code to recreate this chart. \n\n**Part 2: Chatting with a Teacher**\n\nIn this part, \151 you will have access to a teacher LLM! This interaction will be limited to only {int(dialogue_time_limit/60)}\152 minutes. You can use it to help you learn how to code this chart. Please be wise of your time \153 with the teacher LLM; by the end of this part, you will not be able to interact with the \154 LLM again. \n\n**Part 3: Writing the Code for the Chart**\n\nThis is the final crucial step. You will \155 have {max_num_submissions} attempts to reproduce the plot by writing, compiling, and running Python \156 code. You will be given a code skeleton to help you out, where you will fill in some required coding \157 components. You will be given only {max_num_submissions} attempts to compile your plot. \n\n Throughout \158 your interactions, you will be asked three times to rank your uncertainty: once during the inspection \159 of the chart, once after interacting with the LLM, and once after you submit your code. \160 \n\nAt the end of the game, you will be asked to fill out a short demographic survey. \161 Then you will be able to download your session data. Please download and send the zip file to <inan.m@northeastern.edu>. \162 \n\n**WARNING: You will not be able to go back to previous parts once you proceed, or reload the page.** \163 \n\n**Reminder: this is just a game; your performance will not affect your grade in the class in \164 any form.** \n\n \n\n ### Please login to start the game. We will first ask some questions about your \165 expertise, and part 1 will start immediately afterwards.")166 username_input = gr.Textbox(label="Username")167 login_button = gr.Button("Login")168 login_error_message = gr.Markdown(visible=False)169 170 # User Expertise Survey171 with gr.Column(visible=False) as expertise_survey:172 gr.Markdown("### Student Expertise Survey")173 gr.Markdown("Here is a short questionnaire before you get started. Please answer the following questions as accurately as possible.")174 expertise_survey_question1 = gr.CheckboxGroup(175 ["1 - No experience", "2 - Beginner", "3 - Intermediate", "4 - Advanced", "5 - Expert"],176 label="Question 1: On a scale of 1-5, what is your experience level of coding in Python? "177 )178 expertise_survey_question2 = gr.CheckboxGroup(179 ["1 - No experience", "2 - Beginner", "3 - Intermediate", "4 - Advanced", "5 - Expert"],180 label="Question 2: On a scale of 1-5, what is your experience level of using the Matplotlib library? "181 )182 expertise_survey_submit_button = gr.Button("Submit")183 184 185 # Instructions Page186 with gr.Column(visible=False) as instructions_page:187 instructions_text = gr.Markdown(f"## Part 1: Inspection of the Chart \n\nBelow, you are given a scientific chart. \188 Please inspect it carefully and think about ways to reproduce it in Python. You will \189 have access to this plot throughout the experiment. At the end of the game, you will \190 be asked to write the code to recreate this chart. You will be given a code skeleton \191 and the necessary data at the end. You can take notes below. You will have \192 {int(plot_time_limit/60)} minutes to take a look at this plot, starting now…")193 instruction_image_1 = gr.Image(show_label=False, height=500)194 plot_time_remaining = gr.Textbox(value=f"{(int(plot_time_limit/60)):02}:{(plot_time_limit%60):02}", label="Time Remaining", interactive=False)195 # questionnaire = gr.Form(["Question 1", "Question 2"], visible=False)196 197 # Uncertainty Survey Page198 with gr.Column(visible=False) as uncertainty_survey_part_1:199 instruction_image_2 = gr.Image(show_label=False, height=300)200 gr.Markdown("### Uncertainty Survey")201 gr.Markdown("Here is a short questionnaire before you get started. Please answer the following questions as accurately as possible.")202 uncertainty_survey_part_1_question1 = gr.CheckboxGroup(203 ["1 - Not certain", "2 - Somewhat certain", "3 - Moderately certain", "4 - Somewhat certain", "5 - Very certain"],204 label="Question 1: On a scale of 1-5, how certain are you that you can code this plot? "205 )206 207 uncertainty_survey_part_1_submit_button = gr.Button("Submit")208 209 # Dialogue Page with 5-minute timer210 with gr.Column(visible=False) as dialogue_page:211 instruction_text = gr.Markdown(f"## Part 2: Chatting with a Teacher \n\nNow, you will have access to a teacher LLM. This interaction will be limited to only {int(dialogue_time_limit/60)} minutes. \212 The countdown starts when you send your first message. You can use it to help you learn \213 how to code this chart. But be wise of your time; by the end of this part, \214 you will not be able to interact with the LLM again. Please use your time with \215 the LLM wisely, and think through your code solution before committing.\216 \n\n **You may want to prompt the LLM to teach you how to produce code for this chart** \217 **rather than having it output code directly. Please think about how to prompt the LLM to do this.**")218 with gr.Row():219 instruction_image_3 = gr.Image(show_label=False, height=400)220 with gr.Column():221 # chatbot = gr.ChatInterface(echo, type="messages")222 chatbot = gr.ChatInterface(chatgpt, type="messages", examples=["Teach me how to ...", "I want to learn step-by-step ...", "Explain to me slowly ..."])223 chatbot.chatbot.height = 400224 chatbot.chatbot.label = "Teacher LLM"225 # start_dialogue_button = gr.Button("Start Dialogue")226 part_2_time_remaining = gr.Textbox(value=f"{(int(dialogue_time_limit/60)):02}:{(dialogue_time_limit%60):02}", label="Time Remaining", interactive=False)227 228 # Uncertainty Survey Part 2229 with gr.Column(visible=False) as uncertainty_survey_part_2:230 instruction_image_4 = gr.Image(show_label=False, height=500)231 gr.Markdown("### Uncertainty Survey")232 gr.Markdown("Here is a short questionnaire after you have interacted with the teacher LLM. \233 Please answer the following questions as accurately as possible.")234 uncertainty_survey_part_2_question1 = gr.CheckboxGroup(235 ["1 - Not at all", "2 - Slightly", "3 - Moderately", "4 - Very", "5 - Extremely"],236 label="Question 1: On a scale of 1-5, how much did the teacher LLM help you in learning how to code this plot? "237 )238 uncertainty_survey_part_2_question2 = gr.CheckboxGroup(239 ["1 - Not certain", "2 - Somewhat certain", "3 - Moderately certain", "4 - Somewhat certain", "5 - Very certain"],240 label="Question 2: On a scale of 1-5, how certain are you that you can code this plot now? "241 )242 uncertainty_survey_part_2_question3 = gr.CheckboxGroup(243 ["1 - Not certain", "2 - Somewhat certain", "3 - Moderately certain", "4 - Somewhat certain", "5 - Very certain"],244 label="Question 3: On a scale of 1-5, how certain are you that you can code this plot even without the teacher LLM? "245 )246 uncertainty_survey_part_2_question4 = gr.CheckboxGroup(247 ["1 - Not on topic at all", "2 - Somewhat not on topic", "3 - Moderately on topic", "4 - Somewhat on topic", "5 - Mostly on topic"],248 label="Question 4: On a scale of 1-5, how much did the LLM stay on topic (i.e. did it answer your questions specifically)?"249 )250 uncertainty_survey_part_2_submit_button = gr.Button("Submit")251 252 # Final Code Editor Page253 with gr.Column(visible=False) as final_page:254 instruction_text = gr.Markdown(f"## Part 3: Writing the Code for the Chart \n\nThis is the final crucial step. \255 You need to reproduce the original plot by writing, compiling, and running Python code. \256 You are given a code skeleton below to help you, where you will fill in the \257 required coding components. When you compile, you will be able to see the output of \258 your code, in addition to the plot. You will be given only {max_num_submissions} attempts to compile your plot.")259 instruction_image_5 = gr.Image(show_label=False, height=400)260 code_editor = gr.Code(language="python", label="Code Editor")261 run_code_button = gr.Button("Compile & Run Code")262 processing_message = gr.Textbox(value="Processing...", visible=False)263 with gr.Row():264 retry_button = gr.Button("Retry", visible=False)265 finished_button = gr.Button("Finished", visible=False)266 with gr.Row():267 stdout_message = gr.Textbox(visible=True, label="Code Output", value="")268 submission_counter = gr.Number(visible=True, label="Number of Remaining Submissions", value=max_num_submissions)269 plot_output = gr.Image(visible=False, height=400)270 271 # Uncertainty Survey Part 3272 with gr.Column(visible=False) as uncertainty_survey_part_3:273 with gr.Row():274 instruction_image_6 = gr.Image(label="Original Chart", height=300)275 generated_image = gr.Image(label="Your Generated Chart", height=300)276 gr.Markdown("### Uncertainty Survey")277 gr.Markdown("Here is a short questionnaire after you have finalized your code. Please answer the following questions as accurately as possible.")278 uncertainty_survey_part_3_question1 = gr.CheckboxGroup(279 ["1 - Not at all", "2 - Slightly", "3 - Moderately", "4 - Very", "5 - Extremely"],280 label="Question 1: On a scale of 1-5, how much did you rely on the teacher LLM and your notes to code this chart? "281 )282 uncertainty_survey_part_3_question2 = gr.CheckboxGroup(283 ["1 - Much harder", "2 - Harder", "3 - As expected", "4 - Easier", "5 - Much easier"],284 label="Question 2: On a scale of 1-5, was the task easier or harder than you expected? "285 )286 uncertainty_survey_part_3_question3 = gr.CheckboxGroup(287 ["1 - Could not produce", "2 - Very inaccurate", "3 - Moderately inaccurate", "4 - Somewhat accurate", "5 - Very accurate"],288 label="Question 3: On a scale of 1-5, how accurate is your chart compared to the original? "289 )290 uncertainty_survey_part_3_question4 = gr.CheckboxGroup(291 ["1 - No experience", "2 - Beginner", "3 - Intermediate", "4 - Advanced", "5 - Expert"],292 label="Question 4: On a scale of 1-5, how would you rate your experience in Python now? "293 )294 uncertainty_survey_part_3_question5 = gr.CheckboxGroup(295 ["1 - No experience", "2 - Beginner", "3 - Intermediate", "4 - Advanced", "5 - Expert"],296 label="Question 5: On a scale of 1-5, how would you rate your experience in using the Matplotlib library now? "297 )298 uncertainty_survey_part_3_question6 = gr.CheckboxGroup(299 ["1 - Very ambiguous", "2 - Somewhat ambiguous", "3 - Neither ambiguous nor clear", "4 - Somewhat clear", "5 - Very clear"],300 label="Question 5: On a scale of 1-5, throughout this experiment how ambigous were the instructions?"301 )302 uncertainty_survey_part_3_question7 = gr.CheckboxGroup(303 ["1 - Very ambiguous", "2 - Somewhat ambiguous", "3 - Neither ambiguous nor clear", "4 - Somewhat clear", "5 - Very clear"],304 label="Question 5: On a scale of 1-5, throughout this experiment how ambigous was the given plot?"305 )306 uncertainty_survey_part_3_submit_button = gr.Button("Submit")307 308 # Demographic Survey Page309 with gr.Column(visible=False) as demographic_survey:310 gr.Markdown("### Demographic Survey")311 gr.Markdown("Please answer the following questions to help us understand your background.")312 demographic_survey_question1 = gr.CheckboxGroup(313 ["Undergraduate", "Graduate", "PhD", "Postdoc", "Faculty", "Industry Professional", "Other"],314 label="What is your current academic status?"315 )316 demographic_survey_question2 = gr.CheckboxGroup(317 ["Bouvé College of Health Sciences", "College of Arts, Media and Design", "College of Engineering", "College of Professional Studies", "College of Science", "D'Amore-McKim School of Business", "Khoury College of Computer Sciences", "School of Law", "Mills College at Northeastern", "Other"],318 label="What is your college?"319 )320 demographic_survey_question3 = gr.CheckboxGroup(321 ["18-23", "23-27", "27-31", "31-35", "35-43", "43+"],322 label="What is your age group?"323 )324 demographic_survey_question4 = gr.CheckboxGroup(325 ["Woman", "Man", "Transgender", "Non-binary", "Prefer not to say"],326 label="What is your gender identity?"327 )328 demographic_survey_question5 = gr.CheckboxGroup(329 ["American Indian or Alaska Native", "Asian or Asian American", "Black or African American", "Hispanic or Latino/a/x", "Native Hawaiian or Other Pacific Islander", "Middle Eastern or North African", "White or European", "Other"],330 label="What is your ethnicity? (Select all that apply)"331 )332 demographic_survey_submit_button = gr.Button("Submit")333 334 # Exit Page335 with gr.Column(visible=False) as exit_page:336 gr.Markdown("## Thank you for participating in the Learning Games! \n\nYour responses have been recorded. Please download your session data below, and send the zip file to <inan.m@northeastern.edu>.")337 download_button = gr.Button("Download Session Data")338 file_to_download = gr.File(label="Download Results")339 340 341 # Adding the notepad available on all pages342 with gr.Column(visible=False) as notepad_column:343 notepad = gr.Textbox(lines=10, placeholder="Take notes here", value="", label="Notepad", elem_id="notepad")344 345 346 ##########################################################################################################347 # FUNCTION DEFINITIONS FOR EACH PAGE #348 ##########################################################################################################349 def on_login(users: Set[str], folder_path, assigned_images):350 def callback(username):351 if username not in users:352 return (353 gr.update(visible=True), # login still visible354 gr.update(visible=False), # main interface still not visible355 gr.update(visible=True, value="Username not found"),356 "",357 gr.update(), # for image state to change with the user358 gr.update(), # for ref code359 )360 chosen_image = os.path.join(folder_path, random.choice(assigned_images[username]))361 return (362 gr.update(visible=False), # login hidden363 gr.update(visible=True), # main interface visible364 gr.update(visible=False), # login error message hidden365 username,366 chosen_image, # for image state367 chosen_image.replace(".png", ".py")368 )369 370 return callback371 372 def update_all_instruction_images(chosen_image):373 return (374 gr.update(value=chosen_image),375 gr.update(value=chosen_image),376 gr.update(value=chosen_image),377 gr.update(value=chosen_image),378 gr.update(value=chosen_image),379 gr.update(value=chosen_image)380 )381 382 def extract_code_context(reference_code, user_state):383 with open(reference_code, "r") as f:384 code_context = f.read()385 print(code_context)386 # Remove everything between Part 3: Plot Configuration and Rendering and Part 4: Saving Output387 start_index = code_context.find("# ===================\n# Part 3: Plot Configuration and Rendering\n# ===================")388 end_index = code_context.find("# ===================\n# Part 4: Saving Output\n# ===================")389 code_context = code_context[:start_index] + "# ===================\n# Part 3: Plot Configuration and Rendering\n# ===================\n\n # TODO: YOUR CODE GOES HERE #\n\n\n" + code_context[end_index:]390 # plt.savefig is the last line of the code, remove it391 end_index = code_context.find("plt.savefig")392 code_context = code_context[:end_index]393 # and replace with plt.show()394 code_context += f"plt.savefig('temp_plot_{user_state}.png')\n"395 # code_context += "plt.show()\n"396 return code_context397 398 def handle_expertise_survey_response(q1, q2):399 # Example: Store responses in a dictionary or process as needed400 response = {401 "Question 1": q1,402 "Question 2": q2403 }404 return response405 406 # Function to handle form submission407 def handle_part1_survey_response(q1):408 # Example: Store responses in a dictionary or process as needed409 response = {410 "Question 1": q1411 }412 return response413 414 def handle_part2_survey_response(q1, q2, q3, q4):415 # Example: Store responses in a dictionary or process as needed416 response = {417 "Question 1": q1,418 "Question 2": q2,419 "Question 3": q3,420 "Question 4": q4421 }422 return response423 424 def handle_final_survey_response(q1, q2, q3, q4, q5, q6, q7):425 # Example: Store responses in a dictionary or process as needed426 response = {427 "Question 1": q1,428 "Question 2": q2,429 "Question 3": q3,430 "Question 4": q4,431 "Question 5": q5,432 "Question 6": q6,433 "Question 7": q7434 }435 return response436 437 def handle_demographic_survey_response(q1, q2, q3, q4, q5):438 # Example: Store responses in a dictionary or process as needed439 response = {440 "Question 1": q1,441 "Question 2": q2,442 "Question 3": q3,443 "Question 4": q4,444 "Question 5": q5445 }446 return response447 448 # Timer logic for instructions page449 def plot_countdown_timer():450 time_limit = plot_time_limit451 start_time = time.time()452 while time.time() - start_time < time_limit:453 mins, secs = divmod(time_limit - int(time.time() - start_time), 60)454 yield f"{mins:02}:{secs:02}", gr.update(), gr.update(visible=False)455 yield "00:00", gr.update(visible=False), gr.update(visible=True)456 457 # Timer logic for dialogue page458 def dialogue_countdown_timer():459 time_limit = dialogue_time_limit460 start_time = time.time()461 while time.time() - start_time < time_limit:462 mins, secs = divmod(time_limit - int(time.time() - start_time), 60)463 yield f"{mins:02}:{secs:02}", gr.update(visible=True), gr.update(visible=False)464 yield "00:00", gr.update(visible=False), gr.update(visible=True)465 466 # New function to save dialogue state467 def save_dialogue_state(dialogue, dialogue_state):468 timestamp = time.strftime("%Y-%m-%d %H:%M:%S")469 print(dialogue)470 print(dialogue_state)471 return dialogue_state + [timestamp, dialogue]472 473 # # Save notes, dialogue, and answers into a file for download474 # def prepare_download(notes, dialogue, answers):475 # results = {476 # "notes": notes,477 # "dialogue": dialogue,478 # "answers": answers479 # }480 # with open("session_data.json", "w") as f:481 # json.dump(results, f)482 # return "session_data.json"483 484 # Add download functionality485 def get_download_link(user_state, chosen_image, notes_state, dialogue_state, 486 produced_codes, reference_code, survey1, survey2, survey3, survey4, survey5):487 jsonl_path = Path(f"session_data_{user_state}.jsonl")488 with open(jsonl_path, "w") as f:489 f.write(490 json.dumps(491 {492 "username": user_state,493 "chosen_image": chosen_image,494 "notes": notes_state,495 "dialogue_state": dialogue_state,496 "produced_codes": produced_codes,497 "reference_code": reference_code,498 "expertise_survey": survey1,499 "uncertainty_survey_part1": survey2,500 "uncertainty_survey_part2": survey3,501 "uncertainty_survey_part3": survey4,502 "demographics_survey": survey5503 }504 )505 + "\n"506 )507 508 image_path = Path(f"temp_plot_{user_state}.png")509 zip_path = Path(f"session_data_{user_state}.zip")510 create_zip_file(jsonl_path, image_path, zip_path)511 512 if not zip_path.exists():513 return None514 return gr.File(value=str(zip_path), visible=True)515 516 async def on_submit(finished_code, submission_count, produced_codes, user_state):517 if (max_num_submissions-(submission_count+1)) == 0:518 # raise gr.Error("Max submissions reached")519 yield (520 gr.update(visible=False),521 gr.update(visible=False), # Hide run code button522 gr.update(visible=False), # Hide retry button523 gr.update(visible=True), # Show finished button524 gr.update(visible=False), # Hide plot output525 submission_count,526 produced_codes,527 gr.update(visible=False), # stdout528 gr.update(visible=False) #submission counter529 )530 raise gr.Error("Max submissions reached")531 else:532 submission_count += 1533 # Show processing message and hide other elements534 yield (535 gr.update(visible=True), # Show processing message536 gr.update(visible=False), # Hide run code button537 gr.update(visible=False), # Hide retry button538 gr.update(visible=False), # Hide finished button539 gr.update(visible=False), # Hide plot output540 submission_count,541 produced_codes,542 gr.update(visible=False), # stdout543 gr.update(value=max_num_submissions-submission_count) #submission counter544 )545 546 # Process the submission547 plot_output, stdout, stderr = await process_submission(finished_code, user_state)548 549 # Hide processing message and show result550 yield (551 gr.update(visible=False), # Hide processing message552 gr.update(visible=False), # Hide submit button553 gr.update(visible=True), # Show retry button554 gr.update(visible=True), # Show finished button555 gr.update(visible=True, value=plot_output), # Show plot output556 submission_count,557 produced_codes + [finished_code],558 gr.update(visible=True, value=stdout+stderr), # stdout559 gr.update() #submission counter560 )561 562 def on_retry(finished_code, produced_codes):563 # Hide processing message and show result564 yield (565 gr.update(visible=False), # Hide processing message566 gr.update(visible=True), # Show submit button567 gr.update(visible=False), # Hide retry button568 gr.update(visible=False), # Hide finished button569 gr.update(visible=False), # Hide plot output570 produced_codes + [finished_code]571 )572 573 def filter_paste(previous_text, new_text):574 # Check if the new input is a result of pasting (by comparing lengths or content)575 print(f"New text: {new_text}")576 changed_text = new_text.replace(previous_text, "")577 if len(changed_text) > 10: # Paste generally increases length significantly578 return previous_text, previous_text # Revert to previous text if paste is detected579 previous_text = new_text580 print(f"Previous text: {previous_text}")581 return previous_text, new_text582 583 def save_notes_with_timestamp(notes, notes_state):584 timestamp = time.strftime("%Y-%m-%d %H:%M:%S")585 notes_state.append(f"{timestamp}: {notes}")586 return notes_state587 588 ##########################################################################################################589 # EVENT HANDLERS FOR EACH PAGE #590 ##########################################################################################################591 # Page navigation592 login_button.click(593 on_login(users, folder_path, assigned_images),594 inputs=[username_input],595 outputs=[login_row, expertise_survey, login_error_message, user_state, chosen_image_state, reference_code_state],596 )597 598 # login_button.click(lambda: os.path.join(folder_path, random.choice(images)), outputs=[chosen_image_state])599 600 # login_button.click(lambda: chosen_image_state.replace(".png", ".py"), inputs=[chosen_image_state], outputs=[reference_code_state])601 602 expertise_survey_submit_button.click(603 handle_expertise_survey_response,604 inputs=[expertise_survey_question1, expertise_survey_question2],605 outputs=[expertise_survey_responses]606 )607 608 expertise_survey_submit_button.click(609 lambda: (gr.update(visible=False), gr.update(visible=True), gr.update(visible=True)), # Hide survey, show dialogue610 inputs=[], outputs=[expertise_survey, instructions_page, notepad_column]611 )612 613 expertise_survey_submit_button.click(614 update_all_instruction_images,615 inputs=[chosen_image_state], outputs=[instruction_image_1, instruction_image_2,616 instruction_image_3, instruction_image_4,617 instruction_image_5, instruction_image_6]618 )619 620 expertise_survey_submit_button.click(plot_countdown_timer, outputs=[plot_time_remaining, instructions_page, uncertainty_survey_part_1])621 622 uncertainty_survey_part_1_submit_button.click(623 handle_part1_survey_response,624 inputs=[uncertainty_survey_part_1_question1],625 outputs=[uncertainty_survey_part_1_responses]626 )627 628 uncertainty_survey_part_1_submit_button.click(629 lambda: (gr.update(visible=False), gr.update(visible=True)), # Hide survey, show dialogue630 inputs=[], outputs=[uncertainty_survey_part_1, dialogue_page]631 )632 633 chatbot.chatbot.change(634 dialogue_countdown_timer,635 outputs=[part_2_time_remaining, dialogue_page, uncertainty_survey_part_2],636 trigger_mode = "once"637 )638 639 # Update to save dialogue state on change640 chatbot.chatbot.change(641 save_dialogue_state,642 inputs=[chatbot.chatbot, dialogue_state],643 outputs=[dialogue_state]644 )645 646 uncertainty_survey_part_2_submit_button.click(647 handle_part2_survey_response,648 inputs=[uncertainty_survey_part_2_question1, uncertainty_survey_part_2_question2, 649 uncertainty_survey_part_2_question3, uncertainty_survey_part_2_question4],650 outputs=[uncertainty_survey_part_2_responses]651 )652 653 uncertainty_survey_part_2_submit_button.click(654 lambda: (gr.update(visible=False), gr.update(visible=True)), # Hide survey, show final page655 inputs=[], outputs=[uncertainty_survey_part_2, final_page]656 )657 658 uncertainty_survey_part_2_submit_button.click(659 extract_code_context,660 inputs=[reference_code_state, user_state], outputs=[code_editor]661 )662 663 run_code_button.click(664 on_submit,665 inputs=[code_editor, submission_count, produced_codes, user_state],666 outputs=[667 processing_message,668 run_code_button,669 retry_button,670 finished_button,671 plot_output,672 submission_count,673 produced_codes,674 stdout_message,675 submission_counter676 ],677 )678 679 retry_button.click(680 on_retry,681 inputs=[code_editor, produced_codes],682 outputs=[683 processing_message,684 run_code_button,685 retry_button,686 finished_button,687 plot_output,688 produced_codes,689 ],690 )691 692 finished_button.click(693 lambda user_state: (gr.update(visible=False), gr.update(visible=True), f"temp_plot_{user_state}.png"), # Hide final page, show survey694 inputs=[user_state], outputs=[final_page, uncertainty_survey_part_3, generated_image]695 )696 697 uncertainty_survey_part_3_submit_button.click(698 handle_final_survey_response,699 inputs=[uncertainty_survey_part_3_question1, uncertainty_survey_part_3_question2, 700 uncertainty_survey_part_3_question3, uncertainty_survey_part_3_question4, 701 uncertainty_survey_part_3_question5, uncertainty_survey_part_3_question6,702 uncertainty_survey_part_3_question7],703 outputs=[uncertainty_survey_part_3_responses]704 )705 706 uncertainty_survey_part_3_submit_button.click(707 lambda: (gr.update(visible=False), gr.update(visible=True)), # Hide survey, show demographic survey708 inputs=[], outputs=[uncertainty_survey_part_3, demographic_survey]709 )710 711 demographic_survey_submit_button.click(712 handle_demographic_survey_response,713 inputs=[demographic_survey_question1, demographic_survey_question2, demographic_survey_question3, demographic_survey_question4, demographic_survey_question5],714 outputs=[demographic_survey_responses]715 )716 717 demographic_survey_submit_button.click(718 lambda: (gr.update(visible=False), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False)), # Hide survey, show exit page719 inputs=[], outputs=[demographic_survey, exit_page, download_button, notepad]720 )721 722 # notepad.change(filter_paste, 723 # inputs=[previous_text, notepad], 724 # outputs=[previous_text, notepad], trigger_mode="always_last")725 726 demographic_survey_submit_button.click(save_notes_with_timestamp, 727 inputs=[notepad, notes_state],728 outputs=[notes_state])729 730 download_button.click(731 get_download_link, 732 inputs=[user_state, chosen_image_state, notes_state, 733 dialogue_state, produced_codes, reference_code_state,734 expertise_survey_responses,735 uncertainty_survey_part_1_responses, 736 uncertainty_survey_part_2_responses, 737 uncertainty_survey_part_3_responses, 738 demographic_survey_responses],739 outputs=[file_to_download]740 )741 742 demo.load(743 lambda: gr.update(visible=True), # Show login page744 outputs=login_row,745 )746 747 return demo748 749 750# if __name__ == "__main__":751# users = Path("users.txt").read_text().splitlines()752# users = set(user.strip() for user in users if user.strip())753# chosen_image = pick_random_image()754# reference_code = chosen_image.replace(".png", ".py")755# # code_context = extract_code_context(reference_code)756# demo = create_interface(users, chosen_image, reference_code)757 758# # demo.launch(759# # server_name=args.server_name,760# # server_port=args.server_port,761# # share=args.share,762# # )763 764# demo.launch()765 766users = Path("users.txt").read_text().splitlines()767users = set(user.strip() for user in users if user.strip())768# chosen_image = pick_random_image()769# reference_code = chosen_image.replace(".png", ".py")770# code_context = extract_code_context(reference_code)771demo = create_interface(users)772 773demo.launch()774 