django-slack-tools 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_slack_tools/__init__.py +1 -1
- django_slack_tools/app_settings.py +29 -4
- django_slack_tools/slack_messages/admin/message.py +1 -1
- django_slack_tools/slack_messages/apps.py +9 -0
- django_slack_tools/slack_messages/backends/__init__.py +2 -2
- django_slack_tools/slack_messages/backends/base.py +182 -47
- django_slack_tools/slack_messages/backends/dummy.py +8 -9
- django_slack_tools/slack_messages/backends/slack.py +29 -91
- django_slack_tools/slack_messages/message.py +40 -58
- django_slack_tools/slack_messages/migrations/0001_initial.py +0 -2
- django_slack_tools/slack_messages/migrations/0002_default_policy.py +35 -0
- django_slack_tools/slack_messages/migrations/0003_default_recipient.py +35 -0
- django_slack_tools/slack_messages/migrations/0004_slackmessagingpolicy_template_type.py +23 -0
- django_slack_tools/slack_messages/models/messaging_policy.py +29 -3
- django_slack_tools/slack_messages/tasks.py +112 -0
- django_slack_tools/utils/slack/django.py +8 -24
- django_slack_tools/utils/slack/message.py +74 -25
- django_slack_tools/utils/template/__init__.py +5 -0
- django_slack_tools/utils/template/base.py +17 -0
- django_slack_tools/utils/template/dict.py +52 -0
- django_slack_tools/utils/template/django.py +140 -0
- django_slack_tools/views.py +1 -1
- {django_slack_tools-0.1.0.dist-info → django_slack_tools-0.2.0.dist-info}/METADATA +39 -21
- django_slack_tools-0.2.0.dist-info/RECORD +43 -0
- {django_slack_tools-0.1.0.dist-info → django_slack_tools-0.2.0.dist-info}/WHEEL +1 -1
- django_slack_tools/utils/dict_template.py +0 -55
- django_slack_tools-0.1.0.dist-info/RECORD +0 -36
- {django_slack_tools-0.1.0.dist-info → django_slack_tools-0.2.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# noqa: D100
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
5
|
+
|
|
6
|
+
from .base import BaseTemplate
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DictTemplate(BaseTemplate):
|
|
18
|
+
"""Simple dictionary-based template."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, template: dict) -> None:
|
|
21
|
+
"""Initialize template.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
template: Dictionary template.
|
|
25
|
+
kwargs: Keyword arguments passed to template.
|
|
26
|
+
"""
|
|
27
|
+
self.template = template
|
|
28
|
+
|
|
29
|
+
def render(self, *, context: dict[str, Any] | None = None) -> dict: # noqa: D102
|
|
30
|
+
context = {} if context is None else context
|
|
31
|
+
result = self.template.copy()
|
|
32
|
+
for k, v in result.items():
|
|
33
|
+
result[k] = _format_obj(v, context=context)
|
|
34
|
+
|
|
35
|
+
return result
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
T = TypeVar("T", dict, list, str)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _format_obj(obj: T, *, context: dict[str, Any]) -> T:
|
|
42
|
+
"""Format object recursively."""
|
|
43
|
+
if isinstance(obj, dict):
|
|
44
|
+
return {k: _format_obj(v, context=context) for k, v in obj.items()}
|
|
45
|
+
|
|
46
|
+
if isinstance(obj, str):
|
|
47
|
+
return obj.format_map(context)
|
|
48
|
+
|
|
49
|
+
if isinstance(obj, list):
|
|
50
|
+
return [_format_obj(item, context=context) for item in obj]
|
|
51
|
+
|
|
52
|
+
return obj
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# noqa: D100
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import re
|
|
6
|
+
import xml.etree.ElementTree as ET
|
|
7
|
+
from textwrap import dedent
|
|
8
|
+
from typing import TYPE_CHECKING, overload
|
|
9
|
+
|
|
10
|
+
import xmltodict
|
|
11
|
+
from django.template import engines
|
|
12
|
+
|
|
13
|
+
from .base import BaseTemplate
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from django.template.backends.base import BaseEngine
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DjangoTemplate(BaseTemplate):
|
|
24
|
+
"""Template utilizing Django built-in template engine."""
|
|
25
|
+
|
|
26
|
+
@overload
|
|
27
|
+
def __init__(self, *, file: str, engine: BaseEngine | None = None) -> None: ... # pragma: no cover
|
|
28
|
+
|
|
29
|
+
@overload
|
|
30
|
+
def __init__(self, *, inline: str, engine: BaseEngine | None = None) -> None: ... # pragma: no cover
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
*,
|
|
35
|
+
file: str | None = None,
|
|
36
|
+
inline: str | None = None,
|
|
37
|
+
engine: BaseEngine | None = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Initialize template.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
file: Path to file with template.
|
|
43
|
+
inline: XML inline template.
|
|
44
|
+
engine: Template engine to use. Defaults to Django engine.
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
TypeError: Some of the arguments are missing or multiple are provided.
|
|
48
|
+
ValueError: Unsupported value provided.
|
|
49
|
+
"""
|
|
50
|
+
engine = engines["django"] if engine is None else engine
|
|
51
|
+
|
|
52
|
+
if len([value for value in (file, inline) if value is not None]) != 1:
|
|
53
|
+
msg = "Exactly one of 'file' or 'inline' must be provided."
|
|
54
|
+
raise TypeError(msg)
|
|
55
|
+
|
|
56
|
+
if file:
|
|
57
|
+
template = engine.get_template(file)
|
|
58
|
+
elif inline:
|
|
59
|
+
template = engine.from_string(inline)
|
|
60
|
+
else: # pragma: no cover
|
|
61
|
+
msg = "Unreachable code"
|
|
62
|
+
raise NotImplementedError(msg)
|
|
63
|
+
|
|
64
|
+
self.template = template
|
|
65
|
+
|
|
66
|
+
def render(self, *, context: dict[str, Any] | None = None) -> dict: # noqa: D102
|
|
67
|
+
context = {} if context is None else context
|
|
68
|
+
|
|
69
|
+
logger.debug("Rendering template with context: %r", context)
|
|
70
|
+
rendered = self.template.render(context=context)
|
|
71
|
+
return _xml_to_dict(rendered)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _xml_to_dict(xml: str) -> dict:
|
|
75
|
+
"""Parse XML string to Python dictionary.
|
|
76
|
+
|
|
77
|
+
Following transformations are applied by default:
|
|
78
|
+
|
|
79
|
+
- Normalize text nodes: remove single newlines and dedent text, etc.
|
|
80
|
+
- Rename tags for syntactic comfort: block -> blocks, element -> elements
|
|
81
|
+
|
|
82
|
+
Please check the tests for more detailed examples.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
xml: XML string.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Parsed dictionary. Be aware, the returned value will be the child of
|
|
89
|
+
top-level node (e.g. <root>...</root>), regardless of its key name.
|
|
90
|
+
"""
|
|
91
|
+
xml = _preprocess_xml(xml)
|
|
92
|
+
obj = xmltodict.parse(
|
|
93
|
+
xml,
|
|
94
|
+
attr_prefix="",
|
|
95
|
+
cdata_key="text",
|
|
96
|
+
force_list=("blocks", "elements"),
|
|
97
|
+
postprocessor=_xml_postprocessor,
|
|
98
|
+
)
|
|
99
|
+
return dict(next(iter(obj.values())))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _preprocess_xml(xml: str) -> str:
|
|
103
|
+
"""Normalize XML text nodes."""
|
|
104
|
+
root = ET.fromstring(xml) # noqa: S314; TODO(lasuillard): Naive belief that XML is safe
|
|
105
|
+
for node in root.iter():
|
|
106
|
+
node.tag = _rename_tag(node.tag)
|
|
107
|
+
|
|
108
|
+
if node.tag == "text" and node.text:
|
|
109
|
+
text = dedent(node.text)
|
|
110
|
+
text = _remove_single_newline(text)
|
|
111
|
+
logger.debug("Normalized text node: %r -> %r", node.text, text)
|
|
112
|
+
node.text = text
|
|
113
|
+
|
|
114
|
+
return ET.tostring(root, encoding="unicode")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _rename_tag(tag: str) -> str:
|
|
118
|
+
"""Rename tags."""
|
|
119
|
+
if tag == "block":
|
|
120
|
+
return "blocks"
|
|
121
|
+
|
|
122
|
+
if tag == "element":
|
|
123
|
+
return "elements"
|
|
124
|
+
|
|
125
|
+
return tag
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _remove_single_newline(text: str) -> str:
|
|
129
|
+
"""Remove a single newline from repeated newlines. If the are just one newline, replace it with space."""
|
|
130
|
+
return re.sub(r"([\n]+)", lambda m: "\n" * (m.group(1).count("\n") - 1) or " ", text)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _xml_postprocessor(path: Any, key: str, value: Any) -> tuple[str, Any]: # noqa: ARG001
|
|
134
|
+
if value == "true":
|
|
135
|
+
return key, True
|
|
136
|
+
|
|
137
|
+
if value == "false":
|
|
138
|
+
return key, False
|
|
139
|
+
|
|
140
|
+
return key, value
|
django_slack_tools/views.py
CHANGED
|
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
|
|
|
14
14
|
from slack_bolt import App
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class SlackEventHandlerView(View):
|
|
17
|
+
class SlackEventHandlerView(View): # pragma: no cover; TODO(lasuillard): deal with this in future
|
|
18
18
|
"""View for handling Slack events."""
|
|
19
19
|
|
|
20
20
|
app: App | None = None
|
|
@@ -1,21 +1,34 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: django-slack-tools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Little helpers working with Slack bot 🤖 in Django.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Requires-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist:
|
|
18
|
-
Requires-Dist:
|
|
5
|
+
Author-email: Yuchan Lee <lasuillard@gmail.com>
|
|
6
|
+
Requires-Python: <4.0,>=3.8
|
|
7
|
+
Requires-Dist: django<5.2,>=4.2
|
|
8
|
+
Requires-Dist: slack-bolt<2,>=1
|
|
9
|
+
Requires-Dist: xmltodict<1,>=0.14.1
|
|
10
|
+
Provides-Extra: celery
|
|
11
|
+
Requires-Dist: celery<6,>=5; extra == 'celery'
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: django-debug-toolbar~=4.4; extra == 'dev'
|
|
14
|
+
Requires-Dist: django-extensions~=3.2; extra == 'dev'
|
|
15
|
+
Requires-Dist: django-stubs[compatible-mypy]~=5.1; extra == 'dev'
|
|
16
|
+
Requires-Dist: ipykernel~=6.27; extra == 'dev'
|
|
17
|
+
Requires-Dist: mkdocs-material~=9.5; extra == 'dev'
|
|
18
|
+
Requires-Dist: mkdocstrings[python]~=0.26; extra == 'dev'
|
|
19
|
+
Requires-Dist: mkdocs~=1.6; extra == 'dev'
|
|
20
|
+
Requires-Dist: mypy~=1.11; extra == 'dev'
|
|
21
|
+
Requires-Dist: ruff~=0.6; extra == 'dev'
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: coverage~=7.3; extra == 'test'
|
|
24
|
+
Requires-Dist: django-coverage-plugin~=3.1; extra == 'test'
|
|
25
|
+
Requires-Dist: factory-boy~=3.3; extra == 'test'
|
|
26
|
+
Requires-Dist: faker~=30.3; extra == 'test'
|
|
27
|
+
Requires-Dist: pytest-cov~=5.0; extra == 'test'
|
|
28
|
+
Requires-Dist: pytest-django~=4.9; extra == 'test'
|
|
29
|
+
Requires-Dist: pytest-sugar~=1.0; extra == 'test'
|
|
30
|
+
Requires-Dist: pytest-xdist~=3.6; extra == 'test'
|
|
31
|
+
Requires-Dist: pytest~=8.0; extra == 'test'
|
|
19
32
|
Description-Content-Type: text/markdown
|
|
20
33
|
|
|
21
34
|
# django-slack-tools
|
|
@@ -23,6 +36,7 @@ Description-Content-Type: text/markdown
|
|
|
23
36
|
[](https://opensource.org/licenses/MIT)
|
|
24
37
|
[](https://github.com/lasuillard/django-slack-tools/actions/workflows/ci.yaml)
|
|
25
38
|
[](https://codecov.io/gh/lasuillard/django-slack-tools)
|
|
39
|
+

|
|
26
40
|
|
|
27
41
|
Little helpers working with Slack bot 🤖 in Django.
|
|
28
42
|
|
|
@@ -40,19 +54,24 @@ Key features are:
|
|
|
40
54
|
|
|
41
55
|
- [x] Built-in admin for management working with Slack workspace
|
|
42
56
|
|
|
43
|
-
|
|
57
|
+
- [x] Celery support for messaging backends, management and shortcut tasks, etc.
|
|
44
58
|
|
|
45
|
-
- [
|
|
59
|
+
- [x] Django template support
|
|
46
60
|
|
|
47
|
-
|
|
61
|
+
And more in future roadmap...
|
|
48
62
|
|
|
49
63
|
- [ ] New Django apps and helpers for Slack features such as modals, event subscription, etc.
|
|
50
64
|
|
|
65
|
+
- [ ] More fine working example with rich documentation
|
|
66
|
+
|
|
51
67
|
Currently it is focused on messaging features. In future, hoping to bring more helpful features across Slack Bot ecosystem, such as event subscriptions, modals, bot interactions, etc.
|
|
52
68
|
|
|
53
69
|
## 🚀 Installation
|
|
54
70
|
|
|
55
|
-
**django-slack-tools** supports Python 3.8+ and Django
|
|
71
|
+
**django-slack-tools** supports Python 3.8+ and Django 4.2+. Supports for each deps will be dropped as soon as the ends of security updates.
|
|
72
|
+
|
|
73
|
+
> [!WARNING]
|
|
74
|
+
> 0.x versions are for development. Breaking changes can be made at any time. If gonna use this package, recommend to pin down the version.
|
|
56
75
|
|
|
57
76
|
Install the package:
|
|
58
77
|
|
|
@@ -110,4 +129,3 @@ All contributions and helps are welcome. Please check the [CONTRIBUTING.md](./CO
|
|
|
110
129
|
## 📜 License
|
|
111
130
|
|
|
112
131
|
This project is licensed under the terms of the MIT license.
|
|
113
|
-
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
django_slack_tools/__init__.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
|
|
2
|
+
django_slack_tools/app_settings.py,sha256=l95cavui0BzvDLrz_DPB_rnci4iSV2THkdTWh3Pcx4U,3771
|
|
3
|
+
django_slack_tools/views.py,sha256=XuQh0g9A2O51DVkMUhm8x8cswZytZcECNIsDuLsM0UI,1168
|
|
4
|
+
django_slack_tools/locale/ko_KR/LC_MESSAGES/django.po,sha256=ySlOJgCeqFzesrWLm1BugmdLb878GSGQJZp7a1uXopw,14864
|
|
5
|
+
django_slack_tools/slack_messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
django_slack_tools/slack_messages/apps.py,sha256=nLVxuLE-1VIqkcVYEdtlg6ffD3blonGCMKaTd_F5jis,610
|
|
7
|
+
django_slack_tools/slack_messages/message.py,sha256=W6ZiwUF9R4jFNLdoUxUmNZdLHr4qENFJzVREd1yalRM,4170
|
|
8
|
+
django_slack_tools/slack_messages/tasks.py,sha256=EOj84Svncpa3I1ZWSKqlzYydFMhT59z2XkOro0ZwIv8,3316
|
|
9
|
+
django_slack_tools/slack_messages/admin/__init__.py,sha256=FEBZlgze8Nf5wn8ixm9FcsL3na2WYykmm2U-iJXh_mQ,305
|
|
10
|
+
django_slack_tools/slack_messages/admin/mention.py,sha256=Z3Cx-6WKECsbXnyby1qCEMllvxJEIn_TdwexsjEaiIo,4399
|
|
11
|
+
django_slack_tools/slack_messages/admin/message.py,sha256=bC4LHhUc07oq2RaWDPZnxAqrthXXofjqlTpfkkADs2o,4126
|
|
12
|
+
django_slack_tools/slack_messages/admin/message_recipient.py,sha256=CVhvUGT4_jD3kJ2CEW8f-DnG3hcsbFbtlccfL_TcS10,4035
|
|
13
|
+
django_slack_tools/slack_messages/admin/messaging_policy.py,sha256=NRzKGgGMCMfABXZrN9okDXb9NvsyxWFLt8hBRfFzwug,4576
|
|
14
|
+
django_slack_tools/slack_messages/backends/__init__.py,sha256=5wIJbp1n6_3kp1nk8x4LyFDqiHw1H13XsqwuIUiqeJo,276
|
|
15
|
+
django_slack_tools/slack_messages/backends/base.py,sha256=VE1xjxrlaE-4fv_ynf-3S6f7h6NXiQIUdcc-yPVsFN4,8179
|
|
16
|
+
django_slack_tools/slack_messages/backends/dummy.py,sha256=OE9pMhAZH8sAewseK_yuPXDgkSHvLH_X_vIItbwpaMc,1155
|
|
17
|
+
django_slack_tools/slack_messages/backends/logging.py,sha256=2P-pEJFI5r6-45rXt43tzQHzokT9wRlEmhSXZIWvvgU,903
|
|
18
|
+
django_slack_tools/slack_messages/backends/slack.py,sha256=uRfrQSDj7Tf2lB76uD_Ma51dplsoIKViolGR26-KOL4,4948
|
|
19
|
+
django_slack_tools/slack_messages/migrations/0001_initial.py,sha256=mLH8mk__OajzisYuO5j0Y-hmgMOM9ANJSKV-q32-FVY,11594
|
|
20
|
+
django_slack_tools/slack_messages/migrations/0002_default_policy.py,sha256=a1pf6bGhFszOz2awUudQnm9FyxIdWcs3ILsWCADG4YU,1145
|
|
21
|
+
django_slack_tools/slack_messages/migrations/0003_default_recipient.py,sha256=SqqawYxEHItmCYAgfv-EAsnc7Fxg3mJtKw6ccDCF1Eo,1153
|
|
22
|
+
django_slack_tools/slack_messages/migrations/0004_slackmessagingpolicy_template_type.py,sha256=u5WkhdPxzXLraw8oTvC0sBV5GUFR0fDghtoc2RvTFQU,679
|
|
23
|
+
django_slack_tools/slack_messages/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
django_slack_tools/slack_messages/models/__init__.py,sha256=5niO9vU-YMGRCt7wQMXbN-hSrLpJLtukMKjjv-aFeaQ,265
|
|
25
|
+
django_slack_tools/slack_messages/models/mention.py,sha256=C_nowbRv7JU-tD-5boJKu-e3bqG_kTGrrB1WE0ZX1Us,2077
|
|
26
|
+
django_slack_tools/slack_messages/models/message.py,sha256=Ih9lHqJ6M6kKuwRw5_mnoY2kT_RVglUJfOxxpd0vZE4,3644
|
|
27
|
+
django_slack_tools/slack_messages/models/message_recipient.py,sha256=FxaSIOAhZPSa_Tib58P4WXAs0zn8GsK4UgxOjktyduE,1666
|
|
28
|
+
django_slack_tools/slack_messages/models/messaging_policy.py,sha256=Za0wJs5xQ2TWAwUzHuHom98FxdTW4TspgUL387c4mmE,2954
|
|
29
|
+
django_slack_tools/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
django_slack_tools/utils/model_mixins.py,sha256=bNPx3yudzUku8TnjstestOs6YEww58FmBUypL5caYdI,634
|
|
31
|
+
django_slack_tools/utils/widgets.py,sha256=v1EorzDSDXn_ZZgBHVFYMhWPG5RsfEI3L3VFf282svg,876
|
|
32
|
+
django_slack_tools/utils/slack/__init__.py,sha256=_3oJZqfw3VpuXjDT3vEUnjGT3qK1rYhTwCh268f6Jmk,279
|
|
33
|
+
django_slack_tools/utils/slack/django.py,sha256=H6OiGyMYLD6mpZhpPSiosSd7AczBoCXqQHEKhW_LSQE,743
|
|
34
|
+
django_slack_tools/utils/slack/message.py,sha256=FkbM9okHOwJ1NDcg8eX3H3uCA47L-jzu6P-70AFtpqQ,3243
|
|
35
|
+
django_slack_tools/utils/slack/misc.py,sha256=bl7xp43IsTt0FUZWfGH9kk4qYMU0UjIhuGs35zkLyyE,1099
|
|
36
|
+
django_slack_tools/utils/template/__init__.py,sha256=4aMW_Qum7p_-5hm0s5rQXOVSikAwzVBM0MWLmQY93Mo,159
|
|
37
|
+
django_slack_tools/utils/template/base.py,sha256=Ql14Z_ZRnsXS3W724hTbPebzqBoxewf98GaXyny8rYg,422
|
|
38
|
+
django_slack_tools/utils/template/dict.py,sha256=99vNwlO4WzDZuhPvPIcrwkB-sRYl0Ci5tOnW4sqYNTY,1262
|
|
39
|
+
django_slack_tools/utils/template/django.py,sha256=971UIqU8wMtcPT_K2yhn4VdnZB8fOAe28q1YbSoKS58,4104
|
|
40
|
+
django_slack_tools-0.2.0.dist-info/METADATA,sha256=vD4jf9QTxIIEUXY-ep0OejeZcGDsGL4mowB0tQKO46A,4493
|
|
41
|
+
django_slack_tools-0.2.0.dist-info/WHEEL,sha256=3U_NnUcV_1B1kPkYaPzN-irRckL5VW_lytn0ytO_kRY,87
|
|
42
|
+
django_slack_tools-0.2.0.dist-info/licenses/LICENSE,sha256=lPgIV-vu4gapOeftp1Dp5RsN6zlsiMiubqCNeP466hw,1067
|
|
43
|
+
django_slack_tools-0.2.0.dist-info/RECORD,,
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"""Utils for dictionary-based templates."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from typing import Any, TypeVar
|
|
6
|
-
|
|
7
|
-
from django.core.exceptions import ValidationError
|
|
8
|
-
from django.utils.translation import gettext_lazy as _
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def render(template: dict, **kwargs: Any) -> dict:
|
|
12
|
-
"""Render given dictionary template.
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
template: Dictionary template.
|
|
16
|
-
kwargs: Keyword arguments passed to template.
|
|
17
|
-
|
|
18
|
-
Returns:
|
|
19
|
-
Rendered dictionary.
|
|
20
|
-
"""
|
|
21
|
-
# Forget the original to format it in-place
|
|
22
|
-
result = template.copy()
|
|
23
|
-
for k, v in result.items():
|
|
24
|
-
result[k] = _format_obj(v, **kwargs)
|
|
25
|
-
|
|
26
|
-
return result
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def dict_template_validator(value: Any) -> None:
|
|
30
|
-
"""Validate given value is valid dictionary template."""
|
|
31
|
-
if value is None: # No-op template, should work equally to empty dict `{}`
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
if not isinstance(value, dict):
|
|
35
|
-
raise ValidationError(
|
|
36
|
-
_("Given object is not a dictionary: %(value)r"),
|
|
37
|
-
params={"value": value},
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
T = TypeVar("T", dict, list, str)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def _format_obj(obj: T, **kwargs: Any) -> T:
|
|
45
|
-
"""Format object recursively."""
|
|
46
|
-
if isinstance(obj, dict):
|
|
47
|
-
return {k: _format_obj(v, **kwargs) for k, v in obj.items()}
|
|
48
|
-
|
|
49
|
-
if isinstance(obj, str):
|
|
50
|
-
return obj.format_map(kwargs)
|
|
51
|
-
|
|
52
|
-
if isinstance(obj, list):
|
|
53
|
-
return [_format_obj(item, **kwargs) for item in obj]
|
|
54
|
-
|
|
55
|
-
return obj
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
django_slack_tools/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
2
|
-
django_slack_tools/app_settings.py,sha256=xpYTTXFXkxWuVRMdsEYAIURFEAzXcwh1T_57_kXN6Po,2770
|
|
3
|
-
django_slack_tools/locale/ko_KR/LC_MESSAGES/django.po,sha256=ySlOJgCeqFzesrWLm1BugmdLb878GSGQJZp7a1uXopw,14864
|
|
4
|
-
django_slack_tools/slack_messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
django_slack_tools/slack_messages/admin/__init__.py,sha256=FEBZlgze8Nf5wn8ixm9FcsL3na2WYykmm2U-iJXh_mQ,305
|
|
6
|
-
django_slack_tools/slack_messages/admin/mention.py,sha256=Z3Cx-6WKECsbXnyby1qCEMllvxJEIn_TdwexsjEaiIo,4399
|
|
7
|
-
django_slack_tools/slack_messages/admin/message.py,sha256=htZcVsMbKgoaU0fHngtdFpko2-Zey4OSYiv5NIL0jkw,4105
|
|
8
|
-
django_slack_tools/slack_messages/admin/message_recipient.py,sha256=CVhvUGT4_jD3kJ2CEW8f-DnG3hcsbFbtlccfL_TcS10,4035
|
|
9
|
-
django_slack_tools/slack_messages/admin/messaging_policy.py,sha256=NRzKGgGMCMfABXZrN9okDXb9NvsyxWFLt8hBRfFzwug,4576
|
|
10
|
-
django_slack_tools/slack_messages/apps.py,sha256=scqTS4E6J3q9GnGXYepS1m9j8gOXooFwgsvWk8EHdKM,336
|
|
11
|
-
django_slack_tools/slack_messages/backends/__init__.py,sha256=KOMKqgsUkAqBG4jc2nvW4Y-6Msqhs38fdgW7HtB4YZc,276
|
|
12
|
-
django_slack_tools/slack_messages/backends/base.py,sha256=GZuw3TS5rci-iP_jt-Sl6h0Tb0dedSJvVb7fxtJqZtc,3079
|
|
13
|
-
django_slack_tools/slack_messages/backends/dummy.py,sha256=lpaWP56q7cOjfALQpGH-EjlI6IfOX0wXPIL6HXRb-H4,1196
|
|
14
|
-
django_slack_tools/slack_messages/backends/logging.py,sha256=2P-pEJFI5r6-45rXt43tzQHzokT9wRlEmhSXZIWvvgU,903
|
|
15
|
-
django_slack_tools/slack_messages/backends/slack.py,sha256=Af2so4fg2i9TGenH7CiVgBmmVyKNjJ9vmQ1IPnNKkLU,7414
|
|
16
|
-
django_slack_tools/slack_messages/message.py,sha256=eNxxLFYL-0NnOC98Kevciny_JuP07Tyr9dxM5PuSM7o,4428
|
|
17
|
-
django_slack_tools/slack_messages/migrations/0001_initial.py,sha256=hAh5QUeMnK5LkbtwXEWFbe1MrBIuI-t8oisHseozQkE,11741
|
|
18
|
-
django_slack_tools/slack_messages/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
-
django_slack_tools/slack_messages/models/__init__.py,sha256=5niO9vU-YMGRCt7wQMXbN-hSrLpJLtukMKjjv-aFeaQ,265
|
|
20
|
-
django_slack_tools/slack_messages/models/mention.py,sha256=C_nowbRv7JU-tD-5boJKu-e3bqG_kTGrrB1WE0ZX1Us,2077
|
|
21
|
-
django_slack_tools/slack_messages/models/message.py,sha256=Ih9lHqJ6M6kKuwRw5_mnoY2kT_RVglUJfOxxpd0vZE4,3644
|
|
22
|
-
django_slack_tools/slack_messages/models/message_recipient.py,sha256=FxaSIOAhZPSa_Tib58P4WXAs0zn8GsK4UgxOjktyduE,1666
|
|
23
|
-
django_slack_tools/slack_messages/models/messaging_policy.py,sha256=nK1gT25VnJeHCEXbu2RQIlrDVdnh6Yy568WR1nC3qYE,2356
|
|
24
|
-
django_slack_tools/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
-
django_slack_tools/utils/dict_template.py,sha256=smiSi0bUPT2dNKFzMjhAOFyxlKV9A8koHDas9u03Fp8,1421
|
|
26
|
-
django_slack_tools/utils/model_mixins.py,sha256=bNPx3yudzUku8TnjstestOs6YEww58FmBUypL5caYdI,634
|
|
27
|
-
django_slack_tools/utils/slack/__init__.py,sha256=_3oJZqfw3VpuXjDT3vEUnjGT3qK1rYhTwCh268f6Jmk,279
|
|
28
|
-
django_slack_tools/utils/slack/django.py,sha256=tqVXM6mTZXWQk3AN73-7s-3ZZbsFASVwr9fxc7lOK8g,1289
|
|
29
|
-
django_slack_tools/utils/slack/message.py,sha256=72ENRVJv8j2Yi88eYuyPCfi7TEPp01D-tCWbCUMr8PA,1541
|
|
30
|
-
django_slack_tools/utils/slack/misc.py,sha256=bl7xp43IsTt0FUZWfGH9kk4qYMU0UjIhuGs35zkLyyE,1099
|
|
31
|
-
django_slack_tools/utils/widgets.py,sha256=v1EorzDSDXn_ZZgBHVFYMhWPG5RsfEI3L3VFf282svg,876
|
|
32
|
-
django_slack_tools/views.py,sha256=F1mWkKbyp9zn27DzoYe2rccD8O_M0ANSzBKhbaEOYC4,1104
|
|
33
|
-
django_slack_tools-0.1.0.dist-info/LICENSE,sha256=lPgIV-vu4gapOeftp1Dp5RsN6zlsiMiubqCNeP466hw,1067
|
|
34
|
-
django_slack_tools-0.1.0.dist-info/METADATA,sha256=DyhN8t2VPhUCkpdUpqw9fi71CA1eyFQ7jOhi1fmvO8Y,3574
|
|
35
|
-
django_slack_tools-0.1.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
36
|
-
django_slack_tools-0.1.0.dist-info/RECORD,,
|
|
File without changes
|