├── .gitignore ├── logo.png ├── pages ├── gen.jpg ├── ZAbout.py ├── Quiz.py └── Quiz Result.py ├── requirements.txt ├── README.md └── Home.py /.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | venv 3 | streamlit 4 | .streamlit 5 | pdf 6 | .env -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunsw1999/q-wizard/HEAD/logo.png -------------------------------------------------------------------------------- /pages/gen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunsw1999/q-wizard/HEAD/pages/gen.jpg -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit 2 | python-dotenv 3 | openai==0.28 4 | google-generativeai -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MCQ Question Generator App - QWizard 2 | 3 | This is a **MCQ Question Generator App** that allows you to generate multiple-choice questions based on a given topic. You can: 4 | 5 | ✅ Enter a topic of your choice 6 | ✅ Select the difficulty level 7 | ✅ Choose the number of questions you need 8 | ✅ Get evaluated on your answers with an explanation and score 9 | 10 | The app uses **Streamlit** for the web interface and **OpenAI** for generating questions and explanations. 11 | 12 | --- 13 | 14 | ## 🌐 Access the App 15 | 16 | You can visit the app using the following link: 17 | [QWizard - MCQ Question Generator](https://qwizard.streamlit.app) 18 | 19 | --- 20 | -------------------------------------------------------------------------------- /pages/ZAbout.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.sidebar.image("logo.png", width=800) 4 | st.subheader("What is this app?") 5 | st.write("This web app allows users to generate custom quizzes based on their chosen topic, difficulty level, and the number of questions. After completing the quiz, the app automatically grades the responses and provides a score, making it a convenient tool for learning and self-assessment.") 6 | st.subheader("Who am I?") 7 | st.image("pages/gen.jpg") 8 | st.write("Hi, I'm :blue[Nipun Weerasinghe], a Computer Science undergraduate at the University of Jaffna. I’m passionate about technology, especially :green[machine learning, AI, and software development]. I’ve worked on a variety of projects, including chatbots and applications involving PDF embedding. I'm skilled in Python, Streamlit, and AI tools, and I enjoy tackling innovative challenges and exploring new solutions.") 9 | st.subheader("Contact me") 10 | st.page_link("https://www.linkedin.com/in/nipunweerasinghe?utm_source=share&utm_campaign=share_via&utm_content=profile&utm_medium=android_app", label=":blue[Linkedin]") -------------------------------------------------------------------------------- /pages/Quiz.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import json 3 | 4 | if 'stat' not in st.session_state: 5 | st.session_state['stat'] = 0 6 | 7 | if st.session_state['stat'] == 0: 8 | st.subheader("Create quiz first") 9 | st.page_link("Home.py", label=":red[Click here]", icon="👉") 10 | 11 | elif st.session_state['stat'] == 1: 12 | if 'quiz' not in st.session_state: 13 | st.session_state['quiz'] = "" 14 | # Parse quiz_data correctly 15 | st.page_link("Home.py", label=":green[Wanna customize again? Click here]") 16 | if isinstance(st.session_state['quiz'], str): 17 | quiz_data = json.loads(st.session_state['quiz']) 18 | else: 19 | quiz_data = st.session_state['quiz'] 20 | 21 | 22 | # Initialize session state for answers 23 | if 'given_answers' not in st.session_state: 24 | st.session_state.given_answers = {} 25 | 26 | # Function to display a question 27 | def show_question(id, question, answers, correct_answers, explanation, status): 28 | with st.container(border=True): 29 | st.write(f":blue[({id}). {question}]") 30 | selected_answers = [] 31 | 32 | if status == "YES": 33 | for i, answer in enumerate(answers, start=1): 34 | st.write(f"({i}). {answer}") 35 | else: 36 | for i, answer in enumerate(answers, start=1): 37 | if st.checkbox(f"({i}). {answer}", key=f"{id}_{answer}"): 38 | selected_answers.append(answer) 39 | 40 | # Display explanation and correct answers after submission (optional, controlled elsewhere) 41 | return { 42 | "selected": selected_answers, 43 | "correct_answers": correct_answers, 44 | "explanation": explanation, 45 | } 46 | 47 | 48 | for id, data in quiz_data.items(): 49 | result = show_question( 50 | id, 51 | data["question"], 52 | data["answers"], 53 | data["correct_answers"], 54 | data["explanation"], 55 | data["status"] 56 | ) 57 | st.session_state.given_answers[id] = result 58 | 59 | if st.button("Submit"): 60 | st.session_state['stat'] = 2 61 | st.toast(":green[Quiz Completed!]🎉🎉🎉") 62 | st.page_link("pages/Quiz Result.py", label=":red[Check answers]", icon="👉") 63 | 64 | else: 65 | st.subheader("You completed the quiz!") 66 | st.page_link("pages/Quiz Result.py", label=":red[Check marks]", icon="👉") -------------------------------------------------------------------------------- /pages/Quiz Result.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import json 3 | 4 | if 'stat' not in st.session_state: 5 | st.session_state['stat'] = 0 6 | 7 | 8 | def get_grade_and_message(percentage): 9 | if percentage >= 90: 10 | grade = "A+" 11 | message = "Excellent work! You're a true expert." 12 | elif percentage >= 85: 13 | grade = "A" 14 | message = "Great job! You nailed it!" 15 | elif percentage >= 80: 16 | grade = "A-" 17 | message = "Fantastic effort! You're almost at the top." 18 | elif percentage >= 75: 19 | grade = "B+" 20 | message = "Good work! Keep it up." 21 | elif percentage >= 70: 22 | grade = "B" 23 | message = "Nice job! You're doing well." 24 | elif percentage >= 65: 25 | grade = "B-" 26 | message = "Not bad! A little more effort will go a long way." 27 | elif percentage >= 60: 28 | grade = "C+" 29 | message = "Fair attempt! Keep pushing." 30 | elif percentage >= 55: 31 | grade = "C" 32 | message = "You passed, but there's room for improvement." 33 | elif percentage >= 50: 34 | grade = "C-" 35 | message = "Just made it! Aim higher next time." 36 | elif percentage >= 45: 37 | grade = "D+" 38 | message = "Below average. Focus on improving." 39 | elif percentage >= 40: 40 | grade = "D" 41 | message = "Needs improvement. Don't give up!" 42 | elif percentage >= 35: 43 | grade = "D-" 44 | message = "Struggling. Time to hit the books." 45 | else: 46 | grade = "E" 47 | message = "Poor performance. Let's work on this together." 48 | return grade, message 49 | 50 | if st.session_state['stat'] == 0: 51 | st.subheader("Create quiz first") 52 | st.page_link("Home.py", label=":red[Click here]", icon="👉") 53 | 54 | elif st.session_state['stat'] == 1: 55 | st.subheader("Complete quiz first") 56 | st.page_link("pages/Quiz.py", label=":red[Click here]", icon="👉") 57 | 58 | else: 59 | if 'quiz' not in st.session_state: 60 | st.session_state['quiz'] = "" 61 | 62 | if isinstance(st.session_state['quiz'], str): 63 | quiz_data = json.loads(st.session_state['quiz']) 64 | else: 65 | quiz_data = st.session_state['quiz'] 66 | 67 | if 'given_answers' not in st.session_state: 68 | st.session_state.given_answers = {} 69 | st.subheader("Submitted Answers:") 70 | total = 0 71 | grand_total = 0 72 | for id, result in st.session_state.given_answers.items(): 73 | with st.container(border=True): 74 | st.write(f":blue[({id}). {quiz_data[str(id)]['question']}]") # Ensure id is treated as a string 75 | wrong_answers = 0 76 | correct_answers = 0 77 | weight = float(len(result['correct_answers'])) 78 | for i, answer in enumerate(quiz_data[str(id)]['answers'], start=1): 79 | if answer in result['selected']: 80 | if answer in result['correct_answers']: 81 | correct_answers += 1 82 | st.write(f":green[({i}). {answer} ✓]") 83 | else: 84 | wrong_answers += 1 85 | st.write(f":red[({i}). {answer} ✗]") 86 | else: 87 | if answer in result['correct_answers']: 88 | st.write(f":green[({i}). {answer}]") 89 | else: 90 | st.write(f"({i}). {answer}") 91 | st.write(f"Explanation: {result['explanation']}") 92 | marks = correct_answers - (wrong_answers * 0.5) 93 | st.write(f":orange[Marks: {marks}/{weight}]") 94 | total += marks 95 | grand_total += weight 96 | if total > 0: 97 | percentage = total * 100 / grand_total 98 | else: 99 | percentage = 0 100 | grade, message = get_grade_and_message(percentage) 101 | if percentage > 50: 102 | st.header(f":green[Full marks: {percentage:.2f}%]") 103 | st.subheader(f":green[Grade: {grade}] 🎉") 104 | st.write(f":violet[{message}]") 105 | else: 106 | st.header(f":red[Full marks: {percentage:.2f}%]") 107 | st.subheader(f":red[Grade: {grade}] 😕") 108 | st.write(f":violet[{message}]") 109 | st.divider() 110 | st.page_link("Home.py", label=":green[Try Again]", icon="✍️") 111 | -------------------------------------------------------------------------------- /Home.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import os 3 | from dotenv import load_dotenv 4 | import openai 5 | import time 6 | import json 7 | 8 | st.sidebar.image("logo.png", width=800) 9 | 10 | if 'quiz' not in st.session_state: 11 | st.session_state['quiz'] = "" 12 | # Load environment variables 13 | load_dotenv() 14 | 15 | if 'stat' not in st.session_state: 16 | st.session_state['stat'] = 0 17 | 18 | openai_api_key = os.getenv("OPENAI_API_KEY") 19 | # openai_api_key = os.getenv("OPENAI_API_KEY") 20 | # openai.api_key = openai_api_key 21 | # os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY") 22 | 23 | def check_possibility(text): 24 | with st.spinner("Checking..."): 25 | prompt = f""" 26 | Analyze the following text and determine if it is possible to generate meaningful questions based on it. 27 | 28 | ### Criteria: 29 | 1. If the text is random characters, gibberish, or completely unrelated topics, respond with "NO." 30 | 2. If the text is a person’s name: 31 | - Respond with "NO" if the person is unknown or lacks information available on the internet. 32 | - Respond with "YES" if the person is famous or well-documented online. 33 | 3. If the text is the name of an app: 34 | - Respond with "NO" if the app is unknown or not widely recognized. 35 | - Respond with "YES" if the app is popular or has notable information available online. 36 | 4. Otherwise, if the text is a valid topic, keyword, or sentence suitable for generating meaningful questions, respond with "YES." 37 | 38 | Text: {text} 39 | """ 40 | 41 | 42 | 43 | response = openai.ChatCompletion.create( 44 | model="gpt-3.5-turbo", 45 | messages=[{"role": "user", "content": prompt}], 46 | temperature=0, 47 | ) 48 | # st.write(response.choices[0].message['content']) 49 | return response.choices[0].message['content'] 50 | 51 | def generate_questions_from_text(text, question_count,per): 52 | prompt = f""" 53 | You are an intelligent person to generate questions. Your answers and correct answers must be correct 100% and meaningful. Generate questions dificultally level should {per}/3. 54 | Generate {question_count} multiple-choice questions from the following text. 55 | Format the output as JSON with the structure: 56 | {{ 57 | "1": {{ 58 | "question": "What is the question?", 59 | "answers": [ 60 | "Option 1", 61 | "Option 2", 62 | "Option 3", 63 | "Option 4" 64 | ], 65 | "correct_answers": ["Option 1", "Option 3"], # Include one or more correct answers 66 | "explanation": "Explanation of the correct answers", 67 | "status": "NO" # Always set to 'NO' initially 68 | }}, 69 | "2": {{ 70 | ... 71 | }}, 72 | ... 73 | }} 74 | 75 | There are one or more correct answers. Few questions should have multiple correct answers. Do not provide anything else except the JSON. Your topic is 76 | topic: {text} 77 | """ 78 | response = openai.ChatCompletion.create( 79 | model="gpt-3.5-turbo", 80 | messages=[{"role": "user", "content": prompt}], 81 | temperature=1, 82 | ) 83 | content = response.choices[0].message['content'] 84 | # Parse JSON response safely 85 | try: 86 | parsed_json = json.loads(content) 87 | return parsed_json 88 | except json.JSONDecodeError: 89 | st.error("Failed to parse the response as valid JSON. Please try again.") 90 | return None 91 | 92 | def inf(text:str,seconds:int,done_display_time: int = 2): 93 | done_message = st.empty() 94 | done_message.info(text, icon="ℹ️") 95 | time.sleep(seconds) 96 | time.sleep(done_display_time) 97 | done_message.empty() 98 | 99 | def completed(text:str,done_display_time:int = 2): 100 | done_message = st.empty() 101 | done_message.success(text, icon="✅") 102 | time.sleep(done_display_time) 103 | done_message.empty() 104 | 105 | st.title("Q-Wizard") 106 | 107 | if st.session_state['stat'] == 0: 108 | 109 | st.subheader("Customize Your Quiz") 110 | difficulty_level = st.select_slider( 111 | "Choose Your Challenge Mode", 112 | ["Easy", "Intermediate", "Hard"], 113 | key="difficulty_level_slider" 114 | ) 115 | 116 | questions = st.select_slider( 117 | "Choose Number of Questions", 118 | [5,10, 15, 20,25], 119 | key="questions_slider" 120 | ) 121 | 122 | 123 | topic = st.text_area("Enter a topic (Tip: Use a clear and specific topic for the best results!)") 124 | 125 | start = st.button("Generate Quiz") 126 | 127 | per = 1 128 | if start: 129 | if difficulty_level == "Easy": per = 1 130 | elif difficulty_level == "Intermediate": per = 2 131 | elif difficulty_level == "Hard": per = 3 132 | if topic == "": 133 | inf("Please enter a topic to continue",2) 134 | else: 135 | if check_possibility(topic)=="NO": 136 | inf("Sorry, we cannot generate a quiz on this topic. Please try another one.",2) 137 | else: 138 | with st.spinner("Generating..."): 139 | st.session_state['quiz'] = (generate_questions_from_text(topic,questions,per)) 140 | st.session_state['stat'] = 1 141 | completed("Quiz Generated") 142 | st.page_link("pages/Quiz.py", label=":green[Start]", icon="✍️") 143 | 144 | else: 145 | if st.session_state['stat'] == 2: 146 | reset = st.button(":red[Restart new quiz]") 147 | if reset: 148 | st.session_state['stat'] = 0 149 | st.toast("Refresh the page...🔄") 150 | elif st.session_state['stat'] == 1: 151 | st.page_link("pages/Quiz.py", label=":green[Back to quiz]", icon="✍️") 152 | reset = st.button(":red[Give up and Restart new quiz]") 153 | if reset: 154 | st.session_state['stat'] = 0 155 | st.toast("Refresh the page...🔄") 156 | --------------------------------------------------------------------------------