hardpy 0.15.2__tar.gz → 0.17.0__tar.gz

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.
Files changed (95) hide show
  1. {hardpy-0.15.2 → hardpy-0.17.0}/PKG-INFO +17 -2
  2. {hardpy-0.15.2 → hardpy-0.17.0}/README.md +16 -1
  3. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/__init__.py +9 -6
  4. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/cli/cli.py +16 -10
  5. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/cli/template.py +5 -4
  6. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/common/config.py +83 -50
  7. {hardpy-0.15.2/hardpy/pytest_hardpy/utils → hardpy-0.17.0/hardpy/common}/singleton.py +1 -1
  8. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/api.py +44 -13
  9. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/allPaths-CV5wjLMB.js → hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/allPaths-COgYwK8M.js +1 -1
  10. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/allPathsLoader-JIzW_pSb.js → hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/allPathsLoader-B8vBKA-e.js +2 -2
  11. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Bfs1BwbR.ttf → hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-B2twAPZE.ttf +0 -0
  12. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-RCDSkC4W.eot → hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-C0Unyq1d.eot +0 -0
  13. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-CzsyEoPG.svg → hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-CVy9qFng.svg +249 -3
  14. hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Ck1ifK4A.woff +0 -0
  15. hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-DwWyHYRo.woff2 +0 -0
  16. hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-9zitLjlL.woff2 +0 -0
  17. hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-CjKGIKxE.woff +0 -0
  18. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-DyVnGNfQ.svg → hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-DQ09GSQq.svg +249 -3
  19. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-BGGGsqDJ.ttf → hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-DmR755bS.ttf +0 -0
  20. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-Doom1bSH.eot → hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-p9MhBXD8.eot +0 -0
  21. hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-CVrkrold.js +2 -0
  22. hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/index-B-fsa5Ru.js +1 -0
  23. hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/index-B3wEgxl0.js +4673 -0
  24. hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/index-B7T9xvaW.css +1 -0
  25. hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/index-DLOviMB1.js +1 -0
  26. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/splitPathsBySizeLoader-DkZadBcn.js → hardpy-0.17.0/hardpy/hardpy_panel/frontend/dist/assets/splitPathsBySizeLoader-CLCw9W9y.js +1 -1
  27. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/index.html +2 -2
  28. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/locales/de/translation.json +22 -7
  29. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/locales/en/translation.json +22 -7
  30. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/locales/es/translation.json +22 -7
  31. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/locales/fr/translation.json +22 -7
  32. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/locales/ja/translation.json +16 -1
  33. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/locales/ru/translation.json +22 -7
  34. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/locales/zh/translation.json +16 -1
  35. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/db/__init__.py +12 -0
  36. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/db/base_store.py +31 -4
  37. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/db/runstore.py +1 -1
  38. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/db/schema/v1.py +8 -6
  39. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/db/statestore.py +1 -1
  40. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/plugin.py +38 -25
  41. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/pytest_call.py +74 -29
  42. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/pytest_wrapper.py +9 -7
  43. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/reporter/base.py +21 -1
  44. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/reporter/runner_reporter.py +1 -1
  45. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/result/report_loader/stand_cloud_loader.py +35 -4
  46. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/utils/__init__.py +0 -16
  47. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/utils/dialog_box.py +3 -0
  48. {hardpy-0.15.2 → hardpy-0.17.0}/pyproject.toml +1 -1
  49. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-Btb8d-Hu.woff +0 -0
  50. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-DrH54W_x.woff2 +0 -0
  51. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-D9WO2FSG.woff2 +0 -0
  52. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-ZW-9JnPf.woff +0 -0
  53. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-CccdstaD.js +0 -2
  54. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/index-6RIgWzcZ.js +0 -790
  55. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/index-BMEat_ws.js +0 -1
  56. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/index-BwCQzehg.css +0 -1
  57. hardpy-0.15.2/hardpy/hardpy_panel/frontend/dist/assets/index-xb4M2ucX.js +0 -1
  58. hardpy-0.15.2/hardpy/pytest_hardpy/db/base_connector.py +0 -31
  59. hardpy-0.15.2/hardpy/pytest_hardpy/db/base_server.py +0 -14
  60. hardpy-0.15.2/hardpy/pytest_hardpy/utils/connection_data.py +0 -13
  61. {hardpy-0.15.2 → hardpy-0.17.0}/.gitignore +0 -0
  62. {hardpy-0.15.2 → hardpy-0.17.0}/LICENSE +0 -0
  63. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/cli/__init__.py +0 -0
  64. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/common/__init__.py +0 -0
  65. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/common/stand_cloud/__init__.py +0 -0
  66. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/common/stand_cloud/connector.py +0 -0
  67. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/common/stand_cloud/exception.py +0 -0
  68. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/common/stand_cloud/oauth2.py +0 -0
  69. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/common/stand_cloud/registration.py +0 -0
  70. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/common/stand_cloud/token_manager.py +0 -0
  71. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/common/stand_cloud/utils.py +0 -0
  72. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/__init__.py +0 -0
  73. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/assets/logo_smol-CK3jE85c.png +0 -0
  74. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/favicon.ico +0 -0
  75. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/logo192.png +0 -0
  76. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/logo512.png +0 -0
  77. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/hardpy_panel/frontend/dist/manifest.json +0 -0
  78. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/__init__.py +0 -0
  79. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/db/const.py +0 -0
  80. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/db/schema/__init__.py +0 -0
  81. {hardpy-0.15.2/hardpy/pytest_hardpy/utils → hardpy-0.17.0/hardpy/pytest_hardpy/db}/stand_type.py +0 -0
  82. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/reporter/__init__.py +0 -0
  83. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/reporter/hook_reporter.py +0 -0
  84. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/result/__init__.py +0 -0
  85. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/result/couchdb_config.py +0 -0
  86. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/result/report_loader/__init__.py +0 -0
  87. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/result/report_loader/couchdb_loader.py +0 -0
  88. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/result/report_reader/__init__.py +0 -0
  89. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/result/report_reader/couchdb_reader.py +0 -0
  90. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/result/report_reader/stand_cloud_reader.py +0 -0
  91. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/utils/const.py +0 -0
  92. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/utils/exception.py +0 -0
  93. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/utils/machineid.py +0 -0
  94. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/utils/node_info.py +0 -0
  95. {hardpy-0.15.2 → hardpy-0.17.0}/hardpy/pytest_hardpy/utils/progress_calculator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hardpy
