RaghavenderReddy commited on
Commit
33a2aaf
·
verified ·
1 Parent(s): 6e21b07

Upload 13 files

Browse files
app.py ADDED
@@ -0,0 +1,520 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import sqlite3
3
+ import json
4
+ import random
5
+ from datetime import datetime
6
+ import os
7
+
8
+ # Import our custom modules
9
+ from components.mcq_generator import MCQGenerator
10
+ from utils.wiki_api import WikiAPI
11
+ from utils.translator import Translator
12
+
13
+ # Page configuration
14
+ st.set_page_config(
15
+ page_title="TriviaVerse - Multilingual Quiz Generator",
16
+ page_icon="🧠",
17
+ layout="wide",
18
+ initial_sidebar_state="expanded"
19
+ )
20
+
21
+ # Initialize session state
22
+ if 'current_quiz' not in st.session_state:
23
+ st.session_state.current_quiz = None
24
+ if 'score' not in st.session_state:
25
+ st.session_state.score = 0
26
+ if 'current_question' not in st.session_state:
27
+ st.session_state.current_question = 0
28
+ if 'user_answers' not in st.session_state:
29
+ st.session_state.user_answers = []
30
+
31
+ # Initialize components
32
+ @st.cache_resource
33
+ def initialize_components():
34
+ wiki_api = WikiAPI()
35
+ translator = Translator()
36
+ mcq_generator = MCQGenerator()
37
+ return wiki_api, translator, mcq_generator
38
+
39
+ def initialize_database():
40
+ """Initialize SQLite database for offline storage"""
41
+ conn = sqlite3.connect('triviaverse.db')
42
+ cursor = conn.cursor()
43
+
44
+ cursor.execute('''
45
+ CREATE TABLE IF NOT EXISTS cached_content (
46
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
47
+ topic TEXT NOT NULL,
48
+ language TEXT NOT NULL,
49
+ content TEXT NOT NULL,
50
+ summary TEXT,
51
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
52
+ )
53
+ ''')
54
+
55
+ cursor.execute('''
56
+ CREATE TABLE IF NOT EXISTS quiz_history (
57
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
58
+ topic TEXT NOT NULL,
59
+ language TEXT NOT NULL,
60
+ difficulty TEXT NOT NULL,
61
+ mode TEXT NOT NULL,
62
+ score INTEGER,
63
+ total_questions INTEGER,
64
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
65
+ )
66
+ ''')
67
+
68
+ conn.commit()
69
+ conn.close()
70
+
71
+ def main():
72
+ # Initialize database
73
+ initialize_database()
74
+
75
+ # Initialize components
76
+ wiki_api, translator, mcq_generator = initialize_components()
77
+
78
+ # Custom CSS
79
+ st.markdown("""
80
+ <style>
81
+ .main-header {
82
+ text-align: center;
83
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
84
+ -webkit-background-clip: text;
85
+ -webkit-text-fill-color: transparent;
86
+ background-clip: text;
87
+ font-size: 3rem;
88
+ font-weight: bold;
89
+ margin-bottom: 2rem;
90
+ }
91
+
92
+ .quiz-card {
93
+ background: white;
94
+ padding: 2rem;
95
+ border-radius: 15px;
96
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
97
+ margin: 1rem 0;
98
+ }
99
+
100
+ .score-display {
101
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
102
+ color: white;
103
+ padding: 1rem;
104
+ border-radius: 10px;
105
+ text-align: center;
106
+ font-size: 1.2rem;
107
+ font-weight: bold;
108
+ }
109
+
110
+ .question-card {
111
+ background: #f8f9fa;
112
+ padding: 1.5rem;
113
+ border-radius: 10px;
114
+ border-left: 4px solid #667eea;
115
+ margin: 1rem 0;
116
+ }
117
+
118
+ .sidebar-section {
119
+ background: #f0f2f6;
120
+ padding: 1rem;
121
+ border-radius: 10px;
122
+ margin: 1rem 0;
123
+ }
124
+ </style>
125
+ """, unsafe_allow_html=True)
126
+
127
+ # Main header
128
+ st.markdown('<h1 class="main-header">🧠 TriviaVerse</h1>', unsafe_allow_html=True)
129
+ st.markdown('<p style="text-align: center; font-size: 1.2rem; color: #666;">Multilingual AI-Powered Quiz Generator</p>', unsafe_allow_html=True)
130
+
131
+ # Sidebar configuration
132
+ with st.sidebar:
133
+ st.markdown('<div class="sidebar-section">', unsafe_allow_html=True)
134
+ st.header("🎯 Quiz Configuration")
135
+
136
+ # Language selection
137
+ languages = {
138
+ 'en': 'English',
139
+ 'hi': 'हिंदी (Hindi)',
140
+ 'te': 'తెలుగు (Telugu)',
141
+ 'ta': 'தமிழ் (Tamil)',
142
+ 'kn': 'ಕನ್ನಡ (Kannada)',
143
+ 'bn': 'বাংলা (Bengali)'
144
+ }
145
+
146
+ selected_language = st.selectbox(
147
+ "🌐 Select Language",
148
+ options=list(languages.keys()),
149
+ format_func=lambda x: languages[x],
150
+ index=0
151
+ )
152
+
153
+ # Topic input
154
+ topic = st.text_input("📚 Enter Topic", placeholder="e.g., Indian History, Science, Technology")
155
+
156
+ # Mode selection
157
+ mode = st.selectbox(
158
+ "🎮 Select Mode",
159
+ options=['mcq', 'flashcard', 'game'],
160
+ format_func=lambda x: {
161
+ 'mcq': '📝 Multiple Choice Questions',
162
+ 'flashcard': '🃏 Flashcards',
163
+ 'game': '🎲 Quiz Game'
164
+ }[x]
165
+ )
166
+
167
+ # Difficulty level
168
+ difficulty = st.selectbox(
169
+ "⚡ Difficulty Level",
170
+ options=['easy', 'medium', 'hard'],
171
+ format_func=lambda x: {
172
+ 'easy': '🟢 Easy',
173
+ 'medium': '🟡 Medium',
174
+ 'hard': '🔴 Hard'
175
+ }[x]
176
+ )
177
+
178
+ # Online/Offline toggle
179
+ is_online = st.toggle("🌐 Online Mode", value=True)
180
+
181
+ # Number of questions
182
+ num_questions = st.slider("📊 Number of Questions", min_value=5, max_value=20, value=10)
183
+
184
+ st.markdown('</div>', unsafe_allow_html=True)
185
+
186
+ # Generate quiz button
187
+ if st.button("🚀 Generate Quiz", type="primary", use_container_width=True):
188
+ if topic:
189
+ generate_quiz(wiki_api, translator, mcq_generator, topic, selected_language,
190
+ difficulty, mode, is_online, num_questions)
191
+ else:
192
+ st.error("Please enter a topic!")
193
+
194
+ # Quiz history
195
+ st.markdown('<div class="sidebar-section">', unsafe_allow_html=True)
196
+ st.subheader("📈 Recent Quizzes")
197
+ display_quiz_history()
198
+ st.markdown('</div>', unsafe_allow_html=True)
199
+
200
+ # Main content area
201
+ if st.session_state.current_quiz:
202
+ display_quiz()
203
+ else:
204
+ display_welcome_screen()
205
+
206
+ def generate_quiz(wiki_api, translator, mcq_generator, topic, language, difficulty, mode, is_online, num_questions):
207
+ """Generate a new quiz based on user parameters"""
208
+
209
+ with st.spinner(f"🔍 Fetching content about '{topic}'..."):
210
+ # Fetch content from Wikipedia
211
+ if is_online:
212
+ content = wiki_api.fetch_content(topic, language)
213
+ if not content:
214
+ st.error("❌ Could not fetch content. Try offline mode or a different topic.")
215
+ return
216
+ else:
217
+ content = load_cached_content(topic, language)
218
+ if not content:
219
+ st.error("❌ No cached content found. Please try online mode first.")
220
+ return
221
+
222
+ with st.spinner("🧠 Generating quiz questions..."):
223
+ # Generate questions
224
+ questions = mcq_generator.generate_questions(
225
+ content, difficulty, num_questions, language, translator
226
+ )
227
+
228
+ if not questions:
229
+ st.error("❌ Could not generate questions. Please try a different topic.")
230
+ return
231
+
232
+ # Store quiz in session state
233
+ st.session_state.current_quiz = {
234
+ 'topic': topic,
235
+ 'language': language,
236
+ 'difficulty': difficulty,
237
+ 'mode': mode,
238
+ 'questions': questions,
239
+ 'created_at': datetime.now()
240
+ }
241
+
242
+ # Reset quiz state
243
+ st.session_state.current_question = 0
244
+ st.session_state.score = 0
245
+ st.session_state.user_answers = []
246
+
247
+ # Cache content for offline use
248
+ if is_online:
249
+ cache_content(topic, language, content)
250
+
251
+ st.success(f"✅ Generated {len(questions)} questions successfully!")
252
+ st.rerun()
253
+
254
+ def display_quiz():
255
+ """Display the current quiz"""
256
+ quiz = st.session_state.current_quiz
257
+ questions = quiz['questions']
258
+ current_q = st.session_state.current_question
259
+
260
+ # Progress bar
261
+ progress = (current_q + 1) / len(questions)
262
+ st.progress(progress, text=f"Question {current_q + 1} of {len(questions)}")
263
+
264
+ # Score display
265
+ if current_q > 0:
266
+ st.markdown(f'<div class="score-display">Score: {st.session_state.score}/{current_q}</div>',
267
+ unsafe_allow_html=True)
268
+
269
+ if current_q < len(questions):
270
+ question = questions[current_q]
271
+
272
+ # Display question
273
+ st.markdown(f'<div class="question-card">', unsafe_allow_html=True)
274
+ st.markdown(f"### Question {current_q + 1}")
275
+ st.markdown(f"**{question['question']}**")
276
+ st.markdown('</div>', unsafe_allow_html=True)
277
+
278
+ # Display options
279
+ if quiz['mode'] == 'mcq':
280
+ display_mcq_options(question, current_q)
281
+ elif quiz['mode'] == 'flashcard':
282
+ display_flashcard(question, current_q)
283
+ else: # game mode
284
+ display_game_mode(question, current_q)
285
+
286
+ else:
287
+ # Quiz completed
288
+ display_quiz_results()
289
+
290
+ def display_mcq_options(question, current_q):
291
+ """Display multiple choice options"""
292
+ options = question['options']
293
+
294
+ # Use unique key for each question
295
+ selected_option = st.radio(
296
+ "Select your answer:",
297
+ options=options,
298
+ key=f"q_{current_q}",
299
+ index=None
300
+ )
301
+
302
+ col1, col2 = st.columns([1, 1])
303
+
304
+ with col1:
305
+ if st.button("Submit Answer", key=f"submit_{current_q}"):
306
+ if selected_option is not None:
307
+ answer_index = options.index(selected_option)
308
+ is_correct = answer_index == question['correct_answer']
309
+
310
+ if is_correct:
311
+ st.success("✅ Correct!")
312
+ st.session_state.score += 1
313
+ else:
314
+ st.error(f"❌ Incorrect! The correct answer is: {options[question['correct_answer']]}")
315
+
316
+ # Show explanation
317
+ if question.get('explanation'):
318
+ st.info(f"💡 Explanation: {question['explanation']}")
319
+
320
+ st.session_state.user_answers.append({
321
+ 'question': question['question'],
322
+ 'user_answer': selected_option,
323
+ 'correct_answer': options[question['correct_answer']],
324
+ 'is_correct': is_correct
325
+ })
326
+
327
+ st.session_state.current_question += 1
328
+
329
+ # Auto-advance after 3 seconds
330
+ st.rerun()
331
+ else:
332
+ st.warning("Please select an answer!")
333
+
334
+ with col2:
335
+ if st.button("Skip Question", key=f"skip_{current_q}"):
336
+ st.session_state.user_answers.append({
337
+ 'question': question['question'],
338
+ 'user_answer': "Skipped",
339
+ 'correct_answer': options[question['correct_answer']],
340
+ 'is_correct': False
341
+ })
342
+ st.session_state.current_question += 1
343
+ st.rerun()
344
+
345
+ def display_flashcard(question, current_q):
346
+ """Display flashcard mode"""
347
+ if f"show_answer_{current_q}" not in st.session_state:
348
+ st.session_state[f"show_answer_{current_q}"] = False
349
+
350
+ if not st.session_state[f"show_answer_{current_q}"]:
351
+ if st.button("🔍 Show Answer", key=f"show_{current_q}"):
352
+ st.session_state[f"show_answer_{current_q}"] = True
353
+ st.rerun()
354
+ else:
355
+ st.success(f"**Answer:** {question['options'][question['correct_answer']]}")
356
+
357
+ if question.get('explanation'):
358
+ st.info(f"💡 Explanation: {question['explanation']}")
359
+
360
+ col1, col2 = st.columns(2)
361
+ with col1:
362
+ if st.button("✅ I knew it!", key=f"correct_{current_q}"):
363
+ st.session_state.score += 1
364
+ st.session_state.current_question += 1
365
+ st.rerun()
366
+
367
+ with col2:
368
+ if st.button("❌ I didn't know", key=f"incorrect_{current_q}"):
369
+ st.session_state.current_question += 1
370
+ st.rerun()
371
+
372
+ def display_game_mode(question, current_q):
373
+ """Display game mode with timer"""
374
+ # Simple game mode - similar to MCQ but with time pressure
375
+ st.markdown("⏰ **Quick Answer Mode!**")
376
+ display_mcq_options(question, current_q)
377
+
378
+ def display_quiz_results():
379
+ """Display final quiz results"""
380
+ quiz = st.session_state.current_quiz
381
+ total_questions = len(quiz['questions'])
382
+ score = st.session_state.score
383
+ percentage = (score / total_questions) * 100
384
+
385
+ st.balloons()
386
+
387
+ # Results header
388
+ st.markdown('<div class="score-display">', unsafe_allow_html=True)
389
+ st.markdown(f"## 🎉 Quiz Completed!")
390
+ st.markdown(f"### Final Score: {score}/{total_questions} ({percentage:.1f}%)")
391
+ st.markdown('</div>', unsafe_allow_html=True)
392
+
393
+ # Performance message
394
+ if percentage >= 80:
395
+ st.success("🌟 Excellent! You're a trivia master!")
396
+ elif percentage >= 60:
397
+ st.info("👍 Good job! Keep learning!")
398
+ else:
399
+ st.warning("📚 Keep studying! You'll do better next time!")
400
+
401
+ # Save to history
402
+ save_quiz_history(quiz, score, total_questions)
403
+
404
+ # Detailed results
405
+ st.subheader("📊 Detailed Results")
406
+ for i, answer in enumerate(st.session_state.user_answers):
407
+ with st.expander(f"Question {i+1}: {answer['question'][:50]}..."):
408
+ st.write(f"**Your Answer:** {answer['user_answer']}")
409
+ st.write(f"**Correct Answer:** {answer['correct_answer']}")
410
+ if answer['is_correct']:
411
+ st.success("✅ Correct")
412
+ else:
413
+ st.error("❌ Incorrect")
414
+
415
+ # Reset button
416
+ if st.button("🔄 Take Another Quiz", type="primary"):
417
+ st.session_state.current_quiz = None
418
+ st.session_state.current_question = 0
419
+ st.session_state.score = 0
420
+ st.session_state.user_answers = []
421
+ st.rerun()
422
+
423
+ def display_welcome_screen():
424
+ """Display welcome screen when no quiz is active"""
425
+ col1, col2, col3 = st.columns([1, 2, 1])
426
+
427
+ with col2:
428
+ st.markdown("""
429
+ <div style="text-align: center; padding: 2rem;">
430
+ <h2>🎯 Welcome to TriviaVerse!</h2>
431
+ <p style="font-size: 1.1rem; color: #666;">
432
+ Generate intelligent quizzes on any topic in multiple Indian languages
433
+ </p>
434
+
435
+ <div style="margin: 2rem 0;">
436
+ <h3>✨ Features</h3>
437
+ <ul style="text-align: left; display: inline-block;">
438
+ <li>🌐 Multi-language support (Hindi, Telugu, Tamil, Kannada, Bengali)</li>
439
+ <li>📚 Wikipedia & Wikidata integration</li>
440
+ <li>🧠 AI-powered question generation</li>
441
+ <li>🎮 Multiple quiz modes (MCQ, Flashcards, Games)</li>
442
+ <li>📊 Difficulty levels and progress tracking</li>
443
+ <li>💾 Offline mode with caching</li>
444
+ </ul>
445
+ </div>
446
+
447
+ <p style="color: #667eea; font-weight: bold;">
448
+ 👈 Configure your quiz in the sidebar and click "Generate Quiz" to start!
449
+ </p>
450
+ </div>
451
+ """, unsafe_allow_html=True)
452
+
453
+ def load_cached_content(topic, language):
454
+ """Load content from local cache"""
455
+ conn = sqlite3.connect('triviaverse.db')
456
+ cursor = conn.cursor()
457
+
458
+ cursor.execute("""
459
+ SELECT content FROM cached_content
460
+ WHERE topic = ? AND language = ?
461
+ ORDER BY created_at DESC LIMIT 1
462
+ """, (topic, language))
463
+
464
+ result = cursor.fetchone()
465
+ conn.close()
466
+
467
+ return result[0] if result else None
468
+
469
+ def cache_content(topic, language, content):
470
+ """Cache content for offline use"""
471
+ conn = sqlite3.connect('triviaverse.db')
472
+ cursor = conn.cursor()
473
+
474
+ cursor.execute("""
475
+ INSERT INTO cached_content (topic, language, content)
476
+ VALUES (?, ?, ?)
477
+ """, (topic, language, content))
478
+
479
+ conn.commit()
480
+ conn.close()
481
+
482
+ def save_quiz_history(quiz, score, total_questions):
483
+ """Save quiz results to history"""
484
+ conn = sqlite3.connect('triviaverse.db')
485
+ cursor = conn.cursor()
486
+
487
+ cursor.execute("""
488
+ INSERT INTO quiz_history (topic, language, difficulty, mode, score, total_questions)
489
+ VALUES (?, ?, ?, ?, ?, ?)
490
+ """, (quiz['topic'], quiz['language'], quiz['difficulty'],
491
+ quiz['mode'], score, total_questions))
492
+
493
+ conn.commit()
494
+ conn.close()
495
+
496
+ def display_quiz_history():
497
+ """Display recent quiz history"""
498
+ conn = sqlite3.connect('triviaverse.db')
499
+ cursor = conn.cursor()
500
+
501
+ cursor.execute("""
502
+ SELECT topic, score, total_questions, created_at
503
+ FROM quiz_history
504
+ ORDER BY created_at DESC
505
+ LIMIT 5
506
+ """)
507
+
508
+ results = cursor.fetchall()
509
+ conn.close()
510
+
511
+ if results:
512
+ for result in results:
513
+ topic, score, total, created_at = result
514
+ percentage = (score / total) * 100
515
+ st.write(f"📚 {topic}: {score}/{total} ({percentage:.0f}%)")
516
+ else:
517
+ st.write("No quiz history yet!")
518
+
519
+ if __name__ == "__main__":
520
+ main()
assets/.gitkeep ADDED
@@ -0,0 +1 @@
 
 
1
+ # Assets directory for images, icons, etc.
components/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Components package
components/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (174 Bytes). View file
 
