import streamlit as st import openai from openai import OpenAI import time import gspread from oauth2client.service_account import ServiceAccountCredentials import PyPDF2 import io from datetime import datetime from PIL import Image # Add some custom CSS to improve the layout st.markdown(""" """, unsafe_allow_html=True) def show_documentation(): with st.expander("📚 How to Use GeneExam | āļ§āļīāļ˜āļĩāļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ GeneExam"): st.markdown(""" # Program Usage Guide | āļ„āļđāđˆāļĄāļ·āļ­āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™āđ‚āļ›āļĢāđāļāļĢāļĄ ## English Instructions 1. **Input Your Content** - Use the text input box to paste your teaching material - Or upload a PDF file (maximum 10,000 words) 2. **Select Question Type** - Multiple Choice: Generate questions with 3-5 options - Fill in the Blank: Create completion questions - True/False: Generate true/false statements - Open-ended: Create essay-type questions with scoring criteria 3. **Choose Cognitive Level** - Select the appropriate cognitive level (see explanation below) - This determines the complexity and depth of questions 4. **Additional Options** - Case-based: Toggle for medical case scenarios - Extra instructions: Add specific requirements 5. **Generate and Download** - Click "Generate Questions" to create your exam - Use the download button to save your questions ## āļ„āļģāđāļ™āļ°āļ™āļģāļ āļēāļĐāļēāđ„āļ—āļĒ 1. **āļāļēāļĢāđƒāļŠāđˆāđ€āļ™āļ·āđ‰āļ­āļŦāļē** - āļžāļīāļĄāļžāđŒāļŦāļĢāļ·āļ­āļ§āļēāļ‡āđ€āļ™āļ·āđ‰āļ­āļŦāļēāđƒāļ™āļŠāđˆāļ­āļ‡āļ‚āđ‰āļ­āļ„āļ§āļēāļĄ - āļŦāļĢāļ·āļ­āļ­āļąāļžāđ‚āļŦāļĨāļ”āđ„āļŸāļĨāđŒ PDF (āđ„āļĄāđˆāđ€āļāļīāļ™ 10,000 āļ„āļģ) 2. **āđ€āļĨāļ·āļ­āļāļ›āļĢāļ°āđ€āļ āļ—āļ„āļģāļ–āļēāļĄ** - āļ›āļĢāļ™āļąāļĒ: āļŠāļĢāđ‰āļēāļ‡āļ„āļģāļ–āļēāļĄāļžāļĢāđ‰āļ­āļĄāļ•āļąāļ§āđ€āļĨāļ·āļ­āļ 3-5 āļ‚āđ‰āļ­ - āđ€āļ•āļīāļĄāļ„āļģ: āļŠāļĢāđ‰āļēāļ‡āļ„āļģāļ–āļēāļĄāđāļšāļšāđ€āļ•āļīāļĄāļ„āļģāđƒāļ™āļŠāđˆāļ­āļ‡āļ§āđˆāļēāļ‡ - āļ–āļđāļ/āļœāļīāļ”: āļŠāļĢāđ‰āļēāļ‡āļ„āļģāļ–āļēāļĄāđāļšāļšāļ–āļđāļāļœāļīāļ” - āļ­āļąāļ•āļ™āļąāļĒ: āļŠāļĢāđ‰āļēāļ‡āļ„āļģāļ–āļēāļĄāđāļšāļšāļšāļĢāļĢāļĒāļēāļĒāļžāļĢāđ‰āļ­āļĄāđ€āļāļ“āļ‘āđŒāļāļēāļĢāđƒāļŦāđ‰āļ„āļ°āđāļ™āļ™ 3. **āđ€āļĨāļ·āļ­āļāļĢāļ°āļ”āļąāļšāļ„āļ§āļēāļĄāļ„āļīāļ”** - āđ€āļĨāļ·āļ­āļāļĢāļ°āļ”āļąāļšāļ„āļ§āļēāļĄāļ„āļīāļ” (āļ”āļđāļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļ”āđ‰āļēāļ™āļĨāđˆāļēāļ‡) - āļāļģāļŦāļ™āļ”āļ„āļ§āļēāļĄāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āđāļĨāļ°āļ„āļ§āļēāļĄāļĨāļķāļāļ‚āļ­āļ‡āļ„āļģāļ–āļēāļĄ 4. **āļ•āļąāļ§āđ€āļĨāļ·āļ­āļāđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄ** - āļ„āļģāļ–āļēāļĄāđ€āļŠāļīāļ‡āļāļĢāļ“āļĩāļĻāļķāļāļĐāļē: āđ€āļĨāļ·āļ­āļāļŠāļģāļŦāļĢāļąāļšāđ‚āļˆāļ—āļĒāđŒāļ—āļēāļ‡āļāļēāļĢāđāļžāļ—āļĒāđŒ - āļ„āļģāđāļ™āļ°āļ™āļģāđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄ: āđƒāļŠāđˆāļ„āļ§āļēāļĄāļ•āđ‰āļ­āļ‡āļāļēāļĢāđ€āļ‰āļžāļēāļ° 5. **āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āļ”āļēāļ§āļ™āđŒāđ‚āļŦāļĨāļ”** - āļ„āļĨāļīāļ "Generate Questions" āđ€āļžāļ·āđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļ‚āđ‰āļ­āļŠāļ­āļš - āđƒāļŠāđ‰āļ›āļļāđˆāļĄāļ”āļēāļ§āļ™āđŒāđ‚āļŦāļĨāļ”āđ€āļžāļ·āđˆāļ­āļšāļąāļ™āļ—āļķāļāļ‚āđ‰āļ­āļŠāļ­āļš """) with st.expander("🧠 Cognitive Levels Explanation | āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļĢāļ°āļ”āļąāļšāļ„āļ§āļēāļĄāļ„āļīāļ”"): st.markdown(""" # Cognitive Levels Guide | āļ„āļđāđˆāļĄāļ·āļ­āļĢāļ°āļ”āļąāļšāļ„āļ§āļēāļĄāļ„āļīāļ” ## English Explanation ### 1. Recall (Knowledge) - **Definition**: Basic recall of information - **Key Words**: Define, List, Name, Identify - **Example**: What is the capital of Thailand? ### 2. Understanding (Comprehension) - **Definition**: Understanding and explaining ideas - **Key Words**: Explain, Describe, Summarize - **Example**: Explain how photosynthesis works. ### 3. Application - **Definition**: Using information in new situations - **Key Words**: Apply, Use, Solve, Demonstrate - **Example**: Calculate the dosage for a 70kg patient. ### 4. Analysis - **Definition**: Breaking information into parts - **Key Words**: Analyze, Compare, Contrast, Examine - **Example**: Compare and contrast viral and bacterial infections. ### 5. Synthesis - **Definition**: Creating new ideas or solutions - **Key Words**: Create, Design, Develop, Plan - **Example**: Design a treatment plan for a diabetic patient. ### 6. Evaluation - **Definition**: Making judgments based on criteria - **Key Words**: Evaluate, Judge, Assess, Recommend - **Example**: Evaluate the effectiveness of this treatment approach. ## āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļ āļēāļĐāļēāđ„āļ—āļĒ ### 1. āļāļēāļĢāļˆāļģ (Recall) - **āļ„āļ§āļēāļĄāļŦāļĄāļēāļĒ**: āļāļēāļĢāļĢāļ°āļĨāļķāļāļ‚āđ‰āļ­āļĄāļđāļĨāļžāļ·āđ‰āļ™āļāļēāļ™ - **āļ„āļģāļŠāļģāļ„āļąāļ**: āļšāļ­āļ, āļĢāļ°āļšāļļ, āļˆāļģāđāļ™āļ, āļĢāļ°āļĨāļķāļ - **āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡**: āđ€āļĄāļ·āļ­āļ‡āļŦāļĨāļ§āļ‡āļ‚āļ­āļ‡āļ›āļĢāļ°āđ€āļ—āļĻāđ„āļ—āļĒāļ„āļ·āļ­āļ­āļ°āđ„āļĢ? ### 2. āļ„āļ§āļēāļĄāđ€āļ‚āđ‰āļēāđƒāļˆ (Understanding) - **āļ„āļ§āļēāļĄāļŦāļĄāļēāļĒ**: āđ€āļ‚āđ‰āļēāđƒāļˆāđāļĨāļ°āļ­āļ˜āļīāļšāļēāļĒāđāļ™āļ§āļ„āļīāļ” - **āļ„āļģāļŠāļģāļ„āļąāļ**: āļ­āļ˜āļīāļšāļēāļĒ, āļšāļĢāļĢāļĒāļēāļĒ, āļŠāļĢāļļāļ› - **āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡**: āļ­āļ˜āļīāļšāļēāļĒāļāļĢāļ°āļšāļ§āļ™āļāļēāļĢāļŠāļąāļ‡āđ€āļ„āļĢāļēāļ°āļŦāđŒāđāļŠāļ‡ ### 3. āļāļēāļĢāļ›āļĢāļ°āļĒāļļāļāļ•āđŒāđƒāļŠāđ‰ (Application) - **āļ„āļ§āļēāļĄāļŦāļĄāļēāļĒ**: āđƒāļŠāđ‰āļ‚āđ‰āļ­āļĄāļđāļĨāđƒāļ™āļŠāļ–āļēāļ™āļāļēāļĢāļ“āđŒāđƒāļŦāļĄāđˆ - **āļ„āļģāļŠāļģāļ„āļąāļ**: āđƒāļŠāđ‰, āđāļāđ‰āļ›āļąāļāļŦāļē, āļŠāļēāļ˜āļīāļ• - **āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡**: āļ„āļģāļ™āļ§āļ“āļ‚āļ™āļēāļ”āļĒāļēāļŠāļģāļŦāļĢāļąāļšāļœāļđāđ‰āļ›āđˆāļ§āļĒāļ™āđ‰āļģāļŦāļ™āļąāļ 70 āļāļīāđ‚āļĨāļāļĢāļąāļĄ ### 4. āļāļēāļĢāļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒ (Analysis) - **āļ„āļ§āļēāļĄāļŦāļĄāļēāļĒ**: āđāļĒāļāđāļĒāļ°āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ›āđ‡āļ™āļŠāđˆāļ§āļ™āđ† - **āļ„āļģāļŠāļģāļ„āļąāļ**: āļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒ, āđ€āļ›āļĢāļĩāļĒāļšāđ€āļ—āļĩāļĒāļš, āļˆāļģāđāļ™āļ - **āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡**: āđ€āļ›āļĢāļĩāļĒāļšāđ€āļ—āļĩāļĒāļšāļāļēāļĢāļ•āļīāļ”āđ€āļŠāļ·āđ‰āļ­āđ„āļ§āļĢāļąāļŠāđāļĨāļ°āđāļšāļ„āļ—āļĩāđ€āļĢāļĩāļĒ ### 5. āļāļēāļĢāļŠāļąāļ‡āđ€āļ„āļĢāļēāļ°āļŦāđŒ (Synthesis) - **āļ„āļ§āļēāļĄāļŦāļĄāļēāļĒ**: āļŠāļĢāđ‰āļēāļ‡āđāļ™āļ§āļ„āļīāļ”āļŦāļĢāļ·āļ­āļ§āļīāļ˜āļĩāļāļēāļĢāđƒāļŦāļĄāđˆ - **āļ„āļģāļŠāļģāļ„āļąāļ**: āļŠāļĢāđ‰āļēāļ‡, āļ­āļ­āļāđāļšāļš, āļžāļąāļ’āļ™āļē, āļ§āļēāļ‡āđāļœāļ™ - **āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡**: āļ­āļ­āļāđāļšāļšāđāļœāļ™āļāļēāļĢāļĢāļąāļāļĐāļēāļŠāļģāļŦāļĢāļąāļšāļœāļđāđ‰āļ›āđˆāļ§āļĒāđ€āļšāļēāļŦāļ§āļēāļ™ ### 6. āļāļēāļĢāļ›āļĢāļ°āđ€āļĄāļīāļ™āļœāļĨ (Evaluation) - **āļ„āļ§āļēāļĄāļŦāļĄāļēāļĒ**: āļ•āļąāļ”āļŠāļīāļ™āđƒāļˆāļšāļ™āļžāļ·āđ‰āļ™āļāļēāļ™āļ‚āļ­āļ‡āđ€āļāļ“āļ‘āđŒ - **āļ„āļģāļŠāļģāļ„āļąāļ**: āļ›āļĢāļ°āđ€āļĄāļīāļ™, āļ•āļąāļ”āļŠāļīāļ™, āļ§āļąāļ”āļœāļĨ, āđāļ™āļ°āļ™āļģ - **āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡**: āļ›āļĢāļ°āđ€āļĄāļīāļ™āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļžāļ‚āļ­āļ‡āļ§āļīāļ˜āļĩāļāļēāļĢāļĢāļąāļāļĐāļēāļ™āļĩāđ‰ """) # Constants WORD_LIMIT = 11000 DAILY_API_LIMIT = 30 # Set your desired limit per user per day # Set up OpenAI client client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"]) # Google Sheets setup 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): """Check if user exists in sheet""" try: users_list = sheet.col_values(1) # UserID column if username in users_list: return True return False except Exception as e: st.error(f"Error checking user: {str(e)}") return False def get_user_stats(username): """Get user's current API usage statistics""" try: users_list = sheet.col_values(1) # UserID column row_number = users_list.index(username) + 1 daily_count = int(sheet.cell(row_number, 2).value) # DailyAPICount total_count = int(sheet.cell(row_number, 3).value) # TotalAPICount last_used = sheet.cell(row_number, 4).value # LastUsedDate return { 'daily_count': daily_count, 'total_count': total_count, 'last_used': last_used } except Exception as e: st.error(f"Error getting user stats: {str(e)}") return None def update_api_usage(username): """Update both daily and total API usage counts""" try: users_list = sheet.col_values(1) row_number = users_list.index(username) + 1 today = datetime.now().strftime('%Y-%m-%d') # Get current values stats = get_user_stats(username) if not stats: return False, "Error retrieving user statistics" # Reset daily count if it's a new day daily_count = stats['daily_count'] if stats['last_used'] != today: daily_count = 0 # Check daily limit if daily_count >= DAILY_API_LIMIT: return False, f"You have reached your daily limit of {DAILY_API_LIMIT} generations. Please try again tomorrow." # Update counts new_daily_count = daily_count + 1 new_total_count = stats['total_count'] + 1 # Update all values in sheet sheet.update_cell(row_number, 2, new_daily_count) # Update DailyAPICount sheet.update_cell(row_number, 3, new_total_count) # Update TotalAPICount sheet.update_cell(row_number, 4, today) # Update LastUsedDate return True, None except Exception as e: return False, f"Error updating API usage: {str(e)}" def extract_text_from_pdf(pdf_file): """Simple PDF text extraction with word limit check""" try: pdf_reader = PyPDF2.PdfReader(io.BytesIO(pdf_file.read())) text_content = "" for page in pdf_reader.pages: text_content += page.extract_text() + "\n" word_count = len(text_content.split()) if word_count > WORD_LIMIT: return None, f"PDF content exceeds {WORD_LIMIT:,} words (contains {word_count:,} words). Please use a shorter document." return text_content, None except Exception as e: return None, f"Error processing PDF: {str(e)}" def generate_questions_with_retry(username, knowledge_material, question_type, cognitive_level, extra_instructions, case_based, num_choices=None, max_retries=3): """Generate questions and update API usage""" # Check and update API usage before generating can_generate, error_message = update_api_usage(username) if not can_generate: st.error(error_message) return None # Adjust number of questions based on type if question_type == "Multiple Choice": num_questions = 3 format_instructions = f""" For each multiple choice question: 1. Present the question clearly, ending with '?' 2. Leave one blank line after the question 3. Present choices as: A) [choice] B) [choice] C) [choice] {f"D) [choice]" if num_choices > 3 else ""} {f"E) [choice]" if num_choices > 4 else ""} 4. After all questions, provide an ANSWER KEY section with: - The correct answer letter for each question - A brief explanation of why this is the correct answer Example format: 1. Your question text here? A) First choice B) Second choice C) Third choice """ elif question_type == "Fill in the Blank": num_questions = 10 format_instructions = """ For each fill-in-the-blank question: 1. Present the question with a clear blank space indicated by _____ 2. After all questions, provide an ANSWER KEY section with: - The correct answer for each blank - A brief explanation of why this answer is correct - Any alternative acceptable answers if applicable """ elif question_type == "True/False": num_questions = 5 format_instructions = """ For each true/false question: 1. Present the statement clearly 2. After all questions, provide an ANSWER KEY section with: - Whether the statement is True or False - A detailed explanation of why the statement is true or false - The specific part of the source material that supports this answer """ else: # Open-ended num_questions = 3 format_instructions = """ For each open-ended question: 1. Present the question clearly 2. After all questions, provide an ANSWER KEY section with: - A structured scoring checklist of key points (minimum 3-5 points per question) - Each key point should be worth a specific number of marks - Total marks available for each question - Sample answer that would receive full marks - Common points that students might miss """ # Base prompt prompt = f"""Generate {num_questions} {question_type.lower()} exam questions based on {cognitive_level.lower()} level from the following material: {knowledge_material} {format_instructions} {extra_instructions} Please format the output clearly with: 1. Questions section (numbered 1, 2, 3, etc.) 2. Answer Key section (clearly separated from questions) 3. Each answer should include explanation for better understanding Make sure all questions and answers are directly related to the provided material.""" # Modify prompt for case-based medical situations if case_based: prompt = f"""Generate {num_questions} {question_type.lower()} case-based medical exam questions based on {cognitive_level.lower()} level. Use this material as the medical knowledge base: {knowledge_material} Each question should: 1. Start with a medical case scenario/patient presentation 2. Include relevant clinical details 3. Ask about diagnosis, treatment, or management 4. Be at {cognitive_level.lower()} cognitive level {format_instructions} {extra_instructions} Please format the output with: 1. Cases and Questions (numbered 1, 2, 3, etc.) 2. Detailed Answer Key section including: - Correct answers - Clinical reasoning - Key diagnostic or treatment considerations - Common pitfalls to avoid""" retries = 0 while retries < max_retries: try: response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": "You are an expert exam question generator with deep knowledge in medical education. Create clear, well-structured questions with detailed answer keys and explanations."}, {"role": "user", "content": prompt} ], temperature=0.7, max_tokens=3000 # Increased to accommodate answers and explanations ) return response.choices[0].message.content except Exception as e: retries += 1 st.warning(f"Attempt {retries} failed. Retrying... Error: {str(e)}") if retries == max_retries: st.error(f"Failed to generate questions after {max_retries} attempts. Error: {str(e)}") return None time.sleep(2) # Main Streamlit interface # Initialize session state variables if 'login_step' not in st.session_state: st.session_state.login_step = 'username' if 'username' not in st.session_state: st.session_state.username = None # Login system if st.session_state.username is None: # Center align the content col1, col2, col3 = st.columns([1,2,1]) with col2: # Display logo st.image("GenExam.png", width=200) # Assuming the image is saved as logo.png st.title("Login") username_input = st.text_input("Enter your username:") if st.session_state.login_step == 'username' and st.button("Login", use_container_width=True): if username_input: if check_user_in_sheet(username_input): stats = get_user_stats(username_input) if stats: st.success(f"Welcome, {username_input}! 👋") st.info(f""" 📊 Your API Usage Statistics: - Today's Usage: {stats['daily_count']}/{DAILY_API_LIMIT} generations - Total All-Time Usage: {stats['total_count']} generations """) st.session_state.login_step = 'enter_app' else: st.warning("Username not found. Please try again.") else: st.warning("Please enter a valid username.") if st.session_state.login_step == 'enter_app': if st.button("ðŸŽŊ Enter GeneXam Application", use_container_width=True): st.session_state.username = username_input st.rerun() # Show instructions if st.session_state.login_step == 'username': st.markdown(""" ### How to Login: 1. Enter your username and click 'Login' to verify your account 2. After verification, click 'Enter GeneXam Application' to start using the system """) else: # Main application code (āļŠāđˆāļ§āļ™āļ—āļĩāđˆāđ€āļŦāļĨāļ·āļ­āđ€āļŦāļĄāļ·āļ­āļ™āđ€āļ”āļīāļĄ) st.title(f"Welcome to GeneXam, {st.session_state.username}! 🎓") # Add Help button in sidebar # <-- āđ€āļžāļīāđˆāļĄāļŠāđˆāļ§āļ™āļ™āļĩāđ‰āļ•āļĢāļ‡āļ™āļĩāđ‰ with st.sidebar: st.markdown("### Need Help? | āļ•āđ‰āļ­āļ‡āļāļēāļĢāļ„āļ§āļēāļĄāļŠāđˆāļ§āļĒāđ€āļŦāļĨāļ·āļ­?") if st.button("📖 Show Documentation | āđāļŠāļ”āļ‡āļ„āļđāđˆāļĄāļ·āļ­āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™"): show_documentation() # Show current usage stats stats = get_user_stats(st.session_state.username) if stats: remaining = DAILY_API_LIMIT - stats['daily_count'] st.info(f""" 📊 Usage Statistics: - Daily Generations Remaining: {remaining}/{DAILY_API_LIMIT} - Total All-Time Generations: {stats['total_count']} """) # Create tabs for input methods tab1, tab2 = st.tabs(["Text Input", "PDF Upload"]) with tab1: knowledge_material = st.text_area("Enter knowledge material to generate exam questions:") word_count = len(knowledge_material.split()) if word_count > WORD_LIMIT: st.error(f"Text exceeds {WORD_LIMIT:,} words. Please shorten your content.") with tab2: st.info(f"Maximum content length: {WORD_LIMIT:,} words") uploaded_file = st.file_uploader("Upload a PDF file", type="pdf") if uploaded_file is not None: pdf_content, error = extract_text_from_pdf(uploaded_file) if error: st.error(error) else: st.success("PDF processed successfully!") knowledge_material = pdf_content # Question generation options col1, col2 = st.columns(2) with col1: question_type = st.selectbox( "Select question type:", ["Multiple Choice", "Fill in the Blank", "Open-ended", "True/False"] ) if question_type == "Multiple Choice": num_choices = st.selectbox("Select number of choices:", [3, 4, 5]) cognitive_level = st.selectbox( "Select cognitive level:", ["Recall", "Understanding", "Application", "Analysis", "Synthesis", "Evaluation"] ) with col2: case_based = st.checkbox("Generate case-based medical exam questions") extra_instructions = st.text_area("Additional instructions (optional):") # Generate questions button if st.button("Generate Questions"): if 'knowledge_material' in locals() and knowledge_material.strip(): with st.spinner("Generating questions..."): questions = generate_questions_with_retry( st.session_state['username'], knowledge_material, question_type, cognitive_level, extra_instructions, case_based, num_choices if question_type == "Multiple Choice" else None ) if questions: st.write("### Generated Exam Questions:") st.write(questions) # Update displayed stats after generation new_stats = get_user_stats(st.session_state['username']) if new_stats: remaining = DAILY_API_LIMIT - new_stats['daily_count'] st.info(f""" 📊 Updated Usage Statistics: - Daily Generations Remaining: {remaining}/{DAILY_API_LIMIT} - Total All-Time Generations: {new_stats['total_count']} """) # Download button st.download_button( label="Download Questions", data=questions, file_name='generated_questions.txt', mime='text/plain' ) else: st.warning("Please enter knowledge material or upload a PDF file first.")