npc0 commited on
Commit
b6f295f
·
verified ·
1 Parent(s): 21c7fed

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +207 -1
src/streamlit_app.py CHANGED
@@ -13,6 +13,7 @@ import hdbscan
13
 
14
  # Database file path
15
  DB_PATH = '/data/steampolis.duckdb'
 
16
 
17
  # Initialize database tables if they don't exist
18
  def initialize_database():
@@ -97,6 +98,210 @@ def initialize_database():
97
  if 'init_con' in locals() and init_con:
98
  init_con.close()
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  def get_ttl_hash(seconds=360):
101
  """Return the same value withing `seconds` time period"""
102
  return round(time.time() / seconds)
@@ -775,7 +980,7 @@ def view_topic_page():
775
  # Include functional information
776
  st.markdown(f"**Shareable Quest Scroll ID:** `{topic_id}`")
777
  # Construct shareable link using current app URL
778
- app_url = st.query_params.get('base', ['https://huggingface.co/spaces/npc0/SteamPolis/'])[0] # Get base URL if available
779
  shareable_link = f"{app_url}?topic={topic_id}" if app_url else f"?topic={topic_id}"
780
  st.markdown(f"**Shareable Scroll Link:** `{shareable_link}`")
781
 
@@ -1081,6 +1286,7 @@ if 'processed_url_params' not in st.session_state:
1081
 
1082
  # Initialize the database on first run
1083
  initialize_database()
 
1084
 
1085
  # Handle initial load from URL query parameters
1086
  # Process only once per session load using the flag
 
13
 
14
  # Database file path
15
  DB_PATH = '/data/steampolis.duckdb'
16
+ DEFAULT_BASE_URL = 'https://huggingface.co/spaces/npc0/SteamPolis/'
17
 
18
  # Initialize database tables if they don't exist
19
  def initialize_database():
 
98
  if 'init_con' in locals() and init_con:
99
  init_con.close()
100
 
