Bharath370 commited on
Commit
53aff18
·
verified ·
1 Parent(s): d3123bf

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +682 -682
src/streamlit_app.py CHANGED
@@ -1,682 +1,682 @@
1
- # app.py
2
- """TriviaVerse Enhanced - Advanced Quiz Application"""
3
-
4
- import streamlit as st
5
- import time
6
- from datetime import datetime
7
- import random
8
- # Removed dotenv import and os import as .env file is not persisting
9
- # from dotenv import load_dotenv
10
- # import os
11
-
12
- from config.settings import (
13
- DIFFICULTY_LEVELS,
14
- BADGES,
15
- RANDOM_TOPICS,
16
- )
17
- from config.themes import THEMES
18
- from config.languages import SUPPORTED_LANGUAGES
19
- from modules.mcq_generator import generate_quiz_set
20
- from modules.flashcard_generator import generate_smart_deck
21
- from modules.fact_game import generate_random_fact
22
- from modules.adaptive_engine import AdaptiveEngine
23
- from utils.ui_components import (
24
- apply_custom_theme,
25
- display_user_stats,
26
- display_badges,
27
- create_progress_chart,
28
- animated_success,
29
- create_quiz_card,
30
- render_flashcard,
31
- )
32
- from utils.score_tracker import ScoreTracker
33
- from utils.mobile_responsive import responsive_columns, responsive_css
34
- from utils.user_auth import init_authentication, authenticate_user
35
- from utils.challenge_manager import ChallengeManager
36
- from modules.info_page import display_page_info
37
- from utils.translator import get_translated_texts
38
-
39
- # Removed load_dotenv() call as .env file is not persisting
40
- # load_dotenv()
41
-
42
- # --- Page Config ---
43
- st.set_page_config(
44
- page_title="TriviaVerse",
45
- page_icon="🎯",
46
- layout="wide",
47
- initial_sidebar_state="expanded",
48
- )
49
-
50
- # --- App State Initialization ---
51
- if "user_id" not in st.session_state:
52
- st.session_state.user_id = st.session_state.get("username", f"user_{int(time.time())}")
53
- if "score_tracker" not in st.session_state:
54
- st.session_state.score_tracker = ScoreTracker(st.session_state.user_id)
55
- if "theme" not in st.session_state:
56
- st.session_state.theme = "default"
57
- if "language" not in st.session_state:
58
- st.session_state.language = "en"
59
- if "topic" not in st.session_state:
60
- st.session_state.topic = "Python programming"
61
-
62
- # --- Load UI Text based on Language ---
63
- ui = get_translated_texts(st.session_state.language)
64
-
65
- # --- Update Page Title with Translated Text ---
66
- st.title(f"{ui.get('app_name', 'TriviaVerse')} - {ui.get('enhanced_edition', 'Enhanced Edition')}")
67
-
68
- # Initialize other session state variables
69
- if "current_streak" not in st.session_state:
70
- st.session_state.current_streak = 0
71
- if "adaptive_engine" not in st.session_state:
72
- st.session_state.adaptive_engine = AdaptiveEngine(st.session_state.user_id)
73
- if "answer" not in st.session_state:
74
- st.session_state.answer = None
75
- if "mcq_quiz_questions" not in st.session_state:
76
- st.session_state.mcq_quiz_questions = []
77
- if "current_question_index" not in st.session_state:
78
- st.session_state.current_question_index = 0
79
- if "quiz_started" not in st.session_state:
80
- st.session_state.quiz_started = False
81
- if "quiz_results" not in st.session_state:
82
- st.session_state.quiz_results = []
83
- if "flashcard_deck" not in st.session_state:
84
- st.session_state.flashcard_deck = []
85
- if "current_flashcard_index" not in st.session_state:
86
- st.session_state.current_flashcard_index = 0
87
- if "challenge_manager" not in st.session_state:
88
- st.session_state.challenge_manager = ChallengeManager()
89
- if "current_challenge_id" not in st.session_state:
90
- st.session_state.current_challenge_id = None
91
- if "challenge_quiz_questions" not in st.session_state:
92
- st.session_state.challenge_quiz_questions = []
93
- if "challenge_current_question_index" not in st.session_state:
94
- st.session_state.challenge_current_question_index = 0
95
- if "game_state" not in st.session_state:
96
- st.session_state.game_state = "menu"
97
- if "challenge_created_id" not in st.session_state:
98
- st.session_state.challenge_created_id = None
99
- if "current_page" not in st.session_state:
100
- st.session_state.current_page = "home"
101
-
102
- # --- Authentication ---
103
- init_authentication()
104
- if not authenticate_user():
105
- st.stop()
106
-
107
- # --- UI Rendering ---
108
- apply_custom_theme(st.session_state.theme)
109
- st.markdown(responsive_css(), unsafe_allow_html=True)
110
-
111
- def render_homepage():
112
- st.markdown(f"""
113
- <div class="homepage-container" style="text-align: center;">
114
- <h1 class="homepage-title">{ui.get('app_name', 'TriviaVerse')}</h1>
115
- <p class="homepage-description">{ui.get('app_description', 'A Dynamic Quiz App')}</p>
116
- <br><br>
117
- """, unsafe_allow_html=True)
118
-
119
- if st.button(ui.get('start_your_learning_journey', "Start Your Learning Journey"), key="start_journey"):
120
- st.session_state.current_page = "game_modes"
121
- st.rerun()
122
-
123
- with st.sidebar:
124
- st.title(f"🎯 {ui.get('app_name', 'TriviaVerse')}")
125
- st.markdown(f"### 🎮 {ui.get('game_settings', 'Game Settings')}")
126
-
127
- if st.button(f"🏠 {ui.get('home', 'Home')}", key="sidebar_home"):
128
- st.session_state.current_page = "home"
129
- st.rerun()
130
-
131
- st.divider()
132
-
133
- lang_choice_key = st.selectbox(
134
- f"🌐 {ui.get('choose_language', 'Choose Language')}",
135
- options=list(SUPPORTED_LANGUAGES.keys()),
136
- index=list(SUPPORTED_LANGUAGES.values()).index(st.session_state.language)
137
- )
138
- if SUPPORTED_LANGUAGES[lang_choice_key] != st.session_state.language:
139
- st.session_state.language = SUPPORTED_LANGUAGES[lang_choice_key]
140
- st.rerun()
141
-
142
- st.info(f"👤 {ui.get('welcome', 'Welcome')}, {st.session_state.get('username', 'Guest')}!")
143
-
144
- theme_select = st.selectbox(
145
- f"🎨 {ui.get('choose_theme', 'Choose Theme')}",
146
- list(THEMES.keys()),
147
- index=list(THEMES.keys()).index(st.session_state.theme),
148
- )
149
- if theme_select != st.session_state.theme:
150
- st.session_state.theme = theme_select
151
- st.rerun()
152
-
153
- st.divider()
154
-
155
- mode = st.radio(
156
- f"🎲 {ui.get('select_game_mode', 'Select Game Mode')}",
157
- [ui.get(k, k) for k in ["mcq_quiz", "flashcards", "fact_game", "challenge_mode", "page_info"]],
158
- )
159
-
160
- use_adaptive = st.checkbox(f"🤖 {ui.get('use_adaptive_difficulty', 'Use Adaptive Difficulty')}", value=True)
161
- difficulty = st.session_state.adaptive_engine.get_recommended_difficulty() if use_adaptive else st.selectbox(
162
- f"⚡ {ui.get('difficulty_level', 'Difficulty Level')}",
163
- list(DIFFICULTY_LEVELS.keys()),
164
- )
165
- if use_adaptive:
166
- st.info(f"{ui.get('recommended', 'Recommended')}: {difficulty}")
167
-
168
- st.markdown(f"### 📖 {ui.get('choose_topic', 'Choose Topic')}")
169
-
170
- def update_topic_text_input():
171
- st.session_state.topic = st.session_state.topic_text_input
172
-
173
- st.text_input(ui.get("enter_topic", "Enter topic"), value=st.session_state.topic, key="topic_text_input", on_change=update_topic_text_input)
174
-
175
- def update_random_topic():
176
- st.session_state.topic = random.choice(RANDOM_TOPICS)
177
-
178
- if st.button(f"🎲 {ui.get('random_topic', 'Random')}", on_click=update_random_topic):
179
- pass # The topic is updated by the on_click callback
180
-
181
- st.markdown(f"### 🔥 {ui.get('trending_topics', 'Trending Topics')}")
182
- def update_trending_topic():
183
- if st.session_state.trending_topic_select:
184
- st.session_state.topic = st.session_state.trending_topic_select
185
-
186
- st.selectbox(ui.get("quick_select", "Quick select"), [""] + RANDOM_TOPICS, key="trending_topic_select", on_change=update_trending_topic)
187
-
188
- if mode in [ui.get("mcq_quiz"), ui.get("challenge_mode")]:
189
- st.markdown(f"### 🔢 {ui.get('number_of_questions', 'Number of Questions')}")
190
- st.session_state.num_questions = st.selectbox(ui.get("select_number_of_questions", "Select number"), [5, 10, 15], index=1)
191
-
192
- st.divider()
193
- st.markdown(f"### 📊 {ui.get('your_stats', 'Your Stats')}")
194
- stats = st.session_state.score_tracker.get_stats()
195
- display_user_stats(stats, ui)
196
-
197
- if st.button(f"🚪 {ui.get('logout', 'Logout')}", type="secondary", use_container_width=True):
198
- st.session_state.clear()
199
- st.rerun()
200
-
201
- if st.session_state.current_page == "home":
202
- render_homepage()
203
- else:
204
- # Header with user dashboard
205
- col1, col2 = st.columns([2, 1])
206
- with col1:
207
- st.markdown(f"<h1 class='app-title'>🎮 {ui.get('app_name', 'TriviaVerse')} - {ui.get('enhanced_edition', 'Enhanced Edition')}</h1>", unsafe_allow_html=True)
208
- st.markdown(f"*{ui.get('app_description', 'A gamified learning platform powered by Wikimedia')}*")
209
- with col2:
210
- # Display current time and greeting
211
- current_hour = datetime.now().hour
212
- greeting = (
213
- ui.get("good_morning", "Good morning")
214
- if current_hour < 12
215
- else ui.get("good_afternoon", "Good afternoon")
216
- if current_hour < 18
217
- else ui.get("good_evening", "Good evening")
218
- )
219
- st.markdown(f"### {greeting}, {st.session_state.get('username', 'Learner')}! 👋")
220
- st.caption(f"{datetime.now().strftime('%B %d, %Y - %I:%M %p')}")
221
-
222
- # Display user statistics dashboard
223
- stats = st.session_state.score_tracker.get_stats()
224
- display_user_stats(stats, ui)
225
-
226
- # Display badges
227
- if stats["badges"]:
228
- st.markdown(f"### 🏅 {ui.get('your_achievements', 'Your Achievements')}")
229
- display_badges(stats["badges"], ui)
230
-
231
- # Progress chart
232
- with st.expander(f"📈 {ui.get('view_detailed_progress', 'View Detailed Progress')}", expanded=False):
233
- if stats["quizzes_completed"] > 0:
234
- fig = create_progress_chart(stats, ui)
235
- st.plotly_chart(fig, use_container_width=True)
236
- else:
237
- st.info(ui.get("complete_quizzes_prompt", "Complete some quizzes to see your progress chart!"))
238
-
239
- st.divider()
240
-
241
- # Main game area
242
- if mode == ui.get("mcq_quiz"):
243
- st.header(ui.get("mcq_quiz"))
244
-
245
- # Initialize or reset quiz
246
- if not st.session_state.quiz_started:
247
- if st.button(ui.get("start_new_quiz", "Start New Quiz"), type="primary", use_container_width=True):
248
- with st.spinner(ui.get("generating_quiz_questions", "Generating quiz questions...")):
249
- st.session_state.mcq_quiz_questions = generate_quiz_set(
250
- st.session_state.topic,
251
- difficulty,
252
- st.session_state.num_questions
253
- )
254
- if st.session_state.mcq_quiz_questions:
255
- st.session_state.current_question_index = 0
256
- st.session_state.quiz_started = True
257
- st.session_state.answered = False
258
- st.session_state.question_start_time = time.time()
259
- st.session_state.hints_used = 0
260
- st.session_state.quiz_results = [] # Reset quiz results
261
- st.rerun()
262
- else:
263
- st.error(ui.get("failed_quiz_generation", "Failed to generate quiz questions. Please try a different topic."))
264
-
265
- if st.session_state.quiz_started and st.session_state.mcq_quiz_questions:
266
- # Display current question
267
- current_mcq = st.session_state.mcq_quiz_questions[st.session_state.current_question_index]
268
- question_number = st.session_state.current_question_index + 1
269
- total_questions = len(st.session_state.mcq_quiz_questions)
270
-
271
- st.subheader(f"{ui.get('question', 'Question')} {question_number} {ui.get('of', 'of')} {total_questions}")
272
-
273
- col1, col2, col3 = responsive_columns([1, 1, 1], [1, 1, 1])
274
- with col2:
275
- time_limit = DIFFICULTY_LEVELS[difficulty]["time_limit"]
276
- st.info(f"⏱️ {ui.get('time_limit', 'Time Limit')}: {time_limit}s")
277
- with col3:
278
- hints_allowed = DIFFICULTY_LEVELS[difficulty]["hints_allowed"]
279
- st.info(f"💡 {ui.get('hints', 'Hints')}: {hints_allowed}")
280
-
281
- # Timer
282
- elapsed_time = int(time.time() - st.session_state.question_start_time)
283
- remaining_time = max(0, time_limit - elapsed_time)
284
-
285
- if remaining_time > 0 and not st.session_state.answered:
286
- st.progress(remaining_time / time_limit)
287
- st.caption(f"⏱️ {ui.get('time_remaining', 'Time remaining')}: {remaining_time}s")
288
-
289
- create_quiz_card(
290
- current_mcq["question"],
291
- current_mcq["options"],
292
- key="answer"
293
- )
294
-
295
- if hints_allowed > st.session_state.hints_used:
296
- if st.button(f"💡 {ui.get('get_hint', 'Get Hint')} ({hints_allowed - st.session_state.hints_used} {ui.get('left', 'left')})"):
297
- st.session_state.hints_used += 1
298
- st.info(f"💡 {ui.get('hint', 'Hint')}: {current_mcq['explanation'][:100]}...")
299
-
300
- col1_btn, col2_btn, col3_btn = st.columns([1, 2, 1])
301
- with col2_btn:
302
- if st.button(ui.get("submit_answer", "Submit Answer"), type="primary", use_container_width=True):
303
- is_correct = st.session_state.answer == current_mcq["correct_answer"]
304
- st.session_state.answered = True
305
- st.session_state.quiz_results.append({
306
- "question": current_mcq["question"],
307
- "user_answer": st.session_state.answer,
308
- "correct_answer": current_mcq["correct_answer"],
309
- "is_correct": is_correct,
310
- "explanation": current_mcq["explanation"]
311
- })
312
- st.session_state.adaptive_engine.update_performance(is_correct, difficulty, topic=current_mcq.get("topic"))
313
- if is_correct:
314
- st.session_state.current_streak += 1
315
- points = st.session_state.score_tracker.add_quiz_result("MCQ Quiz", 1, 1, difficulty)
316
- animated_success(f"{ui.get('correct', 'Correct')}! +{points} {ui.get('points', 'points')}")
317
- st.balloons()
318
- else:
319
- st.session_state.current_streak = 0
320
- st.error(f"❌ {ui.get('incorrect', 'Incorrect')}. {ui.get('the_answer_was', 'The answer was')}: {current_mcq['correct_answer']}")
321
- st.info(f"📚 {current_mcq['explanation']}")
322
- st.rerun()
323
- elif st.session_state.answered:
324
- if st.session_state.current_question_index < total_questions - 1:
325
- if st.button(ui.get("next_question", "Next Question"), type="secondary", use_container_width=True):
326
- st.session_state.current_question_index += 1
327
- st.session_state.answered = False
328
- st.session_state.question_start_time = time.time()
329
- st.session_state.hints_used = 0
330
- st.rerun()
331
- else:
332
- st.success(ui.get("quiz_completed", "Quiz Completed!"))
333
- st.session_state.quiz_started = False
334
- st.session_state.mcq_quiz_questions = []
335
- st.session_state.current_question_index = 0
336
- st.markdown(f"### {ui.get('quiz_results_summary', 'Quiz Results Summary')}")
337
- for i, result in enumerate(st.session_state.quiz_results):
338
- st.markdown(f"**{ui.get('question', 'Question')} {i+1}:** {result['question']}")
339
- if result['is_correct']:
340
- st.success(f"✅ {ui.get('your_answer', 'Your Answer')}: {result['user_answer']} ({ui.get('correct', 'Correct')})")
341
- else:
342
- st.error(f"❌ {ui.get('your_answer', 'Your Answer')}: {result['user_answer']} ({ui.get('incorrect', 'Incorrect')})")
343
- st.info(f"{ui.get('correct_answer', 'Correct Answer')}: {result['correct_answer']}")
344
- st.caption(f"{ui.get('explanation', 'Explanation')}: {result['explanation']}")
345
- st.divider()
346
- if st.button(ui.get("start_new_quiz", "Start New Quiz"), type="primary", use_container_width=True):
347
- st.rerun()
348
- else:
349
- st.error(f"⏱️ {ui.get('times_up', 'Time\'s up!')}")
350
- st.session_state.answered = True
351
- st.session_state.quiz_results.append({
352
- "question": current_mcq["question"],
353
- "user_answer": ui.get("no_answer_timed_out", "No answer (Time's up)"),
354
- "correct_answer": current_mcq["correct_answer"],
355
- "is_correct": False,
356
- "explanation": current_mcq["explanation"]
357
- })
358
- st.session_state.score_tracker.add_quiz_result("MCQ Quiz", 0, 1, difficulty)
359
- st.rerun()
360
-
361
- elif mode == ui.get("flashcards"):
362
- st.header(ui.get("interactive_flashcards", "Interactive Flashcards"))
363
-
364
- if st.button(ui.get("generate_flashcard", "Generate Flashcard"), type="primary"):
365
- with st.spinner(ui.get("creating_flashcard_deck", "Creating flashcard deck...")):
366
- st.session_state.flashcard_deck = generate_smart_deck(
367
- st.session_state.topic,
368
- deck_size=10,
369
- difficulty=difficulty,
370
- adaptive_engine=st.session_state.adaptive_engine
371
- )
372
- if st.session_state.flashcard_deck:
373
- st.session_state.current_flashcard_index = 0
374
- st.session_state.card_flipped = False
375
- st.rerun()
376
- else:
377
- st.error(ui.get("failed_flashcard_generation", "Failed to generate flashcards. Please try a different topic."))
378
-
379
- if st.session_state.flashcard_deck:
380
- current_flashcard = st.session_state.flashcard_deck[st.session_state.current_flashcard_index]
381
- st.subheader(f"{ui.get('flashcard', 'Flashcard')} {st.session_state.current_flashcard_index + 1} {ui.get('of', 'of')} {len(st.session_state.flashcard_deck)}")
382
-
383
- col1, col2, col3 = st.columns([1, 2, 1])
384
- with col2:
385
- render_flashcard(
386
- current_flashcard["front"],
387
- current_flashcard["back"],
388
- is_flipped=st.session_state.get("card_flipped", False),
389
- )
390
-
391
- if not st.session_state.get("card_flipped", False):
392
- if st.button(ui.get("flip_card", "Flip Card"), use_container_width=True):
393
- st.session_state.card_flipped = True
394
- st.rerun()
395
- else:
396
- st.write("")
397
- btn_col1, btn_col2 = st.columns(2)
398
- with btn_col1:
399
- if st.button(ui.get("got_it", "Got it!"), use_container_width=True):
400
- st.session_state.adaptive_engine.update_performance(
401
- is_correct=True,
402
- difficulty=current_flashcard.get("difficulty", "Medium"),
403
- item_id=current_flashcard.get("item_id"),
404
- topic=current_flashcard.get("topic")
405
- )
406
- points = st.session_state.score_tracker.add_quiz_result("Flashcards", 1, 1, current_flashcard.get("difficulty", "Medium"))
407
- animated_success(f"{ui.get('great', 'Great')}! +{points} {ui.get('points', 'points')}")
408
- st.session_state.card_flipped = False
409
- if st.session_state.current_flashcard_index < len(st.session_state.flashcard_deck) - 1:
410
- st.session_state.current_flashcard_index += 1
411
- else:
412
- st.success(ui.get("flashcard_deck_completed", "Flashcard Deck Completed!"))
413
- st.session_session.flashcard_deck = []
414
- st.session_state.current_flashcard_index = 0
415
- st.rerun()
416
- with btn_col2:
417
- if st.button(ui.get("need_practice", "Need Practice"), use_container_width=True):
418
- st.session_state.adaptive_engine.update_performance(
419
- is_correct=False,
420
- difficulty=current_flashcard.get("difficulty", "Medium"),
421
- item_id=current_flashcard.get("item_id"),
422
- topic=current_flashcard.get("topic")
423
- )
424
- st.session_state.score_tracker.add_quiz_result("Flashcards", 0, 1, current_flashcard.get("difficulty", "Medium"))
425
- st.session_state.card_flipped = False
426
- if st.session_state.current_flashcard_index < len(st.session_state.flashcard_deck) - 1:
427
- st.session_state.current_flashcard_index += 1
428
- else:
429
- st.info(ui.get("flashcard_deck_completed_practice", "Flashcard Deck Completed. Keep practicing!"))
430
- st.session_state.flashcard_deck = []
431
- st.session_state.current_flashcard_index = 0
432
- st.rerun()
433
- else:
434
- st.info(ui.get("no_flashcards_in_deck", "No flashcards in deck. Click 'Generate Flashcard' to create a new one."))
435
-
436
- elif mode == ui.get("fact_game"):
437
- st.header(ui.get("random_fact_game", "Random Fact Game"))
438
-
439
- col1, col2 = st.columns([2, 1])
440
-
441
- with col1:
442
- if st.button(ui.get("get_random_fact", "Get Random Fact"), type="primary", use_container_width=True):
443
- if "current_fact" in st.session_state:
444
- del st.session_state["current_fact"]
445
-
446
- with st.spinner(ui.get("finding_interesting_fact", "Finding an interesting fact...")):
447
- fact = generate_random_fact()
448
- if fact.get("status"):
449
- st.session_state.current_fact = fact
450
- st.session_state.fact_start_time = time.time()
451
- else:
452
- st.session_state.current_fact = {"status": False}
453
-
454
- with col2:
455
- st.info(f"⏱️ {ui.get('quick_read_challenge', 'Quick Read Challenge')}: 15s")
456
-
457
- if "current_fact" in st.session_state:
458
- fact = st.session_state.current_fact
459
-
460
- if fact.get("status"):
461
- elapsed = int(time.time() - st.session_state.get("fact_start_time", time.time()))
462
- remaining = max(0, 15 - elapsed)
463
-
464
- if remaining > 0:
465
- st.progress(remaining / 15)
466
- st.markdown(
467
- f"""
468
- <div class="quiz-container">
469
- <h3>🌟 {ui.get('did_you_know', 'Did you know?')}</h3>
470
- <p style="font-size: 18px; line-height: 1.8;">{fact["fact"]}</p>
471
- <p style="margin-top: 20px; color: #666;">{ui.get('topic', 'Topic')}: <strong>{fact["topic"]}</strong></p>
472
- </div>
473
- """,
474
- unsafe_allow_html=True,
475
- )
476
- if st.button(ui.get("interesting_next_fact", "Interesting! Next fact")):
477
- points = st.session_state.score_tracker.add_quiz_result("Fact Game", 1, 1, difficulty)
478
- animated_success(f"{ui.get('knowledge_gained', 'Knowledge gained')}! +{points} {ui.get('points', 'points')}")
479
- del st.session_state["current_fact"]
480
- else:
481
- st.success(ui.get("times_up_next_fact", "Time's up! Ready for the next fact?"))
482
- if st.button(ui.get("next_fact", "Next Fact")):
483
- del st.session_state["current_fact"]
484
- else:
485
- st.error(ui.get("failed_fact_generation", "Failed to generate fact. Please try again."))
486
-
487
- elif mode == ui.get("challenge_mode"):
488
- st.header(ui.get("challenge_mode", "Challenge Mode"))
489
-
490
- if not st.session_state.current_challenge_id:
491
- tab1, tab2 = st.tabs([ui.get("create_challenge", "Create Challenge"), ui.get("join_challenge", "Join Challenge")])
492
-
493
- with tab1:
494
- st.subheader(ui.get("create_new_challenge", "Create a New Challenge"))
495
- with st.form("create_challenge_form"):
496
- challenge_topic = st.text_input(ui.get("challenge_topic", "Challenge Topic"), value=st.session_state.topic)
497
- challenge_difficulty = st.selectbox(
498
- ui.get("challenge_difficulty", "Challenge Difficulty"),
499
- list(DIFFICULTY_LEVELS.keys()),
500
- index=list(DIFFICULTY_LEVELS.keys()).index(difficulty),
501
- )
502
- challenge_num_questions = st.selectbox(
503
- ui.get("number_of_questions", "Number of Questions"),
504
- [5, 10, 15],
505
- index=[5, 10, 15].index(st.session_state.get("num_questions", 5)),
506
- )
507
- create_challenge_submitted = st.form_submit_button(ui.get("create_challenge", "Create Challenge"), type="primary")
508
-
509
- if create_challenge_submitted:
510
- new_challenge_id = st.session_state.challenge_manager.create_challenge(
511
- creator_id=st.session_state.user_id,
512
- topic=challenge_topic,
513
- difficulty=challenge_difficulty,
514
- num_questions=challenge_num_questions
515
- )
516
- st.session_state.challenge_created_id = new_challenge_id
517
- st.success(f"{ui.get('challenge_created', 'Challenge created! Share this ID')}: **{new_challenge_id}**")
518
-
519
- if st.session_state.challenge_created_id:
520
- st.info(f"{ui.get('challenge_id', 'Challenge ID')}: **{st.session_state.challenge_created_id}**")
521
- if st.button(ui.get("start_challenge_play", "Start Challenge Play"), key="start_created_challenge"):
522
- st.session_state.current_challenge_id = st.session_state.challenge_created_id
523
- st.session_state.challenge_quiz_questions = generate_quiz_set(
524
- st.session_state.challenge_manager.get_challenge(st.session_state.current_challenge_id)["topic"],
525
- st.session_state.challenge_manager.get_challenge(st.session_state.current_challenge_id)["difficulty"],
526
- st.session_state.challenge_manager.get_challenge(st.session_state.current_challenge_id)["num_questions"]
527
- )
528
- st.session_state.challenge_quiz_started = True
529
- st.session_state.challenge_current_question_index = 0
530
- st.session_state.answered = False
531
- st.session_state.question_start_time = time.time()
532
- st.session_state.hints_used = 0
533
- st.session_state.quiz_results = []
534
- st.session_state.challenge_created_id = None
535
- st.rerun()
536
-
537
- with tab2:
538
- st.subheader(ui.get("join_existing_challenge", "Join an Existing Challenge"))
539
- with st.form("join_challenge_form"):
540
- join_challenge_id = st.text_input(ui.get("enter_challenge_id", "Enter Challenge ID"))
541
- join_challenge_submitted = st.form_submit_button(ui.get("join_challenge", "Join Challenge"), type="primary")
542
-
543
- if join_challenge_submitted:
544
- challenge = st.session_state.challenge_manager.get_challenge(join_challenge_id)
545
- if challenge:
546
- st.success(f"{ui.get('joined_challenge', 'Joined challenge')}: {challenge['topic']} ({challenge['difficulty']})")
547
- st.session_state.current_challenge_id = join_challenge_id
548
- st.session_state.challenge_quiz_questions = generate_quiz_set(
549
- challenge["topic"],
550
- challenge["difficulty"],
551
- challenge["num_questions"]
552
- )
553
- st.session_state.challenge_quiz_started = True
554
- st.session_state.challenge_current_question_index = 0
555
- st.session_state.answered = False
556
- st.session_state.question_start_time = time.time()
557
- st.session_state.hints_used = 0
558
- st.session_state.quiz_results = []
559
- st.rerun()
560
- else:
561
- st.error(ui.get("challenge_not_found", "Challenge not found. Please check the ID."))
562
-
563
- if st.session_state.current_challenge_id:
564
- current_challenge = st.session_state.challenge_manager.get_challenge(st.session_state.current_challenge_id)
565
- if current_challenge:
566
- st.markdown(f"---")
567
- st.subheader(f"{ui.get('active_challenge', 'Active Challenge')}: {current_challenge['topic']} ({current_challenge['difficulty']})")
568
- st.write(f"{ui.get('created_by', 'Created by')}: {current_challenge['creator_id']}")
569
- st.write(f"{ui.get('questions', 'Questions')}: {current_challenge['num_questions']}")
570
-
571
- st.markdown(f"#### {ui.get('participants', 'Participants')}:")
572
- if current_challenge["participants"]:
573
- for participant_id, p_data in current_challenge["participants"].items():
574
- st.write(f"- {participant_id}: {ui.get('score', 'Score')} {p_data['score']} ({ui.get('completed', 'Completed')}: {p_data.get('completed_at', 'N/A')})")
575
- else:
576
- st.write(ui.get("no_participants_yet", "No participants yet."))
577
-
578
- if st.session_state.challenge_quiz_started and st.session_state.challenge_quiz_questions:
579
- current_mcq = st.session_state.challenge_quiz_questions[st.session_state.challenge_current_question_index]
580
- question_number = st.session_state.challenge_current_question_index + 1
581
- total_questions = len(st.session_state.challenge_quiz_questions)
582
-
583
- st.subheader(f"{ui.get('challenge_question', 'Challenge Question')} {question_number} {ui.get('of', 'of')} {total_questions}")
584
-
585
- col1, col2, col3 = responsive_columns([1, 1, 1], [1, 1, 1])
586
- with col2:
587
- time_limit = DIFFICULTY_LEVELS[current_challenge['difficulty']]["time_limit"]
588
- st.info(f"⏱️ {ui.get('time_limit', 'Time Limit')}: {time_limit}s")
589
- with col3:
590
- hints_allowed = DIFFICULTY_LEVELS[current_challenge['difficulty']]["hints_allowed"]
591
- st.info(f"💡 {ui.get('hints', 'Hints')}: {hints_allowed}")
592
-
593
- elapsed_time = int(time.time() - st.session_state.question_start_time)
594
- remaining_time = max(0, time_limit - elapsed_time)
595
-
596
- if remaining_time > 0 and not st.session_state.answered:
597
- st.progress(remaining_time / time_limit)
598
- st.caption(f"⏱️ {ui.get('time_remaining', 'Time remaining')}: {remaining_time}s")
599
-
600
- create_quiz_card(current_mcq["question"], current_mcq["options"], key="challenge_answer")
601
-
602
- if hints_allowed > st.session_state.hints_used:
603
- if st.button(f"💡 {ui.get('get_hint', 'Get Hint')} ({hints_allowed - st.session_state.hints_used} {ui.get('left', 'left')})"):
604
- st.session_state.hints_used += 1
605
- st.info(f"💡 {ui.get('hint', 'Hint')}: {current_mcq['explanation'][:100]}...")
606
-
607
- col1_btn, col2_btn, col3_btn = st.columns([1, 2, 1])
608
- with col2_btn:
609
- if st.button(ui.get("submit_answer", "Submit Answer"), type="primary", use_container_width=True):
610
- is_correct = st.session_state.challenge_answer == current_mcq["correct_answer"]
611
- st.session_state.answered = True
612
- st.session_state.quiz_results.append({
613
- "question": current_mcq["question"],
614
- "user_answer": st.session_state.challenge_answer,
615
- "correct_answer": current_mcq["correct_answer"],
616
- "is_correct": is_correct,
617
- "explanation": current_mcq["explanation"]
618
- })
619
- st.session_state.adaptive_engine.update_performance(is_correct, current_challenge['difficulty'], topic=current_mcq.get("topic"))
620
- if is_correct:
621
- st.session_state.current_streak += 1
622
- points = st.session_state.score_tracker.add_quiz_result("Challenge Mode", 1, 1, current_challenge['difficulty'])
623
- animated_success(f"{ui.get('correct', 'Correct')}! +{points} {ui.get('points', 'points')}")
624
- st.balloons()
625
- else:
626
- st.session_state.current_streak = 0
627
- st.error(f"❌ {ui.get('incorrect', 'Incorrect')}. {ui.get('the_answer_was', 'The answer was')}: {current_mcq['correct_answer']}")
628
- st.info(f"📚 {current_mcq['explanation']}")
629
- st.rerun()
630
- elif st.session_state.answered:
631
- if st.session_state.challenge_current_question_index < total_questions - 1:
632
- if st.button(ui.get("next_question", "Next Question"), type="secondary", use_container_width=True):
633
- st.session_state.challenge_current_question_index += 1
634
- st.session_state.answered = False
635
- st.session_state.question_start_time = time.time()
636
- st.session_state.hints_used = 0
637
- st.rerun()
638
- else:
639
- st.success(ui.get("challenge_quiz_completed", "Challenge Quiz Completed!"))
640
- final_score = sum(1 for r in st.session_state.quiz_results if r['is_correct'])
641
- st.session_state.challenge_manager.update_challenge_score(
642
- st.session_state.current_challenge_id,
643
- st.session_state.user_id,
644
- final_score,
645
- st.session_state.quiz_results
646
- )
647
- st.session_state.challenge_quiz_started = False
648
- st.session_state.challenge_quiz_questions = []
649
- st.session_state.challenge_current_question_index = 0
650
- st.session_state.current_challenge_id = None
651
- st.rerun()
652
- else:
653
- st.error(f"⏱️ {ui.get('times_up', 'Time\'s up!')}")
654
- st.session_state.answered = True
655
- st.session_state.quiz_results.append({
656
- "question": current_mcq["question"],
657
- "user_answer": ui.get("no_answer_timed_out", "No answer (Time's up)"),
658
- "correct_answer": current_mcq["correct_answer"],
659
- "is_correct": False,
660
- "explanation": current_mcq["explanation"]
661
- })
662
- st.session_state.score_tracker.add_quiz_result("Challenge Mode", 0, 1, difficulty)
663
- st.rerun()
664
-
665
- elif mode == ui.get("page_info"):
666
- st.header(ui.get("page_information", "Page Information"))
667
- st.write(ui.get("get_page_info_prompt", "Get detailed information and images for any topic."))
668
-
669
- info_topic = st.text_input(ui.get("enter_topic_for_info", "Enter Topic for Information"), value=st.session_state.topic)
670
- num_images = st.slider(ui.get("number_of_images", "Number of Images"), 0, 5, 1)
671
-
672
- if st.button(ui.get("get_information", "Get Information"), type="primary"):
673
- with st.spinner(f"{ui.get('fetching_information_for', 'Fetching information for')} {info_topic}..."):
674
- display_page_info(info_topic, num_images)
675
-
676
- st.divider()
677
- st.markdown(f"""
678
- <div style='text-align: center'>
679
- <p>{ui.get('made_with', 'Made with')} ❤️ {ui.get('using_streamlit_and_wikimedia', 'using Streamlit and Wikimedia APIs')}</p>
680
- <p style='opacity: 0.7; font-size: 0.9em;'>{ui.get('app_name', 'TriviaVerse')} v2.0 - {ui.get('enhanced_edition', 'Enhanced Edition')}</p>
681
- </div>
682
- """, unsafe_allow_html=True)
 
