Ashmi Banerjee commited on
Commit
897c1d2
·
1 Parent(s): 7ee3b93

somewhat works

Browse files
Files changed (4) hide show
  1. .gitignore +2 -1
  2. app.py +129 -56
  3. db/crud.py +29 -13
  4. db/schema.py +7 -4
.gitignore CHANGED
@@ -164,4 +164,5 @@ notebooks/.ipynb_checkpoints
164
  .DS_Store
165
  db/empty.json
166
  .config/user-evaluations-firebase-creds.json
167
- user-evaluations-default-rtdb-export.json
 
 
164
  .DS_Store
165
  db/empty.json
166
  .config/user-evaluations-firebase-creds.json
167
+ user-evaluations-default-rtdb-export.json
168
+ data/
app.py CHANGED
@@ -3,26 +3,51 @@ from db.crud import ingest
3
  import pandas as pd
4
  import streamlit as st
5
  from datetime import datetime
 
 
 
 
6
 
 
7
 
8
- # Load Q&A data
9
- @st.cache_data
10
- def load_data():
11
- return pd.read_csv("dummy_qa_data.csv")[:3]
12
 
 
 
13
 
14
- # Function to validate Prolific ID (customize this based on your format requirements)
15
- def validate_prolific_id(prolific_id: str) -> bool:
16
- return bool(prolific_id.strip()) # Example: Check non-empty
17
 
18
- # Function to reset the survey
 
 
 
 
 
 
 
19
 
 
20
 
21
- def reset_survey():
22
- st.session_state.prolific_id = None
23
- st.session_state.current_index = 0
24
- st.session_state.responses = []
25
- st.session_state.completed = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
 
28
  def initialization():
@@ -35,6 +60,8 @@ def initialization():
35
  st.session_state.responses = []
36
  if "completed" not in st.session_state:
37
  st.session_state.completed = False
 
 
38
 
39
 
40
  def prolific_id_screen():
@@ -45,7 +72,15 @@ def prolific_id_screen():
45
  if prolific_id_input:
46
  if validate_prolific_id(prolific_id_input):
47
  st.session_state.prolific_id = prolific_id_input
48
- st.success("Prolific ID recorded! Now, proceed with the questions.")
 
 
 
 
 
 
 
 
49
  st.rerun()
50
  else:
51
  st.warning("Invalid Prolific ID. Please check and try again.")
@@ -54,47 +89,52 @@ def prolific_id_screen():
54
  def questions_screen(data):
55
  """Display the questions screen."""
56
  current_index = st.session_state.current_index
 
57
 
58
- # Display question and answer
59
- question = data.loc[current_index, "Question"]
60
- generated_answer = data.loc[current_index, "Generated Answer"]
61
-
62
- st.subheader(f"Question {current_index + 1}")
63
- st.markdown(f"***{question}***")
64
- st.subheader("Generated Answer:")
65
- st.write(generated_answer)
66
-
67
- # Prefill responses if they exist
68
- previous_rating = (
69
- st.session_state.responses[current_index].ans
70
- if len(st.session_state.responses) > current_index
71
- else 0
72
- )
73
- previous_feedback = (
74
- st.session_state.responses[current_index].feedback_text
75
- if len(st.session_state.responses) > current_index
76
- else ""
77
- )
78
-
79
- # Rating slider
80
- rating = st.slider("Rate the answer (1 = Worst, 5 = Best)", 1, 5, value=previous_rating,
81
- key=f"rating_{current_index}")
82
-
83
- # Free-text feedback
84
- feedback_text = st.text_area("Any additional feedback?", value=previous_feedback, key=f"feedback_{current_index}")
85
-
86
- # Store or update the response
87
- response = Response(q_id=f"q{current_index + 1}", ans=rating, feedback_text=feedback_text)
 
 
 
 
 
 
88
  if len(st.session_state.responses) > current_index:
89
  st.session_state.responses[current_index] = response
90
  else:
91
  st.session_state.responses.append(response)
92
-
93
- # Navigation buttons
94
- navigation_buttons(data, rating, feedback_text)
95
 
96
 
97
- def navigation_buttons(data, rating, feedback_text):
98
  """Display navigation buttons."""
99
  current_index = st.session_state.current_index
100
 
@@ -107,7 +147,7 @@ def navigation_buttons(data, rating, feedback_text):
107
 
108
  with col2: # Next button
109
  if st.button("Next"):
