# import ast # from pymongo import MongoClient # from datetime import datetime # import openai # import google.generativeai as genai # from google.generativeai import GenerativeModel # from dotenv import load_dotenv # import os # from file_upload_vectorize import resources_collection, vectors_collection, courses_collection2, faculty_collection # # Load environment variables # load_dotenv() # MONGO_URI = os.getenv('MONGO_URI') # OPENAI_KEY = os.getenv('OPENAI_KEY') # GEMINI_KEY = os.getenv('GEMINI_KEY') # # Configure APIs # openai.api_key = OPENAI_KEY # genai.configure(api_key=GEMINI_KEY) # model = genai.GenerativeModel('gemini-pro') # # Connect to MongoDB # client = MongoClient(MONGO_URI) # db = client['novascholar_db'] # quizzes_collection = db["quizzes"] # def strip_code_markers(response_text): # """Strip off the markers ``` and python from a LLM model's response""" # if response_text.startswith("```python"): # response_text = response_text[len("```python"):].strip() # if response_text.startswith("```"): # response_text = response_text[len("```"):].strip() # if response_text.endswith("```"): # response_text = response_text[:-len("```")].strip() # return response_text # # New function to generate MCQs using Gemini # def generate_mcqs(context, num_questions, session_title, session_description): # """Generate MCQs either from context or session details""" # try: # # Initialize Gemini model # if context: # prompt = f""" # Based on the following content, generate {num_questions} multiple choice questions. # Format each question as a Python dictionary with the following structure: # {{ # "question": "Question text here", # "options": ["A) option1", "B) option2", "C) option3", "D) option4"], # "correct_option": "A) option1" or "B) option2" or "C) option3" or "D) option4" # }} # Content: # {context} # Generate challenging but clear questions that test understanding of key concepts. # Return only the Python list of dictionaries. # """ # else: # prompt = f""" # Generate {num_questions} multiple choice questions about the topic: # Title: {session_title} # Description: {session_description} # Format each question as a Python dictionary with the following structure: # {{ # "question": "Question text here", # "options": ["A) option1", "B) option2", "C) option3", "D) option4"], # "correct_option": "A" or "B" or "C" or "D" # }} # Generate challenging but clear questions. # Return only the Python list of dictionaries without any additional formatting or markers # Do not write any other text, do not start the response with (```python), do not end the response with backticks(```) # A Sample response should look like this: Response Text: [ # { # "question": "Which of the following is NOT a valid data type in C++?", # "options": ["int", "double", "boolean", "char"], # "correct_option": "C" # } # ] (Notice that there are no backticks(```) around the response and no (```python)) # . # """ # response = model.generate_content(prompt) # response_text = response.text.strip() # print("Response Text:", response_text) # modified_response_text = strip_code_markers(response_text) # print("Response Text Modified to:", modified_response_text) # # Extract and parse the response to get the list of MCQs # mcqs = ast.literal_eval(modified_response_text) # Be careful with eval, consider using ast.literal_eval for production # print(mcqs) # if not mcqs: # raise ValueError("No questions generated") # return mcqs # except Exception as e: # print(f"Error generating MCQs: , error: {e}") # return None # # New function to save quiz to database # def save_quiz(course_id, session_id, title, questions, user_id): # """Save quiz to database""" # try: # quiz_data = { # "user_id": user_id, # "course_id": course_id, # "session_id": session_id, # "title": title, # "questions": questions, # "created_at": datetime.utcnow(), # "status": "active", # "submissions": [] # } # result = quizzes_collection.insert_one(quiz_data) # return result.inserted_id # except Exception as e: # print(f"Error saving quiz: {e}") # return None # def get_student_quiz_score(quiz_id, student_id): # """Get student's score for a specific quiz""" # quiz = quizzes_collection.find_one( # { # "_id": quiz_id, # "submissions.student_id": student_id # }, # {"submissions.$": 1} # ) # if quiz and quiz.get('submissions'): # return quiz['submissions'][0].get('score') # return None # # def submit_quiz_answers(quiz_id, student_id, student_answers): # # """Submit and score student's quiz answers""" # # quiz = quizzes_collection.find_one({"_id": quiz_id}) # # if not quiz: # # return None # # # Calculate score # # correct_answers = 0 # # total_questions = len(quiz['questions']) # # for q_idx, question in enumerate(quiz['questions']): # # if student_answers.get(str(q_idx)) == question['correct_option']: # # correct_answers += 1 # # score = (correct_answers / total_questions) * 100 # # # Store submission # # submission_data = { # # "student_id": student_id, # # "answers": student_answers, # # "score": score, # # "submitted_at": datetime.utcnow() # # } # # # Update quiz with submission # # quizzes_collection.update_one( # # {"_id": quiz_id}, # # { # # "$push": {"submissions": submission_data} # # } # # ) # # return score # def submit_quiz_answers(quiz_id, student_id, student_answers): # """Submit and score student's quiz answers""" # try: # quiz = quizzes_collection.find_one({"_id": quiz_id}) # if not quiz: # return None # # Calculate score # correct_answers = 0 # total_questions = len(quiz['questions']) # for q_idx, question in enumerate(quiz['questions']): # student_answer = student_answers.get(str(q_idx)) # if student_answer: # Only check if answer was provided # # Extract the option letter (A, B, C, D) from the full answer string # answer_letter = student_answer.split(')')[0].strip() # if answer_letter == question['correct_option']: # correct_answers += 1 # score = (correct_answers / total_questions) * 100 # # Store submission # submission_data = { # "student_id": student_id, # "answers": student_answers, # "score": score, # "submitted_at": datetime.utcnow() # } # # Update quiz with submission # result = quizzes_collection.update_one( # {"_id": quiz_id}, # {"$push": {"submissions": submission_data}} # ) # return score if result.modified_count > 0 else None # except Exception as e: # print(f"Error submitting quiz: {e}") # return None import ast from typing import Dict, List from pymongo import MongoClient from datetime import datetime import openai import google.generativeai as genai from google.generativeai import GenerativeModel from dotenv import load_dotenv import os from file_upload_vectorize import resources_collection, vectors_collection, courses_collection2, faculty_collection # Load environment variables load_dotenv() MONGO_URI = os.getenv('MONGO_URI') OPENAI_KEY = os.getenv('OPENAI_KEY') GEMINI_KEY = os.getenv('GEMINI_KEY') # Configure APIs openai.api_key = OPENAI_KEY genai.configure(api_key=GEMINI_KEY) model = genai.GenerativeModel('gemini-1.5-flash') # Connect to MongoDB client = MongoClient(MONGO_URI) db = client['novascholar_db'] quizzes_collection = db["quizzes"] surprise_quizzes_collection = db["surprise_quizzes"] def strip_code_markers(response_text): """Strip off the markers ``` and python from a LLM model's response""" if response_text.startswith("```python"): response_text = response_text[len("```python"):].strip() if response_text.startswith("```"): response_text = response_text[len("```"):].strip() if response_text.endswith("```"): response_text = response_text[:-len("```")].strip() return response_text # New function to generate MCQs using Gemini def generate_mcqs(context, num_questions, session_title, session_description): """Generate MCQs either from context or session details""" try: # Initialize Gemini model if context: prompt = f""" Based on the following content, generate {num_questions} multiple choice questions. Format each question as a Python dictionary with the following structure: {{ "question": "Question text here", "options": ["A) option1", "B) option2", "C) option3", "D) option4"], "correct_option": "A) option1" or "B) option2" or "C) option3" or "D) option4" }} Content: {context} Generate challenging but clear questions that test understanding of key concepts. Return only the Python list of dictionaries. """ else: prompt = f""" Generate {num_questions} multiple choice questions about the topic: Title: {session_title} Description: {session_description} Format each question as a Python dictionary with the following structure: {{ "question": "Question text here", "options": ["A) option1", "B) option2", "C) option3", "D) option4"], "correct_option": "A" or "B" or "C" or "D" }} Generate challenging but clear questions. Return only the Python list of dictionaries without any additional formatting or markers Do not write any other text, do not start the response with (```python), do not end the response with backticks(```) A Sample response should look like this: Response Text: [ { "question": "Which of the following is NOT a valid data type in C++?", "options": ["int", "double", "boolean", "char"], "correct_option": "C" } ] (Notice that there are no backticks(```) around the response and no (```python)) . """ response = model.generate_content(prompt) response_text = response.text.strip() print("Response Text:", response_text) modified_response_text = strip_code_markers(response_text) print("Response Text Modified to:", modified_response_text) # Extract and parse the response to get the list of MCQs mcqs = ast.literal_eval(modified_response_text) # Be careful with eval, consider using ast.literal_eval for production print(mcqs) if not mcqs: raise ValueError("No questions generated") return mcqs except Exception as e: print(f"Error generating MCQs: , error: {e}") return None def generate_pre_class_question_bank(context: str, session_title: str, session_description: str) -> List[Dict]: """Generate a bank of 40 MCQ questions for pre-class quiz""" try: prompt = f""" Based on the following content, generate 50 multiple choice questions for a question bank. Format each question as a Python dictionary with this exact structure: {{ "question": "Question text here", "options": ["A) option1", "B) option2", "C) option3", "D) option4"], "correct_option": "A) option1" or "B) option2" or "C) option3" or "D) option4", "difficulty": "easy|medium|hard", "topic": "specific topic from content" }} Content: {context} Session Title: {session_title} Description: {session_description} Requirements: 1. Questions should cover all important concepts 2. Mix of easy (20%), medium (50%), and hard (30%) questions 3. Clear and unambiguous questions 4. Options should be plausible but only one correct answer 5. Include topic tags for categorization 6. **NUMBER OF QUESTIONS SHOULD BE GREATER THAN OR EQUAL TO 60** Return ONLY the JSON array of questions. """ response = model.generate_content( prompt, generation_config=genai.GenerationConfig( # temperature=0.7, response_mime_type="application/json" ) ) response_text = response.text.strip() modified_response_text = strip_code_markers(response_text) question_bank = ast.literal_eval(modified_response_text) print(question_bank) # Validate question bank if not isinstance(question_bank, list): raise ValueError("Invalid question bank format or count") return question_bank except Exception as e: print(f"Error generating question bank: {e}") return None def save_pre_class_quiz_with_bank( course_id: str, session_id: str, title: str, question_bank: List[Dict], num_questions: int, duration: int, user_id: str ) -> str: """Save pre-class quiz with question bank to database""" try: quiz_data = { "user_id": user_id, "course_id": course_id, "session_id": session_id, "title": title, "question_bank": question_bank, "num_questions": num_questions, "duration_minutes": duration, "created_at": datetime.now(), "status": "active", "submissions": [], "quiz_type": "pre_class" } result = quizzes_collection.insert_one(quiz_data) return result.inserted_id except Exception as e: print(f"Error saving quiz with question bank: {e}") return None def get_randomized_questions(quiz_id) -> List[Dict]: """Get randomly selected questions from question bank based on quiz settings""" try: quiz = quizzes_collection.find_one({"_id": quiz_id}) if not quiz: return None num_questions = quiz.get("num_questions", 0) question_bank = quiz.get("question_bank", []) if not question_bank or num_questions <= 0: return None # Randomly select questions import random selected_questions = random.sample(question_bank, num_questions) return selected_questions except Exception as e: print(f"Error getting randomized questions: {e}") return None def save_pre_class_quiz(course_id: str, session_id: str, title: str, questions: List[Dict], user_id: str, duration: int): """Save pre-class quiz to database""" try: quiz_data = { "user_id": user_id, "course_id": course_id, "session_id": session_id, "title": title, "questions": questions, "created_at": datetime.now(), "status": "active", "submissions": [], "duration_minutes": duration, "quiz_type": "pre_class" } result = quizzes_collection.insert_one(quiz_data) return result.inserted_id except Exception as e: print(f"Error saving quiz: {e}") return None # New function to save quiz to database def save_quiz(course_id, session_id, title, questions, user_id): """Save quiz to database""" try: quiz_data = { "user_id": user_id, "course_id": course_id, "session_id": session_id, "title": title, "questions": questions, "duration_minutes": 15, "created_at": datetime.utcnow(), "status": "active", "submissions": [] } result = quizzes_collection.insert_one(quiz_data) return result.inserted_id except Exception as e: print(f"Error saving quiz: {e}") return None def save_surprise_quiz(course_id, session_id, title, questions, user_id, no_minutes): """Save quiz to database""" try: quiz_data = { "user_id": user_id, "course_id": course_id, "session_id": session_id, "title": title, "questions": questions, "created_at": datetime.now(), "status": "active", "submissions": [], "no_minutes": no_minutes } result = surprise_quizzes_collection.insert_one(quiz_data) return result.inserted_id except Exception as e: print(f"Error saving quiz: {e}") return None def get_student_quiz_score(quiz_id, student_id): """Get student's score for a specific quiz""" quiz = quizzes_collection.find_one( { "_id": quiz_id, "submissions.student_id": student_id }, {"submissions.$": 1} ) if quiz and quiz.get('submissions'): return quiz['submissions'][0].get('score') return None def get_student_surprise_quiz_score(quiz_id, student_id): """Get student's score for a specific quiz""" quiz = surprise_quizzes_collection.find_one( { "_id": quiz_id, "submissions.student_id": student_id }, {"submissions.$": 1} ) if quiz and quiz.get('submissions'): return quiz['submissions'][0].get('score') return None # def submit_quiz_answers(quiz_id, student_id, student_answers): # """Submit and score student's quiz answers""" # quiz = quizzes_collection.find_one({"_id": quiz_id}) # if not quiz: # return None # # Calculate score # correct_answers = 0 # total_questions = len(quiz['questions']) # for q_idx, question in enumerate(quiz['questions']): # if student_answers.get(str(q_idx)) == question['correct_option']: # correct_answers += 1 # score = (correct_answers / total_questions) * 100 # # Store submission # submission_data = { # "student_id": student_id, # "answers": student_answers, # "score": score, # "submitted_at": datetime.utcnow() # } # # Update quiz with submission # quizzes_collection.update_one( # {"_id": quiz_id}, # { # "$push": {"submissions": submission_data} # } # ) # return score def submit_quiz_answers(quiz_id, student_id, student_answers): """Submit and score student's quiz answers""" try: quiz = quizzes_collection.find_one({"_id": quiz_id}) if not quiz: return None total_questions = len(quiz['questions']) # Debug logging print("\nScoring Debug:") print(f"Total questions: {total_questions}") # Calculate score correct_answers = 0 for q_idx, question in enumerate(quiz['questions']): student_answer = student_answers.get(str(q_idx)) correct_option = question['correct_option'] # Debug logging print(f"\nQuestion {q_idx + 1}:") print(f"Student answer: {student_answer}") print(f"Correct option: {correct_option}") if student_answer: # Only check if answer was provided # Extract the option letter (A, B, C, D) from the full answer string # answer_letter = student_answer.split(')')[0].strip() # if answer_letter == question['correct_option']: # correct_answers += 1 if student_answer == correct_option: correct_answers += 1 print(f"✓ Correct! Total correct: {correct_answers}") else: print("✗ Incorrect") score = (correct_answers / total_questions) * 100 print(f"\nFinal Score: {score}% ({correct_answers}/{total_questions} correct)") # Store submission submission_data = { "student_id": student_id, "answers": student_answers, "score": score, "submitted_at": datetime.utcnow(), "correct_answers_count": correct_answers, "total_questions": total_questions } # Update quiz with submission result = quizzes_collection.update_one( {"_id": quiz_id}, {"$push": {"submissions": submission_data}} ) return score if result.modified_count > 0 else None except Exception as e: print(f"Error submitting quiz: {e}") return None def submit_surprise_quiz_answers(quiz_id, student_id, student_answers): """Submit and score student's quiz answers""" try: quiz = surprise_quizzes_collection.find_one({"_id": quiz_id}) if not quiz: return None # Calculate score correct_answers = 0 total_questions = len(quiz['questions']) for q_idx, question in enumerate(quiz['questions']): student_answer = student_answers.get(str(q_idx)) if student_answer: # Only check if answer was provided # Extract the option letter (A, B, C, D) from the full answer string answer_letter = student_answer.split(')')[0].strip() if answer_letter == question['correct_option']: correct_answers += 1 score = (correct_answers / total_questions) * 100 # Store submission submission_data = { "student_id": student_id, "answers": student_answers, "score": score, "submitted_at": datetime.utcnow() } # Update quiz with submission result = surprise_quizzes_collection.update_one( {"_id": quiz_id}, {"$push": {"submissions": submission_data}} ) return score if result.modified_count > 0 else None except Exception as e: print(f"Error submitting quiz: {e}") return None