167AliRaza commited on
Commit
95fb4d2
Β·
verified Β·
1 Parent(s): 478c204

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +263 -110
app.py CHANGED
@@ -1,128 +1,281 @@
1
- import gradio as gr
2
  import pandas as pd
3
  from pdf2image import convert_from_path
4
  import pytesseract
5
- import google.generativeai as genai
6
  import tempfile
7
- import csv
8
- from io import StringIO
 
 
 
 
 
 
 
 
 
9
 
10
- # Function: Extract text from PDF using OCR
11
- def extract_text_from_pdf(pdf_file):
12
- pages = convert_from_path(pdf_file)
13
- all_text = ""
14
- for page in pages:
15
- text = pytesseract.image_to_string(page)
16
- all_text += text + "\n"
17
- return all_text.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- # Function: Chunk text
20
- def chunk_text(text, chunk_size=1500):
21
  words = text.split()
 
22
  for i in range(0, len(words), chunk_size):
23
- yield " ".join(words[i:i+chunk_size])
24
-
25
- # Models to try (fallbacks)
26
- models_to_try = [
27
- "gemini-2.5-flash-lite",
28
- "gemini-2.5-flash",
29
- "gemini-2.5-pro",
30
- "gemini-2.0-flash-lite",
31
- "gemini-2.0-flash",
32
- "gemini-1.5-flash",
33
- "gemini-1.5-pro",
34
- ]
35
 
36
- # Function: Generate MCQs
37
- def generate_mcqs(text, api_key):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  genai.configure(api_key=api_key)
39
- chunks = list(chunk_text(text, 1500))
40
  mcq_data = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- for i, chunk in enumerate(chunks, start=1):
43
- prompt = f"""
44
- Generate 10 MCQs from the following text.
45
- Return ONLY valid CSV rows with exactly 6 columns:
46
- Question,OptionA,OptionB,OptionC,OptionD,CorrectAnswer
47
-
48
- Rules:
49
- - Do NOT add numbering, quotes, or explanations.
50
- - Do NOT add headers.
51
- - Do NOT add extra commas inside cells.
52
- - Exactly 10 rows per chunk.
53
-
54
- Text:\n{chunk}
55
- """
56
-
57
- response = None
58
- for model_name in models_to_try:
59
- try:
60
- model = genai.GenerativeModel(model_name)
61
- response = model.generate_content(prompt)
62
- if response.text:
63
- break
64
- except Exception:
65
- continue
66
-
67
- if response and response.text:
68
- output = response.text.strip()
69
- try:
70
- reader = csv.reader(StringIO(output))
71
- for row in reader:
72
- if len(row) >= 6 and row[0]:
73
- mcq_data.append(row[:6]) # keep only first 6 cols
74
- except Exception:
75
- continue
76
-
77
- if not mcq_data:
78
- return None, None
79
-
80
- df = pd.DataFrame(
81
- mcq_data,
82
- columns=["Question", "OptionA", "OptionB", "OptionC", "OptionD", "CorrectAnswer"],
83
- )
84
- return df, df.head(10).to_string(index=False)
85
-
86
- # Gradio pipeline
87
- def process_pdf(pdf_file, api_key):
88
  if not api_key:
89
- return "❌ Please enter your Gemini API key.", None
90
-
 
 
 
91
  try:
92
- text = extract_text_from_pdf(pdf_file.name)
93
- df, preview = generate_mcqs(text, api_key)
94
-
95
- if df is None:
96
- return "❌ No valid MCQs generated.", None
97
-
98
- # Save to a temporary Excel file
99
- tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
100
- df.to_excel(tmp_file.name, index=False)
101
-
102
- return preview, tmp_file.name
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  except Exception as e:
104
- return f"Error: {str(e)}", None
105
-
106
- # Gradio UI
107
- with gr.Blocks() as demo:
108
- gr.Markdown("## πŸ“˜ PDF to MCQ Generator (Gemini AI)")
109
- gr.Markdown(
110
- "Upload a PDF, enter your Gemini API key, extract text with OCR, and generate MCQs saved as Excel."
111
- )
112
-
113
- api_key = gr.Textbox(label="Enter your Gemini API Key", type="password")
114
- pdf_input = gr.File(label="Upload PDF", file_types=[".pdf"])
115
- generate_btn = gr.Button("Generate MCQs")
116
-
117
- preview_output = gr.Textbox(label="Preview (First 10 MCQs)", lines=15)
118
- excel_output = gr.File(label="Download Excel (.xlsx)")
119
 
