ginipick commited on
Commit
57f518a
Β·
verified Β·
1 Parent(s): d479d1a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -31
app.py CHANGED
@@ -1,35 +1,236 @@
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()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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": "πŸ“„ PDF/Word based Self EXAM",
36
+ "sidebar_title": "Upload & Settings",
37
+ "upload_prompt": "Upload a file (PDF or Word)",
38
+ "num_questions": "Number of questions",
39
+ "generate_button": "Generate EXAMs",
40
+ "no_file_error": "Please upload a file.",
41
+ "generating": "Extracting text and generating EXAMs...",
42
+ "success": "βœ… EXAMs generated successfully!",
43
+ "error": "Error generating EXAMs:",
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": "πŸ“„ PDF/Word 기반 객관식 문제 생성기",
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 = ("You are an expert EXAM generator. Generate {number} unique multiple-choice questions from the given text.\n"
99
+ "Each question must have exactly 1 correct answer and 3 incorrect options.\n"
100
+ "Strictly return output in the following JSON format (no explanations, no markdown):\n"
101
+ "[\n"
102
+ " {{\n"
103
+ ' "question": "What is ...?",\n'
104
+ ' "options": ["Option A", "Option B", "Option C", "Option D"],\n'
105
+ ' "answer": "Option D"\n'
106
+ " }},\n"
107
+ " ...\n"
108
+ "]\n"
109
+ "TEXT:\n"
110
+ "{text}")
111
+
112
+ template_korean = ("당신은 μ „λ¬Έ 객관식 문제 μΆœμ œμžμž…λ‹ˆλ‹€. μ£Όμ–΄μ§„ ν…μŠ€νŠΈμ—μ„œ {number}개의 κ³ μœ ν•œ 객관식 문제λ₯Ό μƒμ„±ν•˜μ„Έμš”.\n"
113
+ "각 λ¬Έμ œλŠ” μ •ν™•νžˆ 1개의 μ •λ‹΅κ³Ό 3개의 μ˜€λ‹΅μ„ κ°€μ Έμ•Ό ν•©λ‹ˆλ‹€.\n"
114
+ "λ‹€μŒ JSON ν˜•μ‹μœΌλ‘œλ§Œ 좜λ ₯ν•˜μ„Έμš” (μ„€λͺ…μ΄λ‚˜ λ§ˆν¬λ‹€μš΄ 없이):\n"
115
+ "[\n"
116
+ " {{\n"
117
+ ' "question": "...λŠ” λ¬΄μ—‡μž…λ‹ˆκΉŒ?",\n'
118
+ ' "options": ["선택지 A", "선택지 B", "선택지 C", "선택지 D"],\n'
119
+ ' "answer": "선택지 D"\n'
120
+ " }},\n"
121
+ " ...\n"
122
+ "]\n"
123
+ "ν…μŠ€νŠΈ:\n"
124
+ "{text}")
125
+
126
+ # Select template based on language
127
+ template = template_english if language == "English" else template_korean
128
+
129
+ prompt = PromptTemplate(
130
+ input_variables=["text", "number"],
131
+ template=template
132
+ )
133
+
134
+ mcq_chain = LLMChain(llm=llm, prompt=prompt)
135
+
136
+ # Extract text from PDF or Word
137
+ def extract_text(file):
138
+ if file.name.endswith(".pdf"):
139
+ # Read the entire file content into memory
140
+ file_bytes = file.read()
141
+ # Open the PDF from the byte stream
142
+ doc = fitz.open(stream=file_bytes, filetype="pdf")
143
+ # Extract text from all pages
144
+ text = ""
145
+ for page in doc:
146
+ text += page.get_text()
147
+ return text
148
+ elif file.name.endswith(".docx"):
149
+ doc = docx.Document(file)
150
+ return "\n".join([para.text for para in doc.paragraphs])
151
+ return ""
152
+
153
+ # Generate MCQs
154
+ if st.sidebar.button(texts["generate_button"]):
155
+ if uploaded_file is None:
156
+ st.error(texts["no_file_error"])
157
+ else:
158
+ with st.spinner(texts["generating"]):
159
+ text = extract_text(uploaded_file)
160
+ try:
161
+ response = mcq_chain.run(text=text, number=str(number_of_questions))
162
+ # Clean the response to extract JSON
163
+ response = response.strip()
164
+ if response.startswith("```json"):
165
+ response = response[7:]
166
+ if response.endswith("```"):
167
+ response = response[:-3]
168
+ mcqs_json = json.loads(response)
169
+ st.session_state.mcqs = mcqs_json
170
+ st.session_state.current_q = 0
171
+ st.session_state.user_answers = {}
172
+ st.session_state.quiz_finished = False
173
+ st.success(texts["success"])
174
+ except Exception as e:
175
+ st.error(f"{texts['error']} {e}")
176
+
177
+ # Display question
178
+ if st.session_state.mcqs and not st.session_state.quiz_finished:
179
+ idx = st.session_state.current_q
180
+ q_data = st.session_state.mcqs[idx]
181
+
182
+ st.subheader(f"{texts['question_prefix']} {idx + 1}: {q_data['question']}")
183
+
184
+ with st.form(key=f"form_{idx}"):
185
+ selected_option = st.radio(texts["choose_answer"], q_data["options"], key=f"radio_{idx}")
186
+ submitted = st.form_submit_button(texts["next_button"])
187
+
188
+ if submitted:
189
+ st.session_state.user_answers[idx] = selected_option
190
+ if idx < len(st.session_state.mcqs) - 1:
191
+ st.session_state.current_q += 1
192
+ st.rerun()
193
+ else:
194
+ st.session_state.quiz_finished = True
195
+ st.success(texts["quiz_completed"])
196
+ st.rerun()
197
+
198
+ # Show result
199
+ if st.session_state.quiz_finished:
200
+ st.header(texts["results_header"])
201
+ score = 0
202
+ total = len(st.session_state.mcqs)
203
+
204
+ for i, q in enumerate(st.session_state.mcqs):
205
+ user_ans = st.session_state.user_answers.get(i)
206
+ correct_ans = q["answer"]
207
+ if user_ans == correct_ans:
208
+ score += 1
209
 
210
+ # Question display
211
+ st.markdown(f"**{texts['question_prefix']}{i+1}: {q['question']}**")
212
 
213
+ # Answers with color coding
214
+ if user_ans == correct_ans:
215
+ st.markdown(f"- {texts['your_answer']} :green[{user_ans}] βœ“")
216
+ else:
217
+ st.markdown(f"- {texts['your_answer']} :red[{user_ans}] βœ—")
218
+ st.markdown(f"- {texts['correct_answer']} :green[{correct_ans}]")
219
+ st.markdown("---")
220
+
221
+ # Score display
222
+ score_text = texts["score"].format(score=score, total=total)
223
+ st.success(score_text)
224
+
225
+ # Reset button
226
+ if language == "English":
227
+ reset_text = "Start New Quiz"
228
+ else:
229
+ reset_text = "μƒˆ ν€΄μ¦ˆ μ‹œμž‘"
230
+
231
+ if st.button(reset_text):
232
+ st.session_state.mcqs = []
233
+ st.session_state.current_q = 0
234
+ st.session_state.user_answers = {}
235
+ st.session_state.quiz_finished = False
236
+ st.rerun()