1
+ # app.py
2
+ """TriviaVerse Enhanced - Advanced Quiz Application"""
3
+
4
+ import streamlit as st
5
+ import time
6
+ from datetime import datetime
7
+ import random
8
+ # Removed dotenv import and os import as .env file is not persisting
9
+ # from dotenv import load_dotenv
10
+ # import os
11
+
12
+ from config.settings import (
13
+ DIFFICULTY_LEVELS,
14
+ BADGES,
15
+ RANDOM_TOPICS,
16
+ )
17
+ from config.themes import THEMES
18
+ from config.languages import SUPPORTED_LANGUAGES
19
+ from modules.mcq_generator import generate_quiz_set
20
+ from modules.flashcard_generator import generate_smart_deck
21
+ from modules.fact_game import generate_random_fact
22
+ from modules.adaptive_engine import AdaptiveEngine
23
+ from utils.ui_components import (
24
+ apply_custom_theme,
25
+ display_user_stats,
26
+ display_badges,
27
+ create_progress_chart,
28
+ animated_success,
29
+ create_quiz_card,
30
+ render_flashcard,
31
+ )
32
+ from utils.score_tracker import ScoreTracker
33
+ from utils.mobile_responsive import responsive_columns, responsive_css
34
+ from utils.user_auth import init_authentication, authenticate_user
35
+ from utils.challenge_manager import ChallengeManager
36
+ from modules.info_page import display_page_info
37
+ from utils.translator import get_translated_texts
38
+
39
+ # Removed load_dotenv() call as .env file is not persisting
40
+ # load_dotenv()
41
+
42
+ # --- Page Config ---
43
+ st.set_page_config(
44
+ page_title="TriviaVerse",
45
+ page_icon="🎯",
46
+ layout="wide",
47
+ initial_sidebar_state="expanded",
48
+ )
49
+
50
+ # --- App State Initialization ---
51
+ if "user_id" not in st.session_state:
52
+ st.session_state.user_id = st.session_state.get("username", f"user_{int(time.time())}")
53
+ if "score_tracker" not in st.session_state:
54
+ st.session_state.score_tracker = ScoreTracker(st.session_state.user_id)
55
+ if "theme" not in st.session_state:
56
+ st.session_state.theme = "default"
57
+ if "language" not in st.session_state:
58
+ st.session_state.language = "en"
59
+ if "topic" not in st.session_state:
60
+ st.session_state.topic = "Python programming"
61
+
62
+ # --- Load UI Text based on Language ---
63
+ ui = get_translated_texts(st.session_state.language)
64
+
65
+ # --- Update Page Title with Translated Text ---
66
+ st.title(f"{ui.get('app_name', 'TriviaVerse')} - {ui.get('enhanced_edition', 'Enhanced Edition')}")
67
+
68
+ # Initialize other session state variables
69
+ if "current_streak" not in st.session_state:
70
+ st.session_state.current_streak = 0
71
+ if "adaptive_engine" not in st.session_state:
72
+ st.session_state.adaptive_engine = AdaptiveEngine(st.session_state.user_id)
73
+ if "answer" not in st.session_state:
74
+ st.session_state.answer = None
75
+ if "mcq_quiz_questions" not in st.session_state:
76
+ st.session_state.mcq_quiz_questions = []
77
+ if "current_question_index" not in st.session_state:
78
+ st.session_state.current_question_index = 0
79
+ if "quiz_started" not in st.session_state:
80
+ st.session_state.quiz_started = False
81
+ if "quiz_results" not in st.session_state:
82
+ st.session_state.quiz_results = []
83
+ if "flashcard_deck" not in st.session_state:
84
+ st.session_state.flashcard_deck = []
85
+ if "current_flashcard_index" not in st.session_state:
86
+ st.session_state.current_flashcard_index = 0
87
+ if "challenge_manager" not in st.session_state:
88
+ st.session_state.challenge_manager = ChallengeManager()
89
+ if "current_challenge_id" not in st.session_state:
90
+ st.session_state.current_challenge_id = None
91
+ if "challenge_quiz_questions" not in st.session_state:
92
+ st.session_state.challenge_quiz_questions = []
93
+ if "challenge_current_question_index" not in st.session_state:
94
+ st.session_state.challenge_current_question_index = 0
95
+ if "game_state" not in st.session_state:
96
+ st.session_state.game_state = "menu"
97
+ if "challenge_created_id" not in st.session_state:
98
+ st.session_state.challenge_created_id = None
99
+ if "current_page" not in st.session_state:
100
+ st.session_state.current_page = "home"
101
+
102
+ # --- Authentication ---
103
+ init_authentication()
104
+ if not authenticate_user():
105
+ st.stop()
106
+
107
+ # --- UI Rendering ---
108
+ apply_custom_theme(st.session_state.theme)
109
+ st.markdown(responsive_css(), unsafe_allow_html=True)
110
+
111
+ def render_homepage():
112
+ st.markdown(f"""
113
+ <div class="homepage-container" style="text-align: center;">
114
+ <h1 class="homepage-title">{ui.get('app_name', 'TriviaVerse')}</h1>
115
+ <p class="homepage-description">{ui.get('app_description', 'A Dynamic Quiz App')}</p>
116
+ <br><br>
117
+ """, unsafe_allow_html=True)
118
+
119
+ if st.button(ui.get('start_your_learning_journey', "Start Your Learning Journey"), key="start_journey"):
120
+ st.session_state.current_page = "game_modes"
121
+ st.rerun()
122
+
123
+ with st.sidebar:
124
+ st.title(f"🎯 {ui.get('app_name', 'TriviaVerse')}")
125
+ st.markdown(f"### 🎮 {ui.get('game_settings', 'Game Settings')}")
126
+
127
+ if st.button(f"🏠 {ui.get('home', 'Home')}", key="sidebar_home"):
128
+ st.session_state.current_page = "home"
129
+ st.rerun()
130
+
131
+ st.divider()
132
+
133
+ lang_choice_key = st.selectbox(
134
+ f"🌐 {ui.get('choose_language', 'Choose Language')}",
135
+ options=list(SUPPORTED_LANGUAGES.keys()),
136
+ index=list(SUPPORTED_LANGUAGES.values()).index(st.session_state.language)
137
+ )
138
+ if SUPPORTED_LANGUAGES[lang_choice_key] != st.session_state.language:
139
+ st.session_state.language = SUPPORTED_LANGUAGES[lang_choice_key]
140
+ st.rerun()
141
+
142
+ st.info(f"👤 {ui.get('welcome', 'Welcome')}, {st.session_state.get('username', 'Guest')}!")
143
+
144
+ theme_select = st.selectbox(
145
+ f"🎨 {ui.get('choose_theme', 'Choose Theme')}",
146
+ list(THEMES.keys()),
147
+ index=list(THEMES.keys()).index(st.session_state.theme),
148
+ )
149
+ if theme_select != st.session_state.theme:
150
+ st.session_state.theme = theme_select
151
+ st.rerun()
152
+
153
+ st.divider()
154
+
155
+ mode = st.radio(
156
+ f"🎲 {ui.get('select_game_mode', 'Select Game Mode')}",
157
+ [ui.get(k, k) for k in ["mcq_quiz", "flashcards", "fact_game", "challenge_mode", "page_info"]],
158
+ )
159
+
160
+ use_adaptive = st.checkbox(f"🤖 {ui.get('use_adaptive_difficulty', 'Use Adaptive Difficulty')}", value=True)
161
+ difficulty = st.session_state.adaptive_engine.get_recommended_difficulty() if use_adaptive else st.selectbox(
162
+ f"⚡ {ui.get('difficulty_level', 'Difficulty Level')}",
163
+ list(DIFFICULTY_LEVELS.keys()),
164
+ )
165
+ if use_adaptive:
166
+ st.info(f"{ui.get('recommended', 'Recommended')}: {difficulty}")
167
+
168
+ st.markdown(f"### 📖 {ui.get('choose_topic', 'Choose Topic')}")
169
+
170
+ def update_topic_text_input():
171
+ st.session_state.topic = st.session_state.topic_text_input
172
+
173
+ st.text_input(ui.get("enter_topic", "Enter topic"), value=st.session_state.topic, key="topic_text_input", on_change=update_topic_text_input)
174
+
175
+ def update_random_topic():
176
+ st.session_state.topic = random.choice(RANDOM_TOPICS)
177
+
178
+ if st.button(f"🎲 {ui.get('random_topic', 'Random')}", on_click=update_random_topic):
179
+ pass # The topic is updated by the on_click callback
180
+
181
+ st.markdown(f"### 🔥 {ui.get('trending_topics', 'Trending Topics')}")
182
+ def update_trending_topic():
183
+ if st.session_state.trending_topic_select:
184
+ st.session_state.topic = st.session_state.trending_topic_select
185
+
186
+ st.selectbox(ui.get("quick_select", "Quick select"), [""] + RANDOM_TOPICS, key="trending_topic_select", on_change=update_trending_topic)
187
+
188
+ if mode in [ui.get("mcq_quiz"), ui.get("challenge_mode")]:
189
+ st.markdown(f"### 🔢 {ui.get('number_of_questions', 'Number of Questions')}")
190
+ st.session_state.num_questions = st.selectbox(ui.get("select_number_of_questions", "Select number"), [5, 10, 15], index=1)
191
+
192
+ st.divider()
193
+ st.markdown(f"### 📊 {ui.get('your_stats', 'Your Stats')}")
194
+ stats = st.session_state.score_tracker.get_stats()
195
+ display_user_stats(stats, ui)
196
+
197
+ if st.button(f"🚪 {ui.get('logout', 'Logout')}", type="secondary", use_container_width=True):
198
+ st.session_state.clear()
199
+ st.rerun()
200
+
201
+ if st.session_state.current_page == "home":
202
+ render_homepage()
203
+ else:
204
+ # Header with user dashboard
205
+ col1, col2 = st.columns([2, 1])
206
+ with col1:
207
+ st.markdown(f"<h1 class='app-title'>🎮 {ui.get('app_name', 'TriviaVerse')} - {ui.get('enhanced_edition', 'Enhanced Edition')}</h1>", unsafe_allow_html=True)
208
+ st.markdown(f"*{ui.get('app_description', 'A gamified learning platform powered by Wikimedia')}*")
209
+ with col2:
210
+ # Display current time and greeting
211
+ current_hour = datetime.now().hour
212
+ greeting = (
213
+ ui.get("good_morning", "Good morning")
214
+ if current_hour < 12
215
+ else ui.get("good_afternoon", "Good afternoon")
216
+ if current_hour < 18
217
+ else ui.get("good_evening", "Good evening")
218
+ )
219
+ st.markdown(f"### {greeting}, {st.session_state.get('username', 'Learner')}! 👋")
220
+ st.caption(f"{datetime.now().strftime('%B %d, %Y - %I:%M %p')}")
221
+
222
+ # Display user statistics dashboard
223
+ stats = st.session_state.score_tracker.get_stats()
224
+ display_user_stats(stats, ui)
225
+
226
+ # Display badges
227
+ if stats["badges"]:
228
+ st.markdown(f"### 🏅 {ui.get('your_achievements', 'Your Achievements')}")
229
+ display_badges(stats["badges"], ui)
230
+
231
+ # Progress chart
232
+ with st.expander(f"📈 {ui.get('view_detailed_progress', 'View Detailed Progress')}", expanded=False):
233
+ if stats["quizzes_completed"] > 0:
234
+ fig = create_progress_chart(stats, ui)
235
+ st.plotly_chart(fig, use_container_width=True)
236
+ else:
237
+ st.info(ui.get("complete_quizzes_prompt", "Complete some quizzes to see your progress chart!"))
238
+
239
+ st.divider()
240
+
241
+ # Main game area
242
+ if mode == ui.get("mcq_quiz"):
243
+ st.header(ui.get("mcq_quiz"))
244
+
245
+ # Initialize or reset quiz
246
+ if not st.session_state.quiz_started:
247
+ if st.button(ui.get("start_new_quiz", "Start New Quiz"), type="primary", use_container_width=True):
248
+ with st.spinner(ui.get("generating_quiz_questions", "Generating quiz questions...")):
249
+ st.session_state.mcq_quiz_questions = generate_quiz_set(
250
+ st.session_state.topic,
251
+ difficulty,
252
+ st.session_state.num_questions
253
+ )
254
+ if st.session_state.mcq_quiz_questions:
255
+ st.session_state.current_question_index = 0
256
+ st.session_state.quiz_started = True
257
+ st.session_state.answered = False
258
+ st.session_state.question_start_time = time.time()
259
+ st.session_state.hints_used = 0
260
+ st.session_state.quiz_results = [] # Reset quiz results
261
+ st.rerun()
262
+ else:
263
+ st.error(ui.get("failed_quiz_generation", "Failed to generate quiz questions. Please try a different topic."))
264
+
265
+ if st.session_state.quiz_started and st.session_state.mcq_quiz_questions:
266
+ # Display current question
267
+ current_mcq = st.session_state.mcq_quiz_questions[st.session_state.current_question_index]
268
+ question_number = st.session_state.current_question_index + 1
269
+ total_questions = len(st.session_state.mcq_quiz_questions)
270
+
271
+ st.subheader(f"{ui.get('question', 'Question')} {question_number} {ui.get('of', 'of')} {total_questions}")
272
+
273
+ col1, col2, col3 = responsive_columns([1, 1, 1], [1, 1, 1])
274
+ with col2:
275
+ time_limit = DIFFICULTY_LEVELS[difficulty]["time_limit"]
276
+ st.info(f"⏱️ {ui.get('time_limit', 'Time Limit')}: {time_limit}s")
277
+ with col3:
278
+ hints_allowed = DIFFICULTY_LEVELS[difficulty]["hints_allowed"]
279
+ st.info(f"💡 {ui.get('hints', 'Hints')}: {hints_allowed}")
280
+
281
+ # Timer
282
+ elapsed_time = int(time.time() - st.session_state.question_start_time)
283
+ remaining_time = max(0, time_limit - elapsed_time)
284
+
285
+ if remaining_time > 0 and not st.session_state.answered:
286
+ st.progress(remaining_time / time_limit)
287
+ st.caption(f"⏱️ {ui.get('time_remaining', 'Time remaining')}: {remaining_time}s")
288
+
289
+ create_quiz_card(
290
+ current_mcq["question"],
291
+ current_mcq["options"],
292
+ key="answer"
293
+ )
294
+
295
+ if hints_allowed > st.session_state.hints_used:
296
+ if st.button(f"💡 {ui.get('get_hint', 'Get Hint')} ({hints_allowed - st.session_state.hints_used} {ui.get('left', 'left')})"):
297
+ st.session_state.hints_used += 1
298
+ st.info(f"💡 {ui.get('hint', 'Hint')}: {current_mcq['explanation'][:100]}...")
299
+
300
+ col1_btn, col2_btn, col3_btn = st.columns([1, 2, 1])
301
+ with col2_btn:
302
+ if st.button(ui.get("submit_answer", "Submit Answer"), type="primary", use_container_width=True):
303
+ is_correct = st.session_state.answer == current_mcq["correct_answer"]
304
+ st.session_state.answered = True
305
+ st.session_state.quiz_results.append({
306
+ "question": current_mcq["question"],
307
+ "user_answer": st.session_state.answer,
308
+ "correct_answer": current_mcq["correct_answer"],
309
+ "is_correct": is_correct,
310
+ "explanation": current_mcq["explanation"]
311
+ })
312
+ st.session_state.adaptive_engine.update_performance(is_correct, difficulty, topic=current_mcq.get("topic"))
313
+ if is_correct:
314
+ st.session_state.current_streak += 1
315
+ points = st.session_state.score_tracker.add_quiz_result("MCQ Quiz", 1, 1, difficulty)
316
+ animated_success(f"{ui.get('correct', 'Correct')}! +{points} {ui.get('points', 'points')}")
317
+ st.balloons()
318
+ else:
319
+ st.session_state.current_streak = 0
320
+ st.error(f"❌ {ui.get('incorrect', 'Incorrect')}. {ui.get('the_answer_was', 'The answer was')}: {current_mcq['correct_answer']}")
321
+ st.info(f"📚 {current_mcq['explanation']}")
322
+ st.rerun()
323
+ elif st.session_state.answered:
324
+ if st.session_state.current_question_index < total_questions - 1:
325
+ if st.button(ui.get("next_question", "Next Question"), type="secondary", use_container_width=True):
326
+ st.session_state.current_question_index += 1
327
+ st.session_state.answered = False
328
+ st.session_state.question_start_time = time.time()
329
+ st.session_state.hints_used = 0
330
+ st.rerun()
331
+ else:
332
+ st.success(ui.get("quiz_completed", "Quiz Completed!"))
333
+ st.session_state.quiz_started = False
334
+ st.session_state.mcq_quiz_questions = []
335
+ st.session_state.current_question_index = 0
336
+ st.markdown(f"### {ui.get('quiz_results_summary', 'Quiz Results Summary')}")
337
+ for i, result in enumerate(st.session_state.quiz_results):
338
+ st.markdown(f"**{ui.get('question', 'Question')} {i+1}:** {result['question']}")
339
+ if result['is_correct']:
340
+ st.success(f"✅ {ui.get('your_answer', 'Your Answer')}: {result['user_answer']} ({ui.get('correct', 'Correct')})")
341
+ else:
342
+ st.error(f"❌ {ui.get('your_answer', 'Your Answer')}: {result['user_answer']} ({ui.get('incorrect', 'Incorrect')})")
343
+ st.info(f"{ui.get('correct_answer', 'Correct Answer')}: {result['correct_answer']}")
344
+ st.caption(f"{ui.get('explanation', 'Explanation')}: {result['explanation']}")
345
+ st.divider()
346
+ if st.button(ui.get("start_new_quiz", "Start New Quiz"), type="primary", use_container_width=True):
347
+ st.rerun()
348
+ else:
349
+ st.error(f"⏱️ {ui.get('times_up', "Time's up!")}")
350
+ st.session_state.answered = True
351
+ st.session_state.quiz_results.append({
352
+ "question": current_mcq["question"],
353
+ "user_answer": ui.get("no_answer_timed_out", "No answer (Time's up)"),
354
+ "correct_answer": current_mcq["correct_answer"],
355
+ "is_correct": False,
356
+ "explanation": current_mcq["explanation"]
357
+ })
358
+ st.session_state.score_tracker.add_quiz_result("MCQ Quiz", 0, 1, difficulty)
359
+ st.rerun()
360
+
361
+ elif mode == ui.get("flashcards"):
362
+ st.header(ui.get("interactive_flashcards", "Interactive Flashcards"))
363
+
364
+ if st.button(ui.get("generate_flashcard", "Generate Flashcard"), type="primary"):
365
+ with st.spinner(ui.get("creating_flashcard_deck", "Creating flashcard deck...")):
366
+ st.session_state.flashcard_deck = generate_smart_deck(
367
+ st.session_state.topic,
368
+ deck_size=10,
369
+ difficulty=difficulty,
370
+ adaptive_engine=st.session_state.adaptive_engine
371
+ )
372
+ if st.session_state.flashcard_deck:
373
+ st.session_state.current_flashcard_index = 0
374
+ st.session_state.card_flipped = False
375
+ st.rerun()
376
+ else:
377
+ st.error(ui.get("failed_flashcard_generation", "Failed to generate flashcards. Please try a different topic."))
378
+
379
+ if st.session_state.flashcard_deck:
380
+ current_flashcard = st.session_state.flashcard_deck[st.session_state.current_flashcard_index]
381
+ st.subheader(f"{ui.get('flashcard', 'Flashcard')} {st.session_state.current_flashcard_index + 1} {ui.get('of', 'of')} {len(st.session_state.flashcard_deck)}")
382
+
383
+ col1, col2, col3 = st.columns([1, 2, 1])
384
+ with col2:
385
+ render_flashcard(
386
+ current_flashcard["front"],
387
+ current_flashcard["back"],
388
+ is_flipped=st.session_state.get("card_flipped", False),
389
+ )
390
+
391
+ if not st.session_state.get("card_flipped", False):
392
+ if st.button(ui.get("flip_card", "Flip Card"), use_container_width=True):
393
+ st.session_state.card_flipped = True
394
+ st.rerun()
395
+ else:
396
+ st.write("")
397
+ btn_col1, btn_col2 = st.columns(2)
398
+ with btn_col1:
399
+ if st.button(ui.get("got_it", "Got it!"), use_container_width=True):
400
+ st.session_state.adaptive_engine.update_performance(
401
+ is_correct=True,
402
+ difficulty=current_flashcard.get("difficulty", "Medium"),
403
+ item_id=current_flashcard.get("item_id"),
404
+ topic=current_flashcard.get("topic")
405
+ )
406
+ points = st.session_state.score_tracker.add_quiz_result("Flashcards", 1, 1, current_flashcard.get("difficulty", "Medium"))
407
+ animated_success(f"{ui.get('great', 'Great')}! +{points} {ui.get('points', 'points')}")
408
+ st.session_state.card_flipped = False
409
+ if st.session_state.current_flashcard_index < len(st.session_state.flashcard_deck) - 1:
410
+ st.session_state.current_flashcard_index += 1
411
+ else:
412
+ st.success(ui.get("flashcard_deck_completed", "Flashcard Deck Completed!"))
413
+ st.session_session.flashcard_deck = []
414
+ st.session_state.current_flashcard_index = 0
415
+ st.rerun()
416
+ with btn_col2:
417
+ if st.button(ui.get("need_practice", "Need Practice"), use_container_width=True):
418
+ st.session_state.adaptive_engine.update_performance(
419
+ is_correct=False,
420
+ difficulty=current_flashcard.get("difficulty", "Medium"),
421
+ item_id=current_flashcard.get("item_id"),
422
+ topic=current_flashcard.get("topic")
423
+ )
424
+ st.session_state.score_tracker.add_quiz_result("Flashcards", 0, 1, current_flashcard.get("difficulty", "Medium"))
425
+ st.session_state.card_flipped = False
426
+ if st.session_state.current_flashcard_index < len(st.session_state.flashcard_deck) - 1:
427
+ st.session_state.current_flashcard_index += 1
428
+ else:
429
+ st.info(ui.get("flashcard_deck_completed_practice", "Flashcard Deck Completed. Keep practicing!"))
430
+ st.session_state.flashcard_deck = []
431
+ st.session_state.current_flashcard_index = 0
432
+ st.rerun()
433
+ else:
434
+ st.info(ui.get("no_flashcards_in_deck", "No flashcards in deck. Click 'Generate Flashcard' to create a new one."))
435
+
436
+ elif mode == ui.get("fact_game"):
437
+ st.header(ui.get("random_fact_game", "Random Fact Game"))
438
+
439
+ col1, col2 = st.columns([2, 1])
440
+
441
+ with col1:
442
+ if st.button(ui.get("get_random_fact", "Get Random Fact"), type="primary", use_container_width=True):
443
+ if "current_fact" in st.session_state:
444
+ del st.session_state["current_fact"]
445
+
446
+ with st.spinner(ui.get("finding_interesting_fact", "Finding an interesting fact...")):
447
+ fact = generate_random_fact()
448
+ if fact.get("status"):
449
+ st.session_state.current_fact = fact
450
+ st.session_state.fact_start_time = time.time()
451
+ else:
452
+ st.session_state.current_fact = {"status": False}
453
+
454
+ with col2:
455
+ st.info(f"⏱️ {ui.get('quick_read_challenge', 'Quick Read Challenge')}: 15s")
456
+
457
+ if "current_fact" in st.session_state:
458
+ fact = st.session_state.current_fact
459
+
460
+ if fact.get("status"):
461
+ elapsed = int(time.time() - st.session_state.get("fact_start_time", time.time()))
462
+ remaining = max(0, 15 - elapsed)
463
+
464
+ if remaining > 0:
465
+ st.progress(remaining / 15)
466
+ st.markdown(
467
+ f"""
468
+ <div class="quiz-container">
469
+ <h3>🌟 {ui.get('did_you_know', 'Did you know?')}</h3>
470
+ <p style="font-size: 18px; line-height: 1.8;">{fact["fact"]}</p>
471
+ <p style="margin-top: 20px; color: #666;">{ui.get('topic', 'Topic')}: <strong>{fact["topic"]}</strong></p>
472
+ </div>
473
+ """,
474
+ unsafe_allow_html=True,
475
+ )
476
+ if st.button(ui.get("interesting_next_fact", "Interesting! Next fact")):
477
+ points = st.session_state.score_tracker.add_quiz_result("Fact Game", 1, 1, difficulty)
478
+ animated_success(f"{ui.get('knowledge_gained', 'Knowledge gained')}! +{points} {ui.get('points', 'points')}")
479
+ del st.session_state["current_fact"]
480
+ else:
481
+ st.success(ui.get("times_up_next_fact", "Time's up! Ready for the next fact?"))
482
+ if st.button(ui.get("next_fact", "Next Fact")):
483
+ del st.session_state["current_fact"]
484
+ else:
485
+ st.error(ui.get("failed_fact_generation", "Failed to generate fact. Please try again."))
486
+
487
+ elif mode == ui.get("challenge_mode"):
488
+ st.header(ui.get("challenge_mode", "Challenge Mode"))
489
+
490
+ if not st.session_state.current_challenge_id:
491
+ tab1, tab2 = st.tabs([ui.get("create_challenge", "Create Challenge"), ui.get("join_challenge", "Join Challenge")])
492
+
493
+ with tab1:
494
+ st.subheader(ui.get("create_new_challenge", "Create a New Challenge"))
495
+ with st.form("create_challenge_form"):
496
+ challenge_topic = st.text_input(ui.get("challenge_topic", "Challenge Topic"), value=st.session_state.topic)
497
+ challenge_difficulty = st.selectbox(
498
+ ui.get("challenge_difficulty", "Challenge Difficulty"),
499
+ list(DIFFICULTY_LEVELS.keys()),
500
+ index=list(DIFFICULTY_LEVELS.keys()).index(difficulty),
501
+ )
502
+ challenge_num_questions = st.selectbox(
503
+ ui.get("number_of_questions", "Number of Questions"),
504
+ [5, 10, 15],
505
+ index=[5, 10, 15].index(st.session_state.get("num_questions", 5)),
506
+ )
507
+ create_challenge_submitted = st.form_submit_button(ui.get("create_challenge", "Create Challenge"), type="primary")
508
+
509
+ if create_challenge_submitted:
510
+ new_challenge_id = st.session_state.challenge_manager.create_challenge(
511
+ creator_id=st.session_state.user_id,
512
+ topic=challenge_topic,
513
+ difficulty=challenge_difficulty,
514
+ num_questions=challenge_num_questions
515
+ )
516
+ st.session_state.challenge_created_id = new_challenge_id
517
+ st.success(f"{ui.get('challenge_created', 'Challenge created! Share this ID')}: **{new_challenge_id}**")
518
+
519
+ if st.session_state.challenge_created_id:
520
+ st.info(f"{ui.get('challenge_id', 'Challenge ID')}: **{st.session_state.challenge_created_id}**")
521
+ if st.button(ui.get("start_challenge_play", "Start Challenge Play"), key="start_created_challenge"):
522
+ st.session_state.current_challenge_id = st.session_state.challenge_created_id
523
+ st.session_state.challenge_quiz_questions = generate_quiz_set(
524
+ st.session_state.challenge_manager.get_challenge(st.session_state.current_challenge_id)["topic"],
525
+ st.session_state.challenge_manager.get_challenge(st.session_state.current_challenge_id)["difficulty"],
526
+ st.session_state.challenge_manager.get_challenge(st.session_state.current_challenge_id)["num_questions"]
527
+ )
528
+ st.session_state.challenge_quiz_started = True
529
+ st.session_state.challenge_current_question_index = 0
530
+ st.session_state.answered = False
531
+ st.session_state.question_start_time = time.time()
532
+ st.session_state.hints_used = 0
533
+ st.session_state.quiz_results = []
534
+ st.session_state.challenge_created_id = None
535
+ st.rerun()
536
+
537
+ with tab2:
538
+ st.subheader(ui.get("join_existing_challenge", "Join an Existing Challenge"))
539
+ with st.form("join_challenge_form"):
540
+ join_challenge_id = st.text_input(ui.get("enter_challenge_id", "Enter Challenge ID"))
541
+ join_challenge_submitted = st.form_submit_button(ui.get("join_challenge", "Join Challenge"), type="primary")
542
+
543
+ if join_challenge_submitted:
544
+ challenge = st.session_state.challenge_manager.get_challenge(join_challenge_id)
545
+ if challenge:
546
+ st.success(f"{ui.get('joined_challenge', 'Joined challenge')}: {challenge['topic']} ({challenge['difficulty']})")
547
+ st.session_state.current_challenge_id = join_challenge_id
548
+ st.session_state.challenge_quiz_questions = generate_quiz_set(
549
+ challenge["topic"],
550
+ challenge["difficulty"],
551
+ challenge["num_questions"]
552
+ )
553
+ st.session_state.challenge_quiz_started = True
554
+ st.session_state.challenge_current_question_index = 0
555
+ st.session_state.answered = False
556
+ st.session_state.question_start_time = time.time()
557
+ st.session_state.hints_used = 0
558
+ st.session_state.quiz_results = []
559
+ st.rerun()
560
+ else:
561
+ st.error(ui.get("challenge_not_found", "Challenge not found. Please check the ID."))
562
+
563
+ if st.session_state.current_challenge_id:
564
+ current_challenge = st.session_state.challenge_manager.get_challenge(st.session_state.current_challenge_id)
565
+ if current_challenge:
566
+ st.markdown(f"---")
567
+ st.subheader(f"{ui.get('active_challenge', 'Active Challenge')}: {current_challenge['topic']} ({current_challenge['difficulty']})")
568
+ st.write(f"{ui.get('created_by', 'Created by')}: {current_challenge['creator_id']}")
569
+ st.write(f"{ui.get('questions', 'Questions')}: {current_challenge['num_questions']}")
570
+
571
+ st.markdown(f"#### {ui.get('participants', 'Participants')}:")
572
+ if current_challenge["participants"]:
573
+ for participant_id, p_data in current_challenge["participants"].items():
574
+ st.write(f"- {participant_id}: {ui.get('score', 'Score')} {p_data['score']} ({ui.get('completed', 'Completed')}: {p_data.get('completed_at', 'N/A')})")
575
+ else:
576
+ st.write(ui.get("no_participants_yet", "No participants yet."))
577
+
578
+ if st.session_state.challenge_quiz_started and st.session_state.challenge_quiz_questions:
579
+ current_mcq = st.session_state.challenge_quiz_questions[st.session_state.challenge_current_question_index]
580
+ question_number = st.session_state.challenge_current_question_index + 1
581
+ total_questions = len(st.session_state.challenge_quiz_questions)
582
+
583
+ st.subheader(f"{ui.get('challenge_question', 'Challenge Question')} {question_number} {ui.get('of', 'of')} {total_questions}")
584
+
585
+ col1, col2, col3 = responsive_columns([1, 1, 1], [1, 1, 1])
586
+ with col2:
587
+ time_limit = DIFFICULTY_LEVELS[current_challenge['difficulty']]["time_limit"]
588
+ st.info(f"⏱️ {ui.get('time_limit', 'Time Limit')}: {time_limit}s")
589
+ with col3:
590
+ hints_allowed = DIFFICULTY_LEVELS[current_challenge['difficulty']]["hints_allowed"]
591
+ st.info(f"💡 {ui.get('hints', 'Hints')}: {hints_allowed}")
592
+
593
+ elapsed_time = int(time.time() - st.session_state.question_start_time)
594
+ remaining_time = max(0, time_limit - elapsed_time)
595
+
596
+ if remaining_time > 0 and not st.session_state.answered:
597
+ st.progress(remaining_time / time_limit)
598
+ st.caption(f"⏱️ {ui.get('time_remaining', 'Time remaining')}: {remaining_time}s")
599
+
600
+ create_quiz_card(current_mcq["question"], current_mcq["options"], key="challenge_answer")
601
+
602
+ if hints_allowed > st.session_state.hints_used:
603
+ if st.button(f"💡 {ui.get('get_hint', 'Get Hint')} ({hints_allowed - st.session_state.hints_used} {ui.get('left', 'left')})"):
604
+ st.session_state.hints_used += 1
605
+ st.info(f"💡 {ui.get('hint', 'Hint')}: {current_mcq['explanation'][:100]}...")
606
+
607
+ col1_btn, col2_btn, col3_btn = st.columns([1, 2, 1])
608
+ with col2_btn:
609
+ if st.button(ui.get("submit_answer", "Submit Answer"), type="primary", use_container_width=True):
610
+ is_correct = st.session_state.challenge_answer == current_mcq["correct_answer"]
611
+ st.session_state.answered = True
612
+ st.session_state.quiz_results.append({
613
+ "question": current_mcq["question"],
614
+ "user_answer": st.session_state.challenge_answer,
615
+ "correct_answer": current_mcq["correct_answer"],
616
+ "is_correct": is_correct,
617
+ "explanation": current_mcq["explanation"]
618
+ })
619
+ st.session_state.adaptive_engine.update_performance(is_correct, current_challenge['difficulty'], topic=current_mcq.get("topic"))
620
+ if is_correct:
621
+ st.session_state.current_streak += 1
622
+ points = st.session_state.score_tracker.add_quiz_result("Challenge Mode", 1, 1, current_challenge['difficulty'])
623
+ animated_success(f"{ui.get('correct', 'Correct')}! +{points} {ui.get('points', 'points')}")
624
+ st.balloons()
625
+ else:
626
+ st.session_state.current_streak = 0
627
+ st.error(f"❌ {ui.get('incorrect', 'Incorrect')}. {ui.get('the_answer_was', 'The answer was')}: {current_mcq['correct_answer']}")
628
+ st.info(f"📚 {current_mcq['explanation']}")
629
+ st.rerun()
630
+ elif st.session_state.answered:
631
+ if st.session_state.challenge_current_question_index < total_questions - 1:
632
+ if st.button(ui.get("next_question", "Next Question"), type="secondary", use_container_width=True):
633
+ st.session_state.challenge_current_question_index += 1
634
+ st.session_state.answered = False
635
+ st.session_state.question_start_time = time.time()
636
+ st.session_state.hints_used = 0
637
+ st.rerun()
638
+ else:
639
+ st.success(ui.get("challenge_quiz_completed", "Challenge Quiz Completed!"))
640
+ final_score = sum(1 for r in st.session_state.quiz_results if r['is_correct'])
641
+ st.session_state.challenge_manager.update_challenge_score(
642
+ st.session_state.current_challenge_id,
643
+ st.session_state.user_id,
644
+ final_score,
645
+ st.session_state.quiz_results
646
+ )
647
+ st.session_state.challenge_quiz_started = False
648
+ st.session_state.challenge_quiz_questions = []
649
+ st.session_state.challenge_current_question_index = 0
650
+ st.session_state.current_challenge_id = None
651
+ st.rerun()
652
+ else:
653
+ st.error(f"⏱️ {ui.get('times_up', 'Time\'s up!')}")
654
+ st.session_state.answered = True
655
+ st.session_state.quiz_results.append({
656
+ "question": current_mcq["question"],
657
+ "user_answer": ui.get("no_answer_timed_out", "No answer (Time's up)"),
658
+ "correct_answer": current_mcq["correct_answer"],
659
+ "is_correct": False,
660
+ "explanation": current_mcq["explanation"]
661
+ })
662
+ st.session_state.score_tracker.add_quiz_result("Challenge Mode", 0, 1, difficulty)
663
+ st.rerun()
664
+
665
+ elif mode == ui.get("page_info"):
666
+ st.header(ui.get("page_information", "Page Information"))
667
+ st.write(ui.get("get_page_info_prompt", "Get detailed information and images for any topic."))
668
+
669
+ info_topic = st.text_input(ui.get("enter_topic_for_info", "Enter Topic for Information"), value=st.session_state.topic)
670
+ num_images = st.slider(ui.get("number_of_images", "Number of Images"), 0, 5, 1)
671
+
672
+ if st.button(ui.get("get_information", "Get Information"), type="primary"):
673
+ with st.spinner(f"{ui.get('fetching_information_for', 'Fetching information for')} {info_topic}..."):
674
+ display_page_info(info_topic, num_images)
675
+
676
+ st.divider()
677
+ st.markdown(f"""
678
+ <div style='text-align: center'>
679
+ <p>{ui.get('made_with', 'Made with')} ❤️ {ui.get('using_streamlit_and_wikimedia', 'using Streamlit and Wikimedia APIs')}</p>
680
+ <p style='opacity: 0.7; font-size: 0.9em;'>{ui.get('app_name', 'TriviaVerse')} v2.0 - {ui.get('enhanced_edition', 'Enhanced Edition')}</p>
681
+ </div>
682
+ """, unsafe_allow_html=True)