3
- Version: 0.15.2
3
+ Version: 0.17.0
4
4
  Summary: HardPy library for device testing
5
5
  Project-URL: Homepage, https://github.com/everypinio/hardpy/
6
6
  Project-URL: Documentation, https://everypinio.github.io/hardpy/
@@ -68,6 +68,8 @@ HardPy is a python library for creating a test bench for devices.
68
68
  ![python versions](https://img.shields.io/pypi/pyversions/hardpy.svg)
69
69
  [![pytest versions](https://img.shields.io/badge/pytest-%3E%3D7.0%20%3C9.0-blue)](https://docs.pytest.org/en/latest/)
70
70
  [![Documentation](https://img.shields.io/badge/Documentation%20-Overview%20-%20%23007ec6)](https://everypinio.github.io/hardpy/)
71
+ [![Reddit](https://img.shields.io/badge/-Reddit-FF4500?style=flat&logo=reddit&logoColor=white)](https://www.reddit.com/r/HardPy)
72
+ [![Telegram](https://img.shields.io/badge/-Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/everypin)
71
73
 
72
74
  </div>
73
75
 
@@ -109,8 +111,21 @@ hardpy run
109
111
  4. View operator panel in browser: http://localhost:8000/
110
112
  5. View the latest test report: http://localhost:5984/_utils
111
113
 
112
- Login and password: **dev**, database - **runstore**, document - **current**.
114
+ Login and password: **dev**, database - **runstore**.
113
115
 
114
116
  ## Examples
115
117
 
116
118
  For more examples of using **HardPy**, see the [examples](https://github.com/everypinio/hardpy/tree/main/examples) folder and the [documentation](https://everypinio.github.io/hardpy/examples/).
119
+
120
+ ## Measurement instruments
121
+
122
+ **HardPy** does not contain any drivers for interacting with measuring equipment.
123
+ However, **HardPy** allows you to work with any Python code, meaning you can use
124
+ open libraries to interact with measuring equipment.
125
+
126
+ * [InstrumentKit](https://github.com/instrumentkit/InstrumentKit)
127
+ * [Instrumental](https://github.com/mabuchilab/Instrumental)
128
+ * [PyMeasure](https://github.com/pymeasure/pymeasure)
129
+ * [PyTango](https://gitlab.com/tango-controls/pytango)
130
+ * [QCoDeS](https://github.com/microsoft/Qcodes)
131
+ * [QCoDeS contrib drivers](https://github.com/QCoDeS/Qcodes_contrib_drivers)
@@ -12,6 +12,8 @@ HardPy is a python library for creating a test bench for devices.
12
12
  ![python versions](https://img.shields.io/pypi/pyversions/hardpy.svg)
13
13
  [![pytest versions](https://img.shields.io/badge/pytest-%3E%3D7.0%20%3C9.0-blue)](https://docs.pytest.org/en/latest/)
14
14
  [![Documentation](https://img.shields.io/badge/Documentation%20-Overview%20-%20%23007ec6)](https://everypinio.github.io/hardpy/)
15
+ [![Reddit](https://img.shields.io/badge/-Reddit-FF4500?style=flat&logo=reddit&logoColor=white)](https://www.reddit.com/r/HardPy)
16
+ [![Telegram](https://img.shields.io/badge/-Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/everypin)
15
17
 
16
18
  </div>
17
19
 
@@ -53,8 +55,21 @@ hardpy run
53
55
  4. View operator panel in browser: http://localhost:8000/
54
56
  5. View the latest test report: http://localhost:5984/_utils
55
57
 
56
- Login and password: **dev**, database - **runstore**, document - **current**.
58
+ Login and password: **dev**, database - **runstore**.
57
59
 
58
60
  ## Examples
59
61
 
60
62
  For more examples of using **HardPy**, see the [examples](https://github.com/everypinio/hardpy/tree/main/examples) folder and the [documentation](https://everypinio.github.io/hardpy/examples/).
63
+
64
+ ## Measurement instruments
65
+
66
+ **HardPy** does not contain any drivers for interacting with measuring equipment.
67
+ However, **HardPy** allows you to work with any Python code, meaning you can use
68
+ open libraries to interact with measuring equipment.
69
+
70
+ * [InstrumentKit](https://github.com/instrumentkit/InstrumentKit)
71
+ * [Instrumental](https://github.com/mabuchilab/Instrumental)
72
+ * [PyMeasure](https://github.com/pymeasure/pymeasure)
73
+ * [PyTango](https://gitlab.com/tango-controls/pytango)
74
+ * [QCoDeS](https://github.com/microsoft/Qcodes)
75
+ * [QCoDeS contrib drivers](https://github.com/QCoDeS/Qcodes_contrib_drivers)
@@ -2,8 +2,16 @@
2
2
  # GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
3
3
 
4
4
  from hardpy.common.stand_cloud import StandCloudConnector, StandCloudError
5
+ from hardpy.pytest_hardpy.db import (
6
+ Chart,
7
+ Instrument,
8
+ NumericMeasurement,
9
+ StringMeasurement,
10
+ SubUnit,
11
+ )
5
12
  from hardpy.pytest_hardpy.pytest_call import (
6
13
  ErrorCode,
14
+ PassFailDialog,
7
15
  clear_operator_message,
8
16
  get_current_attempt,
9
17
  get_current_report,
@@ -43,7 +51,6 @@ from hardpy.pytest_hardpy.result import (
43
51
  from hardpy.pytest_hardpy.result.couchdb_config import CouchdbConfig
44
52
  from hardpy.pytest_hardpy.utils import (
45
53
  BaseWidget,
46
- Chart,
47
54
  ChartType,
48
55
  CheckboxWidget,
49
56
  ComparisonOperation,
@@ -52,14 +59,10 @@ from hardpy.pytest_hardpy.utils import (
52
59
  Group,
53
60
  HTMLComponent,
54
61
  ImageComponent,
55
- Instrument,
56
62
  MultistepWidget,
57
63
  NumericInputWidget,
58
- NumericMeasurement,
59
64
  RadiobuttonWidget,
60
65
  StepWidget,
61
- StringMeasurement,
62
- SubUnit,
63
66
  TestStandNumberError,
64
67
  TextInputWidget,
65
68
  )
@@ -82,6 +85,7 @@ __all__ = [
82
85
  "MultistepWidget",
83
86
  "NumericInputWidget",
84
87
  "NumericMeasurement",
88
+ "PassFailDialog",
85
89
  "RadiobuttonWidget",
86
90
  "StandCloudConnector",
87
91
  "StandCloudError",
@@ -101,7 +105,6 @@ __all__ = [
101
105
  "set_case_chart",
102
106
  "set_case_measurement",
103
107
  "set_driver_info",
104
- "set_dut",
105
108
  "set_dut_info",
106
109
  "set_dut_name",
107
110
  "set_dut_part_number",
@@ -28,7 +28,7 @@ if __debug__:
28
28
  disable_warnings(InsecureRequestWarning)
29
29
 
30
30
  cli = typer.Typer(add_completion=False)
31
- default_config = ConfigManager().get_config()
31
+ default_config = HardpyConfig()
32
32
 
33
33
 
34
34
  @cli.command()
@@ -92,7 +92,8 @@ def init( # noqa: PLR0913
92
92
  sc_connection_only (bool): Flag to check StandCloud service availability
93
93
  """
94
94
  dir_path = Path(Path.cwd() / tests_dir if tests_dir else "tests")
95
- ConfigManager().init_config(
95
+ config_manager = ConfigManager()
96
+ config_manager.init_config(
96
97
  tests_name=tests_name if tests_name else dir_path.name,
97
98
  database_user=database_user,
98
99
  database_password=database_password,
@@ -112,7 +113,7 @@ def init( # noqa: PLR0913
112
113
  Path.mkdir(dir_path / "database", exist_ok=True, parents=True)
113
114
 
114
115
  # create hardpy.toml
115
- ConfigManager().create_config(dir_path)
116
+ config_manager.create_config(dir_path)
116
117
 
117
118
  config = _get_config(dir_path)
118
119
  template = TemplateGenerator(config)
@@ -151,12 +152,16 @@ def run(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
151
152
 
152
153
  print(f"http://{config.frontend.host}:{config.frontend.port}\n")
153
154
 
154
- uvicorn_run(
155
- "hardpy.hardpy_panel.api:app",
156
- host=config.frontend.host,
157
- port=config.frontend.port,
158
- log_level="critical",
159
- )
155
+ try:
156
+ uvicorn_run(
157
+ "hardpy.hardpy_panel.api:app",
158
+ host=config.frontend.host,
159
+ port=config.frontend.port,
160
+ log_level="critical",
161
+ )
162
+ except RuntimeError as exc:
163
+ print(f"HardPy server cannot be started: {exc}")
164
+ sys.exit()
160
165
 
161
166
 
162
167
  @cli.command()
@@ -260,7 +265,8 @@ def sc_logout(address: Annotated[str, typer.Argument()]) -> None:
260
265
 
261
266
  def _get_config(tests_dir: str | None = None, validate: bool = False) -> HardpyConfig:
262
267
  dir_path = Path.cwd() / tests_dir if tests_dir else Path.cwd()
263
- config = ConfigManager().read_config(dir_path)
268
+ config_manager = ConfigManager()
269
+ config = config_manager.read_config(dir_path)
264
270
 
265
271
  if not config:
266
272
  print(f"Config at path {dir_path} not found.")
@@ -5,9 +5,7 @@ from pathlib import Path
5
5
 
6
6
  from hardpy.common.config import HardpyConfig
7
7
 
8
- docker_compose_yaml = """version: "3.8"
9
-
10
- services:
8
+ docker_compose_yaml = """services:
11
9
  couchserver:
12
10
  image: couchdb:3.4
13
11
  ports:
@@ -16,8 +14,11 @@ services:
16
14
  COUCHDB_USER: {}
17
15
  COUCHDB_PASSWORD: {}
18
16
  volumes:
19
- - ./database/dbdata:/opt/couchdb/data
17
+ - couchdb_data:/opt/couchdb/data
20
18
  - ./database/couchdb.ini:/opt/couchdb/etc/local.ini
19
+
20
+ volumes:
21
+ couchdb_data:
21
22
  """
22
23
 
23
24
  couchdb_ini = """; CouchDB Configuration Settings
@@ -7,7 +7,9 @@ from pathlib import Path
7
7
 
8
8
  import tomli
9
9
  import tomli_w
10
- from pydantic import BaseModel, ConfigDict, ValidationError
10
+ from pydantic import BaseModel, ConfigDict, Field, ValidationError
11
+
12
+ from hardpy.common.singleton import SingletonMeta
11
13
 
12
14
  logger = getLogger(__name__)
13
15
 
@@ -21,8 +23,14 @@ class DatabaseConfig(BaseModel):
21
23
  password: str = "dev"
22
24
  host: str = "localhost"
23
25
  port: int = 5984
26
+ doc_id: str = Field(exclude=True, default="")
27
+ url: str = Field(exclude=True, default="")
28
+
29
+ def model_post_init(self, __context) -> None: # noqa: ANN001,PYI063
30
+ """Get database connection url."""
31
+ self.url = self.get_url()
24
32
 
25
- def connection_url(self) -> str:
33
+ def get_url(self) -> str:
26
34
  """Get database connection url.
27
35
 
28
36
  Returns:
@@ -41,6 +49,19 @@ class FrontendConfig(BaseModel):
41
49
  host: str = "localhost"
42
50
  port: int = 8000
43
51
  language: str = "en"
52
+ full_size_button: bool = False
53
+ sound_on: bool = False
54
+ modal_result: ModalResultConfig = Field(default_factory=lambda: ModalResultConfig())
55
+
56
+
57
+ class ModalResultConfig(BaseModel):
58
+ """Modal result configuration."""
59
+
60
+ model_config = ConfigDict(extra="forbid")
61
+
62
+ enable: bool = False
63
+ auto_dismiss_pass: bool = True
64
+ auto_dismiss_timeout: int = 5
44
65
 
45
66
 
46
67
  class StandCloudConfig(BaseModel):
@@ -63,16 +84,42 @@ class HardpyConfig(BaseModel, extra="allow"):
63
84
  frontend: FrontendConfig = FrontendConfig()
64
85
  stand_cloud: StandCloudConfig = StandCloudConfig()
65
86
 
87
+ def model_post_init(self, __context) -> None: # noqa: ANN001,PYI063
88
+ """Get database document name."""
89
+ self.database.doc_id = self.get_doc_id()
90
+
91
+ def get_doc_id(self) -> str:
92
+ """Update database document name."""
93
+ return f"{self.frontend.host}_{self.frontend.port}"
94
+
66
95
 
67
- class ConfigManager:
96
+ class ConfigManager(metaclass=SingletonMeta):
68
97
  """HardPy configuration manager."""
69
98
 
70
- obj = HardpyConfig()
71
- tests_path = Path.cwd()
99
+ def __init__(self) -> None:
100
+ self._config = HardpyConfig()
101
+ self._test_path = Path.cwd()
102
+
103
+ @property
104
+ def config(self) -> HardpyConfig:
105
+ """Get HardPy configuration.
106
+
107
+ Returns:
108
+ HardpyConfig: HardPy configuration
109
+ """
110
+ return self._config
111
+
112
+ @property
113
+ def tests_path(self) -> Path:
114
+ """Get tests path.
115
+
116
+ Returns:
117
+ Path: HardPy tests path
118
+ """
119
+ return self._tests_path
72
120
 
73
- @classmethod
74
121
  def init_config( # noqa: PLR0913
75
- cls,
122
+ self,
76
123
  tests_name: str,
77
124
  database_user: str,
78
125
  database_password: str,
@@ -84,7 +131,9 @@ class ConfigManager:
84
131
  sc_address: str = "",
85
132
  sc_connection_only: bool = False,
86
133
  ) -> None:
87
- """Initialize HardPy configuration.
134
+ """Initialize the HardPy configuration.
135
+
136
+ Only call once to create a configuration.
88
137
 
89
138
  Args:
90
139
  tests_name (str): Tests suite name.
@@ -98,34 +147,37 @@ class ConfigManager:
98
147
  sc_address (str): StandCloud address.
99
148
  sc_connection_only (bool): StandCloud check availability.
100
149
  """
101
- cls.obj.tests_name = tests_name
102
- cls.obj.database.user = database_user
103
- cls.obj.database.password = database_password
104
- cls.obj.database.host = database_host
105
- cls.obj.database.port = database_port
106
- cls.obj.frontend.host = frontend_host
107
- cls.obj.frontend.port = frontend_port
108
- cls.obj.frontend.language = frontend_language
109
- cls.obj.stand_cloud.address = sc_address
110
- cls.obj.stand_cloud.connection_only = sc_connection_only
111
-
112
- @classmethod
113
- def create_config(cls, parent_dir: Path) -> None:
150
+ self._config.tests_name = tests_name
151
+ self._config.frontend.host = frontend_host
152
+ self._config.frontend.port = frontend_port
153
+ self._config.frontend.language = frontend_language
154
+ self._config.database.user = database_user
155
+ self._config.database.password = database_password
156
+ self._config.database.host = database_host
157
+ self._config.database.port = database_port
158
+ self._config.database.doc_id = self._config.get_doc_id()
159
+ self._config.database.url = self._config.database.get_url()
160
+ self._config.stand_cloud.address = sc_address
161
+ self._config.stand_cloud.connection_only = sc_connection_only
162
+
163
+ def create_config(self, parent_dir: Path) -> None:
114
164
  """Create HardPy configuration.
115
165
 
116
166
  Args:
117
167
  parent_dir (Path): Configuration file parent directory.
118
168
  """
119
- if not cls.obj.stand_cloud.address:
120
- del cls.obj.stand_cloud
121
- if not cls.obj.tests_name:
122
- del cls.obj.tests_name
123
- config_str = tomli_w.dumps(cls.obj.model_dump())
169
+ config = self._config
170
+ if not self._config.stand_cloud.address:
171
+ del config.stand_cloud
172
+ if not self._config.tests_name:
173
+ del config.tests_name
174
+ if not self._config.database.doc_id:
175
+ del config.database.doc_id
176
+ config_str = tomli_w.dumps(config.model_dump())
124
177
  with Path.open(parent_dir / "hardpy.toml", "w") as file:
125
178
  file.write(config_str)
126
179
 
127
- @classmethod
128
- def read_config(cls, toml_path: Path) -> HardpyConfig | None:
180
+ def read_config(self, toml_path: Path) -> HardpyConfig | None:
129
181
  """Read HardPy configuration.
130
182
 
131
183
  Args:
@@ -134,10 +186,9 @@ class ConfigManager:
134
186
  Returns:
135
187
  HardpyConfig | None: HardPy configuration
136
188
  """
137
- cls.tests_path = toml_path
189
+ self._tests_path = toml_path
138
190
  toml_file = toml_path / "hardpy.toml"
139
191
  if not toml_file.exists():
140
- logger.error("File hardpy.toml not found at path: %s", toml_file)
141
192
  return None
142
193
  try:
143
194
  with Path.open(toml_path / "hardpy.toml", "rb") as f:
@@ -148,26 +199,8 @@ class ConfigManager:
148
199
  return None
149
200
 
150
201
  try:
151
- cls.obj = HardpyConfig(**toml_data)
202
+ self._config = HardpyConfig(**toml_data)
152
203
  except ValidationError:
153
204
  logger.exception("Error parsing TOML")
154
205
  return None
155
- return cls.obj
156
-
157
- @classmethod
158
- def get_config(cls) -> HardpyConfig:
159
- """Get HardPy configuration.
160
-
161
- Returns:
162
- HardpyConfig: HardPy configuration
163
- """
164
- return cls.obj
165
-
166
- @classmethod
167
- def get_tests_path(cls) -> Path:
168
- """Get tests path.
169
-
170
- Returns:
171
- Path: HardPy tests path
172
- """
173
- return cls.tests_path
206
+ return self._config
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2024 Everypin
1
+ # Copyright (c) 2025 Everypin
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
 
@@ -2,6 +2,7 @@
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 json
5
6
  import os
6
7
  import re
7
8
  from enum import Enum
@@ -107,34 +108,64 @@ def couch_connection() -> dict:
107
108
  Returns:
108
109
  dict[str, str]: couchdb connection string
109
110
  """
110
- connection_url = ConfigManager().get_config().database.connection_url()
111
+ config_manager = ConfigManager()
111
112
 
112
113
  return {
113
- "connection_str": connection_url,
114
+ "connection_str": config_manager.config.database.url,
114
115
  }
115
116
 
116
117
 
117
- @app.post("/api/confirm_dialog_box/{dialog_box_output}")
118
- def confirm_dialog_box(dialog_box_output: str) -> dict:
119
- """Confirm dialog box.
118
+ @app.get("/api/database_document_id")
119
+ def database_document_id() -> dict:
120
+ """Get couchdb syncronized document name.
121
+
122
+ Returns:
123
+ dict[str, str]: couchdb connection string
124
+ """
125
+ config_manager = ConfigManager()
126
+ return {"document_id": config_manager.config.database.doc_id}
127
+
128
+
129
+ @app.post("/api/confirm_dialog_box")
130
+ def confirm_dialog_box(dbx_data: dict) -> dict:
131
+ """Confirm dialog box with unified JSON structure.
120
132
 
121
133
  Args:
122
- dialog_box_output (str): output data from dialog box.
134
+ dbx_data: dict with 'result' (pass/fail/confirm) and 'data' (widget data)
123
135
 
124
136
  Returns:
125
137
  dict[str, RunStatus]: run status
126
138
  """
127
- hex_base = 16
128
- unquoted_string = unquote(dialog_box_output)
139
+ RESULT_KEY = "result" # noqa: N806
140
+ DATA_KEY = "data" # noqa: N806
141
+ STATUS_KEY = "status" # noqa: N806
142
+ EMPTY_STRING = "" # noqa: N806
143
+
144
+ HEX_BASE = 16 # noqa: N806
145
+ HEX_PATTERN = r"%([0-9a-fA-F]{2})" # noqa: N806
146
+
147
+ result = dbx_data.get(RESULT_KEY, EMPTY_STRING)
148
+ widget_data = dbx_data.get(DATA_KEY, EMPTY_STRING)
149
+
150
+ unquoted_string = unquote(widget_data)
129
151
  decoded_string = re.sub(
130
- "%([0-9a-fA-F]{2})",
131
- lambda match: chr(int(match.group(1), hex_base)),
152
+ HEX_PATTERN,
153
+ lambda match: chr(int(match.group(1), HEX_BASE)),
132
154
  unquoted_string,
133
155
  )
134
156
 
135
- if app.state.pytest_wrp.send_data(str(decoded_string)):
136
- return {"status": Status.BUSY}
137
- return {"status": Status.ERROR}
157
+ # Create JSON structure for all dialog types
158
+ dialog_result = {
159
+ RESULT_KEY: result,
160
+ DATA_KEY: decoded_string,
161
+ }
162
+
163
+ # Convert to JSON string for transmission
164
+ combined_data = json.dumps(dialog_result)
165
+
166
+ if app.state.pytest_wrp.send_data(combined_data):
167
+ return {STATUS_KEY: Status.BUSY}
168
+ return {STATUS_KEY: Status.ERROR}
138
169
 
139
170
 
140
171
  @app.post("/api/confirm_operator_msg/{is_msg_visible}")
@@ -1 +1 @@
1
- import{I as n}from"./index-xb4M2ucX.js";import{I as e}from"./index-BMEat_ws.js";import{p as r,I as s}from"./index-6RIgWzcZ.js";function I(o,t){var a=r(o);return t===s.STANDARD?n[a]:e[a]}function p(o){return r(o)}export{n as IconSvgPaths16,e as IconSvgPaths20,I as getIconPaths,p as iconNameToPathsRecordKey};
1
+ import{I as n}from"./index-DLOviMB1.js";import{I as e}from"./index-B-fsa5Ru.js";import{p as r,I as s}from"./index-B3wEgxl0.js";function I(o,t){var a=r(o);return t===s.STANDARD?n[a]:e[a]}function p(o){return r(o)}export{n as IconSvgPaths16,e as IconSvgPaths20,I as getIconPaths,p as iconNameToPathsRecordKey};
@@ -1,2 +1,2 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/allPaths-CV5wjLMB.js","assets/index-xb4M2ucX.js","assets/index-BMEat_ws.js","assets/index-6RIgWzcZ.js","assets/index-BwCQzehg.css"])))=>i.map(i=>d[i]);
2
- import{_ as o,a as n,b as i}from"./index-6RIgWzcZ.js";var _=function(e,a){return o(void 0,void 0,void 0,function(){var t;return n(this,function(r){switch(r.label){case 0:return[4,i(()=>import("./allPaths-CV5wjLMB.js"),__vite__mapDeps([0,1,2,3,4]))];case 1:return t=r.sent().getIconPaths,[2,t(e,a)]}})})};export{_ as allPathsLoader};
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/allPaths-COgYwK8M.js","assets/index-DLOviMB1.js","assets/index-B-fsa5Ru.js","assets/index-B3wEgxl0.js","assets/index-B7T9xvaW.css"])))=>i.map(i=>d[i]);
2
+ import{_ as o,a as n,b as i}from"./index-B3wEgxl0.js";var _=function(e,a){return o(void 0,void 0,void 0,function(){var t;return n(this,function(r){switch(r.label){case 0:return[4,i(()=>import("./allPaths-COgYwK8M.js"),__vite__mapDeps([0,1,2,3,4]))];case 1:return t=r.sent().getIconPaths,[2,t(e,a)]}})})};export{_ as allPathsLoader};