Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -13,8 +13,9 @@ import os
|
|
| 13 |
from concurrent.futures import ThreadPoolExecutor
|
| 14 |
import requests
|
| 15 |
from bs4 import BeautifulSoup
|
| 16 |
-
|
| 17 |
-
|
|
|
|
| 18 |
|
| 19 |
# Load environment variables
|
| 20 |
load_dotenv()
|
|
@@ -30,10 +31,14 @@ pytesseract.pytesseract.tesseract_cmd = r"/usr/bin/tesseract" # Adjust based on
|
|
| 30 |
|
| 31 |
# Function to enhance image for OCR processing
|
| 32 |
def enhance_image_for_ocr(image):
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
# Function to extract text from images using OCR
|
| 39 |
def extract_text_from_images(images, lang="eng"):
|
|
@@ -112,20 +117,26 @@ def process_files(uploaded_files, lang="eng"):
|
|
| 112 |
ocr_text = extract_text_from_images(images, lang)
|
| 113 |
return combined_text + "\n" + ocr_text
|
| 114 |
|
| 115 |
-
# Function to generate questions
|
| 116 |
-
def generate_questions(syllabus_text, num_questions):
|
| 117 |
-
prompt =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
| 119 |
chain = (ChatPromptTemplate.from_template(prompt) | llm | StrOutputParser())
|
| 120 |
try:
|
| 121 |
questions = chain.invoke({})
|
| 122 |
-
|
| 123 |
-
|
|
|
|
| 124 |
except Exception as e:
|
| 125 |
logging.error(f"Error generating questions: {e}")
|
| 126 |
return ""
|
| 127 |
|
| 128 |
-
#
|
| 129 |
def generate_answers(questions, syllabus_text):
|
| 130 |
answers = {}
|
| 131 |
|
|
@@ -134,6 +145,8 @@ def generate_answers(questions, syllabus_text):
|
|
| 134 |
prompt = f"""
|
| 135 |
Below is a syllabus excerpt. Please answer the following question based on the content provided.
|
| 136 |
Ensure the answer is directly related to the question and specific to the syllabus.
|
|
|
|
|
|
|
| 137 |
Syllabus Content: {syllabus_text}
|
| 138 |
|
| 139 |
Question: {question}
|
|
@@ -145,44 +158,40 @@ def generate_answers(questions, syllabus_text):
|
|
| 145 |
answer = chain.invoke({})
|
| 146 |
answers[f"Answer {i+1}"] = answer.strip()
|
| 147 |
except Exception as e:
|
| 148 |
-
answers[f"Answer {i+1}"] =
|
| 149 |
|
| 150 |
return "\n".join([f"{k}: {v}" for k, v in answers.items()])
|
| 151 |
|
| 152 |
-
# Function to
|
| 153 |
-
def
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
|
| 168 |
-
# Function to generate PDF download
|
| 169 |
-
def generate_pdf(questions, answers):
|
| 170 |
pdf = FPDF()
|
| 171 |
-
pdf.set_auto_page_break(auto=True, margin=15)
|
| 172 |
pdf.add_page()
|
| 173 |
-
|
| 174 |
-
questions_list = questions.split("\n")
|
| 175 |
-
answers_list = answers.split("\n")
|
| 176 |
-
|
| 177 |
pdf.set_font("Arial", size=12)
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
pdf_path = "/tmp/questions_and_answers.pdf"
|
| 184 |
-
pdf.output(pdf_path)
|
| 185 |
-
return pdf_path
|
| 186 |
|
| 187 |
# Streamlit UI
|
| 188 |
st.title("AI-Powered Exam Generator")
|
|
@@ -213,9 +222,30 @@ with tab2:
|
|
| 213 |
# Generate questions and answers
|
| 214 |
with tab3:
|
| 215 |
st.header("Generate Questions and Answers")
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
st.session_state["questions"] = questions
|
| 220 |
st.text_area("Generated Questions", questions, height=300)
|
| 221 |
|
|
@@ -225,14 +255,12 @@ with tab3:
|
|
| 225 |
st.text_area("Generated Answers", answers, height=300)
|
| 226 |
|
| 227 |
# Download questions and answers
|
| 228 |
-
st.download_button("Download Questions", questions, file_name="
|
| 229 |
-
st.download_button("Download
|
| 230 |
-
st.download_button("Download as DOCX", generate_docx(questions, answers), file_name="questions_and_answers.docx")
|
| 231 |
-
st.download_button("Download as PDF", generate_pdf(questions, answers), file_name="questions_and_answers.pdf")
|
| 232 |
|
| 233 |
# Generate answers
|
| 234 |
with tab4:
|
| 235 |
-
st.header("Generate Answers
|
| 236 |
if "questions" in st.session_state:
|
| 237 |
if st.button("Generate Answers"):
|
| 238 |
answers = generate_answers(st.session_state["questions"], st.session_state.get("syllabus_text", ""))
|
|
@@ -240,6 +268,4 @@ with tab4:
|
|
| 240 |
st.text_area("Generated Answers", answers, height=300)
|
| 241 |
|
| 242 |
# Download answers
|
| 243 |
-
st.download_button("Download Answers", answers, file_name="answers.txt")
|
| 244 |
-
st.download_button("Download Answers as DOCX", generate_docx(st.session_state["questions"], answers), file_name="answers.docx")
|
| 245 |
-
st.download_button("Download Answers as PDF", generate_pdf(st.session_state["questions"], answers), file_name="answers.pdf")
|
|
|
|
| 13 |
from concurrent.futures import ThreadPoolExecutor
|
| 14 |
import requests
|
| 15 |
from bs4 import BeautifulSoup
|
| 16 |
+
import re
|
| 17 |
+
import json
|
| 18 |
+
import pandas as pd
|
| 19 |
|
| 20 |
# Load environment variables
|
| 21 |
load_dotenv()
|
|
|
|
| 31 |
|
| 32 |
# Function to enhance image for OCR processing
|
| 33 |
def enhance_image_for_ocr(image):
|
| 34 |
+
try:
|
| 35 |
+
gray_image = image.convert("L")
|
| 36 |
+
enhancer = ImageEnhance.Contrast(gray_image)
|
| 37 |
+
enhanced_image = enhancer.enhance(2.0) # Increase contrast
|
| 38 |
+
return enhanced_image
|
| 39 |
+
except Exception as e:
|
| 40 |
+
logging.error(f"Error enhancing image for OCR: {e}")
|
| 41 |
+
return image
|
| 42 |
|
| 43 |
# Function to extract text from images using OCR
|
| 44 |
def extract_text_from_images(images, lang="eng"):
|
|
|
|
| 117 |
ocr_text = extract_text_from_images(images, lang)
|
| 118 |
return combined_text + "\n" + ocr_text
|
| 119 |
|
| 120 |
+
# Function to generate questions with enhanced realism
|
| 121 |
+
def generate_questions(question_type, syllabus_text, num_questions, difficulty, prompt_template):
|
| 122 |
+
prompt = prompt_template.format(
|
| 123 |
+
num_questions=num_questions,
|
| 124 |
+
question_type=question_type,
|
| 125 |
+
syllabus_text=syllabus_text,
|
| 126 |
+
**difficulty
|
| 127 |
+
)
|
| 128 |
|
| 129 |
chain = (ChatPromptTemplate.from_template(prompt) | llm | StrOutputParser())
|
| 130 |
try:
|
| 131 |
questions = chain.invoke({})
|
| 132 |
+
# Extract only the question text, remove numbering, difficulty, etc.
|
| 133 |
+
questions_clean = re.sub(r"(\d+\.)|(.*?)", "", questions) # Remove question numbers and extra parts
|
| 134 |
+
return questions_clean.strip()
|
| 135 |
except Exception as e:
|
| 136 |
logging.error(f"Error generating questions: {e}")
|
| 137 |
return ""
|
| 138 |
|
| 139 |
+
# Refined function to generate answers (excluding question numbers and difficulty)
|
| 140 |
def generate_answers(questions, syllabus_text):
|
| 141 |
answers = {}
|
| 142 |
|
|
|
|
| 145 |
prompt = f"""
|
| 146 |
Below is a syllabus excerpt. Please answer the following question based on the content provided.
|
| 147 |
Ensure the answer is directly related to the question and specific to the syllabus.
|
| 148 |
+
If necessary, explain key concepts clearly. Answer the question in a concise and detailed manner.
|
| 149 |
+
|
| 150 |
Syllabus Content: {syllabus_text}
|
| 151 |
|
| 152 |
Question: {question}
|
|
|
|
| 158 |
answer = chain.invoke({})
|
| 159 |
answers[f"Answer {i+1}"] = answer.strip()
|
| 160 |
except Exception as e:
|
| 161 |
+
answers[f"Answer {i+1}"] = search_answers_online(question)
|
| 162 |
|
| 163 |
return "\n".join([f"{k}: {v}" for k, v in answers.items()])
|
| 164 |
|
| 165 |
+
# Function to search answers online
|
| 166 |
+
def search_answers_online(question):
|
| 167 |
+
search_url = f"https://www.google.com/search?q={question}"
|
| 168 |
+
headers = {"User-Agent": "Mozilla/5.0"}
|
| 169 |
+
try:
|
| 170 |
+
response = requests.get(search_url, headers=headers)
|
| 171 |
+
soup = BeautifulSoup(response.text, "html.parser")
|
| 172 |
+
snippets = soup.find_all("div", class_="BNeawe")
|
| 173 |
+
return "\n".join([snippet.get_text() for snippet in snippets[:3]])
|
| 174 |
+
except Exception as e:
|
| 175 |
+
logging.error(f"Error fetching online answers: {e}")
|
| 176 |
+
return "No online answer found."
|
| 177 |
|
| 178 |
+
# Function to limit downloads to DOCX, PDF, and TXT formats
|
| 179 |
+
def save_as_txt(questions, answers):
|
| 180 |
+
return f"Questions:\n{questions}\n\nAnswers:\n{answers}"
|
| 181 |
|
| 182 |
+
def save_as_pdf(questions, answers):
|
| 183 |
+
# For simplicity, returning a text-based PDF. For more complex PDFs, libraries like ReportLab can be used.
|
| 184 |
+
from fpdf import FPDF
|
| 185 |
|
|
|
|
|
|
|
| 186 |
pdf = FPDF()
|
|
|
|
| 187 |
pdf.add_page()
|
| 188 |
+
pdf.set_auto_page_break(auto=True, margin=15)
|
|
|
|
|
|
|
|
|
|
| 189 |
pdf.set_font("Arial", size=12)
|
| 190 |
+
pdf.multi_cell(0, 10, f"Questions:\n{questions}\n\nAnswers:\n{answers}")
|
| 191 |
+
pdf_output = BytesIO()
|
| 192 |
+
pdf.output(pdf_output)
|
| 193 |
+
pdf_output.seek(0)
|
| 194 |
+
return pdf_output
|
|
|
|
|
|
|
|
|
|
| 195 |
|
| 196 |
# Streamlit UI
|
| 197 |
st.title("AI-Powered Exam Generator")
|
|
|
|
| 222 |
# Generate questions and answers
|
| 223 |
with tab3:
|
| 224 |
st.header("Generate Questions and Answers")
|
| 225 |
+
question_type = st.selectbox("Select Question Type", ["MCQs", "Short Questions", "Long Questions", "Fill-in-the-Blank", "Case Study"])
|
| 226 |
+
num_questions = st.text_input("Total Number of Questions")
|
| 227 |
+
difficulty_levels = ["Remember", "Understand", "Apply", "Analyze", "Evaluate", "Create"]
|
| 228 |
+
difficulty = {level: st.slider(level, 0, 5, 1) for level in difficulty_levels}
|
| 229 |
+
prompt_template = st.text_area(
|
| 230 |
+
"Edit Prompt Template",
|
| 231 |
+
"""
|
| 232 |
+
Generate {num_questions} {question_type} questions from the syllabus content below.
|
| 233 |
+
Syllabus Content: {syllabus_text}
|
| 234 |
+
Difficulty Levels:
|
| 235 |
+
- Remember: {Remember}
|
| 236 |
+
- Understand: {Understand}
|
| 237 |
+
- Apply: {Apply}
|
| 238 |
+
- Analyze: {Analyze}
|
| 239 |
+
- Evaluate: {Evaluate}
|
| 240 |
+
- Create: {Create}
|
| 241 |
+
""",
|
| 242 |
+
height=200
|
| 243 |
+
)
|
| 244 |
+
if num_questions.isdigit() and st.button("Generate Questions and Answers"):
|
| 245 |
+
num_questions = int(num_questions)
|
| 246 |
+
|
| 247 |
+
# Generate questions
|
| 248 |
+
questions = generate_questions(question_type, st.session_state.get("syllabus_text", ""), num_questions, difficulty, prompt_template)
|
| 249 |
st.session_state["questions"] = questions
|
| 250 |
st.text_area("Generated Questions", questions, height=300)
|
| 251 |
|
|
|
|
| 255 |
st.text_area("Generated Answers", answers, height=300)
|
| 256 |
|
| 257 |
# Download questions and answers
|
| 258 |
+
st.download_button("Download Questions (TXT)", save_as_txt(questions, answers), file_name="qa.txt")
|
| 259 |
+
st.download_button("Download Q&A (PDF)", save_as_pdf(questions, answers), file_name="qa.pdf")
|
|
|
|
|
|
|
| 260 |
|
| 261 |
# Generate answers
|
| 262 |
with tab4:
|
| 263 |
+
st.header("Generate Answers (Optional)")
|
| 264 |
if "questions" in st.session_state:
|
| 265 |
if st.button("Generate Answers"):
|
| 266 |
answers = generate_answers(st.session_state["questions"], st.session_state.get("syllabus_text", ""))
|
|
|
|
| 268 |
st.text_area("Generated Answers", answers, height=300)
|
| 269 |
|
| 270 |
# Download answers
|
| 271 |
+
st.download_button("Download Answers", answers, file_name="answers.txt")
|
|
|
|
|
|