Spaces:
Sleeping
Sleeping
| """ | |
| MongoDB Configuration and Connection Manager | |
| Replaces SQLite with MongoDB for the Adaptive Learning Platform | |
| """ | |
| import sys | |
| import logging | |
| import os | |
| logger = logging.getLogger(__name__) | |
| # Check for bson import conflict before importing pymongo | |
| try: | |
| import bson | |
| # Check if this is the standalone bson package (which doesn't have SON) | |
| if not hasattr(bson, 'SON') and not hasattr(bson, 'objectid'): | |
| logger.error(""" | |
| ⚠️ BSON Import Conflict Detected! | |
| A standalone 'bson' package is installed that conflicts with pymongo. | |
| This will cause import errors. | |
| To fix this, run: | |
| pip uninstall bson | |
| pip install --upgrade pymongo | |
| Or see: BSON_CONFLICT_FIX.md for detailed instructions. | |
| """) | |
| # Don't exit - let pymongo fail with a clearer error | |
| except ImportError: | |
| pass # bson not installed, which is fine | |
| from pymongo import MongoClient, ASCENDING, DESCENDING | |
| from pymongo.errors import ConnectionFailure, OperationFailure | |
| try: | |
| from bson.objectid import ObjectId | |
| except ImportError: | |
| # Fallback if standalone bson package conflicts | |
| try: | |
| from pymongo.bson.objectid import ObjectId | |
| except ImportError: | |
| # Last resort - try direct import | |
| import pymongo | |
| ObjectId = pymongo.bson.objectid.ObjectId | |
| from datetime import datetime | |
| from typing import Optional, Dict, Any, List | |
| # MongoDB Configuration | |
| MONGODB_CONNECTION_STRING = "mongodb+srv://raziullah0316_db_user:8GXp76aJwsg2i6Rn@learning.tlwwzix.mongodb.net/" | |
| MONGODB_DATABASE_NAME = "learning" | |
| class MongoDBManager: | |
| """MongoDB Connection and Operations Manager""" | |
| def __init__(self): | |
| self.client: Optional[MongoClient] = None | |
| self.db = None | |
| self._connected = False | |
| def connect(self) -> bool: | |
| """Connect to MongoDB""" | |
| try: | |
| self.client = MongoClient( | |
| MONGODB_CONNECTION_STRING, | |
| serverSelectionTimeoutMS=5000, | |
| connectTimeoutMS=10000, | |
| retryWrites=True, | |
| w='majority' | |
| ) | |
| # Test connection | |
| self.client.admin.command('ping') | |
| self.db = self.client[MONGODB_DATABASE_NAME] | |
| self._connected = True | |
| logger.info(f"✅ Connected to MongoDB: {MONGODB_DATABASE_NAME}") | |
| self._create_indexes() | |
| return True | |
| except ConnectionFailure as e: | |
| logger.error(f"❌ Failed to connect to MongoDB: {e}") | |
| self._connected = False | |
| return False | |
| except Exception as e: | |
| logger.error(f"❌ Unexpected error connecting to MongoDB: {e}") | |
| self._connected = False | |
| return False | |
| def _create_indexes(self): | |
| """Create indexes for better performance""" | |
| try: | |
| # Admins collection | |
| self.db.admins.create_index([("username", ASCENDING)], unique=True) | |
| # Students collection | |
| self.db.students.create_index([("student_id", ASCENDING)], unique=True) | |
| self.db.students.create_index([("email", ASCENDING)]) | |
| # Syllabi collection | |
| self.db.syllabi.create_index([("title", ASCENDING)]) | |
| self.db.syllabi.create_index([("uploaded_at", DESCENDING)]) | |
| # Assessments collection | |
| self.db.assessments.create_index([("student_id", ASCENDING)]) | |
| self.db.assessments.create_index([("syllabus_id", ASCENDING)]) | |
| self.db.assessments.create_index([("created_at", DESCENDING)]) | |
| # MCQ Questions collection | |
| self.db.mcq_questions.create_index([("syllabus_id", ASCENDING)]) | |
| self.db.mcq_questions.create_index([("topic", ASCENDING)]) | |
| # Student Answers collection | |
| self.db.student_answers.create_index([("student_id", ASCENDING), ("assessment_id", ASCENDING)]) | |
| # Learning Paths collection | |
| self.db.learning_paths.create_index([("student_id", ASCENDING)]) | |
| self.db.learning_paths.create_index([("status", ASCENDING)]) | |
| # Chat Sessions collection | |
| self.db.chat_sessions.create_index([("student_id", ASCENDING)]) | |
| self.db.chat_sessions.create_index([("created_at", DESCENDING)]) | |
| # Learning Progress collection | |
| self.db.learning_progress.create_index([("student_id", ASCENDING)]) | |
| self.db.learning_progress.create_index([("learning_path_id", ASCENDING)]) | |
| # Microsoft Forms collection | |
| self.db.microsoft_forms.create_index([("subject", ASCENDING)]) | |
| self.db.microsoft_forms.create_index([("grade", ASCENDING)]) | |
| self.db.microsoft_forms.create_index([("is_active", ASCENDING)]) | |
| self.db.microsoft_forms.create_index([("created_at", DESCENDING)]) | |
| # Microsoft Form Submissions collection | |
| self.db.microsoft_form_submissions.create_index([("student_id", ASCENDING)]) | |
| self.db.microsoft_form_submissions.create_index([("form_id", ASCENDING)]) | |
| self.db.microsoft_form_submissions.create_index([("subject", ASCENDING)]) | |
| self.db.microsoft_form_submissions.create_index([("submitted_at", DESCENDING)]) | |
| logger.info("✅ MongoDB indexes created successfully") | |
| except Exception as e: | |
| logger.warning(f"⚠️ Failed to create some indexes: {e}") | |
| def is_connected(self) -> bool: | |
| """Check if connected to MongoDB""" | |
| return self._connected and self.client is not None | |
| def close(self): | |
| """Close MongoDB connection""" | |
| if self.client: | |
| self.client.close() | |
| self._connected = False | |
| logger.info("MongoDB connection closed") | |
| def serialize_doc(self, doc: Optional[Dict]) -> Optional[Dict]: | |
| """Convert MongoDB document to JSON-serializable format""" | |
| if doc is None: | |
| return None | |
| doc = dict(doc) # Make a copy | |
| if "_id" in doc: | |
| doc["id"] = str(doc["_id"]) | |
| del doc["_id"] | |
| for key, value in doc.items(): | |
| if isinstance(value, ObjectId): | |
| doc[key] = str(value) | |
| elif isinstance(value, datetime): | |
| doc[key] = value.isoformat() | |
| elif isinstance(value, dict): | |
| doc[key] = self.serialize_doc(value) | |
| elif isinstance(value, list): | |
| doc[key] = [self.serialize_doc(item) if isinstance(item, dict) else item for item in value] | |
| return doc | |
| def get_object_id(self, id_str: str) -> ObjectId: | |
| """Convert string to ObjectId""" | |
| try: | |
| return ObjectId(id_str) | |
| except: | |
| return None | |
| # Global MongoDB instance | |
| mongodb = MongoDBManager() | |
| # Helper functions for backward compatibility with SQLite code | |
| def get_db(): | |
| """Get MongoDB database instance (replaces SQLAlchemy session)""" | |
| if not mongodb.is_connected(): | |
| mongodb.connect() | |
| return mongodb.db | |
| def serialize_document(doc: Dict) -> Dict: | |
| """Serialize MongoDB document""" | |
| return mongodb.serialize_doc(doc) | |