120
- generate_btn.click(
121
- fn=process_pdf,
122
- inputs=[pdf_input, api_key],
123
- outputs=[preview_output, excel_output],
124
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
- # Run app
127
  if __name__ == "__main__":
128
- demo.launch()
 
 
1
+ import os
2
  import pandas as pd
3
  from pdf2image import convert_from_path
4
  import pytesseract
 
5
  import tempfile
6
+ import io
7
+ import gradio as gr
8
+ import google.generativeai as genai
9
+ from typing import List, Tuple
10
+ import time
11
+
12
+ # Configure Gemini API
13
+ def configure_gemini_api(api_key: str):
14
+ """Configure the Gemini API with the provided key"""
15
+ genai.configure(api_key=api_key)
16
+ return "βœ… API Key configured successfully!"
17
 
18
+ def extract_text_from_pdf(pdf_file) -> str:
19
+ """Extract text from PDF using OCR"""
20
+ try:
21
+ # Create temporary file
22
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp_file:
23
+ tmp_file.write(pdf_file)
24
+ tmp_path = tmp_file.name
25
+
26
+ # Convert PDF to images
27
+ pages = convert_from_path(tmp_path)
28
+ all_text = ""
29
+
30
+ for i, page in enumerate(pages):
31
+ text = pytesseract.image_to_string(page)
32
+ all_text += text + "\n"
33
+
34
+ # Clean up temporary file
35
+ os.unlink(tmp_path)
36
+
37
+ return all_text
38
+ except Exception as e:
39
+ return f"Error extracting text: {str(e)}"
40
 
41
+ def chunk_text(text: str, chunk_size: int = 1500) -> List[str]:
42
+ """Split text into chunks for processing"""
43
  words = text.split()
44
+ chunks = []
45
  for i in range(0, len(words), chunk_size):
46
+ chunks.append(' '.join(words[i:i+chunk_size]))
47
+ return chunks
 
 
 
 
 
 
 
 
 
 
48
 
49
+ def generate_mcqs_from_chunk(chunk: str, api_key: str) -> List[List[str]]:
50
+ """Generate MCQs from a text chunk using Gemini API"""
51
+ models_to_try = [
52
+ 'gemini-2.0-flash-exp',
53
+ 'gemini-1.5-flash',
54
+ 'gemini-1.5-pro'
55
+ ]
56
+
57
+ prompt = f"""
58
+ Generate 10 multiple choice questions from the following text.
59
+ Each question must have:
60
+ - A clear, specific question
61
+ - 4 options labeled A, B, C, D
62
+ - One correct answer (A, B, C, or D)
63
+
64
+ Format your response as CSV with headers: Question,OptionA,OptionB,OptionC,OptionD,CorrectAnswer
65
+
66
+ Important formatting rules:
67
+ - Use commas only as field separators
68
+ - If any field contains a comma, wrap it in double quotes
69
+ - Each row should be on a new line
70
+ - Make questions specific and clear
71
+ - Ensure options are distinct and plausible
72
+
73
+ Text to analyze:
74
+ {chunk}
75
+ """
76
+
77
+ # Configure API
78
  genai.configure(api_key=api_key)
79
+
80
  mcq_data = []
81
+ response = None
82
+
83
+ for model_name in models_to_try:
84
+ try:
85
+ model = genai.GenerativeModel(model_name)
86
+ response = model.generate_content(prompt)
87
+
88
+ if response.text:
89
+ break
90
+ except Exception as e:
91
+ print(f"Error with {model_name}: {e}")
92
+ continue
93
+
94
+ if response and response.text:
95
+ output = response.text.strip()
96
+ lines = output.splitlines()
97
+
98
+ # Skip header if present
99
+ for line in lines[1:] if lines and 'Question' in lines[0] else lines:
100
+ if line.strip():
101
+ # Simple CSV parsing (you might want to use csv module for better handling)
102
+ parts = []
103
+ current_part = ""
104
+ in_quotes = False
105
+
106
+ for char in line:
107
+ if char == '"':
108
+ in_quotes = not in_quotes
109
+ elif char == ',' and not in_quotes:
110
+ parts.append(current_part.strip().strip('"'))
111
+ current_part = ""
112
+ else:
113
+ current_part += char
114
+
115
+ # Add the last part
116
+ if current_part:
117
+ parts.append(current_part.strip().strip('"'))
118
+
119
+ if len(parts) >= 6 and parts[0].strip():
120
+ mcq_data.append(parts[:6])
121
+
122
+ return mcq_data
123
 
124
+ def process_pdf_to_mcqs(pdf_file, api_key: str, chunk_size: int = 1500, progress=gr.Progress()) -> Tuple[str, str]:
125
+ """Main function to process PDF and generate MCQs"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  if not api_key:
127
+ return "❌ Please provide your Gemini API key", ""
128
+
129
+ if not pdf_file:
130
+ return "❌ Please upload a PDF file", ""
131
+
132
  try:
133
+ # Extract text from PDF
134
+ progress(0.1, desc="Extracting text from PDF...")
135
+ extracted_text = extract_text_from_pdf(pdf_file)
136
+
137
+ if extracted_text.startswith("Error"):
138
+ return extracted_text, ""
139
+
140
+ # Chunk the text
141
+ progress(0.2, desc="Chunking text...")
142
+ chunks = chunk_text(extracted_text, chunk_size)
143
+
144
+ if not chunks:
145
+ return "❌ No text could be extracted from the PDF", ""
146
+
147
+ # Generate MCQs from each chunk
148
+ all_mcq_data = []
149
+ total_chunks = len(chunks)
150
+
151
+ for i, chunk in enumerate(chunks):
152
+ progress((0.2 + (i / total_chunks) * 0.7), desc=f"Processing chunk {i+1}/{total_chunks}...")
153
+
154
+ chunk_mcqs = generate_mcqs_from_chunk(chunk, api_key)
155
+ all_mcq_data.extend(chunk_mcqs)
156
+
157
+ # Add small delay to avoid rate limiting
158
+ time.sleep(1)
159
+
160
+ progress(0.95, desc="Creating Excel file...")
161
+
162
+ if not all_mcq_data:
163
+ return "❌ No MCQs could be generated from the PDF content", ""
164
+
165
+ # Create DataFrame
166
+ df = pd.DataFrame(all_mcq_data, columns=['Question', 'OptionA', 'OptionB', 'OptionC', 'OptionD', 'CorrectAnswer'])
167
+
168
+ # Create Excel file in memory
169
+ output = io.BytesIO()
170
+ with pd.ExcelWriter(output, engine='openpyxl') as writer:
171
+ df.to_excel(writer, index=False, sheet_name='MCQs')
172
+
173
+ output.seek(0)
174
+
175
+ # Save to temporary file for download
176
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.xlsx')
177
+ temp_file.write(output.getvalue())
178
+ temp_file.close()
179
+
180
+ progress(1.0, desc="Complete!")
181
+
182
+ success_message = f"βœ… Successfully generated {len(all_mcq_data)} MCQs from {total_chunks} text chunks!"
183
+
184
+ return success_message, temp_file.name
185
+
186
  except Exception as e:
187
+ return f"❌ Error processing PDF: {str(e)}", ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
+ # Create Gradio interface
190
+ def create_interface():
191
+ with gr.Blocks(title="PDF to MCQ Generator", theme=gr.themes.Soft()) as demo:
192
+ gr.Markdown(
193
+ """
194
+ # πŸ“š PDF to MCQ Generator
195
+
196
+ Upload a PDF document and generate multiple choice questions automatically using Google's Gemini AI.
197
+
198
+ ## How to use:
199
+ 1. Get your Gemini API key from [Google AI Studio](https://aistudio.google.com/app/apikey)
200
+ 2. Enter your API key below
201
+ 3. Upload your PDF file
202
+ 4. Adjust chunk size if needed (larger = fewer API calls, smaller = more focused questions)
203
+ 5. Click "Generate MCQs" and wait for processing
204
+ 6. Download the generated Excel file with your MCQs
205
+ """
206
+ )
207
+
208
+ with gr.Row():
209
+ with gr.Column(scale=2):
210
+ api_key_input = gr.Textbox(
211
+ label="πŸ”‘ Gemini API Key",
212
+ placeholder="Enter your Gemini API key here...",
213
+ type="password"
214
+ )
215
+
216
+ pdf_input = gr.File(
217
+ label="πŸ“„ Upload PDF File",
218
+ file_types=[".pdf"]
219
+ )
220
+
221
+ chunk_size_input = gr.Slider(
222
+ minimum=500,
223
+ maximum=3000,
224
+ value=1500,
225
+ step=100,
226
+ label="πŸ“ Chunk Size (words per processing batch)"
227
+ )
228
+
229
+ generate_btn = gr.Button(
230
+ "πŸš€ Generate MCQs",
231
+ variant="primary",
232
+ size="lg"
233
+ )
234
+
235
+ with gr.Column(scale=1):
236
+ status_output = gr.Textbox(
237
+ label="πŸ“Š Status",
238
+ placeholder="Status updates will appear here...",
239
+ lines=10
240
+ )
241
+
242
+ download_file = gr.File(
243
+ label="⬇️ Download MCQs Excel File",
244
+ visible=False
245
+ )
246
+
247
+ # Event handlers
248
+ generate_btn.click(
249
+ fn=process_pdf_to_mcqs,
250
+ inputs=[pdf_input, api_key_input, chunk_size_input],
251
+ outputs=[status_output, download_file],
252
+ show_progress=True
253
+ ).then(
254
+ fn=lambda x: gr.update(visible=bool(x)),
255
+ inputs=[download_file],
256
+ outputs=[download_file]
257
+ )
258
+
259
+ gr.Markdown(
260
+ """
261
+ ## πŸ“‹ Features:
262
+ - **OCR Text Extraction**: Converts PDF pages to images and extracts text
263
+ - **Smart Chunking**: Breaks large documents into manageable pieces
264
+ - **Multiple AI Models**: Automatically tries different Gemini models for best results
265
+ - **Excel Output**: Download MCQs in a formatted Excel file
266
+ - **Progress Tracking**: Real-time updates on processing status
267
+
268
+ ## ⚠️ Notes:
269
+ - Processing time depends on PDF length and complexity
270
+ - Large PDFs are processed in chunks to avoid timeouts
271
+ - Make sure your PDF contains readable text (not just images)
272
+ - API key is not stored and only used for your session
273
+ """
274
+ )
275
+
276
+ return demo
277
 
278
+ # Launch the app
279
  if __name__ == "__main__":
280
+ demo = create_interface()
281
+ demo.launch(server_name="0.0.0.0", server_port=7860)