npc0 commited on
Commit
31a0966
Β·
verified Β·
1 Parent(s): f013dfe
Files changed (1) hide show
  1. src/streamlit_app.py +550 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,552 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ import random
3
+ import urllib.parse # To parse URL parameters
4
  import streamlit as st
5
+ import duckdb
6
 
7
+ # Database file path
8
+ DB_PATH = 'steampolis.duckdb'
9
+
10
+ # Initialize database tables if they don't exist
11
+ def initialize_database():
12
+ try:
13
+ init_con = duckdb.connect(database=DB_PATH, read_only=False)
14
+ init_con.execute("""
15
+ CREATE TABLE IF NOT EXISTS topics (
16
+ id TEXT PRIMARY KEY,
17
+ name TEXT NOT NULL,
18
+ description TEXT,
19
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
20
+ )
21
+ """)
22
+ init_con.execute("""
23
+ CREATE TABLE IF NOT EXISTS users (
24
+ id TEXT PRIMARY KEY,
25
+ username TEXT NOT NULL UNIQUE,
26
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
27
+ )
28
+ """)
29
+ init_con.execute("""
30
+ CREATE TABLE IF NOT EXISTS comments (
31
+ id TEXT PRIMARY KEY,
32
+ topic_id TEXT NOT NULL,
33
+ user_id TEXT NOT NULL,
34
+ content TEXT NOT NULL,
35
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
36
+ FOREIGN KEY (topic_id) REFERENCES topics(id),
37
+ FOREIGN KEY (user_id) REFERENCES users(id)
38
+ )
39
+ """)
40
+ init_con.execute("""
41
+ CREATE TABLE IF NOT EXISTS votes (
42
+ id TEXT PRIMARY KEY,
43
+ user_id TEXT NOT NULL,
44
+ comment_id TEXT NOT NULL,
45
+ vote_type TEXT NOT NULL CHECK (vote_type IN ('agree', 'disagree', 'neutral')),
46
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
47
+ FOREIGN KEY (user_id) REFERENCES users(id),
48
+ FOREIGN KEY (comment_id) REFERENCES comments(id),
49
+ UNIQUE (user_id, comment_id)
50
+ )
51
+ """)
52
+ init_con.execute("""
53
+ CREATE TABLE IF NOT EXISTS user_comment_collections (
54
+ id TEXT PRIMARY KEY,
55
+ user_id TEXT NOT NULL,
56
+ comment_id TEXT NOT NULL,
57
+ collected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
58
+ FOREIGN KEY (user_id) REFERENCES users(id),
59
+ FOREIGN KEY (comment_id) REFERENCES comments(id),
60
+ UNIQUE (user_id, comment_id)
61
+ )
62
+ """)
63
+ init_con.execute("""
64
+ CREATE TABLE IF NOT EXISTS user_progress (
65
+ id TEXT PRIMARY KEY,
66
+ user_id TEXT NOT NULL,
67
+ topic_id TEXT NOT NULL,
68
+ last_comment_id_viewed TEXT,
69
+ last_viewed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
70
+ FOREIGN KEY (user_id) REFERENCES users(id),
71
+ FOREIGN KEY (topic_id) REFERENCES topics(id),
72
+ FOREIGN KEY (last_comment_id_viewed) REFERENCES comments(id),
73
+ UNIQUE (user_id, topic_id)
74
+ )
75
+ """)
76
+
77
+ # Create system user if it doesn't exist
78
+ try:
79
+ init_con.execute("""
80
+ INSERT INTO users (id, username)
81
+ VALUES ('system', 'System')
82
+ ON CONFLICT (id) DO NOTHING
83
+ """)
84
+ except Exception as e:
85
+ print(f"Warning: Could not create system user: {e}")
86
+
87
+ except Exception as e:
88
+ st.error(f"Database initialization failed: {e}")
89
+ finally:
90
+ if 'init_con' in locals() and init_con:
91
+ init_con.close()
92
+
93
+ # Helper function to get a random unvoted comment
94
+ def get_random_unvoted_comment(user_id, topic_id):
95
+ local_con = None
96
+ try:
97
+ local_con = duckdb.connect(database=DB_PATH, read_only=False)
98
+
99
+ # First, check if there are any comments at all in the topic
100
+ comment_count = local_con.execute("""
101
+ SELECT COUNT(*) FROM comments WHERE topic_id = ?
102
+ """, [topic_id]).fetchone()[0]
103
+
104
+ if comment_count == 0:
105
+ return None, "No comments in this topic yet."
106
+
107
+ # Attempt to get a random comment that the user has NOT voted on
108
+ result = local_con.execute("""
109
+ SELECT c.id, c.content
110
+ FROM comments c
111
+ WHERE c.topic_id = ?
112
+ AND NOT EXISTS (
113
+ SELECT 1 FROM votes v
114
+ WHERE v.comment_id = c.id AND v.user_id = ?
115
+ )
116
+ ORDER BY RANDOM()
117
+ LIMIT 1
118
+ """, [topic_id, user_id]).fetchone()
119
+
120
+ if result:
121
+ # Found an unvoted comment
122
+ return result[0], result[1]
123
+ else:
124
+ # No unvoted comments found for this user in this topic
125
+ return None, "No new thoughts for now..."
126
+
127
+ except Exception as e:
128
+ st.error(f"Error getting random unvoted comment: {e}")
129
+ return None, f"Error loading comments: {str(e)}"
130
+ finally:
131
+ if local_con:
132
+ local_con.close()
133
+
134
+ # Helper function to find or create a user
135
+ def find_or_create_user(username):
136
+ local_con = None
137
+ try:
138
+ local_con = duckdb.connect(database=DB_PATH, read_only=False)
139
+ user_result = local_con.execute("SELECT id FROM users WHERE username = ?", [username]).fetchone()
140
+ if user_result:
141
+ return user_result[0]
142
+ else:
143
+ user_id = str(uuid.uuid4())
144
+ local_con.execute("INSERT INTO users (id, username) VALUES (?, ?)", [user_id, username])
145
+ return user_id
146
+ except Exception as e:
147
+ st.error(f"Error finding or creating user: {e}")
148
+ return None
149
+ finally:
150
+ if local_con:
151
+ local_con.close()
152
+
153
+ # Helper function to update user progress
154
+ def update_user_progress(user_id, topic_id, comment_id):
155
+ local_con = None
156
+ try:
157
+ local_con = duckdb.connect(database=DB_PATH, read_only=False)
158
+ progress_id = str(uuid.uuid4())
159
+ local_con.execute("""
160
+ INSERT INTO user_progress (id, user_id, topic_id, last_comment_id_viewed) VALUES (?, ?, ?, ?)
161
+ ON CONFLICT (user_id, topic_id) DO UPDATE SET
162
+ last_comment_id_viewed = EXCLUDED.last_comment_id_viewed
163
+ """, [progress_id, user_id, topic_id, comment_id])
164
+ except Exception as e:
165
+ st.error(f"Error updating user progress: {e}")
166
+ finally:
167
+ if local_con:
168
+ local_con.close()
169
+
170
+ # Helper function to handle comment submission UI and logic
171
+ def share_wisdom(prompt, allow_skip=False):
172
+ st.markdown(prompt)
173
+ new_comment_text = st.text_area(f"Your Insight {'that different from others above (Empty to skip)' if allow_skip else ''}", key="new_comment_input")
174
+ if st.button("Share Your Wisdom"):
175
+ if new_comment_text:
176
+ user_id = find_or_create_user(user_email) # Ensure user exists
177
+ if user_id:
178
+ local_con = None
179
+ try:
180
+ local_con = duckdb.connect(database=DB_PATH, read_only=False)
181
+ comment_id = str(uuid.uuid4())
182
+ local_con.execute("""
183
+ INSERT INTO comments (id, topic_id, user_id, content)
184
+ VALUES (?, ?, ?, ?)
185
+ """, [comment_id, topic_id, user_id, new_comment_text])
186
+
187
+ # Append new comment to history
188
+ st.session_state.comment_history += f"\n\nπŸ’¬ {new_comment_text}"
189
+
190
+ # Get next comment (could be the one just submitted)
191
+ next_comment_id, next_comment_content = get_random_unvoted_comment(user_id, topic_id)
192
+ st.session_state.current_comment_id = next_comment_id
193
+ st.session_state.current_comment_content = next_comment_content
194
+
195
+ # Update progress
196
+ update_user_progress(user_id, topic_id, next_comment_id)
197
+
198
+ st.session_state.new_comment_input = "" # Clear input box
199
+ st.rerun() # Rerun to update UI
200
+
201
+ except Exception as e:
202
+ st.error(f"Error sharing information: {e}")
203
+ finally:
204
+ if local_con:
205
+ local_con.close()
206
+ else:
207
+ st.error("Could not find or create user.")
208
+ elif allow_skip:
209
+ return
210
+ else:
211
+ st.warning("Please enter your thought.")
212
+
213
+
214
+ # --- Page Functions ---
215
+
216
+ def home_page():
217
+ st.title("Welcome to SteamPolis")
218
+ st.markdown("Choose an option:")
219
+
220
+ if st.button("Create New Topic"):
221
+ st.session_state.page = 'create_topic'
222
+ st.rerun()
223
+
224
+ st.markdown("---")
225
+ st.markdown("Or join an existing topic:")
226
+ topic_input = st.text_input("Enter Topic ID or URL")
227
+
228
+ if st.button("Join Topic"):
229
+ topic_id = topic_input.strip()
230
+ if topic_id.startswith('http'): # Handle full URL
231
+ parsed_url = urllib.parse.urlparse(topic_id)
232
+ query_params = urllib.parse.parse_qs(parsed_url.query)
233
+ topic_id = query_params.get('topic', [None])[0]
234
+
235
+ if topic_id:
236
+ st.session_state.page = 'view_topic'
237
+ st.session_state.current_topic_id = topic_id
238
+ # Attempt to load email from session state (mimics browser state)
239
+ # If email exists, handle email submission logic immediately on view page load
240
+ st.rerun()
241
+ else:
242
+ st.warning("Please enter a valid Topic ID or URL.")
243
+
244
+
245
+ def create_topic_page():
246
+ st.title("Create a New Topic")
247
+
248
+ new_topic_name = st.text_input("Topic Name (Imagine you are the king, how would you share your concern)")
249
+ new_topic_description = st.text_area('Description (Begin with "I want to figure out...", imagine you are the king, what would you want to know)', height=150)
250
+ new_topic_seed_comments = st.text_area("Initial Comments (separate by new line, imagine there are civilians what will they answer)", height=200)
251
+ creator_email = st.text_input("Enter your Email (required for creation)")
252
+
253
+ if st.button("Create Topic"):
254
+ if not creator_email:
255
+ st.error("Email is required to create a topic.")
256
+ return
257
+
258
+ topic_id = str(uuid.uuid4())[:8]
259
+ user_id = find_or_create_user(creator_email)
260
+
261
+ if user_id:
262
+ local_con = None
263
+ try:
264
+ local_con = duckdb.connect(database=DB_PATH, read_only=False)
265
+ local_con.execute("INSERT INTO topics (id, name, description) VALUES (?, ?, ?)", [topic_id, new_topic_name, new_topic_description])
266
+
267
+ seed_comments = [c.strip() for c in new_topic_seed_comments.split('\n') if c.strip()]
268
+ for comment in seed_comments:
269
+ comment_id = str(uuid.uuid4())
270
+ local_con.execute("INSERT INTO comments (id, topic_id, user_id, content) VALUES (?, ?, ?, ?)",
271
+ [comment_id, topic_id, 'system', comment])
272
+
273
+ # Get the first comment to display after creation
274
+ comment_to_display_id, comment_to_display_content = get_random_unvoted_comment(user_id, topic_id)
275
+
276
+ # Set initial progress for creator
277
+ update_user_progress(user_id, topic_id, comment_to_display_id)
278
+
279
+ st.session_state.page = 'view_topic'
280
+ st.session_state.current_topic_id = topic_id
281
+ st.session_state.user_email = creator_email # Store email in session state
282
+ st.session_state.current_comment_id = comment_to_display_id
283
+ st.session_state.current_comment_content = comment_to_display_content
284
+ st.session_state.comment_history = ""
285
+
286
+ st.success(f"Topic '{new_topic_name}' created!")
287
+ st.rerun()
288
+
289
+ except Exception as e:
290
+ st.error(f"Error creating topic: {e}")
291
+ finally:
292
+ if local_con:
293
+ local_con.close()
294
+ else:
295
+ st.error("Could not find or create user.")
296
+
297
+
298
+ if st.button("Back to Home"):
299
+ st.session_state.page = 'home'
300
+ st.rerun()
301
+
302
+ def view_topic_page():
303
+ topic_id = st.session_state.get('current_topic_id')
304
+ user_email = st.session_state.get('user_email', '')
305
+ current_comment_id = st.session_state.get('current_comment_id')
306
+ current_comment_content = st.session_state.get('current_comment_content', "Loading comments...")
307
+ comment_history = st.session_state.get('comment_history', "")
308
+
309
+ if not topic_id:
310
+ st.warning("No topic selected. Returning to home.")
311
+ st.session_state.page = 'home'
312
+ st.rerun()
313
+ return
314
+
315
+ local_con = None
316
+ topic_name = "Loading..."
317
+ topic_description = "Loading..."
318
+
319
+ try:
320
+ local_con = duckdb.connect(database=DB_PATH, read_only=True)
321
+ topic_result = local_con.execute("SELECT name, description FROM topics WHERE id = ?", [topic_id]).fetchone()
322
+ if topic_result:
323
+ topic_name, topic_description = topic_result
324
+ else:
325
+ st.error(f"Topic ID '{topic_id}' not found.")
326
+ st.session_state.page = 'home'
327
+ st.rerun()
328
+ return
329
+ except Exception as e:
330
+ st.error(f"Error loading topic details: {e}")
331
+ if local_con:
332
+ local_con.close()
333
+ st.session_state.page = 'home'
334
+ st.rerun()
335
+ return
336
+ finally:
337
+ if local_con:
338
+ local_con.close()
339
+
340
+
341
+ # Include functional information
342
+ st.markdown(f"**Quest Scroll ID:** `{topic_id}`")
343
+ # Construct shareable link using current app URL
344
+ app_url = st.query_params.get('base', ['http://localhost:8501/'])[0] # Get base URL if available
345
+ shareable_link = f"{app_url}?topic={topic_id}" if app_url else f"?topic={topic_id}"
346
+ st.markdown(f"**Shareable Scroll Link:** `{shareable_link}`")
347
+
348
+ st.title("Seeker Quest")
349
+
350
+ # Check if user email is available in session state.
351
+ # user_email is already retrieved from st.session_state at the start of view_topic_page.
352
+ if user_email:
353
+ # Get the user ID. find_or_create_user handles the DB connection internally.
354
+ user_id = find_or_create_user(user_email)
355
+ if user_id:
356
+ # Check if user has any progress recorded for this specific topic.
357
+ # This indicates they have viewed comments or interacted before.
358
+ local_con = None
359
+ progress_exists = False
360
+ try:
361
+ local_con = duckdb.connect(database=DB_PATH, read_only=True)
362
+ # Query the user_progress table for a record matching user_id and topic_id
363
+ result = local_con.execute("""
364
+ SELECT 1 FROM user_progress
365
+ WHERE user_id = ? AND topic_id = ?
366
+ LIMIT 1
367
+ """, [user_id, topic_id]).fetchone()
368
+ progress_exists = result is not None
369
+ except Exception as e:
370
+ # Log error but don't stop the app. Assume no progress on error.
371
+ st.error(f"Error checking user progress for greeting: {e}")
372
+ # progress_exists remains False
373
+ finally:
374
+ if local_con:
375
+ local_con.close()
376
+
377
+ # Display the appropriate greeting based on progress
378
+ if progress_exists:
379
+ # Acknowledge return and remind of quest
380
+ st.markdown("Welcome back, Seeker. Your journey through the whispers of Aethelgard continues.")
381
+ st.markdown(f"You pause to recall the heart of the Emperor's concern regarding **{topic_name}**: `{topic_description}`.")
382
+
383
+ # Introduce the next comment
384
+ st.markdown("As you press onward, you encounter another soul willing to share their thoughts on this vital matter.")
385
+ else:
386
+ # Introduce the setting and the Emperor's concern
387
+ st.markdown("Welcome, Seeker, to the ancient Kingdom of Aethelgard, a realm of digital whispers and forgotten wisdom.")
388
+ st.markdown("For centuries, Aethelgard has stood, preserving the echoes of an age long past. But now, a matter of great weight troubles the Emperor's thoughts.")
389
+ st.markdown(f"The Emperor seeks clarity on a crucial topic: **`{topic_name}`**.")
390
+
391
+ # Explain the quest and the user's role
392
+ st.markdown("You, among a select few, have been summoned for a vital quest: to traverse the kingdom, gather insights, and illuminate this matter for the throne.")
393
+ st.markdown(f"At a recent royal gathering, the Emperor revealed the heart of their concern, the very essence of your mission: `{topic_description}`")
394
+
395
+ # Transition to the task
396
+ st.markdown("Your journey begins now. The path leads to the first village, where the voices of the realm await your ear.")
397
+
398
+
399
+ # --- Email Prompt ---
400
+ if not user_email:
401
+ st.subheader("Enter your Email to view comments and progress")
402
+ view_user_email_input = st.text_input("Your Email", key="view_email_input")
403
+ if st.button("Submit Email", key="submit_view_email"):
404
+ if view_user_email_input:
405
+ st.session_state.user_email = view_user_email_input
406
+ user_id = find_or_create_user(view_user_email_input)
407
+ if user_id:
408
+ comment_to_display_id, comment_to_display_content = get_random_unvoted_comment(user_id, topic_id)
409
+ st.session_state.current_comment_id = comment_to_display_id
410
+ st.session_state.current_comment_content = comment_to_display_content
411
+ update_user_progress(user_id, topic_id, comment_to_display_id)
412
+ st.session_state.comment_history = "" # Reset history on new email submission
413
+ st.rerun()
414
+ else:
415
+ st.error("Could not find or create user with that email.")
416
+ else:
417
+ st.warning("Please enter your email.")
418
+ return # Stop rendering the rest until email is submitted
419
+
420
+ # --- Comment Display and Voting ---
421
+ # Define introductory phrases for encountering a new perspective
422
+ intro_phrases = [
423
+ "A new whisper reaches your ear",
424
+ "You ponder a fresh perspective",
425
+ "Another voice shares their view",
426
+ "A thought emerges from the crowd",
427
+ "The wind carries a new idea",
428
+ "Someone offers an insight",
429
+ "You overhear a comment",
430
+ "A different angle appears",
431
+ "The village elder shares",
432
+ "A traveler murmurs",
433
+ ]
434
+ # Randomly select a phrase
435
+ random_phrase = random.choice(intro_phrases)
436
+
437
+ if current_comment_id: # Only show voting if there's a comment to vote on
438
+ # Display comment history and the current comment with the random intro
439
+ st.markdown(f"{comment_history}\n\n[Collected new insight, {random_phrase}]:\n* {current_comment_content}")
440
+
441
+ # Handle vote logic
442
+ def handle_vote(vote_type, comment_id, topic_id, user_id):
443
+ local_con = None
444
+ try:
445
+ local_con = duckdb.connect(database=DB_PATH, read_only=False)
446
+ vote_id = str(uuid.uuid4())
447
+ local_con.execute("""
448
+ INSERT INTO votes (id, user_id, comment_id, vote_type)
449
+ VALUES (?, ?, ?, ?)
450
+ """, [vote_id, user_id, comment_id, vote_type])
451
+
452
+ # Append voted comment to history
453
+ vote_text = "πŸ‘" if vote_type == "agree" else "πŸ‘Ž" if vote_type == "disagree" else "😐"
454
+ st.session_state.comment_history += f"\n\n{vote_text} {current_comment_content}"
455
+
456
+ # Check vote count and trigger special event
457
+ # Initialize vote_count if it doesn't exist
458
+ if 'vote_count' not in st.session_state:
459
+ st.session_state.vote_count = 0
460
+
461
+ st.session_state.vote_count += 1
462
+
463
+ # Check if it's time for a potential special event (every 5 votes)
464
+ if st.session_state.vote_count % 5 == 0:
465
+ st.session_state.vote_count = 0
466
+ # 30% chance to trigger the special sharing event
467
+ if random.random() < 0.3:
468
+ prompts = [
469
+ "An elder approaches you, seeking your perspective on the Emperor's concern. What wisdom do you share?",
470
+ "A letter arrives from the Emperor's office, requesting your personal insight on the matter. What counsel do you offer?",
471
+ "As you walk through the streets, people gather, eager to hear your thoughts on the Emperor's dilemma. What advice do you give?"
472
+ ]
473
+ share_wisdom(random.choice(prompts), allow_skip=True)
474
+
475
+ # Get next comment
476
+ next_comment_id, next_comment_content = get_random_unvoted_comment(user_id, topic_id)
477
+ st.session_state.current_comment_id = next_comment_id
478
+ st.session_state.current_comment_content = next_comment_content
479
+
480
+ # Update progress
481
+ update_user_progress(user_id, topic_id, next_comment_id)
482
+
483
+ st.rerun() # Rerun to update UI
484
+
485
+ except Exception as e:
486
+ st.error(f"Error processing vote: {e}")
487
+ finally:
488
+ if local_con:
489
+ local_con.close()
490
+
491
+ col1, col2, col3, col4 = st.columns(4)
492
+ user_id = find_or_create_user(user_email) # Ensure user exists
493
+
494
+ col1.markdown("*Personally I...*")
495
+ if col2.button("Agree"):
496
+ handle_vote("agree", current_comment_id, topic_id, user_id)
497
+ if col3.button("Neutral"):
498
+ handle_vote("neutral", current_comment_id, topic_id, user_id)
499
+ if col4.button("Disagree"):
500
+ handle_vote("disagree", current_comment_id, topic_id, user_id)
501
+
502
+ else:
503
+ st.info("No more comments to vote on in this topic." if "No more comments" in current_comment_content else current_comment_content)
504
+
505
+
506
+ st.markdown("")
507
+
508
+ # --- Comment Submission ---
509
+ with st.expander("Offer Your Counsel to the Emperor", expanded=False):
510
+ share_wisdom("Having heard the thoughts of others, what wisdom do you wish to share regarding the Emperor's concern?")
511
+ st.markdown("---")
512
+
513
+
514
+ if st.button("Pack all insights and Return to Capital"):
515
+ st.session_state.page = 'home'
516
+ st.rerun()
517
+
518
+ # Initialize session state for navigation and data
519
+ if 'page' not in st.session_state:
520
+ st.session_state.page = 'home'
521
+ if 'current_topic_id' not in st.session_state:
522
+ st.session_state.current_topic_id = None
523
+ if 'user_email' not in st.session_state:
524
+ st.session_state.user_email = '' # Mimics browser state
525
+ if 'current_comment_id' not in st.session_state:
526
+ st.session_state.current_comment_id = None
527
+ if 'current_comment_content' not in st.session_state:
528
+ st.session_state.current_comment_content = "Loading comments..."
529
+ if 'comment_history' not in st.session_state:
530
+ st.session_state.comment_history = ""
531
+
532
+ # Initialize the database on first run
533
+ initialize_database()
534
+
535
+ # Handle initial load from URL query parameters
536
+ query_params = st.query_params
537
+ if 'topic' in query_params and st.session_state.page == 'home':
538
+ topic_id_from_url = query_params['topic']
539
+ st.session_state.page = 'view_topic'
540
+ st.session_state.current_topic_id = topic_id_from_url
541
+ # The view_topic_page will handle loading user/comment based on session_state.user_email
542
+ st.query_params = {} # Clear query params after processing
543
+ st.rerun()
544
+
545
+
546
+ # Render the appropriate page based on session state
547
+ if st.session_state.page == 'home':
548
+ home_page()
549
+ elif st.session_state.page == 'create_topic':
550
+ create_topic_page()
551
+ elif st.session_state.page == 'view_topic':
552
+ view_topic_page()