|
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_dotenv() |
|
MONGO_URI = os.getenv('MONGO_URI') |
|
OPENAI_KEY = os.getenv('OPENAI_KEY') |
|
GEMINI_KEY = os.getenv('GEMINI_KEY') |
|
|
|
|
|
openai.api_key = OPENAI_KEY |
|
genai.configure(api_key=GEMINI_KEY) |
|
model = genai.GenerativeModel('gemini-pro') |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
def generate_mcqs(context, num_questions, session_title, session_description): |
|
"""Generate MCQs either from context or session details""" |
|
try: |
|
|
|
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) |
|
|
|
mcqs = ast.literal_eval(modified_response_text) |
|
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 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""" |
|
try: |
|
quiz = quizzes_collection.find_one({"_id": quiz_id}) |
|
if not quiz: |
|
return None |
|
|
|
|
|
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: |
|
|
|
answer_letter = student_answer.split(')')[0].strip() |
|
if answer_letter == question['correct_option']: |
|
correct_answers += 1 |
|
|
|
score = (correct_answers / total_questions) * 100 |
|
|
|
|
|
submission_data = { |
|
"student_id": student_id, |
|
"answers": student_answers, |
|
"score": score, |
|
"submitted_at": datetime.utcnow() |
|
} |
|
|
|
|
|
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 |