added crud for categories entity

This commit is contained in:
Hosein
2026-01-05 11:55:58 +03:30
parent 7b6c2a3245
commit 8680778584
6 changed files with 338 additions and 5 deletions
+159
View File
@@ -0,0 +1,159 @@
from telebot import types, TeleBot
from database.models import Categories
from bot.handlers.start import startMarkup
from bot.utils.crud_helpers import create_entity_markup
# TODO: Add Option for showing courses with specific tags.
def register(bot: TeleBot):
cancelMarkup = types.InlineKeyboardMarkup()
cancelMarkup.add(types.InlineKeyboardButton(
"Cancel", callback_data="cancel"))
CATEGORY_FIELDS = [
('name', "the category's name"),
('description', "The description for category"),
('parent_id', "The parent category id")
]
EDITABLE_FIELDS = {
'name': 1,
'description': 2,
'parent_id': 3
}
# Showing details of a Category
@bot.callback_query_handler(func=lambda call: call.data.startswith('category_'))
def show_category_details(call):
category_id = call.data.split('_')[1]
category = Categories.getCategorieById(category_id)
if category:
details = f"""
<b>🔖 Category Profile</b>
<b>Name:</b> {category[1]}
<b>Parent Category:</b> {category[3]}
<b>Description:</b> \n{category[2]}
"""
details.strip()
markup = create_entity_markup("category", category_id)
bot.send_message(call.message.chat.id, details,
reply_markup=markup, parse_mode="HTML")
else:
bot.send_message(call.message.chat.id, "Category not found.")
bot.answer_callback_query(call.id)
# Creating a Category Flow
@bot.callback_query_handler(func=lambda call: call.data == 'create_category')
def start_category_creation(call):
msg = bot.send_message(call.message.chat.id,
"Please enter following data: (enter any key to start)",
reply_markup=cancelMarkup)
bot.register_next_step_handler(msg, collect_field, {}, 0)
bot.answer_callback_query(call.id)
def collect_field(message, data, step):
# Save previous field
if step > 0:
field_name = CATEGORY_FIELDS[step - 1][0]
data[field_name] = message.text
# Done collecting?
if step >= len(CATEGORY_FIELDS):
show_confirmation(message, data)
return
# Ask next question
field_name, prompt = CATEGORY_FIELDS[step]
msg = bot.send_message(message.chat.id, f"Now enter {prompt}:",
reply_markup=cancelMarkup)
bot.register_next_step_handler(msg, collect_field, data, step + 1)
def show_confirmation(message, data):
summary = "Is this correct? (enter any key to continue or cancel to exit)\n\n" + "\n".join(
f"{name.replace('_', ' ').title()}: {data[name]}"
for name, _ in CATEGORY_FIELDS
)
msg = bot.send_message(message.chat.id, summary,
reply_markup=cancelMarkup)
bot.register_next_step_handler(msg, create_category, data)
def create_category(message, data):
if Categories.createCategory(**data):
bot.send_message(message.chat.id, "✅ Category created!",
reply_markup=startMarkup())
else:
bot.send_message(
message.chat.id, "❌ Failed to create category.", reply_markup=startMarkup())
# Editing a Category Flow
@bot.callback_query_handler(func=lambda call: call.data.startswith('edit_category_'))
def start_category_editing(call):
category_id = call.data.split('_')[2]
category = Categories.getCategorieById(category_id)
if not category:
bot.send_message(call.message.chat.id, "Category not found.")
bot.answer_callback_query(call.id)
return
editMarkup = types.ReplyKeyboardMarkup(
resize_keyboard=True, one_time_keyboard=True)
for field in list(EDITABLE_FIELDS.keys()) + ['Cancel']:
editMarkup.add(types.KeyboardButton(field.capitalize()))
msg = bot.send_message(call.message.chat.id,
"Please enter the field you want to edit: ", reply_markup=editMarkup)
bot.register_next_step_handler(
msg, process_field_select, category)
bot.answer_callback_query(call.id)
def process_field_select(message, category: tuple):
field = message.text.lower()
if field == 'cancel' or field not in EDITABLE_FIELDS:
msg = "Action cancelled." if field == 'cancel' else "Invalid field. Action cancelled."
bot.send_message(message.chat.id, msg,
reply_markup=startMarkup())
return
current_value = category[EDITABLE_FIELDS[field]]
msg = bot.send_message(
message.chat.id, f"Current value is: {current_value}.\n Please enter new value for {field}:", reply_markup=cancelMarkup)
bot.register_next_step_handler(
msg, process_value_edit, category[0], field, current_value)
def process_value_edit(message, category_id, field, previous_value):
new_value = message.text
# Handle cancellation
if new_value.lower() == 'cancel' or new_value == f"{previous_value}":
msg = "Action cancelled." if new_value.lower(
) == 'cancel' else f"No changes made to {field}."
bot.send_message(message.chat.id, msg, reply_markup=startMarkup())
return
# Update Category
if Categories.updateCategory(category_id, **{field: new_value}):
bot.send_message(
message.chat.id, f"✅ Category's {field} updated successfully.", reply_markup=startMarkup())
else:
bot.send_message(
message.chat.id, f"❌ Failed to update Category's {field}.", reply_markup=startMarkup())
# Deleting a Category
@bot.callback_query_handler(func=lambda call: call.data.startswith('delete_category_'))
def delete_category(call):
category_id = call.data.split('_')[2]
if (Categories.deleteCategory(category_id)):
bot.send_message(call.message.chat.id, "✅ Category deleted.",
reply_markup=startMarkup())
else:
bot.send_message(call.message.chat.id, "❌ Failed to delete Category.",
reply_markup=startMarkup())
bot.answer_callback_query(call.id)
+2 -1
View File
@@ -1,4 +1,4 @@
from . import students, teachers, courses, tags
from . import students, teachers, courses, tags, categories
from bot.handlers.start import startMarkup
@@ -8,6 +8,7 @@ def register_all_callbacks(bot):
teachers.register(bot)
courses.register(bot)
tags.register(bot)
categories.register(bot)
# Cancel is common among all
@bot.callback_query_handler(func=lambda call: call.data == 'cancel')
+3 -3
View File
@@ -22,7 +22,7 @@ def register(bot: TeleBot):
# Showing details of a Tag
@bot.callback_query_handler(func=lambda call: call.data.startswith('tag_'))
def show_teacher_details(call):
def show_tag_details(call):
tag_id = call.data.split('_')[1]
tag = Tags.getTagById(tag_id)
@@ -135,7 +135,7 @@ def register(bot: TeleBot):
bot.send_message(message.chat.id, msg, reply_markup=startMarkup())
return
# Update teacher
# Update tag
if Tags.updateTag(tag_id, **{field: new_value}):
bot.send_message(
message.chat.id, f"✅ Tag's {field} updated successfully.", reply_markup=startMarkup())
@@ -143,7 +143,7 @@ def register(bot: TeleBot):
bot.send_message(
message.chat.id, f"❌ Failed to update Tag's {field}.", reply_markup=startMarkup())
# Deleting a Teacher
# Deleting a Tag
@bot.callback_query_handler(func=lambda call: call.data.startswith('delete_tag_'))
def delete_tag(call):
tag_id = call.data.split('_')[2]
+31
View File
@@ -0,0 +1,31 @@
import logging
from telebot import types
from database.models import Categories
logger = logging.getLogger(__name__)
def register(bot):
@bot.message_handler(func=lambda message: message.text == "Show Categories")
def get_categories(message):
try:
data = Categories.getAllCategories()
if data:
markup = types.InlineKeyboardMarkup(row_width=2)
for row in data:
btn = types.InlineKeyboardButton(
f"{row[1]} | id#{row[0]}", callback_data=f"category_{row[0]}")
markup.add(btn)
# add button for creating a new category
markup.add(types.InlineKeyboardButton(
" Create New Category", callback_data="create_category"))
bot.send_message(
message.chat.id, "Here is the data:", reply_markup=markup)
else:
bot.reply_to(message, "No data found.")
except Exception as e:
logger.error(f"Error in 'Show Categories' handler: {e}")
bot.reply_to(message, "Sorry, an error occurred.")
+2 -1
View File
@@ -1,4 +1,4 @@
from . import start, students, teachers, courses, tags, common
from . import start, students, teachers, courses, tags, categories, common
from bot.callbacks.init import register_all_callbacks
@@ -10,6 +10,7 @@ def register_all_handlers(bot):
teachers.register(bot)
courses.register(bot)
tags.register(bot)
categories.register(bot)
common.register(bot) # Must be last (catch-all)
# Callback handlers
+141
View File
@@ -522,3 +522,144 @@ class Tags:
finally:
if conn:
connection.release_db_connection(conn)
class Categories:
def getAllCategories():
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor()
cursor.execute(
"SELECT id, name, description, parent_id FROM categories")
result = cursor.fetchall()
cursor.close()
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):
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return None
cursor = conn.cursor()
cursor.execute("""
SELECT
c1.id,
c1.name,
c1.description,
COALESCE(c2.name, 'None') AS parent_category
FROM
categories c1
LEFT JOIN categories c2 ON c2.id = c1.parent_id
WHERE
c1.id = %s;
""", (category_id,))
result = cursor.fetchall()
cursor.close()
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):
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor()
cursor.execute(
"INSERT INTO categories (name, description, parent_id) VALUES (%s, %s, %s)",
(name, description, parent_id)
)
conn.commit()
cursor.close()
return True
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):
if not fields:
return False # nothing to update
conn = None
try:
conn = connection.get_db_connection()
if not conn:
return False
cursor = conn.cursor()
# Build dynamic SET clause
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(
"DELETE FROM categories WHERE id = %s", (category_id,))
conn.commit()
cursor.close()
return True
except Exception as e:
logger.error(f"Database query error in deleteCategory: {e}")
return False
finally:
if conn:
connection.release_db_connection(conn)