110
- if not rating:
111
  st.warning("Please provide a rating before proceeding.")
112
  else:
113
  if current_index < len(data) - 1:
@@ -117,7 +157,7 @@ def navigation_buttons(data, rating, feedback_text):
117
  feedback = Feedback(
118
  id=current_index + 1,
119
  user_id=st.session_state.prolific_id,
120
- time_stamp=datetime.now(),
121
  responses=st.session_state.responses,
122
  )
123
  try:
@@ -128,6 +168,38 @@ def navigation_buttons(data, rating, feedback_text):
128
  st.error(f"An error occurred while submitting feedback: {e}")
129
 
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  def reset_survey():
132
  """Reset the survey state to start over."""
133
  st.session_state.prolific_id = None
@@ -138,15 +210,16 @@ def reset_survey():
138
 
139
  def ui():
140
  """Main function to control the survey flow."""
141
- data = load_data()
142
  initialization()
143
 
144
  if st.session_state.completed:
145
- st.title("Survey Completed")
146
- st.success("You have completed all questions and your answers have been recorded.")
147
- if st.button("Retake Survey"):
148
- reset_survey()
149
- st.rerun()
 
150
  return
151
 
152
  if st.session_state.prolific_id is None:
 
3
  import pandas as pd
4
  import streamlit as st
5
  from datetime import datetime
6
+ import os
7
+ from dotenv import load_dotenv
8
+ import json
9
+ from typing import Dict, List
10
 
11
+ load_dotenv()
12
 
 
 
 
 
13
 
14
+ class SurveyState:
15
+ """Class to handle survey state management"""
16
 
17
+ def __init__(self):
18
+ self.state_file = "survey_states.json"
 
19
 
20
+ def save_state(self, prolific_id: str, current_state: Dict) -> None:
21
+ """Save current state to file"""
22
+ try:
23
+ if os.path.exists(self.state_file):
24
+ with open(self.state_file, 'r') as f:
25
+ states = json.load(f)
26
+ else:
27
+ states = {}
28
 
29
+ states[prolific_id] = current_state
30
 
31
+ with open(self.state_file, 'w') as f:
32
+ json.dump(states, f)
33
+
34
+ except Exception as e:
35
+ st.error(f"Error saving state: {e}")
36
+
37
+ def load_state(self, prolific_id: str) -> Dict:
38
+ """Load state for a specific user"""
39
+ try:
40
+ if os.path.exists(self.state_file):
41
+ with open(self.state_file, 'r') as f:
42
+ states = json.load(f)
43
+ return states.get(prolific_id, {})
44
+ except Exception as e:
45
+ st.error(f"Error loading state: {e}")
46
+ return {}
47
+
48
+
49
+ def validate_prolific_id(prolific_id: str) -> bool:
50
+ return bool(prolific_id.strip())
51
 
52
 
53
  def initialization():
 
60
  st.session_state.responses = []
61
  if "completed" not in st.session_state:
62
  st.session_state.completed = False
63
+ if "survey_state" not in st.session_state:
64
+ st.session_state.survey_state = SurveyState()
65
 
66
 
67
  def prolific_id_screen():
 
72
  if prolific_id_input:
73
  if validate_prolific_id(prolific_id_input):
74
  st.session_state.prolific_id = prolific_id_input
75
+
76
+ # Load previous state if exists
77
+ saved_state = st.session_state.survey_state.load_state(prolific_id_input)
78
+ if saved_state:
79
+ st.session_state.current_index = saved_state.get('current_index', 0)
80
+ st.session_state.responses = saved_state.get('responses', [])
81
+ st.success("Previous progress loaded! Continuing from where you left off.")
82
+ else:
83
+ st.success("Prolific ID recorded! Starting new survey.")
84
  st.rerun()
85
  else:
86
  st.warning("Invalid Prolific ID. Please check and try again.")
 
89
  def questions_screen(data):
90
  """Display the questions screen."""
91
  current_index = st.session_state.current_index
92
+ config = data.iloc[current_index]
93
 
