ginipick commited on
Commit
901c3be
Β·
verified Β·
1 Parent(s): 16fc160

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +244 -0
app.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # Session states
12
+ if "mcqs" not in st.session_state:
13
+ st.session_state.mcqs = []
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
+ # Question display
219
+ st.markdown(f"**{texts['question_prefix']}{i+1}: {q['question']}**")
220
+
221
+ # Answers with color coding
222
+ if user_ans == correct_ans:
223
+ st.markdown(f"- {texts['your_answer']} :green[{user_ans}] βœ“")
224
+ else:
225
+ st.markdown(f"- {texts['your_answer']} :red[{user_ans}] βœ—")
226
+ st.markdown(f"- {texts['correct_answer']} :green[{correct_ans}]")
227
+ st.markdown("---")
228
+
229
+ # Score display
230
+ score_text = texts["score"].format(score=score, total=total)
231
+ st.success(score_text)
232
+
233
+ # Reset button
234
+ if language == "English":
235
+ reset_text = "Start New Quiz"
236
+ else:
237
+ reset_text = "μƒˆ ν€΄μ¦ˆ μ‹œμž‘"
238
+
239
+ if st.button(reset_text):
240
+ st.session_state.mcqs = []
241
+ st.session_state.current_q = 0
242
+ st.session_state.user_answers = {}
243
+ st.session_state.quiz_finished = False
244
+ st.rerun()