Update app.py
Browse files
app.py
CHANGED
@@ -1,244 +1,35 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
from langchain.prompts import PromptTemplate
|
3 |
-
from langchain.chains import LLMChain
|
4 |
-
from langchain_google_genai import ChatGoogleGenerativeAI
|
5 |
-
import fitz
|
6 |
-
import json
|
7 |
-
import docx
|
8 |
import os
|
|
|
|
|
|
|
9 |
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
if "current_q" not in st.session_state:
|
15 |
-
st.session_state.current_q = 0
|
16 |
-
if "user_answers" not in st.session_state:
|
17 |
-
st.session_state.user_answers = {}
|
18 |
-
if "quiz_finished" not in st.session_state:
|
19 |
-
st.session_state.quiz_finished = False
|
20 |
-
if "language" not in st.session_state:
|
21 |
-
st.session_state.language = "English"
|
22 |
-
|
23 |
-
# Language selection in sidebar
|
24 |
-
st.sidebar.title("๐ Language / ์ธ์ด")
|
25 |
-
language = st.sidebar.selectbox(
|
26 |
-
"Select Language / ์ธ์ด ์ ํ",
|
27 |
-
["English", "ํ๊ตญ์ด"],
|
28 |
-
index=0 if st.session_state.language == "English" else 1
|
29 |
-
)
|
30 |
-
st.session_state.language = language
|
31 |
-
|
32 |
-
# UI Text Dictionary
|
33 |
-
ui_text = {
|
34 |
-
"English": {
|
35 |
-
"title": "๐ File-based MCQ Generator",
|
36 |
-
"sidebar_title": "Upload & Settings",
|
37 |
-
"upload_prompt": "Upload a file (PDF or Word)",
|
38 |
-
"num_questions": "Number of questions",
|
39 |
-
"generate_button": "Generate MCQs",
|
40 |
-
"no_file_error": "Please upload a file.",
|
41 |
-
"generating": "Extracting text and generating MCQs...",
|
42 |
-
"success": "โ
MCQs generated successfully!",
|
43 |
-
"error": "Error generating MCQs:",
|
44 |
-
"question_prefix": "Question",
|
45 |
-
"choose_answer": "Choose an answer:",
|
46 |
-
"next_button": "Next",
|
47 |
-
"quiz_completed": "๐ Quiz completed!",
|
48 |
-
"results_header": "๐ Quiz Results",
|
49 |
-
"your_answer": "Your answer:",
|
50 |
-
"correct_answer": "Correct answer:",
|
51 |
-
"score": "โ
You scored {score} out of {total}"
|
52 |
-
},
|
53 |
-
"ํ๊ตญ์ด": {
|
54 |
-
"title": "๐ ํ์ผ ๊ธฐ๋ฐ ๊ฐ๊ด์ ๋ฌธ์ ์์ฑ๊ธฐ",
|
55 |
-
"sidebar_title": "ํ์ผ ์
๋ก๋ ๋ฐ ์ค์ ",
|
56 |
-
"upload_prompt": "ํ์ผ ์
๋ก๋ (PDF ๋๋ Word)",
|
57 |
-
"num_questions": "๋ฌธ์ ๊ฐ์",
|
58 |
-
"generate_button": "๋ฌธ์ ์์ฑ",
|
59 |
-
"no_file_error": "ํ์ผ์ ์
๋ก๋ํด์ฃผ์ธ์.",
|
60 |
-
"generating": "ํ
์คํธ ์ถ์ถ ๋ฐ ๋ฌธ์ ์์ฑ ์ค...",
|
61 |
-
"success": "โ
๋ฌธ์ ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋์์ต๋๋ค!",
|
62 |
-
"error": "๋ฌธ์ ์์ฑ ์ค๋ฅ:",
|
63 |
-
"question_prefix": "๋ฌธ์ ",
|
64 |
-
"choose_answer": "๋ต์ ์ ํํ์ธ์:",
|
65 |
-
"next_button": "๋ค์",
|
66 |
-
"quiz_completed": "๐ ํด์ฆ๊ฐ ์๋ฃ๋์์ต๋๋ค!",
|
67 |
-
"results_header": "๐ ํด์ฆ ๊ฒฐ๊ณผ",
|
68 |
-
"your_answer": "๋น์ ์ ๋ต:",
|
69 |
-
"correct_answer": "์ ๋ต:",
|
70 |
-
"score": "โ
{total}๋ฌธ์ ์ค {score}๋ฌธ์ ๋ฅผ ๋ง์ถ์
จ์ต๋๋ค"
|
71 |
-
}
|
72 |
-
}
|
73 |
-
|
74 |
-
# Get current language texts
|
75 |
-
texts = ui_text[language]
|
76 |
-
|
77 |
-
# Title
|
78 |
-
st.title(texts["title"])
|
79 |
-
|
80 |
-
# Sidebar
|
81 |
-
st.sidebar.title(texts["sidebar_title"])
|
82 |
-
|
83 |
-
# Upload file
|
84 |
-
uploaded_file = st.sidebar.file_uploader(texts["upload_prompt"], type=["pdf", "docx"])
|
85 |
-
|
86 |
-
# Number of questions
|
87 |
-
number_of_questions = st.sidebar.slider(texts["num_questions"], min_value=1, max_value=20, value=5)
|
88 |
-
|
89 |
-
# Gemini setup
|
90 |
-
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
|
91 |
-
llm = ChatGoogleGenerativeAI(
|
92 |
-
model="gemini-2.0-flash-exp",
|
93 |
-
google_api_key=GOOGLE_API_KEY,
|
94 |
-
temperature=0.7
|
95 |
-
)
|
96 |
-
|
97 |
-
# Templates for different languages
|
98 |
-
template_english = """
|
99 |
-
You are an expert MCQ generator. Generate {number} unique multiple-choice questions from the given text.
|
100 |
-
Each question must have exactly 1 correct answer and 3 incorrect options.
|
101 |
-
Strictly return output in the following JSON format (no explanations, no markdown):
|
102 |
-
|
103 |
-
[
|
104 |
-
{{
|
105 |
-
"question": "What is ...?",
|
106 |
-
"options": ["Option A", "Option B", "Option C", "Option D"],
|
107 |
-
"answer": "Option D"
|
108 |
-
}},
|
109 |
-
...
|
110 |
-
]
|
111 |
-
|
112 |
-
TEXT:
|
113 |
-
{text}
|
114 |
-
"""
|
115 |
-
|
116 |
-
template_korean = """
|
117 |
-
๋น์ ์ ์ ๋ฌธ ๊ฐ๊ด์ ๋ฌธ์ ์ถ์ ์์
๋๋ค. ์ฃผ์ด์ง ํ
์คํธ์์ {number}๊ฐ์ ๊ณ ์ ํ ๊ฐ๊ด์ ๋ฌธ์ ๋ฅผ ์์ฑํ์ธ์.
|
118 |
-
๊ฐ ๋ฌธ์ ๋ ์ ํํ 1๊ฐ์ ์ ๋ต๊ณผ 3๊ฐ์ ์ค๋ต์ ๊ฐ์ ธ์ผ ํฉ๋๋ค.
|
119 |
-
๋ค์ JSON ํ์์ผ๋ก๋ง ์ถ๋ ฅํ์ธ์ (์ค๋ช
์ด๋ ๋งํฌ๋ค์ด ์์ด):
|
120 |
-
|
121 |
-
[
|
122 |
-
{{
|
123 |
-
"question": "...๋ ๋ฌด์์
๋๊น?",
|
124 |
-
"options": ["์ ํ์ง A", "์ ํ์ง B", "์ ํ์ง C", "์ ํ์ง D"],
|
125 |
-
"answer": "์ ํ์ง D"
|
126 |
-
}},
|
127 |
-
...
|
128 |
-
]
|
129 |
-
|
130 |
-
ํ
์คํธ:
|
131 |
-
{text}
|
132 |
-
"""
|
133 |
-
|
134 |
-
# Select template based on language
|
135 |
-
template = template_english if language == "English" else template_korean
|
136 |
-
|
137 |
-
prompt = PromptTemplate(
|
138 |
-
input_variables=["text", "number"],
|
139 |
-
template=template
|
140 |
-
)
|
141 |
-
|
142 |
-
mcq_chain = LLMChain(llm=llm, prompt=prompt)
|
143 |
-
|
144 |
-
# Extract text from PDF or Word
|
145 |
-
def extract_text(file):
|
146 |
-
if file.name.endswith(".pdf"):
|
147 |
-
# Read the entire file content into memory
|
148 |
-
file_bytes = file.read()
|
149 |
-
# Open the PDF from the byte stream
|
150 |
-
doc = fitz.open(stream=file_bytes, filetype="pdf")
|
151 |
-
# Extract text from all pages
|
152 |
-
text = ""
|
153 |
-
for page in doc:
|
154 |
-
text += page.get_text()
|
155 |
-
return text
|
156 |
-
elif file.name.endswith(".docx"):
|
157 |
-
doc = docx.Document(file)
|
158 |
-
return "\n".join([para.text for para in doc.paragraphs])
|
159 |
-
return ""
|
160 |
-
|
161 |
-
# Generate MCQs
|
162 |
-
if st.sidebar.button(texts["generate_button"]):
|
163 |
-
if uploaded_file is None:
|
164 |
-
st.error(texts["no_file_error"])
|
165 |
-
else:
|
166 |
-
with st.spinner(texts["generating"]):
|
167 |
-
text = extract_text(uploaded_file)
|
168 |
-
try:
|
169 |
-
response = mcq_chain.run(text=text, number=str(number_of_questions))
|
170 |
-
# Clean the response to extract JSON
|
171 |
-
response = response.strip()
|
172 |
-
if response.startswith("```json"):
|
173 |
-
response = response[7:]
|
174 |
-
if response.endswith("```"):
|
175 |
-
response = response[:-3]
|
176 |
-
mcqs_json = json.loads(response)
|
177 |
-
st.session_state.mcqs = mcqs_json
|
178 |
-
st.session_state.current_q = 0
|
179 |
-
st.session_state.user_answers = {}
|
180 |
-
st.session_state.quiz_finished = False
|
181 |
-
st.success(texts["success"])
|
182 |
-
except Exception as e:
|
183 |
-
st.error(f"{texts['error']} {e}")
|
184 |
-
|
185 |
-
# Display question
|
186 |
-
if st.session_state.mcqs and not st.session_state.quiz_finished:
|
187 |
-
idx = st.session_state.current_q
|
188 |
-
q_data = st.session_state.mcqs[idx]
|
189 |
-
|
190 |
-
st.subheader(f"{texts['question_prefix']} {idx + 1}: {q_data['question']}")
|
191 |
-
|
192 |
-
with st.form(key=f"form_{idx}"):
|
193 |
-
selected_option = st.radio(texts["choose_answer"], q_data["options"], key=f"radio_{idx}")
|
194 |
-
submitted = st.form_submit_button(texts["next_button"])
|
195 |
-
|
196 |
-
if submitted:
|
197 |
-
st.session_state.user_answers[idx] = selected_option
|
198 |
-
if idx < len(st.session_state.mcqs) - 1:
|
199 |
-
st.session_state.current_q += 1
|
200 |
-
st.rerun()
|
201 |
-
else:
|
202 |
-
st.session_state.quiz_finished = True
|
203 |
-
st.success(texts["quiz_completed"])
|
204 |
-
st.rerun()
|
205 |
-
|
206 |
-
# Show result
|
207 |
-
if st.session_state.quiz_finished:
|
208 |
-
st.header(texts["results_header"])
|
209 |
-
score = 0
|
210 |
-
total = len(st.session_state.mcqs)
|
211 |
-
|
212 |
-
for i, q in enumerate(st.session_state.mcqs):
|
213 |
-
user_ans = st.session_state.user_answers.get(i)
|
214 |
-
correct_ans = q["answer"]
|
215 |
-
if user_ans == correct_ans:
|
216 |
-
score += 1
|
217 |
|
218 |
-
|
219 |
-
|
|
|
220 |
|
221 |
-
#
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
st.session_state.user_answers = {}
|
243 |
-
st.session_state.quiz_finished = False
|
244 |
-
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import os
|
2 |
+
import sys
|
3 |
+
import streamlit as st
|
4 |
+
from tempfile import NamedTemporaryFile
|
5 |
|
6 |
+
def main():
|
7 |
+
try:
|
8 |
+
# Get the code from secrets
|
9 |
+
code = os.environ.get("MAIN_CODE")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
+
if not code:
|
12 |
+
st.error("โ ๏ธ The application code wasn't found in secrets. Please add the MAIN_CODE secret.")
|
13 |
+
return
|
14 |
|
15 |
+
# Create a temporary Python file
|
16 |
+
with NamedTemporaryFile(suffix='.py', delete=False, mode='w') as tmp:
|
17 |
+
tmp.write(code)
|
18 |
+
tmp_path = tmp.name
|
19 |
+
|
20 |
+
# Execute the code
|
21 |
+
exec(compile(code, tmp_path, 'exec'), globals())
|
22 |
+
|
23 |
+
# Clean up the temporary file
|
24 |
+
try:
|
25 |
+
os.unlink(tmp_path)
|
26 |
+
except:
|
27 |
+
pass
|
28 |
+
|
29 |
+
except Exception as e:
|
30 |
+
st.error(f"โ ๏ธ Error loading or executing the application: {str(e)}")
|
31 |
+
import traceback
|
32 |
+
st.code(traceback.format_exc())
|
33 |
+
|
34 |
+
if __name__ == "__main__":
|
35 |
+
main()
|
|
|
|
|
|