Passed
Push — master ( 6bb124...866a63 )
by torrua
01:34
created

keyboa.keyboards.get_verified_items_in_row()   A

Complexity

Conditions 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 9
nop 4
dl 0
loc 16
ccs 6
cts 6
cp 1
crap 3
rs 9.95
c 0
b 0
f 0
1
# -*- coding:utf-8 -*-
2 1
"""
3
This module contains all the necessary functions for
4
creating complex and functional inline keyboards.
5
"""
6
# pylint: disable=R0913
7
8 1
from collections.abc import Iterable
9 1
from typing import Union, Optional, Tuple
10
11 1
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
12
13 1
from keyboa.constants import InlineButtonData, CallbackDataMarker, \
14
    BlockItems, DEFAULT_ITEMS_IN_LINE
15 1
from keyboa.functions_alignment import calculate_items_in_row
16 1
from keyboa.functions_button_data import get_text, get_verified_button_tuple
17 1
from keyboa.functions_callback import get_callback_data, get_callback
18 1
from keyboa.functions_precheck import _keyboa_pre_check
19
20
21 1
def button_maker(
22
        button_data: InlineButtonData,
23
        front_marker: CallbackDataMarker = str(),
24
        back_marker: CallbackDataMarker = str(),
25
        copy_text_to_callback: bool = False,
26
) -> InlineKeyboardButton:
27
    """
28
    This function creates an InlineKeyboardButton object from various data types,
29
    such as str, int, tuple, dict.
30
31
    :param button_data: InlineButtonData - an object from which the button will be created:
32
    • If string or an integer, it will be used for both text and callback.
33
    • If tuple, the zero element [0] will be the text, and the first [1] will be the callback.
34
    • If dictionary, there are two options:
35
        > if there is no "text" key in dictionary and only one key exists,
36
            the key will be the text, and the value will be the callback.
37
            In this case, no verification of the dictionary's contents is performed!
38
        > if "text" key exists, function passes the whole dictionary to InlineKeyboardButton,
39
            where dictionary's keys represent object's parameters
40
            and dictionary's values represent parameters' values accordingly.
41
        In all other cases ValueError will be called.
42
43
    :param front_marker: CallbackDataMarker - a string to be added to the left side of callback.
44
        Optional. The default value is None.
45
46
    :param back_marker: CallbackDataMarker - a string to be added to the right side of callback.
47
        Optional. The default value is None.
48
49
    :param copy_text_to_callback: If enabled and button_data is a string or integer,
50
        function will copy button text to callback data (and add markers if they exist).
51
        Optional. The default value is False.
52
53
    :return: InlineKeyboardButton
54
55
    Covered by tests.
56
    """
57
58 1
    if isinstance(button_data, InlineKeyboardButton):
59 1
        return button_data
60
61 1
    if isinstance(button_data, dict) and button_data.get("text"):
62 1
        return InlineKeyboardButton(**button_data)
63
64 1
    button_tuple = get_verified_button_tuple(button_data, copy_text_to_callback)
65
66 1
    text = get_text(button_tuple)
67 1
    raw_callback = get_callback(button_tuple)
68 1
    callback_data = get_callback_data(raw_callback, front_marker, back_marker)
69
70 1
    prepared_button = {"text": text, "callback_data": callback_data}
71
72 1
    return InlineKeyboardButton(**prepared_button)
73
74
75 1
def keyboa_maker(
76
        items: BlockItems = None,
77
        front_marker: CallbackDataMarker = None,
78
        back_marker: CallbackDataMarker = None,
79
80
        items_in_row: int = None,
81
        auto_alignment: Union[bool, Iterable] = False,
82
        reverse_alignment_range: bool = False,
83
        slice_start: int = None,
84
        slice_stop: int = None,
85
        slice_step: int = None,
86
87
        copy_text_to_callback: bool = False,
88
        add_to_keyboard: InlineKeyboardMarkup = None,
89
) -> InlineKeyboardMarkup:
90
    """
91
    :param items:
92
    :param front_marker:
93
    :param back_marker:
94
    :param items_in_row:
95
    :param auto_alignment:
96
    :param reverse_alignment_range:
97
    :param slice_start:
98
    :param slice_stop:
99
    :param slice_step:
100
    :param copy_text_to_callback:
101
    :param add_to_keyboard:
102
    :return:
103
    """
104 1
    keyboard = add_to_keyboard if add_to_keyboard else InlineKeyboardMarkup()
105
106 1
    if items is None:
107 1
        return keyboard
108
109 1
    items = get_verified_items(items, slice_start, slice_stop, slice_step)
110
111 1
    _keyboa_pre_check(items=items, items_in_row=items_in_row, keyboard=keyboard)
