refactored database model

This commit is contained in:
Hosein
2026-01-05 14:30:25 +03:30
parent 8680778584
commit 4b5b24c97c
4 changed files with 236 additions and 558 deletions
+2 -1
View File
@@ -3,7 +3,8 @@ from database.models import Categories
from bot.handlers.start import startMarkup from bot.handlers.start import startMarkup
from bot.utils.crud_helpers import create_entity_markup from bot.utils.crud_helpers import create_entity_markup
# TODO: Add Option for showing courses with specific tags. # TODO: Add Option for showing parent categories when creating a new category.
# TODO: Show Some of the courses of a Category.
def register(bot: TeleBot): def register(bot: TeleBot):
+2 -2
View File
@@ -41,7 +41,7 @@ def register(bot: TeleBot):
@bot.callback_query_handler(func=lambda call: call.data.startswith('teacher_')) @bot.callback_query_handler(func=lambda call: call.data.startswith('teacher_'))
def show_teacher_details(call): def show_teacher_details(call):
teacher_id = call.data.split('_')[1] teacher_id = call.data.split('_')[1]
teacher = Teachers.getTeachertById(teacher_id) teacher = Teachers.getTeacherById(teacher_id)
if teacher: if teacher:
details = format_teacher_info(teacher) details = format_teacher_info(teacher)
@@ -101,7 +101,7 @@ def register(bot: TeleBot):
@bot.callback_query_handler(func=lambda call: call.data.startswith('edit_teacher_')) @bot.callback_query_handler(func=lambda call: call.data.startswith('edit_teacher_'))
def start_teacher_editing(call): def start_teacher_editing(call):
teacher_id = call.data.split('_')[2] teacher_id = call.data.split('_')[2]
teacher = Teachers.getTeachertById(teacher_id) teacher = Teachers.getTeacherById(teacher_id)
if not teacher: if not teacher:
bot.send_message(call.message.chat.id, "Teacher not found.") bot.send_message(call.message.chat.id, "Teacher not found.")
+7 -3
View File
@@ -1,11 +1,11 @@
import logging import logging
from telebot import types from telebot import types, TeleBot
from database.models import Tags from database.models import Tags
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def register(bot): def register(bot: TeleBot):
@bot.message_handler(func=lambda message: message.text == "Show Tags") @bot.message_handler(func=lambda message: message.text == "Show Tags")
def get_tags(message): def get_tags(message):
try: try:
@@ -25,7 +25,11 @@ def register(bot):
bot.send_message( bot.send_message(
message.chat.id, "Here is the data:", reply_markup=markup) message.chat.id, "Here is the data:", reply_markup=markup)
else: else:
bot.reply_to(message, "No data found.") markup = types.InlineKeyboardMarkup(row_width=2)
markup.add(types.InlineKeyboardButton(
" Create New Tag", callback_data="create_tag"))
bot.reply_to(message, "No data found.", markup=markup)
except Exception as e: except Exception as e:
logger.error(f"Error in 'Show Tags' handler: {e}") logger.error(f"Error in 'Show Tags' handler: {e}")
bot.reply_to(message, "Sorry, an error occurred.") bot.reply_to(message, "Sorry, an error occurred.")
+224 -551
View File
@@ -4,8 +4,14 @@ import database.connection as connection
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Students: class BaseRepository:
def getAllStudents(): """Base class for all repository classes"""
table_name = None # Should be Overrided in subclasses
@classmethod
def _execute_query(cls, query_func, operation_name):
"""Execute a SELECT query"""
conn = None conn = None
try: try:
conn = connection.get_db_connection() conn = connection.get_db_connection()
@@ -13,40 +19,21 @@ class Students:
return None return None
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("SELECT id,username FROM students") result = query_func(cursor)
result = cursor.fetchall()
cursor.close() cursor.close()
return result
return result if result else None
except Exception as e: except Exception as e:
logger.error(f"Database query error in getAllStudents: {e}") logger.error(
f"Database error in {cls.__name__}.{operation_name}: {e}")
return None return None
finally: finally:
if conn: if conn:
connection.release_db_connection(conn) connection.release_db_connection(conn)
def getStudentById(student_id): @classmethod
conn = None def _execute_mutation(cls, mutation_func, operation_name):
try: """Execute an INSERT, UPDATE, or DELETE"""
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor()
cursor.execute(
"SELECT id,username, name, created_at, email, phone_number, last_seen, is_verfied, birthday FROM students WHERE id = %s", (student_id,))
result = cursor.fetchall()
cursor.close()
return result[0] if result else None
except Exception as e:
logger.error(f"Database query error in getStudentById: {e}")
return None
finally:
if conn:
connection.release_db_connection(conn)
def createStudent(name, email, phone_number, password, username, birthday):
conn = None conn = None
try: try:
conn = connection.get_db_connection() conn = connection.get_db_connection()
@@ -54,612 +41,298 @@ class Students:
return False return False
cursor = conn.cursor() cursor = conn.cursor()
mutation_func(cursor, conn)
conn.commit()
cursor.close()
return True
except Exception as e:
logger.error(
f"Database error in {cls.__name__}.{operation_name}: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
class Students(BaseRepository):
table_name = "students"
@classmethod
def getAllStudents(cls):
def query(cursor):
cursor.execute( cursor.execute(
"INSERT INTO students (name, email, phone_number, hashed_password, username, birthday) VALUES (%s, %s, %s, %s, %s, %s)", f"SELECT id,username FROM {cls.table_name}")
result = cursor.fetchall()
return result if result else None
return cls._execute_query(query, "getAllStudents")
@classmethod
def getStudentById(cls, student_id):
def query(cursor):
cursor.execute(
f"SELECT id,username, name, created_at, email, phone_number, last_seen, is_verfied, birthday FROM {cls.table_name} WHERE id = %s", (student_id,))
result = cursor.fetchall()
return result[0] if result else None
return cls._execute_query(query, "getStudentById")
@classmethod
def createStudent(cls, name, email, phone_number, password, username, birthday):
def mutation(cursor, conn):
cursor.execute(
f"INSERT INTO {cls.table_name} (name, email, phone_number, hashed_password, username, birthday) VALUES (%s, %s, %s, %s, %s, %s)",
(name, email, phone_number, password, (name, email, phone_number, password,
username, birthday) username, birthday))
) return cls._execute_mutation(mutation, "createStudent")
conn.commit()
cursor.close()
return True @classmethod
except Exception as e: def updateStudent(cls, student_id, **fields):
logger.error(f"Database query error in createStudent: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
def updateStudent(student_id, **fields):
if not fields: if not fields:
return False # nothing to update
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False return False
cursor = conn.cursor() def mutation(cursor, conn):
columns = [(f"{key} = %s" if key != 'password' else "hashed_password = %s")
# Build dynamic SET clause for key in fields.keys()]
columns = [] values = list(fields.values()) + [student_id]
values = []
for key, value in fields.items():
columns.append(f"{key} = %s" if key !=
'password' else "hashed_password = %s")
values.append(value)
values.append(student_id)
query = f"""
UPDATE students
SET {', '.join(columns)}
WHERE id = %s
"""
cursor.execute(query, tuple(values))
conn.commit()
cursor.close()
return True
except Exception as e:
logger.error(f"Database query error in updateStudent: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
def deleteStudent(student_id):
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor()
cursor.execute( cursor.execute(
"DELETE FROM students WHERE id = %s", (student_id,)) f"UPDATE {cls.table_name} SET {', '.join(columns)} WHERE id = %s",
conn.commit() tuple(values)
cursor.close() )
return True return cls._execute_mutation(mutation, "updateStudent")
except Exception as e:
logger.error(f"Database query error in deleteStudent: {e}") @classmethod
return False def deleteStudent(cls, student_id):
finally: def mutation(cursor, conn):
if conn: cursor.execute(
connection.release_db_connection(conn) f"DELETE FROM {cls.table_name} WHERE id = %s", (student_id,))
return cls._execute_mutation(mutation, "deleteStudent")
class Teachers: class Teachers(BaseRepository):
def getAllTeachers(): table_name = "teachers"
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor() @classmethod
cursor.execute("SELECT id,username FROM teachers") def getAllTeachers(cls):
def query(cursor):
cursor.execute(
f"SELECT id,username FROM {cls.table_name}")
result = cursor.fetchall() result = cursor.fetchall()
cursor.close()
return result if result else None return result if result else None
except Exception as e:
logger.error(f"Database query error in getAllTeachers: {e}")
return None
finally:
if conn:
connection.release_db_connection(conn)
def getTeachertById(teacher_id): return cls._execute_query(query, "getAllTeachers")
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor() @classmethod
def getTeacherById(cls, teacher_id):
def query(cursor):
cursor.execute( cursor.execute(
"SELECT id, username, name, created_at, email, phone_number, last_seen, is_verfied, birthday, about_me, job_title FROM teachers WHERE id = %s", (teacher_id, )) f"SELECT id, username, name, created_at, email, phone_number, last_seen, is_verfied, birthday, about_me, job_title FROM {cls.table_name} WHERE id = %s", (teacher_id, ))
result = cursor.fetchall() result = cursor.fetchall()
cursor.close()
return result[0] if result else None return result[0] if result else None
except Exception as e:
logger.error(f"Database query error in getTeacherById: {e}")
return None
finally:
if conn:
connection.release_db_connection(conn)
def createTeacher(name, email, phone_number, password, username, birthday, about_me, job_title): return cls._execute_query(query, "getTeacherById")
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor() @classmethod
def createTeacher(cls, name, email, phone_number, password, username, birthday, about_me, job_title):
def mutation(cursor, conn):
cursor.execute( cursor.execute(
"INSERT INTO teachers (name, email, phone_number, hashed_password, username, birthday, about_me, job_title) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)", f"INSERT INTO {cls.table_name} (name, email, phone_number, hashed_password, username, birthday, about_me, job_title) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
(name, email, phone_number, password, (name, email, phone_number, password,
username, birthday, about_me, job_title) username, birthday, about_me, job_title))
) return cls._execute_mutation(mutation, "createTeacher")
conn.commit()
cursor.close()
return True @classmethod
except Exception as e: def updateTeacher(cls, teacher_id, **fields):
logger.error(f"Database query error in createTeacher: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
def updateTeacher(teacher_id, **fields):
if not fields: if not fields:
return False # nothing to update
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False return False
cursor = conn.cursor() def mutation(cursor, conn):
columns = [(f"{key} = %s" if key != 'password' else "hashed_password = %s")
# Build dynamic SET clause for key in fields.keys()]
columns = [] values = list(fields.values()) + [teacher_id]
values = []
for key, value in fields.items():
columns.append(f"{key} = %s" if key !=
'password' else "hashed_password = %s")
values.append(value)
values.append(teacher_id)
query = f"""
UPDATE teachers
SET {', '.join(columns)}
WHERE id = %s
"""
cursor.execute(query, tuple(values))
conn.commit()
cursor.close()
return True
except Exception as e:
logger.error(f"Database query error in updateTeacher: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
def deleteTeacher(teacher_id):
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor()
cursor.execute( cursor.execute(
"DELETE FROM teachers WHERE id = %s", (teacher_id,)) f"UPDATE {cls.table_name} SET {', '.join(columns)} WHERE id = %s",
conn.commit() tuple(values)
cursor.close() )
return True return cls._execute_mutation(mutation, "updateTeacher")
except Exception as e:
logger.error(f"Database query error in deleteTeacher: {e}") @classmethod
return False def deleteTeacher(cls, teacher_id):
finally: def mutation(cursor, conn):
if conn: cursor.execute(
connection.release_db_connection(conn) f"DELETE FROM {cls.table_name} WHERE id = %s", (teacher_id,))
return cls._execute_mutation(mutation, "deleteTeacher")
class Courses: class Courses(BaseRepository):
def getAllCourses(): table_name = "courses"
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor() @classmethod
cursor.execute("SELECT id, name FROM courses") def getAllCourses(cls):
def query(cursor):
cursor.execute(
f"SELECT id, name FROM {cls.table_name}")
result = cursor.fetchall() result = cursor.fetchall()
cursor.close()
return result if result else None return result if result else None
except Exception as e:
logger.error(f"Database query error in getAllCourses: {e}")
return None
finally:
if conn:
connection.release_db_connection(conn)
def getCourseById(course_id): return cls._execute_query(query, "getAllCourses")
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor() @classmethod
def getCourseById(cls, course_id):
def query(cursor):
cursor.execute( cursor.execute(
"SELECT id, name, created_at, teacher_id, updated_at, description, difficulty, language FROM courses WHERE id = %s", (course_id, )) f"SELECT id, name, created_at, teacher_id, updated_at, description, difficulty, language FROM {cls.table_name} WHERE id = %s", (course_id, ))
result = cursor.fetchall() result = cursor.fetchall()
cursor.close()
return result[0] if result else None return result[0] if result else None
except Exception as e:
logger.error(f"Database query error in getCourseById: {e}")
return None
finally:
if conn:
connection.release_db_connection(conn)
def createCourse(name, teacher_id, description, language, difficulty): return cls._execute_query(query, "getCourseById")
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor() @classmethod
def createCourse(cls, name, teacher_id, description, language, difficulty):
def mutation(cursor, conn):
cursor.execute( cursor.execute(
"INSERT INTO courses (name, teacher_id, description, language, difficulty) VALUES (%s, %s, %s, %s, %s)", f"INSERT INTO {cls.table_name} (name, teacher_id, description, language, difficulty) VALUES (%s, %s, %s, %s, %s)",
(name, teacher_id, description, language, difficulty) (name, teacher_id, description, language, difficulty))
) return cls._execute_mutation(mutation, "createCourse")
conn.commit()
cursor.close()
return True @classmethod
except Exception as e: def updateCourse(cls, course_id, **fields):
logger.error(f"Database query error in createCourse: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
def updateCourse(course_id, **fields):
if not fields: if not fields:
return False # nothing to update
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False return False
cursor = conn.cursor() def mutation(cursor, conn):
columns = [f"{key} = %s" for key in fields.keys()]
# Build dynamic SET clause values = list(fields.values()) + [course_id]
columns = []
values = []
for key, value in fields.items():
columns.append(f"{key} = %s")
values.append(value)
values.append(course_id)
query = f"""
UPDATE courses
SET {', '.join(columns)}
WHERE id = %s
"""
cursor.execute(query, tuple(values))
conn.commit()
cursor.close()
return True
except Exception as e:
logger.error(f"Database query error in updateCourse: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
def deleteCourse(course_id):
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor()
cursor.execute( cursor.execute(
"DELETE FROM courses WHERE id = %s", (course_id,)) f"UPDATE {cls.table_name} SET {', '.join(columns)} WHERE id = %s",
conn.commit() tuple(values)
cursor.close() )
return True return cls._execute_mutation(mutation, "updateCourse")
except Exception as e:
logger.error(f"Database query error in deleteCourse: {e}") @classmethod
return False def deleteCourse(cls, course_id):
finally: def mutation(cursor, conn):
if conn: cursor.execute(
connection.release_db_connection(conn) f"DELETE FROM {cls.table_name} WHERE id = %s", (course_id,))
return cls._execute_mutation(mutation, "deleteCourse")
class Tags: class Tags(BaseRepository):
def getAllTags(): table_name = "tags"
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor() @classmethod
cursor.execute("SELECT id, name, slug FROM tags") def getAllTags(cls):
def query(cursor):
cursor.execute(
f"SELECT id, name, slug FROM {cls.table_name}")
result = cursor.fetchall() result = cursor.fetchall()
cursor.close()
return result if result else None return result if result else None
except Exception as e:
logger.error(f"Database query error in getAllTags: {e}")
return None
finally:
if conn:
connection.release_db_connection(conn)
def getTagById(tag_id): return cls._execute_query(query, "getAllTags")
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor() @classmethod
def getTagById(cls, tag_id):
def query(cursor):
cursor.execute( cursor.execute(
"SELECT id, name, slug FROM tags WHERE id = %s", (tag_id, )) f"SELECT id, name, slug FROM {cls.table_name} WHERE id = %s", (tag_id,))
result = cursor.fetchall() result = cursor.fetchall()
cursor.close()
return result[0] if result else None return result[0] if result else None
except Exception as e:
logger.error(f"Database query error in getTagById: {e}")
return None
finally:
if conn:
connection.release_db_connection(conn)
def createTag(name, slug): return cls._execute_query(query, "getTagById")
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor() @classmethod
def createTag(cls, name, slug):
def mutation(cursor, conn):
cursor.execute( cursor.execute(
"INSERT INTO tags (name, slug) VALUES (%s, %s)", f"INSERT INTO {cls.table_name} (name, slug) VALUES (%s, %s)", (name, slug))
(name, slug) return cls._execute_mutation(mutation, "createTag")
)
conn.commit()
cursor.close()
return True @classmethod
except Exception as e: def updateTag(cls, tag_id, **fields):
logger.error(f"Database query error in createTag: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
def updateTag(tag_id, **fields):
if not fields: if not fields:
return False # nothing to update
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False return False
cursor = conn.cursor() def mutation(cursor, conn):
columns = [f"{key} = %s" for key in fields.keys()]
# Build dynamic SET clause values = list(fields.values()) + [tag_id]
columns = []
values = []
for key, value in fields.items():
columns.append(f"{key} = %s")
values.append(value)
values.append(tag_id)
query = f"""
UPDATE tags
SET {', '.join(columns)}
WHERE id = %s
"""
cursor.execute(query, tuple(values))
conn.commit()
cursor.close()
return True
except Exception as e:
logger.error(f"Database query error in updateTag: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
def deleteTag(tag_id):
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor()
cursor.execute( cursor.execute(
"DELETE FROM tags WHERE id = %s", (tag_id,)) f"UPDATE {cls.table_name} SET {', '.join(columns)} WHERE id = %s",
conn.commit() tuple(values)
cursor.close() )
return True return cls._execute_mutation(mutation, "updateTag")
except Exception as e:
logger.error(f"Database query error in deleteTag: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
@classmethod
class Categories: def deleteTag(cls, tag_id):
def getAllCategories(): def mutation(cursor, conn):
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor()
cursor.execute( cursor.execute(
"SELECT id, name, description, parent_id FROM categories") f"DELETE FROM {cls.table_name} WHERE id = %s", (tag_id,))
return cls._execute_mutation(mutation, "deleteTag")
class Categories(BaseRepository):
table_name = "categories"
@classmethod
def getAllCategories(cls):
def query(cursor):
cursor.execute(
f"SELECT id, name, description, parent_id FROM {cls.table_name}")
result = cursor.fetchall() result = cursor.fetchall()
cursor.close()
return result if result else None return result if result else None
except Exception as e:
logger.error(f"Database query error in getAllCategories: {e}")
return None
finally:
if conn:
connection.release_db_connection(conn)
def getCategorieById(category_id): return cls._execute_query(query, "getAllCategories")
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor() @classmethod
cursor.execute(""" def getCategorieById(cls, category_id):
SELECT def query(cursor):
c1.id, cursor.execute(f"""
c1.name, SELECT c1.id, c1.name, c1.description,
c1.description,
COALESCE(c2.name, 'None') AS parent_category COALESCE(c2.name, 'None') AS parent_category
FROM FROM {cls.table_name} c1
categories c1 LEFT JOIN {cls.table_name} c2 ON c2.id = c1.parent_id
LEFT JOIN categories c2 ON c2.id = c1.parent_id WHERE c1.id = %s
WHERE
c1.id = %s;
""", (category_id,)) """, (category_id,))
result = cursor.fetchall() result = cursor.fetchall()
cursor.close()
return result[0] if result else None return result[0] if result else None
except Exception as e:
logger.error(f"Database query error in getCategoryById: {e}")
return None
finally:
if conn:
connection.release_db_connection(conn)
def createCategory(name, description, parent_id=None): return cls._execute_query(query, "getCategoryById")
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor() @classmethod
def createCategory(cls, name, description, parent_id=None):
def mutation(cursor, conn):
cursor.execute( cursor.execute(
"INSERT INTO categories (name, description, parent_id) VALUES (%s, %s, %s)", f"INSERT INTO {cls.table_name} (name, description, parent_id) VALUES (%s, %s, %s)", (name, description, parent_id))
(name, description, parent_id)
)
conn.commit()
cursor.close()
return True return cls._execute_mutation(mutation, "createCategory")
except Exception as e:
logger.error(f"Database query error in createCategory: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
def updateCategory(category_id, **fields): @classmethod
def updateCategory(cls, category_id, **fields):
if not fields: if not fields:
return False # nothing to update
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False return False
cursor = conn.cursor() def mutation(cursor, conn):
columns = [f"{key} = %s" for key in fields.keys()]
# Build dynamic SET clause values = list(fields.values()) + [category_id]
columns = []
values = []
for key, value in fields.items():
columns.append(f"{key} = %s")
values.append(value)
values.append(category_id)
query = f"""
UPDATE categories
SET {', '.join(columns)}
WHERE id = %s
"""
cursor.execute(query, tuple(values))
conn.commit()
cursor.close()
return True
except Exception as e:
logger.error(f"Database query error in updateCategory: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)
def deleteCategory(category_id):
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor()
cursor.execute( cursor.execute(
"DELETE FROM categories WHERE id = %s", (category_id,)) f"UPDATE {cls.table_name} SET {', '.join(columns)} WHERE id = %s",
conn.commit() tuple(values)
cursor.close() )
return True return cls._execute_mutation(mutation, "updateCategory")
except Exception as e:
logger.error(f"Database query error in deleteCategory: {e}") @classmethod
return False def deleteCategory(cls, category_id):
finally: def mutation(cursor, conn):
if conn: cursor.execute(
connection.release_db_connection(conn) f"DELETE FROM {cls.table_name} WHERE id = %s", (category_id,))
return cls._execute_mutation(mutation, "deleteCategory")