File size: 8,470 Bytes
dbd52a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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
191
192
193
194
195
196
197
198
199
200
import streamlit as st
import openai
from openai import OpenAI
import time
import gspread
from oauth2client.service_account import ServiceAccountCredentials

# Set up OpenAI client
client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"])

# Google Sheets setup remains the same
scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
creds = ServiceAccountCredentials.from_json_keyfile_name("genexam-2c8c645ecc0d.json", scope)
client_gs = gspread.authorize(creds)
sheet = client_gs.open("GeneXam user").sheet1

def check_user_in_sheet(username):
    try:
        users_list = sheet.col_values(1)
        if username in users_list:
            return True
        return False
    except Exception as e:
        st.error(f"Error checking user: {str(e)}")
        return False

def update_api_usage(username):
    try:
        users_list = sheet.col_values(1)
        row_number = users_list.index(username) + 1
        api_usage = int(sheet.cell(row_number, 2).value)
        api_usage += 1
        sheet.update_cell(row_number, 2, api_usage)
    except Exception as e:
        st.error(f"Error updating API usage: {str(e)}")

def generate_questions_with_retry(knowledge_material, question_type, cognitive_level, extra_instructions, case_based, num_choices=None, max_retries=3):
    # Adjust number of questions based on type
    if question_type == "Multiple Choice":
        num_questions = 3
    elif question_type == "Fill in the Blank":
        num_questions = 10
    elif question_type == "True/False":
        num_questions = 5
    else:  # Open-ended
        num_questions = 3

    # Base prompt
    prompt = f"Generate {num_questions} {question_type.lower()} exam questions based on {cognitive_level.lower()} level from the following material: {knowledge_material}. {extra_instructions}"

    # Modify prompt for case-based medical situations
    if case_based:
        prompt = f"Generate {num_questions} {question_type.lower()} exam questions based on {cognitive_level.lower()} level from the following medical material: {knowledge_material}. The questions should be based on case-based medical situations, such as patient scenarios. {extra_instructions}"
        if question_type != "Fill in the Blank":
            prompt += " Provide answers with short explanations."

    # Add specific handling for Multiple Choice and True/False
    if question_type == "Multiple Choice" and num_choices:
        prompt += f" Each multiple choice question should have {num_choices} choices."

    if question_type == "True/False":
        prompt += " Provide short explanations for each question based on the given material, without stating True or False explicitly."

    retries = 0
    while retries < max_retries:
        try:
            response = client.chat.completions.create(
                model="gpt-4o-mini",  # or your preferred model
                messages=[
                    {"role": "system", "content": "You are a helpful assistant for generating exam questions."},
                    {"role": "user", "content": prompt}
                ]
            )
            return response.choices[0].message.content
        except Exception as e:
            retries += 1
            if retries == max_retries:
                st.error(f"Failed to generate questions after {max_retries} attempts. Error: {str(e)}")
                return None
            time.sleep(2)  # Wait before retrying

# ระบบ login
if 'username' not in st.session_state:
    st.title("Login")
    username_input = st.text_input("Enter your username:")
    if st.button("Login"):
        if username_input:
            if check_user_in_sheet(username_input):
                st.session_state['username'] = username_input
                st.success(f"Welcome, {username_input}!")
                update_api_usage(username_input)
            else:
                st.warning("Username not found. Please try again.")
        else:
            st.warning("Please enter a valid username.")
else:
    # Main App after login
    st.title(f"Welcome, {st.session_state['username']}! Generate your exam questions")

    # Input field for knowledge material (text) with 3,000-word limit
    knowledge_material = st.text_area("Enter knowledge material to generate exam questions:")
    
    # Word count check
    if len(knowledge_material.split()) > 3000:
        st.warning("Please limit the knowledge material to 3,000 words or fewer.")

    # File uploader for PDFs (limited to 5 MB)
    uploaded_file = st.file_uploader("Upload a file (PDF)", type="pdf")
    
    if uploaded_file is not None:
        if uploaded_file.size > 5 * 1024 * 1024:  # 5 MB limit
            st.warning("File size exceeds 5 MB. Please upload a smaller file.")
        else:
            st.success("File uploaded successfully! (Text extraction not implemented yet.)")

    # Select question type
    question_type = st.selectbox("Select question type:", 
                                 ["Multiple Choice", "Fill in the Blank", "Open-ended", "True/False"])

    # For multiple choice, let users select the number of choices
    num_choices = None
    if question_type == "Multiple Choice":
        num_choices = st.selectbox("Select the number of choices for each question:", [3, 4, 5])

    # Select cognitive level
    cognitive_level = st.selectbox("Select cognitive level:", 
                                   ["Recall", "Understanding", "Application", "Analysis", "Synthesis", "Evaluation"])

    # Checkbox for Case-Based Medical Situations
    case_based = st.checkbox("Generate case-based medical exam questions")

    # Extra input field for additional instructions (placed below cognitive level)
    extra_instructions = st.text_area("Enter additional instructions (e.g., how you want the questions to be phrased):")

    # Generate questions button
    if 'previous_questions' not in st.session_state:
        st.session_state['previous_questions'] = []

    if st.button("Generate Questions"):
        if len(knowledge_material.split()) <= 3000:
            # Generate questions with retry logic
            questions = generate_questions_with_retry(
                knowledge_material, 
                question_type, 
                cognitive_level, 
                extra_instructions, 
                case_based, 
                num_choices
            )
            
            if questions:
                st.write("Generated Exam Questions:")
                st.write(questions)

                # Avoid showing repeated content in future requests
                st.session_state['previous_questions'].append(questions)

                # Option to download the questions as a text file
                st.download_button(
                    label="Download Questions",
                    data=questions,
                    file_name='generated_questions.txt',
                    mime='text/plain'
                )
        else:
            st.warning("Please reduce the word count to 3,000 or fewer.")

    # Button to generate more questions based on the same material
    if st.button("Generate More Questions"):
        if len(knowledge_material.split()) <= 3000:
            # Regenerate new questions, trying to avoid repeated content
            questions = generate_questions_with_retry(
                knowledge_material, 
                question_type, 
                cognitive_level, 
                extra_instructions, 
                case_based, 
                num_choices
            )

            # Check if the new set of questions is not the same as the previous set
            if questions and questions not in st.session_state['previous_questions']:
                st.write("Generated More Exam Questions:")
                st.write(questions)

                # Append the new questions to the session state
                st.session_state['previous_questions'].append(questions)

                # Option to download the new set of questions
                st.download_button(
                    label="Download More Questions",
                    data=questions,
                    file_name='more_generated_questions.txt',
                    mime='text/plain'
                )
            else:
                st.warning("New questions seem to overlap with the previous ones. Try adjusting the instructions.")
        else:
            st.warning("Please reduce the word count to 3,000 or fewer.")