mirror of
https://github.com/Faridik/Carolyn.git
synced 2026-07-08 05:11:52 +00:00
Изменения:
- в объекте assignment возвращается уникальный идентификатор; - сделан ConversationHandler для работы с заданием; - изменено эмодзи неправильного задания; - поправлены функции построения inline-меню
This commit is contained in:
parent
93b313690e
commit
881794535e
@ -1,13 +1,20 @@
|
|||||||
from logging import addLevelName
|
from logging import addLevelName
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
class Assignment(dict):
|
class Assignment(dict):
|
||||||
"""Задание."""
|
"""Задание."""
|
||||||
|
|
||||||
def __init__(self, name: str, points: list, weight: float = 1,
|
def __init__(
|
||||||
subject: str = None, allow_to_display: bool = False,
|
self,
|
||||||
how_to_display: str = 'z,1,1',
|
name: str,
|
||||||
notes: str = 'Замечаний по работе нет.'):
|
points: list,
|
||||||
|
weight: float = 1,
|
||||||
|
subject: str = None,
|
||||||
|
allow_to_display: bool = False,
|
||||||
|
how_to_display: str = "z,1,1",
|
||||||
|
notes: str = "Замечаний по работе нет.",
|
||||||
|
):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
self.weight = weight
|
self.weight = weight
|
||||||
@ -15,6 +22,7 @@ class Assignment(dict):
|
|||||||
self.allow_to_display = allow_to_display
|
self.allow_to_display = allow_to_display
|
||||||
self.how_to_display = how_to_display
|
self.how_to_display = how_to_display
|
||||||
self.notes = notes
|
self.notes = notes
|
||||||
|
self.uuid = uuid.uuid3(uuid.NAMESPACE_DNS, f"{name}{subject}")
|
||||||
dict.__init__(
|
dict.__init__(
|
||||||
self,
|
self,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
@ -25,6 +33,7 @@ class Assignment(dict):
|
|||||||
allow_to_display=self.allow_to_display,
|
allow_to_display=self.allow_to_display,
|
||||||
how_to_display=self.how_to_display,
|
how_to_display=self.how_to_display,
|
||||||
notes=self.notes,
|
notes=self.notes,
|
||||||
|
uuid=self.uuid,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@ -27,7 +27,7 @@ TOKEN = Path(".secrets/bot_token.txt").read_text()
|
|||||||
MESSAGES = Messages()
|
MESSAGES = Messages()
|
||||||
HOST = "http://carolyn-spreadsheets:5000"
|
HOST = "http://carolyn-spreadsheets:5000"
|
||||||
BROADCAST_MESSAGE, BROADCAST_PUBLISH_DONE = range(2)
|
BROADCAST_MESSAGE, BROADCAST_PUBLISH_DONE = range(2)
|
||||||
GRADES_CALLBACK = 1
|
GRADES_ASSNT, GRADES_VIEW = range(2)
|
||||||
|
|
||||||
logging.setLoggerClass(TgLogger)
|
logging.setLoggerClass(TgLogger)
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@ -77,118 +77,105 @@ def start(update: Update, context: CallbackContext):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def grades(update: Update, context: CallbackContext):
|
def grades_start(update: Update, context: CallbackContext):
|
||||||
"""Команда: Получить оценки студента."""
|
"""Сообщение 'Выбери дисциплину'.
|
||||||
|
|
||||||
|
Обновляет текст сообщения и дает клавиатуру с выбором дисциплины.
|
||||||
|
"""
|
||||||
user_id = update.message.from_user.id
|
user_id = update.message.from_user.id
|
||||||
|
|
||||||
start = time.monotonic()
|
start = time.monotonic()
|
||||||
# Длинная операция, сообщим о запущенном процессе.
|
# Длинная операция, сообщим о запущенном процессе.
|
||||||
look_msg = update.message.reply_html(MESSAGES.Assignments.START)
|
look_msg = update.message.reply_html(MESSAGES.Assignments.START)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = requests.get(f"{HOST}/grades", params={"tg_id": user_id}).json()
|
data = requests.get(f"{HOST}/grades", params={"tg_id": user_id}).json()
|
||||||
|
|
||||||
subjects = data["subjects"]
|
|
||||||
assignments = data["assignments"]
|
|
||||||
|
|
||||||
if len(subjects) > 1:
|
|
||||||
context.user_data["subjects"] = subjects
|
|
||||||
context.user_data["all_assignments"] = assignments
|
|
||||||
reply_markup = build_menu_of_subjects(subjects)
|
|
||||||
msg = update.message.reply_text(
|
|
||||||
text=MESSAGES.Assignments.SELECT_COURSE, reply_markup=reply_markup
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
context.user_data["assignments"] = assignments
|
|
||||||
reply_markup = build_menu_of_assignments(assignments)
|
|
||||||
msg = update.message.reply_text(
|
|
||||||
text=MESSAGES.Assignments.SELECT_ASSNT, reply_markup=reply_markup
|
|
||||||
)
|
|
||||||
|
|
||||||
context.user_data["msg"] = msg
|
|
||||||
return_value = GRADES_CALLBACK
|
|
||||||
|
|
||||||
except:
|
except:
|
||||||
LOG.exception("Failed to get grades.")
|
LOG.exception("Failed to get grades.")
|
||||||
update.message.reply_sticker(MESSAGES.Stickers.DEAD)
|
update.message.reply_sticker(MESSAGES.Stickers.DEAD)
|
||||||
return_value = ConversationHandler.END
|
|
||||||
diff = time.monotonic() - start
|
|
||||||
look_msg.edit_text(MESSAGES.Assignments.timeit(diff))
|
|
||||||
return return_value
|
|
||||||
|
|
||||||
|
|
||||||
def callback(update: Update, context: CallbackContext):
|
|
||||||
|
|
||||||
call = update.callback_query.data
|
|
||||||
|
|
||||||
# Нажатие кнопок при выборе задания
|
|
||||||
if call.startswith("assignment#"):
|
|
||||||
|
|
||||||
if "$back$" in call:
|
|
||||||
subjects = context.user_data["subjects"]
|
|
||||||
reply_markup = build_menu_of_subjects(subjects)
|
|
||||||
msg = update.callback_query.message.edit_text(
|
|
||||||
text=MESSAGES.Assignments.SELECT_COURSE, reply_markup=reply_markup
|
|
||||||
)
|
|
||||||
context.user_data["msg"] = msg
|
|
||||||
return GRADES_CALLBACK
|
|
||||||
|
|
||||||
assignments = context.user_data["assignments"]
|
|
||||||
name, subject = call.split("#")[1:3]
|
|
||||||
|
|
||||||
reply_markup = build_menu_of_assignments(
|
|
||||||
assignments, name, has_back_button=context.user_data["has_back_button"]
|
|
||||||
)
|
|
||||||
|
|
||||||
f = lambda ass: ass["name"] == name and ass["subject"] == subject
|
|
||||||
assignment = next(filter(f, assignments))
|
|
||||||
msg = update.callback_query.message.edit_text(
|
|
||||||
text=MESSAGES.Assignments.get(assignment['name'],
|
|
||||||
assignment['points'],
|
|
||||||
assignment['how_to_display'],
|
|
||||||
assignment['notes']),
|
|
||||||
parse_mode=ParseMode.HTML,
|
|
||||||
reply_markup=reply_markup
|
|
||||||
)
|
|
||||||
context.user_data["msg"] = msg
|
|
||||||
|
|
||||||
# Нажатие кнопок при выборе дисциплины
|
|
||||||
if call.startswith("subject#"):
|
|
||||||
all_assignments = context.user_data["all_assignments"]
|
|
||||||
subject = call.split("#")[1]
|
|
||||||
|
|
||||||
f = lambda ass: ass["subject"] == subject
|
|
||||||
assignments = list(filter(f, all_assignments))
|
|
||||||
context.user_data["assignments"] = assignments
|
|
||||||
context.user_data["has_back_button"] = True
|
|
||||||
|
|
||||||
reply_markup = build_menu_of_assignments(assignments, has_back_button=True)
|
|
||||||
msg = update.callback_query.message.edit_text(
|
|
||||||
MESSAGES.Assignments.SELECT_ASSNT, reply_markup=reply_markup
|
|
||||||
)
|
|
||||||
context.user_data["msg"] = msg
|
|
||||||
|
|
||||||
if call.startswith("cancel#"):
|
|
||||||
update.callback_query.message.edit_text(
|
|
||||||
MESSAGES.Assignments.END,
|
|
||||||
)
|
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
return GRADES_CALLBACK
|
context.user_data["subjects"] = data["subjects"]
|
||||||
|
context.user_data["assignments"] = data["assignments"]
|
||||||
def timeout(update: Update, context: CallbackContext):
|
diff = time.monotonic() - start
|
||||||
msg = context.user_data["msg"]
|
look_msg.edit_text(MESSAGES.Assignments.timeit(diff))
|
||||||
msg.edit_text(
|
update.message.reply_text(
|
||||||
MESSAGES.Assignments.TIMEOUT,
|
MESSAGES.Assignments.SELECT_COURSE,
|
||||||
)
|
reply_markup=build_menu_of_subjects(data["subjects"]),
|
||||||
return ConversationHandler.END
|
|
||||||
|
|
||||||
def grades_end(update: Update, context: CallbackContext):
|
|
||||||
msg = context.user_data["msg"]
|
|
||||||
msg.edit_text(
|
|
||||||
MESSAGES.Assignments.END
|
|
||||||
)
|
)
|
||||||
return ConversationHandler.END
|
return GRADES_ASSNT
|
||||||
|
|
||||||
|
|
||||||
|
def grades_pick_assignment(update: Update, context: CallbackContext):
|
||||||
|
"""Сообщение 'Выбери задание'.
|
||||||
|
|
||||||
|
Обновляет текст сообщения и дает клавиатуру с выбором дисциплины.
|
||||||
|
В `callback_query` содержится имя предмета, либо ключевое слово.
|
||||||
|
Может завершить диалог.
|
||||||
|
"""
|
||||||
|
query = update.callback_query
|
||||||
|
query.answer()
|
||||||
|
if query.data == "cancel":
|
||||||
|
query.edit_message_text(MESSAGES.Assignments.END, reply_markup=None)
|
||||||
|
return ConversationHandler.END
|
||||||
|
context.user_data["selected_subject"] = query.data
|
||||||
|
assignments = [
|
||||||
|
a for a in context.user_data["assignments"] if a["subject"] == query.data
|
||||||
|
]
|
||||||
|
query.edit_message_text(
|
||||||
|
MESSAGES.Assignments.SELECT_ASSNT,
|
||||||
|
reply_markup=build_menu_of_assignments(assignments, has_back_button=True),
|
||||||
|
)
|
||||||
|
return GRADES_VIEW
|
||||||
|
|
||||||
|
|
||||||
|
def grades_view(update: Update, context: CallbackContext):
|
||||||
|
"""Отображение домашки.
|
||||||
|
|
||||||
|
В `callback_query` содержится UUID, идентификатор задания. Либо команда:
|
||||||
|
cancel или back. Может завершить диалог.
|
||||||
|
"""
|
||||||
|
query = update.callback_query
|
||||||
|
query.answer()
|
||||||
|
|
||||||
|
if query.data == "cancel":
|
||||||
|
query.edit_message_text(MESSAGES.Assignments.END, reply_markup=None)
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
if query.data == "back":
|
||||||
|
subjects = context.user_data["subjects"]
|
||||||
|
query.edit_message_text(
|
||||||
|
MESSAGES.Assignments.SELECT_COURSE,
|
||||||
|
reply_markup=build_menu_of_subjects(subjects),
|
||||||
|
)
|
||||||
|
return GRADES_ASSNT
|
||||||
|
|
||||||
|
assignments_map = {a["uuid"]: a for a in context.user_data["assignments"]}
|
||||||
|
assignment = assignments_map[query.data]
|
||||||
|
subject = context.user_data["selected_subject"]
|
||||||
|
|
||||||
|
raw_menu = build_menu(
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("Отмена", callback_data="cancel"),
|
||||||
|
InlineKeyboardButton("⏮ Назад", callback_data=subject),
|
||||||
|
],
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
|
||||||
|
reply_markup = InlineKeyboardMarkup(raw_menu)
|
||||||
|
|
||||||
|
query.edit_message_text(
|
||||||
|
text=MESSAGES.Assignments.get(
|
||||||
|
assignment["name"],
|
||||||
|
assignment["points"],
|
||||||
|
assignment["how_to_display"],
|
||||||
|
assignment["notes"],
|
||||||
|
),
|
||||||
|
parse_mode=ParseMode.HTML,
|
||||||
|
reply_markup=reply_markup,
|
||||||
|
)
|
||||||
|
|
||||||
|
return GRADES_ASSNT
|
||||||
|
|
||||||
|
|
||||||
def broadcast(update: Update, context: CallbackContext):
|
def broadcast(update: Update, context: CallbackContext):
|
||||||
"""Команда: разослать студентам."""
|
"""Команда: разослать студентам."""
|
||||||
@ -268,18 +255,12 @@ def main() -> None:
|
|||||||
start_handler = CommandHandler("start", start)
|
start_handler = CommandHandler("start", start)
|
||||||
|
|
||||||
grades_handler = ConversationHandler(
|
grades_handler = ConversationHandler(
|
||||||
entry_points=[CommandHandler("grades", grades)],
|
entry_points=[CommandHandler("grades", grades_start)],
|
||||||
states={
|
states={
|
||||||
GRADES_CALLBACK: [CallbackQueryHandler(callback)],
|
GRADES_ASSNT: [CallbackQueryHandler(grades_pick_assignment)],
|
||||||
ConversationHandler.TIMEOUT: [
|
GRADES_VIEW: [CallbackQueryHandler(grades_view)],
|
||||||
MessageHandler(Filters.text | Filters.command, timeout)
|
|
||||||
],
|
|
||||||
ConversationHandler.END: [
|
|
||||||
MessageHandler(Filters.text | Filters.command, grades_end)
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
fallbacks=[CommandHandler("cancel", cancel)],
|
fallbacks=[CommandHandler("cancel", cancel)],
|
||||||
#conversation_timeout=10,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
broadcast_handler = ConversationHandler(
|
broadcast_handler = ConversationHandler(
|
||||||
@ -294,7 +275,6 @@ def main() -> None:
|
|||||||
dispatcher.add_handler(start_handler)
|
dispatcher.add_handler(start_handler)
|
||||||
dispatcher.add_handler(grades_handler)
|
dispatcher.add_handler(grades_handler)
|
||||||
dispatcher.add_handler(broadcast_handler)
|
dispatcher.add_handler(broadcast_handler)
|
||||||
#dispatcher.add_handler(callback_handler)
|
|
||||||
|
|
||||||
# Начало работы бота
|
# Начало работы бота
|
||||||
updater.start_polling()
|
updater.start_polling()
|
||||||
|
|||||||
@ -30,20 +30,24 @@ class Messages:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(name: str, assignment: list, how_to_display: str, notes: str):
|
def get(name: str, assignment: list, how_to_display: str, notes: str):
|
||||||
is_float, n_cols, n_rows = how_to_display.split(',')
|
is_float, n_cols, n_rows = how_to_display.split(",")
|
||||||
n_cols, n_rows = int(n_cols), int(n_rows)
|
n_cols, n_rows = int(n_cols), int(n_rows)
|
||||||
summary = f'<b>{name}</b>:\n'
|
summary = f"<b>{name}</b>:\n"
|
||||||
if is_float == 'z':
|
if is_float == "z":
|
||||||
d = {0 : '❎', 1 : '✅'}
|
d = {0: "🟥", 1: "✅"}
|
||||||
for item in range(0, len(assignment), n_cols):
|
for item in range(0, len(assignment), n_cols):
|
||||||
summary += ''.join(list(map(d.get,
|
summary += (
|
||||||
assignment[item : item + n_cols]))) + '\n'
|
"".join(list(map(d.get, assignment[item : item + n_cols])))
|
||||||
elif is_float == 'r':
|
+ "\n"
|
||||||
to_str = lambda x: f'{x:.2f}'
|
)
|
||||||
|
elif is_float == "r":
|
||||||
|
to_str = lambda x: f"{x:.2f}"
|
||||||
for item in range(0, len(assignment), n_cols):
|
for item in range(0, len(assignment), n_cols):
|
||||||
summary += ' '.join(list(map(to_str,
|
summary += (
|
||||||
assignment[item: item + n_cols]))) + '\n'
|
" ".join(list(map(to_str, assignment[item : item + n_cols])))
|
||||||
summary += f'Σ: {to_str(sum(assignment))}\n'
|
+ "\n"
|
||||||
|
)
|
||||||
|
summary += f"Σ: {to_str(sum(assignment))}\n"
|
||||||
summary += f"\n{notes}"
|
summary += f"\n{notes}"
|
||||||
return summary
|
return summary
|
||||||
|
|
||||||
|
|||||||
@ -5,41 +5,36 @@ from telegram import (
|
|||||||
|
|
||||||
|
|
||||||
def build_menu_of_subjects(subjects: list) -> InlineKeyboardMarkup:
|
def build_menu_of_subjects(subjects: list) -> InlineKeyboardMarkup:
|
||||||
button_list = []
|
button_list = [
|
||||||
for subject in subjects:
|
InlineKeyboardButton(subject, callback_data=subject) for subject in subjects
|
||||||
button_list.append(
|
]
|
||||||
InlineKeyboardButton(subject, callback_data=f"subject#{subject}")
|
end_button = [InlineKeyboardButton("Отмена", callback_data="cancel")]
|
||||||
)
|
return InlineKeyboardMarkup(
|
||||||
end_button = [InlineKeyboardButton("Отмена", callback_data="cancel#")]
|
build_menu(button_list, n_cols=2, footer_buttons=end_button)
|
||||||
return InlineKeyboardMarkup(build_menu(button_list, n_cols=2,
|
)
|
||||||
footer_buttons=end_button))
|
|
||||||
|
|
||||||
|
|
||||||
def build_menu_of_assignments(
|
def build_menu_of_assignments(
|
||||||
assignments: dict, pressed_name: str = None, has_back_button: bool = False
|
assignments: dict, has_back_button: bool = False
|
||||||
) -> InlineKeyboardMarkup:
|
) -> InlineKeyboardMarkup:
|
||||||
button_list = []
|
button_list = []
|
||||||
for assignment in assignments:
|
for assignment in assignments:
|
||||||
if not assignment['allow_to_display']:
|
if not assignment["allow_to_display"]:
|
||||||
continue
|
continue
|
||||||
if pressed_name and assignment["name"] == pressed_name:
|
|
||||||
title = f'💁♀️ {assignment["name"]}'
|
|
||||||
else:
|
else:
|
||||||
title = assignment["name"]
|
title = assignment["name"]
|
||||||
button_list.append(
|
button_list.append(
|
||||||
InlineKeyboardButton(
|
InlineKeyboardButton(
|
||||||
title,
|
title,
|
||||||
callback_data=f'assignment#{assignment["name"]}'
|
callback_data=assignment["uuid"],
|
||||||
+ f'#{assignment["subject"]}',
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if has_back_button:
|
if has_back_button:
|
||||||
button_list.append(
|
button_list.append(InlineKeyboardButton("⏮ Назад", callback_data="back"))
|
||||||
InlineKeyboardButton("Назад", callback_data=f"assignment#$back$")
|
end_button = [InlineKeyboardButton("Отмена", callback_data="cancel")]
|
||||||
)
|
return InlineKeyboardMarkup(
|
||||||
end_button = [InlineKeyboardButton("Отмена", callback_data="cancel#")]
|
build_menu(button_list, n_cols=3, footer_buttons=end_button)
|
||||||
return InlineKeyboardMarkup(build_menu(button_list, n_cols=3,
|
)
|
||||||
footer_buttons=end_button))
|
|
||||||
|
|
||||||
|
|
||||||
def build_menu(buttons, n_cols, header_buttons=None, footer_buttons=None):
|
def build_menu(buttons, n_cols, header_buttons=None, footer_buttons=None):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user