VerifiedState.run_actions()   D
last analyzed

Complexity

Conditions 13

Size

Total Lines 75
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 55
dl 0
loc 75
rs 4.2
c 0
b 0
f 0
cc 13
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like bot.management.commands._states.VerifiedState.run_actions() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import random
2
import string
3
4
from bot.models import TgUser, TgChatState
5
from bot.tg.client import TgClient
6
from goals.models import Goal, Category
7
8
9
class BaseStateClass:
10
    """Базовый класс для классов состояний чата
11
12
    Args:
13
        tg_user: Telegram пользователь. Предоставляет доступ к атрибутам
14
            пользователя.
15
        tg_client: Telegram клиент. Предоставляет доступ к функциям получения
16
            входящих обновлений и отправки сообщений.
17
    """
18
19
    def __init__(self, tg_user: TgUser, tg_client: TgClient):
20
        self._tg_user = tg_user
21
        self.__tg_client = tg_client
22
23
        #: str: Текст приветствия бота
24
        self._text: str | None = None
25
26
        #: list of string: Список разрешенных в чате команд
27
        self._allowed_commands: list = [
28
            '/start', '/goals', '/create', '/cancel'
29
        ]
30
31
        #: dict: Словарь с вариантами сообщений бота
32
        self._messages = {
33
            'allowed_commands': 'Для продолжения отправьте одну из команд:\n'
34
                                '/goals - просмотреть все цели;\n'
35
                                '/create - создать цель;\n'
36
                                '/cancel - отменить создание цели.',
37
            'unknown_command': '[unknown command]\n',
38
            'verification_required': 'Необходимо пройти верификацию.',
39
            'select_category': 'Отправьте название категории, '
40
                               ' которой будет создана цель:\n',
41
            'goal_title': 'Отправьте название цели.',
42
            'successful': '[successful]',
43
            'failure': '[failure]',
44
        }
45
46
    @staticmethod
47
    def __generate_verification_code(length: int = 16) -> str:
48
        symbols = string.ascii_letters + string.digits
49
        code = ''.join(random.sample(symbols, length))
50
        return code
51
52
    def get_verification_code(self) -> str:
53
        """Возвращает строку из случайных чисел и букв
54
55
        Returns:
56
             str: последовательность из чисел и букв
57
        """
58
        code = self.__generate_verification_code()
59
60
        tg_user = self._tg_user
61
        tg_user.verification_code = code
62
        tg_user.save(update_fields=('verification_code',))
63
        return code
64
65
    def _send_message(self, text: str) -> None:
66
        self.__tg_client.send_message(chat_id=self._tg_user.tg_id, text=text)
67
68
    def run_actions(self) -> None:
69
        """Выполняет характерные для определенного состояния действия"""
70
71
        self._send_message(text=self._text)
72
        self._send_message(text=self.get_verification_code())
73
74
75
class NewState(BaseStateClass):
76
    """Класс состояния чата: пользователь не известен
77
78
    Args:
79
        tg_user: Telegram пользователь. Предоставляет доступ к атрибутам
80
            пользователя.
81
        tg_client: Telegram клиент. Предоставляет доступ к функциям получения
82
            входящих обновлений и отправки сообщений.
83
    """
84
85
    def __init__(self, tg_user: TgUser, tg_client: TgClient):
86
        super().__init__(tg_user, tg_client)
87
88
        #: str: Текст приветствия бота
89
        self._text = 'Привет! Я Telegram бот проекта \"TodoList\"\n' \
90
                     + self._messages['verification_required']
91
92
93
class NotVerifiedState(BaseStateClass):
94
    """Класс состояния чата: пользователь не верифицирован
95
96
    Args:
97
        tg_user: Telegram пользователь. Предоставляет доступ к атрибутам
98
            пользователя.
99
        tg_client: Telegram клиент. Предоставляет доступ к функциям получения
100
            входящих обновлений и отправки сообщений.
101
    """
102
103
    def __init__(self, tg_user: TgUser, tg_client: TgClient):
104
        super().__init__(tg_user, tg_client)
105
106
        #: str: Текст приветствия бота
107
        self._text = 'С возвращением!\n' + self._messages[
108
            'verification_required']
109
110
111
class VerifiedState(BaseStateClass):
112
    """Класс состояния чата: пользователь прошел верификацию
113
114
    Args:
115
        tg_user: Telegram пользователь. Предоставляет доступ к атрибутам
116
            пользователя.
117
        tg_client: Telegram клиент. Предоставляет доступ к функциям получения
118
            входящих обновлений и отправки сообщений.
119
    """
120
121
    def __init__(self, tg_user: TgUser, tg_client: TgClient,
122
                 chat_msg: str = None):
123
        super().__init__(tg_user, tg_client)
124
        self.__chat_msg = chat_msg
125
        self.__chat_state, _ = TgChatState.objects.get_or_create(
126
            tg_user=tg_user)
127
128
    def run_actions(self) -> None:
129
        """Выполняет характерные для определенного состояния действия"""
130
131
        #: bool: Флаг выполнения команды /create
132
        is_create_command = self.__chat_state.is_create_command
133
134
        if self.__chat_msg == '/create':
135
            self.__chat_state.is_create_command = True
136
            self.__chat_state.save(update_fields=('is_create_command',))
137
138
            categories: list = Category.objects.all().filter(
139
                user_id=self._tg_user.user_id,
140
                is_deleted=False
141
            ).values_list('title', flat=True)
142
143
            self._send_message(
144
                text=self._messages['select_category'] + '\n'.join(
145
                    categories) if categories
146
                else '[categories not found]'
147
            )
148
149
        if not is_create_command:
150
            if self.__chat_msg not in self._allowed_commands:
151
                self._send_message(
152
                    text=self._messages['unknown_command'] + self._messages[
153
                        'allowed_commands'])
154
155
            if self.__chat_msg == '/start':
156
                self._send_message(text=self._messages['allowed_commands'])
157
158
            if self.__chat_msg == '/goals':
159
                goals: list = Goal.objects.all().filter(
160
                    category__board__participants__user_id=self._tg_user.user_id,
161
                    category__is_deleted=False,
162
                    status__lt=Goal.Status.archived,
163
                ).values_list('title', flat=True)
164
165
                self._send_message(
166
                    text='\n'.join(goals) if goals else '[goals not found]')
167
168
        elif self.__chat_msg == '/cancel':
169
            self.__chat_state.set_default()
170
            self._send_message(text=self._messages['successful'])
171
172
        else:
173
            if not self.__chat_state.category_id:
174
                categories: list = Category.objects.all().filter(
175
                    user_id=self._tg_user.user_id,
176
                    is_deleted=False
177
                ).values_list('title', flat=True)
178
179
                if self.__chat_msg not in categories:
180
                    self._send_message(
181
                        text=self._messages['select_category'] + '\n'.join(
182
                            categories) if categories
183
                        else '[categories not found]'
184
                    )
185
                else:
186
                    category = Category.objects.all().filter(
187
                        user_id=self._tg_user.user_id).get(
188
                        title=self.__chat_msg)
189
                    self.__chat_state.category_id = category.id
190
                    self.__chat_state.save(update_fields=('category_id',))
191
                    self._send_message(text=self._messages['goal_title'])
192
            else:
193
                goal = Goal.objects.create(
194
                    user_id=self._tg_user.user_id,
195
                    category_id=self.__chat_state.category_id,
196
                    title=self.__chat_msg
197
                )
198
                if goal.id:
199
                    self.__chat_state.set_default()
200
                    self._send_message(text=self._messages['successful'])
201
                else:
202
                    self._send_message(text=self._messages['failure'])
203