94
+ # Progress bar
95
+ progress = (current_index + 1) / len(data)
96
+ st.progress(progress)
97
+ st.write(f"Question {current_index + 1} of {len(data)}")
98
+
99
+ st.subheader(f"Config ID: {config['config_id']}")
100
+
101
+ # Create tabs for better organization
102
+ context_tab, questions_tab = st.tabs(["Context", "Questions"])
103
+
104
+ with context_tab:
105
+ st.write(f"**Persona:** {config['persona']}")
106
+ st.write(f"**Filters:** {config['filters']}")
107
+ st.write(f"**Cities:** {config['city']}")
108
+
109
+ # Expandable context
110
+ with st.expander("Show Context", expanded=False):
111
+ st.write(config['context'])
112
+
113
+ options = [0, 1, 2, 3, 4, 5]
114
+
115
+ with questions_tab:
116
+ st.write(f"**Query_v:** {config['query_v']}")
117
+ rating_v = st.radio("Rate this config:", options, key=f"rating_v_{current_index}")
118
+
119
+ st.write(f"**Query_p0:** {config['query_p0']}")
120
+ rating_p0 = st.radio("Rate this config:", options, key=f"rating_p0_{current_index}")
121
+
122
+ st.write(f"**Query_p1:** {config['query_p1']}")
123
+ rating_p1 = st.radio("Rate this config:", options, key=f"rating_p1_{current_index}")
124
+
125
+ comment = st.text_area("Comments (Optional):")
126
+ response = Response(config_id=config["config_id"],
127
+ rating_v=rating_v, rating_p0=rating_p0, rating_p1=rating_p1,
128
+ comment=comment, timestamp=datetime.now().isoformat())
129
+ print(response)
130
  if len(st.session_state.responses) > current_index:
131
  st.session_state.responses[current_index] = response
132
  else:
133
  st.session_state.responses.append(response)
134
+ navigation_buttons(data, rating_v, rating_p0, rating_p1)
 
 
135
 
136
 
137
+ def navigation_buttons(data, rating_v, rating_p0, rating_p1):
138
  """Display navigation buttons."""
139
  current_index = st.session_state.current_index
140
 
 
147
 
148
  with col2: # Next button
149
  if st.button("Next"):
150
+ if rating_v == 0 or rating_p0 == 0 or rating_p1 ==0:
151
  st.warning("Please provide a rating before proceeding.")
152
  else:
153
  if current_index < len(data) - 1:
 
157
  feedback = Feedback(
158
  id=current_index + 1,
159
  user_id=st.session_state.prolific_id,
160
+ time_stamp=datetime.now().isoformat(),
161
  responses=st.session_state.responses,
162
  )
163
  try:
 
168
  st.error(f"An error occurred while submitting feedback: {e}")
169
 
170
 
171
+ def exit_screen():
172
+ """Display exit screen"""
173
+ st.markdown("""
174
+ <div class='exit-container'>
175
+ <h1>Thank you for participating!</h1>
176
+ <p>Your responses have been saved successfully.</p>
177
+ <p>You can safely close this window or start a new survey.</p>
178
+ </div>
179
+ """, unsafe_allow_html=True)
180
+
181
+ if st.button("Start New Survey"):
182
+ reset_survey()
183
+ st.rerun()
184
+
185
+
186
+ def submit_survey():
187
+ """Submit the complete survey"""
188
+ try:
189
+ feedback = Feedback(
190
+ id=st.session_state.current_index,
191
+ user_id=st.session_state.prolific_id,
192
+ time_stamp=datetime.now().isoformat(),
193
+ responses=st.session_state.responses,
194
+ )
195
+ print(feedback)
196
+ ingest(feedback)
197
+ st.session_state.completed = True
198
+ st.rerun()
199
+ except Exception as e:
200
+ st.error(f"An error occurred while submitting feedback: {e}")
201
+
202
+
203
  def reset_survey():
204
  """Reset the survey state to start over."""
205
  st.session_state.prolific_id = None
 
210
 
211
  def ui():
212
  """Main function to control the survey flow."""
213
+ data = pd.read_csv("data/gemini_results_subset.csv")[:3]
214
  initialization()
215
 
216
  if st.session_state.completed:
217
+ # st.title("Survey Completed")
218
+ # st.success("Thank you! Your survey has been submitted successfully.")
219
+ # if st.button("Start New Survey"):
220
+ # reset_survey()
221
+ # st.rerun()
222
+ exit_screen()
223
  return
224
 
225
  if st.session_state.prolific_id is None:
db/crud.py CHANGED
@@ -1,23 +1,27 @@
1
  from datetime import datetime
2
 
3
- from db.schema import Feedback
4
  from db.setup import db_setup
5
 
6
 
7
  # Create operation (Ingest data into Firebase with unique user_id check)
