Passed
Push — master ( a8cf2f...4ab9d5 )
by torrua
02:00 queued 12s
created

Button.is_auto_copy_text_to_callback()   A

Complexity

Conditions 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 4
nop 1
dl 0
loc 9
ccs 3
cts 3
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
# -*- coding:utf-8 -*-
2 1
"""
3
This module contains all the necessary functions for
4
creating buttons for telegram inline keyboards.
5
"""
6 1
from dataclasses import dataclass
7 1
from typing import Optional
8 1
from telebot.types import InlineKeyboardButton
9 1
from keyboa.button_check import ButtonCheck
10 1
from keyboa.constants import (
11
    InlineButtonData,
12
    CallbackDataMarker,
13
    callback_data_types,
14
    button_text_types,
15
    ButtonText,
16
)
17
18
19 1
@dataclass
20 1
class Button(ButtonCheck):
21
    """Default Button class
22
    :button_data: InlineButtonData - an object from which the button will be created:
23
    • If string or an integer, it will be used for both text and callback.
24
    • If tuple, the zero element [0] will be the text, and the first [1] will be the callback.
25
    • If dictionary, there are two options:
26
        > if there is no "text" key in dictionary and only one key exists,
27
            the key will be the text, and the value will be the callback.
28
            In this case, no verification of the dictionary's contents is performed!
29
        > if "text" key exists, function passes the whole dictionary to InlineKeyboardButton,
30
            where dictionary's keys represent object's parameters
31
            and dictionary's values represent parameters' values accordingly.
32
        In all other cases ValueError will be called.
33
34
    :front_marker: CallbackDataMarker - a string to be added to the left side of callback.
35
        Optional. The default value is None.
36
37
    :back_marker: CallbackDataMarker - a string to be added to the right side of callback.
38
        Optional. The default value is None.
39
40
    :copy_text_to_callback: If enabled and button_data is a string or integer,
41
        function will copy button text to callback data (and add markers if they exist).
42
        Optional. The default value is False."""
43
44 1
    button_data: InlineButtonData = None
45 1
    front_marker: CallbackDataMarker = str()
46 1
    back_marker: CallbackDataMarker = str()
47 1
    copy_text_to_callback: Optional[bool] = None
48
49 1
    def generate(self) -> InlineKeyboardButton:
50
        """
51
        This function creates an InlineKeyboardButton object from various data types,
52
        such as str, int, tuple, dict.
53
        :return: InlineKeyboardButton
54
55
        Covered by tests.
56
        """
57
58 1
        if isinstance(self.button_data, InlineKeyboardButton):
59 1
            return self.button_data
60
61 1
        if isinstance(self.button_data, dict) and self.button_data.get("text"):
62 1
            return InlineKeyboardButton(**self.button_data)
63
64 1
        self.is_auto_copy_text_to_callback()
65
66 1
        button_tuple = self.get_verified_button_tuple(
67
            self.button_data, self.copy_text_to_callback
68
        )
69 1
        text = self.get_text(button_tuple)
70 1
        raw_callback = self.get_callback(button_tuple)
71 1
        callback_data = self.get_callback_data(
72
            raw_callback, self.front_marker, self.back_marker
73
        )
74
75 1
        prepared_button = {"text": text, "callback_data": callback_data}
76
77 1
        return InlineKeyboardButton(**prepared_button)
78
79 1
    def is_auto_copy_text_to_callback(self):
80
        """
81
        Enable copy_text_to_callback parameter if button_data is str or int
82
        :return:
83
        """
84 1
        if self.copy_text_to_callback is None and isinstance(
85
            self.button_data, (str, int)
86
        ):
87 1
            self.copy_text_to_callback = True
88
89 1
    @classmethod
90 1
    def get_callback(cls, button_data: tuple) -> str:
91
        """
92
        :param button_data:
93
        :return:
94
        """
95 1
        callback = button_data[1]
96 1
        cls.is_callback_proper_type(callback)
97 1
        return callback
98
99 1
    @classmethod
100 1
    def get_callback_data(
101
        cls,
102
        raw_callback: CallbackDataMarker,
103
        front_marker: CallbackDataMarker = str(),
104
        back_marker: CallbackDataMarker = str(),
105
    ) -> str:
106
        """
107
        :param raw_callback:
108
        :param front_marker:
109
        :param back_marker:
110
        :return:
111
        """
112
113 1
        front_marker = cls.get_checked_marker(front_marker)
114 1
        back_marker = cls.get_checked_marker(back_marker)
115
116 1
        callback_data = "%s%s%s" % (front_marker, raw_callback, back_marker)
117
118 1
        if not callback_data:
119 1
            raise ValueError("The callback data cannot be empty.")
120
121 1
        cls.is_callback_data_in_limits(callback_data)
122
123 1
        return callback_data
124
125 1
    @staticmethod
126 1
    def get_checked_marker(marker: CallbackDataMarker) -> CallbackDataMarker:
127
        """
128
        :param marker:
129
        :return:
130
        """
131 1
        if marker is None:
132 1
            marker = str()
133
134 1
        if not isinstance(marker, callback_data_types):
135 1
            type_error_message = "Marker could not have %s type. Only %s allowed." % (
136
                type(marker),
137
                CallbackDataMarker,
138
            )
139 1
            raise TypeError(type_error_message)
140
141 1
        return marker
142
143 1
    @staticmethod
144 1
    def get_text(button_data: tuple) -> str:
145
        """
146
        :param button_data:
147
        :return:
148
        """
149 1
        raw_text = button_data[0]
150 1
        if not isinstance(raw_text, button_text_types):
151 1
            type_error_message = "Button text cannot be %s. Only %s allowed." % (
152
                type(raw_text),
153
                ButtonText,
154
            )
155 1
            raise TypeError(type_error_message)
156 1
        text = str(raw_text)
157 1
        if not text:
158 1
            raise ValueError("Button text cannot be empty.")
159 1
        return text
160
161 1
    @classmethod
162 1
    def get_verified_button_tuple(
163
        cls, button_data: InlineButtonData, copy_text_to_callback: bool
164
    ) -> tuple:
165
        """
166
        :param button_data:
167
        :param copy_text_to_callback:
168
        :return:
169
        """
170 1
        cls.is_button_data_proper_type(button_data)
171
172 1
        btn_tuple = cls.get_raw_tuple_from_button_data(
173
            button_data, copy_text_to_callback
174
        )
175
176 1
        if len(btn_tuple) == 1 or btn_tuple[1] is None:
177 1
            btn_tuple = btn_tuple[0], btn_tuple[0] if copy_text_to_callback else str()
178 1
        return btn_tuple
179
180 1
    @staticmethod
181
    def get_raw_tuple_from_button_data(button_data, copy_text_to_callback):
182
        """
183
        :param button_data:
184
        :param copy_text_to_callback:
185
        :return:
186
        """
187 1
        if isinstance(button_data, (str, int)):
188 1
            btn_tuple = button_data, button_data if copy_text_to_callback else str()
189
190 1
        elif isinstance(button_data, dict):
191 1
            if len(button_data.keys()) != 1:
192 1
                value_type_error = (
193
                    "Cannot convert dictionary to InlineButtonData object. "
194
                    "You passed more than one item, but did not add 'text' key."
195
                )
196 1
                raise ValueError(value_type_error)
197
198 1
            btn_tuple = next(iter(button_data.items()))
199
        else:
200 1
            btn_tuple = button_data
201
        return btn_tuple
202