Skip to content

Commit 3359b85

Browse files
authored
Группы как сущность (#30)
## Изменения - Группы выделены в отдельную сущность и созданы подтипы для групп тг/вк - Добавлен create_ts для вебхук стоража - Группы тг теперь создаются автоматически
1 parent d5e4f84 commit 3359b85

21 files changed

+377
-73
lines changed

.github/workflows/checks.yml

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Python tests
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
test:
8+
name: Unit tests
9+
runs-on: ubuntu-latest
10+
services:
11+
postgres:
12+
image: postgres:15
13+
env:
14+
POSTGRES_HOST_AUTH_METHOD: trust
15+
options: >-
16+
--health-cmd pg_isready
17+
--health-interval 10s
18+
--health-timeout 5s
19+
--health-retries 5
20+
-p 5432:5432
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
- uses: actions/setup-python@v4
25+
with:
26+
python-version: "3.11"
27+
- name: Install dependencies
28+
run: |
29+
python -m ensurepip
30+
python -m pip install --upgrade pip
31+
pip install --upgrade -r requirements.txt -r requirements.dev.txt
32+
- name: Migrate DB
33+
run: |
34+
DB_DSN=postgresql://postgres@localhost:5432/postgres alembic upgrade head
35+
- name: Build coverage file
36+
id: pytest
37+
run: |
38+
DB_DSN=postgresql://postgres@localhost:5432/postgres pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=services_backend tests/ | tee pytest-coverage.txt
39+
exit ${PIPESTATUS[0]}
40+
- name: Print report
41+
if: always()
42+
run: |
43+
cat pytest-coverage.txt
44+
- name: Pytest coverage comment
45+
uses: MishaKav/pytest-coverage-comment@main
46+
with:
47+
pytest-coverage-path: ./pytest-coverage.txt
48+
title: Coverage Report
49+
badge-title: Code Coverage
50+
hide-badge: false
51+
hide-report: false
52+
create-new-comment: false
53+
hide-comment: false
54+
report-only-changed-files: false
55+
remove-link-from-badge: false
56+
junitxml-path: ./pytest.xml
57+
junitxml-title: Summary
58+
- name: Fail on pytest errors
59+
if: steps.pytest.outcome == 'failure'
60+
run: exit 1
61+
62+
linting:
63+
runs-on: ubuntu-latest
64+
steps:
65+
- uses: actions/checkout@v4
66+
- uses: actions/setup-python@v2
67+
with:
68+
python-version: 3.11
69+
- uses: isort/isort-action@master
70+
with:
71+
requirementsFiles: "requirements.txt requirements.dev.txt"
72+
- uses: psf/black@stable
73+
- name: Comment if linting failed
74+
if: failure()
75+
uses: thollander/actions-comment-pull-request@v2
76+
with:
77+
message: |
78+
:poop: Code linting failed, use `black` and `isort` to fix it.

Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ format: configure
55
source ./venv/bin/activate && autoflake -r --in-place --remove-all-unused-imports ./social
66
source ./venv/bin/activate && isort ./social
77
source ./venv/bin/activate && black ./social
8+
source ./venv/bin/activate && autoflake -r --in-place --remove-all-unused-imports ./tests
9+
source ./venv/bin/activate && isort ./tests
10+
source ./venv/bin/activate && black ./tests
11+
source ./venv/bin/activate && autoflake -r --in-place --remove-all-unused-imports ./migrations
12+
source ./venv/bin/activate && isort ./migrations
13+
source ./venv/bin/activate && black ./migrations
814

915
configure: venv
1016
source ./venv/bin/activate && pip install -r requirements.dev.txt -r requirements.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"""User defined groups
2+
3+
Revision ID: 1cacaf803a1d
4+
Revises: 9d98c1e9c864
5+
Create Date: 2024-04-14 23:38:18.956845
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
from sqlalchemy.schema import CreateSequence, Sequence
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision = '1cacaf803a1d'
16+
down_revision = '9d98c1e9c864'
17+
branch_labels = None
18+
depends_on = None
19+
20+
21+
def upgrade():
22+
op.create_table(
23+
'group',
24+
sa.Column('id', sa.Integer(), nullable=False),
25+
sa.Column('type', sa.String(), nullable=False),
26+
sa.Column('owner_id', sa.Integer(), nullable=True),
27+
sa.Column('is_deleted', sa.Boolean(), nullable=False),
28+
sa.Column('last_active_ts', sa.DateTime(), nullable=False),
29+
sa.Column('create_ts', sa.DateTime(), nullable=False),
30+
sa.Column('update_ts', sa.DateTime(), nullable=False),
31+
)
32+
op.execute(
33+
'''
34+
INSERT INTO "group"
35+
(id, type, is_deleted, last_active_ts, create_ts, update_ts)
36+
SELECT id, 'vk_group', False, now(), create_ts, update_ts
37+
FROM vk_groups;
38+
'''
39+
)
40+
41+
max_id = op.get_bind().execute(sa.text('SELECT MAX(id) FROM "group";')).scalar() or 0
42+
op.create_primary_key('group_pk', 'group', ['id'])
43+
op.execute(CreateSequence(Sequence('group_id_seq', max_id + 1)))
44+
op.alter_column('group', 'id', server_default=sa.text('nextval(\'group_id_seq\')'))
45+
46+
op.create_table(
47+
'telegram_channel',
48+
sa.Column('id', sa.Integer(), nullable=False),
49+
sa.Column('channel_id', sa.BigInteger(), nullable=False),
50+
sa.ForeignKeyConstraint(
51+
['id'],
52+
['group.id'],
53+
),
54+
sa.PrimaryKeyConstraint('id'),
55+
)
56+
op.create_table(
57+
'telegram_chat',
58+
sa.Column('id', sa.Integer(), nullable=False),
59+
sa.Column('chat_id', sa.BigInteger(), nullable=False),
60+
sa.ForeignKeyConstraint(
61+
['id'],
62+
['group.id'],
63+
),
64+
sa.PrimaryKeyConstraint('id'),
65+
)
66+
op.create_table(
67+
'vk_chat',
68+
sa.Column('id', sa.Integer(), nullable=False),
69+
sa.Column('peer_id', sa.Integer(), nullable=False),
70+
sa.ForeignKeyConstraint(
71+
['id'],
72+
['group.id'],
73+
),
74+
sa.PrimaryKeyConstraint('id'),
75+
)
76+
op.create_foreign_key('group_vkgroup_fk', 'vk_groups', 'group', ['id'], ['id'])
77+
op.drop_column('vk_groups', 'update_ts')
78+
op.drop_column('vk_groups', 'create_ts')
79+
op.execute('DROP SEQUENCE IF EXISTS vk_groups_id_seq CASCADE;')
80+
op.rename_table('vk_groups', 'vk_group')
81+
82+
83+
def downgrade():
84+
op.rename_table('vk_group', 'vk_groups')
85+
86+
max_id = op.get_bind().execute(sa.text('SELECT MAX(id) FROM "vk_groups";')).scalar() or 0
87+
op.execute(CreateSequence(Sequence('vk_groups_id_seq', max_id + 1)))
88+
op.alter_column('vk_groups', 'id', server_default=sa.text('nextval(\'vk_groups_id_seq\')'))
89+
90+
op.add_column('vk_groups', sa.Column('create_ts', sa.DateTime()))
91+
op.add_column('vk_groups', sa.Column('update_ts', sa.DateTime()))
92+
op.execute('UPDATE vk_groups SET create_ts = (SELECT create_ts FROM "group" WHERE "group".id = vk_groups.id);')
93+
op.execute('UPDATE vk_groups SET update_ts = (SELECT update_ts FROM "group" WHERE "group".id = vk_groups.id);')
94+
op.alter_column('vk_groups', 'create_ts', nullable=False)
95+
op.alter_column('vk_groups', 'update_ts', nullable=False)
96+
op.drop_constraint('group_vkgroup_fk', 'vk_groups', type_='foreignkey')
97+
op.drop_table('vk_chat')
98+
op.drop_table('telegram_chat')
99+
op.drop_table('telegram_channel')
100+
op.drop_table('group')
101+
op.execute('DROP SEQUENCE IF EXISTS group_id_seq CASCADE;')

migrations/versions/57c72962d2b4_webhook_storage.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Create Date: 2023-03-12 14:22:34.958257
66
77
"""
8+
89
import sqlalchemy as sa
910
from alembic import op
1011

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""WebhookStorage event_ts
2+
3+
Revision ID: 62addefd9655
4+
Revises: 1cacaf803a1d
5+
Create Date: 2024-04-15 00:21:54.075449
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '62addefd9655'
15+
down_revision = '1cacaf803a1d'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
op.add_column('webhook_storage', sa.Column('event_ts', sa.DateTime(), nullable=True))
22+
23+
24+
def downgrade():
25+
op.drop_column('webhook_storage', 'event_ts')

migrations/versions/9d98c1e9c864_vk.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
Create Date: 2023-08-19 15:53:19.787309
66
77
"""
8-
from alembic import op
8+
99
import sqlalchemy as sa
10+
from alembic import op
1011

1112

1213
# revision identifiers, used by Alembic.
@@ -17,14 +18,15 @@
1718

1819

1920
def upgrade():
20-
op.create_table('vk_groups',
21+
op.create_table(
22+
'vk_groups',
2123
sa.Column('id', sa.Integer(), nullable=False),
2224
sa.Column('group_id', sa.Integer(), nullable=False),
2325
sa.Column('confirmation_token', sa.String(), nullable=False),
2426
sa.Column('secret_key', sa.String(), nullable=False),
2527
sa.Column('create_ts', sa.DateTime(), nullable=False),
2628
sa.Column('update_ts', sa.DateTime(), nullable=False),
27-
sa.PrimaryKeyConstraint('id')
29+
sa.PrimaryKeyConstraint('id'),
2830
)
2931

3032

social/handlers_discord/base.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
from collections.abc import Callable
3+
34
from social.utils.events import EventProcessor
45

56

social/handlers_github/base.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
from collections.abc import Callable
3+
34
from social.utils.events import EventProcessor
45

56

social/handlers_telegram/base.py

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
@lru_cache()
1919
def get_application():
20+
if not settings.TELEGRAM_BOT_TOKEN:
21+
return None
2022
context_types = ContextTypes(context=CustomContext)
2123
app = Application.builder().token(settings.TELEGRAM_BOT_TOKEN).updater(None).context_types(context_types).build()
2224
logger.info("Telegram API initialized successfully")

social/handlers_telegram/handlers_viribus.py

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Для создания нового обработчика создай асинхронную функцию в конце файла с параметрами
44
Update и Context, а потом зарегистрируй ее внутри функции `register_handlers`.
55
"""
6+
67
import logging
78
from random import choice
89
from string import ascii_letters, digits, punctuation

social/models/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from .vk import VkGroups
1+
from .group import TelegramChannel, TelegramChat, VkChat, VkGroup
22
from .webhook_storage import WebhookStorage
33

44

5-
__all__ = ['WebhookStorage', 'VkGroups']
5+
__all__ = ['WebhookStorage', 'TelegramChannel', 'TelegramChat', 'VkGroup', 'VkChat']

social/models/group.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from datetime import UTC, datetime
2+
3+
import sqlalchemy as sa
4+
from sqlalchemy.orm import Mapped, mapped_column
5+
6+
from .base import Base
7+
8+
9+
class Group(Base):
10+
id: Mapped[int] = mapped_column(primary_key=True)
11+
type: Mapped[str]
12+
owner_id: Mapped[int | None]
13+
14+
is_deleted: Mapped[bool] = mapped_column(default=False)
15+
last_active_ts: Mapped[datetime | None]
16+
17+
create_ts: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC))
18+
update_ts: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC))
19+
20+
__mapper_args__ = {
21+
"polymorphic_on": "type",
22+
}
23+
24+
25+
class VkGroup(Group):
26+
id: Mapped[int] = mapped_column(sa.ForeignKey("group.id"), primary_key=True)
27+
group_id: Mapped[int]
28+
confirmation_token: Mapped[str]
29+
secret_key: Mapped[str]
30+
31+
__mapper_args__ = {
32+
"polymorphic_identity": "vk_group",
33+
}
34+
35+
36+
class VkChat(Group):
37+
id: Mapped[int] = mapped_column(sa.ForeignKey("group.id"), primary_key=True)
38+
peer_id: Mapped[int]
39+
40+
__mapper_args__ = {
41+
"polymorphic_identity": "vk_chat",
42+
}
43+
44+
45+
class TelegramChannel(Group):
46+
id: Mapped[int] = mapped_column(sa.ForeignKey("group.id"), primary_key=True)
47+
channel_id: Mapped[int]
48+
49+
__mapper_args__ = {
50+
"polymorphic_identity": "tg_channel",
51+
}
52+
53+
54+
class TelegramChat(Group):
55+
id: Mapped[int] = mapped_column(sa.ForeignKey("group.id"), primary_key=True)
56+
chat_id: Mapped[int]
57+
58+
__mapper_args__ = {
59+
"polymorphic_identity": "tg_chat",
60+
}

social/models/vk.py

-15
This file was deleted.

social/models/webhook_storage.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import UTC, datetime
12
from enum import Enum
23

34
import sqlalchemy as sa
@@ -17,3 +18,4 @@ class WebhookStorage(Base):
1718
id: Mapped[int] = mapped_column(sa.Integer, primary_key=True)
1819
system: Mapped[WebhookSystems] = mapped_column(sa.Enum(WebhookSystems, native_enum=False))
1920
message: Mapped[sa.JSON] = mapped_column(sa.JSON(True))
21+
event_ts: Mapped[datetime] = mapped_column(default=lambda: datetime.now(UTC), nullable=True)

0 commit comments

Comments
 (0)