python-datamodel 0.10.1__cp312-cp312-win32.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.
- datamodel/__init__.py +13 -0
- datamodel/abstract.py +383 -0
- datamodel/adaptive/__init__.py +0 -0
- datamodel/adaptive/models.py +598 -0
- datamodel/aliases/__init__.py +26 -0
- datamodel/base.py +180 -0
- datamodel/converters.c +43471 -0
- datamodel/converters.cp312-win32.pyd +0 -0
- datamodel/converters.html +17387 -0
- datamodel/converters.pyx +1489 -0
- datamodel/exceptions.c +13455 -0
- datamodel/exceptions.cp312-win32.pyd +0 -0
- datamodel/exceptions.html +1261 -0
- datamodel/exceptions.pxd +13 -0
- datamodel/exceptions.pyx +50 -0
- datamodel/fields.cp312-win32.pyd +0 -0
- datamodel/fields.cpp +17401 -0
- datamodel/fields.html +3912 -0
- datamodel/fields.pyx +309 -0
- datamodel/functions.cp312-win32.pyd +0 -0
- datamodel/functions.cpp +9068 -0
- datamodel/functions.html +1766 -0
- datamodel/functions.pxd +9 -0
- datamodel/functions.pyx +82 -0
- datamodel/jsonld/__init__.py +45 -0
- datamodel/jsonld/models.py +500 -0
- datamodel/libs/__init__.py +1 -0
- datamodel/libs/mapping.c +15067 -0
- datamodel/libs/mapping.cp312-win32.pyd +0 -0
- datamodel/libs/mapping.html +2618 -0
- datamodel/libs/mapping.pxd +11 -0
- datamodel/libs/mapping.pyx +135 -0
- datamodel/libs/mutables.py +127 -0
- datamodel/models.py +814 -0
- datamodel/parsers/__init__.py +0 -0
- datamodel/parsers/encoders.py +15 -0
- datamodel/parsers/json.cp312-win32.pyd +0 -0
- datamodel/parsers/json.cpp +17004 -0
- datamodel/parsers/json.html +3365 -0
- datamodel/parsers/json.pyx +250 -0
- datamodel/profiler.py +21 -0
- datamodel/py.typed +0 -0
- datamodel/rs_core/Cargo.toml +17 -0
- datamodel/rs_core/src/lib.rs +294 -0
- datamodel/rs_parsers/Cargo.toml +22 -0
- datamodel/rs_parsers/src/lib.rs +571 -0
- datamodel/rs_parsers.cp312-win32.pyd +0 -0
- datamodel/rs_validators/Cargo.toml +17 -0
- datamodel/rs_validators/src/lib.rs +0 -0
- datamodel/typedefs/__init__.py +9 -0
- datamodel/typedefs/singleton.c +9169 -0
- datamodel/typedefs/singleton.cp312-win32.pyd +0 -0
- datamodel/typedefs/singleton.html +629 -0
- datamodel/typedefs/singleton.pxd +9 -0
- datamodel/typedefs/singleton.pyx +24 -0
- datamodel/typedefs/types.c +11716 -0
- datamodel/typedefs/types.cp312-win32.pyd +0 -0
- datamodel/typedefs/types.html +732 -0
- datamodel/typedefs/types.pxd +11 -0
- datamodel/typedefs/types.pyx +39 -0
- datamodel/types.c +7165 -0
- datamodel/types.cp312-win32.pyd +0 -0
- datamodel/types.html +716 -0
- datamodel/types.pyx +100 -0
- datamodel/validation.cp312-win32.pyd +0 -0
- datamodel/validation.cpp +17085 -0
- datamodel/validation.html +4769 -0
- datamodel/validation.pyx +315 -0
- datamodel/version.py +13 -0
- examples/nn/examples.py +311 -0
- examples/nn/stores.py +151 -0
- examples/tests/sp_types.py +294 -0
- examples/tests/speed_dates.py +26 -0
- python_datamodel-0.10.1.dist-info/LICENSE +29 -0
- python_datamodel-0.10.1.dist-info/METADATA +320 -0
- python_datamodel-0.10.1.dist-info/RECORD +78 -0
- python_datamodel-0.10.1.dist-info/WHEEL +5 -0
- python_datamodel-0.10.1.dist-info/top_level.txt +7 -0
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
from uuid import UUID, uuid4
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import List, Dict, Any, Optional, Union
|
|
4
|
+
import contextlib
|
|
5
|
+
from ..base import BaseModel, Field, register_renderer
|
|
6
|
+
|
|
7
|
+
# https://adaptivecards.io/explorer/Media.html
|
|
8
|
+
|
|
9
|
+
def auto_uuid(*args, **kwargs): # pylint: disable=W0613
|
|
10
|
+
return uuid4()
|
|
11
|
+
|
|
12
|
+
class ContainerStyle(str, Enum):
|
|
13
|
+
"""Shared Style for all containers in a Adaptive Card."""
|
|
14
|
+
default = 'default'
|
|
15
|
+
accent = 'accent'
|
|
16
|
+
good = 'good'
|
|
17
|
+
attention = 'attention'
|
|
18
|
+
warning = 'warning'
|
|
19
|
+
emphasis = 'emphasis'
|
|
20
|
+
|
|
21
|
+
class ContainerColor(str, Enum):
|
|
22
|
+
"""Shared Color for all containers in a Adaptive Card."""
|
|
23
|
+
default = 'default'
|
|
24
|
+
dark = 'dark'
|
|
25
|
+
light = 'light'
|
|
26
|
+
accent = 'accent'
|
|
27
|
+
good = 'good'
|
|
28
|
+
attention = 'attention'
|
|
29
|
+
warning = 'warning'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class FontType(str, Enum):
|
|
33
|
+
"""Font Type for TextBlock."""
|
|
34
|
+
default = 'default'
|
|
35
|
+
monospace = 'monospace'
|
|
36
|
+
|
|
37
|
+
class FontSize(str, Enum):
|
|
38
|
+
"""Font Size for TextBlock."""
|
|
39
|
+
default = 'default'
|
|
40
|
+
small = 'small'
|
|
41
|
+
medium = 'medium'
|
|
42
|
+
large = 'large'
|
|
43
|
+
extraLarge = 'extraLarge'
|
|
44
|
+
|
|
45
|
+
class FontWeight(str, Enum):
|
|
46
|
+
"""Font Weight for TextBlock."""
|
|
47
|
+
default = 'default'
|
|
48
|
+
lighter = 'lighter'
|
|
49
|
+
bolder = 'bolder'
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SelectAction(str, Enum):
|
|
53
|
+
"""Action to be taken when a cell is selected."""
|
|
54
|
+
exectute = 'Action.Execute'
|
|
55
|
+
submit = 'Action.Submit'
|
|
56
|
+
openUrl = 'Action.OpenUrl'
|
|
57
|
+
toggleVisibility = 'Action.ToggleVisibility'
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class CardElement(BaseModel):
|
|
61
|
+
"""Base class for Adaptive card elements."""
|
|
62
|
+
type: str
|
|
63
|
+
|
|
64
|
+
class Meta:
|
|
65
|
+
as_objects: True
|
|
66
|
+
remove_nulls = True
|
|
67
|
+
|
|
68
|
+
def __post_init__(self):
|
|
69
|
+
if not self.type:
|
|
70
|
+
self.type = self.__class__.__name__
|
|
71
|
+
return super().__post_init__()
|
|
72
|
+
|
|
73
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
74
|
+
"""Convert to AdaptiveCard JSON-representation."""
|
|
75
|
+
return self.to_dict()
|
|
76
|
+
|
|
77
|
+
class TextBlock(CardElement):
|
|
78
|
+
text: str
|
|
79
|
+
size: Optional[str] = None
|
|
80
|
+
weight: Optional[str] = None
|
|
81
|
+
horizontalAlignment: Optional[str] = None
|
|
82
|
+
wrap: Optional[bool] = Field(default=True)
|
|
83
|
+
style: Optional[str] = None
|
|
84
|
+
isSubtle: Optional[bool] = None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class TextRun(CardElement):
|
|
88
|
+
"""Defines a single run of formatted text."""
|
|
89
|
+
type: str = Field(default='TextRun')
|
|
90
|
+
text: str
|
|
91
|
+
color: Optional[ContainerStyle] = Field(ContainerStyle.default)
|
|
92
|
+
fontType: Optional[FontType] = Field(FontType.default)
|
|
93
|
+
highlight: bool = Field(default=False)
|
|
94
|
+
isSubtle: bool = Field(default=False)
|
|
95
|
+
italic: bool = Field(default=False)
|
|
96
|
+
strikethrough: bool = Field(default=False)
|
|
97
|
+
underline: bool = Field(default=False)
|
|
98
|
+
selectAction: Optional[SelectAction] = Field(default=None)
|
|
99
|
+
size: Optional[FontSize] = Field(default=None)
|
|
100
|
+
weight: Optional[FontWeight] = Field(default=FontWeight.default)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class RichTextBlock(CardElement):
|
|
104
|
+
type: str = Field(default='RichTextBlock')
|
|
105
|
+
inlines: List[Union[str, TextRun]] = Field(default_factory=list)
|
|
106
|
+
horizontalAlignment: Optional[str] = Field(default='left')
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class Column(CardElement):
|
|
110
|
+
items: Optional[List[CardElement]] = Field(default_factory=list)
|
|
111
|
+
width: Optional[str] = Field(default='auto')
|
|
112
|
+
|
|
113
|
+
def add_item(self, item: Union[CardElement, List[CardElement]]):
|
|
114
|
+
if isinstance(item, list):
|
|
115
|
+
self.items.extend(item)
|
|
116
|
+
else:
|
|
117
|
+
self.items.append(item)
|
|
118
|
+
|
|
119
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
120
|
+
"""Ensure proper JSON serialization."""
|
|
121
|
+
return {
|
|
122
|
+
"type": "Column",
|
|
123
|
+
"items": [
|
|
124
|
+
item.to_adaptive() for item in self.items if isinstance(item, CardElement) # noqa
|
|
125
|
+
], # Prevent duplicates
|
|
126
|
+
"width": self.width
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class InputElement(CardElement):
|
|
131
|
+
"""Base class for input elements."""
|
|
132
|
+
type: str = Field(default='Input.Text')
|
|
133
|
+
id: str
|
|
134
|
+
label: Optional[str] = None
|
|
135
|
+
placeholder: Optional[str] = None
|
|
136
|
+
|
|
137
|
+
class InputText(InputElement):
|
|
138
|
+
style: Optional[str] = None
|
|
139
|
+
isRequired: Optional[bool] = None
|
|
140
|
+
errorMessage: Optional[str] = None
|
|
141
|
+
isMultiline: Optional[bool] = None
|
|
142
|
+
|
|
143
|
+
class InputNumber(InputElement):
|
|
144
|
+
min: Optional[int] = None
|
|
145
|
+
max: Optional[int] = None
|
|
146
|
+
value: Optional[int] = None
|
|
147
|
+
errorMessage: Optional[str] = None
|
|
148
|
+
|
|
149
|
+
class InputDate(InputElement):
|
|
150
|
+
value: Optional[str] = None
|
|
151
|
+
|
|
152
|
+
class InputTime(InputElement):
|
|
153
|
+
value: Optional[str] = None
|
|
154
|
+
|
|
155
|
+
class Choice(BaseModel):
|
|
156
|
+
title: str
|
|
157
|
+
value: str
|
|
158
|
+
|
|
159
|
+
class InputChoiceSet(InputElement):
|
|
160
|
+
choices: List[Choice]
|
|
161
|
+
value: Optional[str] = None
|
|
162
|
+
isMultiSelect: Optional[bool] = None
|
|
163
|
+
style: Optional[str] = None
|
|
164
|
+
|
|
165
|
+
class InputToggle(InputElement):
|
|
166
|
+
title: str
|
|
167
|
+
valueOn: str
|
|
168
|
+
valueOff: str
|
|
169
|
+
isRequired: Optional[bool] = None
|
|
170
|
+
errorMessage: Optional[str] = None
|
|
171
|
+
|
|
172
|
+
class FactElement(BaseModel):
|
|
173
|
+
"""A single fact."""
|
|
174
|
+
title: str
|
|
175
|
+
value: str
|
|
176
|
+
|
|
177
|
+
class Image(CardElement):
|
|
178
|
+
type: str = Field(default='Image')
|
|
179
|
+
url: str
|
|
180
|
+
size: Optional[str] = None
|
|
181
|
+
style: Optional[str] = None
|
|
182
|
+
altText: Optional[str] = None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class MediaSource(CardElement):
|
|
186
|
+
mimeType: str
|
|
187
|
+
url: str
|
|
188
|
+
|
|
189
|
+
class CaptionSource(CardElement):
|
|
190
|
+
mimeType: str = Field(default='vtt')
|
|
191
|
+
label: str = Field(default='English')
|
|
192
|
+
url: str
|
|
193
|
+
|
|
194
|
+
class Media(CardElement):
|
|
195
|
+
type: str = Field(default='Media')
|
|
196
|
+
sources: List[MediaSource] = Field(default_factory=list)
|
|
197
|
+
poster: Optional[str] = None
|
|
198
|
+
captionSources: List[CaptionSource] = Field(default_factory=list)
|
|
199
|
+
|
|
200
|
+
def add_source(self, source: Union[str, MediaSource]):
|
|
201
|
+
if isinstance(source, str):
|
|
202
|
+
source = MediaSource(url=source, mimeType='video/mp4')
|
|
203
|
+
self.sources.append(source)
|
|
204
|
+
return self
|
|
205
|
+
|
|
206
|
+
class ImageFillMode(str, Enum):
|
|
207
|
+
"""Describes how the image should be filled."""
|
|
208
|
+
cover = 'cover'
|
|
209
|
+
repeatHorizontally = 'repeatHorizontally'
|
|
210
|
+
repeatVertically = 'repeatVertically'
|
|
211
|
+
repeat = 'repeat'
|
|
212
|
+
|
|
213
|
+
class BackgroundImage(CardElement):
|
|
214
|
+
type: str = Field(default='BackgroundImage')
|
|
215
|
+
url: str
|
|
216
|
+
fillMode: Optional[ImageFillMode] = Field(default=ImageFillMode.cover)
|
|
217
|
+
|
|
218
|
+
## Table structure:
|
|
219
|
+
|
|
220
|
+
class TableCell(BaseModel):
|
|
221
|
+
"""A single cell in a TableRow."""
|
|
222
|
+
type: str = Field(default='TableCell')
|
|
223
|
+
style: Optional[ContainerStyle] = Field(default=ContainerStyle.default)
|
|
224
|
+
selectAction: Optional[SelectAction] = Field(default=None)
|
|
225
|
+
items: List[CardElement] = Field(default_factory=list)
|
|
226
|
+
backgroundImage: Union[BackgroundImage, str] = Field(default=None)
|
|
227
|
+
|
|
228
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
229
|
+
return {
|
|
230
|
+
"type": self.type,
|
|
231
|
+
"items": [item.to_adaptive() for item in self.items]
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class TableRow(BaseModel):
|
|
236
|
+
"""A row in a Table."""
|
|
237
|
+
type: str = Field(default='TableRow')
|
|
238
|
+
cells: List[TableCell] = Field(default_factory=list)
|
|
239
|
+
|
|
240
|
+
def add_cell(self, cell: TableCell):
|
|
241
|
+
self.cells.append(cell)
|
|
242
|
+
|
|
243
|
+
def new_cell(self, block: Union[CardElement, List[CardElement]]):
|
|
244
|
+
if isinstance(block, CardElement):
|
|
245
|
+
block = [block]
|
|
246
|
+
cell = TableCell(items=block)
|
|
247
|
+
self.cells.append(cell)
|
|
248
|
+
|
|
249
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
250
|
+
return {
|
|
251
|
+
"type": self.type,
|
|
252
|
+
"cells": [cell.to_adaptive() for cell in self.cells]
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
## Containers:
|
|
256
|
+
class FactSet(CardElement):
|
|
257
|
+
"""A set of facts."""
|
|
258
|
+
type: str = Field(default='FactSet')
|
|
259
|
+
facts: List[FactElement] = Field(default_factory=list)
|
|
260
|
+
|
|
261
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
262
|
+
return {
|
|
263
|
+
"type": self.type,
|
|
264
|
+
"facts": [fact.to_dict() for fact in self.facts]
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
class ColumnSet(CardElement):
|
|
268
|
+
"""A set of columns."""
|
|
269
|
+
type: str = Field(default='ColumnSet')
|
|
270
|
+
columns: List[Column] = Field(default_factory=list)
|
|
271
|
+
|
|
272
|
+
def add_column(self, column: Union[Column, List[Column]]):
|
|
273
|
+
if isinstance(column, Column):
|
|
274
|
+
column = [column]
|
|
275
|
+
self.columns.extend(column)
|
|
276
|
+
# return the collection added of columns
|
|
277
|
+
return column
|
|
278
|
+
|
|
279
|
+
def create_column(self, content: CardElement):
|
|
280
|
+
"""Create a new column with a single item."""
|
|
281
|
+
column = Column()
|
|
282
|
+
column.add_item(content)
|
|
283
|
+
self.columns.append(column)
|
|
284
|
+
return column
|
|
285
|
+
|
|
286
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
287
|
+
return {
|
|
288
|
+
"type": self.type,
|
|
289
|
+
"columns": [column.to_adaptive() for column in self.columns]
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
class CardSection(CardElement):
|
|
293
|
+
type: str = Field(default='Container')
|
|
294
|
+
items: List[CardElement] = Field(default_factory=list)
|
|
295
|
+
facts: list[FactSet] = Field(default_factory=list)
|
|
296
|
+
|
|
297
|
+
def addFacts(self, facts: Union[FactSet, List[FactSet]]):
|
|
298
|
+
if isinstance(facts, FactSet):
|
|
299
|
+
facts = [facts]
|
|
300
|
+
self.facts.extend(facts)
|
|
301
|
+
|
|
302
|
+
def addItem(self, item: CardElement):
|
|
303
|
+
self.items.append(item)
|
|
304
|
+
|
|
305
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
306
|
+
return {
|
|
307
|
+
"type": self.type,
|
|
308
|
+
"items": [item.to_adaptive() for item in self.items]
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
class ColumnDefinition(BaseModel):
|
|
312
|
+
"""A column definition in a Table."""
|
|
313
|
+
width: str = Field(default='auto')
|
|
314
|
+
|
|
315
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
316
|
+
return {
|
|
317
|
+
"width": self.width
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
class Table(CardElement):
|
|
321
|
+
"""A table layout containing multiple rows."""
|
|
322
|
+
type: str = Field(default='Table')
|
|
323
|
+
columns: List[ColumnDefinition] = Field(default_factory=list)
|
|
324
|
+
rows: List[TableRow] = Field(default_factory=list)
|
|
325
|
+
firstRowAsHeader: bool = Field(default=True)
|
|
326
|
+
showGridLines: bool = Field(default=True)
|
|
327
|
+
gridStyle: Optional[ContainerStyle] = Field(default=ContainerStyle.default)
|
|
328
|
+
|
|
329
|
+
def add_row(self, row: TableRow):
|
|
330
|
+
self.rows.append(row)
|
|
331
|
+
|
|
332
|
+
def new_row(self, cells: Union[TableCell, List[TableCell]]):
|
|
333
|
+
if isinstance(cells, TableCell):
|
|
334
|
+
cells = [cells]
|
|
335
|
+
row = TableRow(cells=cells)
|
|
336
|
+
self.rows.append(row)
|
|
337
|
+
return row
|
|
338
|
+
|
|
339
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
340
|
+
# Ensure columns are generated based on the number of cells in the first row
|
|
341
|
+
num_columns = max(len(row.cells) for row in self.rows) if self.rows else 0
|
|
342
|
+
self.columns = [ColumnDefinition() for _ in range(num_columns)]
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
"type": self.type,
|
|
346
|
+
"gridStyle": self.gridStyle,
|
|
347
|
+
"firstRowAsHeader": self.firstRowAsHeader,
|
|
348
|
+
"showGridLines": self.showGridLines,
|
|
349
|
+
"columns": [column.to_adaptive() for column in self.columns],
|
|
350
|
+
"rows": [row.to_adaptive() for row in self.rows]
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
# Define a Enum for Image Sizes:
|
|
354
|
+
class ImageSize(str, Enum):
|
|
355
|
+
auto = 'auto'
|
|
356
|
+
stretch = 'stretch'
|
|
357
|
+
small = 'small'
|
|
358
|
+
medium = 'medium'
|
|
359
|
+
large = 'large'
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class ImageSet(CardElement):
|
|
363
|
+
"""A set of images displayed together."""
|
|
364
|
+
type: str = Field(default='ImageSet')
|
|
365
|
+
images: List['Image'] = Field(default_factory=list)
|
|
366
|
+
imageSize: Optional[ImageSize] = Field(default=ImageSize.auto)
|
|
367
|
+
|
|
368
|
+
def add_image(self, image: Union[Image, List[Image]]):
|
|
369
|
+
if isinstance(image, Image):
|
|
370
|
+
image = [image]
|
|
371
|
+
self.images.extend(image)
|
|
372
|
+
|
|
373
|
+
def new_image(self, images: Union[str, Image, tuple]):
|
|
374
|
+
if isinstance(images, str):
|
|
375
|
+
img = Image(
|
|
376
|
+
url=images,
|
|
377
|
+
altText=images
|
|
378
|
+
)
|
|
379
|
+
elif isinstance(images, tuple):
|
|
380
|
+
img = Image(
|
|
381
|
+
url=images[0],
|
|
382
|
+
altText=images[1]
|
|
383
|
+
)
|
|
384
|
+
else:
|
|
385
|
+
img = images
|
|
386
|
+
self.images.append(img)
|
|
387
|
+
|
|
388
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
389
|
+
return {
|
|
390
|
+
"type": self.type,
|
|
391
|
+
"images": [image.to_adaptive() for image in self.images],
|
|
392
|
+
"imageSize": self.imageSize
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
class CardAction(BaseModel):
|
|
397
|
+
"""Base class for Adaptive card actions."""
|
|
398
|
+
type: str
|
|
399
|
+
title: str
|
|
400
|
+
data: Optional[Dict[str, Any]] = Field(default=None)
|
|
401
|
+
card: Optional['AdaptiveCard'] = Field(default=None)
|
|
402
|
+
|
|
403
|
+
class Meta:
|
|
404
|
+
as_objects: True
|
|
405
|
+
remove_nulls = True
|
|
406
|
+
|
|
407
|
+
def __post_init__(self):
|
|
408
|
+
if not self.type:
|
|
409
|
+
self.type = f"Action.{self.__class__.__name__}"
|
|
410
|
+
return super().__post_init__()
|
|
411
|
+
|
|
412
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
413
|
+
"""Convert to AdaptiveCard JSON-representation."""
|
|
414
|
+
return self.to_dict()
|
|
415
|
+
|
|
416
|
+
### Main Class: AdaptiveCard
|
|
417
|
+
class AdaptiveCard(BaseModel):
|
|
418
|
+
card_id: UUID = Field(required=False, default=auto_uuid, repr=False)
|
|
419
|
+
content_type: str = Field(
|
|
420
|
+
default="application/vnd.microsoft.card.adaptive",
|
|
421
|
+
repr=False
|
|
422
|
+
)
|
|
423
|
+
schema: str = "http://adaptivecards.io/schemas/adaptive-card.json"
|
|
424
|
+
card_type: str = "AdaptiveCard"
|
|
425
|
+
title: str
|
|
426
|
+
summary: str
|
|
427
|
+
minHeight: Optional[str] = None
|
|
428
|
+
body: List[CardElement] = Field(default_factory=list)
|
|
429
|
+
backgroundImage: Optional[BackgroundImage] = Field(default=None)
|
|
430
|
+
body_objects: List[Dict[str, Any]] = Field(default_factory=list, repr=False)
|
|
431
|
+
actions: List[CardAction] = Field(default_factory=list)
|
|
432
|
+
sections: List[CardSection] = Field(default_factory=list)
|
|
433
|
+
version: str = Field(default="1.5")
|
|
434
|
+
|
|
435
|
+
def __post_init__(self):
|
|
436
|
+
if self.version == "1.6": # Forcing Version to MS Teams
|
|
437
|
+
self.version = "1.5"
|
|
438
|
+
return super().__post_init__()
|
|
439
|
+
|
|
440
|
+
def add_text(self, text: str, **kwargs):
|
|
441
|
+
self.body.append(
|
|
442
|
+
TextBlock(type="TextBlock", text=text, **kwargs)
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
def add_body_element(self, element: CardElement):
|
|
446
|
+
self.body.append(element)
|
|
447
|
+
return element
|
|
448
|
+
|
|
449
|
+
def add_background(self, background: Union[str, BackgroundImage]):
|
|
450
|
+
"""Adding a Background Image to the Card."""
|
|
451
|
+
if isinstance(background, str):
|
|
452
|
+
background = BackgroundImage(url=background)
|
|
453
|
+
self.backgroundImage = background
|
|
454
|
+
return background
|
|
455
|
+
|
|
456
|
+
def add_action(self, action: CardAction):
|
|
457
|
+
self.actions.append(action)
|
|
458
|
+
|
|
459
|
+
def add_section(self, **kwargs):
|
|
460
|
+
section = CardSection(**kwargs)
|
|
461
|
+
self.sections.append(section)
|
|
462
|
+
return section
|
|
463
|
+
|
|
464
|
+
def add_input(
|
|
465
|
+
self,
|
|
466
|
+
id: str,
|
|
467
|
+
label: str,
|
|
468
|
+
is_required: bool = False,
|
|
469
|
+
error_message: str = None,
|
|
470
|
+
input_type: str = 'Text',
|
|
471
|
+
style: str = None
|
|
472
|
+
):
|
|
473
|
+
element = {
|
|
474
|
+
"type": f"Input.{input_type}",
|
|
475
|
+
"id": id,
|
|
476
|
+
"label": label,
|
|
477
|
+
"isRequired": is_required
|
|
478
|
+
}
|
|
479
|
+
if error_message is not None:
|
|
480
|
+
element["errorMessage"] = error_message
|
|
481
|
+
if style is not None:
|
|
482
|
+
element["style"] = style
|
|
483
|
+
if element:
|
|
484
|
+
self.body_objects.append(
|
|
485
|
+
element
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
def to_dict(self) -> dict:
|
|
489
|
+
data = super().to_dict(remove_nulls=True)
|
|
490
|
+
del data['card_id']
|
|
491
|
+
del data['body_objects']
|
|
492
|
+
del data['actions']
|
|
493
|
+
del data['content_type']
|
|
494
|
+
del data['version']
|
|
495
|
+
data['type'] = data.pop('card_type')
|
|
496
|
+
data['@type'] = "MessageCard"
|
|
497
|
+
data['@context'] = "http://schema.org/extensions"
|
|
498
|
+
return data
|
|
499
|
+
|
|
500
|
+
def to_adaptive(self) -> Dict[str, Any]:
|
|
501
|
+
"""Convert to AdaptiveCard JSON-representation."""
|
|
502
|
+
# Build Body based on content:
|
|
503
|
+
body = []
|
|
504
|
+
properties = {}
|
|
505
|
+
if self.backgroundImage:
|
|
506
|
+
properties["backgroundImage"] = self.backgroundImage.to_dict()
|
|
507
|
+
if self.minHeight:
|
|
508
|
+
properties["minHeight"] = self.minHeight
|
|
509
|
+
if self.title:
|
|
510
|
+
body.append({
|
|
511
|
+
"type": "TextBlock",
|
|
512
|
+
"size": "Medium",
|
|
513
|
+
"weight": "Bolder",
|
|
514
|
+
"text": self.title,
|
|
515
|
+
"horizontalAlignment": "Center",
|
|
516
|
+
"wrap": True,
|
|
517
|
+
"style": "heading"
|
|
518
|
+
})
|
|
519
|
+
if self.summary:
|
|
520
|
+
body.append({
|
|
521
|
+
"type": "TextBlock",
|
|
522
|
+
"size": "large",
|
|
523
|
+
"weight": "bolder",
|
|
524
|
+
"text": self.summary
|
|
525
|
+
})
|
|
526
|
+
if self.body:
|
|
527
|
+
body.extend(element.to_adaptive() for element in self.body)
|
|
528
|
+
if self.sections:
|
|
529
|
+
body.extend(
|
|
530
|
+
{
|
|
531
|
+
"type": "Container",
|
|
532
|
+
"items": [section.to_adaptive() for section in self.sections]
|
|
533
|
+
}
|
|
534
|
+
)
|
|
535
|
+
if self.body_objects:
|
|
536
|
+
body.extend(iter(self.body_objects))
|
|
537
|
+
actions = {}
|
|
538
|
+
if self.actions:
|
|
539
|
+
actions = {
|
|
540
|
+
"actions": [action.to_dict() for action in self.actions]
|
|
541
|
+
}
|
|
542
|
+
# Iterate over each section, actions or body elements
|
|
543
|
+
return {
|
|
544
|
+
"$schema": self.schema,
|
|
545
|
+
"type": self.card_type,
|
|
546
|
+
"version": self.version,
|
|
547
|
+
"contentType": self.content_type,
|
|
548
|
+
"metadata": {
|
|
549
|
+
"webUrl": "https://contoso.com/tab"
|
|
550
|
+
},
|
|
551
|
+
**properties,
|
|
552
|
+
"body": body,
|
|
553
|
+
**actions
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
# Actions:
|
|
558
|
+
class Submit(CardAction):
|
|
559
|
+
type: str = Field(default='Action.Submit')
|
|
560
|
+
title: str = Field(default='Submit')
|
|
561
|
+
|
|
562
|
+
class OpenUrl(CardAction):
|
|
563
|
+
type: str = Field(default='Action.OpenUrl')
|
|
564
|
+
title: str
|
|
565
|
+
url: str
|
|
566
|
+
# role: str = Field(default='button')
|
|
567
|
+
|
|
568
|
+
class ShowCard(CardAction):
|
|
569
|
+
type: str = Field(default='Action.ShowCard')
|
|
570
|
+
title: str
|
|
571
|
+
card: AdaptiveCard
|
|
572
|
+
|
|
573
|
+
@property
|
|
574
|
+
def body(self):
|
|
575
|
+
return self.card.body
|
|
576
|
+
|
|
577
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
578
|
+
data = super().to_dict()
|
|
579
|
+
card = self.card.to_adaptive()
|
|
580
|
+
with contextlib.suppress(KeyError):
|
|
581
|
+
del card['version']
|
|
582
|
+
del card['contentType']
|
|
583
|
+
del card['metadata']
|
|
584
|
+
card['@type'] = "MessageCard"
|
|
585
|
+
card['@context'] = "http://schema.org/extensions"
|
|
586
|
+
data['card'] = card
|
|
587
|
+
return data
|
|
588
|
+
|
|
589
|
+
class ToggleVisibility(CardAction):
|
|
590
|
+
type: str = Field(default='Action.ToggleVisibility')
|
|
591
|
+
targetElements: List[str] = Field(default_factory=list)
|
|
592
|
+
|
|
593
|
+
class ActionSet(CardElement):
|
|
594
|
+
type: str = Field(default='ActionSet')
|
|
595
|
+
actions: List[CardAction] = Field(default_factory=list)
|
|
596
|
+
|
|
597
|
+
def to_adaptive(self):
|
|
598
|
+
return super().to_adaptive()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Aliases Functions:
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
_RE_FIRST_CAP = re.compile(r"(.)([A-Z][a-z]+)")
|
|
5
|
+
_RE_ALL_CAPS = re.compile(r"([a-z0-9])([A-Z])")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def to_snakecase(name: str) -> str:
|
|
9
|
+
"""
|
|
10
|
+
Convert a CamelCase or PascalCase string into snake_case.
|
|
11
|
+
Example: "EmailAddress" -> "email_address"
|
|
12
|
+
"""
|
|
13
|
+
# Insert underscores before capital letters, then lower-case
|
|
14
|
+
s1 = _RE_FIRST_CAP.sub(r"\1_\2", name)
|
|
15
|
+
return _RE_ALL_CAPS.sub(r"\1_\2", s1).lower()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def to_pascalcase(s: str) -> str:
|
|
19
|
+
"""
|
|
20
|
+
Convert a snake_case string into PascalCase.
|
|
21
|
+
Example:
|
|
22
|
+
store_id -> StoreId
|
|
23
|
+
email_address -> EmailAddress
|
|
24
|
+
"""
|
|
25
|
+
parts = s.split("_")
|
|
26
|
+
return "".join(word.capitalize() for word in parts)
|