components/__pycache__/mcq_generator.cpython-312.pyc ADDED
Binary file (10.8 kB). View file
 
components/mcq_generator.py ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import re
3
+ from typing import List, Dict, Any
4
+
5
+ class MCQGenerator:
6
+ """Generate Multiple Choice Questions from content"""
7
+
8
+ def __init__(self):
9
+ self.question_templates = {
10
+ 'easy': [
11
+ "What is {entity}?",
12
+ "When did {event} happen?",
13
+ "Where is {place} located?",
14
+ "Who was {person}?"
15
+ ],
16
+ 'medium': [
17
+ "What was the significance of {event}?",
18
+ "How did {process} work?",
19
+ "What caused {event}?",
20
+ "What were the effects of {action}?"
21
+ ],
22
+ 'hard': [
23
+ "Analyze the relationship between {concept1} and {concept2}.",
24
+ "What would have happened if {event} had not occurred?",
25
+ "Compare and contrast {item1} and {item2}.",
26
+ "Evaluate the impact of {factor} on {outcome}."
27
+ ]
28
+ }
29
+
30
+ def generate_questions(self, content: str, difficulty: str, num_questions: int,
31
+ language: str, translator) -> List[Dict[str, Any]]:
32
+ """Generate MCQ questions from content"""
33
+
34
+ if not content or len(content.strip()) < 100:
35
+ return self._generate_fallback_questions(difficulty, num_questions, language, translator)
36
+
37
+ # Extract key information from content
38
+ sentences = self._extract_sentences(content)
39
+ key_facts = self._extract_key_facts(sentences)
40
+
41
+ questions = []
42
+
43
+ for i in range(num_questions):
44
+ if i < len(key_facts):
45
+ question = self._create_question_from_fact(key_facts[i], difficulty, language, translator)
46
+ else:
47
+ # Generate additional questions using templates
48
+ question = self._generate_template_question(content, difficulty, language, translator)
49
+
50
+ if question:
51
+ questions.append(question)
52
+
53
+ return questions[:num_questions]
54
+
55
+ def _extract_sentences(self, content: str) -> List[str]:
56
+ """Extract sentences from content"""
57
+ # Simple sentence splitting
58
+ sentences = re.split(r'[.!?]+', content)
59
+ sentences = [s.strip() for s in sentences if len(s.strip()) > 20]
60
+ return sentences[:50] # Limit to first 50 sentences
61
+
62
+ def _extract_key_facts(self, sentences: List[str]) -> List[Dict[str, str]]:
63
+ """Extract key facts from sentences"""
64
+ facts = []
65
+
66
+ for sentence in sentences:
67
+ # Look for sentences with dates, numbers, or proper nouns
68
+ if self._contains_factual_info(sentence):
69
+ fact = {
70
+ 'sentence': sentence,
71
+ 'type': self._classify_fact_type(sentence)
72
+ }
73
+ facts.append(fact)
74
+
75
+ return facts
76
+
77
+ def _contains_factual_info(self, sentence: str) -> bool:
78
+ """Check if sentence contains factual information"""
79
+ # Look for dates, numbers, proper nouns, etc.
80
+ patterns = [
81
+ r'\b\d{4}\b', # Years
82
+ r'\b\d+\b', # Numbers
83
+ r'\b[A-Z][a-z]+\s+[A-Z][a-z]+\b', # Proper nouns
84
+ r'\bin\s+\d{4}\b', # "in 1947"
85
+ r'\bwas\s+born\b', # Birth information
86
+ r'\bwas\s+founded\b', # Foundation information
87
+ ]
88
+
89
+ return any(re.search(pattern, sentence) for pattern in patterns)
90
+
91
+ def _classify_fact_type(self, sentence: str) -> str:
92
+ """Classify the type of fact"""
93
+ if re.search(r'\b\d{4}\b', sentence):
94
+ return 'date'
95
+ elif re.search(r'\b\d+\b', sentence):
96
+ return 'number'
97
+ elif re.search(r'\b[A-Z][a-z]+\s+[A-Z][a-z]+\b', sentence):
98
+ return 'person_place'
99
+ else:
100
+ return 'general'
101
+
102
+ def _create_question_from_fact(self, fact: Dict[str, str], difficulty: str,
103
+ language: str, translator) -> Dict[str, Any]:
104
+ """Create a question from a fact"""
105
+ sentence = fact['sentence']
106
+ fact_type = fact['type']
107
+
108
+ # Extract the main subject and predicate
109
+ question_text, correct_answer = self._generate_question_and_answer(sentence, fact_type, difficulty)
110
+
111
+ if not question_text or not correct_answer:
112
+ return None
113
+
114
+ # Generate distractors (wrong options)
115
+ options = self._generate_options(correct_answer, fact_type)
116
+
117
+ # Translate if needed
118
+ if language != 'en':
119
+ question_text = translator.translate_text(question_text, language)
120
+ options = [translator.translate_text(opt, language) for opt in options]
121
+
122
+ return {
123
+ 'question': question_text,
124
+ 'options': options,
125
+ 'correct_answer': 0, # Correct answer is always first, will be shuffled
126
+ 'explanation': f"Based on the text: {sentence[:100]}...",
127
+ 'difficulty': difficulty,
128
+ 'type': fact_type
129
+ }
130
+
131
+ def _generate_question_and_answer(self, sentence: str, fact_type: str, difficulty: str):
132
+ """Generate question and answer from sentence"""
133
+
134
+ if fact_type == 'date':
135
+ # Extract year/date
136
+ date_match = re.search(r'\b(\d{4})\b', sentence)
137
+ if date_match:
138
+ year = date_match.group(1)
139
+ question = f"In which year did the event mentioned in this context occur?"
140
+ return question, year
141
+
142
+ elif fact_type == 'number':
143
+ # Extract number
144
+ num_match = re.search(r'\b(\d+)\b', sentence)
145
+ if num_match:
146
+ number = num_match.group(1)
147
+ question = f"What is the number mentioned in this context?"
148
+ return question, number
149
+
150
+ elif fact_type == 'person_place':
151
+ # Extract proper noun
152
+ name_match = re.search(r'\b([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)\b', sentence)
153
+ if name_match:
154
+ name = name_match.group(1)
155
+ question = f"Who or what is mentioned prominently in this context?"
156
+ return question, name
157
+
158
+ # General question
159
+ words = sentence.split()
160
+ if len(words) > 5:
161
+ # Create a fill-in-the-blank question
162
+ blank_word = random.choice(words[2:-2]) # Avoid first and last words
163
+ question = sentence.replace(blank_word, "______", 1)
164
+ return f"Fill in the blank: {question}", blank_word
165
+
166
+ return None, None
167
+
168
+ def _generate_options(self, correct_answer: str, fact_type: str) -> List[str]:
169
+ """Generate multiple choice options"""
170
+ options = [correct_answer]
171
+
172
+ if fact_type == 'date':
173
+ # Generate nearby years
174
+ try:
175
+ year = int(correct_answer)
176
+ distractors = [
177
+ str(year - 1),
178
+ str(year + 1),
179
+ str(year - 5)
180
+ ]
181
+ except ValueError:
182
+ distractors = ["1947", "1950", "1960"]
183
+
184
+ elif fact_type == 'number':
185
+ # Generate nearby numbers
186
+ try:
187
+ num = int(correct_answer)
188
+ distractors = [
189
+ str(num + 1),
190
+ str(num - 1),
191
+ str(num * 2)
192
+ ]
193
+ except ValueError:
194
+ distractors = ["10", "20", "100"]
195
+
196
+ else:
197
+ # Generate generic distractors
198
+ distractors = [
199
+ "Option B",
200
+ "Option C",
201
+ "Option D"
202
+ ]
203
+
204
+ options.extend(distractors[:3]) # Add up to 3 distractors
205
+
206
+ # Shuffle options and remember correct position
207
+ correct_index = 0
208
+ random.shuffle(options)
209
+ correct_index = options.index(correct_answer)
210
+
211
+ return options
212
+
213
+ def _generate_template_question(self, content: str, difficulty: str,
214
+ language: str, translator) -> Dict[str, Any]:
215
+ """Generate question using templates"""
216
+
217
+ # Extract a random sentence for context
218
+ sentences = self._extract_sentences(content)
219
+ if not sentences:
220
+ return None
221
+
222
+ sentence = random.choice(sentences)
223
+
224
+ # Simple template-based question
225
+ question = f"Based on the content, which of the following is true?"
226
+
227
+ # Create options
228
+ options = [
229
+ "Statement related to the main topic",
230
+ "Unrelated statement A",
231
+ "Unrelated statement B",
232
+ "Unrelated statement C"
233
+ ]
234
+
235
+ if language != 'en':
236
+ question = translator.translate_text(question, language)
237
+ options = [translator.translate_text(opt, language) for opt in options]
238
+
239
+ return {
240
+ 'question': question,
241
+ 'options': options,
242
+ 'correct_answer': 0,
243
+ 'explanation': f"Based on the content about the topic.",
244
+ 'difficulty': difficulty,
245
+ 'type': 'general'
246
+ }
247
+
248
+ def _generate_fallback_questions(self, difficulty: str, num_questions: int,
249
+ language: str, translator) -> List[Dict[str, Any]]:
250
+ """Generate fallback questions when content is insufficient"""
251
+
252
+ fallback_questions = {
253
+ 'easy': [
254
+ {
255
+ 'question': 'What is the capital of India?',
256
+ 'options': ['New Delhi', 'Mumbai', 'Kolkata', 'Chennai'],
257
+ 'correct_answer': 0,
258
+ 'explanation': 'New Delhi is the capital of India.'
259
+ },
260
+ {
261
+ 'question': 'Which river is considered sacred in Hinduism?',
262
+ 'options': ['Ganges', 'Yamuna', 'Narmada', 'Godavari'],
263
+ 'correct_answer': 0,
264
+ 'explanation': 'The Ganges river is considered most sacred in Hinduism.'
265
+ }
266
+ ],
267
+ 'medium': [
268
+ {
269
+ 'question': 'In which year did India gain independence?',
270
+ 'options': ['1947', '1946', '1948', '1950'],
271
+ 'correct_answer': 0,
272
+ 'explanation': 'India gained independence on August 15, 1947.'
273
+ },
274
+ {
275
+ 'question': 'Who was the first Prime Minister of India?',
276
+ 'options': ['Jawaharlal Nehru', 'Mahatma Gandhi', 'Sardar Patel', 'Subhas Chandra Bose'],
277
+ 'correct_answer': 0,
278
+ 'explanation': 'Jawaharlal Nehru was the first Prime Minister of India.'
279
+ }
280
+ ],
281
+ 'hard': [
282
+ {
283
+ 'question': 'Which constitutional amendment is known as the "Mini Constitution"?',
284
+ 'options': ['42nd Amendment', '44th Amendment', '73rd Amendment', '74th Amendment'],
285
+ 'correct_answer': 0,
286
+ 'explanation': 'The 42nd Amendment is called the "Mini Constitution" due to its extensive changes.'
287
+ }
288
+ ]
289
+ }
290
+
291
+ questions = fallback_questions.get(difficulty, fallback_questions['easy'])
292
+ selected_questions = random.sample(questions, min(num_questions, len(questions)))
293
+
294
+ # Translate if needed
295
+ if language != 'en':
296
+ for q in selected_questions:
297
+ q['question'] = translator.translate_text(q['question'], language)
298
+ q['options'] = [translator.translate_text(opt, language) for opt in q['options']]
299
+ q['explanation'] = translator.translate_text(q['explanation'], language)
300
+
301
+ return selected_questions
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ streamlit>=1.28.0
2
+ requests>=2.31.0
3
+ googletrans==4.0.0rc1
4
+ sqlite3
5
+ transformers>=4.21.0
6
+ torch>=1.12.0
7
+ pandas>=1.5.0
8
+ numpy>=1.21.0
utils/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Utils package
utils/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (169 Bytes). View file
 
