diff --git a/.github/workflows/build_and_publish.yml b/.github/workflows/build_and_publish.yml
new file mode 100644
index 0000000..06a7270
--- /dev/null
+++ b/.github/workflows/build_and_publish.yml
@@ -0,0 +1,129 @@
+name: Build, publish and deploy docker
+
+on:
+ push:
+ branches: [ 'main' ]
+ tags:
+ - 'v*'
+
+env:
+ REGISTRY: ghcr.io
+ IMAGE_NAME: ${{ github.repository }}
+
+jobs:
+ build-and-push-image:
+ name: Build and push
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Log in to the Container registry
+ uses: docker/login-action@v2
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@v4
+ with:
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+ tags: |
+ type=ref,event=tag,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
+ type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
+ type=raw,value=test,enable=true
+ - name: Build and push Docker image
+ uses: docker/build-push-action@v4
+ with:
+ context: .
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+
+ deploy-testing:
+ name: Deploy Testing
+ needs: build-and-push-image
+ runs-on: [ self-hosted, Linux, testing ]
+ environment:
+ name: Testing
+ env:
+ CONTAINER_NAME: com_profcomff_tgbot_print_test
+ permissions:
+ packages: read
+ steps:
+ - name: Pull new version
+ run: docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test
+ - name: Migrate DB
+ run: |
+ docker run \
+ --rm \
+ --env DB_DSN=${{ secrets.DB_DSN }} \
+ --env TG_TOKEN=${{ secrets.BOT_TOKEN }} \
+ --env MARKETING_URL=${{ vars.MARKETING_URL }} \
+ --env DEFAULT_PARE_iD=${{ vars.DEFAULT_PARE_iD }} \
+ --env ADMIN_TG_ID=${{ vars.ADMIN_TG_ID }} \
+ --env DEFAULT_PARE_USERNAME=${{ vars.DEFAULT_PARE_USERNAME }} \
+ --name ${{ env.CONTAINER_NAME }}_migration \
+ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test \
+ alembic upgrade head
+ - name: Run new version
+ run: |
+ docker stop ${{ env.CONTAINER_NAME }} || true && docker rm ${{ env.CONTAINER_NAME }} || true
+ docker run \
+ --detach \
+ --restart on-failure:3 \
+ --env DB_DSN=${{ secrets.DB_DSN }} \
+ --env TG_TOKEN=${{ secrets.BOT_TOKEN }} \
+ --env MARKETING_URL=${{ vars.MARKETING_URL }} \
+ --env DEFAULT_PARE_iD=${{ vars.DEFAULT_PARE_iD }} \
+ --env ADMIN_TG_ID=${{ vars.ADMIN_TG_ID }} \
+ --env DEFAULT_PARE_USERNAME=${{ vars.DEFAULT_PARE_USERNAME }} \
+ --name ${{ env.CONTAINER_NAME }} \
+ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test
+
+ deploy-production:
+ name: Deploy Production
+ needs: build-and-push-image
+ if: startsWith(github.ref, 'refs/tags/v')
+ runs-on: [ self-hosted, Linux, production ]
+ environment:
+ name: Production
+ env:
+ CONTAINER_NAME: com_profcomff_tgbot_print
+ permissions:
+ packages: read
+ steps:
+ - name: Pull new version
+ run: docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
+ - name: Migrate DB
+ run: |
+ docker run \
+ --rm \
+ --env DB_DSN=${{ secrets.DB_DSN }} \
+ --env TG_TOKEN=${{ secrets.BOT_TOKEN }} \
+ --env MARKETING_URL=${{ vars.MARKETING_URL }} \
+ --env DEFAULT_PARE_iD=${{ vars.DEFAULT_PARE_iD }} \
+ --env ADMIN_TG_ID=${{ vars.ADMIN_TG_ID }} \
+ --env DEFAULT_PARE_USERNAME=${{ vars.DEFAULT_PARE_USERNAME }} \
+ --name ${{ env.CONTAINER_NAME }}_migration \
+ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
+ alembic upgrade head
+ - name: Run new version
+ run: |
+ docker stop ${{ env.CONTAINER_NAME }} || true && docker rm ${{ env.CONTAINER_NAME }} || true
+ docker run \
+ --detach \
+ --restart always \
+ --env DB_DSN=${{ secrets.DB_DSN }} \
+ --env TG_TOKEN=${{ secrets.BOT_TOKEN }} \
+ --env MARKETING_URL=${{ vars.MARKETING_URL }} \
+ --env DEFAULT_PARE_iD=${{ vars.DEFAULT_PARE_iD }} \
+ --env ADMIN_TG_ID=${{ vars.ADMIN_TG_ID }} \
+ --env DEFAULT_PARE_USERNAME=${{ vars.DEFAULT_PARE_USERNAME }} \
+ --name ${{ env.CONTAINER_NAME }} \
+ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
\ No newline at end of file
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
new file mode 100644
index 0000000..07479ca
--- /dev/null
+++ b/.github/workflows/checks.yml
@@ -0,0 +1,24 @@
+name: Python package
+
+on:
+ pull_request:
+
+
+jobs:
+ linting:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v4
+ with:
+ python-version: 3.11
+ - uses: isort/isort-action@master
+ with:
+ requirementsFiles: "requirements.txt requirements.dev.txt"
+ - uses: psf/black@stable
+ - name: Comment if linting failed
+ if: ${{ failure() }}
+ uses: thollander/actions-comment-pull-request@v2
+ with:
+ message: |
+ :poop: Code linting failed, use `black` and `isort` to fix it.
\ No newline at end of file
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
deleted file mode 100644
index e28a2c1..0000000
--- a/.github/workflows/deploy.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-name: deploy_coffee_bot
-
-on:
- push:
- branches: [ "main" ]
-
-jobs:
- build_and_push_to_docker_hub:
- name: Push Docker image to Docker Hub
- runs-on: ubuntu-latest
- steps:
- - name: Check out the repo
- uses: actions/checkout@v2
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v1
- - name: Login to Docker
- uses: docker/login-action@v1
- with:
- username: ${{ secrets.DOCKER_USERNAME }}
- password: ${{ secrets.DOCKER_PASSWORD }}
- - name: Push to Docker Hub
- uses: docker/build-push-action@v2
- with:
- push: true
- tags: ${{ secrets.DOCKER_USERNAME }}/random_coffee_bot:latest
-
- deploy:
- runs-on: ubuntu-latest
- needs: build_and_push_to_docker_hub
- steps:
- - name: executing remote ssh commands to deploy
- uses: appleboy/ssh-action@master
- with:
- host: ${{ secrets.HOST }}
- username: ${{ secrets.USER }}
- key: ${{ secrets.SSH_KEY }}
- script: |
- cd /home/random_coffee/
- sudo docker compose down
- sudo docker rmi ${{ secrets.DOCKER_USERNAME }}/random_coffee_bot:latest
- sudo docker pull ${{ secrets.DOCKER_USERNAME }}/random_coffee_bot:latest
- sudo docker compose up -d
-
- send_message:
- runs-on: ubuntu-latest
- needs: deploy
- steps:
- - name: send message
- uses: appleboy/telegram-action@master
- with:
- to: ${{ secrets.TELEGRAM_TO }}
- token: ${{ secrets.TELEGRAM_TOKEN }}
- message: ${{ github.workflow }} успешно выполнен!
diff --git a/.github/workflows/test_deploy.yml b/.github/workflows/test_deploy.yml
deleted file mode 100644
index 8f5728f..0000000
--- a/.github/workflows/test_deploy.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-name: deploy_coffee_bot_test
-
-on:
- push:
- branches: [ "test" ]
-
-jobs:
- build_and_push_to_docker_hub:
- name: Push Docker image to Docker Hub
- runs-on: ubuntu-latest
- steps:
- - name: Check out the repo
- uses: actions/checkout@v2
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v1
- - name: Login to Docker
- uses: docker/login-action@v1
- with:
- username: ${{ secrets.DOCKER_USERNAME }}
- password: ${{ secrets.DOCKER_PASSWORD }}
- - name: Push to Docker Hub
- uses: docker/build-push-action@v2
- with:
- push: true
- tags: ${{ secrets.DOCKER_USERNAME }}/random_coffee_bot:test
-
- deploy:
- runs-on: ubuntu-latest
- needs: build_and_push_to_docker_hub
- steps:
- - name: executing remote ssh commands to deploy
- uses: appleboy/ssh-action@master
- with:
- host: ${{ secrets.HOST }}
- username: ${{ secrets.USER }}
- key: ${{ secrets.SSH_KEY }}
- script: |
- cd /home/test_random_coffee/
- sudo docker compose down
- sudo docker rmi ${{ secrets.DOCKER_USERNAME }}/random_coffee_bot:test
- sudo docker pull ${{ secrets.DOCKER_USERNAME }}/random_coffee_bot:test
- sudo docker compose up -d
-
- send_message:
- runs-on: ubuntu-latest
- needs: deploy
- steps:
- - name: send message
- uses: appleboy/telegram-action@master
- with:
- to: ${{ secrets.TELEGRAM_TO }}
- token: ${{ secrets.TELEGRAM_TOKEN }}
- message: ${{ github.workflow }} успешно выполнен!
diff --git a/alembic.ini b/alembic.ini
new file mode 100644
index 0000000..317057e
--- /dev/null
+++ b/alembic.ini
@@ -0,0 +1,102 @@
+# A generic, single database configuration.
+
+[alembic]
+# path to migration scripts
+script_location = migrations
+
+# template used to generate migration files
+# file_template = %%(rev)s_%%(slug)s
+
+# sys.path path, will be prepended to sys.path if present.
+# defaults to the current working directory.
+prepend_sys_path = .
+
+# timezone to use when rendering the date within the migration file
+# as well as the filename.
+# If specified, requires the python-dateutil library that can be
+# installed by adding `alembic[tz]` to the pip requirements
+# string value is passed to dateutil.tz.gettz()
+# leave blank for localtime
+# timezone =
+
+# max length of characters to apply to the
+# "slug" field
+# truncate_slug_length = 40
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+# set to 'true' to allow .pyc and .pyo files without
+# a source .py file to be detected as revisions in the
+# versions/ directory
+# sourceless = false
+
+# version location specification; This defaults
+# to migrations/versions. When using multiple version
+# directories, initial revisions must be specified with --version-path.
+# The path separator used here should be the separator specified by "version_path_separator" below.
+# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
+# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
+# Valid values for version_path_separator are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
+
+# the output encoding used when revision files
+# are written from script.py.mako
+# output_encoding = utf-8
+
+sqlalchemy.url = driver://user:pass@localhost/dbname
+
+
+[post_write_hooks]
+# post_write_hooks defines scripts or Python functions that are run
+# on newly generated revision scripts. See the documentation for further
+# detail and examples
+
+# format using "black" - use the console_scripts runner, against the "black" entrypoint
+# hooks = black
+# black.type = console_scripts
+# black.entrypoint = black
+# black.options = -l 79 REVISION_SCRIPT_FILENAME
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
\ No newline at end of file
diff --git a/controllerBD/add_info_to_db.py b/controllerBD/add_info_to_db.py
index 2a63217..c9e567a 100644
--- a/controllerBD/add_info_to_db.py
+++ b/controllerBD/add_info_to_db.py
@@ -9,9 +9,9 @@
def add_gender_info():
- if not db_session.query(exists().where(
- Gender.gender_name == 'Не указано'
- )).scalar():
+ if not db_session.query(
+ exists().where(Gender.gender_name == "Не указано")
+ ).scalar():
db_session.add_all([el1, el2, el3])
db_session.commit()
diff --git a/controllerBD/db_loader.py b/controllerBD/db_loader.py
index 90c8705..4568b1a 100644
--- a/controllerBD/db_loader.py
+++ b/controllerBD/db_loader.py
@@ -1,7 +1,9 @@
from sqlalchemy import create_engine
-from sqlalchemy.orm import declarative_base, Session
+from sqlalchemy.orm import Session, declarative_base
-engine = create_engine('sqlite:///db/coffee_database.db')
+from data.config import DB_DSN
+
+engine = create_engine(DB_DSN)
engine.connect()
db_session = Session(bind=engine)
diff --git a/controllerBD/models.py b/controllerBD/models.py
index 884f712..eaa1e4e 100644
--- a/controllerBD/models.py
+++ b/controllerBD/models.py
@@ -1,77 +1,86 @@
-from sqlalchemy import Integer, Column, ForeignKey, Text
+from sqlalchemy import ForeignKey, Integer, String
+from sqlalchemy.orm import Mapped, mapped_column
-from .db_loader import engine, Base
+from .db_loader import Base, engine
class Gender(Base):
- __tablename__ = 'genders'
- id = Column(Integer, primary_key=True, unique=True)
- gender_name = Column(Text(100), nullable=False, unique=True)
+ __tablename__ = "genders"
+ id: Mapped[int] = mapped_column(primary_key=True, unique=True)
+ gender_name: Mapped[str] = mapped_column(String(100), nullable=False, unique=True)
class Users(Base):
- __tablename__ = 'user_info'
- id = Column(Integer, primary_key=True, autoincrement=True)
- teleg_id = Column(Integer, nullable=False, unique=True)
- name = Column(Text(100), nullable=False)
- birthday = Column(Text(), nullable=False)
- about = Column(Text(500), nullable=False)
- gender = Column(Integer, ForeignKey('genders.id'))
+ __tablename__ = "user_info"
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
+ teleg_id: Mapped[int] = mapped_column(nullable=False, unique=True)
+ name: Mapped[str] = mapped_column(String(100), nullable=False)
+ birthday: Mapped[str]
+ about: Mapped[str] = mapped_column(String(500), nullable=False)
+ gender: Mapped[int] = mapped_column(Integer, ForeignKey("genders.id"))
class BanList(Base):
- __tablename__ = 'ban_list'
- id = Column(Integer, primary_key=True, autoincrement=True)
- banned_user_id = Column(Integer, ForeignKey('user_info.id'))
- ban_status = Column(Integer, nullable=False, default=1)
- date_of_ban = Column(Text(), nullable=False, default='null')
- comment_to_ban = Column(Text(500), nullable=False)
- date_of_unban = Column(Text(), nullable=False, default='null')
- comment_to_unban = Column(Text(500), nullable=False, default='null')
+ __tablename__ = "ban_list"
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
+ banned_user_id: Mapped[int] = mapped_column(Integer, ForeignKey("user_info.id"))
+ ban_status: Mapped[int] = mapped_column(Integer, default=1)
+ date_of_ban: Mapped[str] = mapped_column(default="null")
+ comment_to_ban: Mapped[str] = mapped_column(String(500))
+ date_of_unban: Mapped[str] = mapped_column(default="null")
+ comment_to_unban: Mapped[str] = mapped_column(String(500), default="null")
class Holidays(Base):
- __tablename__ = 'holidays_status'
- id = Column(Integer, ForeignKey('user_info.id'), primary_key=True)
- status = Column(Integer, nullable=False, default=0)
- till_date = Column(Text, nullable=False, default='null')
+ __tablename__ = "holidays_status"
+ id: Mapped[int] = mapped_column(
+ Integer, ForeignKey("user_info.id"), primary_key=True
+ )
+ status: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
+ till_date: Mapped[str] = mapped_column(String, nullable=False, default="null")
class MetInfo(Base):
- __tablename__ = 'met_info'
- id = Column(Integer, primary_key=True, autoincrement=True)
- first_user_id = Column(Integer, ForeignKey('user_info.id'))
- second_user_id = Column(Integer, ForeignKey('user_info.id'))
- date = Column(Text())
+ __tablename__ = "met_info"
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
+ first_user_id: Mapped[int] = mapped_column(ForeignKey("user_info.id"))
+ second_user_id: Mapped[int] = mapped_column(ForeignKey("user_info.id"))
+ date: Mapped[str]
class MetsReview(Base):
- __tablename__ = 'mets_reviews'
- id = Column(Integer, primary_key=True, autoincrement=True)
- met_id = Column(Integer, ForeignKey('met_info.id'))
- who_id = Column(Integer, ForeignKey('user_info.id'))
- about_whom_id = Column(Integer, ForeignKey('user_info.id'))
- grade = Column(Integer, nullable=False)
- comment = Column(Text(500), nullable=True)
- date_of_comment = Column(Text())
+ __tablename__ = "mets_reviews"
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
+ met_id: Mapped[int] = mapped_column(ForeignKey("met_info.id"))
+ who_id: Mapped[int] = mapped_column(ForeignKey("user_info.id"))
+ about_whom_id: Mapped[int] = mapped_column(ForeignKey("user_info.id"))
+ grade: Mapped[int]
+ comment: Mapped[str | None] = mapped_column(String(500), nullable=True)
+ date_of_comment: Mapped[str]
class UserMets(Base):
- __tablename__ = 'user_mets'
- id = Column(Integer, ForeignKey('user_info.id'), primary_key=True)
- met_info = Column(Text(), nullable=False, default='{}')
+ __tablename__ = "user_mets"
+ id: Mapped[int] = mapped_column(
+ Integer, ForeignKey("user_info.id"), primary_key=True
+ )
+ met_info: Mapped[str] = mapped_column(nullable=False, default="{}")
class UserStatus(Base):
- __tablename__ = 'user_status'
- id = Column(Integer, ForeignKey('user_info.id'), primary_key=True)
- status = Column(Integer, nullable=False, default=1)
+ __tablename__ = "user_status"
+ id: Mapped[int] = mapped_column(
+ Integer, ForeignKey("user_info.id"), primary_key=True
+ )
+ status: Mapped[int] = mapped_column(default=1)
class Username(Base):
- __tablename__ = 'tg_usernames'
- id = Column(Integer, ForeignKey('user_info.id'), primary_key=True)
- username = Column(Text(), nullable=True)
+ __tablename__ = "tg_usernames"
+ id: Mapped[int] = mapped_column(
+ Integer, ForeignKey("user_info.id"), primary_key=True
+ )
+ username: Mapped[str]
def create_tables():
diff --git a/controllerBD/services.py b/controllerBD/services.py
index 75c43a0..ad61f11 100644
--- a/controllerBD/services.py
+++ b/controllerBD/services.py
@@ -16,31 +16,44 @@ async def update_mets(match_info: dict):
if all(match):
first_user = match[0]
second_user = match[1]
- db_session.add(MetInfo(first_user_id=first_user,
- second_user_id=second_user,
- date=str(date.today())))
+ db_session.add(
+ MetInfo(
+ first_user_id=first_user,
+ second_user_id=second_user,
+ date=str(date.today()),
+ )
+ )
except Exception as error:
- logger.error(f'Встреча для пользователей {match} '
- f'не записана. Ошибка - {error}')
+ logger.error(
+ f"Встреча для пользователей {match} " f"не записана. Ошибка - {error}"
+ )
continue
def update_one_user_mets(first_user: int, second_user: int):
"""Записывает в user_mets информацию об одном пользователе."""
- first_user_mets = db_session.query(UserMets.met_info).filter(
- UserMets.id == first_user
- ).first()
+ first_user_mets = (
+ db_session.query(UserMets.met_info).filter(UserMets.id == first_user).first()
+ )
user_mets = json.loads(first_user_mets[0])
- new_met_id = db_session.query(MetInfo.id).filter(
- MetInfo.date == date.today(),
- or_(
- MetInfo.first_user_id == first_user,
- MetInfo.second_user_id == first_user
- )).order_by(desc(MetInfo.id)).limit(1).first()[0]
+ new_met_id = (
+ db_session.query(MetInfo.id)
+ .filter(
+ MetInfo.date == date.today(),
+ or_(
+ MetInfo.first_user_id == first_user,
+ MetInfo.second_user_id == first_user,
+ ),
+ )
+ .order_by(desc(MetInfo.id))
+ .limit(1)
+ .first()[0]
+ )
user_mets[new_met_id] = second_user
new_mets_value = json.dumps(user_mets)
- db_session.query(UserMets).filter(UserMets.id == first_user). \
- update({'met_info': new_mets_value})
+ db_session.query(UserMets).filter(UserMets.id == first_user).update(
+ {"met_info": new_mets_value}
+ )
db_session.commit()
@@ -53,48 +66,48 @@ def update_all_user_mets(match_info: dict):
try:
update_one_user_mets(first_user, second_user)
except Exception as error:
- logger.error(f'Информация о встречах пользователя '
- f'{first_user} не обновлена. '
- f' Ошибка - {error}')
+ logger.error(
+ f"Информация о встречах пользователя "
+ f"{first_user} не обновлена. "
+ f" Ошибка - {error}"
+ )
first_user = match[1]
second_user = match[0]
try:
update_one_user_mets(first_user, second_user)
except Exception as error:
- logger.error(f'Информация о встречах пользователя '
- f'{first_user} не обновлена. '
- f' Ошибка - {error}')
- logger.info('Запись информации о новых встречах завершена')
+ logger.error(
+ f"Информация о встречах пользователя "
+ f"{first_user} не обновлена. "
+ f" Ошибка - {error}"
+ )
+ logger.info("Запись информации о новых встречах завершена")
def get_defaulf_pare_base_id():
"""Получить id дефолтного юзера из базы."""
- return db_session.query(Users.id).filter(
- Users.teleg_id == int(DEFAULT_PARE_iD)
- ).first()[0]
+ return (
+ db_session.query(Users.id)
+ .filter(Users.teleg_id == int(DEFAULT_PARE_iD))
+ .first()[0]
+ )
def get_user_count_from_db():
all_users = db_session.query(Users).count()
- active_users = db_session.query(UserStatus).filter(
- UserStatus.status == 1
- ).count()
+ active_users = db_session.query(UserStatus).filter(UserStatus.status == 1).count()
return {"all_users": all_users, "active_users": active_users}
def get_user_id_from_db(teleg_id: int) -> int:
"""Получает id юзера в базе по телеграм id"""
- return db_session.query(Users.id).filter(
- Users.teleg_id == teleg_id
- ).first()[0]
+ return db_session.query(Users.id).filter(Users.teleg_id == teleg_id).first()[0]
def get_tg_username_from_db_by_teleg_id(teleg_id: int) -> int:
"""Получает телеграм-юзернейм по telegram id"""
base_id = get_user_id_from_db(teleg_id)
- answer = db_session.query(Username.username).filter(
- Username.id == base_id
- ).first()
+ answer = db_session.query(Username.username).filter(Username.id == base_id).first()
if answer:
return answer[0]
return None
@@ -102,9 +115,7 @@ def get_tg_username_from_db_by_teleg_id(teleg_id: int) -> int:
def get_tg_username_from_db_by_base_id(base_id: int) -> int:
"""Получает телеграм-юзернейм по id в базе"""
- answer = db_session.query(Username.username).filter(
- Username.id == base_id
- ).first()
+ answer = db_session.query(Username.username).filter(Username.id == base_id).first()
if answer:
return answer[0]
return None
@@ -116,4 +127,4 @@ async def send_message_to_admins(message):
try:
await bot.send_message(i, message)
except Exception as error:
- logger.error(f'Сообщение {message} не ушло админу {i}. {error}')
+ logger.error(f"Сообщение {message} не ушло админу {i}. {error}")
diff --git a/data/__init__.py b/data/__init__.py
index d085c3a..27c9ec6 100644
--- a/data/__init__.py
+++ b/data/__init__.py
@@ -1 +1 @@
-from .config import *
\ No newline at end of file
+from .config import *
diff --git a/data/config.py b/data/config.py
index b477eeb..9376600 100644
--- a/data/config.py
+++ b/data/config.py
@@ -4,7 +4,8 @@
load_dotenv()
-TOKEN = os.getenv('TG_TOKEN')
-ADMIN_TG_ID = os.getenv('ADMIN_TG_ID')
-DEFAULT_PARE_iD = os.getenv('DEFAULT_PARE_iD')
-DEFAULT_PARE_USERNAME = os.getenv('DEFAULT_PARE_USERNAME')
+TOKEN = os.getenv("TG_TOKEN")
+DB_DSN = os.getenv("DB_DSN")
+ADMIN_TG_ID = os.getenv("ADMIN_TG_ID")
+DEFAULT_PARE_iD = os.getenv("DEFAULT_PARE_iD")
+DEFAULT_PARE_USERNAME = os.getenv("DEFAULT_PARE_USERNAME")
diff --git a/docker-compose.yml b/docker-compose.yml
index 481d5a2..2722b68 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,11 +1,29 @@
-version: '3.0'
+version: '3.8'
services:
- bot:
- image: tbf
- working_dir: /bot
+ telegram-bot:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ container_name: random-coffee-bot
+ env_file:
+ - .env
+ environment:
+ - DB_DSN=postgresql://bot_user:bot_password@db:5432/bot_database
+ depends_on:
+ - db
+ restart: always
+
+ db:
+ image: postgres:latest
+ container_name: postgres-random-coffee
+ environment:
+ POSTGRES_USER: bot_user
+ POSTGRES_PASSWORD: bot_password
+ POSTGRES_DB: bot_database
volumes:
- - tbf:/bot/db
- - tbf:/bot/logs
+ - postgres_data:/var/lib/postgresql/data
restart: always
- env_file: tbf/.env
+
+volumes:
+ postgres_data:
\ No newline at end of file
diff --git a/handlers/admin/admin_report.py b/handlers/admin/admin_report.py
index 428a4b1..1fcecd0 100644
--- a/handlers/admin/admin_report.py
+++ b/handlers/admin/admin_report.py
@@ -1,30 +1,51 @@
from sqlalchemy import text
+
from controllerBD.db_loader import db_session
from handlers.user.work_with_date import date_from_db_to_message
def prepare_user_info():
"""Формируем список пользователей со штрафными баллами и другой инф."""
- query = text("""SELECT mr.about_whom_id, ui.teleg_id, ui.name, un.username,
+ query = text(
+ """SELECT
+ mr.about_whom_id,
+ ui.teleg_id,
+ ui.name,
+ un.username,
COUNT(
- CASE
- WHEN mr.grade=0 THEN 1
- ELSE NULL
- END
- ) as cnt_fail,
- MAX(mr.date_of_comment) as last_comment, mr.comment,
+ CASE
+ WHEN mr.grade = 0 THEN 1
+ ELSE NULL
+ END
+ ) AS cnt_fail,
+ MAX(mr.date_of_comment) AS last_comment,
+ mr.comment,
bl.ban_status
- FROM mets_reviews as mr
- LEFT JOIN user_info as ui
+ FROM mets_reviews AS mr
+ LEFT JOIN user_info AS ui
ON mr.about_whom_id = ui.id
- LEFT JOIN tg_usernames as un
+ LEFT JOIN tg_usernames AS un
ON mr.about_whom_id = un.id
- LEFT JOIN (SELECT *, MAX(id) FROM ban_list GROUP BY banned_user_id)
- as bl
+ LEFT JOIN (
+ SELECT
+ banned_user_id,
+ MAX(id) AS max_id,
+ ban_status
+ FROM ban_list
+ GROUP BY banned_user_id, ban_status
+ ) AS bl
ON mr.about_whom_id = bl.banned_user_id
- WHERE mr.grade = 0
- GROUP BY mr.about_whom_id
- ORDER BY mr.date_of_comment""")
+ WHERE mr.grade = 0
+ GROUP BY
+ mr.about_whom_id,
+ ui.teleg_id,
+ ui.name,
+ un.username,
+ mr.comment,
+ bl.ban_status
+ ORDER BY MAX(mr.date_of_comment) DESC;
+ """
+ )
users = db_session.execute(query)
return users
@@ -38,27 +59,29 @@ def prepare_report_message(users):
if not user[3]:
username = ""
else:
- username = f' (@{user[3]})'
+ username = f" (@{user[3]})"
if user[7] == 0 or user[7] is None:
status = "Не забанен"
else:
status = "Забанен"
- if user[6] == 'null':
+ if user[6] == "null":
comment = "Комментарий не был добавлен."
else:
comment = f"{user[6]}"
date = date_from_db_to_message(user[5])
- user_message = f'ID пользователя: {user[0]};\n' \
- f'Ник пользователя: ' \
- f'{user[2]}{username}.\n' \
- f'Статус: {status}.\n' \
- f'Штрафных балов - {user[4]}\n' \
- f'{date} Последний комментарий: {comment}'
- if len(message + '\n\n' + user_message) > 4095:
+ user_message = (
+ f"ID пользователя: {user[0]};\n"
+ f'Ник пользователя: '
+ f"{user[2]}{username}.\n"
+ f"Статус: {status}.\n"
+ f"Штрафных балов - {user[4]}\n"
+ f"{date} Последний комментарий: {comment}"
+ )
+ if len(message + "\n\n" + user_message) > 4095:
message_list.append(message)
message = user_message
else:
- message = message + '\n\n' + user_message
+ message = message + "\n\n" + user_message
message_list.append(message)
return message_list
diff --git a/handlers/admin/ban_handlers.py b/handlers/admin/ban_handlers.py
index 8420400..c783894 100644
--- a/handlers/admin/ban_handlers.py
+++ b/handlers/admin/ban_handlers.py
@@ -1,20 +1,24 @@
import datetime
-from aiogram import types, Dispatcher
+from aiogram import Dispatcher, types
from aiogram.dispatcher import FSMContext
from controllerBD.db_loader import db_session
from controllerBD.models import BanList, Holidays, UserStatus
from handlers.admin.handlers import admin_menu
-from handlers.admin.validators import ban_validator, comment_validator, \
- unban_validator
+from handlers.admin.validators import ban_validator, comment_validator, unban_validator
from handlers.decorators import admin_handlers
-from keyboards.admin import cancel, ban_list, admin_ban_markup, \
- add_to_ban_list, admin_cancel_markup, remove_from_ban_list, \
- back_to_main_markup
+from keyboards.admin import (
+ add_to_ban_list,
+ admin_ban_markup,
+ admin_cancel_markup,
+ back_to_main_markup,
+ ban_list,
+ cancel,
+ remove_from_ban_list,
+)
from keyboards.user import back_to_main
from loader import bot, logger
-
from states import AdminData
@@ -30,9 +34,7 @@ async def cancel_message(message: types.Message, state: FSMContext):
async def ban_list_message(message: types.Message):
"""Вывод сообщения с выбором действия по бану."""
await bot.send_message(
- message.from_user.id,
- "Что вы хотите сделать?",
- reply_markup=admin_ban_markup()
+ message.from_user.id, "Что вы хотите сделать?", reply_markup=admin_ban_markup()
)
@@ -44,7 +46,7 @@ async def ban_list_add(message: types.Message):
await bot.send_message(
message.from_user.id,
"Введите id пользователя, которого необходимо забанить:",
- reply_markup=admin_cancel_markup()
+ reply_markup=admin_cancel_markup(),
)
await AdminData.user_ban.set()
@@ -52,8 +54,7 @@ async def ban_list_add(message: types.Message):
# @dp.message_handler(state=AdminData.user_ban)
async def ban_list_add_answer(message: types.Message, state: FSMContext):
"""Получение ответа от админа и проверка введенного id."""
- logger.info(f"Для добавления в бан введен пользователь "
- f"с if {message.text}.")
+ logger.info(f"Для добавления в бан введен пользователь " f"с if {message.text}.")
user_id = message.text
if not await ban_validator(message):
return
@@ -81,27 +82,32 @@ async def comment_to_ban_answer(message: types.Message, state: FSMContext):
if not await comment_validator(comment):
await bot.send_message(
message.from_user.id,
- "Комментарий должен быть не менее 10 и не более 500 символов"
+ "Комментарий должен быть не менее 10 и не более 500 символов",
)
return
data = await state.get_data()
- banned_user_id = data.get('banned_user_id')
+ banned_user_id = data.get("banned_user_id")
await save_to_ban(banned_user_id, comment)
- await bot.send_message(message.from_user.id,
- "Пользователь добавлен в бан-лист")
+ await bot.send_message(message.from_user.id, "Пользователь добавлен в бан-лист")
await back_to_main_message(message, state)
async def save_to_ban(banned_user_id, comment):
"""Запись в БД пользователя с баном."""
- db_session.add(BanList(banned_user_id=banned_user_id,
- ban_status=1,
- comment_to_ban=comment,
- date_of_ban=str(datetime.date.today())))
- db_session.query(Holidays).filter(Holidays.id == banned_user_id). \
- update({'status': 0, 'till_date': 'null'})
- db_session.query(UserStatus).filter(UserStatus.id == banned_user_id). \
- update({'status': 0})
+ db_session.add(
+ BanList(
+ banned_user_id=banned_user_id,
+ ban_status=1,
+ comment_to_ban=comment,
+ date_of_ban=str(datetime.date.today()),
+ )
+ )
+ db_session.query(Holidays).filter(Holidays.id == banned_user_id).update(
+ {"status": 0, "till_date": "null"}
+ )
+ db_session.query(UserStatus).filter(UserStatus.id == banned_user_id).update(
+ {"status": 0}
+ )
db_session.commit()
@@ -113,7 +119,7 @@ async def ban_list_remove(message: types.Message):
await bot.send_message(
message.from_user.id,
"Введите id пользователя, которого необходимо убрать из бан листа:",
- reply_markup=admin_cancel_markup()
+ reply_markup=admin_cancel_markup(),
)
await AdminData.user_unban.set()
@@ -121,8 +127,7 @@ async def ban_list_remove(message: types.Message):
# @dp.message_handler(state=AdminData.user_unban)
async def ban_list_remove_answer(message: types.Message, state: FSMContext):
"""Получение ответа с id пользователем для вывода из бана. Валидация."""
- logger.info(f"Для вывода из бана введен пользователь "
- f"с if {message.text}.")
+ logger.info(f"Для вывода из бана введен пользователь " f"с if {message.text}.")
user_id = message.text
if not await unban_validator(message):
return
@@ -149,26 +154,28 @@ async def comment_to_unban_answer(message: types.Message, state: FSMContext):
if not await comment_validator(comment):
await bot.send_message(
message.from_user.id,
- "Комментарий должен быть не менее 10 и не более 500 символов"
+ "Комментарий должен быть не менее 10 и не более 500 символов",
)
return
data = await state.get_data()
- unbanned_user_id = data.get('unbanned_user_id')
+ unbanned_user_id = data.get("unbanned_user_id")
await save_to_unban(unbanned_user_id, comment)
- await bot.send_message(message.from_user.id,
- "Пользователь исключен из бан-листа")
+ await bot.send_message(message.from_user.id, "Пользователь исключен из бан-листа")
await back_to_main_message(message, state)
async def save_to_unban(unbanned_user_id, comment):
"""Сохранение в БД, что пользователь выведен из бана."""
- db_session.query(BanList).filter(
- BanList.banned_user_id == unbanned_user_id
- ).update({'ban_status': 0,
- 'date_of_unban': datetime.date.today(),
- 'comment_to_unban': comment})
- db_session.query(UserStatus).filter(UserStatus.id == unbanned_user_id). \
- update({'status': 1})
+ db_session.query(BanList).filter(BanList.banned_user_id == unbanned_user_id).update(
+ {
+ "ban_status": 0,
+ "date_of_unban": datetime.date.today(),
+ "comment_to_unban": comment,
+ }
+ )
+ db_session.query(UserStatus).filter(UserStatus.id == unbanned_user_id).update(
+ {"status": 1}
+ )
db_session.commit()
@@ -179,7 +186,7 @@ async def back_to_main_message(message: types.Message, state: FSMContext):
await bot.send_message(
message.from_user.id,
"Вы в главном меню",
- reply_markup=back_to_main_markup(message)
+ reply_markup=back_to_main_markup(message),
)
@@ -188,12 +195,10 @@ def register_admin_ban_handlers(dp: Dispatcher):
dp.register_message_handler(ban_list_message, text=ban_list)
dp.register_message_handler(ban_list_add, text=add_to_ban_list)
dp.register_message_handler(ban_list_add_answer, state=AdminData.user_ban)
- dp.register_message_handler(comment_to_ban_answer,
- state=AdminData.comment_to_ban)
+ dp.register_message_handler(comment_to_ban_answer, state=AdminData.comment_to_ban)
dp.register_message_handler(ban_list_remove, text=remove_from_ban_list)
- dp.register_message_handler(ban_list_remove_answer,
- state=AdminData.user_unban)
- dp.register_message_handler(comment_to_unban_answer,
- state=AdminData.comment_to_unban)
- dp.register_message_handler(back_to_main_message,
- text=back_to_main, state="*")
+ dp.register_message_handler(ban_list_remove_answer, state=AdminData.user_unban)
+ dp.register_message_handler(
+ comment_to_unban_answer, state=AdminData.comment_to_unban
+ )
+ dp.register_message_handler(back_to_main_message, text=back_to_main, state="*")
diff --git a/handlers/admin/handlers.py b/handlers/admin/handlers.py
index ec7d1e8..ce77ea4 100644
--- a/handlers/admin/handlers.py
+++ b/handlers/admin/handlers.py
@@ -3,24 +3,39 @@
from aiogram import Dispatcher, types
from aiogram.dispatcher import FSMContext
from aiogram.utils.exceptions import BotBlocked
+
from controllerBD.db_loader import db_session
from controllerBD.models import UserStatus
from controllerBD.services import get_user_count_from_db
-from keyboards.admin import (admin_cancel_markup, admin_change_status_markup,
- admin_inform_markup, admin_menu_button,
- admin_menu_markup, algo_start, cancel,
- change_status, do_not_take_part_button, go_back,
- inform, inform_active_users, inform_bad_users,
- send_message_to_all_button, take_part_button)
-from loader import bot, logger
-from match_algoritm.MatchingHelper import start_algoritm
-from states import AdminData
-
-from handlers.admin.admin_report import (prepare_report_message,
- prepare_user_info)
+from handlers.admin.admin_report import prepare_report_message, prepare_user_info
from handlers.decorators import admin_handlers
from handlers.user.check_message import prepare_user_list, send_message
from handlers.user.get_info_from_table import get_id_from_user_info_table
+from keyboards.admin import (
+ admin_cancel_markup,
+ admin_change_status_markup,
+ admin_inform_markup,
+ admin_menu_button,
+ admin_menu_markup,
+ admin_pair_generation_markup,
+ cancel,
+ change_pair_generation_date,
+ change_status,
+ do_not_take_part_button,
+ force_pair_generation,
+ go_back,
+ inform,
+ inform_active_users,
+ inform_bad_users,
+ pair_generation,
+ renew_pair_generation,
+ send_message_to_all_button,
+ stop_pair_generation,
+ take_part_button,
+)
+from loader import bot, logger
+from match_algoritm.MatchingHelper import start_algoritm
+from states import AdminData
@admin_handlers
@@ -35,7 +50,7 @@ async def admin_menu(message: types.Message):
await bot.send_message(
message.from_user.id,
text="Выберите из доступных вариантов:",
- reply_markup=admin_menu_markup()
+ reply_markup=admin_menu_markup(),
)
@@ -45,7 +60,7 @@ async def inform_message(message: types.Message):
await bot.send_message(
message.from_user.id,
"Выберите из доступных вариантов:",
- reply_markup=admin_inform_markup()
+ reply_markup=admin_inform_markup(),
)
@@ -56,7 +71,7 @@ async def inform_message_1(message: types.Message):
await bot.send_message(
message.from_user.id,
f"Всего пользователей - {users['all_users']};\n\n"
- f"Активных пользователей - {users['active_users']}."
+ f"Активных пользователей - {users['active_users']}.",
)
@@ -65,16 +80,12 @@ async def inform_message_2(message: types.Message):
"""Получение сообщения о пользователях со штрафными балами."""
bad_users = prepare_user_info()
message_list = prepare_report_message(bad_users)
- if message_list[0] == '':
- await bot.send_message(
- message.from_user.id,
- "Отчёт пустой")
+ if message_list[0] == "":
+ await bot.send_message(message.from_user.id, "Отчёт пустой")
else:
for message_text in message_list:
await bot.send_message(
- message.from_user.id,
- f"{message_text}",
- parse_mode="HTML"
+ message.from_user.id, f"{message_text}", parse_mode="HTML"
)
@@ -84,7 +95,58 @@ async def change_status_message(message: types.Message):
await bot.send_message(
message.from_user.id,
"Выберите вариант:",
- reply_markup=admin_change_status_markup()
+ reply_markup=admin_change_status_markup(),
+ )
+
+
+@admin_handlers
+async def pairs_config_message(message: types.Message):
+ """Вывод кнопок настроек генерации пар."""
+ await bot.send_message(
+ message.from_user.id,
+ "Выберите вариант:",
+ reply_markup=admin_pair_generation_markup(),
+ )
+
+
+@admin_handlers
+async def stop_generation(message: types.Message):
+ """Остановить генерацию пар."""
+ await bot.send_message(
+ message.from_user.id,
+ "Данный функционал пока что не реализован :)",
+ reply_markup=admin_pair_generation_markup(),
+ )
+
+
+@admin_handlers
+async def renew_generation(message: types.Message):
+ """Возобновить генерацию пар."""
+ await bot.send_message(
+ message.from_user.id,
+ "Данный функционал пока что не реализован :)",
+ reply_markup=admin_pair_generation_markup(),
+ )
+
+
+@admin_handlers
+async def change_generation_date(message: types.Message):
+ """Изменить день недели и время генерации пар."""
+ await bot.send_message(
+ message.from_user.id,
+ "Данный функционал пока что не реализован :)",
+ reply_markup=admin_pair_generation_markup(),
+ )
+
+
+@admin_handlers
+async def generate_pairs(message: types.Message):
+ """Сгенерировать пары вручную."""
+ await start_algoritm()
+ await bot.send_message(
+ message.from_user.id,
+ "Пары успешно сгенерированы!",
+ reply_markup=admin_pair_generation_markup(),
)
@@ -93,8 +155,7 @@ async def take_part_yes(message: types.Message):
"""Изменение статуса на принимать участие."""
change_admin_status(message, 1)
await bot.send_message(
- message.from_user.id,
- "Теперь вы участвуете в распределении."
+ message.from_user.id, "Теперь вы участвуете в распределении."
)
@@ -104,20 +165,15 @@ async def take_part_no(message: types.Message):
change_admin_status(message, 0)
await bot.send_message(
message.from_user.id,
- "Вы изменили статус и теперь не участвуете в распределении."
+ "Вы изменили статус и теперь не участвуете в распределении.",
)
-@admin_handlers
-async def handler_start_algoritm(message: types.Message):
- """Ручной запуск алгоритма."""
- await start_algoritm()
-
-
def change_admin_status(message: types.Message, status):
user_id = get_id_from_user_info_table(message.from_user.id)
- db_session.query(UserStatus).filter(UserStatus.id == user_id). \
- update({'status': status})
+ db_session.query(UserStatus).filter(UserStatus.id == user_id).update(
+ {"status": status}
+ )
@admin_handlers
@@ -125,7 +181,7 @@ async def request_message_to_all(message: types.Message):
await bot.send_message(
message.from_user.id,
"Введите сообщение которое будет отправлено всем пользователям",
- reply_markup=admin_cancel_markup()
+ reply_markup=admin_cancel_markup(),
)
await AdminData.message_send.set()
@@ -139,9 +195,7 @@ async def get_message_and_send(message: types.Message, state=FSMContext):
try:
for user in user_list:
await send_photo(
- teleg_id=user,
- photo=message_answer,
- caption=message_caption
+ teleg_id=user, photo=message_answer, caption=message_caption
)
await sleep(0.05)
except TypeError:
@@ -152,9 +206,9 @@ async def get_message_and_send(message: types.Message, state=FSMContext):
await bot.send_message(
message.from_user.id,
"Сообщения отправлены",
- reply_markup=admin_menu_markup()
+ reply_markup=admin_menu_markup(),
)
- elif message.content_type == 'text':
+ elif message.content_type == "text":
message_answer = message.text
if message_answer == cancel:
await admin_menu(message)
@@ -174,12 +228,14 @@ async def get_message_and_send(message: types.Message, state=FSMContext):
await bot.send_message(
message.from_user.id,
"Сообщения отправлены",
- reply_markup=admin_menu_markup()
+ reply_markup=admin_menu_markup(),
)
logger.info("Сообщения пользователям доставлены.")
else:
- await message.answer("Данный тип сообщения я обработать не могу",
- reply_markup=admin_menu_markup())
+ await message.answer(
+ "Данный тип сообщения я обработать не могу",
+ reply_markup=admin_menu_markup(),
+ )
await state.finish()
@@ -188,12 +244,15 @@ async def send_photo(teleg_id, **kwargs):
try:
await bot.send_photo(teleg_id, **kwargs)
except BotBlocked:
- logger.error(f"Невозможно доставить сообщение пользователю {teleg_id}."
- f"Бот заблокирован.")
+ logger.error(
+ f"Невозможно доставить сообщение пользователю {teleg_id}."
+ f"Бот заблокирован."
+ )
await change_status(teleg_id)
except Exception as error:
- logger.error(f"Невозможно доставить сообщение пользователю {teleg_id}."
- f"{error}")
+ logger.error(
+ f"Невозможно доставить сообщение пользователю {teleg_id}." f"{error}"
+ )
def register_admin_handlers(dp: Dispatcher):
@@ -203,11 +262,18 @@ def register_admin_handlers(dp: Dispatcher):
dp.register_message_handler(inform_message_1, text=inform_active_users)
dp.register_message_handler(inform_message_2, text=inform_bad_users)
dp.register_message_handler(change_status_message, text=change_status)
+ dp.register_message_handler(pairs_config_message, text=pair_generation)
dp.register_message_handler(take_part_yes, text=take_part_button)
dp.register_message_handler(take_part_no, text=do_not_take_part_button)
- dp.register_message_handler(handler_start_algoritm, text=algo_start)
- dp.register_message_handler(request_message_to_all,
- text=send_message_to_all_button)
- dp.register_message_handler(get_message_and_send,
- state=AdminData.message_send,
- content_types=types.ContentTypes.ANY)
+ dp.register_message_handler(generate_pairs, text=force_pair_generation)
+ dp.register_message_handler(stop_generation, text=stop_pair_generation)
+ dp.register_message_handler(renew_generation, text=renew_pair_generation)
+ dp.register_message_handler(
+ change_generation_date, text=change_pair_generation_date
+ )
+ dp.register_message_handler(request_message_to_all, text=send_message_to_all_button)
+ dp.register_message_handler(
+ get_message_and_send,
+ state=AdminData.message_send,
+ content_types=types.ContentTypes.ANY,
+ )
diff --git a/handlers/admin/validators/__init__.py b/handlers/admin/validators/__init__.py
index ef5f67c..7946f53 100644
--- a/handlers/admin/validators/__init__.py
+++ b/handlers/admin/validators/__init__.py
@@ -1 +1 @@
-from .validators import *
\ No newline at end of file
+from .validators import *
diff --git a/handlers/admin/validators/validators.py b/handlers/admin/validators/validators.py
index 9507ade..e7e8890 100644
--- a/handlers/admin/validators/validators.py
+++ b/handlers/admin/validators/validators.py
@@ -23,20 +23,13 @@ async def ban_validator(message: types.Message):
if not await check_id_in_ban_with_status(message.text, 1):
logger.info("Валидация пройдена.")
return True
- await bot.send_message(
- message.from_user.id,
- "Пользователь уже забаннен."
- )
+ await bot.send_message(message.from_user.id, "Пользователь уже забаннен.")
return False
await bot.send_message(
- message.from_user.id,
- "Пользователя с таким id не существует."
+ message.from_user.id, "Пользователя с таким id не существует."
)
return False
- await bot.send_message(
- message.from_user.id,
- "Неверный ввод, введите число."
- )
+ await bot.send_message(message.from_user.id, "Неверный ввод, введите число.")
return False
@@ -47,20 +40,13 @@ async def unban_validator(message: types.Message):
if await check_id_in_ban_with_status(message.text, 1):
logger.info("Валидация пройдена.")
return True
- await bot.send_message(
- message.from_user.id,
- "Пользователь не забаннен."
- )
+ await bot.send_message(message.from_user.id, "Пользователь не забаннен.")
return False
await bot.send_message(
- message.from_user.id,
- "Пользователя с таким id не существует."
+ message.from_user.id, "Пользователя с таким id не существует."
)
return False
- await bot.send_message(
- message.from_user.id,
- "Неверный ввод, введите число."
- )
+ await bot.send_message(message.from_user.id, "Неверный ввод, введите число.")
return False
diff --git a/handlers/decorators.py b/handlers/decorators.py
index cdac136..9dfd65b 100644
--- a/handlers/decorators.py
+++ b/handlers/decorators.py
@@ -13,6 +13,7 @@ async def wrapped(*args):
message = list(args)[0]
if message.from_user.id in list(map(int, ADMIN_TG_ID.split())):
return await func(*args)
+
return wrapped
@@ -25,9 +26,8 @@ async def wrapped(*args):
else:
await bot.send_message(
message.from_user.id,
- ("Ты заблокирован. Пожалуйста, "
- "обратись к администратору."),
- reply_markup=ReplyKeyboardRemove()
+ ("Ты заблокирован. Пожалуйста, " "обратись к администратору."),
+ reply_markup=ReplyKeyboardRemove(),
)
else:
await bot.send_message(
@@ -35,6 +35,7 @@ async def wrapped(*args):
"Ты не зарегистрирован. Введи 'Регистрация' без кавычек "
"или нажми кнопку снизу.",
reply_markup=start_registr_markup(),
- )
+ )
await UserData.start.set()
+
return wrapped
diff --git a/handlers/user/add_username.py b/handlers/user/add_username.py
index 9d78cae..c465903 100644
--- a/handlers/user/add_username.py
+++ b/handlers/user/add_username.py
@@ -1,5 +1,5 @@
from aiogram import types
-from sqlalchemy import exists, and_
+from sqlalchemy import and_, exists
from controllerBD.db_loader import db_session
from controllerBD.models import Username
@@ -10,19 +10,17 @@
async def check_username(message: types.Message):
user_id = get_id_from_user_info_table(message.from_user.id)
username = message.from_user.username
- is_exist = db_session.query(exists().where(
- Username.id == user_id
- )).scalar()
+ is_exist = db_session.query(exists().where(Username.id == user_id)).scalar()
if not is_exist:
db_session.add(Username(id=user_id, username=username))
db_session.commit()
logger.info(f"В базу добавлен Username пользователя {user_id}")
- elif not db_session.query(exists().where(and_(
- Username.id == user_id,
- Username.username == username))
+ elif not db_session.query(
+ exists().where(and_(Username.id == user_id, Username.username == username))
).scalar():
- db_session.query(Username).filter(Username.id == user_id). \
- update({'username': username})
+ db_session.query(Username).filter(Username.id == user_id).update(
+ {"username": username}
+ )
db_session.commit()
logger.info(f"Обновлен Username пользователя {user_id}")
else:
diff --git a/handlers/user/ban_check.py b/handlers/user/ban_check.py
index 76e6cb0..c5e3701 100644
--- a/handlers/user/ban_check.py
+++ b/handlers/user/ban_check.py
@@ -1,5 +1,5 @@
from aiogram import types
-from sqlalchemy import exists, and_
+from sqlalchemy import and_, exists
from controllerBD.db_loader import db_session
from controllerBD.models import BanList
@@ -16,9 +16,11 @@ async def check_user_in_ban(message: types.Message):
async def check_id_in_ban_with_status(user_id, status):
"""Проверяем пользователя на наличие в бане с определенным статусом."""
- is_exist = db_session.query(exists().where(
- and_(BanList.banned_user_id == user_id, BanList.ban_status == status)
- )).scalar()
+ is_exist = db_session.query(
+ exists().where(
+ and_(BanList.banned_user_id == user_id, BanList.ban_status == status)
+ )
+ ).scalar()
if not is_exist:
return False
return True
diff --git a/handlers/user/check_message.py b/handlers/user/check_message.py
index 3e79477..c346a34 100644
--- a/handlers/user/check_message.py
+++ b/handlers/user/check_message.py
@@ -23,9 +23,12 @@ async def check_message():
def prepare_user_list():
"""Подготовка списка id пользователей со статусом готов к встрече."""
logger.info("""Подготавливаем список пользователей из базы""")
- data = db_session.query(Users.teleg_id).join(UserStatus).filter(
- UserStatus.status == 1
- ).all()
+ data = (
+ db_session.query(Users.teleg_id)
+ .join(UserStatus)
+ .filter(UserStatus.status == 1)
+ .all()
+ )
return [element[0] for element in data]
@@ -34,16 +37,18 @@ async def send_message(teleg_id, **kwargs):
try:
await bot.send_message(teleg_id, **kwargs)
except BotBlocked:
- logger.error(f"Невозможно доставить сообщение пользователю {teleg_id}."
- f"Бот заблокирован.")
+ logger.error(
+ f"Невозможно доставить сообщение пользователю {teleg_id}."
+ f"Бот заблокирован."
+ )
await change_status(teleg_id)
except Exception as error:
- logger.error(f"Невозможно доставить сообщение пользователю {teleg_id}."
- f"{error}")
+ logger.error(
+ f"Невозможно доставить сообщение пользователю {teleg_id}." f"{error}"
+ )
async def change_status(teleg_id):
"""Смена статуса участия."""
user_id = get_id_from_user_info_table(teleg_id)
- db_session.query(UserStatus).filter(UserStatus.id == user_id). \
- update({'status': 0})
+ db_session.query(UserStatus).filter(UserStatus.id == user_id).update({"status": 0})
diff --git a/handlers/user/first_check.py b/handlers/user/first_check.py
index 599c842..f6259b5 100644
--- a/handlers/user/first_check.py
+++ b/handlers/user/first_check.py
@@ -2,12 +2,12 @@
from aiogram.types import ReplyKeyboardRemove
from data import ADMIN_TG_ID
-from handlers.user.get_info_from_table import check_user_in_base
from handlers.user.ban_check import check_user_in_ban
+from handlers.user.get_info_from_table import check_user_in_base
from keyboards.admin import admin_main_markup
-from keyboards.user import start_registr_markup, menu_markup
+from keyboards.user import menu_markup, start_registr_markup
from loader import bot
-from states import UserData, BannedState
+from states import BannedState, UserData
async def check_and_add_registration_button(message: types.Message):
@@ -15,17 +15,18 @@ async def check_and_add_registration_button(message: types.Message):
if not await check_user_in_base(message):
await bot.send_message(
message.from_user.id,
- text=("Добро пожаловать в бот!\n\n"
- "Для подбора пары нужно пройти небольшую регистрацию: "
- "представиться и ответить на пару вопросов о себе, "
- "чтобы собеседнику было проще начать с тобой разговор. Если"
- " отвечать не хочется, то часть шагов можно пропустить.\n\n"
- "Нажми кнопку \"Регистрация\" ниже.\n\n"
- "Для общения, помощи и рассказов о том, как прошла "
- "встреча присоединяйся к уютному сообществу бота в "
- "телеграм https://t.me/+Ai1RweqsyjFhNmFi"
- ),
- reply_markup=start_registr_markup()
+ text=(
+ "Добро пожаловать в бот!\n\n"
+ "Для подбора пары нужно пройти небольшую регистрацию: "
+ "представиться и ответить на пару вопросов о себе, "
+ "чтобы собеседнику было проще начать с тобой разговор. Если"
+ " отвечать не хочется, то часть шагов можно пропустить.\n\n"
+ 'Нажми кнопку "Регистрация" ниже.\n\n'
+ "Для общения, помощи и рассказов о том, как прошла "
+ "встреча присоединяйся к нашему IT сообществу в "
+ "телеграм https://t.me/ViribusUnitisGroup"
+ ),
+ reply_markup=start_registr_markup(),
)
await UserData.start.set()
elif message.from_user.id in list(map(int, ADMIN_TG_ID.split())):
@@ -45,8 +46,8 @@ async def check_and_add_registration_button(message: types.Message):
await bot.send_message(
message.from_user.id,
text="К сожалению ты нарушил наши правила и попал в бан. "
- "Для решения данного вопроса обратись к "
- "администратору @Loravel",
- reply_markup=ReplyKeyboardRemove()
+ "Для решения данного вопроса обратись к "
+ "администратору @Loravel",
+ reply_markup=ReplyKeyboardRemove(),
)
await BannedState.start.set()
diff --git a/handlers/user/get_info_from_table.py b/handlers/user/get_info_from_table.py
index 1dd60c1..a40da15 100644
--- a/handlers/user/get_info_from_table.py
+++ b/handlers/user/get_info_from_table.py
@@ -1,15 +1,13 @@
from sqlalchemy import exists
from controllerBD.db_loader import db_session
-from controllerBD.models import Gender, Users, Holidays, UserStatus
+from controllerBD.models import Gender, Holidays, Users, UserStatus
from loader import logger
def get_id_from_user_info_table(teleg_id):
"""Получение id пользователя по телеграм id."""
- id_obj = db_session.query(Users.id).filter(
- Users.teleg_id == teleg_id
- ).first()
+ id_obj = db_session.query(Users.id).filter(Users.teleg_id == teleg_id).first()
return id_obj[0]
@@ -21,9 +19,9 @@ def get_teleg_id_from_user_info_table(id):
async def check_user_in_base(message):
"""Проверяем пользователя на наличие в БД."""
- is_exist = db_session.query(exists().where(
- Users.teleg_id == message.from_user.id
- )).scalar()
+ is_exist = db_session.query(
+ exists().where(Users.teleg_id == message.from_user.id)
+ ).scalar()
if not is_exist:
return False
return True
@@ -37,17 +35,13 @@ def get_user_data_from_db(teleg_id):
def get_user_status_from_db(user_id):
"""Получение статуса участия пользователя из БД"""
- user_status = db_session.query(UserStatus).filter(
- UserStatus.id == user_id
- ).first()
+ user_status = db_session.query(UserStatus).filter(UserStatus.id == user_id).first()
return user_status.__dict__
def get_holidays_status_from_db(user_id):
"""Получение статуса каникул пользователя из БД"""
- holidays = db_session.query(Holidays).filter(
- Holidays.id == user_id
- ).first()
+ holidays = db_session.query(Holidays).filter(Holidays.id == user_id).first()
return holidays.__dict__
@@ -59,14 +53,19 @@ def get_user_info_by_id(user_id):
def get_full_user_info_by_id(user_id):
try:
- result = db_session.query(
- Users.id,
- Users.teleg_id,
- Users.name,
- Users.birthday,
- Users.about,
- Gender.gender_name
- ).join(Gender).filter(Users.id == user_id).first()
+ result = (
+ db_session.query(
+ Users.id,
+ Users.teleg_id,
+ Users.name,
+ Users.birthday,
+ Users.about,
+ Gender.gender_name,
+ )
+ .join(Gender)
+ .filter(Users.id == user_id)
+ .first()
+ )
except Exception as error:
logger.error(f"{error}")
result = None
diff --git a/handlers/user/handlers.py b/handlers/user/handlers.py
index 9118c3e..6e8adc5 100644
--- a/handlers/user/handlers.py
+++ b/handlers/user/handlers.py
@@ -1,31 +1,29 @@
-from aiogram import types, exceptions
+from aiogram import exceptions, types
from aiogram.dispatcher import Dispatcher
from controllerBD.db_loader import db_session
from controllerBD.models import MetInfo
from handlers.decorators import user_handlers
from handlers.user.add_username import check_username
-from handlers.user.reviews import get_met_id_with_user_last_week
from handlers.user.get_info_from_table import (
+ get_full_user_info_by_id,
+ get_holidays_status_from_db,
+ get_id_from_user_info_table,
get_user_data_from_db,
get_user_status_from_db,
- get_holidays_status_from_db,
- get_id_from_user_info_table, get_full_user_info_by_id
)
+from handlers.user.new_member import get_gender_from_db, start_registration
+from handlers.user.reviews import get_met_id_with_user_last_week
from handlers.user.work_with_date import date_from_db_to_message
from keyboards.user import *
from loader import bot, logger
-
-from handlers.user.new_member import get_gender_from_db, start_registration
from sendler import make_message
# @dp.errors_handler(exception=exceptions.RetryAfter)
-async def exception_handler(update: types.Update,
- exception: exceptions.RetryAfter):
- await update.message.answer('Превышен лимит на данный запрос. '
- 'Подожди 5 минут')
- logger.info(f'Пользователь {update.message.from_user.id} флудит')
+async def exception_handler(update: types.Update, exception: exceptions.RetryAfter):
+ await update.message.answer("Превышен лимит на данный запрос. " "Подожди 5 минут")
+ logger.info(f"Пользователь {update.message.from_user.id} флудит")
return True
@@ -34,9 +32,7 @@ async def exception_handler(update: types.Update,
async def main_menu(message: types.Message):
"""Вывод меню"""
await bot.send_message(
- message.from_user.id,
- text="Меню:",
- reply_markup=menu_markup(message)
+ message.from_user.id, text="Меню:", reply_markup=menu_markup(message)
)
@@ -45,21 +41,22 @@ async def main_menu(message: types.Message):
async def send_profile(message: types.Message):
"""Вывод данных о пользователе"""
await check_username(message)
- logger.info(f"Пользователь с TG_ID {message.from_user.id} "
- f"запросил информацию о себе")
+ logger.info(
+ f"Пользователь с TG_ID {message.from_user.id} " f"запросил информацию о себе"
+ )
data = dict(get_user_data_from_db(message.from_user.id))
- gender_id = data['gender']
+ gender_id = data["gender"]
gender_status = get_gender_from_db(gender_id)
- data['gender'] = gender_status
- if data['birthday'] != 'Не указано':
- data['birthday'] = date_from_db_to_message(data['birthday'])
+ data["gender"] = gender_status
+ if data["birthday"] != "Не указано":
+ data["birthday"] = date_from_db_to_message(data["birthday"])
await bot.send_message(
message.from_user.id,
f"Имя: {data['name']}\n"
f"Дата рождения: {data['birthday']}\n"
f"О себе: {data['about']}\n"
f"Пол: {data['gender']}",
- reply_markup=edit_profile_markup()
+ reply_markup=edit_profile_markup(),
)
@@ -67,16 +64,19 @@ async def send_profile(message: types.Message):
@user_handlers
async def edit_profile(message: types.Message):
"""Перенаправление на повторную регистрацию"""
- logger.info(f"Пользователь с TG_ID {message.from_user.id} "
- f"отправлен на повторную регистрацию")
+ logger.info(
+ f"Пользователь с TG_ID {message.from_user.id} "
+ f"отправлен на повторную регистрацию"
+ )
await start_registration(message)
# @dp.message_handler(text=about_bot_message)
async def about_bot(message: types.Message):
"""Вывод информации о боте"""
- logger.info(f"Пользователь с TG_ID {message.from_user.id} "
- f"запросил информацию о боте")
+ logger.info(
+ f"Пользователь с TG_ID {message.from_user.id} " f"запросил информацию о боте"
+ )
await bot.send_message(
message.from_user.id,
"""*Добро пожаловать в бот для нетворкинга\.*\n
@@ -150,29 +150,34 @@ async def about_bot(message: types.Message):
async def status_message(message: types.Message):
"""Вывод статуса участия в распределении"""
await check_username(message)
- logger.info(f"Пользователь с TG_ID {message.from_user.id} "
- f"запросил информацию о статусе участия")
+ logger.info(
+ f"Пользователь с TG_ID {message.from_user.id} "
+ f"запросил информацию о статусе участия"
+ )
user_row = get_user_data_from_db(message.from_user.id)
- status_row = get_user_status_from_db(user_row['id'])
- if status_row['status'] == 1:
+ status_row = get_user_status_from_db(user_row["id"])
+ if status_row["status"] == 1:
status = "Вы участвуете в распределении пар на следующей неделе"
else:
- holidays_row = get_holidays_status_from_db(user_row['id'])
- till_value = holidays_row['till_date']
- if till_value == 'null' or till_value == 'Неопределенный срок':
- holidays_till = 'неопределенной даты'
+ holidays_row = get_holidays_status_from_db(user_row["id"])
+ till_value = holidays_row["till_date"]
+ if till_value == "null" or till_value == "Неопределенный срок":
+ holidays_till = "неопределенной даты"
else:
holidays_till = date_from_db_to_message(till_value)
- status = (f"Ты на каникулах до {holidays_till}. "
- f"В это время пара для встречи тебе предложена не будет. "
- f"После указанной даты статус 'Активен' "
- f"будет восстановлен автоматически. Если дата не "
- f"определена, то отключить каникулы необходимо "
- f"вручную кнопкой 'Отключить' в меню 'Каникулы'"
- )
+ status = (
+ f"Ты на каникулах до {holidays_till}. "
+ f"В это время пара для встречи тебе предложена не будет. "
+ f"После указанной даты статус 'Активен' "
+ f"будет восстановлен автоматически. Если дата не "
+ f"определена, то отключить каникулы необходимо "
+ f"вручную кнопкой 'Отключить' в меню 'Каникулы'"
+ )
await bot.send_message(message.from_user.id, text=status)
- logger.info(f"Пользователь с TG_ID {message.from_user.id} "
- f"получил информацию о статусе участия")
+ logger.info(
+ f"Пользователь с TG_ID {message.from_user.id} "
+ f"получил информацию о статусе участия"
+ )
# @dp.message_handler(text=my_pare_button)
@@ -183,34 +188,37 @@ async def my_pare_check(message: types.Message):
met_id = get_met_id_with_user_last_week(user_id)
if met_id is None:
await bot.send_message(
- message.from_user.id,
- "Ты не участвовал в последнем распределении."
+ message.from_user.id, "Ты не участвовал в последнем распределении."
)
else:
- users = db_session.query(MetInfo). \
- filter(MetInfo.id == met_id[0]).first().__dict__
- if users['first_user_id'] == user_id:
- user_info = get_full_user_info_by_id(users['second_user_id'])
+ users = (
+ db_session.query(MetInfo).filter(MetInfo.id == met_id[0]).first().__dict__
+ )
+ if users["first_user_id"] == user_id:
+ user_info = get_full_user_info_by_id(users["second_user_id"])
else:
- user_info = get_full_user_info_by_id(users['first_user_id'])
+ user_info = get_full_user_info_by_id(users["first_user_id"])
message_text = make_message(user_info)
try:
await bot.send_message(
message.from_user.id,
message_text,
parse_mode="HTML",
- reply_markup=help_texts_markup()
+ reply_markup=help_texts_markup(),
)
except Exception as error:
- logger.error(f'Сообщение для пользователя {user_id} '
- f'не отправлено. Ошибка {error}')
- logger.info(f"Пользователь с TG_ID {message.from_user.id} "
- f"получил информацию о своей паре")
+ logger.error(
+ f"Сообщение для пользователя {user_id} "
+ f"не отправлено. Ошибка {error}"
+ )
+ logger.info(
+ f"Пользователь с TG_ID {message.from_user.id} "
+ f"получил информацию о своей паре"
+ )
def register_user_handlers(dp: Dispatcher):
- dp.register_errors_handler(exception_handler,
- exception=exceptions.RetryAfter)
+ dp.register_errors_handler(exception_handler, exception=exceptions.RetryAfter)
dp.register_message_handler(main_menu, text=[menu_message, back_to_menu])
dp.register_message_handler(send_profile, text=my_profile_message)
dp.register_message_handler(edit_profile, text=edit_profile_message)
diff --git a/handlers/user/help_texts.py b/handlers/user/help_texts.py
index 0809552..34417e9 100644
--- a/handlers/user/help_texts.py
+++ b/handlers/user/help_texts.py
@@ -8,16 +8,24 @@
from loader import bot
help_texts_messages = [
- ("Привет! Я твой собеседник на этой неделе. "
- "Как насчет сходить вместе выпить кофе на выходных?"),
+ (
+ "Привет! Я твой собеседник на этой неделе. "
+ "Как насчет сходить вместе выпить кофе на выходных?"
+ ),
"Здравствуйте! Когда у Вас есть время на этой неделе?",
- ("Привет! Бот пишет, что ты мой собеседник на эту неделю. "
- "Можем встретиться или созвониться. Тебе как удобно?"),
- ("Добрый день! Ты мне выпал в боте. "
- "Скажи, когда тебе удобно будет созвониться?"),
- ("Приветствую! Буду рад/а встретиться, но у меня немного "
- "загружена вторая половина недели. "
- "Может, у нас получится во вторник или среду?")
+ (
+ "Привет! Бот пишет, что ты мой собеседник на эту неделю. "
+ "Можем встретиться или созвониться. Тебе как удобно?"
+ ),
+ (
+ "Добрый день! Ты мне выпал в боте. "
+ "Скажи, когда тебе удобно будет созвониться?"
+ ),
+ (
+ "Приветствую! Буду рад/а встретиться, но у меня немного "
+ "загружена вторая половина недели. "
+ "Может, у нас получится во вторник или среду?"
+ ),
]
@@ -26,14 +34,13 @@
async def send_help_texts(message: types.Message):
"""Отправка сообщений примеров."""
await bot.send_message(
- message.from_user.id,
- "Ты можешь скопировать текст нажав на него."
+ message.from_user.id, "Ты можешь скопировать текст нажав на него."
)
for text in help_texts_messages:
await sleep(0.05)
- await bot.send_message(message.from_user.id,
- f'{text}
',
- parse_mode='HTML')
+ await bot.send_message(
+ message.from_user.id, f"{text}
", parse_mode="HTML"
+ )
def register_help_texts_handlers(dp: Dispatcher):
diff --git a/handlers/user/holidays.py b/handlers/user/holidays.py
index b7c9c0b..0320e0c 100644
--- a/handlers/user/holidays.py
+++ b/handlers/user/holidays.py
@@ -8,12 +8,19 @@
from controllerBD.models import Holidays, UserStatus
from handlers.decorators import user_handlers
from handlers.user.check_message import send_message
-from handlers.user.get_info_from_table import get_id_from_user_info_table, \
- get_teleg_id_from_user_info_table
+from handlers.user.get_info_from_table import (
+ get_id_from_user_info_table,
+ get_teleg_id_from_user_info_table,
+)
from handlers.user.work_with_date import date_from_db_to_message
-from keyboards.user import holidays_length, set_holiday_message, \
- one_week_holidays_message, two_week_holidays_message, \
- three_week_holidays_message, turn_off_holidays
+from keyboards.user import (
+ holidays_length,
+ one_week_holidays_message,
+ set_holiday_message,
+ three_week_holidays_message,
+ turn_off_holidays,
+ two_week_holidays_message,
+)
from loader import bot, logger
@@ -21,22 +28,24 @@
@user_handlers
async def check_and_choice_holidays(message: types.Message):
"""Проверка и выбор срока каникул"""
- logger.info(f"Пользователь с TG_ID {message.from_user.id} "
- f"перешел к установке срока каникул")
+ logger.info(
+ f"Пользователь с TG_ID {message.from_user.id} "
+ f"перешел к установке срока каникул"
+ )
await check_holidays_until(message.from_user.id)
await bot.send_message(
message.from_user.id,
- text='Выбери на какой срок ты хочешь установить каникулы',
- reply_markup=holidays_length()
+ text="Выбери на какой срок ты хочешь установить каникулы",
+ reply_markup=holidays_length(),
)
def message_for_holidays(date_to_return):
return (
- f'Ты установил каникулы до '
+ f"Ты установил каникулы до "
f'{date_to_return.strftime("%d.%m.%Y")} '
- f'и начнёшь участвовать в '
- f'распределении с '
+ f"и начнёшь участвовать в "
+ f"распределении с "
f'{(date_to_return + timedelta(days=1)).strftime("%d.%m.%Y")}.'
)
@@ -48,8 +57,7 @@ async def get_one_week_holidays(message: types.Message):
date_to_return = date.today() + timedelta(days=7)
await get_holidays(message, date_to_return)
await bot.send_message(
- message.from_user.id,
- text=message_for_holidays(date_to_return)
+ message.from_user.id, text=message_for_holidays(date_to_return)
)
@@ -60,8 +68,7 @@ async def get_two_week_holidays(message: types.Message):
date_to_return = date.today() + timedelta(days=14)
await get_holidays(message, date_to_return)
await bot.send_message(
- message.from_user.id,
- text=message_for_holidays(date_to_return)
+ message.from_user.id, text=message_for_holidays(date_to_return)
)
@@ -72,8 +79,7 @@ async def get_three_week_holidays(message: types.Message):
date_to_return = date.today() + timedelta(days=21)
await get_holidays(message, date_to_return)
await bot.send_message(
- message.from_user.id,
- text=message_for_holidays(date_to_return)
+ message.from_user.id, text=message_for_holidays(date_to_return)
)
@@ -82,79 +88,75 @@ async def get_three_week_holidays(message: types.Message):
async def cancel_holidays(message: types.Message):
"""Отключение режима каникул"""
user_id = get_id_from_user_info_table(message.from_user.id)
- db_session.query(Holidays).filter(Holidays.id == user_id). \
- update({'status': 0, 'till_date': 'null'})
- db_session.query(UserStatus).filter(UserStatus.id == user_id). \
- update({'status': 1})
+ db_session.query(Holidays).filter(Holidays.id == user_id).update(
+ {"status": 0, "till_date": "null"}
+ )
+ db_session.query(UserStatus).filter(UserStatus.id == user_id).update({"status": 1})
db_session.commit()
- await bot.send_message(
- message.from_user.id,
- text='Режим каникул был отключен'
+ await bot.send_message(message.from_user.id, text="Режим каникул был отключен")
+ logger.info(
+ f"Пользователь с TG_ID {message.from_user.id} " f"отключил режим каникул"
)
- logger.info(f"Пользователь с TG_ID {message.from_user.id} "
- f"отключил режим каникул")
async def get_holidays(message: types.Message, date_to_return):
"""Запись данных о каникулах в БД"""
user_id = get_id_from_user_info_table(message.from_user.id)
- db_session.query(Holidays).filter(Holidays.id == user_id). \
- update({'status': 1, 'till_date': str(date_to_return)})
- db_session.query(UserStatus).filter(UserStatus.id == user_id). \
- update({'status': 0})
+ db_session.query(Holidays).filter(Holidays.id == user_id).update(
+ {"status": 1, "till_date": str(date_to_return)}
+ )
+ db_session.query(UserStatus).filter(UserStatus.id == user_id).update({"status": 0})
db_session.commit()
- logger.info(f"Пользователь с TG_ID {message.from_user.id} "
- f"установил режим каникул до {date_to_return}")
+ logger.info(
+ f"Пользователь с TG_ID {message.from_user.id} "
+ f"установил режим каникул до {date_to_return}"
+ )
async def check_holidays_until(teleg_id):
"""Проверка даты окончания каникул пользователя в БД"""
user_id = get_id_from_user_info_table(teleg_id)
- row = db_session.query(Holidays).filter(
- Holidays.id == user_id
- ).first().__dict__
- if row['status'] == 0:
+ row = db_session.query(Holidays).filter(Holidays.id == user_id).first().__dict__
+ if row["status"] == 0:
pass
else:
await bot.send_message(
teleg_id,
- text=f'Каникулы установлены до '
- f'{date_from_db_to_message(row["till_date"])}.\n'
- f'Ты начнешь участвовать в распределении пар '
- f'со следующего дня.'
+ text=f"Каникулы установлены до "
+ f'{date_from_db_to_message(row["till_date"])}.\n'
+ f"Ты начнешь участвовать в распределении пар "
+ f"со следующего дня.",
)
async def sheduled_check_holidays():
"""Отключение режима каникул при окончании срока. Проверка по расписанию"""
- logger.info('Начало автопроверки статуса каникул')
- data = db_session.query(Holidays.id).filter(and_(
- Holidays.status == 1,
- Holidays.till_date == str(date.today())
- )).all()
+ logger.info("Начало автопроверки статуса каникул")
+ data = (
+ db_session.query(Holidays.id)
+ .filter(and_(Holidays.status == 1, Holidays.till_date == str(date.today())))
+ .all()
+ )
if data:
for row in data:
user_id = get_teleg_id_from_user_info_table(row[0])
- db_session.query(Holidays).filter(Holidays.id == row[0]). \
- update({'status': 0, 'till_date': 'null'})
- db_session.query(UserStatus).filter(UserStatus.id == row[0]). \
- update({'status': 1})
- db_session.commit()
- await send_message(
- teleg_id=user_id,
- text='Режим каникул был отключен'
+ db_session.query(Holidays).filter(Holidays.id == row[0]).update(
+ {"status": 0, "till_date": "null"}
)
- logger.info(f'Каникулы юзера {user_id} отключены автопроверкой')
- logger.info('Конец автопроверки статуса каникул')
+ db_session.query(UserStatus).filter(UserStatus.id == row[0]).update(
+ {"status": 1}
+ )
+ db_session.commit()
+ await send_message(teleg_id=user_id, text="Режим каникул был отключен")
+ logger.info(f"Каникулы юзера {user_id} отключены автопроверкой")
+ logger.info("Конец автопроверки статуса каникул")
def register_holidays_handlers(dp: Dispatcher):
- dp.register_message_handler(check_and_choice_holidays,
- text=set_holiday_message)
- dp.register_message_handler(get_one_week_holidays,
- text=one_week_holidays_message)
- dp.register_message_handler(get_two_week_holidays,
- text=two_week_holidays_message)
- dp.register_message_handler(get_three_week_holidays,
- text=three_week_holidays_message)
+ dp.register_message_handler(check_and_choice_holidays, text=set_holiday_message)
+ dp.register_message_handler(get_one_week_holidays, text=one_week_holidays_message)
+ dp.register_message_handler(get_two_week_holidays, text=two_week_holidays_message)
+ dp.register_message_handler(
+ get_three_week_holidays, text=three_week_holidays_message
+ )
dp.register_message_handler(cancel_holidays, text=turn_off_holidays)
diff --git a/handlers/user/new_member.py b/handlers/user/new_member.py
index 4321c00..a736246 100644
--- a/handlers/user/new_member.py
+++ b/handlers/user/new_member.py
@@ -1,30 +1,39 @@
-from aiogram import types, Dispatcher
+from aiogram import Dispatcher, types
from aiogram.dispatcher import FSMContext
from aiogram.types import ReplyKeyboardRemove
from controllerBD.db_loader import db_session
-from controllerBD.models import Gender, Users, UserStatus, UserMets, Holidays
+from controllerBD.models import Gender, Holidays, UserMets, Users, UserStatus
from handlers.admin.ban_handlers import back_to_main_markup
from handlers.user.add_username import check_username
from handlers.user.first_check import check_and_add_registration_button
from handlers.user.get_info_from_table import (
check_user_in_base,
- get_id_from_user_info_table
+ get_id_from_user_info_table,
+)
+from handlers.user.validators import (
+ validate_about,
+ validate_birthday,
+ validate_check_info,
+ validate_gender,
+ validate_name,
)
from handlers.user.work_with_date import date_from_message_to_db
-from keyboards.user import (back_message, confirm_markup,
- man_message, register_can_skip_reply_markup,
- register_man_or_woman_markup,
- skip_message, woman_message,
- return_to_begin_button, registr_message,
- return_to_begin_markup)
+from keyboards.user import (
+ back_message,
+ confirm_markup,
+ man_message,
+ register_can_skip_reply_markup,
+ register_man_or_woman_markup,
+ registr_message,
+ return_to_begin_button,
+ return_to_begin_markup,
+ skip_message,
+ woman_message,
+)
from loader import bot, logger
from states.states import UserData
-from handlers.user.validators import (validate_about, validate_birthday,
- validate_check_info, validate_gender,
- validate_name)
-
# @dp.message_handler(text=return_to_begin_button, state="*")
async def return_to_begin(message: types.Message, state: FSMContext):
@@ -35,9 +44,7 @@ async def return_to_begin(message: types.Message, state: FSMContext):
def get_gender_from_db(status):
"""Получаем пол пользователя по id пола"""
- info = db_session.query(Gender.gender_name).filter(
- Gender.id == status
- ).first()
+ info = db_session.query(Gender.gender_name).filter(Gender.id == status).first()
return info[0]
@@ -52,10 +59,10 @@ async def confirmation_and_save(message: types.Message, state: FSMContext):
else:
await bot.send_message(
message.from_user.id,
- 'Ура! Теперь ты участвуешь в распределении на следующей неделе. '
- 'Бот напомнит: перед распределением придет сообщение, что '
- 'скоро тебе подберут пару.',
- reply_markup=ReplyKeyboardRemove()
+ "Ура! Теперь ты участвуешь в распределении на следующей неделе. "
+ "Бот напомнит: перед распределением придет сообщение, что "
+ "скоро тебе подберут пару.",
+ reply_markup=ReplyKeyboardRemove(),
)
await bot.send_message(
message.from_user.id,
@@ -64,17 +71,21 @@ async def confirmation_and_save(message: types.Message, state: FSMContext):
)
data = await state.get_data()
if await check_user_in_base(message):
- update_profile_db(message.from_user.id,
- data.get('name'),
- data.get('birthday'),
- data.get('about'),
- data.get('gender'))
+ update_profile_db(
+ message.from_user.id,
+ data.get("name"),
+ data.get("birthday"),
+ data.get("about"),
+ data.get("gender"),
+ )
else:
- add_to_db(message.from_user.id,
- data.get('name'),
- data.get('birthday'),
- data.get('about'),
- data.get('gender'))
+ add_to_db(
+ message.from_user.id,
+ data.get("name"),
+ data.get("birthday"),
+ data.get("about"),
+ data.get("gender"),
+ )
await add_new_user_in_status_table(message)
await state.reset_state()
@@ -91,11 +102,13 @@ def add_to_db(teleg_id, name, birthday, about, gender):
pass
else:
birthday = date_from_message_to_db(birthday)
- db_session.add(Users(teleg_id=teleg_id, name=name,
- birthday=birthday, about=about, gender=gender))
+ db_session.add(
+ Users(
+ teleg_id=teleg_id, name=name, birthday=birthday, about=about, gender=gender
+ )
+ )
db_session.commit()
- logger.info(f"Пользователь с TG_ID {teleg_id} "
- f"добавлен в БД как новый участник")
+ logger.info(f"Пользователь с TG_ID {teleg_id} " f"добавлен в БД как новый участник")
def update_profile_db(teleg_id, name, birthday, about, gender):
@@ -104,12 +117,11 @@ def update_profile_db(teleg_id, name, birthday, about, gender):
pass
else:
birthday = date_from_message_to_db(birthday)
- db_session.query(Users).filter(Users.teleg_id == teleg_id). \
- update({'name': name, 'birthday': birthday,
- 'about': about, 'gender': gender})
+ db_session.query(Users).filter(Users.teleg_id == teleg_id).update(
+ {"name": name, "birthday": birthday, "about": about, "gender": gender}
+ )
db_session.commit()
- logger.info(f"Пользователь с TG_ID {teleg_id} "
- f"обновил информацию о себе")
+ logger.info(f"Пользователь с TG_ID {teleg_id} " f"обновил информацию о себе")
async def add_new_user_in_status_table(message):
@@ -127,12 +139,13 @@ async def add_new_user_in_status_table(message):
# @dp.message_handler(text=registr_message, state=UserData.start)
async def start_registration(message: types.Message):
"""Первое состояние. Старт регистрации."""
- logger.info(f"Пользователь с TG_ID {message.from_user.id} "
- f"начал процесс регистрации")
+ logger.info(
+ f"Пользователь с TG_ID {message.from_user.id} " f"начал процесс регистрации"
+ )
await bot.send_message(
message.from_user.id,
- 'Как тебя представить собеседнику? (Введи только имя)',
- reply_markup=return_to_begin_markup()
+ "Как тебя представить собеседнику? (Введи только имя)",
+ reply_markup=return_to_begin_markup(),
)
await UserData.name.set()
@@ -147,7 +160,7 @@ async def check_data(tg_id, name, birthday, about, gender):
f"О себе: {about}\n"
f"Пол: {gender}\n\n"
f"Все верно?",
- reply_markup=confirm_markup()
+ reply_markup=confirm_markup(),
)
await UserData.check_info.set()
@@ -155,10 +168,10 @@ async def check_data(tg_id, name, birthday, about, gender):
async def end_registration(state, message):
"""Формирование данных пользователя для проверки"""
data = await state.get_data()
- name = data.get('name')
- birthday = data.get('birthday')
- about = data.get('about')
- gender = get_gender_from_db(data.get('gender'))
+ name = data.get("name")
+ birthday = data.get("birthday")
+ about = data.get("about")
+ gender = get_gender_from_db(data.get("gender"))
tg_id = message.from_user.id
await check_data(tg_id, name, birthday, about, gender)
@@ -169,9 +182,9 @@ async def answer_name(message: types.Message, state: FSMContext):
name = message.text
if not validate_name(name):
await message.answer(
- 'Что-то не так с введенным именем. '
- 'Имя должно состоять из букв русского или латинского алфавитов '
- 'и быть менее 100 символов.'
+ "Что-то не так с введенным именем. "
+ "Имя должно состоять из букв русского или латинского алфавитов "
+ "и быть менее 100 символов."
)
return
await state.update_data(name=name)
@@ -182,8 +195,8 @@ async def question_birthday(message: types.Message):
"""Запрос даты рождения"""
await bot.send_message(
message.from_user.id,
- 'Введи, пожалуйста, дату рождения в формате ДД.ММ.ГГГГ',
- reply_markup=register_can_skip_reply_markup()
+ "Введи, пожалуйста, дату рождения в формате ДД.ММ.ГГГГ",
+ reply_markup=register_can_skip_reply_markup(),
)
await UserData.birthday.set()
@@ -196,7 +209,7 @@ async def answer_birthday(message: types.Message, state: FSMContext):
await start_registration(message)
else:
if birthday == skip_message:
- birthday = 'Не указано'
+ birthday = "Не указано"
else:
if not await validate_birthday(message):
return
@@ -209,7 +222,7 @@ async def question_about(message: types.Message):
await bot.send_message(
message.from_user.id,
"Расскажи немного о себе?",
- reply_markup=register_can_skip_reply_markup()
+ reply_markup=register_can_skip_reply_markup(),
)
await UserData.about.set()
@@ -223,7 +236,7 @@ async def answer_about(message: types.Message, state: FSMContext):
await question_birthday(message)
else:
if about == skip_message:
- about = 'Не указано'
+ about = "Не указано"
else:
if not await validate_about(message):
return
@@ -234,9 +247,7 @@ async def answer_about(message: types.Message, state: FSMContext):
async def question_gender(message: types.Message):
"""Запрос пола пользователя"""
await bot.send_message(
- message.from_user.id,
- "Выбери пол",
- reply_markup=register_man_or_woman_markup()
+ message.from_user.id, "Выбери пол", reply_markup=register_man_or_woman_markup()
)
await UserData.gender.set()
@@ -261,12 +272,11 @@ async def answer_gender(message: types.Message, state: FSMContext):
def register_new_member_handler(dp: Dispatcher):
- dp.register_message_handler(return_to_begin, text=return_to_begin_button,
- state="*")
- dp.register_message_handler(confirmation_and_save,
- state=UserData.check_info)
- dp.register_message_handler(start_registration, text=registr_message,
- state=UserData.start)
+ dp.register_message_handler(return_to_begin, text=return_to_begin_button, state="*")
+ dp.register_message_handler(confirmation_and_save, state=UserData.check_info)
+ dp.register_message_handler(
+ start_registration, text=registr_message, state=UserData.start
+ )
dp.register_message_handler(answer_name, state=UserData.name)
dp.register_message_handler(answer_birthday, state=UserData.birthday)
dp.register_message_handler(answer_about, state=UserData.about)
diff --git a/handlers/user/review_history.py b/handlers/user/review_history.py
index 9a7674e..3645e4c 100644
--- a/handlers/user/review_history.py
+++ b/handlers/user/review_history.py
@@ -1,8 +1,8 @@
-from aiogram import types, Dispatcher
+from aiogram import Dispatcher, types
from aiogram.dispatcher import FSMContext
-from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
+from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.callback_data import CallbackData
-from sqlalchemy import or_, desc, and_
+from sqlalchemy import and_, desc, or_
from sqlalchemy.exc import NoResultFound
from controllerBD.db_loader import db_session
@@ -10,10 +10,12 @@
from controllerBD.services import get_tg_username_from_db_by_base_id
from handlers.decorators import user_handlers
from handlers.user.add_username import check_username
-from handlers.user.get_info_from_table import get_id_from_user_info_table, \
- get_user_info_by_id
+from handlers.user.get_info_from_table import (
+ get_id_from_user_info_table,
+ get_user_info_by_id,
+)
from handlers.user.work_with_date import date_from_db_to_message
-from keyboards.user import review_yes_or_no, my_reviews
+from keyboards.user import my_reviews, review_yes_or_no
from loader import bot, logger
from states import ReviewState
@@ -22,10 +24,15 @@
def list_of_user_mets_id(user_id):
"""Получение id всех встреч пользователя."""
- met_ids = db_session.query(MetInfo.id).filter(
- or_(MetInfo.first_user_id == user_id,
- MetInfo.second_user_id == user_id)
- ).order_by(desc(MetInfo.id)).limit(10).all()
+ met_ids = (
+ db_session.query(MetInfo.id)
+ .filter(
+ or_(MetInfo.first_user_id == user_id, MetInfo.second_user_id == user_id)
+ )
+ .order_by(desc(MetInfo.id))
+ .limit(10)
+ .all()
+ )
return [met_id[0] for met_id in met_ids]
@@ -38,8 +45,7 @@ async def get_my_reviews(message: types.Message):
met_ids = list_of_user_mets_id(user_id)
count_of_mets = len(met_ids)
if count_of_mets == 0:
- await bot.send_message(message.from_user.id,
- "У тебя еще не было встреч.")
+ await bot.send_message(message.from_user.id, "У тебя еще не было встреч.")
else:
met_id = met_ids[0]
review_info = get_sqliterow_review(met_id, user_id)
@@ -48,19 +54,24 @@ async def get_my_reviews(message: types.Message):
else:
edit_button = "Доб. отзыв"
message_text = prepare_message(user_id, met_id, review_info)
- await bot.send_message(message.from_user.id, message_text,
- parse_mode='HTML',
- reply_markup=inline_markup(count_of_mets, 0,
- edit_button))
- logger.info(f'Пользователь {message.from_user.id} получил информация '
- f'о последней встрече {met_id}')
+ await bot.send_message(
+ message.from_user.id,
+ message_text,
+ parse_mode="HTML",
+ reply_markup=inline_markup(count_of_mets, 0, edit_button),
+ )
+ logger.info(
+ f"Пользователь {message.from_user.id} получил информация "
+ f"о последней встрече {met_id}"
+ )
# @dp.callback_query_handler(review_callbackdata.filter())
-async def button_press(call: types.CallbackQuery, callback_data: dict,
- state: FSMContext):
+async def button_press(
+ call: types.CallbackQuery, callback_data: dict, state: FSMContext
+):
"""Выводим информацию о встрече в зависимости от позиции."""
- position = int(callback_data.get('position'))
+ position = int(callback_data.get("position"))
user_id = get_id_from_user_info_table(call.from_user.id)
met_ids = list_of_user_mets_id(user_id)
count_of_mets = len(met_ids)
@@ -70,58 +81,64 @@ async def button_press(call: types.CallbackQuery, callback_data: dict,
edit_button = "Ред. отзыв"
else:
edit_button = "Доб. отзыв"
- if int(callback_data.get('edit')) == 1:
+ if int(callback_data.get("edit")) == 1:
await call.answer()
await bot.edit_message_reply_markup(
chat_id=call.from_user.id,
message_id=call.message.message_id,
- reply_markup=None
+ reply_markup=None,
)
await bot.send_message(
call.from_user.id,
"Состоялась ли твоя встреча?",
- reply_markup=review_yes_or_no()
+ reply_markup=review_yes_or_no(),
+ )
+ logger.info(
+ f"Пользователь {call.from_user.id} начал редактировать "
+ f"отзыв о встрече {met_id}"
)
- logger.info(f'Пользователь {call.from_user.id} начал редактировать '
- f'отзыв о встрече {met_id}')
await ReviewState.start.set()
user_id = get_id_from_user_info_table(call.from_user.id)
await state.update_data(user_id=user_id)
await state.update_data(met_id=met_id)
else:
message_text = prepare_message(user_id, met_id, review_info)
- await bot.edit_message_text(message_text, call.from_user.id,
- call.message.message_id, parse_mode='HTML',
- reply_markup=inline_markup(count_of_mets,
- position,
- edit_button))
- logger.info(f'Пользователь {call.from_user.id} получил информация '
- f'о последней встрече {met_id}')
+ await bot.edit_message_text(
+ message_text,
+ call.from_user.id,
+ call.message.message_id,
+ parse_mode="HTML",
+ reply_markup=inline_markup(count_of_mets, position, edit_button),
+ )
+ logger.info(
+ f"Пользователь {call.from_user.id} получил информация "
+ f"о последней встрече {met_id}"
+ )
def inline_markup(count_of_mets, position, edit_button):
"""Инлайн-кнопки для карточки встречи."""
markup = InlineKeyboardMarkup(row_width=3)
if position < (count_of_mets - 1):
- markup.insert(InlineKeyboardButton(
- "Пред.",
- callback_data=review_callbackdata.new(
- position=position+1,
- edit=0
- )))
- markup.insert(InlineKeyboardButton(
- f"{edit_button}",
- callback_data=review_callbackdata.new(
- position=position,
- edit=1
- )))
+ markup.insert(
+ InlineKeyboardButton(
+ "Пред.",
+ callback_data=review_callbackdata.new(position=position + 1, edit=0),
+ )
+ )
+ markup.insert(
+ InlineKeyboardButton(
+ f"{edit_button}",
+ callback_data=review_callbackdata.new(position=position, edit=1),
+ )
+ )
if position > 0:
- markup.insert(InlineKeyboardButton(
- "След.",
- callback_data=review_callbackdata.new(
- position=position-1,
- edit=0
- )))
+ markup.insert(
+ InlineKeyboardButton(
+ "След.",
+ callback_data=review_callbackdata.new(position=position - 1, edit=0),
+ )
+ )
return markup
@@ -129,30 +146,27 @@ def inline_markup(count_of_mets, position, edit_button):
def prepare_message(user_id, met_id, review_info):
"""Подготавливаем сообщение о встрече с коментарием."""
met_info = get_sqliterow_about_met(met_id)
- date = date_from_db_to_message(met_info['date'])
- if met_info['first_user_id'] == user_id:
- pare_id = met_info['second_user_id']
+ date = date_from_db_to_message(met_info["date"])
+ if met_info["first_user_id"] == user_id:
+ pare_id = met_info["second_user_id"]
else:
- pare_id = met_info['first_user_id']
+ pare_id = met_info["first_user_id"]
pare_info = get_user_info_by_id(pare_id)
pare_tg_username = get_tg_username_from_db_by_base_id(pare_id)
if pare_tg_username:
- pare_username_for_message = f'(@{pare_tg_username})'
+ pare_username_for_message = f"(@{pare_tg_username})"
else:
- pare_username_for_message = ''
+ pare_username_for_message = ""
if review_info:
- grade = review_info['grade']
+ grade = review_info["grade"]
if grade == 0:
- grade_text = 'Встреча не состоялась.'
+ grade_text = "Встреча не состоялась."
else:
grade_text = f"Оценка - {grade}."
- comment = review_info['comment']
- if comment == 'null':
- comment = 'Не указано'
- review = (
- f" {grade_text}\n"
- f" {comment}"
- )
+ comment = review_info["comment"]
+ if comment == "null":
+ comment = "Не указано"
+ review = f" {grade_text}\n" f" {comment}"
else:
review = "Комментарий на встречу не был добавлен."
@@ -162,26 +176,25 @@ def prepare_message(user_id, met_id, review_info):
f"{pare_info['name']} {pare_username_for_message}\n\n"
f"Отзыв о встрече:\n"
f"{review}"
-
)
return message
def get_sqliterow_about_met(met_id):
"""Получаем словарь строки MetInfo."""
- met_info = db_session.query(MetInfo).filter(
- MetInfo.id == met_id
- ).first().__dict__
+ met_info = db_session.query(MetInfo).filter(MetInfo.id == met_id).first().__dict__
return met_info
def get_sqliterow_review(met_id, user_id):
"""Получаем словарь строки Review."""
try:
- review_info = db_session.query(MetsReview).filter(and_(
- MetsReview.met_id == met_id,
- MetsReview.who_id == user_id
- )).one().__dict__
+ review_info = (
+ db_session.query(MetsReview)
+ .filter(and_(MetsReview.met_id == met_id, MetsReview.who_id == user_id))
+ .one()
+ .__dict__
+ )
except NoResultFound:
review_info = None
return review_info
@@ -189,5 +202,4 @@ def get_sqliterow_review(met_id, user_id):
def register_review_history_handler(dp: Dispatcher):
dp.register_message_handler(get_my_reviews, text=my_reviews)
- dp.register_callback_query_handler(button_press,
- review_callbackdata.filter())
+ dp.register_callback_query_handler(button_press, review_callbackdata.filter())
diff --git a/handlers/user/reviews.py b/handlers/user/reviews.py
index e18e378..e9c5868 100644
--- a/handlers/user/reviews.py
+++ b/handlers/user/reviews.py
@@ -1,20 +1,30 @@
import datetime
-from aiogram import types, Dispatcher
+from aiogram import Dispatcher, types
from aiogram.dispatcher import FSMContext
-from sqlalchemy import and_, or_, desc, exists
+from sqlalchemy import and_, desc, exists, or_
from controllerBD.db_loader import db_session
from controllerBD.models import MetInfo, MetsReview
from handlers.decorators import admin_handlers
-from handlers.user.get_info_from_table import \
- get_teleg_id_from_user_info_table, \
- get_id_from_user_info_table
-from handlers.user.validators import validate_review_yes_or_no, \
- validate_about, validate_review_grade
+from handlers.user.get_info_from_table import (
+ get_id_from_user_info_table,
+ get_teleg_id_from_user_info_table,
+)
+from handlers.user.validators import (
+ validate_about,
+ validate_review_grade,
+ validate_review_yes_or_no,
+)
from keyboards.admin import review_messages
-from keyboards.user import skip_message, menu_markup, \
- review_yes_or_no, no_button, yes_button, review_skip
+from keyboards.user import (
+ menu_markup,
+ no_button,
+ review_skip,
+ review_yes_or_no,
+ skip_message,
+ yes_button,
+)
from loader import bot, logger
from states import ReviewState
@@ -28,8 +38,7 @@ async def start_review(message: types.Message):
if len(users_id) > 0:
await request_review(users_id)
else:
- await bot.send_message(message.from_user.id,
- "На этой неделе не было встреч.")
+ await bot.send_message(message.from_user.id, "На этой неделе не было встреч.")
async def request_review(users_id):
@@ -41,21 +50,23 @@ async def request_review(users_id):
await bot.send_message(
user_teleg_id,
"Состоялась ли твоя встреча?",
- reply_markup=review_yes_or_no()
+ reply_markup=review_yes_or_no(),
)
await ReviewState.start.set()
- logger.info(f"Сообщение пользователю {user_teleg_id} "
- f"для оценки встречи отправлено.")
+ logger.info(
+ f"Сообщение пользователю {user_teleg_id} "
+ f"для оценки встречи отправлено."
+ )
except Exception as error:
- logger.error(f"Сообщение пользователю c {user_teleg_id} "
- f"не доставлено. {error}")
+ logger.error(
+ f"Сообщение пользователю c {user_teleg_id} " f"не доставлено. {error}"
+ )
continue
logger.info("Все сообщения разосланы")
# @dp.message_handler(state=ReviewState.start)
-async def review_answer_yes_or_now(message: types.Message,
- state: FSMContext):
+async def review_answer_yes_or_now(message: types.Message, state: FSMContext):
"""Вопрос состоялась ли встреча."""
answer = message.text
if not await validate_review_yes_or_no(message):
@@ -65,11 +76,12 @@ async def review_answer_yes_or_now(message: types.Message,
message.from_user.id,
"Ты можешь оставить отзыв позже. "
"Для этого нажми в главном меню кнопку Мои встречи.",
- reply_markup=menu_markup(message)
+ reply_markup=menu_markup(message),
)
await state.reset_state()
- logger.info(f'Пользователь {message.from_user.id} отклонил '
- f'предоставление отзыва')
+ logger.info(
+ f"Пользователь {message.from_user.id} отклонил " f"предоставление отзыва"
+ )
elif answer == no_button:
await state.update_data(grade=0)
await question_comment(message)
@@ -82,7 +94,7 @@ async def question_comment(message: types.Message):
await bot.send_message(
message.from_user.id,
"Пожалуйста, введи комментарий к оценке (не более 500 символов).",
- reply_markup=review_skip()
+ reply_markup=review_skip(),
)
await ReviewState.comment.set()
@@ -97,7 +109,7 @@ async def answer_review_comment(message: types.Message, state: FSMContext):
answer = "null"
await state.update_data(comment=answer)
data = await state.get_data()
- if not data.get('user_id') or not data.get('met_id'):
+ if not data.get("user_id") or not data.get("met_id"):
user_id = get_id_from_user_info_table(message.from_user.id)
await state.update_data(user_id=user_id)
met_id = get_met_id_with_user_last_week(user_id)[0]
@@ -112,7 +124,7 @@ async def question_grade(message: types.Message):
"Оцени встречу от 1 до 5, где \n"
" - 1 - Совсем не понравилось,\n"
" - 5 - Все было супер.",
- reply_markup=review_skip()
+ reply_markup=review_skip(),
)
await ReviewState.grade.set()
@@ -126,11 +138,12 @@ async def answer_review_grade(message: types.Message, state: FSMContext):
message.from_user.id,
"Ты можешь оставить отзыв позже. "
"Для этого нажми в главном меню кнопку Мои встречи.",
- reply_markup=menu_markup(message)
+ reply_markup=menu_markup(message),
)
await state.reset_state()
- logger.info(f'Пользователь {message.from_user.id} отклонил '
- f'предоставление отзыва')
+ logger.info(
+ f"Пользователь {message.from_user.id} отклонил " f"предоставление отзыва"
+ )
elif not await validate_review_grade(grade):
await bot.send_message(message.from_user.id, "Введи оценку от 1 до 5")
return
@@ -143,11 +156,11 @@ def preparing_list_of_users_id():
"""Выгрузка списка ID пользователей из
таблицы проведенных встреч за неделю."""
start_period = datetime.date.today() - datetime.timedelta(days=7)
- data = db_session.query(
- MetInfo.first_user_id,
- MetInfo.second_user_id
- ).filter(
- MetInfo.date.between(str(start_period), str(datetime.date.today))).all()
+ data = (
+ db_session.query(MetInfo.first_user_id, MetInfo.second_user_id)
+ .filter(MetInfo.date.between(str(start_period), str(datetime.date.today)))
+ .all()
+ )
logger.info("Список ID для рассылки на отзывы сформирован")
return [element[0] for element in data] + [element[1] for element in data]
@@ -155,39 +168,45 @@ def preparing_list_of_users_id():
async def save_or_update_review(message, state):
"""Сохранение комментария в БД."""
data = await state.get_data()
- user_id = data.get('user_id')
- met_id = data.get('met_id')
- grade = data.get('grade')
- comment = data.get('comment')
+ user_id = data.get("user_id")
+ met_id = data.get("met_id")
+ grade = data.get("grade")
+ comment = data.get("comment")
if await check_comment_in_bd(user_id, met_id):
update_review(user_id, met_id, grade, comment)
else:
add_review(user_id, met_id, grade, comment)
await state.reset_state()
await bot.send_message(
- message.from_user.id,
- "Спасибо за отзыв.",
- reply_markup=menu_markup(message)
+ message.from_user.id, "Спасибо за отзыв.", reply_markup=menu_markup(message)
)
def get_met_id_with_user_last_week(user_id):
"""Получение id встречи по пользователю за прошедшую неделю."""
start_period = datetime.date.today() - datetime.timedelta(days=7)
- met_id = db_session.query(MetInfo.id).filter(
- and_(MetInfo.date.between(str(start_period), str(datetime.date.today)),
- or_(
- MetInfo.first_user_id == user_id,
- MetInfo.second_user_id == user_id
- ))).order_by(desc(MetInfo.id)).limit(1).first()
+ met_id = (
+ db_session.query(MetInfo.id)
+ .filter(
+ and_(
+ MetInfo.date.between(str(start_period), str(datetime.date.today)),
+ or_(
+ MetInfo.first_user_id == user_id, MetInfo.second_user_id == user_id
+ ),
+ )
+ )
+ .order_by(desc(MetInfo.id))
+ .limit(1)
+ .first()
+ )
return met_id
async def check_comment_in_bd(user_id, met_id):
"""Проверка наличия отзыва на встречу."""
- is_exist = db_session.query(exists().where(
- and_(MetsReview.met_id == met_id, MetsReview.who_id == user_id)
- )).scalar()
+ is_exist = db_session.query(
+ exists().where(and_(MetsReview.met_id == met_id, MetsReview.who_id == user_id))
+ ).scalar()
if not is_exist:
return False
return True
@@ -197,32 +216,41 @@ def update_review(user_id, met_id, grade, comment):
"""Обновление комментария о встрече."""
db_session.query(MetsReview).filter(
and_(MetsReview.met_id == met_id, MetsReview.who_id == user_id)
- ).update({
- 'grade': grade,
- 'comment': comment,
- 'date_of_comment': str(datetime.date.today())
- })
+ ).update(
+ {
+ "grade": grade,
+ "comment": comment,
+ "date_of_comment": str(datetime.date.today()),
+ }
+ )
db_session.commit()
- logger.info(f"Пользователь с ID {user_id} "
- f"обновил комментарий о встрече {met_id}")
+ logger.info(
+ f"Пользователь с ID {user_id} " f"обновил комментарий о встрече {met_id}"
+ )
def add_review(user_id, met_id, grade, comment):
"""Добавление комментария о встрече"""
- users = db_session.query(MetInfo).filter(
- MetInfo.id == met_id
- ).first().__dict__
- if users['first_user_id'] == user_id:
- about_whom_id = users['second_user_id']
+ users = db_session.query(MetInfo).filter(MetInfo.id == met_id).first().__dict__
+ if users["first_user_id"] == user_id:
+ about_whom_id = users["second_user_id"]
else:
- about_whom_id = users['first_user_id']
- db_session.add(MetsReview(met_id=met_id, who_id=user_id,
- about_whom_id=about_whom_id, grade=grade,
- comment=comment,
- date_of_comment=datetime.date.today()))
+ about_whom_id = users["first_user_id"]
+ db_session.add(
+ MetsReview(
+ met_id=met_id,
+ who_id=user_id,
+ about_whom_id=about_whom_id,
+ grade=grade,
+ comment=comment,
+ date_of_comment=datetime.date.today(),
+ )
+ )
db_session.commit()
- logger.info(f"Пользователь с ID {user_id} "
- f"добавил комментарий о встрече {met_id}")
+ logger.info(
+ f"Пользователь с ID {user_id} " f"добавил комментарий о встрече {met_id}"
+ )
+
# def get_met_id_with_user_last_three(user_id):
# """Получение id встречи по пользователю за прошедшую неделю."""
@@ -240,8 +268,6 @@ def add_review(user_id, met_id, grade, comment):
def register_review_handlers(dp: Dispatcher):
dp.register_message_handler(start_review, text=review_messages)
- dp.register_message_handler(review_answer_yes_or_now,
- state=ReviewState.start)
- dp.register_message_handler(answer_review_comment,
- state=ReviewState.comment)
+ dp.register_message_handler(review_answer_yes_or_now, state=ReviewState.start)
+ dp.register_message_handler(answer_review_comment, state=ReviewState.comment)
dp.register_message_handler(answer_review_grade, state=ReviewState.grade)
diff --git a/handlers/user/start_handler.py b/handlers/user/start_handler.py
index 855585d..90dc13f 100644
--- a/handlers/user/start_handler.py
+++ b/handlers/user/start_handler.py
@@ -1,4 +1,4 @@
-from aiogram import types, Dispatcher
+from aiogram import Dispatcher, types
from aiogram.dispatcher import FSMContext
from handlers.user.first_check import check_and_add_registration_button
@@ -6,14 +6,17 @@
# @dp.message_handler(commands=['start', 'help'], state='*')
-async def process_start_command(message: types.Message,
- state: FSMContext):
+async def process_start_command(message: types.Message, state: FSMContext):
"""Функция первого обращения к боту."""
await state.reset_state()
- logger.info(f"user id-{message.from_user.id} "
- f"tg-@{message.from_user.username} start a bot")
+ logger.info(
+ f"user id-{message.from_user.id} "
+ f"tg-@{message.from_user.username} start a bot"
+ )
await check_and_add_registration_button(message)
+
def register_start_handler(dp: Dispatcher):
- dp.register_message_handler(process_start_command,
- commands=['start', 'help'], state='*')
+ dp.register_message_handler(
+ process_start_command, commands=["start", "help"], state="*"
+ )
diff --git a/handlers/user/unknown_message.py b/handlers/user/unknown_message.py
index 45d5701..b8dcf4a 100644
--- a/handlers/user/unknown_message.py
+++ b/handlers/user/unknown_message.py
@@ -1,4 +1,4 @@
-from aiogram import types, Dispatcher
+from aiogram import Dispatcher, types
from handlers.decorators import user_handlers
from keyboards.user import menu_markup
@@ -11,7 +11,7 @@ async def unknown_message(message: types.Message):
await bot.send_message(
message.from_user.id,
"Я тебя не понимаю. Пожалуйста, воспользуйся меню.",
- reply_markup=menu_markup(message)
+ reply_markup=menu_markup(message),
)
diff --git a/handlers/user/validators/__init__.py b/handlers/user/validators/__init__.py
index ef5f67c..7946f53 100644
--- a/handlers/user/validators/__init__.py
+++ b/handlers/user/validators/__init__.py
@@ -1 +1 @@
-from .validators import *
\ No newline at end of file
+from .validators import *
diff --git a/handlers/user/validators/validators.py b/handlers/user/validators/validators.py
index 7bcdcbe..5fc651c 100644
--- a/handlers/user/validators/validators.py
+++ b/handlers/user/validators/validators.py
@@ -2,17 +2,22 @@
from datetime import datetime
from aiogram import types
-from keyboards.user import (all_right_message, back_message, man_message,
- skip_message, woman_message, yes_button, no_button)
+
+from keyboards.user import (
+ all_right_message,
+ back_message,
+ man_message,
+ no_button,
+ skip_message,
+ woman_message,
+ yes_button,
+)
from loader import bot
def validate_name(message):
"""Валидация введенных данных в поле Имя"""
- return (re.fullmatch(
- r"^[a-яА-ЯЁёa-zA-Z\s]{1,100}$",
- message
- ) and len(message) <= 100)
+ return re.fullmatch(r"^[a-яА-ЯЁёa-zA-Z\s]{1,100}$", message) and len(message) <= 100
async def validate_birthday(message: types.Message):
@@ -20,36 +25,35 @@ async def validate_birthday(message: types.Message):
if re.fullmatch(
r"^((0[1-9]|[12]\d)\.(0[1-9]|1[012])|"
r"30\.(0[13-9]|1[012])|31\.(0[13578]|1[02]))\.(19|20)\d\d$",
- message.text
+ message.text,
):
- date_obj = datetime.strptime(message.text, '%d.%m.%Y')
+ date_obj = datetime.strptime(message.text, "%d.%m.%Y")
difference = datetime.now() - date_obj
age = int(difference.days) / 365.2
if age < 0:
await bot.send_message(
message.from_user.id,
- 'Дата из будущего?)) Введи правильную дату рождения'
+ "Дата из будущего?)) Введи правильную дату рождения",
)
return False
elif age <= 14:
await bot.send_message(
message.from_user.id,
- 'Твой возраст должен быть больше 14 лет. '
- 'Введи правильную дату рождения'
+ "Твой возраст должен быть больше 14 лет. "
+ "Введи правильную дату рождения",
)
return False
elif age > 120:
await bot.send_message(
message.from_user.id,
- 'Указанный возраст болеее 120 лет. '
- 'Введи правильную дату рождения'
+ "Указанный возраст болеее 120 лет. " "Введи правильную дату рождения",
)
return False
return True
await bot.send_message(
message.from_user.id,
- 'Что-то не так с введенными данными. '
- 'Дата должна состоять из цифр и точек в формате ДД.ММ.ГГГГ'
+ "Что-то не так с введенными данными. "
+ "Дата должна состоять из цифр и точек в формате ДД.ММ.ГГГГ",
)
return False
@@ -58,14 +62,13 @@ async def validate_about(message):
"""Валидация введенных данных в поле О себе"""
if len(message.text) > 500:
await bot.send_message(
- message.from_user.id,
- 'Текст должен быть меньше 500 символов. Повтори ввод.'
+ message.from_user.id, "Текст должен быть меньше 500 символов. Повтори ввод."
)
return False
elif not re.fullmatch(r"^[^<>]+$", message.text):
await bot.send_message(
message.from_user.id,
- 'В тексте запрещено использовать символы <>. Повтори ввод.'
+ "В тексте запрещено использовать символы <>. Повтори ввод.",
)
return False
return True
@@ -77,8 +80,7 @@ async def validate_gender(message: types.Message):
if message.text not in choice:
await bot.send_message(
message.from_user.id,
- 'Пожалуйста, выбери из доступных вариантов или '
- 'нажми "Пропустить"'
+ "Пожалуйста, выбери из доступных вариантов или " 'нажми "Пропустить"',
)
return False
return True
@@ -90,8 +92,7 @@ async def validate_check_info(message):
choice = [all_right_message, back_message]
if message.text not in choice:
await bot.send_message(
- message.from_user.id,
- 'Пожалуйста, выбери из доступных вариантов.'
+ message.from_user.id, "Пожалуйста, выбери из доступных вариантов."
)
return False
return True
@@ -101,8 +102,7 @@ async def validate_review_yes_or_no(message):
choice = [yes_button, no_button, skip_message]
if message.text not in choice:
await bot.send_message(
- message.from_user.id,
- 'Пожалуйста, выбери из доступных вариантов.'
+ message.from_user.id, "Пожалуйста, выбери из доступных вариантов."
)
return False
return True
diff --git a/handlers/user/work_with_date.py b/handlers/user/work_with_date.py
index 12ba74b..33140b1 100644
--- a/handlers/user/work_with_date.py
+++ b/handlers/user/work_with_date.py
@@ -2,12 +2,12 @@
def date_from_db_to_message(date):
- date_from_db = datetime.datetime.strptime(date, '%Y-%m-%d')
- date_text = date_from_db.strftime('%d.%m.%Y')
+ date_from_db = datetime.datetime.strptime(date, "%Y-%m-%d")
+ date_text = date_from_db.strftime("%d.%m.%Y")
return date_text
def date_from_message_to_db(date):
- date_from_message = datetime.datetime.strptime(date, '%d.%m.%Y')
- date_text = date_from_message.strftime('%Y-%m-%d')
+ date_from_message = datetime.datetime.strptime(date, "%d.%m.%Y")
+ date_text = date_from_message.strftime("%Y-%m-%d")
return date_text
diff --git a/keyboards/admin/admin_markups.py b/keyboards/admin/admin_markups.py
index 1507f4c..38f2a06 100644
--- a/keyboards/admin/admin_markups.py
+++ b/keyboards/admin/admin_markups.py
@@ -1,17 +1,18 @@
from aiogram import types
from aiogram.types import ReplyKeyboardMarkup
+
from data import ADMIN_TG_ID
-from keyboards.user.defalt_markups import (back_to_main, menu_markup,
- menu_message)
+from keyboards.user.defalt_markups import back_to_main, menu_markup, menu_message
admin_menu_button = "Меню администратора"
inform = "Отчет"
ban_list = "Бан-лист"
+pair_generation = "Настройки генерации пар"
add_to_ban_list = "Добавить в бан-лист"
remove_from_ban_list = "Убрать из бана"
go_back = "Назад"
-algo_start = "Алгоритм"
+# algo_start = "Сгенерировать пары сейчас"
review_messages = "Опрос"
change_status = "Статус участия"
cancel = "Отмена"
@@ -19,6 +20,11 @@
do_not_take_part_button = "Не принимать участие"
send_message_to_all_button = "Сообщение пользователям"
+force_pair_generation = "Сгенерировать пары сейчас"
+stop_pair_generation = "Остановить генерацию пар"
+renew_pair_generation = "Начать генерацию пар"
+change_pair_generation_date = "Изменить дату генерации пар"
+
inform_active_users = "Участники"
inform_bad_users = "Нарушители"
@@ -43,8 +49,10 @@ def admin_menu_markup():
"""Меню админа."""
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
markup.row(inform, send_message_to_all_button)
- markup.row(ban_list, change_status)
- markup.row(back_to_main,)
+ markup.row(ban_list, change_status, pair_generation)
+ markup.row(
+ back_to_main,
+ )
return markup
@@ -66,6 +74,16 @@ def admin_change_status_markup():
return markup
+def admin_pair_generation_markup():
+ markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
+ markup.add(force_pair_generation)
+ markup.add(stop_pair_generation)
+ markup.add(renew_pair_generation)
+ markup.add(change_pair_generation_date)
+ markup.add(go_back)
+ return markup
+
+
def admin_back_markup():
"""Кнопка назад"""
markup = ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
diff --git a/keyboards/user/defalt_markups.py b/keyboards/user/defalt_markups.py
index cd8d1fb..658857c 100644
--- a/keyboards/user/defalt_markups.py
+++ b/keyboards/user/defalt_markups.py
@@ -2,13 +2,13 @@
from data import ADMIN_TG_ID
-back_message = '👈 Назад'
-skip_message = '👉 Пропустить'
-all_right_message = '✅ Все верно'
-cancel_message = '🚫 Отменить'
-menu_message = '🏠 Меню'
-confirm_message = '✅ Да'
-reject_message = '❌ Нет'
+back_message = "👈 Назад"
+skip_message = "👉 Пропустить"
+all_right_message = "✅ Все верно"
+cancel_message = "🚫 Отменить"
+menu_message = "🏠 Меню"
+confirm_message = "✅ Да"
+reject_message = "❌ Нет"
edit_profile_message = "👩🏿🎨 Изменить Профиль"
my_profile_message = "Мой профиль"
my_status_message = "Мой статус"
@@ -26,7 +26,7 @@
turn_off_holidays = "Отключить"
back_to_menu = "Вернуться в меню"
my_pare_button = "Моя пара"
-back_to_main = 'Главное меню'
+back_to_main = "Главное меню"
def main_markup():
diff --git a/loader.py b/loader.py
index fbad553..0041ca9 100644
--- a/loader.py
+++ b/loader.py
@@ -1,4 +1,5 @@
import logging
+import os
from datetime import datetime
from logging.handlers import RotatingFileHandler
@@ -11,35 +12,38 @@
from controllerBD.models import create_tables
from data import config
-timezone = pytz.timezone('Etc/GMT-3')
+timezone = pytz.timezone("Etc/GMT-3")
def timetz(*args):
return datetime.now(timezone).timetuple()
-logger = logging.getLogger('main_logger')
+logger = logging.getLogger("main_logger")
-aiogram_logger = logging.getLogger('aio_logger')
+aiogram_logger = logging.getLogger("aio_logger")
-schedule_logger = logging.getLogger('schedule')
+schedule_logger = logging.getLogger("schedule")
logger.setLevel(logging.INFO)
aiogram_logger.setLevel(logging.INFO)
schedule_logger.setLevel(level=logging.DEBUG)
+if not (os.path.isdir("logs")):
+ os.mkdir("logs")
+
main_handler = RotatingFileHandler(
- 'logs/my_logger.log',
+ "logs/my_logger.log",
maxBytes=30000000,
backupCount=5,
)
aiogram_handler = RotatingFileHandler(
- 'logs/aiogram_logger.log',
+ "logs/aiogram_logger.log",
maxBytes=30000000,
backupCount=2,
)
schedule_handler = RotatingFileHandler(
- 'logs/schedule_logger.log',
+ "logs/schedule_logger.log",
maxBytes=30000000,
backupCount=2,
)
@@ -50,9 +54,10 @@ def timetz(*args):
schedule_logger.addHandler(schedule_handler)
formatter = logging.Formatter(
- fmt=('%(asctime)s.%(msecs)d %(levelname)s '
- '%(filename)s %(funcName)s %(message)s'),
- datefmt='%d-%m-%Y %H:%M:%S',
+ fmt=(
+ "%(asctime)s.%(msecs)d %(levelname)s " "%(filename)s %(funcName)s %(message)s"
+ ),
+ datefmt="%d-%m-%Y %H:%M:%S",
)
formatter.converter = timetz
diff --git a/main.py b/main.py
index 5ead731..b29fcb6 100644
--- a/main.py
+++ b/main.py
@@ -8,8 +8,7 @@
from handlers.admin.handlers import register_admin_handlers
from handlers.user.handlers import register_user_handlers
from handlers.user.help_texts import register_help_texts_handlers
-from handlers.user.holidays import (register_holidays_handlers,
- sheduled_check_holidays)
+from handlers.user.holidays import register_holidays_handlers, sheduled_check_holidays
from handlers.user.new_member import register_new_member_handler
from handlers.user.review_history import register_review_history_handler
from handlers.user.reviews import register_review_handlers
@@ -43,20 +42,19 @@ async def on_startup(_):
"""Выполняется во время запуска бота."""
loop = asyncio.get_event_loop()
loop.create_task(scheduler())
- message = 'Бот запущен'
+ message = "Бот запущен"
await send_message_to_admins(message)
logger.info(message)
async def on_shutdown(_):
"""Выполняется во время остановки бота."""
- message = 'Бот остановлен'
+ message = "Бот остановлен"
await send_message_to_admins(message)
logger.info(message)
-if __name__ == '__main__':
- executor.start_polling(dp, skip_updates=True,
- on_startup=on_startup,
- on_shutdown=on_shutdown
- )
+if __name__ == "__main__":
+ executor.start_polling(
+ dp, skip_updates=True, on_startup=on_startup, on_shutdown=on_shutdown
+ )
diff --git a/match_algoritm/MatchingHelper.py b/match_algoritm/MatchingHelper.py
index 80cf46f..253a7be 100644
--- a/match_algoritm/MatchingHelper.py
+++ b/match_algoritm/MatchingHelper.py
@@ -3,15 +3,19 @@
from controllerBD.db_loader import db_session
from controllerBD.models import UserMets, UserStatus
-from controllerBD.services import (send_message_to_admins,
- update_all_user_mets, update_mets)
+from controllerBD.services import (
+ send_message_to_admins,
+ update_all_user_mets,
+ update_mets,
+)
from handlers.user.check_message import check_message
from loader import bot, logger
from sendler.match_messages import send_match_messages
-class MachingHelper():
+class MachingHelper:
"""Класс - интерфейс алгоритма"""
+
vertex_conunt: int
edges_count: int
@@ -23,13 +27,16 @@ def prepare(self):
"""Подготовка алгоритма"""
logger.info("Начало подготовки работы алгоритма")
data_from_bd = {}
- active_users = db_session.query(UserStatus.id).\
- filter(UserStatus.status == 1).all()
+ active_users = (
+ db_session.query(UserStatus.id).filter(UserStatus.status == 1).all()
+ )
active_users = [i[0] for i in active_users]
for now_user in active_users:
- connected_user = db_session.query(UserMets.met_info).filter(
- UserMets.id == now_user
- ).first()[0]
+ connected_user = (
+ db_session.query(UserMets.met_info)
+ .filter(UserMets.id == now_user)
+ .first()[0]
+ )
connected_user = list(json.loads(connected_user).values())
data_from_bd[now_user] = connected_user
@@ -37,7 +44,8 @@ def prepare(self):
self.all_active = list(data_from_bd.keys())
for v in self.all_active:
adjacency_list[v] = [
- item for item in self.all_active if item not in data_from_bd[v]+[v]]
+ item for item in self.all_active if item not in data_from_bd[v] + [v]
+ ]
edges = []
for v in self.all_active:
for i in adjacency_list[v]:
@@ -69,7 +77,12 @@ async def send_and_write(self, t: dict):
def start(self):
"""Запуск алгоритма"""
logger.info("Начало работы алгоритма")
- subprocess.call(['./match_algoritm/matchingalogitm -f ./data/match_algoritm_data/input.txt --max'], shell=True)
+ subprocess.call(
+ [
+ "./match_algoritm/matchingalogitm -f ./data/match_algoritm_data/input.txt --max"
+ ],
+ shell=True,
+ )
res = []
with open("./data/match_algoritm_data/output.txt", "r") as text:
res = text.readlines()
@@ -83,18 +96,20 @@ def start(self):
matches[i] = None
self.matchings = matches
logger.info("Завершение работы алгоритма")
- logger.info(f'пары {matches}')
+ logger.info(f"пары {matches}")
return matches
async def start_algoritm():
"""Запуск алгоритма распределения"""
- await send_message_to_admins('Начинаем распределение')
+ await send_message_to_admins("Начинаем распределение")
await check_message()
mc = MachingHelper()
res = mc.start()
- await send_message_to_admins(f'Количество пар: {len(res)}.\n'
- f'Начинаем отправку сообщений.')
+ await send_message_to_admins(
+ f"Количество пар: {len(res)}.\n" f"Начинаем отправку сообщений."
+ )
await mc.send_and_write(res)
- await send_message_to_admins('Сообщения пользователям отправлены.\n'
- 'Распределение завершено.')
+ await send_message_to_admins(
+ "Сообщения пользователям отправлены.\n" "Распределение завершено."
+ )
diff --git a/match_algoritm/__init__.py b/match_algoritm/__init__.py
index f7bae82..1f468f0 100644
--- a/match_algoritm/__init__.py
+++ b/match_algoritm/__init__.py
@@ -1 +1 @@
-from .MatchingHelper import *
\ No newline at end of file
+from .MatchingHelper import *
diff --git a/match_algoritm/blossom.py b/match_algoritm/blossom.py
index 28e3956..fcd7a6d 100644
--- a/match_algoritm/blossom.py
+++ b/match_algoritm/blossom.py
@@ -3,13 +3,15 @@ def get_maximum_matching(graph, matching, i):
print(i)
augmenting_path = get_augmenting_path(graph, matching)
if len(augmenting_path) > 0:
- return get_maximum_matching(graph, matching.augment(augmenting_path), i+1)
+ return get_maximum_matching(graph, matching.augment(augmenting_path), i + 1)
else:
return matching
# https://en.wikipedia.org/wiki/Blossom_algorithm
"""Получить наибольший поток в графе"""
+
+
def get_augmenting_path(graph, matching):
forest = Forest()
graph.unmark_all_edges()
@@ -31,14 +33,14 @@ def get_augmenting_path(graph, matching):
else:
if forest.get_root(v) != forest.get_root(w):
path = forest.get_path_from_root_to(
- v) + forest.get_path_to_root_from(w)
+ v
+ ) + forest.get_path_to_root_from(w)
return path
else:
blossom = forest.get_blossom(v, w)
graph_prime = graph.contract(blossom)
matching_prime = matching.contract(blossom)
- path_prime = get_augmenting_path(
- graph_prime, matching_prime)
+ path_prime = get_augmenting_path(graph_prime, matching_prime)
path = graph.lift_path(path_prime, blossom)
return path
graph.mark_edge(e)
@@ -61,13 +63,15 @@ def __init__(self):
def __assert_representation(self):
for t in self.adjacency:
- assert len(
- self.adjacency[t]) > 0, 'Если вершина существует в матрице смежности, она должна иметь хотя бы одного соседа.'
+ assert (
+ len(self.adjacency[t]) > 0
+ ), "Если вершина существует в матрице смежности, она должна иметь хотя бы одного соседа."
for u in self.adjacency[t]:
self.__assert_edge_exists((t, u))
for t in self.unmarked_adjacency:
- assert len(
- self.unmarked_adjacency[t]) > 0, 'Если вершина существует в непомеченной матрице смежности, она должна иметь хотя бы одного соседа.'
+ assert (
+ len(self.unmarked_adjacency[t]) > 0
+ ), "Если вершина существует в непомеченной матрице смежности, она должна иметь хотя бы одного соседа."
for u in self.unmarked_adjacency[t]:
self.__assert_edge_exists((t, u))
self.__assert_unmarked_edge_exists((t, u))
@@ -75,38 +79,52 @@ def __assert_representation(self):
def __assert_edge_exists(self, edge):
v, w = edge
assert (v in self.adjacency) and (
- w in self.adjacency[v]), 'Край должен существовать в матрице смежности'
+ w in self.adjacency[v]
+ ), "Край должен существовать в матрице смежности"
assert (w in self.adjacency) and (
- v in self.adjacency[w]), 'Взаимное ребро должно существовать в матрице смежности'
+ v in self.adjacency[w]
+ ), "Взаимное ребро должно существовать в матрице смежности"
def __assert_edge_does_not_exist(self, edge):
v, w = edge
print(v, w)
assert (v not in self.adjacency) or (
- w not in self.adjacency[v]), 'Ребро не должно существовать в матрице смежности'
+ w not in self.adjacency[v]
+ ), "Ребро не должно существовать в матрице смежности"
assert (w not in self.adjacency) or (
- v not in self.adjacency[w]), 'Взаимное ребро не должно существовать в матрице смежности'
+ v not in self.adjacency[w]
+ ), "Взаимное ребро не должно существовать в матрице смежности"
def __assert_unmarked_edge_exists(self, edge):
v, w = edge
assert (v in self.unmarked_adjacency) and (
- w in self.unmarked_adjacency[v]), 'Ребро должно существовать в непомеченной матрице смежности'
+ w in self.unmarked_adjacency[v]
+ ), "Ребро должно существовать в непомеченной матрице смежности"
assert (w in self.unmarked_adjacency) and (
- v in self.unmarked_adjacency[w]), 'Взаимное ребро должно существовать в непомеченной матрице смежности'
+ v in self.unmarked_adjacency[w]
+ ), "Взаимное ребро должно существовать в непомеченной матрице смежности"
def __assert_unmarked_edge_does_not_exist(self, edge):
v, w = edge
assert (v not in self.unmarked_adjacency) or (
- w not in self.unmarked_adjacency[v]), 'Ребро не должно существовать в непомеченной матрице смежности'
+ w not in self.unmarked_adjacency[v]
+ ), "Ребро не должно существовать в непомеченной матрице смежности"
assert (w not in self.unmarked_adjacency) or (
- v not in self.unmarked_adjacency[w]), 'Обратное ребро не должно существовать в непомеченной матрице смежности'
+ v not in self.unmarked_adjacency[w]
+ ), "Обратное ребро не должно существовать в непомеченной матрице смежности"
def __assert_vertice_exists(self, vertice):
- assert vertice in self.adjacency, 'Вершина должна существовать в матрице смежности'
+ assert (
+ vertice in self.adjacency
+ ), "Вершина должна существовать в матрице смежности"
def __assert_vertice_does_not_exist(self, vertice):
- assert vertice not in self.adjacency, 'Вершина не должна существовать в матрице смежности'
- assert vertice not in self.unmarked_adjacency, 'Вершина не должна существовать в непомеченной матрице смежности'
+ assert (
+ vertice not in self.adjacency
+ ), "Вершина не должна существовать в матрице смежности"
+ assert (
+ vertice not in self.unmarked_adjacency
+ ), "Вершина не должна существовать в непомеченной матрице смежности"
"""Копировать граф"""
@@ -219,7 +237,7 @@ def lift_path(self, path, blossom):
if len(path) == 0:
return path
if len(path) == 1:
- assert False, 'Путь не может содержать ровно одну вершину'
+ assert False, "Путь не может содержать ровно одну вершину"
if path[0] == blossom.get_id():
############################################################################################################
@@ -237,7 +255,9 @@ def lift_path(self, path, blossom):
blossom_path.append(v)
if (w in self.adjacency[v]) and (len(blossom_path) % 2 != 0):
return blossom_path + path[1:]
- assert False, 'At least one path with even edges must exist through the blossom'
+ assert (
+ False
+ ), "At least one path with even edges must exist through the blossom"
if path[-1] == blossom.get_id():
############################################################################################################
@@ -254,10 +274,12 @@ def lift_path(self, path, blossom):
blossom_path.append(v)
if (u in self.adjacency[v]) and (len(blossom_path) % 2 != 0):
return path[:-1] + list(reversed(blossom_path))
- assert False, 'At least one path with even edges must exist through the blossom'
+ assert (
+ False
+ ), "At least one path with even edges must exist through the blossom"
for i, v in enumerate(path):
if v == blossom.get_id():
- u, w = path[i-1], path[i+1]
+ u, w = path[i - 1], path[i + 1]
if u in self.adjacency[blossom.get_base()]:
####################################################################################################
@@ -268,13 +290,15 @@ def lift_path(self, path, blossom):
for v in blossom.traverse_left():
blossom_path.append(v)
if (w in self.adjacency[v]) and (len(blossom_path) % 2 != 0):
- return path[:i] + blossom_path + path[i+1:]
+ return path[:i] + blossom_path + path[i + 1 :]
blossom_path = []
for v in blossom.traverse_right():
blossom_path.append(v)
if (w in self.adjacency[v]) and (len(blossom_path) % 2 != 0):
- return path[:i] + blossom_path + path[i+1:]
- assert False, 'At least one path with even edges must exist through the blossom'
+ return path[:i] + blossom_path + path[i + 1 :]
+ assert (
+ False
+ ), "At least one path with even edges must exist through the blossom"
elif w in self.adjacency[blossom.get_base()]:
####################################################################################################
@@ -285,15 +309,23 @@ def lift_path(self, path, blossom):
for v in blossom.traverse_left():
blossom_path.append(v)
if (u in self.adjacency[v]) and (len(blossom_path) % 2 != 0):
- return path[:i] + list(reversed(blossom_path)) + path[i+1:]
+ return (
+ path[:i] + list(reversed(blossom_path)) + path[i + 1 :]
+ )
blossom_path = []
for v in blossom.traverse_right():
blossom_path.append(v)
if (u in self.adjacency[v]) and (len(blossom_path) % 2 != 0):
- return path[:i] + list(reversed(blossom_path)) + path[i+1:]
- assert False, 'Через цветок должен существовать хотя бы один путь с ровными краями.'
+ return (
+ path[:i] + list(reversed(blossom_path)) + path[i + 1 :]
+ )
+ assert (
+ False
+ ), "Через цветок должен существовать хотя бы один путь с ровными краями."
else:
- assert False, 'Ровно одна сторона пути должна быть инцидентна основанию цветка.'
+ assert (
+ False
+ ), "Ровно одна сторона пути должна быть инцидентна основанию цветка."
return path
@@ -319,43 +351,61 @@ def __assert_representation(self):
def __assert_edge_exists(self, edge):
v, w = edge
assert (v in self.adjacency) and (
- w in self.adjacency[v]), 'Край должен существовать в матрице смежности'
+ w in self.adjacency[v]
+ ), "Край должен существовать в матрице смежности"
assert (w in self.adjacency) and (
- v in self.adjacency[w]), 'Взаимное ребро должно существовать в матрице смежности'
- assert edge in self.edges, 'Край должен существовать в наборе ребер'
+ v in self.adjacency[w]
+ ), "Взаимное ребро должно существовать в матрице смежности"
+ assert edge in self.edges, "Край должен существовать в наборе ребер"
def __assert_edge_does_not_exist(self, edge):
v, w = edge
assert (v not in self.adjacency) or (
- w not in self.adjacency[v]), 'Ребро не должно существовать в матрице смежности'
+ w not in self.adjacency[v]
+ ), "Ребро не должно существовать в матрице смежности"
assert (w not in self.adjacency) or (
- v not in self.adjacency[w]), 'Взаимное ребро не должно существовать в матрице смежности'
- assert edge not in self.edges, 'Ребро не должно существовать в наборе ребер'
+ v not in self.adjacency[w]
+ ), "Взаимное ребро не должно существовать в матрице смежности"
+ assert edge not in self.edges, "Ребро не должно существовать в наборе ребер"
def __assert_vertice_is_exposed(self, vertice):
- assert vertice in self.adjacency, 'Вершина должна существовать в матрице смежности'
- assert vertice in self.exposed_vertices, 'Вершина должна существовать в наборе вершин.'
- assert len(self.adjacency[vertice]
- ) == 0, 'Вершина не должна иметь соседей'
+ assert (
+ vertice in self.adjacency
+ ), "Вершина должна существовать в матрице смежности"
+ assert (
+ vertice in self.exposed_vertices
+ ), "Вершина должна существовать в наборе вершин."
+ assert len(self.adjacency[vertice]) == 0, "Вершина не должна иметь соседей"
def __assert_vertice_is_not_exposed(self, vertice):
- assert vertice in self.adjacency, 'Вершина должна существовать в матрице смежности'
- assert vertice not in self.exposed_vertices, 'Вершина должна существовать в наборе вершин'
- assert len(
- self.adjacency[vertice]) == 1, 'Вершина должна иметь ровно одного соседа'
+ assert (
+ vertice in self.adjacency
+ ), "Вершина должна существовать в матрице смежности"
+ assert (
+ vertice not in self.exposed_vertices
+ ), "Вершина должна существовать в наборе вершин"
+ assert (
+ len(self.adjacency[vertice]) == 1
+ ), "Вершина должна иметь ровно одного соседа"
def __assert_vertice_exists(self, vertice):
- assert vertice in self.adjacency, 'Вершина должна существовать в матрице смежности'
+ assert (
+ vertice in self.adjacency
+ ), "Вершина должна существовать в матрице смежности"
if vertice in self.exposed_vertices:
- assert len(
- self.adjacency[vertice]) == 0, 'Если вершина открыта, у нее не должно быть соседей'
+ assert (
+ len(self.adjacency[vertice]) == 0
+ ), "Если вершина открыта, у нее не должно быть соседей"
else:
- assert len(
- self.adjacency[vertice]) == 1, 'Если вершина не открыта, она должна иметь ровно одного соседа.'
+ assert (
+ len(self.adjacency[vertice]) == 1
+ ), "Если вершина не открыта, она должна иметь ровно одного соседа."
def __assert_vertice_does_not_exist(self, vertice):
- assert vertice not in self.adjacency, 'Вершина не должна существовать в матрице смежности'
- assert vertice not in self.exposed_vertices, 'Вершина не должна быть открыта'
+ assert (
+ vertice not in self.adjacency
+ ), "Вершина не должна существовать в матрице смежности"
+ assert vertice not in self.exposed_vertices, "Вершина не должна быть открыта"
def copy(self):
self.__assert_representation()
@@ -377,8 +427,8 @@ def augment(self, path):
matching.__assert_vertice_is_exposed(path[-1])
matching.exposed_vertices.remove(path[0])
matching.exposed_vertices.remove(path[-1])
- for i in range(len(path)-1):
- v, w = path[i], path[i+1]
+ for i in range(len(path) - 1):
+ v, w = path[i], path[i + 1]
edge = tuple(sorted((v, w)))
if edge in matching.edges:
matching.__assert_edge_exists(edge)
@@ -456,24 +506,34 @@ def __init__(self):
def __assert_representation(self):
for vertice in self.roots:
self.__assert_vertice_exists(vertice)
- assert self.roots.keys() == self.distances_to_root.keys(
- ), 'Корни и расстояния до корня должны иметь одинаковые ключи'
- assert self.roots.keys() == self.parents.keys(
- ), 'Корни и родители mut имеют одинаковые ключи'
+ assert (
+ self.roots.keys() == self.distances_to_root.keys()
+ ), "Корни и расстояния до корня должны иметь одинаковые ключи"
+ assert (
+ self.roots.keys() == self.parents.keys()
+ ), "Корни и родители mut имеют одинаковые ключи"
for vertice in self.unmarked_even_vertices:
self.__assert_vertice_exists(vertice)
- assert self.distances_to_root[vertice] % 2 == 0, 'Неотмеченная четная вершина должна иметь четное расстояние до корня'
+ assert (
+ self.distances_to_root[vertice] % 2 == 0
+ ), "Неотмеченная четная вершина должна иметь четное расстояние до корня"
def __assert_vertice_exists(self, vertice):
- assert vertice in self.roots, 'Вершина должна иметь корень'
- assert vertice in self.distances_to_root, 'Вершина должна иметь расстояние до корня'
- assert vertice in self.parents, 'Вершина должна иметь родителя'
+ assert vertice in self.roots, "Вершина должна иметь корень"
+ assert (
+ vertice in self.distances_to_root
+ ), "Вершина должна иметь расстояние до корня"
+ assert vertice in self.parents, "Вершина должна иметь родителя"
def __assert_vertice_does_not_exist(self, vertice):
- assert vertice not in self.roots, 'Вершина не должна иметь корня'
- assert vertice not in self.distances_to_root, 'Вершина не должна иметь расстояние до корня'
- assert vertice not in self.unmarked_even_vertices, 'Вершина не должна существовать в наборе непомеченных четных вершин.'
- assert vertice not in self.parents, 'Вершина не должна иметь родителя'
+ assert vertice not in self.roots, "Вершина не должна иметь корня"
+ assert (
+ vertice not in self.distances_to_root
+ ), "Вершина не должна иметь расстояние до корня"
+ assert (
+ vertice not in self.unmarked_even_vertices
+ ), "Вершина не должна существовать в наборе непомеченных четных вершин."
+ assert vertice not in self.parents, "Вершина не должна иметь родителя"
def add_singleton_tree(self, vertice):
self.__assert_vertice_does_not_exist(vertice)
@@ -493,7 +553,9 @@ def get_unmarked_even_vertice(self):
def mark_vertice(self, vertice):
self.__assert_vertice_exists(vertice)
if self.distances_to_root[vertice] % 2 == 0:
- assert vertice in self.unmarked_even_vertices, 'Если вершина имеет четное расстояние до корня, она должна существовать в наборе непомеченных четных вершин.'
+ assert (
+ vertice in self.unmarked_even_vertices
+ ), "Если вершина имеет четное расстояние до корня, она должна существовать в наборе непомеченных четных вершин."
self.unmarked_even_vertices.remove(vertice)
self.__assert_representation()
@@ -520,7 +582,9 @@ def add_edge(self, edge):
self.unmarked_even_vertices.add(w)
self.parents[w] = v
else:
- assert False, 'По крайней мере, одна инцидентная вершина не должна существовать'
+ assert (
+ False
+ ), "По крайней мере, одна инцидентная вершина не должна существовать"
self.__assert_representation()
def get_distance_to_root(self, vertice):
@@ -548,7 +612,8 @@ def get_path_to_root_from(self, vertice):
parent = self.parents[parent]
path.append(root)
assert len(set(path)) == len(
- path), 'Путь к корню не должен содержать повторяющихся вершин.'
+ path
+ ), "Путь к корню не должен содержать повторяющихся вершин."
return path
def get_blossom(self, v, w):
@@ -564,19 +629,22 @@ def get_blossom(self, v, w):
break
else:
v_blossom_vertices.append(u)
- assert common_ancestor is not None, 'Должен существовать общий предок'
+ assert common_ancestor is not None, "Должен существовать общий предок"
w_blossom_vertices = []
for u in w_path:
if u == common_ancestor:
break
else:
w_blossom_vertices.append(u)
- blossom_vertices = [common_ancestor] + \
- list(reversed(v_blossom_vertices)) + w_blossom_vertices
+ blossom_vertices = (
+ [common_ancestor] + list(reversed(v_blossom_vertices)) + w_blossom_vertices
+ )
assert len(set(blossom_vertices)) == len(
- blossom_vertices), 'Цветок не должен содержать повторяющихся вершин.'
- assert len(
- blossom_vertices) % 2 != 0, 'Цветок должен содержать нечетное количество вершин'
+ blossom_vertices
+ ), "Цветок не должен содержать повторяющихся вершин."
+ assert (
+ len(blossom_vertices) % 2 != 0
+ ), "Цветок должен содержать нечетное количество вершин"
blossom = Blossom(blossom_vertices, common_ancestor)
return blossom
@@ -593,11 +661,13 @@ def __init__(self, vertices, base):
self.__assert_representation()
def __assert_representation(self):
- assert self.vertices[0] == self.base, 'Цветок должен начинаться с базовой вершины'
- assert len(
- self.vertices) % 2 != 0, 'Цветок должен иметь нечетное количество вершин'
- assert len(
- self.vertices) >= 3, 'Цветок должен иметь не менее трех вершин.'
+ assert (
+ self.vertices[0] == self.base
+ ), "Цветок должен начинаться с базовой вершины"
+ assert (
+ len(self.vertices) % 2 != 0
+ ), "Цветок должен иметь нечетное количество вершин"
+ assert len(self.vertices) >= 3, "Цветок должен иметь не менее трех вершин."
def get_id(self):
self.__assert_representation()
diff --git a/match_algoritm/test_blossom.py b/match_algoritm/test_blossom.py
index da712bd..a752e93 100644
--- a/match_algoritm/test_blossom.py
+++ b/match_algoritm/test_blossom.py
@@ -1,5 +1,8 @@
-import blossom
from datetime import datetime
+
+import blossom
+
+
def test():
start_time = datetime.now()
graph = blossom.Graph()
@@ -9,11 +12,11 @@ def test():
print(datetime.now())
matching = blossom.Matching()
matching.add_vertices(graph.get_vertices())
- actual = sorted(blossom.get_maximum_matching(graph, matching,0).edges)
+ actual = sorted(blossom.get_maximum_matching(graph, matching, 0).edges)
print("end in", datetime.now() - start_time)
- with open("./new.txt","w") as text:
+ with open("./new.txt", "w") as text:
text.write(str(actual))
-if __name__ == '__main__':
- test()
+if __name__ == "__main__":
+ test()
diff --git a/match_algoritm/tests/create_test_graph.py b/match_algoritm/tests/create_test_graph.py
index 72bbe28..37ddf04 100644
--- a/match_algoritm/tests/create_test_graph.py
+++ b/match_algoritm/tests/create_test_graph.py
@@ -1,4 +1,5 @@
import random
+
res = {}
res2 = []
n = 5000
@@ -6,27 +7,26 @@
for i in range(n):
res[i] = []
for i in range(q):
- first = random.randint(0,n-1)
- second = random.randint(0,n-1)
+ first = random.randint(0, n - 1)
+ second = random.randint(0, n - 1)
while first == second:
- first = random.randint(0,n-1)
- second = random.randint(0,n-1)
- t = (max(first,second), min(first,second))
+ first = random.randint(0, n - 1)
+ second = random.randint(0, n - 1)
+ t = (max(first, second), min(first, second))
res2.append(t)
res2 = list(set(res2))
-with open("../input.txt","w") as text:
+with open("../input.txt", "w") as text:
res = list(sorted(res2))
st = ""
- st+=f"{n}\n{q}\n"
+ st += f"{n}\n{q}\n"
for i in res:
- st += str(i[0])+ " "+ str(i[1])+" 0\n"
+ st += str(i[0]) + " " + str(i[1]) + " 0\n"
text.write(st)
# for https://programforyou.ru/graph-redactor
-with open("./graph.txt","w") as text:
+with open("./graph.txt", "w") as text:
df = ""
for i in res:
- df += str(i[0])+ " -- "+ str(i[1])+"\n"
+ df += str(i[0]) + " -- " + str(i[1]) + "\n"
text.write(df)
-
diff --git a/migrations/env.py b/migrations/env.py
new file mode 100644
index 0000000..4230bff
--- /dev/null
+++ b/migrations/env.py
@@ -0,0 +1,81 @@
+from logging.config import fileConfig
+
+from alembic import context
+from sqlalchemy import engine_from_config, pool
+
+from controllerBD.db_loader import Base
+from controllerBD.models import *
+from data.config import DB_DSN
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+# settings = Settings()
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+if config.config_file_name is not None:
+ fileConfig(config.config_file_name)
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+target_metadata = Base.metadata
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+
+def run_migrations_offline():
+ """Run migrations in 'offline' mode.
+
+ This configures the context with just a URL
+ and not an Engine, though an Engine is acceptable
+ here as well. By skipping the Engine creation
+ we don't even need a DBAPI to be available.
+
+ Calls to context.execute() here emit the given string to the
+ script output.
+
+ """
+ url = config.get_main_option("sqlalchemy.url")
+ context.configure(
+ url=url,
+ target_metadata=target_metadata,
+ literal_binds=True,
+ dialect_opts={"paramstyle": "named"},
+ )
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+def run_migrations_online():
+ """Run migrations in 'online' mode.
+
+ In this scenario we need to create an Engine
+ and associate a connection with the context.
+
+ """
+ configuration = config.get_section(config.config_ini_section)
+ configuration["sqlalchemy.url"] = DB_DSN
+ connectable = engine_from_config(
+ configuration,
+ prefix="sqlalchemy.",
+ poolclass=pool.NullPool,
+ )
+
+ with connectable.connect() as connection:
+ context.configure(connection=connection, target_metadata=target_metadata)
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+if context.is_offline_mode():
+ run_migrations_offline()
+else:
+ run_migrations_online()
diff --git a/migrations/script.py.mako b/migrations/script.py.mako
new file mode 100644
index 0000000..1e4564e
--- /dev/null
+++ b/migrations/script.py.mako
@@ -0,0 +1,24 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision | comma,n}
+Create Date: ${create_date}
+
+"""
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
+
+# revision identifiers, used by Alembic.
+revision = ${repr(up_revision)}
+down_revision = ${repr(down_revision)}
+branch_labels = ${repr(branch_labels)}
+depends_on = ${repr(depends_on)}
+
+
+def upgrade():
+ ${upgrades if upgrades else "pass"}
+
+
+def downgrade():
+ ${downgrades if downgrades else "pass"}
\ No newline at end of file
diff --git a/migrations/versions/cbba5d89b3ac_init.py b/migrations/versions/cbba5d89b3ac_init.py
new file mode 100644
index 0000000..67902f7
--- /dev/null
+++ b/migrations/versions/cbba5d89b3ac_init.py
@@ -0,0 +1,153 @@
+"""init
+
+Revision ID: cbba5d89b3ac
+Revises:
+Create Date: 2024-12-11 10:34:50.845505
+
+"""
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = "cbba5d89b3ac"
+down_revision = None
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table(
+ "genders",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("gender_name", sa.String(length=100), nullable=False),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("gender_name"),
+ sa.UniqueConstraint("id"),
+ )
+ op.create_table(
+ "user_info",
+ sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column("teleg_id", sa.Integer(), nullable=False),
+ sa.Column("name", sa.String(length=100), nullable=False),
+ sa.Column("birthday", sa.String(), nullable=False),
+ sa.Column("about", sa.String(length=500), nullable=False),
+ sa.Column("gender", sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["gender"],
+ ["genders.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("teleg_id"),
+ )
+ op.create_table(
+ "ban_list",
+ sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column("banned_user_id", sa.Integer(), nullable=False),
+ sa.Column("ban_status", sa.Integer(), nullable=False),
+ sa.Column("date_of_ban", sa.String(), nullable=False),
+ sa.Column("comment_to_ban", sa.String(length=500), nullable=False),
+ sa.Column("date_of_unban", sa.String(), nullable=False),
+ sa.Column("comment_to_unban", sa.String(length=500), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["banned_user_id"],
+ ["user_info.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ op.create_table(
+ "holidays_status",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("status", sa.Integer(), nullable=False),
+ sa.Column("till_date", sa.String(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["id"],
+ ["user_info.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ op.create_table(
+ "met_info",
+ sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column("first_user_id", sa.Integer(), nullable=False),
+ sa.Column("second_user_id", sa.Integer(), nullable=False),
+ sa.Column("date", sa.String(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["first_user_id"],
+ ["user_info.id"],
+ ),
+ sa.ForeignKeyConstraint(
+ ["second_user_id"],
+ ["user_info.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ op.create_table(
+ "tg_usernames",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("username", sa.String(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["id"],
+ ["user_info.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ op.create_table(
+ "user_mets",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("met_info", sa.String(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["id"],
+ ["user_info.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ op.create_table(
+ "user_status",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("status", sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["id"],
+ ["user_info.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ op.create_table(
+ "mets_reviews",
+ sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column("met_id", sa.Integer(), nullable=False),
+ sa.Column("who_id", sa.Integer(), nullable=False),
+ sa.Column("about_whom_id", sa.Integer(), nullable=False),
+ sa.Column("grade", sa.Integer(), nullable=False),
+ sa.Column("comment", sa.String(length=500), nullable=True),
+ sa.Column("date_of_comment", sa.String(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["about_whom_id"],
+ ["user_info.id"],
+ ),
+ sa.ForeignKeyConstraint(
+ ["met_id"],
+ ["met_info.id"],
+ ),
+ sa.ForeignKeyConstraint(
+ ["who_id"],
+ ["user_info.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table("mets_reviews")
+ op.drop_table("user_status")
+ op.drop_table("user_mets")
+ op.drop_table("tg_usernames")
+ op.drop_table("met_info")
+ op.drop_table("holidays_status")
+ op.drop_table("ban_list")
+ op.drop_table("user_info")
+ op.drop_table("genders")
+ # ### end Alembic commands ###
diff --git a/requirements.dev.txt b/requirements.dev.txt
new file mode 100644
index 0000000..3cfb3fc
--- /dev/null
+++ b/requirements.dev.txt
@@ -0,0 +1,2 @@
+black
+isort
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index b4525fb..b2ccc01 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -19,3 +19,5 @@ pytz==2022.7
SQLAlchemy==2.0.4
typing_extensions==4.5.0
yarl==1.8.2
+psycopg2
+alembic
diff --git a/sendler/__init__.py b/sendler/__init__.py
index a33510d..0a47454 100644
--- a/sendler/__init__.py
+++ b/sendler/__init__.py
@@ -1 +1 @@
-from .match_messages import *
\ No newline at end of file
+from .match_messages import *
diff --git a/sendler/match_messages.py b/sendler/match_messages.py
index f82707a..c7a0f1c 100644
--- a/sendler/match_messages.py
+++ b/sendler/match_messages.py
@@ -3,9 +3,13 @@
from aiogram import Bot
from controllerBD.db_loader import db_session
-from controllerBD.models import Users, Gender
-from controllerBD.services import get_defaulf_pare_base_id, update_mets, \
- update_all_user_mets, get_tg_username_from_db_by_teleg_id
+from controllerBD.models import Gender, Users
+from controllerBD.services import (
+ get_defaulf_pare_base_id,
+ get_tg_username_from_db_by_teleg_id,
+ update_all_user_mets,
+ update_mets,
+)
from handlers.user.work_with_date import date_from_db_to_message
from keyboards.user import help_texts_markup
from loader import logger
@@ -17,8 +21,7 @@ async def send_match_messages(match_info: dict, bot: Bot):
await sleep(0.1)
users_info = pare_users_query(match)
if not users_info:
- logger.error(f'Не удалось получить информацию '
- f'из БД для пары {match}')
+ logger.error(f"Не удалось получить информацию " f"из БД для пары {match}")
continue
elif len(users_info) == 2:
first_user = users_info[0]
@@ -28,31 +31,47 @@ async def send_match_messages(match_info: dict, bot: Bot):
try:
first_message = make_message(first_user)
except Exception as error:
- logger.error(f'Не удалось сформировать сообщение о '
- f'пользователе {first_user}. Ошибка {error}')
+ logger.error(
+ f"Не удалось сформировать сообщение о "
+ f"пользователе {first_user}. Ошибка {error}"
+ )
try:
second_message = make_message(second_user)
except Exception as error:
- logger.error(f'Не удалось сформировать сообщение о '
- f'пользователе {second_user}. Ошибка {error}')
+ logger.error(
+ f"Не удалось сформировать сообщение о "
+ f"пользователе {second_user}. Ошибка {error}"
+ )
try:
- await bot.send_message(second_user_id, first_message,
- parse_mode="HTML",
- reply_markup=help_texts_markup())
- logger.info(f'Сообщение для пользователя {second_user_id} '
- f'отправлено')
+ await bot.send_message(
+ second_user_id,
+ first_message,
+ parse_mode="HTML",
+ reply_markup=help_texts_markup(),
+ )
+ logger.info(
+ f"Сообщение для пользователя {second_user_id} " f"отправлено"
+ )
except Exception as error:
- logger.error(f'Сообщение для пользователя {second_user_id} '
- f'не отправлено. Ошибка {error}')
+ logger.error(
+ f"Сообщение для пользователя {second_user_id} "
+ f"не отправлено. Ошибка {error}"
+ )
try:
- await bot.send_message(first_user_id, second_message,
- parse_mode="HTML",
- reply_markup=help_texts_markup())
- logger.info(f'Сообщение для пользователя {first_user_id} '
- f'отправлено')
+ await bot.send_message(
+ first_user_id,
+ second_message,
+ parse_mode="HTML",
+ reply_markup=help_texts_markup(),
+ )
+ logger.info(
+ f"Сообщение для пользователя {first_user_id} " f"отправлено"
+ )
except Exception as error:
- logger.error(f'Сообщение для пользователя {first_user_id} '
- f'не отправлено. Ошибка {error}')
+ logger.error(
+ f"Сообщение для пользователя {first_user_id} "
+ f"не отправлено. Ошибка {error}"
+ )
else:
fail_user = users_info[0]
fail_user_db_id = fail_user[0]
@@ -78,23 +97,25 @@ def make_message(user_info: tuple) -> str:
user_gender = user_info[5]
user_tg_username = get_tg_username_from_db_by_teleg_id(user_id)
- base_message = (f'На этой неделе твоя пара для кофе: '
- f'{user_name}')
- tg_username_message = (f'@{user_tg_username}')
- birth_day_message = f'Дата рождения: {user_birthday}'
- about_message = f'Информация: {user_about}'
- gender_message = f'Пол: {user_gender}'
+ base_message = (
+ f"На этой неделе твоя пара для кофе: "
+ f'{user_name}'
+ )
+ tg_username_message = f"@{user_tg_username}"
+ birth_day_message = f"Дата рождения: {user_birthday}"
+ about_message = f"Информация: {user_about}"
+ gender_message = f"Пол: {user_gender}"
row_message_list = [base_message]
if user_tg_username:
row_message_list.append(tg_username_message)
- if user_birthday != 'Не указано':
+ if user_birthday != "Не указано":
row_message_list.append(birth_day_message)
- if user_about != 'Не указано':
+ if user_about != "Не указано":
row_message_list.append(about_message)
- if user_gender != 'Не указано':
+ if user_gender != "Не указано":
row_message_list.append(gender_message)
- message = '\n'.join(row_message_list)
+ message = "\n".join(row_message_list)
return message
@@ -102,14 +123,19 @@ def make_message(user_info: tuple) -> str:
def pare_users_query(pare: tuple):
"""Запрашивает информацию по паре юзеров из базы."""
try:
- result = db_session.query(
- Users.id,
- Users.teleg_id,
- Users.name,
- Users.birthday,
- Users.about,
- Gender.gender_name
- ).join(Gender).filter(Users.id.in_(pare)).all()
+ result = (
+ db_session.query(
+ Users.id,
+ Users.teleg_id,
+ Users.name,
+ Users.birthday,
+ Users.about,
+ Gender.gender_name,
+ )
+ .join(Gender)
+ .filter(Users.id.in_(pare))
+ .all()
+ )
except Exception:
result = None
finally:
diff --git a/states/__init__.py b/states/__init__.py
index 8950754..38eb373 100644
--- a/states/__init__.py
+++ b/states/__init__.py
@@ -1 +1 @@
-from .states import *
\ No newline at end of file
+from .states import *
diff --git a/states/states.py b/states/states.py
index 48ad5d9..c0e4795 100644
--- a/states/states.py
+++ b/states/states.py
@@ -3,6 +3,7 @@
class UserData(StatesGroup):
"""Машина состояний пользователя"""
+
start = State()
name = State()
birthday = State()
@@ -14,6 +15,7 @@ class UserData(StatesGroup):
class AdminData(StatesGroup):
"""Машина состояний админа"""
+
start = State()
user_ban = State()
comment_to_ban = State()