added some new features. (Showing student's reviews and courses, Show course reviews, Show Top Courses with a tag or in a category, skipping optional fields when creating entity)

This commit is contained in:
Hosein
2026-01-08 10:13:25 +03:30
parent caac6c3a08
commit 45138bb234
10 changed files with 314 additions and 37 deletions
+27 -6
View File
@@ -4,7 +4,6 @@ from bot.handlers.start import startMarkup
from bot.utils.crud_helpers import create_entity_markup
# 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):
@@ -14,8 +13,8 @@ def register(bot: TeleBot):
CATEGORY_FIELDS = [
('name', "the category's name"),
('description', "The description for category"),
('parent_id', "The parent category id")
('description', "The description for category (<Optional>) ('s' to skip)"),
('parent_id', "The parent category id (<Optional>) ('s' to skip))")
]
EDITABLE_FIELDS = {
'name': 1,
@@ -27,7 +26,7 @@ def register(bot: TeleBot):
@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)
category = Categories.getCategoryById(category_id)
if category:
details = f"""
@@ -39,6 +38,8 @@ def register(bot: TeleBot):
"""
details.strip()
markup = create_entity_markup("category", category_id, True)
markup.add(types.InlineKeyboardButton("📁 Show Top Courses",
callback_data=f"categoryCourses_{category_id}"))
bot.send_message(call.message.chat.id, details,
reply_markup=markup, parse_mode="HTML")
@@ -60,7 +61,8 @@ def register(bot: TeleBot):
# Save previous field
if step > 0:
field_name = CATEGORY_FIELDS[step - 1][0]
data[field_name] = message.text
data[field_name] = None if (
(field_name in ['description', 'parent_id']) and message.text == 's') else message.text
# Done collecting?
if step >= len(CATEGORY_FIELDS):
@@ -99,7 +101,7 @@ def register(bot: TeleBot):
return
category_id = call.data.split('_')[2]
category = Categories.getCategorieById(category_id)
category = Categories.getCategoryById(category_id)
if not category:
bot.send_message(call.message.chat.id, "Category not found.")
@@ -167,3 +169,22 @@ def register(bot: TeleBot):
bot.send_message(call.message.chat.id, "❌ Failed to delete Category.",
reply_markup=startMarkup())
bot.answer_callback_query(call.id)
# Show Top Courses in a Category
@bot.callback_query_handler(func=lambda call: call.data.startswith('categoryCourses_'))
def category_courses(call):
category_id = call.data.split('_')[1]
courses = Categories.getTopCoursesByCategory(category_id)
markup = types.InlineKeyboardMarkup()
if courses:
for course in courses:
markup.add(types.InlineKeyboardButton(
course[1], callback_data=f"course_{course[0]}"))
bot.send_message(
call.message.chat.id, "📚 Top Courses in this Category:", reply_markup=markup)
else:
bot.send_message(
call.message.chat.id, "There are no courses in this category.", reply_markup=markup)
bot.answer_callback_query(call.id)
+24 -4
View File
@@ -1,7 +1,7 @@
from telebot import TeleBot, types
from database.models import Courses, Admins
from bot.utils.crud_helpers import create_entity_markup
from bot.utils.formatters import format_course_info
from bot.utils.formatters import format_course_info, format_course_review
from bot.handlers.start import startMarkup
# TODO: Add Option for selecting Teacher in a more user-friendly way (name instead of ID) + pagination for it.
@@ -16,9 +16,9 @@ def register(bot: TeleBot):
COURSE_FIELDS = [
('name', "the course's name"),
('teacher_id', "The course's Teacher ID"),
('description', "description (<Optional>)"),
('description', "description (<Optional>) ('s' to skip)"),
('language', "language ('english', 'spanish', 'german', 'french', 'persian')"),
('difficulty', "difficulty (<Optional>) ('beginner', 'intermediate', 'expert')"),
('difficulty', "difficulty (<Optional>) ('s' to skip) ('beginner', 'intermediate', 'expert')"),
]
EDITABLE_FIELDS = {
'name': 1,
@@ -35,12 +35,15 @@ def register(bot: TeleBot):
if not course:
bot.send_message(call.message.chat.id, "Course not found.")
bot.answer_callback_query(call.id)
return
details = format_course_info(course)
markup = create_entity_markup("course", course_id, True)
markup.add(types.InlineKeyboardButton(
f"👨‍🏫 Teacher's Info", callback_data=f"teacher_{course[3]}"))
markup.add(types.InlineKeyboardButton(
f" 📰 Course Reviews", callback_data=f"courseReviews_{course_id}"))
bot.send_message(call.message.chat.id, details,
reply_markup=markup, parse_mode="HTML")
@@ -60,7 +63,8 @@ def register(bot: TeleBot):
# Save previous field
if step > 0:
field_name = COURSE_FIELDS[step - 1][0]
data[field_name] = message.text
data[field_name] = None if (
(field_name in ['description', 'difficulty']) and message.text == 's') else message.text
# Done collecting?
if step >= len(COURSE_FIELDS):
@@ -167,3 +171,19 @@ def register(bot: TeleBot):
bot.send_message(call.message.chat.id, "❌ Failed to delete Course.",
reply_markup=startMarkup())
bot.answer_callback_query(call.id)
# Show Some of the course reviews
@bot.callback_query_handler(func=lambda call: call.data.startswith('courseReviews_'))
def show_course_reviews(call):
course_id = call.data.split('_')[1]
reviews = Courses.getCourseReviews(course_id)
if reviews:
for rev in reviews:
details = format_course_review(rev)
bot.send_message(call.message.chat.id,
details, parse_mode="HTML")
else:
bot.send_message(call.message.chat.id, "No reviews yet")
bot.answer_callback_query(call.id)
+3
View File
@@ -1,6 +1,9 @@
from . import students, teachers, courses, tags, categories
from bot.handlers.start import startMarkup
# TODO: Create a new entity for Reviews (Change The Corresponding codes from courses and students to use that)
# TODO: Add Option for handling all updates at once.
def register_all_callbacks(bot):
"""Register all callback handlers"""
+55 -3
View File
@@ -1,6 +1,6 @@
from telebot import types, TeleBot
from database.models import Students, Admins
from bot.utils.formatters import format_student_info
from bot.utils.formatters import format_course_review, format_student_info
from bot.utils.crud_helpers import create_entity_markup
from bot.handlers.start import startMarkup
@@ -13,7 +13,7 @@ def register(bot: TeleBot):
STUDENT_FIELDS = [
('name', "the student's name"),
('email', "email"),
('phone_number', "phone (<Optional>)"),
('phone_number', "phone (<Optional>) ('s' to skip)"),
('password', "password"),
('username', "username"),
('birthday', "birthday (YY/MM/DD)"),
@@ -41,6 +41,10 @@ def register(bot: TeleBot):
if student:
details = format_student_info(student)
markup = create_entity_markup("student", student_id, True)
markup.add(types.InlineKeyboardButton("📚 Show Courses 🔒",
callback_data=f"studentCourses_{student_id}"))
markup.add(types.InlineKeyboardButton("📰 Reviews By Student 🔒",
callback_data=f"studentReviews_{student_id}"))
bot.send_message(call.message.chat.id, details,
reply_markup=markup, parse_mode="HTML")
@@ -67,7 +71,8 @@ def register(bot: TeleBot):
# Save previous field
if step > 0:
field_name = STUDENT_FIELDS[step - 1][0]
data[field_name] = message.text
data[field_name] = None if (
(field_name == 'phone_number') and message.text == 's') else message.text
# Done collecting?
if step >= len(STUDENT_FIELDS):
@@ -174,3 +179,50 @@ def register(bot: TeleBot):
bot.send_message(call.message.chat.id, "❌ Failed to delete Student.",
reply_markup=startMarkup())
bot.answer_callback_query(call.id)
# Showing Courses a Students has enrolled in
@bot.callback_query_handler(func=lambda call: call.data.startswith("studentCourses_"))
def show_student_course(call):
if not Admins.is_authenticated(call.from_user.id):
bot.answer_callback_query(call.id, "⛔ Unauthorized access!")
bot.send_message(call.message.chat.id, "Please /login first.")
return
student_id = call.data.split('_')[1]
courses = Students.getStudentCourses(student_id)
if courses:
markup = types.InlineKeyboardMarkup(row_width=1)
for c in courses:
markup.add(types.InlineKeyboardButton(
f"{c[1][:26] + "..."} | id#{c[0]}", callback_data=f"course_{c[0]}")
)
bot.send_message(call.message.chat.id,
"Courses: ", reply_markup=markup)
else:
bot.send_message(call.message.chat.id, "No courses.")
bot.answer_callback_query(call.id)
# Showing Reviews a Students has written
@bot.callback_query_handler(func=lambda call: call.data.startswith("studentReviews_"))
def show_student_reviews(call):
if not Admins.is_authenticated(call.from_user.id):
bot.answer_callback_query(call.id, "⛔ Unauthorized access!")
bot.send_message(call.message.chat.id, "Please /login first.")
return
student_id = call.data.split('_')[1]
reviews = Students.getStudentReviews(student_id)
if reviews:
for rev in reviews:
details = format_course_review(rev)
bot.send_message(call.message.chat.id,
details, parse_mode="HTML")
else:
bot.send_message(call.message.chat.id, "No reviews.")
bot.answer_callback_query(call.id)
+21 -2
View File
@@ -3,8 +3,6 @@ from database.models import Tags, Admins
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()
@@ -35,6 +33,8 @@ def register(bot: TeleBot):
"""
details.strip()
markup = create_entity_markup("tag", tag_id, True)
markup.add(types.InlineKeyboardButton("📁 Show Top Courses",
callback_data=f"tagCourses_{tag_id}"))
bot.send_message(call.message.chat.id, details,
reply_markup=markup, parse_mode="HTML")
@@ -163,3 +163,22 @@ def register(bot: TeleBot):
bot.send_message(call.message.chat.id, "❌ Failed to delete Tag.",
reply_markup=startMarkup())
bot.answer_callback_query(call.id)
# Show Top Courses with a Tag
@bot.callback_query_handler(func=lambda call: call.data.startswith('tagCourses_'))
def delete_tag(call):
tag_id = call.data.split('_')[1]
courses = Tags.getTopCoursesByTag(tag_id)
markup = types.InlineKeyboardMarkup()
if courses:
for course in courses:
markup.add(types.InlineKeyboardButton(
course[1], callback_data=f"course_{course[0]}"))
bot.send_message(
call.message.chat.id, "📚 Top Courses with this Tag:", reply_markup=markup)
else:
bot.send_message(
call.message.chat.id, "There are no courses with this Tag.", reply_markup=markup)
bot.answer_callback_query(call.id)
+3 -7
View File
@@ -4,11 +4,6 @@ from bot.utils.formatters import format_teacher_info
from bot.handlers.start import startMarkup
from bot.utils.crud_helpers import create_entity_markup
# TODO: Add Buttons for skipping Optional Fields (Add to all entities).
# TODO: Add Option for handling all updates at once.\
# TODO: Add Option for seeing courses taught by a teacher in Teacher Details.
def register(bot: TeleBot):
cancelMarkup = types.InlineKeyboardMarkup()
@@ -18,7 +13,7 @@ def register(bot: TeleBot):
TEACHER_FIELDS = [
('name', "the teacher's name"),
('email', "email"),
('phone_number', "phone (<Optional>)"),
('phone_number', "phone (<Optional>) ('s' to skip)"),
('password', "password"),
('username', "username"),
('birthday', "birthday (YY/MM/DD)"),
@@ -71,7 +66,8 @@ def register(bot: TeleBot):
# Save previous field
if step > 0:
field_name = TEACHER_FIELDS[step - 1][0]
data[field_name] = message.text
data[field_name] = None if (
(field_name == 'phone_number') and message.text == 's') else message.text
# Done collecting?
if step >= len(TEACHER_FIELDS):
+1 -1
View File
@@ -6,6 +6,6 @@ def create_entity_markup(entity_name, enitity_id, is_auth_needed=False):
markup.add(types.InlineKeyboardButton(
f"✏️ Edit {entity_name} " + ("🔒" if is_auth_needed else ""), callback_data=f"edit_{entity_name}_{enitity_id}"))
markup.add(types.InlineKeyboardButton(
f"🗑️ Delete {entity_name}" + ("🔒" if is_auth_needed else ""), callback_data=f"delete_{entity_name}_{enitity_id}"))
f"🗑️ Delete {entity_name} " + ("🔒" if is_auth_needed else ""), callback_data=f"delete_{entity_name}_{enitity_id}"))
return markup
+30 -2
View File
@@ -115,11 +115,11 @@ def format_teacher_info(teacher):
def format_course_info(course):
"""
Format course data into a nice message
course is a tuple: (id, name, created_at, teacher_id, updated_at, description, difficulty, language)
course is a tuple: (id, name, created_at, teacher_id, updated_at, description, difficulty, language, avgerage_rate)
"""
# print(course)
id, name, created_at, teacher_id, updated_at, description, difficulty, language = course
id, name, created_at, teacher_id, updated_at, description, difficulty, language, avgerage_rate = course
# Format dates nicely
created_date = format_date(created_at)
@@ -129,6 +129,7 @@ def format_course_info(course):
<b>📚 Course Profile</b>
<b>Name:</b> {name}
<b>⭐️ Average Rating:</b> {avgerage_rate}
<b>Description:</b>
{description}
@@ -141,3 +142,30 @@ def format_course_info(course):
<b>ID:</b> <code>{id}</code>
"""
return details.strip()
def format_course_review(review):
"""
Formate course review into a nice message
:param review: tuple(body: str, rate: int, student_name: str, course_name: str, created_at: date, updated_at: date)
"""
reviewBody, rate, student_name, course_name, created_at, updated_at = review
created_date = format_date(created_at)
updated_at = format_date(updated_at)
details = f"""
<b>✉️ Review</b>
<b>rate:</b> {'⭐️' * rate}
<b>Body:</b>\n{reviewBody}
<b>Course name:</b> {course_name}
<b>Student name:</b> {student_name}
<b>📅 Review Info</b>
<b>Created At:</b> {created_date}
<b>Last Updated At:</b> {updated_at}
"""
return details.strip()