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
|
@@ -30,6 +30,7 @@ class EnhancedAIvsRealGazeAnalyzer:
|
|
30 |
self.model = None
|
31 |
self.scaler = None
|
32 |
self.feature_names = []
|
|
|
33 |
|
34 |
def _find_and_standardize_participant_col(self, df, filename):
|
35 |
participant_col = next((c for c in df.columns if 'participant' in str(c).lower()), None)
|
@@ -84,7 +85,10 @@ 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 |
-
|
|
|
|
|
|
|
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
|
@@ -178,28 +182,22 @@ class EnhancedAIvsRealGazeAnalyzer:
|
|
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():
|
199 |
repo_url = "https://github.com/RextonRZ/GenAIEyeTrackingCleanedDataset"
|
200 |
repo_dir = "GenAIEyeTrackingCleanedDataset"
|
201 |
-
if not os.path.exists(repo_dir):
|
202 |
-
|
|
|
|
|
|
|
203 |
base_path = repo_dir
|
204 |
response_file_path = os.path.join(repo_dir, "GenAI Response.xlsx")
|
205 |
analyzer = EnhancedAIvsRealGazeAnalyzer().load_and_process_data(base_path, response_file_path)
|
@@ -248,35 +246,52 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
248 |
|
249 |
rq1_metric_dropdown.change(fn=analyzer.analyze_rq1_metric, inputs=rq1_metric_dropdown, outputs=[rq1_plot_output, rq1_summary_output])
|
250 |
|
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 |
-
|
279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
|
281 |
if __name__ == "__main__":
|
282 |
demo.launch()
|
|
|
1 |
+
# app.py (Corrected and Ready to Run)
|
2 |
import pandas as pd
|
3 |
import numpy as np
|
4 |
import matplotlib.pyplot as plt
|
|
|
30 |
self.model = None
|
31 |
self.scaler = None
|
32 |
self.feature_names = []
|
33 |
+
self.time_metrics = [] # Initialize here
|
34 |
|
35 |
def _find_and_standardize_participant_col(self, df, filename):
|
36 |
participant_col = next((c for c in df.columns if 'participant' in str(c).lower()), None)
|
|
|
85 |
self.combined_data['Answer_Correctness'] = self.combined_data['Correct'].map({True: 'Correct', False: 'Incorrect'})
|
86 |
|
87 |
self.numeric_cols = self.combined_data.select_dtypes(include=np.number).columns.tolist()
|
88 |
+
|
89 |
+
# <<< FIX: Removed the space in the variable name here >>>
|
90 |
+
self.time_metrics = [c for c in self.numeric_cols if any(k in c.lower() for k in ['time', 'duration', 'fixation'])]
|
91 |
+
|
92 |
self.valid_playback_participants = sorted(list(self.valid_playback_trials.keys()))
|
93 |
print(f"--- Data Loading Successful. Found {len(self.valid_playback_participants)} participants with fixation data. ---")
|
94 |
return self
|
|
|
182 |
valid_questions = self.valid_playback_trials.get(participant, [])
|
183 |
return gr.Dropdown(choices=sorted(valid_questions), interactive=True, value=None, label="2. Select a Question")
|
184 |
|
|
|
|
|
185 |
def handle_new_trial_selection(self, participant, question):
|
186 |
"""Called when a new trial is selected. Resets the view to the first fixation."""
|
187 |
if not participant or not question:
|
|
|
188 |
return "Select a trial to begin.", None, gr.Slider(value=0, interactive=False)
|
|
|
|
|
|
|
189 |
initial_fixation_num = 1
|
|
|
|
|
190 |
return self.generate_gaze_playback(participant, question, initial_fixation_num)
|
|
|
191 |
|
192 |
# --- DATA SETUP & GRADIO APP ---
|
193 |
def setup_and_load_data():
|
194 |
repo_url = "https://github.com/RextonRZ/GenAIEyeTrackingCleanedDataset"
|
195 |
repo_dir = "GenAIEyeTrackingCleanedDataset"
|
196 |
+
if not os.path.exists(repo_dir):
|
197 |
+
print(f"Cloning repository {repo_url}...")
|
198 |
+
git.Repo.clone_from(repo_url, repo_dir)
|
199 |
+
else:
|
200 |
+
print("Data repository already exists.")
|
201 |
base_path = repo_dir
|
202 |
response_file_path = os.path.join(repo_dir, "GenAI Response.xlsx")
|
203 |
analyzer = EnhancedAIvsRealGazeAnalyzer().load_and_process_data(base_path, response_file_path)
|
|
|
246 |
|
247 |
rq1_metric_dropdown.change(fn=analyzer.analyze_rq1_metric, inputs=rq1_metric_dropdown, outputs=[rq1_plot_output, rq1_summary_output])
|
248 |
|
249 |
+
train_event = rq2_test_size_slider.release(fn=analyzer.run_prediction_model, inputs=[rq2_test_size_slider, rq2_estimators_slider], outputs=outputs_rq2)
|
250 |
rq2_estimators_slider.release(fn=analyzer.run_prediction_model, inputs=[rq2_test_size_slider, rq2_estimators_slider], outputs=outputs_rq2)
|
251 |
|
|
|
|
|
252 |
playback_participant.change(
|
253 |
fn=analyzer.update_question_dropdown,
|
254 |
inputs=playback_participant,
|
255 |
outputs=playback_question
|
256 |
)
|
257 |
|
|
|
|
|
258 |
playback_question.change(
|
259 |
fn=analyzer.handle_new_trial_selection,
|
260 |
inputs=[playback_participant, playback_question],
|
261 |
outputs=outputs_playback
|
262 |
)
|
263 |
|
|
|
264 |
playback_slider.release(
|
265 |
fn=analyzer.generate_gaze_playback,
|
266 |
inputs=[playback_participant, playback_question, playback_slider],
|
267 |
outputs=outputs_playback
|
268 |
)
|
|
|
269 |
|
270 |
+
# Pre-load the initial state of the dashboard
|
271 |
+
def initial_load():
|
272 |
+
# Load the first tab's content
|
273 |
+
rq1_fig, rq1_summary = analyzer.analyze_rq1_metric(analyzer.time_metrics[0] if analyzer.time_metrics else None)
|
274 |
+
|
275 |
+
# Train the initial model for the second tab
|
276 |
+
model_summary, report_df, feature_fig, status_md = analyzer.run_prediction_model(0.3, 100)
|
277 |
+
|
278 |
+
# Return all the values needed to populate the outputs on load
|
279 |
+
return {
|
280 |
+
rq1_plot_output: rq1_fig,
|
281 |
+
rq1_summary_output: rq1_summary,
|
282 |
+
rq2_summary_output: model_summary,
|
283 |
+
rq2_table_output: report_df,
|
284 |
+
rq2_plot_output: feature_fig,
|
285 |
+
rq2_status: status_md
|
286 |
+
}
|
287 |
+
|
288 |
+
demo.load(
|
289 |
+
fn=initial_load,
|
290 |
+
outputs=[
|
291 |
+
rq1_plot_output, rq1_summary_output,
|
292 |
+
rq2_summary_output, rq2_table_output, rq2_plot_output, rq2_status
|
293 |
+
]
|
294 |
+
)
|
295 |
|
296 |
if __name__ == "__main__":
|
297 |
demo.launch()
|