utils/__pycache__/translator.cpython-312.pyc ADDED
Binary file (7 kB). View file
 
utils/__pycache__/wiki_api.cpython-312.pyc ADDED
Binary file (9.05 kB). View file
 
utils/translator.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from googletrans import Translator as GoogleTranslator
2
+ import json
3
+ from typing import Optional
4
+
5
+ class Translator:
6
+ """Handle text translation for multiple languages"""
7
+
8
+ def __init__(self):
9
+ self.translator = GoogleTranslator()
10
+ self.supported_languages = {
11
+ 'en': 'English',
12
+ 'hi': 'Hindi',
13
+ 'te': 'Telugu',
14
+ 'ta': 'Tamil',
15
+ 'kn': 'Kannada',
16
+ 'bn': 'Bengali'
17
+ }
18
+
19
+ # Fallback translations for common terms
20
+ self.fallback_translations = {
21
+ 'hi': {
22
+ 'Question': 'प्रश्न',
23
+ 'Answer': 'उत्तर',
24
+ 'Score': 'स्कोर',
25
+ 'Correct': 'सही',
26
+ 'Incorrect': 'गलत',
27
+ 'Next': 'अगला',
28
+ 'Previous': 'पिछला',
29
+ 'Submit': 'जमा करें',
30
+ 'Skip': 'छोड़ें',
31
+ 'Easy': 'आसान',
32
+ 'Medium': 'मध्यम',
33
+ 'Hard': 'कठिन'
34
+ },
35
+ 'te': {
36
+ 'Question': 'ప్రశ్న',
37
+ 'Answer': 'సమాధానం',
38
+ 'Score': 'స్కోర్',
39
+ 'Correct': 'సరైనది',
40
+ 'Incorrect': 'తప్పు',
41
+ 'Next': 'తదుపరి',
42
+ 'Previous': 'మునుపటి',
43
+ 'Submit': 'సమర్పించు',
44
+ 'Skip': 'దాటవేయి',
45
+ 'Easy': 'సులువు',
46
+ 'Medium': 'మధ్యమ',
47
+ 'Hard': 'కష్టం'
48
+ },
49
+ 'ta': {
50
+ 'Question': 'கேள்வி',
51
+ 'Answer': 'பதில்',
52
+ 'Score': 'மதிப்பெண்',
53
+ 'Correct': 'சரி',
54
+ 'Incorrect': 'தவறு',
55
+ 'Next': 'அடுத்து',
56
+ 'Previous': 'முந்தைய',
57
+ 'Submit': 'சமர்ப்பிக்கவும்',
58
+ 'Skip': 'தவிர்க்கவும்',
59
+ 'Easy': 'எளிதான',
60
+ 'Medium': 'நடுத்தர',
61
+ 'Hard': 'கடினமான'
62
+ },
63
+ 'kn': {
64
+ 'Question': 'ಪ್ರಶ್ನೆ',
65
+ 'Answer': 'ಉತ್ತರ',
66
+ 'Score': 'ಅಂಕ',
67
+ 'Correct': 'ಸರಿ',
68
+ 'Incorrect': 'ತಪ್ಪು',
69
+ 'Next': 'ಮುಂದಿನ',
70
+ 'Previous': 'ಹಿಂದಿನ',
71
+ 'Submit': 'ಸಲ್ಲಿಸಿ',
72
+ 'Skip': 'ಬಿಟ್ಟುಬಿಡಿ',
73
+ 'Easy': 'ಸುಲಭ',
74
+ 'Medium': 'ಮಧ್ಯಮ',
75
+ 'Hard': 'ಕಠಿಣ'
76
+ },
77
+ 'bn': {
78
+ 'Question': 'প্রশ্ন',
79
+ 'Answer': 'উত্তর',
80
+ 'Score': 'স্কোর',
81
+ 'Correct': 'সঠিক',
82
+ 'Incorrect': 'ভুল',
83
+ 'Next': 'পরবর্তী',
84
+ 'Previous': 'পূর্ববর্তী',
85
+ 'Submit': 'জমা দিন',
86
+ 'Skip': 'এড়িয়ে যান',
87
+ 'Easy': 'সহজ',
88
+ 'Medium': 'মাধ্যম',
89
+ 'Hard': 'কঠিন'
90
+ }
91
+ }
92
+
93
+ def translate_text(self, text: str, target_language: str) -> str:
94
+ """Translate text to target language"""
95
+
96
+ if not text or target_language == 'en':
97
+ return text
98
+
99
+ try:
100
+ # Try Google Translate first
101
+ result = self.translator.translate(text, dest=target_language)
102
+ return result.text
103
+
104
+ except Exception as e:
105
+ print(f"Translation error: {e}")
106
+ # Try fallback translation
107
+ return self._fallback_translate(text, target_language)
108
+
109
+ def _fallback_translate(self, text: str, target_language: str) -> str:
110
+ """Fallback translation using predefined dictionary"""
111
+
112
+ if target_language not in self.fallback_translations:
113
+ return text
114
+
115
+ translations = self.fallback_translations[target_language]
116
+
117
+ # Check if the text is a common term
118
+ if text in translations:
119
+ return translations[text]
120
+
121
+ # Try to translate word by word for simple phrases
122
+ words = text.split()
123
+ translated_words = []
124
+
125
+ for word in words:
126
+ if word in translations:
127
+ translated_words.append(translations[word])
128
+ else:
129
+ translated_words.append(word)
130
+
131
+ return ' '.join(translated_words)
132
+
133
+ def translate_quiz_content(self, quiz_data: dict, target_language: str) -> dict:
134
+ """Translate entire quiz content"""
135
+
136
+ if target_language == 'en':
137
+ return quiz_data
138
+
139
+ translated_quiz = quiz_data.copy()
140
+
141
+ # Translate quiz title if present
142
+ if 'title' in translated_quiz:
143
+ translated_quiz['title'] = self.translate_text(translated_quiz['title'], target_language)
144
+
145
+ # Translate questions
146
+ if 'questions' in translated_quiz:
147
+ for question in translated_quiz['questions']:
148
+ question['question'] = self.translate_text(question['question'], target_language)
149
+
150
+ if 'options' in question:
151
+ question['options'] = [
152
+ self.translate_text(option, target_language)
153
+ for option in question['options']
154
+ ]
155
+
156
+ if 'explanation' in question:
157
+ question['explanation'] = self.translate_text(question['explanation'], target_language)
158
+
159
+ return translated_quiz
160
+
161
+ def get_ui_text(self, key: str, language: str) -> str:
162
+ """Get UI text in specified language"""
163
+
164
+ if language == 'en':
165
+ return key
166
+
167
+ if language in self.fallback_translations:
168
+ return self.fallback_translations[language].get(key, key)
169
+
170
+ return key
171
+
172
+ def batch_translate(self, texts: list, target_language: str) -> list:
173
+ """Translate multiple texts at once"""
174
+
175
+ if target_language == 'en':
176
+ return texts
177
+
178
+ translated_texts = []
179
+
180
+ for text in texts:
181
+ translated_text = self.translate_text(text, target_language)
182
+ translated_texts.append(translated_text)
183
+
184
+ return translated_texts
185
+
186
+ def detect_language(self, text: str) -> Optional[str]:
187
+ """Detect the language of given text"""
188
+
189
+ try:
190
+ result = self.translator.detect(text)
191
+ return result.lang
192
+ except Exception as e:
193
+ print(f"Language detection error: {e}")
194
+ return 'en' # Default to English
195
+
196
+ def is_supported_language(self, language_code: str) -> bool:
197
+ """Check if language is supported"""
198
+ return language_code in self.supported_languages
199
+
200
+ def get_language_name(self, language_code: str) -> str:
201
+ """Get full language name from code"""
202
+ return self.supported_languages.get(language_code, 'Unknown')
utils/wiki_api.py ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+ from typing import Optional, Dict, Any
4
+ import time
5
+
6
+ class WikiAPI:
7
+ """Wikipedia and Wikidata API client"""
8
+
9
+ def __init__(self):
10
+ self.wikipedia_base_url = "https://en.wikipedia.org/api/rest_v1"
11
+ self.wikidata_base_url = "https://www.wikidata.org/w/api.php"
12
+ self.session = requests.Session()
13
+ self.session.headers.update({
14
+ 'User-Agent': 'TriviaVerse/1.0 (https://github.com/your-repo/triviaverse)'
15
+ })
16
+
17
+ def fetch_content(self, topic: str, language: str = 'en') -> Optional[str]:
18
+ """Fetch content from Wikipedia"""
19
+ try:
20
+ # First, search for the article
21
+ search_results = self._search_wikipedia(topic, language)
22
+ if not search_results:
23
+ return None
24
+
25
+ # Get the first result
26
+ page_title = search_results[0]['title']
27
+
28
+ # Fetch the full content
29
+ content = self._get_wikipedia_content(page_title, language)
30
+ return content
31
+
32
+ except Exception as e:
33
+ print(f"Error fetching content: {e}")
34
+ return None
35
+
36
+ def _search_wikipedia(self, query: str, language: str = 'en') -> Optional[list]:
37
+ """Search Wikipedia for articles"""
38
+
39
+ # Map language codes to Wikipedia domains
40
+ lang_domains = {
41
+ 'en': 'en.wikipedia.org',
42
+ 'hi': 'hi.wikipedia.org',
43
+ 'te': 'te.wikipedia.org',
44
+ 'ta': 'ta.wikipedia.org',
45
+ 'kn': 'kn.wikipedia.org',
46
+ 'bn': 'bn.wikipedia.org'
47
+ }
48
+
49
+ domain = lang_domains.get(language, 'en.wikipedia.org')
50
+
51
+ url = f"https://{domain}/api/rest_v1/page/search/{query}"
52
+
53
+ try:
54
+ response = self.session.get(url, timeout=10)
55
+ response.raise_for_status()
56
+
57
+ data = response.json()
58
+ return data.get('pages', [])
59
+
60
+ except Exception as e:
61
+ print(f"Search error: {e}")
62
+ return None
63
+
64
+ def _get_wikipedia_content(self, page_title: str, language: str = 'en') -> Optional[str]:
65
+ """Get full content of a Wikipedia page"""
66
+
67
+ # Map language codes to Wikipedia domains
68
+ lang_domains = {
69
+ 'en': 'en.wikipedia.org',
70
+ 'hi': 'hi.wikipedia.org',
71
+ 'te': 'te.wikipedia.org',
72
+ 'ta': 'ta.wikipedia.org',
73
+ 'kn': 'kn.wikipedia.org',
74
+ 'bn': 'bn.wikipedia.org'
75
+ }
76
+
77
+ domain = lang_domains.get(language, 'en.wikipedia.org')
78
+
79
+ # Get page summary first
80
+ summary_url = f"https://{domain}/api/rest_v1/page/summary/{page_title}"
81
+
82
+ try:
83
+ response = self.session.get(summary_url, timeout=10)
84
+ response.raise_for_status()
85
+
86
+ summary_data = response.json()
87
+ extract = summary_data.get('extract', '')
88
+
89
+ # Try to get more detailed content
90
+ content_url = f"https://{domain}/w/api.php"
91
+ params = {
92
+ 'action': 'query',
93
+ 'format': 'json',
94
+ 'titles': page_title,
95
+ 'prop': 'extracts',
96
+ 'exintro': True,
97
+ 'explaintext': True,
98
+ 'exsectionformat': 'plain'
99
+ }
100
+
101
+ response = self.session.get(content_url, params=params, timeout=10)
102
+ response.raise_for_status()
103
+
104
+ data = response.json()
105
+ pages = data.get('query', {}).get('pages', {})
106
+
107
+ for page_id, page_data in pages.items():
108
+ if 'extract' in page_data:
109
+ full_extract = page_data['extract']
110
+ # Return the longer content
111
+ return full_extract if len(full_extract) > len(extract) else extract
112
+
113
+ return extract
114
+
115
+ except Exception as e:
116
+ print(f"Content fetch error: {e}")
117
+ return None
118
+
119
+ def get_wikidata_info(self, topic: str) -> Optional[Dict[str, Any]]:
120
+ """Get structured data from Wikidata"""
121
+ try:
122
+ # Search for Wikidata entity
123
+ search_url = f"{self.wikidata_base_url}"
124
+ params = {
125
+ 'action': 'wbsearchentities',
126
+ 'search': topic,
127
+ 'language': 'en',
128
+ 'format': 'json'
129
+ }
130
+
131
+ response = self.session.get(search_url, params=params, timeout=10)
132
+ response.raise_for_status()
133
+
134
+ data = response.json()
135
+ entities = data.get('search', [])
136
+
137
+ if not entities:
138
+ return None
139
+
140
+ entity_id = entities[0]['id']
141
+
142
+ # Get entity data
143
+ entity_url = f"{self.wikidata_base_url}"
144
+ params = {
145
+ 'action': 'wbgetentities',
146
+ 'ids': entity_id,
147
+ 'format': 'json',
148
+ 'languages': 'en'
149
+ }
150
+
151
+ response = self.session.get(entity_url, params=params, timeout=10)
152
+ response.raise_for_status()
153
+
154
+ data = response.json()
155
+ entity_data = data.get('entities', {}).get(entity_id, {})
156
+
157
+ return self._process_wikidata_entity(entity_data)
158
+
159
+ except Exception as e:
160
+ print(f"Wikidata error: {e}")
161
+ return None
162
+
163
+ def _process_wikidata_entity(self, entity_data: Dict[str, Any]) -> Dict[str, Any]:
164
+ """Process Wikidata entity to extract useful information"""
165
+
166
+ processed_data = {
167
+ 'label': '',
168
+ 'description': '',
169
+ 'claims': {},
170
+ 'aliases': []
171
+ }
172
+
173
+ # Extract label
174
+ labels = entity_data.get('labels', {})
175
+ if 'en' in labels:
176
+ processed_data['label'] = labels['en']['value']
177
+
178
+ # Extract description
179
+ descriptions = entity_data.get('descriptions', {})
180
+ if 'en' in descriptions:
181
+ processed_data['description'] = descriptions['en']['value']
182
+
183
+ # Extract aliases
184
+ aliases = entity_data.get('aliases', {})
185
+ if 'en' in aliases:
186
+ processed_data['aliases'] = [alias['value'] for alias in aliases['en']]
187
+
188
+ # Extract some important claims
189
+ claims = entity_data.get('claims', {})
190
+ important_properties = [
191
+ 'P31', # instance of
192
+ 'P279', # subclass of
193
+ 'P17', # country
194
+ 'P569', # date of birth
195
+ 'P570', # date of death
196
+ 'P571', # inception
197
+ 'P576', # dissolved
198
+ ]
199
+
200
+ for prop in important_properties:
201
+ if prop in claims:
202
+ processed_data['claims'][prop] = claims[prop]
203
+
204
+ return processed_data
205
+
206
+ def get_related_topics(self, topic: str, limit: int = 5) -> list:
207
+ """Get related topics for additional content"""
208
+ try:
209
+ # This is a simplified implementation
210
+ # In a real app, you might use Wikipedia's "See also" sections
211
+ # or Wikidata relationships
212
+
213
+ search_results = self._search_wikipedia(topic)
214
+ if not search_results:
215
+ return []
216
+
217
+ # Return related pages from search results
218
+ related = []
219
+ for result in search_results[1:limit+1]: # Skip first result (exact match)
220
+ related.append({
221
+ 'title': result['title'],
222
+ 'description': result.get('description', ''),
223
+ 'url': result.get('content_urls', {}).get('desktop', {}).get('page', '')
224
+ })
225
+
226
+ return related
227
+
228
+ except Exception as e:
229
+ print(f"Related topics error: {e}")
230
+ return []
231
+
232
+ def get_random_article(self, language: str = 'en') -> Optional[Dict[str, str]]:
233
+ """Get a random Wikipedia article"""
234
+
235
+ lang_domains = {
236
+ 'en': 'en.wikipedia.org',
237
+ 'hi': 'hi.wikipedia.org',
238
+ 'te': 'te.wikipedia.org',
239
+ 'ta': 'ta.wikipedia.org',
240
+ 'kn': 'kn.wikipedia.org',
241
+ 'bn': 'bn.wikipedia.org'
242
+ }
243
+
244
+ domain = lang_domains.get(language, 'en.wikipedia.org')
245
+
246
+ try:
247
+ url = f"https://{domain}/api/rest_v1/page/random/summary"
248
+ response = self.session.get(url, timeout=10)
249
+ response.raise_for_status()
250
+
251
+ data = response.json()
252
+ return {
253
+ 'title': data.get('title', ''),
254
+ 'extract': data.get('extract', ''),
255
+ 'url': data.get('content_urls', {}).get('desktop', {}).get('page', '')
256
+ }
257
+
258
+ except Exception as e:
259
+ print(f"Random article error: {e}")
260
+ return None