yixuantt commited on
Commit
2e6a46f
Β·
verified Β·
1 Parent(s): 00b7bd9

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +95 -30
app.py CHANGED
@@ -24,7 +24,27 @@ os.makedirs(DATA_DIR, exist_ok=True)
24
  with open("test_pairs2.json", "r") as f:
25
  response_pairs = json.load(f)
26
 
27
- # CSS (unchanged)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  custom_css = """
29
  @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
30
  body { font-family: 'Roboto', sans-serif !important; line-height: 1.6; }
@@ -45,13 +65,31 @@ class State:
45
  def __init__(self):
46
  self.current_idx = 0
47
  self.prolific_id = ""
48
- self.selected_indices = [] # List of 40 question indices for this user
49
- self.annotations = [] # Annotations for the 40 questions
50
  self.form_responses = {} # Responses to post-test forms
51
  self.forms_completed = False # Flag for form completion
52
  self.start_time = datetime.now()
53
 
54
  state = State()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  # Updated save_annotations to include new fields
57
  def save_annotations():
@@ -61,6 +99,7 @@ def save_annotations():
61
  filepath = os.path.join(DATA_DIR, filename)
62
  data = {
63
  "prolific_id": state.prolific_id,
 
64
  "selected_indices": state.selected_indices,
65
  "duration": (datetime.now() - state.start_time).total_seconds(),
66
  "current_idx": state.current_idx,
@@ -84,7 +123,7 @@ def load_latest_data(prolific_id):
84
  state.annotations = data.get("annotations", [])
85
  state.form_responses = data.get("form_responses", {})
86
  state.forms_completed = data.get("forms_completed", False)
87
- state.current_idx = min(max(data.get("current_idx", 0), 0), 39) # Cap at 39 (0-39 for 40 questions)
88
  return data
89
  except Exception as e:
90
  logger.error(f"Error loading {filepath}: {e}")
@@ -99,6 +138,7 @@ In this task, you'll act as a judge comparing two AI chatbot responses. Your goa
99
  - You'll evaluate multiple questions (prompts), each with two responses (Response A and B)
100
  - Select the better response for each question based on the criteria below
101
  - Your progress will be tracked
 
102
 
103
  ### πŸ… Evaluation Criteria:
104
  1. **Perceived Usefulness**
@@ -194,10 +234,11 @@ def create_interface():
194
  gr.Markdown("## Prolific ID Verification")
195
  prolific_id = gr.Textbox(label="Enter your Prolific ID")
196
  id_submit_btn = gr.Button("Submit", variant="primary")
 
197
 
198
- # Main Interface (updated for 40 questions)
199
  with gr.Column(visible=False, elem_id="main_interface") as main_interface:
200
- progress_md = gr.Markdown("**Progress:** 0% (0/40)", elem_classes="progress")
201
  gr.HTML('<style>.prompt-highlight { background-color: #e6f7ff; padding: 10px; border: 1px solid #91d5ff; border-radius: 5px; }</style>')
202
  gr.Markdown(MINI_INSTRUCTION)
203
  gr.Markdown("---")
@@ -245,26 +286,48 @@ def create_interface():
245
  <p>Click <a href="https://app.prolific.com/researcher/submissions/complete?cc=CA7IOI65" target="_blank">here</a> to complete the task.</p>
246
  """)
247
 
248
- # Updated handle_id_submit to assign 40 random questions
249
  def handle_id_submit(prolific_id_val):
250
  if not prolific_id_val.strip():
251
  raise gr.Error("Please enter a valid Prolific ID")
252
- state.prolific_id = prolific_id_val.strip()
253
- data = load_latest_data(state.prolific_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  if data:
255
  if state.forms_completed:
256
  return {
257
  id_section: gr.update(visible=False),
258
  main_interface: gr.update(visible=False),
259
  forms_section: gr.update(visible=False),
260
- completion_section: gr.update(visible=True)
 
261
  }
262
- elif state.current_idx >= 40:
263
  return {
264
  id_section: gr.update(visible=False),
265
  main_interface: gr.update(visible=False),
266
  forms_section: gr.update(visible=True),
267
- completion_section: gr.update(visible=False)
 
268
  }
269
  else:
270
  return {
@@ -272,35 +335,28 @@ def create_interface():
272
  main_interface: gr.update(visible=True),
273
  forms_section: gr.update(visible=False),
274
  completion_section: gr.update(visible=False),
 
275
  **update_interface(state.current_idx)
276
  }
277
  else:
278
- # New user: assign 40 random questions
279
- seed = hash(state.prolific_id) % 1000000
280
- random.seed(seed)
281
- total_questions = len(response_pairs)
282
- if total_questions < 40:
283
- raise ValueError("Not enough questions available")
284
- state.selected_indices = random.sample(range(total_questions), 40)
285
- state.annotations = [None] * 40
286
- state.form_responses = {}
287
- state.forms_completed = False
288
  state.current_idx = 0
289
  return {
290
  id_section: gr.update(visible=False),
291
  main_interface: gr.update(visible=True),
292
  forms_section: gr.update(visible=False),
293
  completion_section: gr.update(visible=False),
 
294
  **update_interface(0)
295
  }
296
 
297
  # Updated update_interface to use selected_indices
298
  def update_interface(current_idx):
299
- if current_idx >= 40:
300
  current_idx = 39
301
  actual_idx = state.selected_indices[current_idx]
302
  current_data = response_pairs[actual_idx]
303
- progress = f"**Progress:** {current_idx/40:.0%} ({min(current_idx, 40)}/40)"
304
  annotation = state.annotations[current_idx] if current_idx < len(state.annotations) else None
305
  return {
306
  prompt_box: current_data.get("prompt", ""),
@@ -312,7 +368,7 @@ def create_interface():
312
  selection_radio: annotation["selected"] if annotation else None
313
  }
314
 
315
- # Updated handle_navigation to transition to forms_section after 40 questions
316
  def handle_navigation(direction, selection, confidence_val, feedback_val):
317
  error_msg = None
318
  if direction == "next":
@@ -340,12 +396,12 @@ def create_interface():
340
  }
341
  state.annotations[state.current_idx] = annotation
342
  if direction == "next":
343
- new_idx = min(state.current_idx + 1, 40)
344
  else:
345
  new_idx = max(0, state.current_idx - 1)
346
  state.current_idx = new_idx
347
  save_annotations()
348
- if new_idx >= 40:
349
  return {
350
  main_interface: gr.update(visible=False),
351
  forms_section: gr.update(visible=True),
@@ -362,13 +418,13 @@ def create_interface():
362
 
363
  # New function to handle returning to questions from forms
364
  def handle_back_to_questions():
365
- state.current_idx = 39
366
  save_annotations()
367
  return {
368
  main_interface: gr.update(visible=True),
369
  forms_section: gr.update(visible=False),
370
  completion_section: gr.update(visible=False),
371
- **update_interface(39)
372
  }
373
 
374
  # New function to handle form submission
@@ -397,7 +453,7 @@ def create_interface():
397
  id_submit_btn.click(
398
  handle_id_submit,
399
  inputs=prolific_id,
400
- outputs=[id_section, main_interface, forms_section, completion_section, prompt_box,
401
  response_a, response_b, progress_md, feedback, confidence, selection_radio]
402
  )
403
 
@@ -431,5 +487,14 @@ def create_interface():
431
  return demo
432
 
433
  if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
434
  app = create_interface()
435
  app.launch()
 
24
  with open("test_pairs2.json", "r") as f:
25
  response_pairs = json.load(f)
26
 
27
+ # Function to generate assignments ensuring each question gets 7 labels
28
+ def generate_assignments(num_questions=120, num_annotators=30, labels_per_question=7, questions_per_annotator=28):
29
+ assignments = {f"annotator_{i+1}": [] for i in range(num_annotators)}
30
+ question_assignments = {i: [] for i in range(num_questions)}
31
+ annotator_capacities = [questions_per_annotator] * num_annotators
32
+
33
+ for q in range(num_questions):
34
+ available_annotators = [(a, annotator_capacities[a]) for a in range(num_annotators) if annotator_capacities[a] > 0]
35
+ if len(available_annotators) < labels_per_question:
36
+ raise ValueError(f"Not enough annotators with capacity for question {q}")
37
+
38
+ available_annotators.sort(key=lambda x: x[1], reverse=True)
39
+ selected_annotators = [a for a, _ in available_annotators[:labels_per_question]]
40
+
41
+ for a in selected_annotators:
42
+ assignments[f"annotator_{a+1}"].append(q)
43
+ question_assignments[q].append(a)
44
+ annotator_capacities[a] -= 1
45
+
46
+ return assignments, question_assignments
47
+
48
  custom_css = """
49
  @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
50
  body { font-family: 'Roboto', sans-serif !important; line-height: 1.6; }
 
65
  def __init__(self):
66
  self.current_idx = 0
67
  self.prolific_id = ""
68
+ self.selected_indices = [] # List of 28 question indices for this user
69
+ self.annotations = [] # Annotations for the 28 questions
70
  self.form_responses = {} # Responses to post-test forms
71
  self.forms_completed = False # Flag for form completion
72
  self.start_time = datetime.now()
73
 
74
  state = State()
75
+ ASSIGNED_FILE = "assigned.json"
76
+
77
+ def load_assigned():
78
+ if os.path.exists(ASSIGNED_FILE):
79
+ with open(ASSIGNED_FILE, "r") as f:
80
+ return json.load(f)
81
+ return {}
82
+
83
+ def save_assigned(assigned):
84
+ with open(ASSIGNED_FILE, "w") as f:
85
+ json.dump(assigned, f, indent=2)
86
+
87
+ def get_next_available_assignment(assigned, total_assignments=30):
88
+ for i in range(1, total_assignments + 1):
89
+ key = f"annotator_{i}"
90
+ if key not in assigned.values():
91
+ return key
92
+ return None
93
 
94
  # Updated save_annotations to include new fields
95
  def save_annotations():
 
99
  filepath = os.path.join(DATA_DIR, filename)
100
  data = {
101
  "prolific_id": state.prolific_id,
102
+ "assignment_key": state.assignment_key,
103
  "selected_indices": state.selected_indices,
104
  "duration": (datetime.now() - state.start_time).total_seconds(),
105
  "current_idx": state.current_idx,
 
123
  state.annotations = data.get("annotations", [])
124
  state.form_responses = data.get("form_responses", {})
125
  state.forms_completed = data.get("forms_completed", False)
126
+ state.current_idx = min(max(data.get("current_idx", 0), 0), 27)
127
  return data
128
  except Exception as e:
129
  logger.error(f"Error loading {filepath}: {e}")
 
138
  - You'll evaluate multiple questions (prompts), each with two responses (Response A and B)
139
  - Select the better response for each question based on the criteria below
140
  - Your progress will be tracked
141
+ - After completing all questions, you'll answer a few post-test forms
142
 
143
  ### πŸ… Evaluation Criteria:
144
  1. **Perceived Usefulness**
 
234
  gr.Markdown("## Prolific ID Verification")
235
  prolific_id = gr.Textbox(label="Enter your Prolific ID")
236
  id_submit_btn = gr.Button("Submit", variant="primary")
237
+ id_message = gr.Markdown("", visible=False)
238
 
239
+ # Main Interface (updated for 28 questions)
240
  with gr.Column(visible=False, elem_id="main_interface") as main_interface:
241
+ progress_md = gr.Markdown("**Progress:** 0% (0/28)", elem_classes="progress")
242
  gr.HTML('<style>.prompt-highlight { background-color: #e6f7ff; padding: 10px; border: 1px solid #91d5ff; border-radius: 5px; }</style>')
243
  gr.Markdown(MINI_INSTRUCTION)
244
  gr.Markdown("---")
 
286
  <p>Click <a href="https://app.prolific.com/researcher/submissions/complete?cc=CA7IOI65" target="_blank">here</a> to complete the task.</p>
287
  """)
288
 
289
+ # Updated handle_id_submit to assign 28 random questions
290
  def handle_id_submit(prolific_id_val):
291
  if not prolific_id_val.strip():
292
  raise gr.Error("Please enter a valid Prolific ID")
293
+ prolific_id = prolific_id_val.strip()
294
+ assigned = load_assigned()
295
+ if prolific_id in assigned:
296
+ assignment_key = assigned[prolific_id]
297
+ else:
298
+ next_key = get_next_available_assignment(assigned)
299
+ if next_key is None:
300
+ return {
301
+ id_section: gr.update(visible=True),
302
+ main_interface: gr.update(visible=False),
303
+ forms_section: gr.update(visible=False),
304
+ completion_section: gr.update(visible=False),
305
+ id_message: gr.update(value="The study is full. Thank you for your interest.", visible=True)
306
+ }
307
+ assigned[prolific_id] = next_key
308
+ save_assigned(assigned)
309
+ assignment_key = next_key
310
+
311
+ state.prolific_id = prolific_id
312
+ state.assignment_key = assignment_key
313
+ state.selected_indices = assignments[assignment_key]
314
+ data = load_latest_data(prolific_id)
315
  if data:
316
  if state.forms_completed:
317
  return {
318
  id_section: gr.update(visible=False),
319
  main_interface: gr.update(visible=False),
320
  forms_section: gr.update(visible=False),
321
+ completion_section: gr.update(visible=True),
322
+ id_message: gr.update(value="", visible=False)
323
  }
324
+ elif state.current_idx >= 28:
325
  return {
326
  id_section: gr.update(visible=False),
327
  main_interface: gr.update(visible=False),
328
  forms_section: gr.update(visible=True),
329
+ completion_section: gr.update(visible=False),
330
+ id_message: gr.update(value="", visible=False)
331
  }
332
  else:
333
  return {
 
335
  main_interface: gr.update(visible=True),
336
  forms_section: gr.update(visible=False),
337
  completion_section: gr.update(visible=False),
338
+ id_message: gr.update(value="", visible=False),
339
  **update_interface(state.current_idx)
340
  }
341
  else:
342
+ state.annotations = [None] * 28
 
 
 
 
 
 
 
 
 
343
  state.current_idx = 0
344
  return {
345
  id_section: gr.update(visible=False),
346
  main_interface: gr.update(visible=True),
347
  forms_section: gr.update(visible=False),
348
  completion_section: gr.update(visible=False),
349
+ id_message: gr.update(value="", visible=False),
350
  **update_interface(0)
351
  }
352
 
353
  # Updated update_interface to use selected_indices
354
  def update_interface(current_idx):
355
+ if current_idx >= 28:
356
  current_idx = 39
357
  actual_idx = state.selected_indices[current_idx]
358
  current_data = response_pairs[actual_idx]
359
+ progress = f"**Progress:** {current_idx/28:.0%} ({min(current_idx, 28)}/28)"
360
  annotation = state.annotations[current_idx] if current_idx < len(state.annotations) else None
361
  return {
362
  prompt_box: current_data.get("prompt", ""),
 
368
  selection_radio: annotation["selected"] if annotation else None
369
  }
370
 
371
+ # Updated handle_navigation to transition to forms_section after 28 questions
372
  def handle_navigation(direction, selection, confidence_val, feedback_val):
373
  error_msg = None
374
  if direction == "next":
 
396
  }
397
  state.annotations[state.current_idx] = annotation
398
  if direction == "next":
399
+ new_idx = min(state.current_idx + 1, 28)
400
  else:
401
  new_idx = max(0, state.current_idx - 1)
402
  state.current_idx = new_idx
403
  save_annotations()
404
+ if new_idx >= 28:
405
  return {
406
  main_interface: gr.update(visible=False),
407
  forms_section: gr.update(visible=True),
 
418
 
419
  # New function to handle returning to questions from forms
420
  def handle_back_to_questions():
421
+ state.current_idx = 27
422
  save_annotations()
423
  return {
424
  main_interface: gr.update(visible=True),
425
  forms_section: gr.update(visible=False),
426
  completion_section: gr.update(visible=False),
427
+ **update_interface(27)
428
  }
429
 
430
  # New function to handle form submission
 
453
  id_submit_btn.click(
454
  handle_id_submit,
455
  inputs=prolific_id,
456
+ outputs=[id_section, main_interface, forms_section, completion_section, id_message, prompt_box,
457
  response_a, response_b, progress_md, feedback, confidence, selection_radio]
458
  )
459
 
 
487
  return demo
488
 
489
  if __name__ == "__main__":
490
+ if not os.path.exists("assignments.json"):
491
+ assignments,_ = generate_assignments()
492
+ print("Assignments generated.")
493
+ with open("assignments.json", "w") as f:
494
+ json.dump(assignments, f, indent=2)
495
+ else:
496
+ with open("assignments.json", "r") as f:
497
+ assignments = json.load(f)
498
+ print("Assignments loaded.")
499
  app = create_interface()
500
  app.launch()