quamble / models.py
raj-tomar001's picture
Update models.py
46e737c verified
import mysql.connector
from flask_bcrypt import Bcrypt
from flask import request, jsonify
import logging
import json
import traceback
from datetime import datetime
from question_verify import verify_question
from flask_jwt_extended import get_jwt_identity
import uuid
import random
import re
import json
from generate_question import generate_ques
import os
import pymysql
logging.basicConfig(
filename=os.path.join('/tmp', 'app.log'),
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
bcrypt = Bcrypt()
# CA_CERT_PATH = os.path.join(os.getcwd(), 'ca.pem')
# assert os.path.exists(CA_CERT_PATH), f"Missing: {CA_CERT_PATH}"
def get_db_connection():
try:
connection = pymysql.connect(
host="quamble-quamble.c.aivencloud.com",
port=17939,
user="avnadmin",
password=os.getenv("DB_PASSWORD"),
database="quamble",
ssl={'ca': '/app/ca.pem'},
cursorclass=pymysql.cursors.DictCursor
)
print("Database connection established.")
logging.info("Database connection established.")
return connection
except mysql.connector.Error as err :
print(f"Error: {err}")
logging.error("Error connecting to the database: %s", err)
return None
def add_user(username, email, password, role, preference_1=None, preference_2=None, preference_3=None, preference_4=None):
try:
user_id = str(uuid.uuid4())
connection = get_db_connection()
if connection:
cursor = connection.cursor()
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
cursor.execute("""
INSERT INTO users (id, username, email, password, role, preference_1, preference_2, preference_3, preference_4)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
""", (user_id, username, email, hashed_password, role, preference_1, preference_2, preference_3, preference_4))
connection.commit()
logging.info("User added: %s", username)
except mysql.connector.Error as err:
logging.error("Error adding user %s: %s", username, err)
finally:
#cursor.close()
#connection.close()
logging.info("Database connection closed after adding user.")
def get_user_by_email(email):
connection = get_db_connection()
if connection:
try:
cursor = connection.cursor()
cursor.execute("SELECT * FROM users WHERE email=%s", (email,))
user = cursor.fetchone()
logging.info("User retrieved: %s", user)
return user
except mysql.connector.Error as err:
logging.error("Error retrieving user by email %s: %s", email, err)
return None
finally:
cursor.close()
connection.close()
logging.info("Database connection closed after retrieving user.")
def add_quiz(user_id, theme, questions, correct_options):
connection = get_db_connection()
if not connection:
logging.error("Database connection failed.")
return None
try:
cursor = connection.cursor()
cursor.execute("SELECT theme_quiz_table FROM Themes WHERE theme = %s", (theme,))
theme_data = cursor.fetchone()
if not theme_data:
logging.error("Invalid theme provided: %s", theme)
return None
theme_table = theme_data[0]
cursor.execute("SELECT COUNT(*) FROM users WHERE id = %s", (user_id,))
user_exists = cursor.fetchone()[0]
if user_exists == 0:
logging.error("User ID does not exist: %s", user_id)
return None
cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id, theme))
quiz_id = cursor.lastrowid
questions_json = json.dumps(questions)
correct_options_json = json.dumps(correct_options)
query = f"""
INSERT INTO {theme_table} (quiz_id, user_id_creator, questions_by_llm, correct_options_llm)
VALUES (%s, %s, %s, %s)
"""
cursor.execute(query, (quiz_id, user_id, questions_json, correct_options_json))
connection.commit()
logging.info("Quiz added for user_id %s with theme %s in table %s", user_id, theme, theme_table)
return quiz_id
except mysql.connector.Error as err:
logging.error("Error adding quiz: %s", err)
return None
finally:
if cursor:
cursor.close()
if connection:
connection.close()
logging.info("Database connection closed after adding quiz.")
def add_question_from_master():
if request.method == 'POST':
logging.info("Received a POST request to add questions to quiz.")
theme = request.form.get('theme')
if not theme:
logging.error("Theme not provided in the request.")
return jsonify({"error": "Please provide the theme."}), 400
question = request.form.get('question')
correct_option = request.form.get('correct_option')
if not question or not correct_option:
logging.error("Missing question or correct option in the request.")
return jsonify({"error": "Please provide both question and correct option."}), 400
question = question.replace("\t", "")
validation, feedback, difficulty = verify_question(question, correct_option)
if validation != 'valid':
logging.warning(f"Verification failed: {feedback}")
return jsonify({"error": f"Verification failed: {feedback}"}), 400
logging.info(f"Verified question: {question}")
logging.info(f"Verified correct option: {correct_option}")
logging.info(f"Difficulty level: {difficulty}")
user_id_creator = get_jwt_identity()
logging.info(f"Authenticated user ID: {user_id_creator}")
connection = get_db_connection()
if connection:
try:
cursor = connection.cursor()
cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
result = cursor.fetchone()
if not result:
logging.error("Invalid theme provided: %s", theme)
return jsonify({"error": "Invalid theme provided."}), 400
theme_bank = result[0]
question_json = json.dumps({"question": question})
check_query = f"""
SELECT COUNT(*)
FROM {theme_bank}
WHERE question_by_master = %s AND user_id_creator = %s
"""
cursor.execute(check_query, (question_json, user_id_creator))
is_duplicate = cursor.fetchone()[0] > 0
if is_duplicate:
logging.warning("Duplicate question detected for user %s: %s", user_id_creator, question)
return jsonify({"error": "Duplicate question detected."}), 400
insert_query = f"""
INSERT INTO {theme_bank} (user_id_creator, question_by_master, correct_option_master, Difficulty)
VALUES (%s, %s, %s, %s)
"""
cursor.execute(insert_query, (user_id_creator, question_json, correct_option, difficulty))
connection.commit()
logging.info("Question added for theme %s", theme)
return 'True'
except mysql.connector.Error as err:
logging.error("Error adding question: %s", err)
return jsonify({"error": "Failed to add question to the database."}), 500
finally:
#cursor.close()
#connection.close()
logging.info("Database connection closed.")
else:
logging.error("Database connection failed.")
return jsonify({"error": "Database connection failed."}), 500
def add_ques_llm(theme, question, options, correct_option, difficulty):
connection = get_db_connection()
if connection:
try:
cursor = connection.cursor()
cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
theme_bank_result = cursor.fetchone()
if not theme_bank_result:
logging.error("Invalid theme provided: %s", theme)
return None
theme_bank = theme_bank_result["theme_bank"]
user_id_creator = get_jwt_identity()
question_json = json.dumps({
"question": question,
"options": options
})
check_query = f"SELECT COUNT(*) FROM {theme_bank} WHERE question_by_llm = %s"
cursor.execute(check_query, (question_json,))
is_duplicate = cursor.fetchone()["COUNT(*)"] > 0
if is_duplicate:
logging.warning("Duplicate question detected for theme %s: %s", theme, question)
return "Duplicate"
insert_query = f"""
INSERT INTO {theme_bank} (user_id_creator, question_by_llm, correct_option_llm, Difficulty)
VALUES (%s, %s, %s, %s)
"""
cursor.execute(insert_query, (user_id_creator, question_json, correct_option, difficulty))
connection.commit()
logging.info("Question added for theme %s", theme)
return 'True'
except mysql.connector.Error as err:
logging.error("Error adding question: %s", err)
return None
finally:
#cursor.close()
#connection.close()
logging.info("Database connection closed after adding question.")
else:
logging.error("Database connection failed.")
return None
def add_question_to_db(theme, question, correct_option, difficulty):
connection = get_db_connection()
try:
cursor = connection.cursor()
theme_check_query = "SELECT COUNT(*) FROM themes WHERE theme = %s"
cursor.execute(theme_check_query, (theme,))
theme_exists = cursor.fetchone()["COUNT(*)"]
if theme_exists == 0:
insert_query = "INSERT INTO themes (theme, theme_bank, theme_quiz_table) VALUES (%s, %s, %s)"
cursor.execute(insert_query, (theme, f"{theme}_bank", f"theme_{theme}"))
safe_theme = re.sub(r'\W+', '_', theme)
table_creation_query = f"""
CREATE TABLE IF NOT EXISTS {safe_theme}_bank (
ques_id INT AUTO_INCREMENT PRIMARY KEY,
user_id_creator VARCHAR(64),
question_by_llm MEDIUMTEXT,
question_hash VARCHAR(640),
correct_option_llm MEDIUMTEXT,
question_by_master MEDIUMTEXT,
correct_option_master MEDIUMTEXT,
Difficulty VARCHAR(64),
is_used VARCHAR(64),
Issue VARCHAR(6400)
)
"""
cursor.execute(table_creation_query)
theme_table_creation_query = f"""
CREATE TABLE IF NOT EXISTS theme_{safe_theme} (
quiz_id INT(8) PRIMARY KEY AUTO_INCREMENT,
user_id_creator VARCHAR(64),
questions_by_llm MEDIUMTEXT,
correct_options_llm MEDIUMTEXT,
questions_by_master MEDIUMTEXT,
correct_options_master MEDIUMTEXT,
num_questions INT(11),
user_id_attempt MEDIUMTEXT,
user_responses MEDIUMTEXT
)
"""
cursor.execute(theme_table_creation_query)
check_query = f"SELECT COUNT(*) FROM `{safe_theme}_bank` WHERE question_by_llm = %s"
cursor.execute(check_query, (question,))
is_duplicate = cursor.fetchone()["COUNT(*)"] > 0
if is_duplicate:
logging.warning("Duplicate question detected for theme %s: %s", safe_theme, question)
return "Duplicate"
query = f"INSERT INTO {safe_theme}_bank (question_by_llm, correct_option_llm, Difficulty, is_used) VALUES (%s, %s, %s, 'unused')"
cursor.execute(query, (question, correct_option, difficulty))
connection.commit()
return 'True'
except mysql.connector.Error as err:
logging.error("Error adding question: %s", err)
return None
finally:
#cursor.close()
#connection.close()
logging.info("Database connection closed")
def create_quiz(theme, num_questions):
connection = get_db_connection()
if connection:
try:
cursor = connection.cursor()
cursor.execute("SELECT theme_bank, theme_quiz_table FROM themes WHERE theme = %s", (theme,))
theme_data = cursor.fetchone()
if not theme_data:
logging.error("Invalid theme provided: %s", theme)
return jsonify({"error": "Invalid theme provided."}), 400
theme_bank, theme_quiz_table = theme_data
query_available_questions = f"""
SELECT COUNT(*)
FROM {theme_bank}
WHERE question_by_llm IS NOT NULL
AND question_by_llm != ''
AND (is_used IS NULL OR is_used = '')
"""
cursor.execute(query_available_questions)
available_count = cursor.fetchone()[0]
if available_count < int(num_questions):
logging.warning("Not enough questions available for theme %s: Available = %d, Requested = %d", theme, available_count, num_questions)
return jsonify({"error": "Not enough questions available."}), 400
query_random_questions = f"""
SELECT ques_id, question_by_llm, correct_option_llm
FROM {theme_bank}
WHERE question_by_llm IS NOT NULL
AND question_by_llm != ''
AND (is_used IS NULL OR is_used = '')
ORDER BY RAND()
LIMIT {int(num_questions)}
"""
cursor.execute(query_random_questions)
selected_questions = cursor.fetchall()
questions = []
correct_options = []
selected_question_ids = []
for question_id, question, correct_option in selected_questions:
questions.append(question)
correct_options.append(correct_option)
selected_question_ids.append(question_id)
mark_as_used_query = f"""
UPDATE {theme_bank}
SET is_used = 'used'
WHERE ques_id IN ({', '.join(map(str, selected_question_ids))})
"""
cursor.execute(mark_as_used_query)
user_id_creator = get_jwt_identity()
cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id_creator, theme))
quiz_id = cursor.lastrowid
questions_json = json.dumps(questions)
correct_options_json = json.dumps(correct_options)
insert_quiz_query = f"""
INSERT INTO {theme_quiz_table} (quiz_id, user_id_creator, questions_by_llm, correct_options_llm, num_questions)
VALUES (%s, %s, %s, %s, %s)
"""
cursor.execute(insert_quiz_query, (quiz_id, user_id_creator, questions_json, correct_options_json, num_questions))
connection.commit()
logging.info("Quiz created successfully for theme %s with %d questions.", theme, num_questions)
questions_reflect = json.loads(questions_json)
correct_options_reflect = json.loads(correct_options_json)
return jsonify({
"quiz_id": quiz_id,
"questions": questions_reflect,
"correct_options": correct_options_reflect
}), 201
except mysql.connector.Error as err:
logging.error("Error creating quiz: %s", err)
return jsonify({"error": "Failed to create quiz."}), 500
except Exception as e:
logging.error("An unexpected error occurred while creating the quiz: %s", str(e))
return jsonify({"error": "An unexpected error occurred."}), 500
finally:
if cursor:
cursor.close()
if connection:
connection.close()
logging.info("Database connection closed.")
else:
logging.error("Database connection failed.")
return jsonify({"error": "Database connection failed."}), 500
def create_quiz_master(user_id_creator, theme, num_questions):
connection = get_db_connection()
if connection:
try:
cursor = connection.cursor()
cursor.execute("SELECT theme_bank, theme_quiz_table FROM themes WHERE theme = %s", (theme,))
theme_data = cursor.fetchone()
if not theme_data:
logging.error("Invalid theme provided: %s", theme)
return jsonify({"error": "Invalid theme provided."}), 400
theme_bank, theme_quiz_table = theme_data
query_available_questions = f"""
SELECT COUNT(*) FROM {theme_bank} WHERE question_by_master IS NOT NULL AND is_used IS NULL OR is_used = "unused"
"""
cursor.execute(query_available_questions)
available_count = cursor.fetchone()[0]
if available_count < int(num_questions):
logging.warning("Not enough questions available for theme %s: Available = %d, Requested = %d", theme, available_count, num_questions)
return jsonify({"error": "Not enough questions available."}), 400
query_random_questions = f"""
SELECT ques_id, question_by_master, correct_option_master
FROM {theme_bank}
WHERE question_by_master IS NOT NULL AND question_by_master != ''
ORDER BY RAND()
LIMIT {int(num_questions)}
"""
cursor.execute(query_random_questions)
selected_questions = cursor.fetchall()
questions, correct_options, selected_question_ids = [], [], []
for question_id, question, correct_option in selected_questions:
questions.append(question)
correct_options.append(correct_option)
selected_question_ids.append(question_id)
mark_as_used_query = f"""
UPDATE {theme_bank}
SET is_used = 'used'
WHERE ques_id IN ({', '.join(map(str, selected_question_ids))})
"""
cursor.execute(mark_as_used_query)
cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id_creator, theme))
quiz_id = cursor.lastrowid
insert_quiz_query = f"""
INSERT INTO {theme_quiz_table} (quiz_id, user_id_creator, questions_by_master, correct_options_master, num_questions)
VALUES (%s, %s, %s, %s, %s)
"""
cursor.execute(insert_quiz_query, (quiz_id, user_id_creator, json.dumps(questions), json.dumps(correct_options), num_questions))
connection.commit()
return jsonify({
"quiz_id": quiz_id,
"ques_id": selected_question_ids,
"correct_options": correct_options
}), 201
except mysql.connector.Error as err:
logging.error("Error creating quiz: %s", err)
return jsonify({"error": "Failed to create quiz."}), 500
except Exception as e:
logging.error("An unexpected error occurred while creating the quiz: %s", str(e))
return jsonify({"error": "An unexpected error occurred."}), 500
finally:
if cursor:
cursor.close()
if connection:
connection.close()
logging.info("Database connection closed.")
else:
logging.error("Database connection failed.")
return jsonify({"error": "Database connection failed."}), 500
def recording_issue(theme, ques_id, issue_description):
connection = get_db_connection()
if connection:
try:
cursor = connection.cursor()
cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
theme_data = cursor.fetchone()
if not theme_data:
logging.error("Invalid theme provided: %s", theme)
return jsonify({"error": "Invalid theme provided."}), 400
theme_bank = theme_data[0]
select_query = f"SELECT COUNT(*) FROM {theme_bank} WHERE ques_id = %s"
cursor.execute(select_query, (ques_id,))
count = cursor.fetchone()[0]
if count == 0:
return jsonify({"error": f"Question ID {ques_id} does not exist in the database"}), 400
update_query = f"""
UPDATE {theme_bank}
SET Issue = %s
WHERE ques_id = %s
"""
cursor.execute(update_query, (issue_description, ques_id))
connection.commit()
logging.info("Issue recorded for theme %s and question_id %s.", theme, ques_id)
return jsonify({"message": "Issue reported successfully"}), 201
except mysql.connector.Error as err:
return jsonify({"error": "An error occurred while reporting the issue", "details": str(err)}), 500
finally:
cursor.close()
connection.close()
logging.info("Database connection closed.")
else:
logging.error("Database connection failed.")
return jsonify({"error": "Database connection failed."}), 500
def record_feedback(user_id, rating, comments):
connection = get_db_connection()
if connection:
try:
cursor = connection.cursor()
insert_query= """
INSERT INTO feedbacks (user_id, rating, comments)
VALUES (%s, %s, %s)
"""
cursor.execute(insert_query, (user_id, rating, comments))
# Commit the transaction and close the connection
connection.commit()
cursor.close()
connection.close()
return jsonify({"message": "Feedback submitted successfully"}), 201
except mysql.connector.Error as err:
print(f"Error: {err}")
return jsonify({"error": "Failed to submit feedback"}), 500
def create_quiz_by_id(theme, num_questions):
connection = get_db_connection()
if not connection:
logging.error("Database connection failed.")
return jsonify({"error": "Database connection failed."}), 500
try:
cursor = connection.cursor()
cursor.execute("SELECT theme_bank FROM themes WHERE theme = %s", (theme,))
result = cursor.fetchone()
if not result:
logging.error("Invalid theme provided: %s", theme)
return jsonify({"error": "Invalid theme provided."}), 400
theme_bank = result[0]
cursor.execute(f"""
SELECT COUNT(*) FROM {theme_bank} WHERE question_by_llm IS NOT NULL AND is_used IS NULL OR is_used = "unused"
""")
available_count = cursor.fetchone()[0]
shortfall = int(num_questions) - available_count
logging.info("Available questions: %d; Required: %d; Shortfall: %d", available_count, int(num_questions), shortfall)
added_count = 0
attempts = 0
max_attempts = shortfall * 10
while added_count < shortfall and attempts < max_attempts:
attempts += 1
ques = generate_ques(theme)
question_match = re.search(r'Question:\s*(.*)', ques)
options_matches = re.findall(r'([A-D])\)\s*(.*)', ques)
correct_answer_match = re.search(r'Correct answer:\s*([A-D])', ques)
difficulty_match = re.search(r'Difficulty level:\s*(.*)', ques)
if question_match and options_matches and correct_answer_match:
question = question_match.group(1).strip()
options = [f"{opt[0]}) {opt[1].strip()}" for opt in options_matches]
correct_option = correct_answer_match.group(1).strip().upper()
difficulty = difficulty_match.group(1).strip() if difficulty_match else "medium"
if correct_option not in ['A', 'B', 'C', 'D']:
continue
status = add_ques_llm(theme, question, options, correct_option, difficulty)
if status == "True":
added_count += 1
elif status == "Duplicate":
continue # retry
else:
logging.warning("Failed to insert generated question.")
else:
logging.warning("Malformed question generated; skipping.")
if added_count < shortfall:
return jsonify({"error": f"Could not generate enough unique questions. Needed {shortfall}, added {added_count}."}), 400
cursor.execute(f"""
SELECT COUNT(*) FROM {theme_bank} WHERE question_by_llm IS NOT NULL AND is_used IS NULL OR is_used = "unused"
""")
available_count = cursor.fetchone()[0]
if available_count < int(num_questions):
logging.warning("Not enough questions available for theme %s: Available = %d, Requested = %d", theme, available_count, num_questions)
return jsonify({"error": "Not enough questions available."}), 400
cursor.execute(f"""
SELECT ques_id, correct_option_llm FROM {theme_bank}
WHERE question_by_llm IS NOT NULL AND question_by_llm != ''
AND (is_used IS NULL OR is_used = '')
ORDER BY RAND() LIMIT {int(num_questions)}
""")
selected_questions = cursor.fetchall()
questions = [q[0] for q in selected_questions]
correct_options = [q[1] for q in selected_questions]
cursor.execute(f"""
UPDATE {theme_bank} SET is_used = 'used'
WHERE ques_id IN ({', '.join(map(str, questions))})
""")
user_id_creator = get_jwt_identity()
cursor.execute("INSERT INTO quiz (user_id_creator, theme) VALUES (%s, %s)", (user_id_creator, theme))
quiz_id = cursor.lastrowid
cursor.execute("SELECT theme_quiz_table FROM themes WHERE theme = %s", (theme,))
theme_table = cursor.fetchone()[0]
cursor.execute(f"""
INSERT INTO {theme_table} (quiz_id, user_id_creator, questions_by_llm, correct_options_llm, num_questions)
VALUES (%s, %s, %s, %s, %s)
""", (quiz_id, user_id_creator, json.dumps(questions), json.dumps(correct_options), num_questions))
connection.commit()
logging.info("Quiz created successfully for theme %s with %d questions.", theme, num_questions)
return jsonify({"quiz_id": quiz_id, "questions": questions, "correct_options": correct_options}), 201
except mysql.connector.Error as err:
logging.error("Error creating quiz: %s", err)
return jsonify({"error": "Failed to create quiz."}), 500
except Exception as e:
logging.error("An unexpected error occurred while creating the quiz: %s", str(e))
return jsonify({"error": "An unexpected error occurred."}), 500
finally:
if cursor:
cursor.close()
if connection:
connection.close()
logging.info("Database connection closed.")
def add_theme_if_not_exists(theme):
connection = get_db_connection()
try:
cursor = connection.cursor()
bank_table = f"{theme}_bank"
quiz_table = f"theme_{theme}"
theme_check_query = "SELECT COUNT(*) FROM themes WHERE theme = %s"
cursor.execute(theme_check_query, (theme,))
theme_exists = cursor.fetchone()["COUNT(*)"]
if theme_exists == 0:
insert_query = "INSERT INTO themes (theme, theme_bank, theme_quiz_table) VALUES (%s, %s, %s)"
cursor.execute(insert_query, (theme, f"{theme}_bank", f"theme_{theme}"))
connection.commit()
create_bank_table = f"""
CREATE TABLE IF NOT EXISTS `{bank_table}` (
ques_id INT PRIMARY KEY AUTO_INCREMENT,
user_id_creator VARCHAR(64),
question_by_llm MEDIUMTEXT,
question_hash VARCHAR(640),
correct_option_llm MEDIUMTEXT,
question_by_master MEDIUMTEXT,
correct_option_master MEDIUMTEXT,
Difficulty VARCHAR(64),
is_used VARCHAR(64),
Issue VARCHAR(6400)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
"""
cursor.execute(create_bank_table)
logging.info("Created table: %s", bank_table)
# Create theme_ table
create_quiz_table = f"""
CREATE TABLE IF NOT EXISTS `{quiz_table}` (
quiz_id INT PRIMARY KEY AUTO_INCREMENT,
user_id_creator VARCHAR(64),
questions_by_llm MEDIUMTEXT,
correct_options_llm MEDIUMTEXT,
questions_by_master MEDIUMTEXT,
correct_options_master MEDIUMTEXT,
num_questions INT,
user_id_attempt MEDIUMTEXT,
user_responses MEDIUMTEXT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
"""
cursor.execute(create_quiz_table)
logging.info("Created table: %s", quiz_table)
except mysql.connector.Error as err:
logging.error("Error adding theme: %s", err)
return jsonify({"error": "Failed to add theme."}), 500
finally:
cursor.close()
connection.close()
logging.info("Database connection is closed")
return not theme_exists
def profile_added_db(first_name, last_name, organisation, industry, bio):
connection = get_db_connection()
if connection:
try:
cursor = connection.cursor()
user_id_creator = get_jwt_identity()
logging.info(f"Authenticated user ID: {user_id_creator}")
query_check_user = "SELECT COUNT(*) AS user_count FROM users WHERE id = %s"
cursor.execute(query_check_user, (user_id_creator,))
user_exists = cursor.fetchone()['user_count']
if user_exists:
query_update_user = """
UPDATE users
SET first_name = %s, last_name = %s, organisation = %s, industry = %s, bio = %s
WHERE id = %s
"""
cursor.execute(query_update_user, (first_name, last_name, organisation, industry, bio, user_id_creator))
connection.commit()
logging.info(f"Profile for user {user_id_creator} updated successfully.")
return jsonify({"message": "Profile updated successfully!"}), 200
else:
logging.warning(f"User with ID {user_id_creator} does not exist.")
return jsonify({"error": "User does not exist."}), 404
except Exception as e:
logging.error(f"Unexpected error: {e}")
return jsonify({"error":"Failed to update profile"}), 500
finally:
cursor.close()
connection.close()
logging.info("Database connection closed.")
def view_profile_db():
try:
user_id_creator = get_jwt_identity()
logging.info(f"Authenticated user ID: {user_id_creator}")
connection = get_db_connection()
if connection:
cursor = connection.cursor()
query_check_user = "SELECT COUNT(*) AS user_count FROM users WHERE id = %s"
cursor.execute(query_check_user, (user_id_creator,))
user_exists = cursor.fetchone()['user_count']
if user_exists:
query_get_profile = """
SELECT first_name, last_name, role, organisation, industry, bio
FROM users
WHERE id = %s
"""
cursor.execute(query_get_profile, (user_id_creator,))
profile = cursor.fetchone()
if profile:
logging.info(f"User {user_id_creator}'s profile fetched successfully.")
return jsonify({
"first_name": profile["first_name"],
"last_name": profile["last_name"],
"role": profile["role"],
"organisation": profile["organisation"],
"industry": profile["industry"],
"bio": profile["bio"]
}), 200
else:
logging.warning(f"Profile for user {user_id_creator} not found.")
return jsonify({"error": "Profile not found"}), 404
else:
logging.warning(f"User with ID {user_id_creator} does not exist.")
return jsonify({"error": "User does not exist."}), 404
except Exception as e:
logging.error(f"Unexpected error: {e}")
return jsonify({"error": f"Unexpected error: {str(e)}"}), 500
finally:
cursor.close()
connection.close()
logging.info("Database connection closed.")
def view_quiz_score_db(user_id, theme_table, theme, quiz_id):
try:
connection = get_db_connection()
if connection:
cursor = connection.cursor()
query = f"""
SELECT num_questions
FROM {theme_table}
WHERE quiz_id = %s
"""
cursor.execute(query, (quiz_id,))
theme_data = cursor.fetchone()
num_questions = theme_data['num_questions'] if theme_data else None
if num_questions is None:
logging.warning(f"Theme '{theme}' not found in theme_table.")
return jsonify({"error": "Theme not found"}), 404
query = """
SELECT score, time_taken, user_response
FROM quiz_response
WHERE quiz_id = %s AND user_id_attempt = %s
"""
cursor.execute(query, (quiz_id, user_id))
quiz_response = cursor.fetchone()
if quiz_response:
user_response = quiz_response['user_response']
score = quiz_response['score']
time = quiz_response['time_taken']
accuracy = (score / num_questions) * 100 if num_questions > 0 else 0
response_data = {
"quiz_id": quiz_id,
"theme": theme,
"user_response": user_response,
"score": score,
"accuracy": round(accuracy, 2),
"time_taken": time
}
logging.info(f"Score and accuracy for user {user_id}, quiz {quiz_id} fetched successfully.")
return jsonify(response_data), 200
logging.warning(f"No quiz response found for user {user_id} and quiz {quiz_id}.")
return jsonify({"error": "Quiz response not found"}), 404
except Exception as e:
logging.error(f"Error fetching quiz score: {e}")
return jsonify({"error": "Unable to fetch quiz score"}), 500
finally:
cursor.close()
connection.close()
def get_recent_quizzes_db(user_id, limit=5):
try:
connection = get_db_connection()
if not connection:
return {"error": "Database connection failed."}
cursor = connection.cursor()
cursor.execute("""
SELECT qr.quiz_id, qr.theme, qr.user_response, qr.score, qr.time_taken, qr.submitted_on,
t.theme_quiz_table
FROM quiz_response qr
JOIN themes t ON qr.theme = t.theme
WHERE qr.user_id_attempt = %s
ORDER BY qr.submitted_on DESC
LIMIT %s
""", (user_id, limit))
recent_quizzes = cursor.fetchall()
if not recent_quizzes:
return {"message": "No quizzes found for the user."}
results = []
for quiz in recent_quizzes:
quiz_id = quiz['quiz_id']
theme = quiz['theme']
theme_quiz_table = quiz['theme_quiz_table']
if not theme_quiz_table:
logging.warning(f"No quiz table found for theme: {theme}")
continue
cursor.execute(f"""
SELECT questions_by_llm, correct_options_llm,
questions_by_master, correct_options_master
FROM {theme_quiz_table}
WHERE quiz_id = %s
""", (quiz_id,))
quiz_details = cursor.fetchone()
if not quiz_details:
logging.warning(f"Quiz '{quiz_id}' not found in theme table '{theme_quiz_table}'.")
continue
questions = quiz_details['questions_by_llm'] or quiz_details['questions_by_master']
correct_answers = quiz_details['correct_options_llm'] or quiz_details['correct_options_master']
if not questions or not correct_answers:
logging.warning(f"Quiz '{quiz_id}' has no questions or answers.")
continue
results.append({
"quiz_id": quiz_id,
"theme": theme,
"questions": questions,
"correct_answers": correct_answers,
"user_responses": quiz['user_response'],
"score": quiz["score"],
"time_taken": quiz['time_taken'],
"date_attempted": quiz['submitted_on']
})
return results
except mysql.connector.Error as err:
logging.error(f"MySQL Error: {err}")
return {"error": "Database operation failed."}
except Exception as e:
logging.error(f"Unexpected error: {str(e)}")
return {"error": "An unexpected error occurred."}
finally:
if 'cursor' in locals():
cursor.close()
if 'connection' in locals():
connection.close()
logging.info("Database connection closed.")
def get_quiz_details_db(user_id, quiz_id):
try:
connection = get_db_connection()
if not connection:
return {"error": "Database connection failed."}
cursor = connection.cursor()
logging.info(f"Checking for user_id_attempt: {user_id}, quiz_id: {quiz_id}")
cursor.execute("""
SELECT qr.quiz_id, qr.theme, qr.user_response, qr.score, qr.time_taken, qr.submitted_on,
t.theme_quiz_table
FROM quiz_response qr
JOIN themes t ON qr.theme = t.theme
WHERE qr.user_id_attempt = %s AND qr.quiz_id = %s
""", (user_id, quiz_id))
quiz = cursor.fetchone()
if not quiz:
return {"message": "Quiz not found."}
theme_quiz_table = quiz['theme_quiz_table']
if not theme_quiz_table:
logging.warning(f"No quiz table found for theme: {quiz['theme']}")
return {"error": "Invalid theme data."}
cursor.execute(f"""
SELECT questions_by_llm, correct_options_llm,
questions_by_master, correct_options_master
FROM {theme_quiz_table}
WHERE quiz_id = %s
""", (quiz_id,))
quiz_details = cursor.fetchone()
if not quiz_details:
logging.warning(f"Quiz '{quiz_id}' not found in theme table '{theme_quiz_table}'.")
return {"error": "Quiz questions not found."}
questions = quiz_details['questions_by_llm'] or quiz_details['questions_by_master']
correct_answers = quiz_details['correct_options_llm'] or quiz_details['correct_options_master']
if not questions or not correct_answers:
logging.warning(f"Quiz '{quiz_id}' has incomplete data.")
return {"error": "Incomplete quiz data."}
return {
"quiz_id": quiz["quiz_id"],
"theme": quiz["theme"],
"questions": questions,
"correct_answers": correct_answers,
"user_responses": quiz["user_response"],
"score": quiz["score"],
"time_taken": quiz["time_taken"],
"date_attempted": quiz["submitted_on"]
}
except mysql.connector.Error as err:
logging.error(f"MySQL Error: {err}")
return {"error": "Database operation failed."}
except Exception as e:
logging.error(f"Unexpected error: {str(e)}")
return {"error": "An unexpected error occurred."}
finally:
if 'cursor' in locals():
cursor.close()
if 'connection' in locals():
connection.close()
logging.info("Database connection closed.")
def fetch_quiz_for_theme_db(user_id, theme):
try:
connection = get_db_connection()
if not connection:
return {"error": "Database connection failed."}
cursor = connection.cursor()
cursor.execute("SELECT theme_quiz_table, theme_bank FROM themes WHERE theme = %s", (theme,))
theme_data = cursor.fetchone()
if not theme_data or not theme_data['theme_quiz_table'] or not theme_data['theme_bank']:
return {"error": "No quiz table found for this theme."}
theme_quiz_table = theme_data['theme_quiz_table']
theme_bank_table = theme_data['theme_bank']
cursor.execute("SELECT quiz_id FROM quiz_response WHERE user_id_attempt = %s", (user_id,))
attempted_quizzes = {row['quiz_id'] for row in cursor.fetchall()}
cursor.execute(f"""
SELECT quiz_id, num_questions, questions_by_llm, correct_options_llm,
questions_by_master, correct_options_master
FROM {theme_quiz_table}
""")
quizzes = cursor.fetchall()
if not quizzes:
return {"message": "No new quizzes available for this theme."}
results = []
for quiz in quizzes:
quiz_id = quiz['quiz_id']
if quiz_id in attempted_quizzes:
continue
ques_id_list = None
if quiz['questions_by_llm']:
try:
ques_id_list = json.loads(quiz['questions_by_llm'])
except json.JSONDecodeError:
logging.error(f"Invalid JSON in questions_by_llm for quiz_id {quiz_id}: {quiz['questions_by_llm']}")
continue
elif quiz['questions_by_master']:
try:
ques_id_list = json.loads(quiz['questions_by_master'])
except json.JSONDecodeError:
logging.error(f"Invalid JSON in questions_by_master for quiz_id {quiz_id}: {quiz['questions_by_master']}")
continue
logging.info(f"Quiz ID {quiz_id} - Extracted ques_id_list: {ques_id_list} (Type: {type(ques_id_list)})")
if isinstance(ques_id_list, dict):
ques_id_list = list(ques_id_list.values())
elif not isinstance(ques_id_list, list):
logging.error(f"Invalid ques_id_list type for quiz_id {quiz_id}: {type(ques_id_list)}")
continue
if not ques_id_list:
logging.warning(f"No questions found for quiz_id {quiz_id}")
continue
number_of_questions = quiz['num_questions']
format_strings = ",".join(["%s"] * len(ques_id_list))
logging.info(f"Executing SQL query with ques_id_list: {ques_id_list}")
cursor.execute(f"""
SELECT ques_id, question_by_llm, correct_option_llm,
question_by_master, correct_option_master
FROM {theme_bank_table}
WHERE ques_id IN ({format_strings})
""", tuple(ques_id_list))
question_details = cursor.fetchall()
final_questions = []
final_correct_options = []
final_ques_ids = []
for q in question_details:
question_text = q['question_by_llm'] or q['question_by_master']
correct_option = q['correct_option_llm'] or q['correct_option_master']
if question_text and correct_option:
final_questions.append(question_text)
final_correct_options.append(correct_option)
final_ques_ids.append(q['ques_id'])
results.append({
"quiz_id": quiz_id,
"num_questions": number_of_questions,
"ques_id": final_ques_ids,
"questions": final_questions,
"correct_options": final_correct_options
})
return {"quizzes": results}
except mysql.connector.Error as err:
logging.error(f"MySQL Error: {err}")
return {"error": "Database operation failed."}
except Exception as e:
logging.error(f"Unexpected error: {str(e)}")
return {"error": "An unexpected error occurred."}
finally:
if 'cursor' in locals():
cursor.close()
if 'connection' in locals():
connection.close()
logging.info("Database connection closed.")