hardpy 0.10.0__py3-none-any.whl → 0.11.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.
- hardpy/__init__.py +2 -0
- hardpy/cli/cli.py +0 -12
- hardpy/cli/template.py +0 -4
- hardpy/common/config.py +11 -21
- hardpy/common/stand_cloud/registration.py +28 -1
- hardpy/common/stand_cloud/token_storage.py +5 -1
- hardpy/hardpy_panel/frontend/dist/asset-manifest.json +3 -3
- hardpy/hardpy_panel/frontend/dist/index.html +1 -1
- hardpy/hardpy_panel/frontend/dist/static/js/main.114c5914.js +3 -0
- hardpy/hardpy_panel/frontend/dist/static/js/main.114c5914.js.map +1 -0
- hardpy/pytest_hardpy/db/base_store.py +7 -2
- hardpy/pytest_hardpy/db/const.py +3 -0
- hardpy/pytest_hardpy/db/schema/v1.py +22 -0
- hardpy/pytest_hardpy/plugin.py +4 -12
- hardpy/pytest_hardpy/pytest_call.py +30 -41
- hardpy/pytest_hardpy/pytest_wrapper.py +7 -17
- hardpy/pytest_hardpy/reporter/base.py +9 -4
- hardpy/pytest_hardpy/reporter/hook_reporter.py +7 -0
- hardpy/pytest_hardpy/utils/__init__.py +2 -0
- hardpy/pytest_hardpy/utils/connection_data.py +0 -4
- hardpy/pytest_hardpy/utils/dialog_box.py +59 -6
- hardpy/pytest_hardpy/utils/exception.py +1 -0
- {hardpy-0.10.0.dist-info → hardpy-0.11.0.dist-info}/METADATA +1 -1
- {hardpy-0.10.0.dist-info → hardpy-0.11.0.dist-info}/RECORD +28 -28
- hardpy/hardpy_panel/frontend/dist/static/js/main.8a7d8f7d.js +0 -3
- hardpy/hardpy_panel/frontend/dist/static/js/main.8a7d8f7d.js.map +0 -1
- /hardpy/hardpy_panel/frontend/dist/static/js/{main.8a7d8f7d.js.LICENSE.txt → main.114c5914.js.LICENSE.txt} +0 -0
- {hardpy-0.10.0.dist-info → hardpy-0.11.0.dist-info}/WHEEL +0 -0
- {hardpy-0.10.0.dist-info → hardpy-0.11.0.dist-info}/entry_points.txt +0 -0
- {hardpy-0.10.0.dist-info → hardpy-0.11.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -36,8 +36,8 @@ class BaseStore(BaseConnector):
|
|
|
36
36
|
"""
|
|
37
37
|
return glom(self._doc, key)
|
|
38
38
|
|
|
39
|
-
def
|
|
40
|
-
"""Update document.
|
|
39
|
+
def update_doc_value(self, key: str, value: Any) -> None: # noqa: ANN401
|
|
40
|
+
"""Update document value.
|
|
41
41
|
|
|
42
42
|
HardPy collecting uses a simple key without dots.
|
|
43
43
|
Assign is used to update a document.
|
|
@@ -60,6 +60,11 @@ class BaseStore(BaseConnector):
|
|
|
60
60
|
self._doc["_rev"] = self._db.get(self._doc_id)["_rev"]
|
|
61
61
|
self._doc = self._db.save(self._doc)
|
|
62
62
|
|
|
63
|
+
def update_doc(self) -> None:
|
|
64
|
+
"""Update current document by database."""
|
|
65
|
+
self._doc["_rev"] = self._db.get(self._doc_id)["_rev"]
|
|
66
|
+
self._doc = self._db.get(self._doc_id)
|
|
67
|
+
|
|
63
68
|
def get_document(self) -> ModelMetaclass:
|
|
64
69
|
"""Get document by schema.
|
|
65
70
|
|
hardpy/pytest_hardpy/db/const.py
CHANGED
|
@@ -204,6 +204,24 @@ class TestStand(BaseModel):
|
|
|
204
204
|
location: str | None = None
|
|
205
205
|
|
|
206
206
|
|
|
207
|
+
class OperatorData(BaseModel):
|
|
208
|
+
"""Operator data from operator panel.
|
|
209
|
+
|
|
210
|
+
Example:
|
|
211
|
+
```
|
|
212
|
+
{
|
|
213
|
+
"operator_data": {
|
|
214
|
+
"dialog": "hello",
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
model_config = ConfigDict(extra="forbid")
|
|
221
|
+
|
|
222
|
+
dialog: str | None = None
|
|
223
|
+
|
|
224
|
+
|
|
207
225
|
class ResultStateStore(IBaseResult):
|
|
208
226
|
"""Test run description.
|
|
209
227
|
|
|
@@ -218,6 +236,9 @@ class ResultStateStore(IBaseResult):
|
|
|
218
236
|
"status": "failed",
|
|
219
237
|
"name": "hardpy-stand",
|
|
220
238
|
"alert": "",
|
|
239
|
+
"operator_data": {
|
|
240
|
+
"dialog": ""
|
|
241
|
+
},
|
|
221
242
|
"dut": {
|
|
222
243
|
"serial_number": "92c5a4bb-ecb0-42c5-89ac-e0caca0919fd",
|
|
223
244
|
"part_number": "part_1",
|
|
@@ -305,6 +326,7 @@ class ResultStateStore(IBaseResult):
|
|
|
305
326
|
modules: dict[str, ModuleStateStore] = {}
|
|
306
327
|
operator_msg: dict = {}
|
|
307
328
|
alert: str
|
|
329
|
+
operator_data: OperatorData
|
|
308
330
|
|
|
309
331
|
|
|
310
332
|
class ResultRunStore(IBaseResult):
|
hardpy/pytest_hardpy/plugin.py
CHANGED
|
@@ -56,17 +56,17 @@ def pytest_addoption(parser: Parser) -> None:
|
|
|
56
56
|
default=con_data.database_url,
|
|
57
57
|
help="database url",
|
|
58
58
|
)
|
|
59
|
+
# TODO (xorialexandrov): Remove --hardpy-sp and --hardpy-sh in HardPy major version.
|
|
60
|
+
# Addoptions left for compatibility with version 0.10.1 and below
|
|
59
61
|
parser.addoption(
|
|
60
62
|
"--hardpy-sp",
|
|
61
63
|
action="store",
|
|
62
|
-
|
|
63
|
-
help="internal socket port",
|
|
64
|
+
help="DEPRECATED, UNUSED: internal socket port",
|
|
64
65
|
)
|
|
65
66
|
parser.addoption(
|
|
66
67
|
"--hardpy-sh",
|
|
67
68
|
action="store",
|
|
68
|
-
|
|
69
|
-
help="internal socket host",
|
|
69
|
+
help="DEPRECATED, UNUSED: internal socket host",
|
|
70
70
|
)
|
|
71
71
|
parser.addoption(
|
|
72
72
|
"--hardpy-clear-database",
|
|
@@ -136,14 +136,6 @@ class HardpyPlugin:
|
|
|
136
136
|
|
|
137
137
|
is_clear_database = config.getoption("--hardpy-clear-database")
|
|
138
138
|
|
|
139
|
-
socket_port = config.getoption("--hardpy-sp")
|
|
140
|
-
if socket_port:
|
|
141
|
-
con_data.socket_port = int(socket_port) # type: ignore
|
|
142
|
-
|
|
143
|
-
socket_host = config.getoption("--hardpy-sh")
|
|
144
|
-
if socket_host:
|
|
145
|
-
con_data.socket_host = str(socket_host) # type: ignore
|
|
146
|
-
|
|
147
139
|
sc_address = config.getoption("--sc-address")
|
|
148
140
|
if sc_address:
|
|
149
141
|
con_data.sc_address = str(sc_address) # type: ignore
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import socket
|
|
6
5
|
from dataclasses import dataclass
|
|
7
6
|
from os import environ
|
|
8
|
-
from
|
|
7
|
+
from time import sleep
|
|
9
8
|
from typing import Any
|
|
10
9
|
from uuid import uuid4
|
|
11
10
|
|
|
@@ -19,12 +18,12 @@ from hardpy.pytest_hardpy.db import (
|
|
|
19
18
|
)
|
|
20
19
|
from hardpy.pytest_hardpy.reporter import RunnerReporter
|
|
21
20
|
from hardpy.pytest_hardpy.utils import (
|
|
22
|
-
ConnectionData,
|
|
23
21
|
DialogBox,
|
|
24
22
|
DuplicatePartNumberError,
|
|
25
23
|
DuplicateSerialNumberError,
|
|
26
24
|
DuplicateTestStandLocationError,
|
|
27
25
|
DuplicateTestStandNameError,
|
|
26
|
+
HTMLComponent,
|
|
28
27
|
ImageComponent,
|
|
29
28
|
)
|
|
30
29
|
|
|
@@ -279,6 +278,7 @@ def run_dialog_box(dialog_box_data: DialogBox) -> Any: # noqa: ANN401
|
|
|
279
278
|
If the title_bar field is missing, it is the case name.
|
|
280
279
|
- widget (DialogBoxWidget | None): Widget information.
|
|
281
280
|
- image (ImageComponent | None): Image information.
|
|
281
|
+
- html (HTMLComponent | None): HTML information.
|
|
282
282
|
|
|
283
283
|
Returns:
|
|
284
284
|
Any: An object containing the user's response.
|
|
@@ -312,18 +312,18 @@ def run_dialog_box(dialog_box_data: DialogBox) -> Any: # noqa: ANN401
|
|
|
312
312
|
reporter.set_doc_value(key, dialog_box_data.to_dict(), statestore_only=True)
|
|
313
313
|
reporter.update_db_by_doc()
|
|
314
314
|
|
|
315
|
-
|
|
316
|
-
input_dbx_data = _get_socket_raw_data()
|
|
315
|
+
input_dbx_data = _get_operator_data()
|
|
317
316
|
|
|
318
317
|
_cleanup_widget(reporter, key)
|
|
319
318
|
return dialog_box_data.widget.convert_data(input_dbx_data)
|
|
320
319
|
|
|
321
320
|
|
|
322
|
-
def set_operator_message(
|
|
321
|
+
def set_operator_message( # noqa: PLR0913
|
|
323
322
|
msg: str,
|
|
324
323
|
title: str | None = None,
|
|
325
324
|
block: bool = True,
|
|
326
325
|
image: ImageComponent | None = None,
|
|
326
|
+
html: HTMLComponent | None = None,
|
|
327
327
|
font_size: int = 14,
|
|
328
328
|
) -> None:
|
|
329
329
|
"""Set operator message.
|
|
@@ -334,7 +334,8 @@ def set_operator_message(
|
|
|
334
334
|
Args:
|
|
335
335
|
msg (str): message
|
|
336
336
|
title (str | None): title
|
|
337
|
-
image (ImageComponent | None): operator message
|
|
337
|
+
image (ImageComponent | None): operator message image
|
|
338
|
+
html (HTMLComponent | None): operator message html page
|
|
338
339
|
block (bool): if True, the function will block until the message is closed
|
|
339
340
|
font_size (int): font size
|
|
340
341
|
"""
|
|
@@ -351,6 +352,7 @@ def set_operator_message(
|
|
|
351
352
|
DF.TITLE: title,
|
|
352
353
|
DF.VISIBLE: True,
|
|
353
354
|
DF.IMAGE: image.to_dict() if image else None,
|
|
355
|
+
DF.HTML: html.to_dict() if html else None,
|
|
354
356
|
DF.ID: str(uuid4()),
|
|
355
357
|
DF.FONT_SIZE: int(font_size),
|
|
356
358
|
}
|
|
@@ -358,10 +360,9 @@ def set_operator_message(
|
|
|
358
360
|
reporter.update_db_by_doc()
|
|
359
361
|
|
|
360
362
|
if block:
|
|
361
|
-
|
|
362
|
-
is_msg_visible = _get_socket_raw_data()
|
|
363
|
-
|
|
363
|
+
is_msg_visible = _get_operator_data()
|
|
364
364
|
msg_data[DF.VISIBLE] = is_msg_visible
|
|
365
|
+
|
|
365
366
|
reporter.set_doc_value(key, msg_data, statestore_only=True)
|
|
366
367
|
reporter.update_db_by_doc()
|
|
367
368
|
|
|
@@ -414,37 +415,25 @@ def _get_current_test() -> CurrentTestInfo:
|
|
|
414
415
|
return CurrentTestInfo(module_id=module_id, case_id=case_id)
|
|
415
416
|
|
|
416
417
|
|
|
417
|
-
def
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
client, _ = server.accept()
|
|
437
|
-
break
|
|
438
|
-
|
|
439
|
-
# receive data
|
|
440
|
-
max_input_data_len = 1024
|
|
441
|
-
socket_data = client.recv(max_input_data_len).decode("utf-8")
|
|
442
|
-
|
|
443
|
-
# close connection
|
|
444
|
-
client.close()
|
|
445
|
-
server.close()
|
|
446
|
-
|
|
447
|
-
return socket_data
|
|
418
|
+
def _get_operator_data() -> str:
|
|
419
|
+
"""Get operator panel data.
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
str: operator panel data
|
|
423
|
+
"""
|
|
424
|
+
reporter = RunnerReporter()
|
|
425
|
+
|
|
426
|
+
data = ""
|
|
427
|
+
key = reporter.generate_key(DF.OPERATOR_DATA, DF.DIALOG)
|
|
428
|
+
while not data:
|
|
429
|
+
reporter.update_doc_by_db()
|
|
430
|
+
|
|
431
|
+
data = reporter.get_field(key)
|
|
432
|
+
if data:
|
|
433
|
+
reporter.set_doc_value(key, "", statestore_only=True)
|
|
434
|
+
break
|
|
435
|
+
sleep(0.1)
|
|
436
|
+
return data
|
|
448
437
|
|
|
449
438
|
|
|
450
439
|
def _cleanup_widget(reporter: RunnerReporter, key: str) -> None:
|
|
@@ -6,9 +6,10 @@ import signal
|
|
|
6
6
|
import subprocess
|
|
7
7
|
import sys
|
|
8
8
|
from platform import system
|
|
9
|
-
from socket import socket
|
|
10
9
|
|
|
11
10
|
from hardpy.common.config import ConfigManager
|
|
11
|
+
from hardpy.pytest_hardpy.db import DatabaseField as DF # noqa: N817
|
|
12
|
+
from hardpy.pytest_hardpy.reporter import RunnerReporter
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class PyTestWrapper:
|
|
@@ -16,6 +17,7 @@ class PyTestWrapper:
|
|
|
16
17
|
|
|
17
18
|
def __init__(self) -> None:
|
|
18
19
|
self._proc = None
|
|
20
|
+
self._reporter = RunnerReporter()
|
|
19
21
|
self.python_executable = sys.executable
|
|
20
22
|
|
|
21
23
|
# Make sure test structure is stored in DB
|
|
@@ -43,10 +45,6 @@ class PyTestWrapper:
|
|
|
43
45
|
"pytest",
|
|
44
46
|
"--hardpy-db-url",
|
|
45
47
|
self.config.database.connection_url(),
|
|
46
|
-
"--hardpy-sp",
|
|
47
|
-
str(self.config.socket.port),
|
|
48
|
-
"--hardpy-sh",
|
|
49
|
-
self.config.socket.host,
|
|
50
48
|
"--sc-address",
|
|
51
49
|
self.config.stand_cloud.address,
|
|
52
50
|
"--sc-connection-only"
|
|
@@ -64,10 +62,6 @@ class PyTestWrapper:
|
|
|
64
62
|
"pytest",
|
|
65
63
|
"--hardpy-db-url",
|
|
66
64
|
self.config.database.connection_url(),
|
|
67
|
-
"--hardpy-sp",
|
|
68
|
-
str(self.config.socket.port),
|
|
69
|
-
"--hardpy-sh",
|
|
70
|
-
self.config.socket.host,
|
|
71
65
|
"--sc-address",
|
|
72
66
|
self.config.stand_cloud.address,
|
|
73
67
|
"--sc-connection-only"
|
|
@@ -117,10 +111,6 @@ class PyTestWrapper:
|
|
|
117
111
|
"--collect-only",
|
|
118
112
|
"--hardpy-db-url",
|
|
119
113
|
self.config.database.connection_url(),
|
|
120
|
-
"--hardpy-sp",
|
|
121
|
-
str(self.config.socket.port),
|
|
122
|
-
"--hardpy-sh",
|
|
123
|
-
self.config.socket.host,
|
|
124
114
|
"--hardpy-pt",
|
|
125
115
|
]
|
|
126
116
|
|
|
@@ -144,10 +134,10 @@ class PyTestWrapper:
|
|
|
144
134
|
bool: True if dialog box was confirmed/closed, else False
|
|
145
135
|
"""
|
|
146
136
|
try:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
137
|
+
self._reporter.update_doc_by_db()
|
|
138
|
+
key = self._reporter.generate_key(DF.OPERATOR_DATA, DF.DIALOG)
|
|
139
|
+
self._reporter.set_doc_value(key, data, statestore_only=True)
|
|
140
|
+
self._reporter.update_db_by_doc()
|
|
151
141
|
except Exception: # noqa: BLE001
|
|
152
142
|
return False
|
|
153
143
|
return True
|
|
@@ -45,19 +45,24 @@ class BaseReporter:
|
|
|
45
45
|
msg = "Both runstore_only and statestore_only cannot be True"
|
|
46
46
|
raise ValueError(msg)
|
|
47
47
|
if runstore_only:
|
|
48
|
-
self._runstore.
|
|
48
|
+
self._runstore.update_doc_value(key, value)
|
|
49
49
|
return
|
|
50
50
|
if statestore_only:
|
|
51
|
-
self._statestore.
|
|
51
|
+
self._statestore.update_doc_value(key, value)
|
|
52
52
|
return
|
|
53
|
-
self._runstore.
|
|
54
|
-
self._statestore.
|
|
53
|
+
self._runstore.update_doc_value(key, value)
|
|
54
|
+
self._statestore.update_doc_value(key, value)
|
|
55
55
|
|
|
56
56
|
def update_db_by_doc(self) -> None:
|
|
57
57
|
"""Update database by current document."""
|
|
58
58
|
self._statestore.update_db()
|
|
59
59
|
self._runstore.update_db()
|
|
60
60
|
|
|
61
|
+
def update_doc_by_db(self) -> None:
|
|
62
|
+
"""Update document by current database."""
|
|
63
|
+
self._statestore.update_doc()
|
|
64
|
+
self._runstore.update_doc()
|
|
65
|
+
|
|
61
66
|
def generate_key(self, *args: Any) -> str: # noqa: ANN401
|
|
62
67
|
"""Generate key for database.
|
|
63
68
|
|
|
@@ -38,6 +38,7 @@ class HookReporter(BaseReporter):
|
|
|
38
38
|
self.set_doc_value(DF.ARTIFACT, {}, runstore_only=True)
|
|
39
39
|
self.set_doc_value(DF.OPERATOR_MSG, {}, statestore_only=True)
|
|
40
40
|
self.set_doc_value(DF.ALERT, "", statestore_only=True)
|
|
41
|
+
self.set_doc_value(DF.OPERATOR_DATA, {}, statestore_only=True)
|
|
41
42
|
|
|
42
43
|
test_stand_tz = self.generate_key(DF.TEST_STAND, DF.TIMEZONE)
|
|
43
44
|
self.set_doc_value(test_stand_tz, str(get_localzone().key))
|
|
@@ -45,6 +46,9 @@ class HookReporter(BaseReporter):
|
|
|
45
46
|
test_stand_id_key = self.generate_key(DF.TEST_STAND, DF.HW_ID)
|
|
46
47
|
self.set_doc_value(test_stand_id_key, machine_id())
|
|
47
48
|
|
|
49
|
+
operator_data_key = self.generate_key(DF.OPERATOR_DATA, DF.DIALOG)
|
|
50
|
+
self.set_doc_value(operator_data_key, "", statestore_only=True)
|
|
51
|
+
|
|
48
52
|
def start(self) -> None:
|
|
49
53
|
"""Start test."""
|
|
50
54
|
self._log.debug("Starting test run.")
|
|
@@ -54,6 +58,9 @@ class HookReporter(BaseReporter):
|
|
|
54
58
|
self.set_doc_value(DF.PROGRESS, 0, statestore_only=True)
|
|
55
59
|
self.set_doc_value(DF.ALERT, "", statestore_only=True)
|
|
56
60
|
|
|
61
|
+
operator_data_key = self.generate_key(DF.OPERATOR_DATA, DF.DIALOG)
|
|
62
|
+
self.set_doc_value(operator_data_key, "", statestore_only=True)
|
|
63
|
+
|
|
57
64
|
def finish(self, status: TestStatus) -> None:
|
|
58
65
|
"""Finish test.
|
|
59
66
|
|
|
@@ -7,6 +7,7 @@ from hardpy.pytest_hardpy.utils.dialog_box import (
|
|
|
7
7
|
BaseWidget,
|
|
8
8
|
CheckboxWidget,
|
|
9
9
|
DialogBox,
|
|
10
|
+
HTMLComponent,
|
|
10
11
|
ImageComponent,
|
|
11
12
|
MultistepWidget,
|
|
12
13
|
NumericInputWidget,
|
|
@@ -36,6 +37,7 @@ __all__ = [
|
|
|
36
37
|
"DuplicateSerialNumberError",
|
|
37
38
|
"DuplicateTestStandLocationError",
|
|
38
39
|
"DuplicateTestStandNameError",
|
|
40
|
+
"HTMLComponent",
|
|
39
41
|
"ImageComponent",
|
|
40
42
|
"ImageError",
|
|
41
43
|
"MultistepWidget",
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# Copyright (c) 2024 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
3
|
|
|
4
|
-
from socket import gethostname
|
|
5
|
-
|
|
6
4
|
from hardpy.pytest_hardpy.utils.singleton import SingletonMeta
|
|
7
5
|
|
|
8
6
|
|
|
@@ -11,7 +9,5 @@ class ConnectionData(metaclass=SingletonMeta):
|
|
|
11
9
|
|
|
12
10
|
def __init__(self) -> None:
|
|
13
11
|
self.database_url: str = "http://dev:dev@localhost:5984/"
|
|
14
|
-
self.socket_host: str = gethostname()
|
|
15
|
-
self.socket_port: int = 6525
|
|
16
12
|
self.sc_address: str = ""
|
|
17
13
|
self.sc_connection_only: bool = False
|
|
@@ -184,6 +184,7 @@ class StepWidget(IWidget):
|
|
|
184
184
|
title (str): Step title
|
|
185
185
|
text (str | None): Step text
|
|
186
186
|
image (ImageComponent | None): Step image
|
|
187
|
+
html (HTMLComponent | None): Step html
|
|
187
188
|
|
|
188
189
|
Raises:
|
|
189
190
|
WidgetInfoError: If the text or widget are not provided.
|
|
@@ -194,16 +195,19 @@ class StepWidget(IWidget):
|
|
|
194
195
|
title: str,
|
|
195
196
|
text: str | None,
|
|
196
197
|
image: ImageComponent | None = None,
|
|
198
|
+
html: HTMLComponent | None = None,
|
|
197
199
|
) -> None:
|
|
198
200
|
super().__init__(WidgetType.STEP)
|
|
199
|
-
if text is None and image is None:
|
|
200
|
-
msg = "Text
|
|
201
|
+
if text is None and image is None and html is None:
|
|
202
|
+
msg = "Text, image and html must be provided"
|
|
201
203
|
raise WidgetInfoError(msg)
|
|
202
204
|
self.info["title"] = title
|
|
203
205
|
if isinstance(text, str):
|
|
204
206
|
self.info["text"] = text
|
|
205
207
|
if isinstance(image, ImageComponent):
|
|
206
208
|
self.info["image"] = image.__dict__
|
|
209
|
+
if isinstance(html, HTMLComponent):
|
|
210
|
+
self.info["html"] = html.__dict__
|
|
207
211
|
|
|
208
212
|
def convert_data(self, input_data: str) -> bool: # noqa: ARG002
|
|
209
213
|
"""Get the step widget data in the correct format.
|
|
@@ -264,7 +268,7 @@ class ImageComponent:
|
|
|
264
268
|
width: int = 100,
|
|
265
269
|
border: int = 0,
|
|
266
270
|
) -> None:
|
|
267
|
-
"""
|
|
271
|
+
"""Initialize the image component.
|
|
268
272
|
|
|
269
273
|
Args:
|
|
270
274
|
address (str): image address
|
|
@@ -272,7 +276,7 @@ class ImageComponent:
|
|
|
272
276
|
border (int): image border
|
|
273
277
|
|
|
274
278
|
Raises:
|
|
275
|
-
ImageError: If both address and base64data are specified
|
|
279
|
+
ImageError: If both address and base64data are specified
|
|
276
280
|
"""
|
|
277
281
|
if width < 1:
|
|
278
282
|
msg = "Width must be positive"
|
|
@@ -297,7 +301,7 @@ class ImageComponent:
|
|
|
297
301
|
"""Convert ImageComponent to dictionary.
|
|
298
302
|
|
|
299
303
|
Returns:
|
|
300
|
-
dict: ImageComponent dictionary
|
|
304
|
+
dict: ImageComponent dictionary
|
|
301
305
|
"""
|
|
302
306
|
return {
|
|
303
307
|
"address": self.address,
|
|
@@ -307,6 +311,51 @@ class ImageComponent:
|
|
|
307
311
|
}
|
|
308
312
|
|
|
309
313
|
|
|
314
|
+
class HTMLComponent:
|
|
315
|
+
"""HTML component."""
|
|
316
|
+
|
|
317
|
+
def __init__(
|
|
318
|
+
self,
|
|
319
|
+
html: str,
|
|
320
|
+
width: int = 100,
|
|
321
|
+
border: int = 0,
|
|
322
|
+
is_raw_html: bool = True,
|
|
323
|
+
) -> None:
|
|
324
|
+
"""Initialize the HTML component.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
html (str): html string or URL
|
|
328
|
+
width (int): html component width
|
|
329
|
+
border (int): html component border
|
|
330
|
+
is_raw_html (bool): True if the html code is raw, else False
|
|
331
|
+
"""
|
|
332
|
+
if width < 1:
|
|
333
|
+
msg = "Width must be positive"
|
|
334
|
+
raise WidgetInfoError(msg)
|
|
335
|
+
|
|
336
|
+
if border < 0:
|
|
337
|
+
msg = "Border must be non-negative"
|
|
338
|
+
raise WidgetInfoError(msg)
|
|
339
|
+
|
|
340
|
+
self.code_or_url = html
|
|
341
|
+
self.width = width
|
|
342
|
+
self.border = border
|
|
343
|
+
self.is_raw_html = is_raw_html
|
|
344
|
+
|
|
345
|
+
def to_dict(self) -> dict:
|
|
346
|
+
"""Convert HtmlComponent to dictionary.
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
dict: HtmlComponent dictionary.
|
|
350
|
+
"""
|
|
351
|
+
return {
|
|
352
|
+
"code_or_url": self.code_or_url,
|
|
353
|
+
"width": self.width,
|
|
354
|
+
"border": self.border,
|
|
355
|
+
"is_raw_html": self.is_raw_html,
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
310
359
|
@dataclass
|
|
311
360
|
class DialogBox:
|
|
312
361
|
"""Dialog box data.
|
|
@@ -319,16 +368,18 @@ class DialogBox:
|
|
|
319
368
|
font_size (int): font size
|
|
320
369
|
"""
|
|
321
370
|
|
|
322
|
-
def __init__(
|
|
371
|
+
def __init__( # noqa: PLR0913
|
|
323
372
|
self,
|
|
324
373
|
dialog_text: str,
|
|
325
374
|
title_bar: str | None = None,
|
|
326
375
|
widget: IWidget | None = None,
|
|
327
376
|
image: ImageComponent | None = None,
|
|
377
|
+
html: HTMLComponent | None = None,
|
|
328
378
|
font_size: int = 14,
|
|
329
379
|
) -> None:
|
|
330
380
|
self.widget: IWidget = BaseWidget() if widget is None else widget
|
|
331
381
|
self.image: ImageComponent | None = image
|
|
382
|
+
self.html: HTMLComponent | None = html
|
|
332
383
|
self.dialog_text: str = dialog_text
|
|
333
384
|
self.title_bar: str | None = title_bar
|
|
334
385
|
self.visible: bool = True
|
|
@@ -349,4 +400,6 @@ class DialogBox:
|
|
|
349
400
|
dbx_dict["widget"] = deepcopy(self.widget.__dict__)
|
|
350
401
|
if self.image:
|
|
351
402
|
dbx_dict["image"] = deepcopy(self.image.__dict__)
|
|
403
|
+
if self.html:
|
|
404
|
+
dbx_dict["html"] = deepcopy(self.html.__dict__)
|
|
352
405
|
return dbx_dict
|