112
113 1
    if items_in_row or auto_alignment:
114 1
        return get_generated_keyboard(
115
            items, front_marker, back_marker, items_in_row, auto_alignment,
116
            reverse_alignment_range, copy_text_to_callback, keyboard)
117
118 1
    return get_preformatted_keyboard(
119
        items, front_marker, back_marker,
120
        copy_text_to_callback, keyboard)
121
122
123 1
def get_verified_items(items, slice_start, slice_stop, slice_step):
124
    """
125
    :param items:
126
    :param slice_start:
127
    :param slice_stop:
128
    :param slice_step:
129
    :return:
130
    """
131
132 1
    if items and not isinstance(items, list):
133 1
        items = [items, ]
134
135 1
    return items[slice_start:slice_stop:slice_step] if items else items
136
137
138 1
def get_preformatted_keyboard(
139
        items, front_marker, back_marker,
140
        copy_text_to_callback, keyboard):
141
    """
142
    :param items:
143
    :param front_marker:
144
    :param back_marker:
145
    :param copy_text_to_callback:
146
    :param keyboard:
147
    :return:
148
    """
149 1
    for index, item in enumerate(items):
150 1
        if not isinstance(item, list):
151 1
            items[index] = [item, ]
152 1
    for row in items:
153 1
        buttons = [button_maker(
154
            button_data=item,
155
            front_marker=front_marker,
156
            back_marker=back_marker,
157
            copy_text_to_callback=copy_text_to_callback
158
        ) for item in row]
159 1
        keyboard.row(*buttons)
160 1
    return keyboard
161
162
163 1
def get_generated_keyboard(
164
        items, front_marker, back_marker, items_in_row,
165
        auto_alignment, reverse_alignment_range,
166
        copy_text_to_callback, keyboard):
167
    """
168
    :param items:
169
    :param front_marker:
170
    :param back_marker:
171
    :param items_in_row:
172
    :param auto_alignment:
173
    :param reverse_alignment_range:
174
    :param copy_text_to_callback:
175
    :param keyboard:
176
    :return:
177
    """
178
179 1
    items_in_row = get_verified_items_in_row(
180
        items, items_in_row, auto_alignment, reverse_alignment_range)
181
182 1
    rows_in_keyboard = (len(items) // items_in_row)
183
184 1
    buttons = [button_maker(
185
        button_data=item,
186
        front_marker=front_marker,
187
        back_marker=back_marker,
188
        copy_text_to_callback=copy_text_to_callback,
189
    ) for item in items]
190
191 1
    for _row in range(rows_in_keyboard):
192 1
        keyboard.row(*[buttons.pop(0) for _button in range(items_in_row)])
193 1
    keyboard.row(*buttons)
194
195 1
    return keyboard
196
197
198 1
def get_verified_items_in_row(
199
        items, items_in_row,
200
        auto_alignment, reverse_alignment_range):
201
    """
202
    :param items:
203
    :param items_in_row:
204
    :param auto_alignment:
205
    :param reverse_alignment_range:
206
    :return:
207
    """
208 1
    if auto_alignment:
209 1
        items_in_row = calculate_items_in_row(
210
            items, auto_alignment, reverse_alignment_range)
211 1
    if not items_in_row:
212 1
        items_in_row = DEFAULT_ITEMS_IN_LINE
213 1
    return items_in_row
214
215
216 1
def keyboa_combiner(
217
        keyboards: Optional[Union[Tuple[InlineKeyboardMarkup, ...], InlineKeyboardMarkup]] = None
218
) -> InlineKeyboardMarkup:
219
    """
220
    This function combines multiple InlineKeyboardMarkup objects into one.
221
222
    :param keyboards: Sequence of InlineKeyboardMarkup objects.
223
        Also could be presented as a standalone InlineKeyboardMarkup.
224
225
    :return: InlineKeyboardMarkup
226
    """
227
228 1
    if keyboards is None:
229 1
        return InlineKeyboardMarkup()
230
231 1
    if isinstance(keyboards, InlineKeyboardMarkup):
232 1
        keyboards = (keyboards, )
233
234 1
    data = merge_keyboards_data(keyboards)
235
236 1
    return keyboa_maker(data)
237
238
239 1
def merge_keyboards_data(keyboards):
240 1
    data = []
241 1
    for keyboard in keyboards:
242 1
        if keyboard is None:
243 1
            continue
244
245 1
        if not isinstance(keyboard, InlineKeyboardMarkup):
246 1
            type_error_message = \
247
                "Keyboard element cannot be %s. Only InlineKeyboardMarkup allowed." \
248
                % type(keyboard)
249 1
            raise TypeError(type_error_message)
250
251 1
        data.extend(keyboard.keyboard)
252
    return data
253