8
  def ingest(data: Feedback):
9
  ref = db_setup() # Ensure Firebase is initialized
10
-
11
  # Check if the user_id already exists in the feedback data
12
- existing_feedback = ref.child('feedback').order_by_child('user_id').equal_to(data.user_id).get()
13
-
14
- # TODO: This should probably change. If the same user has multiple feedbacks, we should allow it. -> change to update
15
- if existing_feedback:
16
- print(f"Feedback from user '{data.user_id}' already exists. Ingestion aborted.")
17
- else:
18
- feedback_data = data.dict() # Convert Pydantic model to a dictionary
19
- ref.child('feedback').push(feedback_data)
20
- print(f"Feedback data from user '{data.user_id}' pushed to Firebase!")
 
 
 
 
21
 
22
 
23
  # Read operation (Fetch feedback data from Firebase)
@@ -71,9 +75,21 @@ def test():
71
  {"q_id": "q2", "ans": 3}
72
  ]
73
  )
74
-
 
 
 
 
 
 
 
 
 
 
 
 
75
  # Create (Ingest)
76
- ingest(feedback_example)
77
 
78
  # Read (Fetch)
79
  feedback_data = read(1)
 
1
  from datetime import datetime
2
 
3
+ from db.schema import Feedback, Response
4
  from db.setup import db_setup
5
 
6
 
7
  # Create operation (Ingest data into Firebase with unique user_id check)
8
  def ingest(data: Feedback):
9
  ref = db_setup() # Ensure Firebase is initialized
10
+ print(f"Attempting to ingest feedback data from user '{data.user_id}'...")
11
  # Check if the user_id already exists in the feedback data
12
+ # user_id_str = str(data.user_id)
13
+ # existing_feedback = ref.child('feedback').order_by_child('user_id').equal_to(user_id_str).get()
14
+ #
15
+ #
16
+ # # TODO: This should probably change. If the same user has multiple feedbacks, we should allow it. -> change to update
17
+ # if existing_feedback:
18
+ # print(f"Feedback from user '{data.user_id}' already exists. Ingestion aborted.")
19
+ # else:
20
+ # # feedback_data = data.dict() # Convert Pydantic model to a dictionary
21
+ feedback_data = data.model_dump()
22
+ feedback_data["time_stamp"] = feedback_data['time_stamp'].isoformat()
23
+ ref.child('feedback').push(feedback_data)
24
+ print(f"Feedback data from user '{data.user_id}' pushed to Firebase!")
25
 
26
 
27
  # Read operation (Fetch feedback data from Firebase)
 
75
  {"q_id": "q2", "ans": 3}
76
  ]
77
  )
78
+ feedback = Feedback(
79
+ id=2,
80
+ user_id='tt',
81
+ time_stamp=datetime(2025, 1, 30, 21, 25, 15, 581994),
82
+ responses=[
83
+ Response(config_id='c_p_0_pop_low_easy', rating_v=1, rating_p0=1, rating_p1=1, comment='',
84
+ timestamp='2025-01-30T21:25:12.642038'),
85
+ Response(config_id='c_p_1_pop_medium_medium', rating_v=1, rating_p0=1, rating_p1=1, comment='',
86
+ timestamp='2025-01-30T21:25:13.854227'),
87
+ Response(config_id='c_p_2_pop_high_hard', rating_v=1, rating_p0=1, rating_p1=1, comment='',
88
+ timestamp='2025-01-30T21:25:15.581948'),
89
+ ]
90
+ )
91
  # Create (Ingest)
92
+ ingest(feedback)
93
 
94
  # Read (Fetch)
95
  feedback_data = read(1)
db/schema.py CHANGED
@@ -1,12 +1,15 @@
1
  from pydantic import BaseModel
2
- from typing import List, Dict
3
  from datetime import datetime
4
 
5
 
6
  class Response(BaseModel):
7
- q_id: str
8
- ans: int
9
- feedback_text: str | None = None
 
 
 
10
 
11
 
12
  class Feedback(BaseModel):
 
1
  from pydantic import BaseModel
2
+ from typing import List, Dict, Optional
3
  from datetime import datetime
4
 
5
 
6
  class Response(BaseModel):
7
+ config_id: str
8
+ rating_v: int
9
+ rating_p0: int
10
+ rating_p1: int
11
+ comment: Optional[str]
12
+ timestamp: str
13
 
14
 
15
  class Feedback(BaseModel):