Skip to content
This repository has been archived by the owner on May 5, 2022. It is now read-only.

Commit

Permalink
feat: Add checktable widget
Browse files Browse the repository at this point in the history
feat: Add checktable widget
  • Loading branch information
Kondrahin authored Jul 19, 2021
1 parent 7522c91 commit 855f4c1
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 1 deletion.
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
### Виджет Pagination
![calendar_img](pybotx_widgets/images/pagination.png?raw=true "Pagination")

---

### Виджет Checktable
![checktable_img](pybotx_widgets/images/checktable.png?raw=true "Checktable")

---
### Пример использования виджета Carousel:

Expand Down Expand Up @@ -179,6 +184,51 @@ async def some_command(message: Message, bot: Bot) -> None:

---

### Пример использования виджета Checktable:

```python
from pybotx_widgets.checktable import checktable

...

@collector.handler(command="/some_command")
async def some_command(message: Message, bot: Bot) -> None:
await checktable(
message,
bot,
content, # All content to be displayed: List[CheckboxContent]
label, # Text of message
"uncheck_command", # Command for handler which uncheck value
"some_command", # Command for bubbles command attribute
additional_markup # Additional markup for attaching to widget
)
```
Для корректной работы виджета нужно создать хэндлер с командой `uncheck_command` и прописать в нем поведение при сбрасывании значения.

#### Пример хэндлера:

```python
from pybotx_widgets.checktable import checktable
from pybotx_widgets.undefined import undefined

...

UNCHECK_COMMAND = "/_checkbox:uncheck"

@collector.hidden(command=UNCHECK_COMMAND)
async def checkbox_uncheck(message: Message, bot: Bot) -> None:
field_name = message.data.get("field_name")
my_object = await get_my_object()

setattr(my_object, field_name, undefined)
await set_my_object(my_object)

checkboxes = await build_checkboxes(my_object)
await checktable(message, bot, my_object, "some_label", UNCHECK_COMMAND)
```

---

## ЭМОДЗИ
В `pybotx_widgets.resources.strings` есть следующие эмодзи:

Expand Down
111 changes: 111 additions & 0 deletions pybotx_widgets/checktable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from typing import Any, Dict, List, Optional, TypeVar, Generic, Union

from botx import Bot, Message, MessageMarkup
from pydantic import root_validator, BaseModel

from pybotx_widgets.resources import strings
from pybotx_widgets.service import merge_markup, send_or_update_message
from pybotx_widgets.undefined import Undefined, undefined

T = TypeVar("T") # noqa: WPS111


class CheckboxContent(BaseModel, Generic[T]):
"""Checkbox content."""

#: text that will be shown on the element.
label: str
#: command that will be triggered by click on the element.
command: str
#: value which will be displayed on the button associated with this checkbox.
checkbox_value: Optional[Union[T, Undefined]] = undefined
#: list of available values.
mapping: Optional[Dict[T, str]] = None
#: extra payload that will be stored in button and then received in new message.
data: Optional[Dict[str, Any]] = None

class Config:
arbitrary_types_allowed = True

@root_validator(pre=True)
def is_checkbox_value_exist_in_mapping(cls, values):
mapping = values.get("mapping")
checkbox_value = values.get("checkbox_value")
if (
mapping
and not isinstance(checkbox_value, Undefined)
and checkbox_value not in mapping
):
raise ValueError(
f"'mapping' should contains 'checkbox_value' - '{checkbox_value}'"
)

return values


def get_value_text(checkbox: CheckboxContent) -> str:
"""Get value text for button."""
if checkbox.checkbox_value is None:
return strings.EMPTY

is_undefined = isinstance(checkbox.checkbox_value, Undefined)

if checkbox.mapping:
if is_undefined:
return strings.CHOOSE_LABEL
return checkbox.mapping[checkbox.checkbox_value]

if is_undefined:
return strings.FILL_LABEL

return checkbox.checkbox_value


def add_checkboxes(checkboxes: List[CheckboxContent], uncheck_command: str) -> MessageMarkup:
"""Add checkbox."""
markup = MessageMarkup()

for checkbox in checkboxes:
checkbox.data = checkbox.data or {}

if isinstance(checkbox.checkbox_value, Undefined):
checkbox_status = strings.CHECKBOX_UNCHECKED
else:
checkbox_status = strings.CHECKBOX_CHECKED

checkbox_text = f"{checkbox_status} {checkbox.label}"

value_text = get_value_text(checkbox)

markup.add_bubble(uncheck_command, checkbox_text, data=checkbox.data)
markup.add_bubble(
checkbox.command, value_text, new_row=False, data=checkbox.data
)

return markup


async def checktable(
message: Message,
bot: Bot,
checkboxes: List[CheckboxContent],
label: str,
uncheck_command: str,
additional_markup: MessageMarkup = None,
) -> None:
"""Create checktable widget.
:param message - botx Message
:param bot - botx Bot
:param checkboxes - All content to be displayed
:param label - Text of message
:param uncheck_command - Command for handler which uncheck value
:param additional_markup - Additional markup for attaching to widget
"""

markup = add_checkboxes(checkboxes, uncheck_command)

if additional_markup:
markup = merge_markup(markup, additional_markup)

await send_or_update_message(message, bot, label, markup)
Binary file added pybotx_widgets/images/checktable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions pybotx_widgets/resources/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ def get_template(self, uri: str) -> FormatTemplate:
# ========

SELECTED_VALUE_LABEL = "{label} {selected_val}"
CHOOSE_LABEL = "Выбрать"
FILL_LABEL = "Ввести"
EMPTY = "[Пусто]"

LEFT_ARROW = "⬅️"
RIGHT_ARROW = "➡️"
Expand Down
11 changes: 11 additions & 0 deletions pybotx_widgets/undefined.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Singleton for `undefined` value."""
from functools import lru_cache


class Undefined:
@lru_cache()
def __new__(cls) -> "Undefined":
return super().__new__(cls)


undefined = Undefined()
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pybotx-widgets"
version = "0.3.1"
version = "0.3.2"
description = "Widgets for pybotx"
authors = ["Tamirlan Dzhemirzoev", "Alexandr Samojlenko", "Vladimir Karabanov", "Samat Yunusov"]

Expand Down

0 comments on commit 855f4c1

Please sign in to comment.