diff --git a/src/core/utils.py b/src/core/utils.py index 7e5133b..a5a2fe9 100644 --- a/src/core/utils.py +++ b/src/core/utils.py @@ -2,6 +2,7 @@ import uuid import hashlib import base64 import re +import pytz from datetime import datetime from urllib.parse import urlparse @@ -11,7 +12,7 @@ def UUID_code_generator(code_length: int) -> str: Генерирует уникальный код в формате UUID. Возвращает строку с кодом. """ - return str(uuid.uuid4().hex[:{code_length}].upper()) + return str(uuid.uuid4().hex[:code_length].upper()) def generate_session_code(telegram_id: int, phone: str, consultation_date_time: str) -> str: @@ -40,6 +41,22 @@ def is_phone_correct(phone: str) -> bool: return re.match(regex, normalized) +def get_timezones(): + return_value = {} + for tz in pytz.common_timezones: + c = tz.split("/") + if len(c) > 1: + if c[0] not in return_value.keys(): + return_value[c[0]] = [] + return_value[c[0]].append(c[1]) + + for i in ["GMT"]: + if i in return_value.keys(): + return_value.pop(i) + + return return_value + + def is_valid_url(text: str) -> bool: try: result = urlparse(text) diff --git a/src/db/models.py b/src/db/models.py index e968565..b4ac707 100644 --- a/src/db/models.py +++ b/src/db/models.py @@ -83,6 +83,8 @@ class Doctors(Base): __tablename__ = "doctors" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + code: Mapped[str] = mapped_column(unique=True, nullable=True) + time_zone: Mapped[Optional[str]] = mapped_column(nullable=True) telegram_id: Mapped[int] = mapped_column(unique=True, nullable=False) name: Mapped[str] = mapped_column(nullable=False) available_formats: Mapped[Optional[List[str]] diff --git a/src/docbot/handlers/admins/doctors_handler.py b/src/docbot/handlers/admins/doctors_handler.py index 6e0eee7..127aade 100644 --- a/src/docbot/handlers/admins/doctors_handler.py +++ b/src/docbot/handlers/admins/doctors_handler.py @@ -5,6 +5,7 @@ from telegram.ext import ( CommandHandler, ) +from core.logging import logger from docbot.services.doctors_service import get_doctors_names from docbot.services.admins_service import get_admin_info @@ -22,9 +23,14 @@ async def get_doctors(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int return ConversationHandler.END doctors = await get_doctors_names() + doctors_table = "" + for row in doctors: + logger.info(f"{row.Doctors.name} {row.VerificationRequests.code}") + doctors_table += f"\n- {row.Doctors.name} (код врача: {row.Doctors.code}) (код верификации: {row.VerificationRequests.code})" + if doctors: await update.message.reply_text( - f"📝 Список активных врачей. \n {doctors}", + f"📝 Список активных врачей. \n {doctors_table}", parse_mode="Markdown" ) return ConversationHandler.END diff --git a/src/docbot/handlers/doctors/register_handler.py b/src/docbot/handlers/doctors/register_handler.py index cd2f484..72be75b 100644 --- a/src/docbot/handlers/doctors/register_handler.py +++ b/src/docbot/handlers/doctors/register_handler.py @@ -1,4 +1,4 @@ -from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove +from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove, InlineKeyboardButton from telegram.ext import ( ContextTypes, ConversationHandler, @@ -11,7 +11,8 @@ from docbot.handlers.utils.cancel_handler import get_cancel_handler from docbot.services.referral_service import validate_referral_code from docbot.services.doctors_service import create_doctor from core.enums.dialog_helpers import ConfirmationMessage, Acknowledgement -from core.utils import UUID_code_generator +from core.utils import UUID_code_generator, get_timezones +from core.logging import logger ASK_REFERRAL_CODE = 1 SEND_ACKNOWLEDGEMENT_INFO = 2 @@ -19,6 +20,8 @@ ASK_NAME = 3 ASK_SPECIALITY = 4 SEND_DIPLOMA_ACK_INFO = 5 SEND_CONSULTATION_TYPE_ACK_INFO = 6 +ASK_TIMEZONE = 7 +ASK_CONTINENT = 8 SEND_ME_REFERRAL_CODE_TEXT = ( "✌️ Пожалуйста, пришлите реферальный код, чтобы начать процесс регистрации." @@ -34,6 +37,9 @@ SEND_ME_YOUR_SPECIALITY_TEXT = ( "📝 Пожалуйста, введите вашу специальность/специальности, в соответствии с которой планируете проводить консультации.\n" "Например: терапевт, кардиолог, невролог и т.д." ) +SELECT_YOUR_TIMEZONE_TEXT = ( + "📝 Пожалуйста, выбирете временную зону, в соответствии с которой планируете проводить консультации.\n" +) WAIT_FOR_ACTIVATION_TEXT = ( "📝 Заявка принята, направьте диплом и аккредитацию с указанием кода верификации {0} в теме письма на адрес электронной почты: docbot@docbot.ru\n" "В этом шаге нужно встроить подсказку: в каком формате направлять диплом, как скачать с госуслуг\n" @@ -101,6 +107,43 @@ async def receive_doctor_name(update: Update, context: ContextTypes.DEFAULT_TYPE doctor_name = update.message.text.strip() context.user_data["doctor_name"] = doctor_name + keyboard = [] + + for continent in sorted(get_timezones().keys()): + keyboard.append([InlineKeyboardButton(continent)]) + + reply_markup = ReplyKeyboardMarkup(keyboard, one_time_keyboard=True) + + await update.message.reply_text( + SELECT_YOUR_TIMEZONE_TEXT, + parse_mode="Markdown", + reply_markup=reply_markup, + ) + return ASK_CONTINENT + + +async def receive_continent(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: + keyboard = [] + + context.user_data["selected_continent"] = selected_continent = update.message.text + + for continent in sorted(get_timezones()[selected_continent]): + keyboard.append([InlineKeyboardButton(continent)]) + reply_markup = ReplyKeyboardMarkup(keyboard, one_time_keyboard=True) + + await update.message.reply_text( + SELECT_YOUR_TIMEZONE_TEXT, + parse_mode="Markdown", + reply_markup=reply_markup, + ) + return ASK_TIMEZONE + + +async def receive_time_zone(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: + context.user_data["time_zone"] = context.user_data["selected_continent"] + "/" + update.message.text + + logger.info(f"User {context.user_data['doctor_telegram_id']} selected timezone {context.user_data['time_zone']}") + await update.message.reply_text( SEND_ME_YOUR_SPECIALITY_TEXT, parse_mode="Markdown" @@ -168,11 +211,15 @@ async def receive_doctor_consultation_packages_acknowledgement_status(update: Up reply_markup=ReplyKeyboardRemove() ) + doc_code = UUID_code_generator(code_length=5) + await create_doctor( context.user_data["ref_obj"], context.user_data["doctor_telegram_id"], context.user_data["doctor_name"], - context.user_data["verification_request_code"] + context.user_data["verification_request_code"], + code=doc_code, + time_zone=context.user_data["time_zone"] ) return ConversationHandler.END @@ -198,6 +245,14 @@ def get_register_doctor_first_stage_handler() -> ConversationHandler: MessageHandler(filters.TEXT & ~filters.COMMAND, receive_doctor_name) ], + ASK_TIMEZONE: [ + MessageHandler(filters.TEXT & ~filters.COMMAND, + receive_time_zone) + ], + ASK_CONTINENT: [ + MessageHandler(filters.TEXT & ~filters.COMMAND, + receive_continent) + ], ASK_SPECIALITY: [ MessageHandler(filters.TEXT & ~filters.COMMAND, receive_doctor_speciality) diff --git a/src/docbot/handlers/patients/consultation_handler.py b/src/docbot/handlers/patients/consultation_handler.py index 14c99cc..8d965aa 100644 --- a/src/docbot/handlers/patients/consultation_handler.py +++ b/src/docbot/handlers/patients/consultation_handler.py @@ -17,6 +17,7 @@ from docbot.services.patients_service import ( create_patient, update_patient_phone, get_patient_by_telegram_id, is_user_has_phone ) +from docbot.services.doctors_service import get_doctors_payment_link from docbot.services.session_service import create_session from core.logging import logger @@ -283,7 +284,8 @@ async def pay_consultation(update: Update, context: ContextTypes.DEFAULT_TYPE) - patient=patient ) - link = f"https://example.com/consultation/{user_id}" + link = await get_doctors_payment_link(context.user_data['doctor_number']) + await update.callback_query.answer() await update.callback_query.message.reply_text( f"Чтобы оплатить консультацию перейдите по ссылке {link}.", diff --git a/src/docbot/handlers/utils/help.py b/src/docbot/handlers/utils/help.py index d8dab50..b888c0d 100644 --- a/src/docbot/handlers/utils/help.py +++ b/src/docbot/handlers/utils/help.py @@ -24,6 +24,7 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No "/start ‒ Запустить бота\n" "/genref ‒ Сгенерировать новый реферальный код для врача\n" "/doctors ‒ Посмотреть список зарегистрированных врачей\n" + "/verify ‒ Верифицировать врача по реферальному коду\n" "/help ‒ Показать это меню\n" ) # (При необходимости в будущем можно добавить другие admin-команды) diff --git a/src/docbot/services/doctors_service.py b/src/docbot/services/doctors_service.py index 95bb265..974510f 100644 --- a/src/docbot/services/doctors_service.py +++ b/src/docbot/services/doctors_service.py @@ -24,10 +24,28 @@ async def get_doctor(telegram_id: int) -> Doctors | None: async def get_doctors_names() -> Doctors | None: async with AsyncSessionLocal() as session: result = await session.execute( - select(Doctors.name) + select(Doctors, VerificationRequests) + .join(VerificationRequests, Doctors.id == VerificationRequests.doctor_id) .where(Doctors.is_active) ) - return result.all() + return result.fetchall() + + +async def get_doctors_payment_link(doctor_code: str) -> str | None: + async with AsyncSessionLocal() as session: + result = await session.execute( + select(PaymentMethod.method) + .join(Doctors, PaymentMethod.doctor_id == Doctors.id) + .where( + Doctors.code == doctor_code, + PaymentMethod.is_active.is_(True), + PaymentMethod.is_primary.is_(True) + ) + ) + payment_link = result.scalar_one_or_none() + if payment_link: + return payment_link + return None async def is_there_primary_payment_method() -> PaymentMethod | None: @@ -67,7 +85,7 @@ async def add_doctor(telegram_id: int, name: str, available_formats: Consultatio )) -async def create_doctor(referral_obj: ReferralCode, telegram_id: int, name: str, verification_code: str) -> Doctors: +async def create_doctor(referral_obj: ReferralCode, telegram_id: int, name: str, verification_code: str, code: str, time_zone: str) -> Doctors: """ Помечает referral_obj как использованный, создаёт запись в таблице Doctors с привязкой к telegram_id и возвращает объект Doctor. @@ -84,7 +102,9 @@ async def create_doctor(referral_obj: ReferralCode, telegram_id: int, name: str, name=name, is_active=False, created_at=datetime.utcnow(), - referral=referral_obj + referral=referral_obj, + code=code, + time_zone=time_zone ) verification_request = VerificationRequests()