Spaces:
Sleeping
Sleeping
Merge pull request #12 from DishaKushwah/main
Browse files- app.py +7 -0
- mcq_generator.py +114 -149
- truefalse_quiz.py +91 -55
app.py
CHANGED
@@ -19,7 +19,10 @@ question_type = col1.selectbox("Question Type", ["Multiple Choice", "Short Answe
|
|
19 |
difficulty = col2.selectbox("Difficulty", ["easy", "medium", "hard"])
|
20 |
|
21 |
num_questions = st.slider("🔢 Number of Questions", min_value=1, max_value=10, value=3)
|
|
|
|
|
22 |
|
|
|
23 |
|
24 |
# Generate button
|
25 |
if st.button("⚡ Generate Quiz"):
|
@@ -82,3 +85,7 @@ if st.button("⚡ Generate Quiz"):
|
|
82 |
if questions:
|
83 |
st.download_button("⬇️ Download Quiz as PDF", output.getvalue(), file_name="quizcraft_quiz.pdf")
|
84 |
|
|
|
|
|
|
|
|
|
|
19 |
difficulty = col2.selectbox("Difficulty", ["easy", "medium", "hard"])
|
20 |
|
21 |
num_questions = st.slider("🔢 Number of Questions", min_value=1, max_value=10, value=3)
|
22 |
+
#<<<<<<< main
|
23 |
+
#=======
|
24 |
|
25 |
+
#>>>>>>> main
|
26 |
|
27 |
# Generate button
|
28 |
if st.button("⚡ Generate Quiz"):
|
|
|
85 |
if questions:
|
86 |
st.download_button("⬇️ Download Quiz as PDF", output.getvalue(), file_name="quizcraft_quiz.pdf")
|
87 |
|
88 |
+
#<<<<<<< main
|
89 |
+
|
90 |
+
#=======
|
91 |
+
#>>>>>>> main
|
mcq_generator.py
CHANGED
@@ -1,171 +1,136 @@
|
|
1 |
-
|
2 |
import random
|
3 |
import nltk
|
4 |
-
import
|
5 |
-
from
|
6 |
-
from
|
7 |
-
from sklearn.feature_extraction.text import TfidfVectorizer
|
8 |
-
from typing import List, Dict, Any
|
9 |
-
nltk.download('punkt')
|
10 |
-
nltk.download('stopwords')
|
11 |
|
12 |
class AdvancedMCQGenerator:
|
13 |
def __init__(self):
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
self.qa_pipeline = pipeline("question-answering"
|
19 |
-
self.
|
20 |
-
self.tfidf_vectorizer = TfidfVectorizer(stop_words='english', ngram_range=(1, 2))
|
21 |
-
self.sentence_tokenizer = nltk.sent_tokenize
|
22 |
-
self.generated_questions = set()
|
23 |
|
24 |
-
def
|
25 |
-
"""
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
|
40 |
-
def
|
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 |
-
distractors.append(distractor)
|
67 |
-
used_options.add(distractor.lower())
|
68 |
-
|
69 |
-
except Exception as e:
|
70 |
-
print(f"Distractor generation error: {e}")
|
71 |
-
distractors.append("A contextual detail")
|
72 |
-
return distractors[:num_distractors]
|
73 |
|
74 |
-
def
|
75 |
-
"""
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
-
def generate_mcq(self, context
|
88 |
-
"""
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
self.generated_questions.clear() # Reset generated questions
|
94 |
mcq_questions = []
|
|
|
95 |
|
96 |
-
for
|
97 |
try:
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
answer_result = self.qa_pipeline(question=question, context=context) # Extract answer using QA pipeline
|
104 |
-
correct_answer = answer_result['answer'] # Get correct answer
|
105 |
-
distractors = self._generate_smart_distractors(correct_answer, context_features) # Generate contextually relevant distractors
|
106 |
-
all_options = [correct_answer] + distractors # Combine options
|
107 |
random.shuffle(all_options)
|
108 |
-
correct_index = all_options.index(correct_answer)
|
109 |
-
mcq_questions.append({"question": question,"options": all_options,"correct_answer": correct_index
|
110 |
except Exception as e:
|
111 |
-
print(f"
|
112 |
return mcq_questions
|
113 |
-
|
114 |
-
def _preprocess_context(self, context: str) -> str:
|
115 |
-
"""Advanced context preprocessing"""
|
116 |
-
context = re.sub(r'\s+', ' ', context).strip() # Remove extra whitespaces and special characters
|
117 |
-
context = ''.join(char for char in context if char.isprintable()) # Remove non-printable characters
|
118 |
-
if len(context.split()) < 20: # Append context if too short
|
119 |
-
context += " Additional context to enhance question generation."
|
120 |
-
return context
|
121 |
-
|
122 |
def main():
|
|
|
123 |
generator = AdvancedMCQGenerator()
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
try:
|
128 |
-
num_questions = int(input("\n>> How many questions do you want to generate? "))
|
129 |
-
break
|
130 |
-
except ValueError:
|
131 |
-
print("Please enter a valid number.")
|
132 |
-
|
133 |
-
while True:
|
134 |
-
difficulty = input("\n>> Enter difficulty level (easy/medium/hard): ").lower()
|
135 |
-
if difficulty in ['easy', 'medium', 'hard']:
|
136 |
-
break
|
137 |
-
print("Invalid difficulty level. Please choose easy, medium, or hard.")
|
138 |
-
questions = generator.generate_mcq(context, num_questions, difficulty)
|
139 |
-
if questions:
|
140 |
-
print("\n--- Multiple Choice Quiz ---")
|
141 |
-
correct_answers = 0 # Simple score tracking
|
142 |
-
total_questions = len(questions)
|
143 |
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
if user_input in ['A', 'B', 'C', 'D']:
|
153 |
-
break
|
154 |
-
print("Invalid input. Please enter A, B, C, or D.")
|
155 |
-
|
156 |
-
user_answer_index = ord(user_input) - 65
|
157 |
-
if user_answer_index == q['correct_answer']:
|
158 |
-
print("✅ Correct!")
|
159 |
-
correct_answers += 1
|
160 |
-
else:
|
161 |
-
print(f"❌ Incorrect. Correct Answer: {chr(65+q['correct_answer'])}")
|
162 |
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
|
169 |
if __name__ == "__main__":
|
170 |
main()
|
171 |
-
# SAMPLE CONTEXT- The French Revolution began in 1789 and marked a significant turning point in European history. It was fueled by widespread social inequality, financial crisis, and the rise of Enlightenment ideas. The French monarchy was overthrown, and King Louis XVI was executed. The revolution introduced the ideals of liberty, equality, and fraternity. It led to the rise of Napoleon Bonaparte and had a lasting impact on modern democracy and human rights movements around the world.
|
|
|
1 |
+
|
2 |
import random
|
3 |
import nltk
|
4 |
+
from nltk.corpus import stopwords
|
5 |
+
from nltk.tokenize import sent_tokenize, word_tokenize
|
6 |
+
from transformers import pipeline
|
|
|
|
|
|
|
|
|
7 |
|
8 |
class AdvancedMCQGenerator:
|
9 |
def __init__(self):
|
10 |
+
nltk.download('punkt', quiet=True)
|
11 |
+
nltk.download('stopwords', quiet=True)
|
12 |
+
|
13 |
+
# Initialize NLP models
|
14 |
+
self.qa_pipeline = pipeline("question-answering")
|
15 |
+
self.stop_words = set(stopwords.words('english'))
|
|
|
|
|
|
|
16 |
|
17 |
+
def extract_key_concepts(self, context):
|
18 |
+
"""Extract key concepts and important phrases"""
|
19 |
+
# Tokenize sentences
|
20 |
+
sentences = sent_tokenize(context)
|
21 |
+
|
22 |
+
# Extract potential key concepts
|
23 |
+
key_concepts = []
|
24 |
+
for sentence in sentences:
|
25 |
+
# Look for sentences with unique, meaningful content
|
26 |
+
words = word_tokenize(sentence)
|
27 |
+
filtered_words = [word.lower() for word in words if word.isalnum() and word.lower() not in self.stop_words and len(word) > 2]
|
28 |
+
# Prioritize sentences with named entities or specific concepts
|
29 |
+
if len(filtered_words) > 3:
|
30 |
+
key_concepts.append(sentence)
|
31 |
+
return key_concepts[:5] # Return top 5 key concepts
|
32 |
|
33 |
+
def generate_intelligent_question(self, concept, context, difficulty):
|
34 |
+
if difficulty == 'easy':
|
35 |
+
templates = [
|
36 |
+
f"What is {concept}?",
|
37 |
+
f"Describe {concept}.",
|
38 |
+
f"Define {concept}.",
|
39 |
+
f"What do you understand by {concept}?",
|
40 |
+
f"Give a simple explanation of {concept}."
|
41 |
+
]
|
42 |
+
elif difficulty == 'hard':
|
43 |
+
templates = [
|
44 |
+
f"In what way does {concept} reflect a broader implication?",
|
45 |
+
f"Critically analyze the role of {concept} in the given context.",
|
46 |
+
f"How can {concept} be interpreted in complex scenarios?",
|
47 |
+
f"What deeper insights does {concept} provide?",
|
48 |
+
f"Discuss the nuanced impact of {concept} in this context."
|
49 |
+
]
|
50 |
+
else: # medium
|
51 |
+
templates = [
|
52 |
+
f"What is the primary significance of {concept}?",
|
53 |
+
f"How does {concept} impact the broader context?",
|
54 |
+
f"What key role does {concept} play in the narrative?",
|
55 |
+
f"Explain the importance of {concept} in this context.",
|
56 |
+
f"What makes {concept} crucial to understanding the situation?"
|
57 |
+
]
|
58 |
+
return random.choice(templates)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
+
def generate_contextual_distractors(self, correct_answer, context, difficulty):
|
61 |
+
"""Create semantically related but incorrect distractors"""
|
62 |
+
sentences = sent_tokenize(context)
|
63 |
+
distractors = []
|
64 |
+
potential_distractors = [sent for sent in sentences if correct_answer.lower() not in sent.lower() and len(sent.split()) > 3]
|
65 |
+
fallback_distractors = ["A partially related historical context","An alternative interpretation","A peripheral aspect of the main theme"]
|
66 |
+
# Generating diverse distractors
|
67 |
+
while len(distractors) < 3:
|
68 |
+
if potential_distractors:
|
69 |
+
distractor = random.choice(potential_distractors)
|
70 |
+
potential_distractors.remove(distractor)
|
71 |
+
words = word_tokenize(distractor)
|
72 |
+
if difficulty == 'easy':
|
73 |
+
phrase = ' '.join([w for w in words if w.lower() not in self.stop_words][:2])
|
74 |
+
elif difficulty == 'hard':
|
75 |
+
phrase = ' '.join([w for w in words if w.lower() not in self.stop_words][:5])
|
76 |
+
else: # medium
|
77 |
+
phrase = ' '.join([w for w in words if w.lower() not in self.stop_words][:3])
|
78 |
+
distractors.append(phrase.strip())
|
79 |
+
else:
|
80 |
+
distractors.append(random.choice(fallback_distractors))
|
81 |
+
return distractors
|
82 |
|
83 |
+
def generate_mcq(self, context, num_questions=3, difficulty='medium'):
|
84 |
+
"""Generate Multiple Choice Questions"""
|
85 |
+
# Validate context
|
86 |
+
if not context or len(context.split()) < 30:
|
87 |
+
raise ValueError("Context is too short. Provide more detailed text.")
|
88 |
+
|
|
|
89 |
mcq_questions = []
|
90 |
+
key_concepts = self.extract_key_concepts(context)
|
91 |
|
92 |
+
for concept in key_concepts[:num_questions]:
|
93 |
try:
|
94 |
+
question = self.generate_intelligent_question(concept, context, difficulty)
|
95 |
+
answer_result = self.qa_pipeline(question=question, context=context)
|
96 |
+
correct_answer = answer_result['answer']
|
97 |
+
distractors = self.generate_contextual_distractors(correct_answer, context, difficulty)
|
98 |
+
all_options = [correct_answer] + distractors
|
|
|
|
|
|
|
|
|
99 |
random.shuffle(all_options)
|
100 |
+
correct_index = all_options.index(correct_answer) # Determine correct option index
|
101 |
+
mcq_questions.append({"question": question,"options": all_options,"correct_answer": correct_index}) # Create MCQ
|
102 |
except Exception as e:
|
103 |
+
print(f"Error generating question: {e}")
|
104 |
return mcq_questions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
def main():
|
106 |
+
# Create generator instance
|
107 |
generator = AdvancedMCQGenerator()
|
108 |
+
context = input("Enter context text: ")
|
109 |
+
num_questions = int(input("How many questions do you want? "))
|
110 |
+
difficulty = input("Choose difficulty (easy / medium / hard): ").lower().strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
|
112 |
+
questions = generator.generate_mcq(context, num_questions, difficulty)
|
113 |
+
# Display and solve quiz
|
114 |
+
print("\n--- Quiz Started ---")
|
115 |
+
score = 0
|
116 |
+
for i, q in enumerate(questions, 1):
|
117 |
+
print(f"\nQuestion {i}: {q['question']}")
|
118 |
+
for j, option in enumerate(q['options']):
|
119 |
+
print(f"{chr(65+j)}. {option}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
|
121 |
+
while True:
|
122 |
+
user_answer = input("\nYour Answer (A/B/C/D): ").upper()
|
123 |
+
if user_answer in ['A', 'B', 'C', 'D']:
|
124 |
+
break
|
125 |
+
print("Invalid input. Please enter A, B, C, or D.")
|
126 |
+
|
127 |
+
user_index = ord(user_answer) - 65
|
128 |
+
if user_index == q['correct_answer']:
|
129 |
+
print("Correct!")
|
130 |
+
score += 1
|
131 |
+
else:
|
132 |
+
print(f"Incorrect. Correct answer was: {chr(65 + q['correct_answer'])}")
|
133 |
+
print(f"\nFinal Score: {score}/{len(questions)}")
|
134 |
|
135 |
if __name__ == "__main__":
|
136 |
main()
|
|
truefalse_quiz.py
CHANGED
@@ -2,66 +2,102 @@ import random
|
|
2 |
import nltk
|
3 |
from transformers import pipeline
|
4 |
from nltk.tokenize import sent_tokenize
|
5 |
-
|
6 |
-
# Download required tokenizer
|
7 |
-
nltk.download('punkt', quiet=True)
|
8 |
-
|
9 |
# Load NLI model
|
10 |
nli = pipeline("text-classification", model="facebook/bart-large-mnli")
|
11 |
|
12 |
-
|
13 |
-
def
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
raise ValueError("Difficulty must be 'easy', 'medium', or 'hard'.")
|
21 |
-
return sentences
|
22 |
|
23 |
-
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
return sentence
|
27 |
-
elif level == "medium":
|
28 |
-
if "Sun" in sentence:
|
29 |
-
return sentence.replace("Sun", "Moon")
|
30 |
-
return sentence.replace(" is ", " is not ") if " is " in sentence else sentence
|
31 |
-
elif level == "hard":
|
32 |
-
if "eight" in sentence:
|
33 |
-
return sentence.replace("eight", "ten")
|
34 |
-
return sentence.replace("planets", "stars") if "planets" in sentence else sentence
|
35 |
-
return sentence
|
36 |
|
37 |
-
# Statement generator
|
38 |
-
def generate_statements(context, n, difficulty, sentences):
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
|
51 |
-
#
|
52 |
-
def
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
|
61 |
-
|
62 |
-
|
63 |
-
nli_result = nli(input_text)[0]
|
64 |
-
label = "ENTAILMENT" if nli_result["label"] == "entailment" else "CONTRADICTION"
|
65 |
-
result.append((statement, label))
|
66 |
-
|
67 |
-
return result
|
|
|
2 |
import nltk
|
3 |
from transformers import pipeline
|
4 |
from nltk.tokenize import sent_tokenize
|
5 |
+
nltk.download('punkt_tab', quiet=True)
|
|
|
|
|
|
|
6 |
# Load NLI model
|
7 |
nli = pipeline("text-classification", model="facebook/bart-large-mnli")
|
8 |
|
9 |
+
class generate_true_false:
|
10 |
+
def __init__(self):
|
11 |
+
pass
|
12 |
+
def validate_inputs(self, context, num_questions, difficulty):
|
13 |
+
if not context.strip():
|
14 |
+
raise ValueError("Context cannot be empty.")
|
15 |
+
sentences = sent_tokenize(context)
|
16 |
+
return sentences
|
|
|
|
|
17 |
|
18 |
+
def apply_noise(self, sentence: str, level: str) -> str:
|
19 |
+
if level == "easy":
|
20 |
+
return sentence
|
21 |
+
elif level == "medium":
|
22 |
+
if "Sun" in sentence:
|
23 |
+
return sentence.replace("Sun", "Moon")
|
24 |
+
return sentence.replace("is", "is not") if "is" in sentence else sentence
|
25 |
+
elif level == "hard":
|
26 |
+
if "eight" in sentence:
|
27 |
+
return sentence.replace("eight", "ten")
|
28 |
+
return sentence.replace("planets", "stars") if "planets" in sentence else sentence
|
29 |
return sentence
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
|
31 |
+
# Statement generator
|
32 |
+
def generate_statements(self, context, n, difficulty, sentences):
|
33 |
+
random.seed(42)
|
34 |
+
selected = random.sample(sentences, min(n * 2, len(sentences)))
|
35 |
+
final = []
|
36 |
+
for s in selected:
|
37 |
+
clean = s.strip()
|
38 |
+
modified = self.apply_noise(clean, difficulty)
|
39 |
+
label = "ENTAILMENT" if clean == modified else "CONTRADICTION"
|
40 |
+
final.append((modified, label))
|
41 |
+
if len(final) >= n:
|
42 |
+
break
|
43 |
+
return final
|
44 |
+
|
45 |
+
# Get valid user answer
|
46 |
+
def get_user_answer(self):
|
47 |
+
while True:
|
48 |
+
user = input("True or False? ").strip().lower()
|
49 |
+
if user in ["true", "false"]:
|
50 |
+
return user
|
51 |
+
print("Please enter 'true' or 'false'.")
|
52 |
|
53 |
+
# Main quiz logic
|
54 |
+
def run_quiz(self, context, num_questions, difficulty):
|
55 |
+
try:
|
56 |
+
sentences = self.validate_inputs(context, num_questions, difficulty)
|
57 |
+
questions = self.generate_statements(context, num_questions, difficulty, sentences)
|
58 |
+
|
59 |
+
print("\n--- QUIZ STARTS ---\n")
|
60 |
+
score = 0
|
61 |
+
|
62 |
+
for idx, (statement, actual_label) in enumerate(questions, 1):
|
63 |
+
print(f"Q{idx}: {statement}")
|
64 |
+
user = self.get_user_answer()
|
65 |
+
|
66 |
+
# Format input for facebook/bart-large-mnli
|
67 |
+
input_text = f"{context} [SEP] {statement}"
|
68 |
+
result = nli(input_text)[0]
|
69 |
+
if result["label"] == "neutral":
|
70 |
+
print("Skipping ambiguous statement.\n")
|
71 |
+
continue
|
72 |
+
model_label = "ENTAILMENT" if result["label"] == "entailment" else "CONTRADICTION"
|
73 |
+
|
74 |
+
if model_label == "ENTAILMENT" and user == "true":
|
75 |
+
print("Correct!\n")
|
76 |
+
score += 1
|
77 |
+
elif model_label == "CONTRADICTION" and user == "false":
|
78 |
+
print("Correct!\n")
|
79 |
+
score += 1
|
80 |
+
else:
|
81 |
+
print(f"Incorrect! (Correct answer: {'True' if model_label == 'ENTAILMENT' else 'False'})\n")
|
82 |
+
print(f"\n--- Final Score: {score}/{len(questions)} ---")
|
83 |
+
|
84 |
+
except ValueError as e:
|
85 |
+
print(f"Error: {e}")
|
86 |
+
|
87 |
+
def main():
|
88 |
+
context = input(">> Enter context text: ").strip()
|
89 |
+
try:
|
90 |
+
num_questions = int(input(">> How many questions do you want to generate? ").strip())
|
91 |
+
except ValueError:
|
92 |
+
print("Please enter a valid integer for number of questions.")
|
93 |
+
return
|
94 |
+
|
95 |
+
difficulty = input(">> Enter difficulty level (easy / medium / hard): ").strip().lower()
|
96 |
+
if difficulty not in ["easy", "medium", "hard"]:
|
97 |
+
print("Invalid difficulty level.")
|
98 |
+
return
|
99 |
+
quiz_generator = generate_true_false()
|
100 |
+
quiz_generator.run_quiz(context, num_questions, difficulty)
|
101 |
|
102 |
+
if __name__ == "__main__":
|
103 |
+
main()
|
|
|
|
|
|
|
|
|
|