101
+
102
+ import random # Import random for generating votes
103
+
104
+ def add_example_topic(topic_id, topic_title, topic_description, comments_list):
105
+ """
106
+ Adds an example topic, its comments, and example cluster votes to the database.
107
+
108
+ Args:
109
+ topic_id (str): The unique ID for the topic.
110
+ topic_title (str): The title of the topic.
111
+ topic_description (str): The description of the topic.
112
+ comments_list (list): A list of strings, where each string is a comment.
113
+ """
114
+ con = None
115
+ try:
116
+ con = duckdb.connect(database=DB_PATH)
117
+
118
+ # Insert the topic
119
+ con.execute("""
120
+ INSERT INTO topics (id, title, description)
121
+ VALUES (?, ?, ?)
122
+ ON CONFLICT (id) DO NOTHING
123
+ """, [topic_id, topic_title, topic_description])
124
+
125
+ # --- Add Cluster Users ---
126
+ # Create users who will cast votes, separate from comment authors
127
+ num_users_per_cluster = 5
128
+ cluster1_users = [] # e.g., Pro-Tech supporters
129
+ cluster2_users = [] # e.g., Anti-Tech skeptics
130
+ cluster3_users = [] # e.g., Mixed/Neutral voters
131
+
132
+ all_cluster_users = []
133
+
134
+ for i in range(num_users_per_cluster):
135
+ user_id = str(uuid.uuid4())
136
+ username = f"cluster1_user_{i+1}_{uuid.uuid4().hex[:4]}@example.com"
137
+ con.execute("INSERT INTO users (id, username) VALUES (?, ?) ON CONFLICT (id) DO NOTHING", [user_id, username])
138
+ cluster1_users.append(user_id)
139
+ all_cluster_users.append(user_id)
140
+
141
+ user_id = str(uuid.uuid4())
142
+ username = f"cluster2_user_{i+1}_{uuid.uuid4().hex[:4]}@example.com"
143
+ con.execute("INSERT INTO users (id, username) VALUES (?, ?) ON CONFLICT (id) DO NOTHING", [user_id, username])
144
+ cluster2_users.append(user_id)
145
+ all_cluster_users.append(user_id)
146
+
147
+ user_id = str(uuid.uuid4())
148
+ username = f"cluster3_user_{i+1}_{uuid.uuid4().hex[:4]}@example.com"
149
+ con.execute("INSERT INTO users (id, username) VALUES (?, ?) ON CONFLICT (id) DO NOTHING", [user_id, username])
150
+ cluster3_users.append(user_id)
151
+ all_cluster_users.append(user_id)
152
+
153
+
154
+ # --- Insert comments and associated users ---
155
+ comment_id_map = {} # Map comment text to comment ID
156
+ for comment_text in comments_list:
157
+ comment_id = str(uuid.uuid4())
158
+ # Generate a random user ID and username for the comment author
159
+ author_user_id = str(uuid.uuid4())
160
+ author_username = f"author_{uuid.uuid4().hex[:8]}@example.com"
161
+
162
+ # Insert the author user
163
+ con.execute("""
164
+ INSERT INTO users (id, username)
165
+ VALUES (?, ?)
166
+ ON CONFLICT (id) DO NOTHING
167
+ """, [author_user_id, author_username])
168
+
169
+ # Insert the comment
170
+ con.execute("""
171
+ INSERT INTO comments (id, topic_id, user_id, text)
172
+ VALUES (?, ?, ?, ?)
173
+ """, [comment_id, topic_id, author_user_id, comment_text])
174
+ comment_id_map[comment_text] = comment_id # Store the mapping
175
+
176
+ # --- Add Cluster Votes ---
177
+ # Define comment categories based on the example topic context (Civic Tech Initiative)
178
+ # This is hardcoded based on the context provided in the prompt
179
+ pro_tech_comments = [
180
+ "Finally! A system to track rebel scum more efficiently. This will be a glorious day for the Empire!",
181
+ "Anything that improves the speed of selling junk is good in my book. Maybe I can finally get a decent price for this thermal detonator...",
182
+ "Fascinating! I am programmed to be compliant. I shall analyze this initiative and report my findings to the Emperor.",
183
+ "This is a welcome step towards greater efficiency and transparency... cough... as long as it doesn't affect my personal interests.",
184
+ "As long as it helps me track down my targets, I'm in. The more data, the better.",
185
+ "The Emperor's vision is one of unparalleled order and prosperity! This initiative will usher in a new era of galactic harmony!",
186
+ "I'm interested... Will it help me collect debts more efficiently?",
187
+ "If it improves the entertainment options on Coruscant, I'm all for it.",
188
+ "Another set of orders. Understood, sir!",
189
+ "Excellent... with this, I will have even greater control over the galaxy... cackles maniacally"
190
+ ]
191
+ anti_tech_comments = [
192
+ "This is clearly a data-mining operation. They're going to use it to crush the Rebellion. We need to sabotage it!",
193
+ "The Force guides us to see through their deception. This 'civic tech' will only serve to tighten their grip on the galaxy.",
194
+ "I just want a reliable power converter. Is that too much to ask? This 'civic tech' sounds like more bureaucracy.",
195
+ "I'm already dreading the help desk calls. 'My Death Star won't fire!' 'The Force isn't working!'",
196
+ "This is just a fancy way to track our X-wings. We'll find a way to disable it, just like we did with the Death Star.",
197
+ "Another reason to drown my sorrows in a Jawa Juice. This whole thing stinks of the Empire's incompetence.",
198
+ "This initiative is a waste of resources. We should be focusing on military expansion, not 'civic engagement.'"
199
+ ]
200
+ # Comments not in pro or anti lists are considered neutral/other for this example
201
+
202
+ votes_to_insert = []
203
+
204
+ # Cluster 1 (Pro-Tech) votes: Agree with pro, Disagree with anti, Mixed/Neutral on others
205
+ for user_id in cluster1_users:
206
+ for comment_text in comments_list:
207
+ comment_id = comment_id_map.get(comment_text)
208
+ if not comment_id: continue
209
+
210
+ vote_type = None
211
+ if comment_text in pro_tech_comments:
212
+ vote_type = 'agree'
213
+ elif comment_text in anti_tech_comments:
214
+ vote_type = 'disagree'
215
+ else:
216
+ # For neutral/other comments, a chance of neutral or skipping
217
+ vote_type = random.choice(['neutral'] * 2 + [None] * 3) # More likely neutral or skip
218
+
219
+ if vote_type:
220
+ votes_to_insert.append((user_id, comment_id, vote_type))
221
+
222
+
223
+ # Cluster 2 (Anti-Tech) votes: Disagree with pro, Agree with anti, Mixed/Neutral on others
224
+ for user_id in cluster2_users:
225
+ for comment_text in comments_list:
226
+ comment_id = comment_id_map.get(comment_text)
227
+ if not comment_id: continue
228
+
229
+ vote_type = None
230
+ if comment_text in pro_tech_comments:
231
+ vote_type = 'disagree'
232
+ elif comment_text in anti_tech_comments:
233
+ vote_type = 'agree'
234
+ else:
235
+ # For neutral/other comments, a chance of neutral or skipping
236
+ vote_type = random.choice(['neutral'] * 2 + [None] * 3) # More likely neutral or skip
237
+
238
+ if vote_type:
239
+ votes_to_insert.append((user_id, comment_id, vote_type))
240
+
241
+ # Cluster 3 (Mixed/Neutral) votes: Mostly neutral, some random agree/disagree, many skipped
242
+ for user_id in cluster3_users:
243
+ for comment_text in comments_list:
244
+ comment_id = comment_id_map.get(comment_text)
245
+ if not comment_id: continue
246
+
247
+ # Mostly neutral, some random agree/disagree, many skipped
248
+ vote_type = random.choice(['neutral'] * 5 + ['agree', 'disagree'] + [None] * 5) # Weighted towards neutral and skipping
249
+
250
+ if vote_type:
251
+ votes_to_insert.append((user_id, comment_id, vote_type))
252
+
253
+ # Insert all collected votes
254
+ if votes_to_insert:
255
+ con.executemany("""
256
+ INSERT INTO votes (user_id, comment_id, vote_type)
257
+ VALUES (?, ?, ?)
258
+ ON CONFLICT (user_id, comment_id) DO NOTHING
259
+ """, votes_to_insert)
260
+
261
+
262
+ con.commit()
263
+ # print(f"Successfully added topic '{topic_title}', {len(comments_list)} comments, and {len(all_cluster_users)} cluster users with votes.") # Use print for console output
264
+ # st.success(f"Successfully added topic '{topic_title}', {len(comments_list)} comments, and {len(all_cluster_users)} cluster users with votes.") # Use st.success if in Streamlit context
265
+
266
+ except Exception as e:
267
+ if con:
268
+ con.rollback()
269
+ print(f"Error adding example topic '{topic_title}' and votes: {e}") # Use print for console output
270
+ # st.error(f"Error adding example topic '{topic_title}' and votes: {e}") # Use st.error if in Streamlit context
271
+ finally:
272
+ if con:
273
+ con.close()
274
+ # Example usage (can be called elsewhere, e.g., in an initialization script)
275
+ def add_dummy_topic():
276
+ example_topic_id = "15736626"
277
+ example_topic_title = "New Civic Tech Initiative"
278
+ example_topic_description = "I want to figure out what the citizens of the Empire really think about the Emperor's new 'Civic Tech' initiative. He's promising streamlined governance, enhanced citizen engagement (apparently), and a 'more user-friendly experience' for everyone. But let's be honest, we all know how the Empire's tech usually works out. So, what are your thoughts? Is this a path to order, or a trap set by the Dark Side? Let's get some honest opinions flowing!"
279
+ example_comments = [
280
+ "Finally! A system to track rebel scum more efficiently. This will be a glorious day for the Empire!",
281
+ "This is clearly a data-mining operation. They're going to use it to crush the Rebellion. We need to sabotage it!",
282
+ "The Force guides us to see through their deception. This 'civic tech' will only serve to tighten their grip on the galaxy.",
283
+ "As long as it doesn't mess with my profit margins, I'm indifferent. But I suspect it will.",
284
+ "I just want a reliable power converter. Is that too much to ask? This 'civic tech' sounds like more bureaucracy.",
285
+ "Anything that improves the speed of selling junk is good in my book. Maybe I can finally get a decent price for this thermal detonator...",
286
+ "Fascinating! I am programmed to be compliant. I shall analyze this initiative and report my findings to the Emperor.",
287
+ "I'm already dreading the help desk calls. 'My Death Star won't fire!' 'The Force isn't working!'",
288
+ "This is a welcome step towards greater efficiency and transparency... cough... as long as it doesn't affect my personal interests.",
289
+ "This is just a fancy way to track our X-wings. We'll find a way to disable it, just like we did with the Death Star.",
290
+ "As long as it helps me track down my targets, I'm in. The more data, the better.",
291
+ "Another reason to drown my sorrows in a Jawa Juice. This whole thing stinks of the Empire's incompetence.",
292
+ "The Emperor's vision is one of unparalleled order and prosperity! This initiative will usher in a new era of galactic harmony!",
293
+ "Will it have cool spaceships in it? Can I play with it?",
294
+ "Beware the allure of technology. It can be a tool for both good and evil. Trust in the Force, young Padawans.",
295
+ "This initiative is a waste of resources. We should be focusing on military expansion, not 'civic engagement.'",
296
+ "I'm interested... Will it help me collect debts more efficiently?",
297
+ "I'm just trying to survive. This sounds like more trouble than it's worth.",
298
+ "If it improves the entertainment options on Coruscant, I'm all for it.",
299
+ "Another set of orders. Understood, sir!",
300
+ "Excellent... with this, I will have even greater control over the galaxy... cackles maniacally"
301
+ ]
302
+ add_example_topic(example_topic_id, example_topic_title, example_topic_description, example_comments)
303
+
304
+
305
  def get_ttl_hash(seconds=360):
306
  """Return the same value withing `seconds` time period"""
307
  return round(time.time() / seconds)
 
980
  # Include functional information
981
  st.markdown(f"**Shareable Quest Scroll ID:** `{topic_id}`")
982
  # Construct shareable link using current app URL
983
+ app_url = st.query_params.get('base', [DEFAULT_BASE_URL])[0] # Get base URL if available
984
  shareable_link = f"{app_url}?topic={topic_id}" if app_url else f"?topic={topic_id}"
985
  st.markdown(f"**Shareable Scroll Link:** `{shareable_link}`")
986
 
 
1286
 
1287
  # Initialize the database on first run
1288
  initialize_database()
1289
+ add_dummy_topic()
1290
 
1291
  # Handle initial load from URL query parameters
1292
  # Process only once per session load using the flag