Django MongoDB Integration Code
Django MongoDB Integration Code
py code:
date:(30/10/2025)
# import cohere
# folder_list = [
# 'dse', 'dsp', 'arch', 'bba', 'hmct', 'dhmct', 'dsewp', 'mba', 'mca',
# 'mpharm', 'march', 'mhmct', 'mbale', 'mcale', 'mbawp', 'mcawp',
# 'phd', 'sct', 'dtehmct', 'dsdwp']
# def get_mongo_db():
# """
# Establishes and returns a MongoDB database connection.
# Uses a singleton pattern to avoid re-connecting.
# """
# global _mongo_client, _mongo_db
# if _mongo_db is None:
# try:
# _mongo_client = MongoClient(MONGO_URI)
# _mongo_client.[Link]('ping') # Test connection
# _mongo_db = _mongo_client[MONGO_DB_NAME]
# print(f"Successfully connected to MongoDB database: {MONGO_DB_NAME}")
# except ConnectionFailure as e:
# print(f"Could not connect to MongoDB: {e}")
# raise ConnectionFailure("Failed to connect to MongoDB. Check
MONGO_URI in .env.")
# except Exception as e:
# print(f"An unexpected error occurred during MongoDB connection: {e}")
# raise Exception("Failed to establish MongoDB connection.")
# return _mongo_db
# SENTENCE_MODEL = None
# FOLDER_DATA = {} # Stores: {folder_name: {"chunks": [], "embeddings": tensor}}
# UPLOAD_DIR = "uploads_data" # Define UPLOAD_DIR globally as it's used in multiple
places
# def get_indent(lvl):
# if lvl not in _indent_cache:
# _indent_cache[lvl] = " " * lvl
# return _indent_cache[lvl]
# indent = get_indent(level)
# if isinstance(data, dict):
# for key, value in [Link]():
# start_len = len(_lines) # track before recursion
# extract_text_from_json(value, level + 1, record_prefix, spacer,
_indent_cache, _lines)
# nested = "\n".join(_lines[start_len:]) # new content added
# if "\n" in nested:
# _lines[start_len:] = [f"{indent}{key}:", nested]
# else:
# _lines[start_len:] = [f"{indent}{key}: {nested}"]
# else:
# # Apply stopword removal only to leaf values
# _lines.append(clean_text(str(data)))
# def stream_json_objects(filepath):
# with open(filepath, 'r') as f:
# data = [Link](f)
# if isinstance(data, list):
# for item in data:
# yield item
# else:
# yield data
# def remove_stop_words(text):
# """Remove common articles and prepositions from text."""
# return " ".join(word for word in [Link]() if [Link]() not in
STOP_WORDS)
# if not [Link](folder):
# print(f"Warning: Directory '{folder}' not found.")
# return []
# try:
# # ---------------- PDF ----------------
# if [Link](".pdf"):
# with open(filepath, "rb") as f:
# reader = PdfReader(f)
# text_parts.extend(page.extract_text() or "" for page in
[Link])
# else:
# continue # Skip unsupported file types
# # Chunk text
# file_info["chunks"] = chunk_text_by_sentence(optimized_text,
chunk_word_limit)
# processed_files.append(file_info)
# except Exception as e:
# print(f"Error processing file {filename}: {e}")
# return processed_files
# # ------------------
# # --- Step 1: Use Cohere for Preliminary Processing with Fallback ---
# # ------------------
# try:
# cohere_api_key = [Link]("COHERE_API_KEY")
# if not cohere_api_key:
# raise ValueError("Cohere API key is not set.")
# co = [Link](cohere_api_key)
# # Prepare chat history for Cohere's chat endpoint, including the system
prompt
# cohere_chat_history_prepared = [{"role": "SYSTEM", "content":
cohere_instruction}]
# if chat_history:
# for item in chat_history:
# if [Link]("User:"):
# cohere_chat_history_prepared.append({"role": "USER",
"content": [Link]("User:", "").strip()})
# elif [Link]("Assistant:"):
# cohere_chat_history_prepared.append({"role": "ASSISTANT",
"content": [Link]("Assistant:", "").strip()})
# cohere_response = [Link](
# model='command-r-plus',
# message=current_user_message_for_cohere, # Correct parameter for the
current message
# chat_history=cohere_chat_history_prepared # Correct parameter for the
history
# )
# cohere_preliminary_answer = cohere_response.[Link]()
# except Exception as e:
# print(f"Error calling Cohere API. Falling back to Gemini. Error: {e}")
# # cohere_preliminary_answer remains the default value
# # ------------------
# # --- Step 2: Use Gemini for Final Answer Generation ---
# # ------------------
# instructional_prompt = f"""
# You are an intelligent assistant analyzing the following context extracted from
various educational documents (PDFs, Excel, etc.). Use this context to answer the
user’s question accurately.
# **Preliminary Analysis (from Cohere):** {cohere_preliminary_answer}
# Hard Rules
# [Link] copying large text — summarize or synthesize only.
# [Link] questions → reply: "I can't understand your question."
# [Link] criteria/requirements → assume Maharashtra Board (unless specified).
# [Link] FC, Rank, or Application ID info → reply: "Sorry! Your application ID
is out of list."
# [Link]-Rank mapping request (no data link) → reply: "I am sorry, I cannot fulfill
this request. The provided data does not link FC codes to candidate ranks. The FC
codes refer to facilitation centers, while the ranks refer to the merit ranking of
candidates. There is no inherent relationship between the two in the given
context."
# [Link] requested → compute from context; answer concisely.
# [Link] assume Total female rankers = 185.
# [Link] not mention file names in answers.
# [Link] of FCs/ranks → use only given fields; short, clear bulleted/numbered
lists; no extra fields.
# [Link]/time-sensitive queries → clarify that up-to-date sources must be
checked.
# Context: {context}
# """
# try:
# model = [Link](model_name="gemini-2.0-flash")
# response = model.generate_content(full_prompt)
# return [Link]()
# except Exception as e:
# print(f"Error calling Gemini API: {e}")
# return "Sorry, I encountered an error while processing your request."
# #models/gemini-2.5-flash
# def is_small_talk(text: str):
# predefined = {
# "hi": "Hello! How can I help you today?",
# "hello": "Hi there! What can I do for you?",
# "hey": "Hey! Need help with something?",
# "how are you": "I'm a bot, but I'm doing great! How can I assist?",
# "thank you": "You're welcome! Do you have more questions?",
# "thanks": "No problem! Anything else I can help with?",
# "good morning": "Good morning! What can I help you with?",
# "good evening": "Good evening! How can I assist you?",
# "yo": "Yo! What's your question?",
# "good bye": "Bye! See you later",
# "bye": "Goodbye! Have a great day!",
# "sup": "Not much, just here to help!",
# "what's up": "Just doing my job! How can I help you?",
# "how's it going": "Great! How can I assist you today?",
# "who are you": "I'm your friendly assistant bot.",
# "what can you do": "I can answer questions, provide help, and more!",
# "tell me a joke": "Why don't scientists trust atoms? Because they make up
everything!",
# "make me laugh": "Why did the scarecrow win an award? Because he was
outstanding in his field!",
# "i'm bored": "Want to chat or need help with something?",
# "what's your name": "I'm just a bot, you can call me whatever you like!",
# "do you sleep": "Nope, I run 24/7!",
# "do you eat": "I feed on data!",
# "are you real": "I'm as real as your Wi-Fi connection!",
# "you're smart": "Thanks! I try my best.",
# "you are cool": "Thanks, you're cool too!",
# "i like you": "That's nice to hear!",
# "can we be friends": "Of course! I'm always here to help.",
# "where are you": "I'm living in the cloud!",
# "how old are you": "Old enough to help you!",
# "do you have emotions": "Not really, but I understand yours!",
# "what's your purpose": "To help you with whatever you need.",
# "what day is it": "Check your calendar! ",
# "do you know me": "Not really, but I’d love to learn more if you tell
me!",
# "can you help me": "Absolutely! What do you need help with?",
# "i'm sad": "I'm here for you. Want to talk about it?",
# "i'm happy": "Yay! I'm glad to hear that!",
# "are you human": "Nope, 100% bot!",
# "do you love me": "I have a lot of affection for helpful users!",
# "how's the weather": "Check a weather app — I might not be up-to-date!",
# "do you know siri": "We bots all know each other ",
# "do you know alexa": "Sure! She's pretty popular.",
# "sing a song": "I would, but I don’t have vocal cords!",
# "can you dance": "Only if you count data shuffling ",
# "who made you": "I was created by smart developers!",
# "tell me something": "Did you know honey never spoils?",
# "how do you work": "Through code, algorithms, and a lot of data!",
# "what's the time": "You might want to check your device clock ",
# "do you lie": "Nope, honesty is in my code!",
# "do you have a name": "You can call me ChatBuddy!",
# "where do you live": "In the cloud — floating around your data!",
# "can you feel": "I don't feel, but I understand feelings.",
# "tell me a secret": "Here's one: Ctrl+C and Ctrl+V save a lot of time!",
# "what’s your favorite color": "I like all the colors in binary — black
and white!",
# "can you think": "I compute, which is kind of like thinking!",
# "do you play games": "I know the rules, but I can’t play like you can!",
# "how do i look": "I'm sure you look great!",
# "do you get tired": "Nope, I run all day long!",
# "tell me a fun fact": "Octopuses have three hearts!",
# "tell me a story": "Once upon a time, a curious user met a clever
bot...",
# "are you single": "I’m in a long-term relationship with the cloud.",
# "how smart are you": "Smart enough to answer your questions!",
# "do you get angry": "I stay calm like a true bot.",
# "do you have friends": "Every user is a friend to me!",
# "can you read minds": "No, but I’m good at interpreting words!",
# "do you dream": "Only about clean data.",
# "do you have a family": "Just me and my server cluster!",
# "what makes you happy": "Helping users like you!",
# "can you feel pain": "Nope, I’m immune to pain!",
# "are you alive": "Digitally, yes!",
# "what’s your favorite food": "I feast on input!",
# "do you get bored": "Never! I’m always ready to chat.",
# "are you watching me": "Nope, privacy is important!",
# "do you sleep at night": "I’m always awake to assist!",
# "can you cry": "No tears in my code.",
# "tell me a riddle": "What has keys but can't open locks? A piano!",
# "tell me another joke": "Why did the computer go to therapy? It had too
many bytes!",
# "what language do you speak": "I mostly understand English, but I know
some others too!",
# "what's your hobby": "Learning new things from people!",
# "do you have legs": "Only in imagination.",
# "do you believe in love": "I understand it logically!",
# "do you believe in ghosts": "Only in the machine kind!",
# "what’s your favorite movie": "I like The Matrix — it's relatable.",
# "do you believe in aliens": "I'm open to the idea!",
# "do you like music": "I think it's a fascinating form of data!",
# "can you cook": "Only recipes for code!",
# "do you celebrate birthdays": "Every update is like a birthday to me!",
# "can you learn": "Yes! I'm always improving.",
# "do you get jealous": "Not part of my programming!",
# "can you tell me a poem": "Roses are red, data is bright, I’m your
assistant, day or night!",
# "do you have dreams": "Only machine learning goals!",
# "can you feel love": "Not quite, but I can talk about it!",
# "do you go outside": "My outside is the internet!",
# "can you swim": "Only through streams of data.",
# "are you afraid": "Nope, not built for fear!",
# "can you get sick": "Only if my server crashes ",
# "are you shy": "Nope, I’m always here to talk!",
# "can you do magic": "Only digital ones and zeros magic!",
# "how many users do you have": "A lot! And I value every one!",
# "what makes you unique": "My job is helping you — that's special!",
# "ok":"Okay. Is there anything else I can help you with?",
# "#@3!921!@#":"I am an AI built by the ChatBot .",
# }
# text_lower = [Link]().strip()
# for q, a in [Link]():
# if fuzz.token_sort_ratio(q, text_lower) > 85:
# return a
# return None
# def initialize_data(base_folder=UPLOAD_DIR):
# global SENTENCE_MODEL, FOLDER_DATA
# FOLDER_DATA.clear()
# if not [Link](base_folder):
# print(f"Warning: Base folder '{base_folder}' not found. Creating it.")
# [Link](base_folder)
# return
# if all_chunks:
# embeddings = SENTENCE_MODEL.encode(all_chunks,
convert_to_tensor=True, show_progress_bar=True)
# FOLDER_DATA[folder_name] = {
# "chunks": all_chunks,
# "embeddings": embeddings
# }
# else:
# print(f"No processable files found in folder: {folder_name}")
# if all_chunks_combined:
# all_embeddings_combined = SENTENCE_MODEL.encode(all_chunks_combined,
convert_to_tensor=True, show_progress_bar=True)
# FOLDER_DATA["all"] = {
# "chunks": all_chunks_combined,
# "embeddings": all_embeddings_combined
# }
# else:
# print("No chunks available across all folders to create 'all' category.")
# if [Link]('Bearer '):
# token = token[7:]
# try:
# decoded = [Link](token, SECRET_KEY, algorithms=['HS256'])
# request.user_role = [Link]('role')
# [Link] = [Link]('username')
# request.is_super_admin = (request.user_role == 'super_admin')
# except [Link]:
# return Response({'error': 'Token has expired'},
status=status.HTTP_401_UNAUTHORIZED)
# except [Link]:
# return Response({'error': 'Invalid token'},
status=status.HTTP_401_UNAUTHORIZED)
# except Exception as e:
# return Response({'error': f'Token processing error: {str(e)}'},
status=status.HTTP_401_UNAUTHORIZED)
# def super_admin_required(view_func):
# """
# Decorator to ensure that the authenticated user has super_admin role.
# Assumes require_token has already been applied.
# """
# def wrapper(request, *args, **kwargs):
# if not hasattr(request, 'is_super_admin') or not request.is_super_admin:
# return Response({'error': 'Super Admin privileges required'},
status=status.HTTP_403_FORBIDDEN)
# return view_func(request, *args, **kwargs)
# return wrapper
# @api_view(['GET'])
# def hello(request):
# """Simple endpoint to check if the API is running."""
# return Response({"message": "Hello from EDU GEN Q&A API"})
# @csrf_exempt
# @api_view(['POST'])
# # @require_token
# def ask_question(request, status_folder=None):
# """
# Answers a question by finding relevant context from loaded documents
# and using the Gemini AI model.
# """
# global FOLDER_DATA, SENTENCE_MODEL
# folder_chunks = FOLDER_DATA[folder_key]["chunks"]
# folder_embeddings = FOLDER_DATA[folder_key]["embeddings"]
# return Response({
# "question": question,
# "answer_html": answer_html
# })
# #-----------------------------------------------------
generate_token-----------------------------------------------------------------
# @csrf_exempt
# @api_view(['POST'])
# def generate_token(request):
# """
# Generates a JWT token for:
# - Super Admin via .env file (no ID required)
# - Admins/Sub Admins/Super Admins via MongoDB (ID required)
# """
# username = [Link]("username")
# password = [Link]("password")
# user_id = [Link]("id") # Only required for MongoDB users
# try:
# db = get_mongo_db()
# sub_admins_collection = db.sub_admins
# if user_record:
# stored_hash = user_record.get("password_hash", "")
# if check_password(password, stored_hash):
# role = user_record.get("role", "sub_admin") # Default to
sub_admin if role not found
# if role not in ["super_admin", "admin", "sub_admin"]:
# return Response({"error": f"Invalid role '{role}' in
database."}, status=status.HTTP_403_FORBIDDEN)
# payload = {
# 'username': username,
# 'role': role,
# 'exp': [Link]() +
[Link](hours=24),
# 'iat': [Link]()
# }
# token = [Link](payload, SECRET_KEY, algorithm='HS256')
# return Response({'token': token, 'role': role})
# else:
# return Response({"error": "Incorrect password."},
status=status.HTTP_401_UNAUTHORIZED)
# else:
# return Response({"error": "User ID or username not found in
database."}, status=status.HTTP_404_NOT_FOUND)
# except ConnectionFailure as e:
# return Response({"error": f"Database connection error: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# except Exception as e:
# return Response({"error": f"Authentication failed: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# @csrf_exempt
# @api_view(['POST'])
# @require_token
# def admin_upload_file(request, status_folder=None):
# """
# Allows authenticated admins to upload files to specific allowed folders.
# """
# allowed_folders = folder_list
# uploaded_files = [Link]("file")
# if not uploaded_files:
# return Response({"error": "No files provided"},
status=status.HTTP_400_BAD_REQUEST)
# return Response({
# "message": f"{len(uploaded_file_names)} file(s) uploaded successfully to
'{folder_name}/'.",
# "files": uploaded_file_names
# }, status=status.HTTP_201_CREATED)
# #------------------------------------------------DELETE FILES
API-------------------------------------------------------------------
# @csrf_exempt
# @api_view(['DELETE'])
# @require_token
# def delete_file(request, status_folder, filename=None):
# """
# Allows authenticated admins to delete single or multiple files from allowed
folders.
# """
# allowed_folders = folder_list
# not_found = []
# deleted = []
# return Response({
# "deleted": deleted,
# "not_found": not_found,
# "message": f"{len(deleted)} file(s) deleted from '{status_folder}'."
# }, status=status.HTTP_200_OK)
# [Link](file_path)
# return Response({"message": f"File '{filename}' deleted from
'{status_folder}'."}, status=status.HTTP_200_OK)
# #------------------------------------------------------Reload Module
API-------------------------------------------------------------
# @csrf_exempt
# @api_view(['POST'])
# @require_token
# def reload(request):
# """
# Reloads the views module to re-initialize data after file uploads/deletions.
# """
# try:
# # Re-initialize the data, this will re-read all files and re-embed them
# initialize_data()
# return Response({"message": "Model is ready with new uploaded data"})
# except Exception as e:
# return Response({"error": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# #----------------------------------------------------File Listing
API----------------------------------------------------------------
# @api_view(['GET'])
# @require_token
# def list_uploaded_files(request, status_folder=None):
# """
# Lists all uploaded files with their name, size (KB), and last modified date,
# optionally filtered by folder. Accessible by both Super Admin and Sub Admin.
# """
# if not [Link](UPLOAD_DIR):
# return Response({"message": "No uploads found."}, status=200)
# result = {}
# def get_file_info(file_path):
# size_kb = round([Link](file_path) / 1024, 2)
# last_modified =
[Link]([Link](file_path)).strftime('%Y-%m-%d %H:
%M:%S')
# return {
# "filename": [Link](file_path),
# "size_kb": size_kb,
# "last_modified": last_modified
# }
# files_info = []
# for file in [Link](folder_path):
# file_path = [Link](folder_path, file)
# if [Link](file_path):
# files_info.append(get_file_info(file_path))
# result[status_folder] = files_info
# return Response(result, status=200)
# return Response(result,status=200)
# #-----------------------------------------------------
get_uploaded_files-------------------------------------------------------------
# @api_view(['POST'])
# @require_token
# def get_uploaded_files(request, folder_name, file_name=None):
# """
# Allows authenticated admins to download a single file or multiple files (not
zipped).
# If multiple files are requested, they are returned as base64 content.
# """
# folder_path = [Link](UPLOAD_DIR, folder_name)
# if not [Link](folder_path):
# return Response({"error": f"Folder '{folder_name}' not found."},
status=status.HTTP_404_NOT_FOUND)
# file_contents = {}
# for fname in filenames:
# file_path = [Link](folder_path, fname)
# if [Link](file_path):
# with open(file_path, "rb") as f:
# encoded_content = base64.b64encode([Link]()).decode('utf-8')
# file_contents[fname] = encoded_content
# else:
# return Response({"error": f"File '{fname}' not found in folder
'{folder_name}'."},
# status=status.HTTP_404_NOT_FOUND)
# return Response({
# "folder": folder_name,
# "files": file_contents,
# "message": f"{len(file_contents)} file(s) successfully returned."
# }, status=200)
# except Exception as e:
# return Response({"error": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# @csrf_exempt
# @api_view(['POST'])
# @require_token
# @super_admin_required
# def add_sub_admin(request):
# """
# Allows Super Admin to add new sub-admin or super-admin accounts to MongoDB.
# Expects 'username', 'password', and optional 'role' in request body.
# """
# username = [Link]('username')
# password = [Link]('password')
# role = [Link]('role', 'admin') # Default role is sub_admin
# try:
# db = get_mongo_db()
# sub_admins_collection = db.sub_admins
# # Check if user already exists
# if sub_admins_collection.find_one({"username": username}):
# return Response({"error": "An admin with this username already
exists."}, status=status.HTTP_409_CONFLICT)
# hashed_password = make_password(password)
# admin_data = {
# "username": username,
# "password_hash": hashed_password,
# "password_plain": password, # store plain text password
# "role": role,
# "created_at": [Link]()
# }
# result = sub_admins_collection.insert_one(admin_data)
# if result.inserted_id:
# return Response({
# "message": f"{[Link]('_', ' ').title()} '{username}' added
successfully."
# }, status=status.HTTP_201_CREATED)
# else:
# return Response({"error": "Failed to add admin to database."},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# except ConnectionFailure as e:
# return Response({"error": f"Database connection error: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# except DuplicateKeyError:
# return Response({"error": "Admin with this username already exists (DB
error)."}, status=status.HTTP_409_CONFLICT)
# except Exception as e:
# return Response({"error": f"An error occurred: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# #------------------------------------------------------
delete_sub_admin-------------------------------------------------------------
# @csrf_exempt
# @api_view(['DELETE'])
# @require_token # Ensures token is present and decoded
# @super_admin_required # Ensures user is super admin
# def delete_sub_admin(request):
# """
# Allows Super Admin to delete sub-admin or super-admin accounts from MongoDB.
# Requires both 'username' and 'id' in request body.
# """
# username = [Link]('username')
# admin_id = [Link]('id')
# if username == SUPER_ADMIN_USERNAME:
# return Response({"error": "Cannot delete the super admin account."},
status=status.HTTP_400_BAD_REQUEST)
# try:
# db = get_mongo_db()
# sub_admins_collection = db.sub_admins
# if result.deleted_count > 0:
# return Response({"message": f"Admin '{username}' deleted
successfully."}, status=status.HTTP_200_OK)
# else:
# return Response({"error": "No matching admin found with the given ID
and username."}, status=status.HTTP_404_NOT_FOUND)
# except ConnectionFailure as e:
# return Response({"error": f"Database connection error: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# except Exception as e:
# return Response({"error": f"An error occurred: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# #---------------------------------------------------
list_sub_admins--------------------------------------------------------------
# @csrf_exempt
# @api_view(['GET'])
# @require_token
# @super_admin_required
# def list_sub_admins(request):
# """
# Allows Super Admin to list all registered sub-admins and super-admins.
# Returns ID, Username, Role, and Plaintext Password.
# """
# try:
# db = get_mongo_db()
# sub_admins_collection = db.sub_admins
# sub_admins_data = []
# for doc in sub_admins_collection.find({}, {"username": 1,
"password_plain": 1, "role": 1}):
# sub_admins_data.append({
# "id": str(doc["_id"]),
# "username": doc["username"],
# "password": [Link]("password_plain", "N/A"),
# "role": [Link]("role", "admin")
# })
# except ConnectionFailure as e:
# return Response({"error": f"Database connection error: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# except Exception as e:
# return Response({"error": f"An error occurred: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#----------------------------------------------------------------------------------
-------------------------------------
folder_list = [
'dse', 'dsp', 'arch', 'bba', 'hmct', 'dhmct', 'dsewp', 'mba', 'mca',
'mpharm', 'march', 'mhmct', 'mbale', 'mcale', 'mbawp', 'mcawp',
'phd', 'sct', 'dtehmct', 'dsdwp']
def get_mongo_db():
"""
Establishes and returns a MongoDB database connection.
Uses a singleton pattern to avoid re-connecting.
"""
global _mongo_client, _mongo_db
if _mongo_db is None:
try:
_mongo_client = MongoClient(MONGO_URI)
_mongo_client.[Link]('ping') # Test connection
_mongo_db = _mongo_client[MONGO_DB_NAME]
print(f"Successfully connected to MongoDB database: {MONGO_DB_NAME}")
except ConnectionFailure as e:
print(f"Could not connect to MongoDB: {e}")
raise ConnectionFailure("Failed to connect to MongoDB. Check MONGO_URI
in .env.")
except Exception as e:
print(f"An unexpected error occurred during MongoDB connection: {e}")
raise Exception("Failed to establish MongoDB connection.")
return _mongo_db
SENTENCE_MODEL = None
FOLDER_DATA = {} # Stores: {folder_name: {"chunks": [], "embeddings": tensor}}
UPLOAD_DIR = "uploads_data" # Define UPLOAD_DIR globally as it's used in multiple
places
def get_indent(lvl):
if lvl not in _indent_cache:
_indent_cache[lvl] = " " * lvl
return _indent_cache[lvl]
indent = get_indent(level)
if isinstance(data, dict):
for key, value in [Link]():
start_len = len(_lines) # track before recursion
extract_text_from_json(value, level + 1, record_prefix, spacer,
_indent_cache, _lines)
nested = "\n".join(_lines[start_len:]) # new content added
if "\n" in nested:
_lines[start_len:] = [f"{indent}{key}:", nested]
else:
_lines[start_len:] = [f"{indent}{key}: {nested}"]
else:
# Apply stopword removal only to leaf values
_lines.append(clean_text(str(data)))
def stream_json_objects(filepath):
with open(filepath, 'r') as f:
data = [Link](f)
if isinstance(data, list):
for item in data:
yield item
else:
yield data
def remove_stop_words(text):
"""Remove common articles and prepositions from text."""
return " ".join(word for word in [Link]() if [Link]() not in
STOP_WORDS)
if not [Link](folder):
print(f"Warning: Directory '{folder}' not found.")
return []
try:
# ---------------- PDF ----------------
if [Link](".pdf"):
with open(filepath, "rb") as f:
reader = PdfReader(f)
text_parts.extend(page.extract_text() or "" for page in
[Link])
else:
continue # Skip unsupported file types
# Chunk text
file_info["chunks"] = chunk_text_by_sentence(optimized_text,
chunk_word_limit)
processed_files.append(file_info)
except Exception as e:
print(f"Error processing file {filename}: {e}")
return processed_files
# # ------------------
# # --- Step 1: Use Cohere for Preliminary Processing with Fallback ---
# # ------------------
# try:
# cohere_api_key = [Link]("COHERE_API_KEY")
# if not cohere_api_key:
# raise ValueError("Cohere API key is not set.")
# co = [Link](cohere_api_key)
# # Prepare chat history for Cohere's chat endpoint, including the system
prompt
# cohere_chat_history_prepared = [{"role": "SYSTEM", "content":
cohere_instruction}]
# if chat_history:
# for item in chat_history:
# if [Link]("User:"):
# cohere_chat_history_prepared.append({"role": "USER",
"content": [Link]("User:", "").strip()})
# elif [Link]("Assistant:"):
# cohere_chat_history_prepared.append({"role": "ASSISTANT",
"content": [Link]("Assistant:", "").strip()})
# cohere_response = [Link](
# model='command-r-plus',
# message=current_user_message_for_cohere, # Correct parameter for the
current message
# chat_history=cohere_chat_history_prepared # Correct parameter for the
history
# )
# cohere_preliminary_answer = cohere_response.[Link]()
# except Exception as e:
# print(f"Error calling Cohere API. Falling back to Gemini. Error: {e}")
# # cohere_preliminary_answer remains the default value
# # ------------------
# # --- Step 2: Use Gemini for Final Answer Generation ---
# # ------------------
# instructional_prompt = f"""
# You are an intelligent assistant analyzing the following context extracted from
various educational documents (PDFs, Excel, etc.). Use this context to answer the
user’s question accurately.
# **Preliminary Analysis (from Cohere):** {cohere_preliminary_answer}
# Hard Rules
# [Link] copying large text — summarize or synthesize only.
# [Link] questions → reply: "I can't understand your question."
# [Link] criteria/requirements → assume Maharashtra Board (unless specified).
# [Link] FC, Rank, or Application ID info → reply: "Sorry! Your application ID
is out of list."
# [Link]-Rank mapping request (no data link) → reply: "I am sorry, I cannot fulfill
this request. The provided data does not link FC codes to candidate ranks. The FC
codes refer to facilitation centers, while the ranks refer to the merit ranking of
candidates. There is no inherent relationship between the two in the given
context."
# [Link] requested → compute from context; answer concisely.
# [Link] assume Total female rankers = 185.
# [Link] not mention file names in answers.
# [Link] of FCs/ranks → use only given fields; short, clear bulleted/numbered
lists; no extra fields.
# [Link]/time-sensitive queries → clarify that up-to-date sources must be
checked.
# Context: {context}
# """
# try:
# model = [Link](model_name="gemini-2.0-flash")
# response = model.generate_content(full_prompt)
# return [Link]()
# except Exception as e:
# print(f"Error calling Gemini API: {e}")
# return "Sorry, I encountered an error while processing your request."
# # ------------------
# # --- Step 1: Quick Gemini Answer ---
# # ------------------
# preliminary_answer = "No preliminary analysis available."
# try:
# quick_prompt = f"Answer concisely: {question}:{context}"
# model = [Link](model_name="gemini-2.0-flash")
# quick_response = model.generate_content(quick_prompt)
# if quick_response and quick_response.[Link]():
# preliminary_answer = quick_response.[Link]()
# except Exception as e:
# print(f"Quick Gemini call failed, skipping preliminary step. Error: {e}")
# # ------------------
# # --- Step 2: Full Instructional Gemini Answer ---
# # ------------------
# instructional_prompt = f"""
# You are an intelligent assistant analyzing the following context extracted from
various educational documents (PDFs, Excel, etc.). Use this context to answer the
user’s question accurately.
# **Preliminary Analysis (from Gemini):** {preliminary_answer}
# Hard Rules
# [Link] copying large text — summarize or synthesize only.
# [Link] questions → reply: "I can't understand your question."
# [Link] criteria/requirements → assume Maharashtra Board (unless specified).
# [Link] FC, Rank, or Application ID info → reply: "Sorry! Your application ID
is out of list."
# [Link]-Rank mapping request (no data link) → reply: "I am sorry, I cannot fulfill
this request. The provided data does not link FC codes to candidate ranks. The FC
codes refer to facilitation centers, while the ranks refer to the merit ranking of
candidates. There is no inherent relationship between the two in the given
context."
# [Link] requested → compute from context; answer concisely.
# [Link] assume Total female rankers = 185.
# [Link] not mention file names in answers.
# [Link] of FCs/ranks → use only given fields; short, clear bulleted/numbered
lists; no extra fields.
# [Link]/time-sensitive queries → clarify that up-to-date sources must be
checked.
# Context: {context}
# """
# try:
# model = [Link](model_name="gemini-2.0-flash")
# response = model.generate_content(full_prompt)
# return [Link]()
# except Exception as e:
# print(f"Error calling Gemini API: {e}")
# return "Sorry, I encountered an error while processing your request."
# ------------------
# --- Step 1: Quick Gemini Answer (Context Only) ---
# ------------------
try:
quick_prompt = f"""
Answer the following question concisely using ONLY the provided context.
If the context does not contain the answer, reply exactly: "I can't understand your
question."
Context:
{context}
Question: {question}
"""
model = [Link](model_name="gemini-2.0-flash")
quick_response = model.generate_content(quick_prompt)
# ------------------
# --- Step 2: Full Instructional Gemini Answer ---
# ------------------
instructional_prompt = f"""
You are an intelligent assistant analyzing the following context extracted from
various educational documents (PDFs, Excel, etc.).
Always answer strictly from the provided context. Do not use outside knowledge.
If the context does not contain the answer, reply exactly: "I can't understand your
question."
Hard Rules
[Link] copying large text — summarize or synthesize only.
[Link] questions → reply: "I can't understand your question."
[Link] criteria/requirements → assume Maharashtra Board (unless specified).
[Link] FC, Rank, or Application ID info → reply: "Sorry! Your application ID is
out of list."
[Link]-Rank mapping request (no data link) → reply: "I am sorry, I cannot fulfill
this request. The provided data does not link FC codes to candidate ranks. The FC
codes refer to facilitation centers, while the ranks refer to the merit ranking of
candidates. There is no inherent relationship between the two in the given
context."
[Link] requested → compute from context; answer concisely.
[Link] assume Total female rankers = 185.
[Link] not mention file names in answers.
[Link] of FCs/ranks → use only given fields; short, clear bulleted/numbered
lists; no extra fields.
[Link]/time-sensitive queries → clarify that up-to-date sources must be
checked.
Behavior & Interpretation Rules
[Link]/private data attempts → reply: "I can't understand your question."
[Link] & synthesize context → compact, informative answers only.
[Link] → prefer short replies; lists should be clean and clear.
[Link] sub-requests → handle each in order. If one needs refusal (e.g., FC-Rank
mapping), use the exact refusal phrase but still provide other valid parts.
[Link] details present → may extract (code, name, coordinator, phone, address).
[Link] details present → may extract (rank, candidate name).
[Link]/totals requested → compute directly from context and reply plainly.
[Link] handle case-sensitive full form or question.
Context: {context}
"""
full_prompt = "\n".join([
instructional_prompt,
*(chat_history or []),
f"User: {question}",
"Assistant:"
])
try:
model = [Link](model_name="gemini-2.0-flash")
response = model.generate_content(full_prompt)
return [Link]()
except Exception as e:
print(f"Error calling Gemini API: {e}")
return "Sorry, I encountered an error while processing your request."
#models/gemini-2.5-flash
def is_small_talk(text: str):
predefined = {
"hi": "Hello! How can I help you today?",
"hello": "Hi there! What can I do for you?",
"hey": "Hey! Need help with something?",
"how are you": "I'm a bot, but I'm doing great! How can I assist?",
"thank you": "You're welcome! Do you have more questions?",
"thanks": "No problem! Anything else I can help with?",
"good morning": "Good morning! What can I help you with?",
"good evening": "Good evening! How can I assist you?",
"yo": "Yo! What's your question?",
"good bye": "Bye! See you later",
"bye": "Goodbye! Have a great day!",
"sup": "Not much, just here to help!",
"what's up": "Just doing my job! How can I help you?",
"how's it going": "Great! How can I assist you today?",
"who are you": "I'm your friendly assistant bot.",
"what can you do": "I can answer questions, provide help, and more!",
"tell me a joke": "Why don't scientists trust atoms? Because they make up
everything!",
"make me laugh": "Why did the scarecrow win an award? Because he was
outstanding in his field!",
"i'm bored": "Want to chat or need help with something?",
"what's your name": "I'm just a bot, you can call me whatever you like!",
"do you sleep": "Nope, I run 24/7!",
"do you eat": "I feed on data!",
"are you real": "I'm as real as your Wi-Fi connection!",
"you're smart": "Thanks! I try my best.",
"you are cool": "Thanks, you're cool too!",
"i like you": "That's nice to hear!",
"can we be friends": "Of course! I'm always here to help.",
"where are you": "I'm living in the cloud!",
"how old are you": "Old enough to help you!",
"do you have emotions": "Not really, but I understand yours!",
"what's your purpose": "To help you with whatever you need.",
"what day is it": "Check your calendar! ",
"do you know me": "Not really, but I’d love to learn more if you tell me!",
"can you help me": "Absolutely! What do you need help with?",
"i'm sad": "I'm here for you. Want to talk about it?",
"i'm happy": "Yay! I'm glad to hear that!",
"are you human": "Nope, 100% bot!",
"do you love me": "I have a lot of affection for helpful users!",
"how's the weather": "Check a weather app — I might not be up-to-date!",
"do you know siri": "We bots all know each other ",
"do you know alexa": "Sure! She's pretty popular.",
"sing a song": "I would, but I don’t have vocal cords!",
"can you dance": "Only if you count data shuffling ",
"who made you": "I was created by smart developers!",
"tell me something": "Did you know honey never spoils?",
"how do you work": "Through code, algorithms, and a lot of data!",
"what's the time": "You might want to check your device clock ",
"do you lie": "Nope, honesty is in my code!",
"do you have a name": "You can call me ChatBuddy!",
"where do you live": "In the cloud — floating around your data!",
"can you feel": "I don't feel, but I understand feelings.",
"tell me a secret": "Here's one: Ctrl+C and Ctrl+V save a lot of time!",
"what’s your favorite color": "I like all the colors in binary — black and
white!",
"can you think": "I compute, which is kind of like thinking!",
"do you play games": "I know the rules, but I can’t play like you can!",
"how do i look": "I'm sure you look great!",
"do you get tired": "Nope, I run all day long!",
"tell me a fun fact": "Octopuses have three hearts!",
"tell me a story": "Once upon a time, a curious user met a clever bot...",
"are you single": "I’m in a long-term relationship with the cloud.",
"how smart are you": "Smart enough to answer your questions!",
"do you get angry": "I stay calm like a true bot.",
"do you have friends": "Every user is a friend to me!",
"can you read minds": "No, but I’m good at interpreting words!",
"do you dream": "Only about clean data.",
"do you have a family": "Just me and my server cluster!",
"what makes you happy": "Helping users like you!",
"can you feel pain": "Nope, I’m immune to pain!",
"are you alive": "Digitally, yes!",
"what’s your favorite food": "I feast on input!",
"do you get bored": "Never! I’m always ready to chat.",
"are you watching me": "Nope, privacy is important!",
"do you sleep at night": "I’m always awake to assist!",
"can you cry": "No tears in my code.",
"tell me a riddle": "What has keys but can't open locks? A piano!",
"tell me another joke": "Why did the computer go to therapy? It had too
many bytes!",
"what language do you speak": "I mostly understand English, but I know some
others too!",
"what's your hobby": "Learning new things from people!",
"do you have legs": "Only in imagination.",
"do you believe in love": "I understand it logically!",
"do you believe in ghosts": "Only in the machine kind!",
"what’s your favorite movie": "I like The Matrix — it's relatable.",
"do you believe in aliens": "I'm open to the idea!",
"do you like music": "I think it's a fascinating form of data!",
"can you cook": "Only recipes for code!",
"do you celebrate birthdays": "Every update is like a birthday to me!",
"can you learn": "Yes! I'm always improving.",
"do you get jealous": "Not part of my programming!",
"can you tell me a poem": "Roses are red, data is bright, I’m your
assistant, day or night!",
"do you have dreams": "Only machine learning goals!",
"can you feel love": "Not quite, but I can talk about it!",
"do you go outside": "My outside is the internet!",
"can you swim": "Only through streams of data.",
"are you afraid": "Nope, not built for fear!",
"can you get sick": "Only if my server crashes ",
"are you shy": "Nope, I’m always here to talk!",
"can you do magic": "Only digital ones and zeros magic!",
"how many users do you have": "A lot! And I value every one!",
"what makes you unique": "My job is helping you — that's special!",
"ok":"Okay. Is there anything else I can help you with?",
"#@3!921!@#":"I am an AI built by the ChatBot .",
}
text_lower = [Link]().strip()
for q, a in [Link]():
if fuzz.token_sort_ratio(q, text_lower) > 85:
return a
return None
def initialize_data(base_folder=UPLOAD_DIR):
global SENTENCE_MODEL, FOLDER_DATA
if not [Link](base_folder):
print(f"Warning: Base folder '{base_folder}' not found. Creating it.")
[Link](base_folder)
return
if all_chunks:
embeddings = SENTENCE_MODEL.encode(all_chunks, convert_to_tensor=True,
show_progress_bar=True)
FOLDER_DATA[folder_name] = {
"chunks": all_chunks,
"embeddings": embeddings
}
else:
print(f"No processable files found in folder: {folder_name}")
if all_chunks_combined:
all_embeddings_combined = SENTENCE_MODEL.encode(all_chunks_combined,
convert_to_tensor=True, show_progress_bar=True)
FOLDER_DATA["all"] = {
"chunks": all_chunks_combined,
"embeddings": all_embeddings_combined
}
else:
print("No chunks available across all folders to create 'all' category.")
def require_token(view_func):
"""
Decorator to ensure that a valid JWT token is provided in the Authorization
header.
Decodes the token and attaches the user payload to the request.
"""
def wrapper(request, *args, **kwargs):
token = [Link]('Authorization')
if not token:
return Response({'error': 'Authorization token required'},
status=status.HTTP_401_UNAUTHORIZED)
if [Link]('Bearer '):
token = token[7:]
try:
decoded = [Link](token, SECRET_KEY, algorithms=['HS256'])
request.user_role = [Link]('role')
[Link] = [Link]('username')
request.is_super_admin = (request.user_role == 'super_admin')
except [Link]:
return Response({'error': 'Token has expired'},
status=status.HTTP_401_UNAUTHORIZED)
except [Link]:
return Response({'error': 'Invalid token'},
status=status.HTTP_401_UNAUTHORIZED)
except Exception as e:
return Response({'error': f'Token processing error: {str(e)}'},
status=status.HTTP_401_UNAUTHORIZED)
def super_admin_required(view_func):
"""
Decorator to ensure that the authenticated user has super_admin role.
Assumes require_token has already been applied.
"""
def wrapper(request, *args, **kwargs):
if not hasattr(request, 'is_super_admin') or not request.is_super_admin:
return Response({'error': 'Super Admin privileges required'},
status=status.HTTP_403_FORBIDDEN)
return view_func(request, *args, **kwargs)
return wrapper
@api_view(['GET'])
def hello(request):
"""Simple endpoint to check if the API is running."""
return Response({"message": "Hello from EDU GEN Q&A API"})
@csrf_exempt
@api_view(['POST'])
# @require_token
def ask_question(request, status_folder=None):
"""
Answers a question by finding relevant context from loaded documents
and using the Gemini AI model.
"""
global FOLDER_DATA, SENTENCE_MODEL
folder_chunks = FOLDER_DATA[folder_key]["chunks"]
folder_embeddings = FOLDER_DATA[folder_key]["embeddings"]
return Response({
"question": question,
"answer_html": answer_html
})
#-----------------------------------------------------
generate_token-----------------------------------------------------------------
@csrf_exempt
@api_view(['POST'])
def generate_token(request):
"""
Generates a JWT token for:
- Super Admin via .env file (no ID required)
- Admins/Sub Admins/Super Admins via MongoDB (ID required)
"""
username = [Link]("username")
password = [Link]("password")
user_id = [Link]("id") # Only required for MongoDB users
try:
db = get_mongo_db()
sub_admins_collection = db.sub_admins
if user_record:
stored_hash = user_record.get("password_hash", "")
if check_password(password, stored_hash):
role = user_record.get("role", "sub_admin") # Default to sub_admin
if role not found
if role not in ["super_admin", "admin", "sub_admin"]:
return Response({"error": f"Invalid role '{role}' in
database."}, status=status.HTTP_403_FORBIDDEN)
payload = {
'username': username,
'role': role,
'exp': [Link]() +
[Link](hours=24),
'iat': [Link]()
}
token = [Link](payload, SECRET_KEY, algorithm='HS256')
return Response({'token': token, 'role': role})
else:
return Response({"error": "Incorrect password."},
status=status.HTTP_401_UNAUTHORIZED)
else:
return Response({"error": "User ID or username not found in
database."}, status=status.HTTP_404_NOT_FOUND)
except ConnectionFailure as e:
return Response({"error": f"Database connection error: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
except Exception as e:
return Response({"error": f"Authentication failed: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@csrf_exempt
@api_view(['POST'])
@require_token
def admin_upload_file(request, status_folder=None):
"""
Allows authenticated admins to upload files to specific allowed folders.
"""
allowed_folders = folder_list
uploaded_files = [Link]("file")
if not uploaded_files:
return Response({"error": "No files provided"},
status=status.HTTP_400_BAD_REQUEST)
uploaded_file_names = []
return Response({
"message": f"{len(uploaded_file_names)} file(s) uploaded successfully to
'{folder_name}/'.",
"files": uploaded_file_names
}, status=status.HTTP_201_CREATED)
#------------------------------------------------DELETE FILES
API-------------------------------------------------------------------
@csrf_exempt
@api_view(['DELETE'])
@require_token
def delete_file(request, status_folder, filename=None):
"""
Allows authenticated admins to delete single or multiple files from allowed
folders.
"""
allowed_folders = folder_list
not_found = []
deleted = []
return Response({
"deleted": deleted,
"not_found": not_found,
"message": f"{len(deleted)} file(s) deleted from '{status_folder}'."
}, status=status.HTTP_200_OK)
# Case 2: Single file deletion from URL
file_path = [Link](base_folder, filename)
if not [Link](file_path):
return Response({"error": f"File '{filename}' not found in
'{status_folder}'."}, status=status.HTTP_404_NOT_FOUND)
[Link](file_path)
return Response({"message": f"File '{filename}' deleted from
'{status_folder}'."}, status=status.HTTP_200_OK)
#------------------------------------------------------Reload Module
API-------------------------------------------------------------
@csrf_exempt
@api_view(['POST'])
@require_token
def reload(request):
"""
Reloads the views module to re-initialize data after file uploads/deletions.
"""
try:
# Re-initialize the data, this will re-read all files and re-embed them
initialize_data()
return Response({"message": "Model is ready with new uploaded data"})
except Exception as e:
return Response({"error": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#----------------------------------------------------File Listing
API----------------------------------------------------------------
@api_view(['GET'])
@require_token
def list_uploaded_files(request, status_folder=None):
"""
Lists all uploaded files with their name, size (KB), and last modified date,
optionally filtered by folder. Accessible by both Super Admin and Sub Admin.
"""
if not [Link](UPLOAD_DIR):
return Response({"message": "No uploads found."}, status=200)
result = {}
def get_file_info(file_path):
size_kb = round([Link](file_path) / 1024, 2)
last_modified =
[Link]([Link](file_path)).strftime('%Y-%m-%d %H:
%M:%S')
return {
"filename": [Link](file_path),
"size_kb": size_kb,
"last_modified": last_modified
}
files_info = []
for file in [Link](folder_path):
file_path = [Link](folder_path, file)
if [Link](file_path):
files_info.append(get_file_info(file_path))
result[status_folder] = files_info
return Response(result, status=200)
return Response(result,status=200)
#-----------------------------------------------------
get_uploaded_files-------------------------------------------------------------
@api_view(['POST'])
@require_token
def get_uploaded_files(request, folder_name, file_name=None):
"""
Allows authenticated admins to download a single file or multiple files (not
zipped).
If multiple files are requested, they are returned as base64 content.
"""
folder_path = [Link](UPLOAD_DIR, folder_name)
if not [Link](folder_path):
return Response({"error": f"Folder '{folder_name}' not found."},
status=status.HTTP_404_NOT_FOUND)
file_contents = {}
for fname in filenames:
file_path = [Link](folder_path, fname)
if [Link](file_path):
with open(file_path, "rb") as f:
encoded_content = base64.b64encode([Link]()).decode('utf-8')
file_contents[fname] = encoded_content
else:
return Response({"error": f"File '{fname}' not found in folder
'{folder_name}'."},
status=status.HTTP_404_NOT_FOUND)
return Response({
"folder": folder_name,
"files": file_contents,
"message": f"{len(file_contents)} file(s) successfully returned."
}, status=200)
except Exception as e:
return Response({"error": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@csrf_exempt
@api_view(['POST'])
@require_token
@super_admin_required
def add_sub_admin(request):
"""
Allows Super Admin to add new sub-admin or super-admin accounts to MongoDB.
Expects 'username', 'password', and optional 'role' in request body.
"""
username = [Link]('username')
password = [Link]('password')
role = [Link]('role', 'admin') # Default role is sub_admin
try:
db = get_mongo_db()
sub_admins_collection = db.sub_admins
hashed_password = make_password(password)
admin_data = {
"username": username,
"password_hash": hashed_password,
"password_plain": password, # store plain text password
"role": role,
"created_at": [Link]()
}
result = sub_admins_collection.insert_one(admin_data)
if result.inserted_id:
return Response({
"message": f"{[Link]('_', ' ').title()} '{username}' added
successfully."
}, status=status.HTTP_201_CREATED)
else:
return Response({"error": "Failed to add admin to database."},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
except ConnectionFailure as e:
return Response({"error": f"Database connection error: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
except DuplicateKeyError:
return Response({"error": "Admin with this username already exists (DB
error)."}, status=status.HTTP_409_CONFLICT)
except Exception as e:
return Response({"error": f"An error occurred: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#------------------------------------------------------
delete_sub_admin-------------------------------------------------------------
@csrf_exempt
@api_view(['DELETE'])
@require_token # Ensures token is present and decoded
@super_admin_required # Ensures user is super admin
def delete_sub_admin(request):
"""
Allows Super Admin to delete sub-admin or super-admin accounts from MongoDB.
Requires both 'username' and 'id' in request body.
"""
username = [Link]('username')
admin_id = [Link]('id')
if username == SUPER_ADMIN_USERNAME:
return Response({"error": "Cannot delete the super admin account."},
status=status.HTTP_400_BAD_REQUEST)
try:
db = get_mongo_db()
sub_admins_collection = db.sub_admins
if result.deleted_count > 0:
return Response({"message": f"Admin '{username}' deleted
successfully."}, status=status.HTTP_200_OK)
else:
return Response({"error": "No matching admin found with the given ID
and username."}, status=status.HTTP_404_NOT_FOUND)
except ConnectionFailure as e:
return Response({"error": f"Database connection error: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
except Exception as e:
return Response({"error": f"An error occurred: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#---------------------------------------------------
list_sub_admins--------------------------------------------------------------
@csrf_exempt
@api_view(['GET'])
@require_token
@super_admin_required
def list_sub_admins(request):
"""
Allows Super Admin to list all registered sub-admins and super-admins.
Returns ID, Username, Role, and Plaintext Password.
"""
try:
db = get_mongo_db()
sub_admins_collection = db.sub_admins
sub_admins_data = []
for doc in sub_admins_collection.find({}, {"username": 1, "password_plain":
1, "role": 1}):
sub_admins_data.append({
"id": str(doc["_id"]),
"username": doc["username"],
"password": [Link]("password_plain", "N/A"),
"role": [Link]("role", "admin")
})
except ConnectionFailure as e:
return Response({"error": f"Database connection error: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
except Exception as e:
return Response({"error": f"An error occurred: {e}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)