aditizyy commited on
Commit
c2573cf
·
2 Parent(s): 8c77551 d03b12b

Merge pull request #12 from DishaKushwah/main

Browse files
Files changed (3) hide show
  1. app.py +7 -0
  2. mcq_generator.py +114 -149
  3. 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
- import torch
2
  import random
3
  import nltk
4
- import re
5
- from transformers import (pipeline, AutoModelForQuestionAnswering, AutoTokenizer)
6
- from sentence_transformers import SentenceTransformer
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
- """Advanced Multiple Choice Question Generator with Intelligent Distractor Strategy"""
15
- # Question Answering Model
16
- qa_model_name = "deepset/roberta-base-squad2"
17
- self.qa_tokenizer = AutoTokenizer.from_pretrained(qa_model_name)
18
- self.qa_pipeline = pipeline("question-answering", model=qa_model_name,device=0 if torch.cuda.is_available() else -1)
19
- self.sentence_embedder = SentenceTransformer('all-mpnet-base-v2')
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 _extract_context_features(self, context: str) -> Dict[str, Any]:
25
- """Advanced context feature extraction"""
26
- sentences = self.sentence_tokenizer(context)
27
- try:
28
- tfidf_matrix = self.tfidf_vectorizer.fit_transform(sentences)
29
- feature_names = self.tfidf_vectorizer.get_feature_names_out()
30
- top_keywords = []
31
- for i, sentence in enumerate(sentences):
32
- feature_indices = tfidf_matrix[i].nonzero()[1] # Get top TF-IDF scores for each sentence
33
- top_sentence_keywords = [feature_names[idx] for idx in feature_indices][:3]
34
- top_keywords.extend(top_sentence_keywords)
35
- return {'sentences': sentences,'keywords': list(set(top_keywords)),'total_sentences': len(sentences)}
36
- except Exception as e:
37
- print(f"Context feature extraction error: {e}")
38
- return {'sentences': sentences,'keywords': context.split()[:10],'total_sentences': len(sentences)}
39
 
40
- def _generate_smart_distractors(self, correct_answer: str, context_features: Dict[str, Any], num_distractors: int = 3) -> List[str]:
41
- """Intelligent Distractor Generation Strategy"""
42
- distractors = []
43
- used_options = set([correct_answer.lower()])
44
- sentences = context_features['sentences']
45
- keywords = context_features['keywords']
46
- # Semantic similarity-based distractor generation
47
- for _ in range(num_distractors):
48
- try:
49
- semantic_candidates = [sent for sent in sentences if sent.lower() not in used_options and len(sent.split()) > 3]
50
- if semantic_candidates:
51
- candidate_similarities = [(sent, self._calculate_semantic_similarity(correct_answer, sent)) for sent in semantic_candidates]
52
- candidate_similarities.sort(key=lambda x: abs(0.5 - x[1]))
53
- if candidate_similarities:
54
- best_distractor = candidate_similarities[0][0]
55
- distractors.append(best_distractor)
56
- used_options.add(best_distractor.lower())
57
- continue
58
-
59
- if keywords:
60
- keyword_distractor = f"A key aspect related to {random.choice(keywords)}"
61
- distractors.append(keyword_distractor)
62
- used_options.add(keyword_distractor.lower())
63
- continue
64
- fallback_distractors = ["A related contextual detail","An alternative interpretation","A supplementary concept"]
65
- distractor = random.choice(fallback_distractors)
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 _calculate_semantic_similarity(self, text1: str, text2: str) -> float:
75
- """Calculate semantic similarity between two texts"""
76
- try:
77
- # Embed texts
78
- embedding1 = self.sentence_embedder.encode(text1)
79
- embedding2 = self.sentence_embedder.encode(text2)
80
-
81
- # Calculate cosine similarity
82
- similarity = torch.nn.functional.cosine_similarity(torch.tensor(embedding1), torch.tensor(embedding2)).item()
83
- return abs(similarity)
84
- except Exception:
85
- return 0.0
 
 
 
 
 
 
 
 
 
 
86
 
87
- def generate_mcq(self, context: str, num_questions: int = 5, difficulty: str = "medium") -> List[Dict[str, Any]]:
88
- """
89
- Generate Multiple Choice Questions
90
- """
91
- context = self._preprocess_context(context) # Preprocess context
92
- context_features = self._extract_context_features(context) # Extract context features
93
- self.generated_questions.clear() # Reset generated questions
94
  mcq_questions = []
 
95
 
96
- for _ in range(num_questions):
97
  try:
98
- keywords = context_features['keywords']
99
- subject = random.choice(keywords)
100
- # Question templates
101
- templates = [f"What is the significance of {subject} in this context?",f"Explain the role of {subject}.",f"How does {subject} contribute to the overall understanding?"]
102
- question = random.choice(templates)
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) # Determine correct option index
109
- mcq_questions.append({"question": question,"options": all_options,"correct_answer": correct_index,"explanation": f"Correct answer based on the context: {correct_answer}"})
110
  except Exception as e:
111
- print(f"MCQ generation error: {e}")
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
- print(" -------------Multiple Choice Question Generator-------------")
125
- context = input("\n>> Enter context text: ")
126
- while True:
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
- for i, q in enumerate(questions, 1):
145
- print(f"\nQuestion {i}: {q['question']}")
146
- print("Options:")
147
- for j, option in enumerate(q['options']):
148
- print(f"{chr(65+j)}. {option}")
149
-
150
- while True:
151
- user_input = input("\nYour Answer (A/B/C/D): ").upper()
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
- # Simple score display
164
- print(f"\n-----Score: {correct_answers}/{total_questions}-----")
165
-
166
- else:
167
- print("\nNo multiple choice questions were generated.")
 
 
 
 
 
 
 
 
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
- # Input validation
13
- def validate_inputs(context, num_questions, difficulty):
14
- if not context.strip():
15
- raise ValueError("Context cannot be empty.")
16
- sentences = sent_tokenize(context)
17
- if len(sentences) < num_questions:
18
- raise ValueError(f"Context has only {len(sentences)} sentences, but {num_questions} questions requested.")
19
- if difficulty not in ["easy", "medium", "hard"]:
20
- raise ValueError("Difficulty must be 'easy', 'medium', or 'hard'.")
21
- return sentences
22
 
23
- # Difficulty-based sentence modifier
24
- def apply_noise(sentence: str, level: str) -> str:
25
- if level == "easy":
 
 
 
 
 
 
 
 
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
- random.seed(42)
40
- selected = random.sample(sentences, min(n * 2, len(sentences)))
41
- final = []
42
- for s in selected:
43
- clean = s.strip()
44
- modified = apply_noise(clean, difficulty)
45
- label = "ENTAILMENT" if clean == modified else "CONTRADICTION"
46
- final.append((modified, label))
47
- if len(final) >= n:
48
- break
49
- return final
 
 
 
 
 
 
 
 
50
 
51
- # MAIN BACKEND FUNCTION
52
- def generate_true_false(context, num_questions, difficulty):
53
- """
54
- Returns a list of (statement, label) pairs for Streamlit frontend.
55
- label: 'ENTAILMENT' (True) or 'CONTRADICTION' (False)
56
- """
57
- sentences = validate_inputs(context, num_questions, difficulty)
58
- questions = generate_statements(context, num_questions, difficulty, sentences)
59
- result = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
- for statement, _ in questions:
62
- input_text = f"{context} [SEP] {statement}"
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()