Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,10 +1,6 @@
|
|
1 |
-
import gspread
|
2 |
-
from oauth2client.service_account import ServiceAccountCredentials
|
3 |
import streamlit as st
|
4 |
import openai
|
5 |
import time
|
6 |
-
from datetime import datetime
|
7 |
-
from openai import OpenAIError # Import OpenAIError for handling API errors
|
8 |
|
9 |
# Set your OpenAI API key from Hugging Face Secrets
|
10 |
openai.api_key = st.secrets["OPENAI_API_KEY"]
|
@@ -12,33 +8,6 @@ openai.api_key = st.secrets["OPENAI_API_KEY"]
|
|
12 |
# Initialize OpenAI client
|
13 |
client = openai.OpenAI(api_key=openai.api_key)
|
14 |
|
15 |
-
# Google Sheets setup
|
16 |
-
scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
|
17 |
-
creds = ServiceAccountCredentials.from_json_keyfile_name("genexam-2c8c645ecc0d.json", scope)
|
18 |
-
client_gs = gspread.authorize(creds)
|
19 |
-
sheet = client_gs.open("GeneXam user").sheet1 # Open the Google Sheet
|
20 |
-
|
21 |
-
# Function to get user data from Google Sheets
|
22 |
-
def get_user_data(user_id):
|
23 |
-
users = sheet.get_all_records()
|
24 |
-
for user in users:
|
25 |
-
if user['UserID'] == user_id:
|
26 |
-
return user
|
27 |
-
return None
|
28 |
-
|
29 |
-
# Function to update user data in Google Sheets
|
30 |
-
def update_user_data(user_id, daily_api_count, total_api_count, last_used_date):
|
31 |
-
cell = sheet.find(user_id)
|
32 |
-
if cell:
|
33 |
-
sheet.update_cell(cell.row, cell.col + 1, daily_api_count) # Update DailyAPICount
|
34 |
-
sheet.update_cell(cell.row, cell.col + 2, total_api_count) # Update TotalAPICount
|
35 |
-
sheet.update_cell(cell.row, cell.col + 3, last_used_date) # Update LastUsedDate
|
36 |
-
else:
|
37 |
-
sheet.append_row([user_id, daily_api_count, total_api_count, last_used_date]) # Add new user if not found
|
38 |
-
|
39 |
-
# Admin user ID
|
40 |
-
admin_user_id = "admin"
|
41 |
-
|
42 |
# Function to generate exam questions using OpenAI API with retry logic
|
43 |
def generate_questions_with_retry(knowledge_material, question_type, cognitive_level, extra_instructions, case_based, num_choices=None, max_retries=3):
|
44 |
# Adjust the number of questions based on the type
|
@@ -70,32 +39,22 @@ def generate_questions_with_retry(knowledge_material, question_type, cognitive_l
|
|
70 |
retries = 0
|
71 |
while retries < max_retries:
|
72 |
try:
|
73 |
-
|
74 |
-
|
75 |
-
model="GPT-4o mini", # You can use "gpt-4" if available
|
76 |
messages=[
|
77 |
{"role": "system", "content": "You are a helpful assistant for generating exam questions."},
|
78 |
{"role": "user", "content": prompt}
|
79 |
-
]
|
80 |
-
max_tokens=1000,
|
81 |
-
temperature=0.7
|
82 |
)
|
83 |
-
return response.choices[0].message
|
84 |
-
except openai.APIConnectionError
|
85 |
retries += 1
|
86 |
time.sleep(2) # Wait for 2 seconds before retrying
|
87 |
-
st.error(f"Failed to connect to OpenAI API: {str(e)}")
|
88 |
if retries == max_retries:
|
89 |
st.error("Failed to connect to OpenAI API after several attempts.")
|
90 |
return None
|
91 |
-
|
92 |
-
|
93 |
-
return None
|
94 |
-
except openai.APIError as e:
|
95 |
-
st.error(f"API Error: {str(e)}")
|
96 |
-
return None
|
97 |
-
|
98 |
-
# Main logic for the app
|
99 |
if 'username' not in st.session_state:
|
100 |
# Show the login form if the username is not set
|
101 |
st.title("Login")
|
@@ -107,142 +66,108 @@ if 'username' not in st.session_state:
|
|
107 |
else:
|
108 |
st.warning("Please enter a valid username.")
|
109 |
else:
|
110 |
-
|
111 |
-
|
|
|
|
|
|
|
112 |
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
# Check if the user has reached their daily limit (except for admin)
|
125 |
-
if user_id != admin_user_id and user_data['DailyAPICount'] >= 5:
|
126 |
-
st.warning("You have reached your daily limit of 5 API calls.")
|
127 |
else:
|
128 |
-
#
|
129 |
-
|
|
|
130 |
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
# Word count check
|
135 |
-
if len(knowledge_material.split()) > 3000:
|
136 |
-
st.warning("Please limit the knowledge material to 3,000 words or fewer.")
|
137 |
|
138 |
-
|
139 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
|
141 |
-
if
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
new_daily_api_count = user_data['DailyAPICount'] + 1
|
191 |
-
new_total_api_count = user_data['TotalAPICount'] + 1
|
192 |
-
update_user_data(user_id, new_daily_api_count, new_total_api_count, today)
|
193 |
-
|
194 |
-
st.success(f"API call #{new_daily_api_count} successful today!")
|
195 |
-
st.info(f"Total API calls: {new_total_api_count}")
|
196 |
-
|
197 |
-
# Option to download the questions as a text file
|
198 |
-
st.download_button(
|
199 |
-
label="Download Questions",
|
200 |
-
data=questions,
|
201 |
-
file_name='generated_questions.txt',
|
202 |
-
mime='text/plain'
|
203 |
-
)
|
204 |
-
else:
|
205 |
-
st.warning("Please reduce the word count to 3,000 or fewer.")
|
206 |
-
|
207 |
-
# Button to generate more questions based on the same material
|
208 |
-
if st.button("Generate More Questions"):
|
209 |
-
if len(knowledge_material.split()) <= 3000:
|
210 |
-
# Regenerate new questions, trying to avoid repeated content
|
211 |
-
questions = generate_questions_with_retry(
|
212 |
-
knowledge_material,
|
213 |
-
question_type,
|
214 |
-
cognitive_level,
|
215 |
-
extra_instructions,
|
216 |
-
case_based,
|
217 |
-
num_choices
|
218 |
-
)
|
219 |
-
|
220 |
-
# Check if the new set of questions is not the same as the previous set
|
221 |
-
if questions and questions not in st.session_state['previous_questions']:
|
222 |
-
st.write("Generated More Exam Questions:")
|
223 |
-
st.write(questions)
|
224 |
-
|
225 |
-
# Append the new questions to the session state
|
226 |
-
st.session_state['previous_questions'].append(questions)
|
227 |
-
|
228 |
-
# Update user API usage for more questions
|
229 |
-
new_daily_api_count = user_data['DailyAPICount'] + 1
|
230 |
-
new_total_api_count = user_data['TotalAPICount'] + 1
|
231 |
-
update_user_data(user_id, new_daily_api_count, new_total_api_count, today)
|
232 |
-
|
233 |
-
st.success(f"API call #{new_daily_api_count} successful today!")
|
234 |
-
st.info(f"Total API calls: {new_total_api_count}")
|
235 |
-
|
236 |
-
# Option to download the new set of questions
|
237 |
-
st.download_button(
|
238 |
-
label="Download More Questions",
|
239 |
-
data=questions,
|
240 |
-
file_name='more_generated_questions.txt',
|
241 |
-
mime='text/plain'
|
242 |
-
)
|
243 |
-
else:
|
244 |
-
st.warning("New questions seem to overlap with the previous ones. Try adjusting the instructions.")
|
245 |
-
else:
|
246 |
-
st.warning("Please reduce the word count to 3,000 or fewer.")
|
247 |
-
|
248 |
-
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
import openai
|
3 |
import time
|
|
|
|
|
4 |
|
5 |
# Set your OpenAI API key from Hugging Face Secrets
|
6 |
openai.api_key = st.secrets["OPENAI_API_KEY"]
|
|
|
8 |
# Initialize OpenAI client
|
9 |
client = openai.OpenAI(api_key=openai.api_key)
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
# Function to generate exam questions using OpenAI API with retry logic
|
12 |
def generate_questions_with_retry(knowledge_material, question_type, cognitive_level, extra_instructions, case_based, num_choices=None, max_retries=3):
|
13 |
# Adjust the number of questions based on the type
|
|
|
39 |
retries = 0
|
40 |
while retries < max_retries:
|
41 |
try:
|
42 |
+
response = client.chat.completions.create(
|
43 |
+
model="gpt-4o-mini",
|
|
|
44 |
messages=[
|
45 |
{"role": "system", "content": "You are a helpful assistant for generating exam questions."},
|
46 |
{"role": "user", "content": prompt}
|
47 |
+
]
|
|
|
|
|
48 |
)
|
49 |
+
return response.choices[0].message.content
|
50 |
+
except openai.error.APIConnectionError:
|
51 |
retries += 1
|
52 |
time.sleep(2) # Wait for 2 seconds before retrying
|
|
|
53 |
if retries == max_retries:
|
54 |
st.error("Failed to connect to OpenAI API after several attempts.")
|
55 |
return None
|
56 |
+
|
57 |
+
# Login page
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
if 'username' not in st.session_state:
|
59 |
# Show the login form if the username is not set
|
60 |
st.title("Login")
|
|
|
66 |
else:
|
67 |
st.warning("Please enter a valid username.")
|
68 |
else:
|
69 |
+
# Main App after login
|
70 |
+
st.title(f"Welcome, {st.session_state['username']}! Generate your exam questions")
|
71 |
+
|
72 |
+
# Input field for knowledge material (text) with 3,000-word limit
|
73 |
+
knowledge_material = st.text_area("Enter knowledge material to generate exam questions:")
|
74 |
|
75 |
+
# Word count check
|
76 |
+
if len(knowledge_material.split()) > 3000:
|
77 |
+
st.warning("Please limit the knowledge material to 3,000 words or fewer.")
|
78 |
+
|
79 |
+
# File uploader for PDFs (limited to 5 MB)
|
80 |
+
uploaded_file = st.file_uploader("Upload a file (PDF)", type="pdf")
|
81 |
+
|
82 |
+
if uploaded_file is not None:
|
83 |
+
if uploaded_file.size > 5 * 1024 * 1024: # 5 MB limit
|
84 |
+
st.warning("File size exceeds 5 MB. Please upload a smaller file.")
|
|
|
|
|
|
|
|
|
85 |
else:
|
86 |
+
# Here you can add code to extract text from the PDF if needed
|
87 |
+
# For simplicity, we're focusing on the text input for now
|
88 |
+
st.success("File uploaded successfully! (Text extraction not implemented yet.)")
|
89 |
|
90 |
+
# Select question type
|
91 |
+
question_type = st.selectbox("Select question type:",
|
92 |
+
["Multiple Choice", "Fill in the Blank", "Open-ended", "True/False"])
|
|
|
|
|
|
|
93 |
|
94 |
+
# For multiple choice, let users select the number of choices
|
95 |
+
num_choices = None
|
96 |
+
if question_type == "Multiple Choice":
|
97 |
+
num_choices = st.selectbox("Select the number of choices for each question:", [3, 4, 5])
|
98 |
+
|
99 |
+
# Select cognitive level
|
100 |
+
cognitive_level = st.selectbox("Select cognitive level:",
|
101 |
+
["Recall", "Understanding", "Application", "Analysis", "Synthesis", "Evaluation"])
|
102 |
+
|
103 |
+
# Checkbox for Case-Based Medical Situations
|
104 |
+
case_based = st.checkbox("Generate case-based medical exam questions")
|
105 |
+
|
106 |
+
# Extra input field for additional instructions (placed below cognitive level)
|
107 |
+
extra_instructions = st.text_area("Enter additional instructions (e.g., how you want the questions to be phrased):")
|
108 |
+
|
109 |
+
# Generate questions button
|
110 |
+
if 'previous_questions' not in st.session_state:
|
111 |
+
st.session_state['previous_questions'] = []
|
112 |
+
|
113 |
+
if st.button("Generate Questions"):
|
114 |
+
if len(knowledge_material.split()) <= 3000:
|
115 |
+
# Generate questions with retry logic
|
116 |
+
questions = generate_questions_with_retry(
|
117 |
+
knowledge_material,
|
118 |
+
question_type,
|
119 |
+
cognitive_level,
|
120 |
+
extra_instructions,
|
121 |
+
case_based,
|
122 |
+
num_choices
|
123 |
+
)
|
124 |
|
125 |
+
if questions:
|
126 |
+
st.write("Generated Exam Questions:")
|
127 |
+
st.write(questions)
|
128 |
+
|
129 |
+
# Avoid showing repeated content in future requests
|
130 |
+
st.session_state['previous_questions'].append(questions)
|
131 |
+
|
132 |
+
# Option to download the questions as a text file
|
133 |
+
st.download_button(
|
134 |
+
label="Download Questions",
|
135 |
+
data=questions,
|
136 |
+
file_name='generated_questions.txt',
|
137 |
+
mime='text/plain'
|
138 |
+
)
|
139 |
+
else:
|
140 |
+
st.warning("Please reduce the word count to 3,000 or fewer.")
|
141 |
+
|
142 |
+
# Button to generate more questions based on the same material
|
143 |
+
if st.button("Generate More Questions"):
|
144 |
+
if len(knowledge_material.split()) <= 3000:
|
145 |
+
# Regenerate new questions, trying to avoid repeated content
|
146 |
+
questions = generate_questions_with_retry(
|
147 |
+
knowledge_material,
|
148 |
+
question_type,
|
149 |
+
cognitive_level,
|
150 |
+
extra_instructions,
|
151 |
+
case_based,
|
152 |
+
num_choices
|
153 |
+
)
|
154 |
+
|
155 |
+
# Check if the new set of questions is not the same as the previous set
|
156 |
+
if questions and questions not in st.session_state['previous_questions']:
|
157 |
+
st.write("Generated More Exam Questions:")
|
158 |
+
st.write(questions)
|
159 |
+
|
160 |
+
# Append the new questions to the session state
|
161 |
+
st.session_state['previous_questions'].append(questions)
|
162 |
+
|
163 |
+
# Option to download the new set of questions
|
164 |
+
st.download_button(
|
165 |
+
label="Download More Questions",
|
166 |
+
data=questions,
|
167 |
+
file_name='more_generated_questions.txt',
|
168 |
+
mime='text/plain'
|
169 |
+
)
|
170 |
+
else:
|
171 |
+
st.warning("New questions seem to overlap with the previous ones. Try adjusting the instructions.")
|
172 |
+
else:
|
173 |
+
st.warning("Please reduce the word count to 3,000 or fewer.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|