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.
Files changed (27) hide show
  1. hardpy/__init__.py +2 -0
  2. hardpy/cli/cli.py +6 -0
  3. hardpy/common/config.py +19 -0
  4. hardpy/hardpy_panel/api.py +62 -1
  5. hardpy/hardpy_panel/frontend/dist/assets/{allPaths-C_-7WXHD.js → allPaths-BXbcAtew.js} +1 -1
  6. hardpy/hardpy_panel/frontend/dist/assets/{allPathsLoader-DgH0Xily.js → allPathsLoader-lJLHMNNZ.js} +2 -2
  7. hardpy/hardpy_panel/frontend/dist/assets/{browser-ponyfill-BbOvdqIF.js → browser-ponyfill-DzwgrUwX.js} +1 -1
  8. hardpy/hardpy_panel/frontend/dist/assets/{index-DEJb2W0B.js → index-CVhA7vmQ.js} +158 -158
  9. hardpy/hardpy_panel/frontend/dist/assets/{splitPathsBySizeLoader-o5HCcdVL.js → splitPathsBySizeLoader-BdwEQHyO.js} +1 -1
  10. hardpy/hardpy_panel/frontend/dist/index.html +1 -1
  11. hardpy/hardpy_panel/frontend/dist/locales/cs/translation.json +85 -0
  12. hardpy/pytest_hardpy/db/__init__.py +0 -2
  13. hardpy/pytest_hardpy/db/runstore.py +378 -10
  14. hardpy/pytest_hardpy/db/statestore.py +390 -5
  15. hardpy/pytest_hardpy/db/tempstore.py +219 -17
  16. hardpy/pytest_hardpy/plugin.py +24 -0
  17. hardpy/pytest_hardpy/pytest_wrapper.py +6 -1
  18. hardpy/pytest_hardpy/reporter/hook_reporter.py +18 -0
  19. hardpy/pytest_hardpy/result/__init__.py +2 -0
  20. hardpy/pytest_hardpy/result/report_loader/json_loader.py +49 -0
  21. hardpy/pytest_hardpy/result/report_synchronizer/synchronizer.py +25 -9
  22. {hardpy-0.19.0.dist-info → hardpy-0.20.0.dist-info}/METADATA +18 -2
  23. {hardpy-0.19.0.dist-info → hardpy-0.20.0.dist-info}/RECORD +26 -25
  24. hardpy/pytest_hardpy/db/base_store.py +0 -179
  25. {hardpy-0.19.0.dist-info → hardpy-0.20.0.dist-info}/WHEEL +0 -0
  26. {hardpy-0.19.0.dist-info → hardpy-0.20.0.dist-info}/entry_points.txt +0 -0
  27. {hardpy-0.19.0.dist-info → hardpy-0.20.0.dist-info}/licenses/LICENSE +0 -0
@@ -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
- return (
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 = 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
- if not self._tempstore.reports():
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._tempstore.reports():
50
+ for _report in self._get_tempstore.reports():
39
51
  try:
40
- report_id = _report.get("id")
41
- document: dict = _report.get("doc")
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._tempstore.dict_to_schema(document)
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._tempstore.delete(report_id):
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._tempstore.push_report(report)
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.19.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
  [![pytest versions](https://img.shields.io/badge/pytest-%3E%3D7.0-blue)](https://docs.pytest.org/en/latest/)
70
71
  [![Documentation](https://img.shields.io/badge/Documentation%20-Overview%20-%20%23007ec6)](https://everypinio.github.io/hardpy/)
71
72
  [![Reddit](https://img.shields.io/badge/-Reddit-FF4500?style=flat&logo=reddit&logoColor=white)](https://www.reddit.com/r/HardPy)
73
+ [![Discord](https://img.shields.io/discord/1304494076799877172?color=7389D8&label&logo=discord&logoColor=ffffff)](https://discord.gg/98bWadmG8J)
72
74
  [![Telegram](https://img.shields.io/badge/-Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](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=B99MElYhd8HHgOJaT-RVPyvIN18uPaG2pb78TJ3lqvE,2957
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=eg89XAcPSosykXyeWitcTigE-ZYiDeCknPWp9TpXxeA,11359
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=cgs4rGgRbKsDasM1NupmdERK2XaHZin3lLn346WRnKk,7730
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=F4wSHiE5kiFvthR_TdrcmXSbfq-gP00uZKC0Pnoyq-4,10744
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=Hhb9wXuzSNSaG5KgX_63fvg0XEzwkp80jc-g-uobwqQ,1851
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-C_-7WXHD.js,sha256=g19yyAeijLh7WccMXT8_tuk1S-uWTOyPFzWHpyUZLQc,309
23
- hardpy/hardpy_panel/frontend/dist/assets/allPathsLoader-DgH0Xily.js,sha256=CRNnNN2zycYIwYtzHnBbPj5EbtZSGVhLGfvbOswm4SM,550
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-BbOvdqIF.js,sha256=ga4pi7C6UzfwyVTi8QiAL0rpgvlXPi37PXqlLP8A-Ug,10294
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-DEJb2W0B.js,sha256=TVKRBm2PplF8vsQM_bEiA7VNhUKZHMDpD5Il33haQ40,6026022
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-o5HCcdVL.js,sha256=ZvdGFd8j0qHvEPUqfpcCFtwRg2ptF-uPZCoJMUUK2Ac,472
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=RqBZeIjdrV7szq0tShjqq6TkpWWBhzQN89KcCHSYhEo,23335
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=QqqkoPJPBxYg3WQQSmq72RyBjYfLm1DJsTzelpptKRw,7579
52
- hardpy/pytest_hardpy/db/__init__.py,sha256=nat_tUO2cxPIp9e6U8Fvg6V4NcZ9TVg27u0GHoKelD4,865
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=humoVBDZJGh7j_v5Xrf3P0HHaeRXC8oRdIj1QWGZizg,946
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=1s615c4OdzHd5u-464O-lcXoKulMfZ6moblhTWDHwSg,583
58
- hardpy/pytest_hardpy/db/tempstore.py,sha256=WKIykL_4A9j8n-F7pIy_9fj4BNOfzqWkUwg0_8DR05s,1802
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=t6R-H6SGhBCGvROStCbeyry6EJxf7n8oUOMbcTCzSEk,15634
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=2afpuEuOcxYfIEOwWzsGZe960iQaPVCmsbYujijQg1s,592
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=b1DDDFgEJzsxYRvkzNqeuDvvuWFUIYat1UK0Ct_9N5Y,4745
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.19.0.dist-info/METADATA,sha256=FbZRcpbdZutNFOErGOlgFhGHVcjChz4B1YAiDhrR-e4,4971
83
- hardpy-0.19.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
84
- hardpy-0.19.0.dist-info/entry_points.txt,sha256=nL2sMkKMScNaOE0IPkYnu9Yr-BUswZvGSrwY-SxHY3E,102
85
- hardpy-0.19.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
86
- hardpy-0.19.0.dist-info/RECORD,,
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