hardpy 0.19.0__py3-none-any.whl → 0.20.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 +6 -0
- hardpy/common/config.py +19 -0
- hardpy/hardpy_panel/api.py +62 -1
- hardpy/hardpy_panel/frontend/dist/assets/{allPaths-C_-7WXHD.js → allPaths-BXbcAtew.js} +1 -1
- hardpy/hardpy_panel/frontend/dist/assets/{allPathsLoader-DgH0Xily.js → allPathsLoader-lJLHMNNZ.js} +2 -2
- hardpy/hardpy_panel/frontend/dist/assets/{browser-ponyfill-BbOvdqIF.js → browser-ponyfill-DzwgrUwX.js} +1 -1
- hardpy/hardpy_panel/frontend/dist/assets/{index-DEJb2W0B.js → index-CVhA7vmQ.js} +158 -158
- hardpy/hardpy_panel/frontend/dist/assets/{splitPathsBySizeLoader-o5HCcdVL.js → splitPathsBySizeLoader-BdwEQHyO.js} +1 -1
- hardpy/hardpy_panel/frontend/dist/index.html +1 -1
- hardpy/hardpy_panel/frontend/dist/locales/cs/translation.json +85 -0
- hardpy/pytest_hardpy/db/__init__.py +0 -2
- hardpy/pytest_hardpy/db/runstore.py +378 -10
- hardpy/pytest_hardpy/db/statestore.py +390 -5
- hardpy/pytest_hardpy/db/tempstore.py +219 -17
- hardpy/pytest_hardpy/plugin.py +24 -0
- hardpy/pytest_hardpy/pytest_wrapper.py +6 -1
- hardpy/pytest_hardpy/reporter/hook_reporter.py +18 -0
- hardpy/pytest_hardpy/result/__init__.py +2 -0
- hardpy/pytest_hardpy/result/report_loader/json_loader.py +49 -0
- hardpy/pytest_hardpy/result/report_synchronizer/synchronizer.py +25 -9
- {hardpy-0.19.0.dist-info → hardpy-0.20.0.dist-info}/METADATA +18 -2
- {hardpy-0.19.0.dist-info → hardpy-0.20.0.dist-info}/RECORD +26 -25
- hardpy/pytest_hardpy/db/base_store.py +0 -179
- {hardpy-0.19.0.dist-info → hardpy-0.20.0.dist-info}/WHEEL +0 -0
- {hardpy-0.19.0.dist-info → hardpy-0.20.0.dist-info}/entry_points.txt +0 -0
- {hardpy-0.19.0.dist-info → hardpy-0.20.0.dist-info}/licenses/LICENSE +0 -0
hardpy/pytest_hardpy/plugin.py
CHANGED
|
@@ -205,6 +205,7 @@ class HardpyPlugin:
|
|
|
205
205
|
status = self._get_run_status(exitstatus)
|
|
206
206
|
if status == TestStatus.STOPPED:
|
|
207
207
|
self._stop_tests()
|
|
208
|
+
self._validate_stop_time()
|
|
208
209
|
self._reporter.finish(status)
|
|
209
210
|
self._reporter.update_db_by_doc()
|
|
210
211
|
self._reporter.compact_all()
|
|
@@ -483,6 +484,29 @@ class HardpyPlugin:
|
|
|
483
484
|
case _:
|
|
484
485
|
return TestStatus.FAILED
|
|
485
486
|
|
|
487
|
+
def _validate_stop_time(self) -> None:
|
|
488
|
+
"""Update module and case stop times if they are not set.
|
|
489
|
+
|
|
490
|
+
If module start time is set but module stop time is not set,
|
|
491
|
+
set module stop time to module start time.
|
|
492
|
+
If case start time is set but case stop time is not set, set
|
|
493
|
+
case stop time to case start time.
|
|
494
|
+
"""
|
|
495
|
+
for module_id, module_data in self._results.items():
|
|
496
|
+
module_start_time = self._reporter.get_module_start_time(module_id)
|
|
497
|
+
module_stop_time = self._reporter.get_module_stop_time(module_id)
|
|
498
|
+
if module_start_time and not module_stop_time:
|
|
499
|
+
self._reporter.set_module_stop_time(module_id)
|
|
500
|
+
for module_data_key in module_data:
|
|
501
|
+
# skip module status
|
|
502
|
+
if module_data_key == "module_status":
|
|
503
|
+
continue
|
|
504
|
+
case_id = module_data_key
|
|
505
|
+
case_start_time = self._reporter.get_case_start_time(module_id, case_id)
|
|
506
|
+
case_stop_time = self._reporter.get_case_stop_time(module_id, case_id)
|
|
507
|
+
if case_start_time and not case_stop_time:
|
|
508
|
+
self._reporter.set_case_stop_time(module_id, case_id)
|
|
509
|
+
|
|
486
510
|
def _stop_tests(self) -> None:
|
|
487
511
|
"""Update module and case statuses to stopped and skipped."""
|
|
488
512
|
is_module_stopped = False
|
|
@@ -208,11 +208,16 @@ class PyTestWrapper:
|
|
|
208
208
|
return config_manager.config.model_dump()
|
|
209
209
|
|
|
210
210
|
def _tests_name(self) -> str:
|
|
211
|
-
|
|
211
|
+
manual_str = ""
|
|
212
|
+
if self.config.frontend.manual_collect:
|
|
213
|
+
manual_str = " Manual mode"
|
|
214
|
+
|
|
215
|
+
tests_name = (
|
|
212
216
|
self.config.tests_name + f" {self.config.current_test_config}"
|
|
213
217
|
if self.config.current_test_config
|
|
214
218
|
else self.config.tests_name
|
|
215
219
|
)
|
|
220
|
+
return tests_name + manual_str
|
|
216
221
|
|
|
217
222
|
def _add_config_file(self, cmd: list) -> None:
|
|
218
223
|
"""Add test configuration file if specified."""
|
|
@@ -231,6 +231,15 @@ class HookReporter(BaseReporter):
|
|
|
231
231
|
key = self.generate_key(DF.MODULES, module_id, DF.START_TIME)
|
|
232
232
|
return self._statestore.get_field(key)
|
|
233
233
|
|
|
234
|
+
def get_module_stop_time(self, module_id: str) -> int:
|
|
235
|
+
"""Get module stop time.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
int: module time
|
|
239
|
+
"""
|
|
240
|
+
key = self.generate_key(DF.MODULES, module_id, DF.STOP_TIME)
|
|
241
|
+
return self._statestore.get_field(key)
|
|
242
|
+
|
|
234
243
|
def get_case_start_time(self, module_id: str, case_id: str) -> int:
|
|
235
244
|
"""Get case start time.
|
|
236
245
|
|
|
@@ -240,6 +249,15 @@ class HookReporter(BaseReporter):
|
|
|
240
249
|
key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.START_TIME)
|
|
241
250
|
return self._statestore.get_field(key)
|
|
242
251
|
|
|
252
|
+
def get_case_stop_time(self, module_id: str, case_id: str) -> int:
|
|
253
|
+
"""Get case stop time.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
int: module time
|
|
257
|
+
"""
|
|
258
|
+
key = self.generate_key(DF.MODULES, module_id, DF.CASES, case_id, DF.STOP_TIME)
|
|
259
|
+
return self._statestore.get_field(key)
|
|
260
|
+
|
|
243
261
|
def set_caused_dut_failure_id(self, module_id: str, case_id: str) -> None:
|
|
244
262
|
"""Set caused DUT failure id.
|
|
245
263
|
|
|
@@ -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
|
|
|
4
4
|
from hardpy.pytest_hardpy.result.report_loader.couchdb_loader import CouchdbLoader
|
|
5
|
+
from hardpy.pytest_hardpy.result.report_loader.json_loader import JsonLoader
|
|
5
6
|
from hardpy.pytest_hardpy.result.report_loader.stand_cloud_loader import (
|
|
6
7
|
StandCloudLoader,
|
|
7
8
|
)
|
|
@@ -13,6 +14,7 @@ from hardpy.pytest_hardpy.result.report_reader.stand_cloud_reader import (
|
|
|
13
14
|
__all__ = [
|
|
14
15
|
"CouchdbLoader",
|
|
15
16
|
"CouchdbReader",
|
|
17
|
+
"JsonLoader",
|
|
16
18
|
"StandCloudLoader",
|
|
17
19
|
"StandCloudReader",
|
|
18
20
|
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Copyright (c) 2024 Everypin
|
|
2
|
+
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from logging import getLogger
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from uuid6 import uuid7
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from hardpy.pytest_hardpy.db.schema import ResultRunStore
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class JsonLoader:
|
|
17
|
+
"""JSON report generator."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, storage_dir: Path | None = None) -> None:
|
|
20
|
+
if not storage_dir:
|
|
21
|
+
storage_dir = Path.cwd() / "reports"
|
|
22
|
+
self._storage_dir = storage_dir
|
|
23
|
+
self._storage_dir.mkdir(parents=True, exist_ok=True)
|
|
24
|
+
self._log = getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
def load(self, report: ResultRunStore, new_report_id: str | None = None) -> bool:
|
|
27
|
+
"""Load report to the report database.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
report (ResultRunStore): report
|
|
31
|
+
new_report_id (str | None, optional): user's report ID. Defaults to uuid7.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
bool: True if success, else False
|
|
35
|
+
"""
|
|
36
|
+
report_dict = report.model_dump()
|
|
37
|
+
report_id = new_report_id if new_report_id else str(uuid7())
|
|
38
|
+
report_dict["id"] = report_id
|
|
39
|
+
report_file = self._storage_dir / f"{report_id}.json"
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
with report_file.open("w") as f:
|
|
43
|
+
json.dump(report_dict, f, indent=2, default=str)
|
|
44
|
+
except Exception as exc: # noqa: BLE001
|
|
45
|
+
self._log.error(f"Error while saving report {report_id}: {exc}")
|
|
46
|
+
return False
|
|
47
|
+
else:
|
|
48
|
+
self._log.debug(f"Report saved with id: {report_id}")
|
|
49
|
+
return True
|
|
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING
|
|
|
8
8
|
from pydantic import ValidationError
|
|
9
9
|
|
|
10
10
|
from hardpy.common.stand_cloud.exception import StandCloudError
|
|
11
|
-
from hardpy.pytest_hardpy.db.tempstore import TempStore
|
|
11
|
+
from hardpy.pytest_hardpy.db.tempstore import CouchDBTempStore, TempStore
|
|
12
12
|
from hardpy.pytest_hardpy.result.report_loader.stand_cloud_loader import (
|
|
13
13
|
StandCloudLoader,
|
|
14
14
|
)
|
|
@@ -21,7 +21,18 @@ class StandCloudSynchronizer:
|
|
|
21
21
|
"""Synchronize reports with StandCloud."""
|
|
22
22
|
|
|
23
23
|
def __init__(self) -> None:
|
|
24
|
-
self._tempstore =
|
|
24
|
+
self._tempstore: TempStore | None = None
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def _get_tempstore(self) -> TempStore:
|
|
28
|
+
"""Get TempStore instance lazily.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
TempStore: TempStore singleton instance
|
|
32
|
+
"""
|
|
33
|
+
if self._tempstore is None:
|
|
34
|
+
self._tempstore = TempStore()
|
|
35
|
+
return self._tempstore
|
|
25
36
|
|
|
26
37
|
def sync(self) -> str:
|
|
27
38
|
"""Sync reports with StandCloud.
|
|
@@ -29,16 +40,21 @@ class StandCloudSynchronizer:
|
|
|
29
40
|
Returns:
|
|
30
41
|
str: Synchronization message
|
|
31
42
|
"""
|
|
32
|
-
|
|
43
|
+
_tempstore = self._get_tempstore
|
|
44
|
+
if not self._get_tempstore.reports():
|
|
33
45
|
return "All reports are synchronized with StandCloud"
|
|
34
46
|
loader = self._create_sc_loader()
|
|
35
47
|
|
|
36
48
|
invalid_reports = []
|
|
37
49
|
success_report_counter = 0
|
|
38
|
-
for _report in self.
|
|
50
|
+
for _report in self._get_tempstore.reports():
|
|
39
51
|
try:
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
if isinstance(_tempstore, CouchDBTempStore):
|
|
53
|
+
document: dict = _report.get("doc") # type: ignore[assignment]
|
|
54
|
+
report_id: str = _report.get("id") # type: ignore[assignment]
|
|
55
|
+
else:
|
|
56
|
+
document: dict = _report
|
|
57
|
+
report_id: str = _report.get("_id")
|
|
42
58
|
document.pop("rev")
|
|
43
59
|
except KeyError:
|
|
44
60
|
try:
|
|
@@ -50,7 +66,7 @@ class StandCloudSynchronizer:
|
|
|
50
66
|
invalid_reports.append({report_id: reason})
|
|
51
67
|
continue
|
|
52
68
|
try:
|
|
53
|
-
schema_report = self.
|
|
69
|
+
schema_report = self._get_tempstore.dict_to_schema(document)
|
|
54
70
|
except ValidationError as exc:
|
|
55
71
|
reason = f"Report has invalid format: {exc}"
|
|
56
72
|
invalid_reports.append({report_id: reason})
|
|
@@ -65,7 +81,7 @@ class StandCloudSynchronizer:
|
|
|
65
81
|
reason = f"Staus code: {response.status_code}, text: {response.text}"
|
|
66
82
|
invalid_reports.append({report_id: reason})
|
|
67
83
|
continue
|
|
68
|
-
if not self.
|
|
84
|
+
if not self._get_tempstore.delete(report_id):
|
|
69
85
|
reason = f"Report {report_id} not deleted from the temporary storage"
|
|
70
86
|
invalid_reports.append({report_id: reason})
|
|
71
87
|
success_report_counter += 1
|
|
@@ -80,7 +96,7 @@ class StandCloudSynchronizer:
|
|
|
80
96
|
Returns:
|
|
81
97
|
bool: True if success, else False
|
|
82
98
|
"""
|
|
83
|
-
return self.
|
|
99
|
+
return self._get_tempstore.push_report(report)
|
|
84
100
|
|
|
85
101
|
def push_to_sc(self, report: ResultRunStore) -> bool:
|
|
86
102
|
"""Push report to the StandCloud.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hardpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.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/
|
|
@@ -42,6 +42,7 @@ Requires-Dist: tomli-w<2,>=1.1.0
|
|
|
42
42
|
Requires-Dist: tomli<3,>=2.0.1
|
|
43
43
|
Requires-Dist: typer<1,>=0.12
|
|
44
44
|
Requires-Dist: tzlocal~=5.2
|
|
45
|
+
Requires-Dist: uuid6
|
|
45
46
|
Requires-Dist: uvicorn>=0.23.2
|
|
46
47
|
Provides-Extra: build
|
|
47
48
|
Requires-Dist: build==1.0.3; extra == 'build'
|
|
@@ -69,6 +70,7 @@ HardPy is a python library for creating a test bench for devices.
|
|
|
69
70
|
[](https://docs.pytest.org/en/latest/)
|
|
70
71
|
[](https://everypinio.github.io/hardpy/)
|
|
71
72
|
[](https://www.reddit.com/r/HardPy)
|
|
73
|
+
[](https://discord.gg/98bWadmG8J)
|
|
72
74
|
[](https://t.me/everypin)
|
|
73
75
|
|
|
74
76
|
</div>
|
|
@@ -81,7 +83,7 @@ HardPy allows you to:
|
|
|
81
83
|
|
|
82
84
|
* Create test benches for devices using [pytest](https://docs.pytest.org/);
|
|
83
85
|
* Use a browser to view, start, stop, and interact with tests;
|
|
84
|
-
* Store test results in the [CouchDB](https://couchdb.apache.org/) database;
|
|
86
|
+
* Store test results in the [CouchDB](https://couchdb.apache.org/) database or to simple JSON files;
|
|
85
87
|
* Store test results on the [StandCloud](https://standcloud.io/) analytics platform.
|
|
86
88
|
|
|
87
89
|
<h1 align="center">
|
|
@@ -96,6 +98,8 @@ pip install hardpy
|
|
|
96
98
|
|
|
97
99
|
## Getting Started
|
|
98
100
|
|
|
101
|
+
### With CouchDB
|
|
102
|
+
|
|
99
103
|
1. Create your first test bench.
|
|
100
104
|
```bash
|
|
101
105
|
hardpy init
|
|
@@ -114,6 +118,18 @@ hardpy run
|
|
|
114
118
|
|
|
115
119
|
Login and password: **dev**, database - **runstore**.
|
|
116
120
|
|
|
121
|
+
### Without a database
|
|
122
|
+
|
|
123
|
+
1. Create your first test bench.
|
|
124
|
+
```bash
|
|
125
|
+
hardpy init --no-create-database --storage-type json
|
|
126
|
+
```
|
|
127
|
+
2. Launch HardPy operator panel.
|
|
128
|
+
```bash
|
|
129
|
+
hardpy run
|
|
130
|
+
```
|
|
131
|
+
3. View operator panel in browser: http://localhost:8000/
|
|
132
|
+
|
|
117
133
|
## Examples
|
|
118
134
|
|
|
119
135
|
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/).
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
hardpy/__init__.py,sha256=
|
|
1
|
+
hardpy/__init__.py,sha256=UC-h56_4xL23SGY_esaN3hklenl1lKQ6KJEy5rYTeco,2991
|
|
2
2
|
hardpy/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
hardpy/cli/cli.py,sha256=
|
|
3
|
+
hardpy/cli/cli.py,sha256=P6ThIcIvcMwJht7994-0xcR6OTXALSIka58QU-wIOMc,11623
|
|
4
4
|
hardpy/cli/template.py,sha256=kOl8hsj6iBTFIDUli_dzHkH8mlnoJzOlr9muLpTEayg,6230
|
|
5
5
|
hardpy/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
hardpy/common/config.py,sha256=
|
|
6
|
+
hardpy/common/config.py,sha256=GmDCeCkv3eSY214x_n3f1q5G9qkbXRBp5sZ7llH_UFk,8340
|
|
7
7
|
hardpy/common/singleton.py,sha256=RVMqbluN-mhlJ4QOYcRzQLA68Hs8t83XNyihyUwhYGo,948
|
|
8
8
|
hardpy/common/stand_cloud/__init__.py,sha256=fezdiYAehtT2H-GAef-xZU12CbmCRe64XHA9UB3kJDU,456
|
|
9
9
|
hardpy/common/stand_cloud/connector.py,sha256=PD1Gr1QOuFUmCsnlPY2zb_WF9Nrr8BGTlbRC2_LTKFE,8147
|
|
@@ -13,14 +13,14 @@ hardpy/common/stand_cloud/registration.py,sha256=UW-JGcvON5CMQQ-s2Mb4Ee3u_jmdQfS
|
|
|
13
13
|
hardpy/common/stand_cloud/token_manager.py,sha256=oHLDip0a-0mmAiRQN5IuypTSNyOnSB43TSWlprOLRI0,4843
|
|
14
14
|
hardpy/common/stand_cloud/utils.py,sha256=GN3wzbrmF-Xe5iUXf_HurGO-YKltqd3Gc_7vG2eEL7c,692
|
|
15
15
|
hardpy/hardpy_panel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
hardpy/hardpy_panel/api.py,sha256=
|
|
16
|
+
hardpy/hardpy_panel/api.py,sha256=On7WeTBFg9efFAgsNjh5nAOrikrzX-D0T100y_dF6a4,12662
|
|
17
17
|
hardpy/hardpy_panel/frontend/dist/favicon.ico,sha256=sgIk5PKUKEKBDpkSrc8dJgjpObp0iF82Mec0GpfKId4,15406
|
|
18
|
-
hardpy/hardpy_panel/frontend/dist/index.html,sha256=
|
|
18
|
+
hardpy/hardpy_panel/frontend/dist/index.html,sha256=JtaLnshdTxzo6aKFuGaiJ4Hvcb70_D8vq4FYS9iX_iU,1851
|
|
19
19
|
hardpy/hardpy_panel/frontend/dist/logo192.png,sha256=E4K7drvhJCg9HcTpRihOXZhVJVBZ7-W97Se-3tDb46o,14485
|
|
20
20
|
hardpy/hardpy_panel/frontend/dist/logo512.png,sha256=-fIMbqX7PYUpheK4kX1C1erRTe_hHZwFQYDLrAbhFRU,34188
|
|
21
21
|
hardpy/hardpy_panel/frontend/dist/manifest.json,sha256=PfmJlN2JMJtHS6OnhU4b4X5wPQC_yRBdjesjoirObSA,502
|
|
22
|
-
hardpy/hardpy_panel/frontend/dist/assets/allPaths-
|
|
23
|
-
hardpy/hardpy_panel/frontend/dist/assets/allPathsLoader-
|
|
22
|
+
hardpy/hardpy_panel/frontend/dist/assets/allPaths-BXbcAtew.js,sha256=O19nV0Z8066gVhAfp8IWcx_yvTGNeb52iR24YejQ8vA,309
|
|
23
|
+
hardpy/hardpy_panel/frontend/dist/assets/allPathsLoader-lJLHMNNZ.js,sha256=Zh3VVQfqIPs9FFKhyrusCS_TkFoX5Mi7hmU92oFvDBc,550
|
|
24
24
|
hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-B2twAPZE.ttf,sha256=OcrUHPBAaLvJxb2DOethXFXg8PClDcyvpsh0mToLFPM,136248
|
|
25
25
|
hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-C0Unyq1d.eot,sha256=Bl93LjY8pyBj0Iip1lUxMM-0l1zLrRjoGmKPPnAQIgw,136456
|
|
26
26
|
hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-16-CVy9qFng.svg,sha256=57jlcc-NFRVJNJ3t-1fOnJvgdbYhcrF5a06LJLhWc5A,601027
|
|
@@ -31,13 +31,14 @@ hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-CjKGIKxE.woff,sha256
|
|
|
31
31
|
hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-DQ09GSQq.svg,sha256=3gLRYNxd_Y4iz6pspH4Bf7Ql4F6LH5haZzbTfxA53HQ,638634
|
|
32
32
|
hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-DmR755bS.ttf,sha256=yr5g5Jw9ZnxJJ7e1quOv977VE3NU2GRB60BMRrOJrcI,139424
|
|
33
33
|
hardpy/hardpy_panel/frontend/dist/assets/blueprint-icons-20-p9MhBXD8.eot,sha256=CFx8t8ONhB4INIrK860N56_t1dmS7FuRF7i0HKKo58k,139632
|
|
34
|
-
hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-
|
|
34
|
+
hardpy/hardpy_panel/frontend/dist/assets/browser-ponyfill-DzwgrUwX.js,sha256=RFhwMCb6yd2G1Wed9APir2qK2Zhgxv71TNiPpLc3mx8,10294
|
|
35
35
|
hardpy/hardpy_panel/frontend/dist/assets/index-B-fsa5Ru.js,sha256=IonL7d7ppdDr-_FRJZQPWI4HHFTiygYvZGVlUxHY9R8,294235
|
|
36
36
|
hardpy/hardpy_panel/frontend/dist/assets/index-B7T9xvaW.css,sha256=5m7QXWbthqi_Va8qlvnTZeuRzSN_ZJUdhyeb3JD6ZME,315862
|
|
37
|
-
hardpy/hardpy_panel/frontend/dist/assets/index-
|
|
37
|
+
hardpy/hardpy_panel/frontend/dist/assets/index-CVhA7vmQ.js,sha256=aIaCyBi75O1XOfSLpfXtxreTMrV2_9MmMXOrlfamuY0,6027073
|
|
38
38
|
hardpy/hardpy_panel/frontend/dist/assets/index-DLOviMB1.js,sha256=sI0W1vvwqvIwKP2_jglrwOhej3n5rJD72-d4ZhlUHqM,285612
|
|
39
39
|
hardpy/hardpy_panel/frontend/dist/assets/logo_smol-CK3jE85c.png,sha256=E4K7drvhJCg9HcTpRihOXZhVJVBZ7-W97Se-3tDb46o,14485
|
|
40
|
-
hardpy/hardpy_panel/frontend/dist/assets/splitPathsBySizeLoader-
|
|
40
|
+
hardpy/hardpy_panel/frontend/dist/assets/splitPathsBySizeLoader-BdwEQHyO.js,sha256=bOuL64ihCfZQao3ML5zVwlUdzkko3eqZno4Xs0rhCnQ,472
|
|
41
|
+
hardpy/hardpy_panel/frontend/dist/locales/cs/translation.json,sha256=kCSbBWfQQ6lrVKlOeByXAhAoG5bBxrxf7-PTvvQgR2M,2964
|
|
41
42
|
hardpy/hardpy_panel/frontend/dist/locales/de/translation.json,sha256=wZRO5iz8VvaEjACSWHJNjjKerd8YcPo7gpziCpjfF1Q,3073
|
|
42
43
|
hardpy/hardpy_panel/frontend/dist/locales/en/translation.json,sha256=_vsAG4aOto5C-xiCQK0zip02u6f9wHr-T_BRr_DUdTo,2776
|
|
43
44
|
hardpy/hardpy_panel/frontend/dist/locales/es/translation.json,sha256=vkXYntVaRnl-aEZZpPFlQE7qJXC2rAuDvbAcXMwQ9V8,3154
|
|
@@ -46,32 +47,32 @@ hardpy/hardpy_panel/frontend/dist/locales/ja/translation.json,sha256=4_MrBUHCneX
|
|
|
46
47
|
hardpy/hardpy_panel/frontend/dist/locales/ru/translation.json,sha256=LvJavIT8_HdrIMIvgGtRBoGOCs3DeBNyYg0BHALwmEg,4097
|
|
47
48
|
hardpy/hardpy_panel/frontend/dist/locales/zh/translation.json,sha256=ZmoqrndiAtatXSVDpz6looKb_2U_XnJdIidfK9o-uXY,2781
|
|
48
49
|
hardpy/pytest_hardpy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
|
-
hardpy/pytest_hardpy/plugin.py,sha256=
|
|
50
|
+
hardpy/pytest_hardpy/plugin.py,sha256=1URbMU2jbZHCVWN7SvuwmKktetQu0AFF2IK5cCLJy10,24586
|
|
50
51
|
hardpy/pytest_hardpy/pytest_call.py,sha256=qUDrK1iUjhGEs4bmBFTk9E0YfFzsePoHhVDRY6ngRV8,22878
|
|
51
|
-
hardpy/pytest_hardpy/pytest_wrapper.py,sha256=
|
|
52
|
-
hardpy/pytest_hardpy/db/__init__.py,sha256=
|
|
53
|
-
hardpy/pytest_hardpy/db/base_store.py,sha256=d1lkTB7CpHTKysD2yuuGQFai44OtOmtTbq-WaBYojhw,5545
|
|
52
|
+
hardpy/pytest_hardpy/pytest_wrapper.py,sha256=TS-Ysm1ou-bkkc50A0tdJWXCNx51sB1EOkUUYxQ64GA,7737
|
|
53
|
+
hardpy/pytest_hardpy/db/__init__.py,sha256=3uNc4xjbxFnEGX8hErEbrhrbky_XlplVGr7LmiR1XTg,791
|
|
54
54
|
hardpy/pytest_hardpy/db/const.py,sha256=E_A0IKGeS3qyPX4fTfUE5ksARsrTKSVWqUkdmh8S_fo,1414
|
|
55
|
-
hardpy/pytest_hardpy/db/runstore.py,sha256=
|
|
55
|
+
hardpy/pytest_hardpy/db/runstore.py,sha256=qeg4mxlgEW7LCNklKOJvmJ18gXznfZj1LUqma-N9Y3o,13025
|
|
56
56
|
hardpy/pytest_hardpy/db/stand_type.py,sha256=p3AFtgMt-sn8QXRp60YM-xo2mEjZHUhYr_Mxhz1WyP0,7438
|
|
57
|
-
hardpy/pytest_hardpy/db/statestore.py,sha256=
|
|
58
|
-
hardpy/pytest_hardpy/db/tempstore.py,sha256=
|
|
57
|
+
hardpy/pytest_hardpy/db/statestore.py,sha256=xTWDZ2uKtVAJzGjHuyltZhesc3mVdU6PraJNUPiIpM8,13444
|
|
58
|
+
hardpy/pytest_hardpy/db/tempstore.py,sha256=QpN5VfX-dtfL6ZcTnHKt73DU-xaDK7YunuN5qRFqeCk,8084
|
|
59
59
|
hardpy/pytest_hardpy/db/schema/__init__.py,sha256=1S73W3PLQt8gX5Y33nbX1JdwLvnrtlKH4cElID3pwuc,263
|
|
60
60
|
hardpy/pytest_hardpy/db/schema/v1.py,sha256=0RGZP-2lDeA3r8-simEEnjlHOAyziYSMXb9BINQyVbM,6377
|
|
61
61
|
hardpy/pytest_hardpy/reporter/__init__.py,sha256=rztpM2HlLUpMOvad0JHbZU4Mk8PDDQyCFXLhpLktGQI,322
|
|
62
62
|
hardpy/pytest_hardpy/reporter/base.py,sha256=hUt0UTzZOa9KdHj66cOnqgDVakYh4GncE1YkCFqwCBs,3666
|
|
63
|
-
hardpy/pytest_hardpy/reporter/hook_reporter.py,sha256=
|
|
63
|
+
hardpy/pytest_hardpy/reporter/hook_reporter.py,sha256=Et312HLWQ-WUSjRSNa4Yg392HbRRbCNfzRQWvcCC11c,16199
|
|
64
64
|
hardpy/pytest_hardpy/reporter/runner_reporter.py,sha256=d9hyThq0tywelPnIIHVED2SyztavE5LbgcBSejXfnhA,787
|
|
65
|
-
hardpy/pytest_hardpy/result/__init__.py,sha256=
|
|
65
|
+
hardpy/pytest_hardpy/result/__init__.py,sha256=po1zsaNOqS-DaQO_6Xo2mW3fGdSopeZ646BdcU56SR4,687
|
|
66
66
|
hardpy/pytest_hardpy/result/couchdb_config.py,sha256=ujxyJYM2pdZzi3GZ2Zysbz2_ZeTRN5sQc8AGuzRJm_0,3243
|
|
67
67
|
hardpy/pytest_hardpy/result/report_loader/__init__.py,sha256=wq5Y-_JW2ExCRnQ9VVesKmTToEQrcTY5RxNJIWaT9ag,374
|
|
68
68
|
hardpy/pytest_hardpy/result/report_loader/couchdb_loader.py,sha256=KcZ0JkCgWhrj2J9M04JBDy0fpqtpVEYtu9GCLDG27pU,2255
|
|
69
|
+
hardpy/pytest_hardpy/result/report_loader/json_loader.py,sha256=7jiDiKnCnADC6JDxctKucXJSg33yraiJoeOem7KAHeo,1647
|
|
69
70
|
hardpy/pytest_hardpy/result/report_loader/stand_cloud_loader.py,sha256=CjvkS6N1OzKmMAqfs6bhQLK2mlB_NIKX-YnJn39SHqY,3991
|
|
70
71
|
hardpy/pytest_hardpy/result/report_reader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
71
72
|
hardpy/pytest_hardpy/result/report_reader/couchdb_reader.py,sha256=lnWSX-0QKbdMwtqfCtW0tiH9W_ZEPqQ3rb7Lc8gES7E,5726
|
|
72
73
|
hardpy/pytest_hardpy/result/report_reader/stand_cloud_reader.py,sha256=uT7YSBu1QyURH9IkgRCdpbinn8LKXUhgVEhwPmGZV7I,3636
|
|
73
74
|
hardpy/pytest_hardpy/result/report_synchronizer/__init__.py,sha256=QezaT_Yk8LrciygdsFPJeZn0EBUaKpd0GfCQjSuIo-I,273
|
|
74
|
-
hardpy/pytest_hardpy/result/report_synchronizer/synchronizer.py,sha256=
|
|
75
|
+
hardpy/pytest_hardpy/result/report_synchronizer/synchronizer.py,sha256=r26sfyf__HiYlC385D4Vc3gYNl46jlAc86CaasLzckg,5379
|
|
75
76
|
hardpy/pytest_hardpy/utils/__init__.py,sha256=zHln8ySBHesYAwYatLYkHol5TuuTTNOqrsMP7ONFEG0,1338
|
|
76
77
|
hardpy/pytest_hardpy/utils/const.py,sha256=xS3jBrW_D6IUTlAjSnLiHvSthieRHCj3uN_6fFAXS0w,1832
|
|
77
78
|
hardpy/pytest_hardpy/utils/dialog_box.py,sha256=eCLGQ-Z8rDPd_8ABHRtbkd7piSZcJoG-bCBmnyq29Pw,11375
|
|
@@ -79,8 +80,8 @@ hardpy/pytest_hardpy/utils/exception.py,sha256=1l2VBZLUnjPDoOs744MtaP7Y9FuXUq7ko
|
|
|
79
80
|
hardpy/pytest_hardpy/utils/machineid.py,sha256=6JAzUt7KtjTYn8kL9hSMaCQ20U8liH-zDT9v-5Ch7Q8,296
|
|
80
81
|
hardpy/pytest_hardpy/utils/node_info.py,sha256=DaW566WvsyWR66CThuZ38UoHwQa-pu-4WRLg61OXDnE,7134
|
|
81
82
|
hardpy/pytest_hardpy/utils/progress_calculator.py,sha256=TPl2gG0ZSvMe8otPythhF9hkD6fa6-mJAhy9yI83-yE,1071
|
|
82
|
-
hardpy-0.
|
|
83
|
-
hardpy-0.
|
|
84
|
-
hardpy-0.
|
|
85
|
-
hardpy-0.
|
|
86
|
-
hardpy-0.
|
|
83
|
+
hardpy-0.20.0.dist-info/METADATA,sha256=WdcxNOzCqsrSOLwKnjku-DtWSQN_qJ-CeXb-aF08X1Q,5416
|
|
84
|
+
hardpy-0.20.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
85
|
+
hardpy-0.20.0.dist-info/entry_points.txt,sha256=nL2sMkKMScNaOE0IPkYnu9Yr-BUswZvGSrwY-SxHY3E,102
|
|
86
|
+
hardpy-0.20.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
87
|
+
hardpy-0.20.0.dist-info/RECORD,,
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Everypin
|
|
2
|
-
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
-
|
|
4
|
-
from json import dumps
|
|
5
|
-
from logging import getLogger
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
from glom import assign, glom
|
|
9
|
-
from pycouchdb import Server as DbServer
|
|
10
|
-
from pycouchdb.client import Database
|
|
11
|
-
from pycouchdb.exceptions import Conflict, GenericError, NotFound
|
|
12
|
-
from pydantic._internal._model_construction import ModelMetaclass
|
|
13
|
-
from requests.exceptions import ConnectionError # noqa: A004
|
|
14
|
-
|
|
15
|
-
from hardpy.common.config import ConfigManager
|
|
16
|
-
from hardpy.pytest_hardpy.db.const import DatabaseField as DF # noqa: N817
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class BaseStore:
|
|
20
|
-
"""HardPy base storage interface for CouchDB."""
|
|
21
|
-
|
|
22
|
-
def __init__(self, db_name: str) -> None:
|
|
23
|
-
config_manager = ConfigManager()
|
|
24
|
-
config = config_manager.config
|
|
25
|
-
self._db_srv = DbServer(config.database.url)
|
|
26
|
-
self._db_name = db_name
|
|
27
|
-
self._db = self._init_db()
|
|
28
|
-
self._doc_id = config.database.doc_id
|
|
29
|
-
self._log = getLogger(__name__)
|
|
30
|
-
self._doc: dict = self._init_doc()
|
|
31
|
-
self._schema: ModelMetaclass
|
|
32
|
-
|
|
33
|
-
def compact(self) -> None:
|
|
34
|
-
"""Compact database."""
|
|
35
|
-
self._db.compact()
|
|
36
|
-
|
|
37
|
-
def get_field(self, key: str) -> Any: # noqa: ANN401
|
|
38
|
-
"""Get field from the state store.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
key (str): field name
|
|
42
|
-
|
|
43
|
-
Returns:
|
|
44
|
-
Any: field value
|
|
45
|
-
"""
|
|
46
|
-
return glom(self._doc, key)
|
|
47
|
-
|
|
48
|
-
def update_doc_value(self, key: str, value: Any) -> None: # noqa: ANN401
|
|
49
|
-
"""Update document value.
|
|
50
|
-
|
|
51
|
-
HardPy collecting uses a simple key without dots.
|
|
52
|
-
Assign is used to update a document.
|
|
53
|
-
Assign is a longer function.
|
|
54
|
-
|
|
55
|
-
Args:
|
|
56
|
-
key (str): document key
|
|
57
|
-
value: document value
|
|
58
|
-
"""
|
|
59
|
-
try:
|
|
60
|
-
dumps(value)
|
|
61
|
-
except Exception: # noqa: BLE001
|
|
62
|
-
# serialize non-serializable objects as string
|
|
63
|
-
value = dumps(value, default=str)
|
|
64
|
-
if "." in key:
|
|
65
|
-
assign(self._doc, key, value)
|
|
66
|
-
else:
|
|
67
|
-
self._doc[key] = value
|
|
68
|
-
|
|
69
|
-
def update_db(self) -> None:
|
|
70
|
-
"""Update database by current document."""
|
|
71
|
-
try:
|
|
72
|
-
self._doc = self._db.save(self._doc)
|
|
73
|
-
except Conflict:
|
|
74
|
-
self._doc["_rev"] = self._db.get(self._doc_id)["_rev"]
|
|
75
|
-
self._doc = self._db.save(self._doc)
|
|
76
|
-
|
|
77
|
-
def update_doc(self) -> None:
|
|
78
|
-
"""Update current document by database."""
|
|
79
|
-
self._doc["_rev"] = self._db.get(self._doc_id)["_rev"]
|
|
80
|
-
self._doc = self._db.get(self._doc_id)
|
|
81
|
-
|
|
82
|
-
def get_document(self) -> ModelMetaclass:
|
|
83
|
-
"""Get document by schema.
|
|
84
|
-
|
|
85
|
-
Returns:
|
|
86
|
-
ModelMetaclass: document by schema
|
|
87
|
-
"""
|
|
88
|
-
self._doc = self._db.get(self._doc_id)
|
|
89
|
-
return self._schema(**self._doc)
|
|
90
|
-
|
|
91
|
-
def clear(self) -> None:
|
|
92
|
-
"""Clear database."""
|
|
93
|
-
try:
|
|
94
|
-
# Clear statestore and runstore databases before each launch
|
|
95
|
-
self._db.delete(self._doc_id)
|
|
96
|
-
except (Conflict, NotFound):
|
|
97
|
-
self._log.debug("Database will be created for the first time")
|
|
98
|
-
self._doc: dict = self._init_doc()
|
|
99
|
-
|
|
100
|
-
def _init_db(self) -> Database:
|
|
101
|
-
try:
|
|
102
|
-
return self._db_srv.create(self._db_name) # type: ignore
|
|
103
|
-
except Conflict:
|
|
104
|
-
# database is already created
|
|
105
|
-
return self._db_srv.database(self._db_name)
|
|
106
|
-
except GenericError as exc:
|
|
107
|
-
msg = f"Error initializing database {exc}"
|
|
108
|
-
raise RuntimeError(msg) from exc
|
|
109
|
-
except ConnectionError as exc:
|
|
110
|
-
msg = f"Error initializing database: {exc}"
|
|
111
|
-
raise RuntimeError(msg) from exc
|
|
112
|
-
|
|
113
|
-
def _init_doc(self) -> dict:
|
|
114
|
-
try:
|
|
115
|
-
doc = self._db.get(self._doc_id)
|
|
116
|
-
except NotFound:
|
|
117
|
-
return {
|
|
118
|
-
"_id": self._doc_id,
|
|
119
|
-
DF.MODULES: {},
|
|
120
|
-
DF.DUT: {
|
|
121
|
-
DF.TYPE: None,
|
|
122
|
-
DF.NAME: None,
|
|
123
|
-
DF.REVISION: None,
|
|
124
|
-
DF.SERIAL_NUMBER: None,
|
|
125
|
-
DF.PART_NUMBER: None,
|
|
126
|
-
DF.SUB_UNITS: [],
|
|
127
|
-
DF.INFO: {},
|
|
128
|
-
},
|
|
129
|
-
DF.TEST_STAND: {
|
|
130
|
-
DF.HW_ID: None,
|
|
131
|
-
DF.NAME: None,
|
|
132
|
-
DF.REVISION: None,
|
|
133
|
-
DF.TIMEZONE: None,
|
|
134
|
-
DF.LOCATION: None,
|
|
135
|
-
DF.NUMBER: None,
|
|
136
|
-
DF.INSTRUMENTS: [],
|
|
137
|
-
DF.DRIVERS: {},
|
|
138
|
-
DF.INFO: {},
|
|
139
|
-
},
|
|
140
|
-
DF.PROCESS: {
|
|
141
|
-
DF.NAME: None,
|
|
142
|
-
DF.NUMBER: None,
|
|
143
|
-
DF.INFO: {},
|
|
144
|
-
},
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
# init document
|
|
148
|
-
if DF.MODULES not in doc:
|
|
149
|
-
doc[DF.MODULES] = {}
|
|
150
|
-
|
|
151
|
-
doc[DF.DUT] = {
|
|
152
|
-
DF.TYPE: None,
|
|
153
|
-
DF.NAME: None,
|
|
154
|
-
DF.REVISION: None,
|
|
155
|
-
DF.SERIAL_NUMBER: None,
|
|
156
|
-
DF.PART_NUMBER: None,
|
|
157
|
-
DF.SUB_UNITS: [],
|
|
158
|
-
DF.INFO: {},
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
doc[DF.TEST_STAND] = {
|
|
162
|
-
DF.HW_ID: None,
|
|
163
|
-
DF.NAME: None,
|
|
164
|
-
DF.REVISION: None,
|
|
165
|
-
DF.TIMEZONE: None,
|
|
166
|
-
DF.LOCATION: None,
|
|
167
|
-
DF.NUMBER: None,
|
|
168
|
-
DF.INSTRUMENTS: [],
|
|
169
|
-
DF.DRIVERS: {},
|
|
170
|
-
DF.INFO: {},
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
doc[DF.PROCESS] = {
|
|
174
|
-
DF.NAME: None,
|
|
175
|
-
DF.NUMBER: None,
|
|
176
|
-
DF.INFO: {},
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return doc
|
|
File without changes
|
|
File without changes
|
|
File without changes
|