Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# app.py
|
| 2 |
import pandas as pd
|
| 3 |
import numpy as np
|
| 4 |
import matplotlib.pyplot as plt
|
|
@@ -84,7 +84,7 @@ class EnhancedAIvsRealGazeAnalyzer:
|
|
| 84 |
self.combined_data['Answer_Correctness'] = self.combined_data['Correct'].map({True: 'Correct', False: 'Incorrect'})
|
| 85 |
|
| 86 |
self.numeric_cols = self.combined_data.select_dtypes(include=np.number).columns.tolist()
|
| 87 |
-
self.
|
| 88 |
self.valid_playback_participants = sorted(list(self.valid_playback_trials.keys()))
|
| 89 |
print(f"--- Data Loading Successful. Found {len(self.valid_playback_participants)} participants with fixation data. ---")
|
| 90 |
return self
|
|
@@ -161,7 +161,7 @@ class EnhancedAIvsRealGazeAnalyzer:
|
|
| 161 |
trial_info = self.combined_data[(self.combined_data['participant_id'] == str(participant)) & (self.combined_data['Question'] == question)].iloc[0]
|
| 162 |
summary_text = f"**Actual Answer:** `{trial_info['Answer_Correctness']}`"
|
| 163 |
|
| 164 |
-
return summary_text, fig, gr.Slider(maximum=slider_max, value=fixation_num, interactive=True)
|
| 165 |
|
| 166 |
def analyze_rq1_metric(self, metric):
|
| 167 |
if not metric or metric not in self.combined_data.columns: return None, "Metric not found."
|
|
@@ -176,7 +176,23 @@ class EnhancedAIvsRealGazeAnalyzer:
|
|
| 176 |
def update_question_dropdown(self, participant):
|
| 177 |
"""Dynamically updates the question dropdown based on the selected participant."""
|
| 178 |
valid_questions = self.valid_playback_trials.get(participant, [])
|
| 179 |
-
return gr.Dropdown(choices=sorted(valid_questions), interactive=True, value=None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
|
| 181 |
# --- DATA SETUP & GRADIO APP ---
|
| 182 |
def setup_and_load_data():
|
|
@@ -235,10 +251,29 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 235 |
rq2_test_size_slider.release(fn=analyzer.run_prediction_model, inputs=[rq2_test_size_slider, rq2_estimators_slider], outputs=outputs_rq2)
|
| 236 |
rq2_estimators_slider.release(fn=analyzer.run_prediction_model, inputs=[rq2_test_size_slider, rq2_estimators_slider], outputs=outputs_rq2)
|
| 237 |
|
|
|
|
| 238 |
# Chained dropdown logic for Tab 3
|
| 239 |
-
playback_participant.change(
|
| 240 |
-
|
| 241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
|
| 243 |
demo.load(fn=analyzer.analyze_rq1_metric, inputs=rq1_metric_dropdown, outputs=[rq1_plot_output, rq1_summary_output])
|
| 244 |
demo.load(fn=analyzer.run_prediction_model, inputs=[rq2_test_size_slider, rq2_estimators_slider], outputs=outputs_rq2)
|
|
|
|
| 1 |
+
# app.py
|
| 2 |
import pandas as pd
|
| 3 |
import numpy as np
|
| 4 |
import matplotlib.pyplot as plt
|
|
|
|
| 84 |
self.combined_data['Answer_Correctness'] = self.combined_data['Correct'].map({True: 'Correct', False: 'Incorrect'})
|
| 85 |
|
| 86 |
self.numeric_cols = self.combined_data.select_dtypes(include=np.number).columns.tolist()
|
| 87 |
+
self.time_ metrics = [c for c in self.numeric_cols if any(k in c.lower() for k in ['time', 'duration', 'fixation'])]
|
| 88 |
self.valid_playback_participants = sorted(list(self.valid_playback_trials.keys()))
|
| 89 |
print(f"--- Data Loading Successful. Found {len(self.valid_playback_participants)} participants with fixation data. ---")
|
| 90 |
return self
|
|
|
|
| 161 |
trial_info = self.combined_data[(self.combined_data['participant_id'] == str(participant)) & (self.combined_data['Question'] == question)].iloc[0]
|
| 162 |
summary_text = f"**Actual Answer:** `{trial_info['Answer_Correctness']}`"
|
| 163 |
|
| 164 |
+
return summary_text, fig, gr.Slider(maximum=slider_max, value=fixation_num, interactive=True, step=1, minimum=0)
|
| 165 |
|
| 166 |
def analyze_rq1_metric(self, metric):
|
| 167 |
if not metric or metric not in self.combined_data.columns: return None, "Metric not found."
|
|
|
|
| 176 |
def update_question_dropdown(self, participant):
|
| 177 |
"""Dynamically updates the question dropdown based on the selected participant."""
|
| 178 |
valid_questions = self.valid_playback_trials.get(participant, [])
|
| 179 |
+
return gr.Dropdown(choices=sorted(valid_questions), interactive=True, value=None, label="2. Select a Question")
|
| 180 |
+
|
| 181 |
+
# <<< FIX START: Step 1 -> Add a new handler method for when a question is selected. >>>
|
| 182 |
+
# This function provides a clean, single entry point to reset the visualization.
|
| 183 |
+
def handle_new_trial_selection(self, participant, question):
|
| 184 |
+
"""Called when a new trial is selected. Resets the view to the first fixation."""
|
| 185 |
+
if not participant or not question:
|
| 186 |
+
# Return a default/empty state for all outputs if nothing is selected
|
| 187 |
+
return "Select a trial to begin.", None, gr.Slider(value=0, interactive=False)
|
| 188 |
+
|
| 189 |
+
# Start the playback at the very first fixation for a better user experience.
|
| 190 |
+
# You can change '1' to '0' if you prefer it to start completely empty.
|
| 191 |
+
initial_fixation_num = 1
|
| 192 |
+
|
| 193 |
+
# Call the main generation function with this fixed initial value
|
| 194 |
+
return self.generate_gaze_playback(participant, question, initial_fixation_num)
|
| 195 |
+
# <<< FIX END >>>
|
| 196 |
|
| 197 |
# --- DATA SETUP & GRADIO APP ---
|
| 198 |
def setup_and_load_data():
|
|
|
|
| 251 |
rq2_test_size_slider.release(fn=analyzer.run_prediction_model, inputs=[rq2_test_size_slider, rq2_estimators_slider], outputs=outputs_rq2)
|
| 252 |
rq2_estimators_slider.release(fn=analyzer.run_prediction_model, inputs=[rq2_test_size_slider, rq2_estimators_slider], outputs=outputs_rq2)
|
| 253 |
|
| 254 |
+
# <<< FIX START: Step 2 -> Update the event wiring for Tab 3. >>>
|
| 255 |
# Chained dropdown logic for Tab 3
|
| 256 |
+
playback_participant.change(
|
| 257 |
+
fn=analyzer.update_question_dropdown,
|
| 258 |
+
inputs=playback_participant,
|
| 259 |
+
outputs=playback_question
|
| 260 |
+
)
|
| 261 |
+
|
| 262 |
+
# When a question is selected, call the new handler to reset the entire playback view.
|
| 263 |
+
# This is much cleaner and more reliable than the previous .then() chain.
|
| 264 |
+
playback_question.change(
|
| 265 |
+
fn=analyzer.handle_new_trial_selection,
|
| 266 |
+
inputs=[playback_participant, playback_question],
|
| 267 |
+
outputs=outputs_playback
|
| 268 |
+
)
|
| 269 |
+
|
| 270 |
+
# The slider event handler remains the same and works correctly for scrubbing through fixations.
|
| 271 |
+
playback_slider.release(
|
| 272 |
+
fn=analyzer.generate_gaze_playback,
|
| 273 |
+
inputs=[playback_participant, playback_question, playback_slider],
|
| 274 |
+
outputs=outputs_playback
|
| 275 |
+
)
|
| 276 |
+
# <<< FIX END >>>
|
| 277 |
|
| 278 |
demo.load(fn=analyzer.analyze_rq1_metric, inputs=rq1_metric_dropdown, outputs=[rq1_plot_output, rq1_summary_output])
|
| 279 |
demo.load(fn=analyzer.run_prediction_model, inputs=[rq2_test_size_slider, rq2_estimators_slider], outputs=outputs_rq2)
|