hardpy 0.10.1__py3-none-any.whl → 0.11.1__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 (35) hide show
  1. hardpy/__init__.py +10 -2
  2. hardpy/cli/cli.py +91 -13
  3. hardpy/cli/template.py +0 -4
  4. hardpy/common/config.py +17 -21
  5. hardpy/common/stand_cloud/__init__.py +2 -1
  6. hardpy/common/stand_cloud/connector.py +32 -38
  7. hardpy/hardpy_panel/api.py +24 -2
  8. hardpy/hardpy_panel/frontend/dist/asset-manifest.json +3 -3
  9. hardpy/hardpy_panel/frontend/dist/index.html +1 -1
  10. hardpy/hardpy_panel/frontend/dist/static/js/main.fb8b84a3.js +3 -0
  11. hardpy/hardpy_panel/frontend/dist/static/js/main.fb8b84a3.js.map +1 -0
  12. hardpy/pytest_hardpy/db/base_store.py +7 -2
  13. hardpy/pytest_hardpy/db/const.py +3 -0
  14. hardpy/pytest_hardpy/db/schema/v1.py +22 -0
  15. hardpy/pytest_hardpy/plugin.py +27 -20
  16. hardpy/pytest_hardpy/pytest_call.py +30 -41
  17. hardpy/pytest_hardpy/pytest_wrapper.py +21 -17
  18. hardpy/pytest_hardpy/reporter/base.py +9 -4
  19. hardpy/pytest_hardpy/reporter/hook_reporter.py +7 -0
  20. hardpy/pytest_hardpy/result/__init__.py +4 -0
  21. hardpy/pytest_hardpy/result/couchdb_config.py +6 -8
  22. hardpy/pytest_hardpy/result/report_loader/stand_cloud_loader.py +6 -9
  23. hardpy/pytest_hardpy/result/report_reader/stand_cloud_reader.py +84 -0
  24. hardpy/pytest_hardpy/utils/__init__.py +2 -0
  25. hardpy/pytest_hardpy/utils/connection_data.py +0 -4
  26. hardpy/pytest_hardpy/utils/dialog_box.py +61 -8
  27. hardpy/pytest_hardpy/utils/exception.py +1 -0
  28. {hardpy-0.10.1.dist-info → hardpy-0.11.1.dist-info}/METADATA +1 -1
  29. {hardpy-0.10.1.dist-info → hardpy-0.11.1.dist-info}/RECORD +33 -32
  30. hardpy/hardpy_panel/frontend/dist/static/js/main.8a7d8f7d.js +0 -3
  31. hardpy/hardpy_panel/frontend/dist/static/js/main.8a7d8f7d.js.map +0 -1
  32. /hardpy/hardpy_panel/frontend/dist/static/js/{main.8a7d8f7d.js.LICENSE.txt → main.fb8b84a3.js.LICENSE.txt} +0 -0
  33. {hardpy-0.10.1.dist-info → hardpy-0.11.1.dist-info}/WHEEL +0 -0
  34. {hardpy-0.10.1.dist-info → hardpy-0.11.1.dist-info}/entry_points.txt +0 -0
  35. {hardpy-0.10.1.dist-info → hardpy-0.11.1.dist-info}/licenses/LICENSE +0 -0
hardpy/__init__.py CHANGED
@@ -1,7 +1,7 @@
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 hardpy.common.stand_cloud import StandCloudError
4
+ from hardpy.common.stand_cloud import StandCloudConnector, StandCloudError
5
5
  from hardpy.pytest_hardpy.pytest_call import (
6
6
  clear_operator_message,
7
7
  get_current_attempt,
@@ -20,7 +20,11 @@ from hardpy.pytest_hardpy.pytest_call import (
20
20
  set_stand_location,
21
21
  set_stand_name,
22
22
  )
23
- from hardpy.pytest_hardpy.result import CouchdbLoader, StandCloudLoader
23
+ from hardpy.pytest_hardpy.result import (
24
+ CouchdbLoader,
25
+ StandCloudLoader,
26
+ StandCloudReader,
27
+ )
24
28
  from hardpy.pytest_hardpy.result.couchdb_config import CouchdbConfig
25
29
  from hardpy.pytest_hardpy.utils import (
26
30
  BaseWidget,
@@ -30,6 +34,7 @@ from hardpy.pytest_hardpy.utils import (
30
34
  DuplicateSerialNumberError,
31
35
  DuplicateTestStandLocationError,
32
36
  DuplicateTestStandNameError,
37
+ HTMLComponent,
33
38
  ImageComponent,
34
39
  MultistepWidget,
35
40
  NumericInputWidget,
@@ -48,12 +53,15 @@ __all__ = [
48
53
  "DuplicateSerialNumberError",
49
54
  "DuplicateTestStandLocationError",
50
55
  "DuplicateTestStandNameError",
56
+ "HTMLComponent",
51
57
  "ImageComponent",
52
58
  "MultistepWidget",
53
59
  "NumericInputWidget",
54
60
  "RadiobuttonWidget",
61
+ "StandCloudConnector",
55
62
  "StandCloudError",
56
63
  "StandCloudLoader",
64
+ "StandCloudReader",
57
65
  "StepWidget",
58
66
  "TextInputWidget",
59
67
  "clear_operator_message",
hardpy/cli/cli.py CHANGED
@@ -6,11 +6,12 @@ import sys
6
6
  from pathlib import Path
7
7
  from typing import Annotated, Optional
8
8
 
9
+ import requests
9
10
  import typer
10
11
  from uvicorn import run as uvicorn_run
11
12
 
12
13
  from hardpy.cli.template import TemplateGenerator
13
- from hardpy.common.config import ConfigManager
14
+ from hardpy.common.config import ConfigManager, HardpyConfig
14
15
  from hardpy.common.stand_cloud import (
15
16
  StandCloudConnector,
16
17
  StandCloudError,
@@ -31,6 +32,10 @@ default_config = ConfigManager().get_config()
31
32
  @cli.command()
32
33
  def init( # noqa: PLR0913
33
34
  tests_dir: Annotated[Optional[str], typer.Argument()] = None,
35
+ tests_name: str = typer.Option(
36
+ default="",
37
+ help="Specify a tests suite name.",
38
+ ),
34
39
  create_database: bool = typer.Option(
35
40
  default=True,
36
41
  help="Create CouchDB database.",
@@ -59,14 +64,6 @@ def init( # noqa: PLR0913
59
64
  default=default_config.frontend.port,
60
65
  help="Specify a frontend port.",
61
66
  ),
62
- socket_host: str = typer.Option(
63
- default=default_config.socket.host,
64
- help="Specify a socket host.",
65
- ),
66
- socket_port: int = typer.Option(
67
- default=default_config.socket.port,
68
- help="Specify a socket port.",
69
- ),
70
67
  sc_address: str = typer.Option(
71
68
  default="",
72
69
  help="Specify a StandCloud address.",
@@ -80,6 +77,7 @@ def init( # noqa: PLR0913
80
77
 
81
78
  Args:
82
79
  tests_dir (str | None): Tests directory. Current directory + `tests` by default
80
+ tests_name (str): Tests suite name, "Tests" by default
83
81
  create_database (bool): Flag to create database
84
82
  database_user (str): Database user name
85
83
  database_password (str): Database password
@@ -87,22 +85,20 @@ def init( # noqa: PLR0913
87
85
  database_port (int): Database port
88
86
  frontend_host (str): Panel operator host
89
87
  frontend_port (int): Panel operator port
90
- socket_host (str): Socket host
91
- socket_port (int): Socket port
92
88
  sc_address (str): StandCloud address
93
89
  sc_connection_only (bool): Flag to check StandCloud service availability
94
90
  """
95
91
  _tests_dir = tests_dir if tests_dir else default_config.tests_dir
92
+ _tests_name = tests_name if tests_name else default_config.tests_name
96
93
  ConfigManager().init_config(
97
94
  tests_dir=str(_tests_dir),
95
+ tests_name=_tests_name,
98
96
  database_user=database_user,
99
97
  database_password=database_password,
100
98
  database_host=database_host,
101
99
  database_port=database_port,
102
100
  frontend_host=frontend_host,
103
101
  frontend_port=frontend_port,
104
- socket_host=socket_host,
105
- socket_port=socket_port,
106
102
  sc_address=sc_address,
107
103
  sc_connection_only=sc_connection_only,
108
104
  )
@@ -164,6 +160,48 @@ def run(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
164
160
  )
165
161
 
166
162
 
163
+ @cli.command()
164
+ def start(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
165
+ """Start HardPy tests.
166
+
167
+ Args:
168
+ tests_dir (Optional[str]): Test directory. Current directory by default
169
+ """
170
+ config = _get_config(tests_dir)
171
+ _check_config(config)
172
+
173
+ url = f"http://{config.frontend.host}:{config.frontend.port}/api/start"
174
+ _request_hardpy(url)
175
+
176
+
177
+ @cli.command()
178
+ def stop(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
179
+ """Stop HardPy tests.
180
+
181
+ Args:
182
+ tests_dir (Optional[str]): Test directory. Current directory by default
183
+ """
184
+ config = _get_config(tests_dir)
185
+ _check_config(config)
186
+
187
+ url = f"http://{config.frontend.host}:{config.frontend.port}/api/stop"
188
+ _request_hardpy(url)
189
+
190
+
191
+ @cli.command()
192
+ def status(tests_dir: Annotated[Optional[str], typer.Argument()] = None) -> None:
193
+ """Get HardPy test launch status.
194
+
195
+ Args:
196
+ tests_dir (Optional[str]): Test directory. Current directory by default
197
+ """
198
+ config = _get_config(tests_dir)
199
+ _check_config(config)
200
+
201
+ url = f"http://{config.frontend.host}:{config.frontend.port}/api/status"
202
+ _request_hardpy(url)
203
+
204
+
167
205
  @cli.command()
168
206
  def sc_login(
169
207
  address: Annotated[str, typer.Argument()],
@@ -207,5 +245,45 @@ def sc_logout() -> None:
207
245
  print("HardPy logout failed")
208
246
 
209
247
 
248
+ def _get_config(tests_dir: str | None = None) -> HardpyConfig:
249
+ dir_path = Path.cwd() / tests_dir if tests_dir else Path.cwd()
250
+ config = ConfigManager().read_config(dir_path)
251
+
252
+ if not config:
253
+ print(f"Config at path {dir_path} not found.")
254
+ sys.exit()
255
+
256
+ return config
257
+
258
+
259
+ def _check_config(config: HardpyConfig) -> None:
260
+ url = f"http://{config.frontend.host}:{config.frontend.port}/api/hardpy_config"
261
+ error_msg = f"HardPy in directory {config.tests_dir} does not run."
262
+ try:
263
+ response = requests.get(url, timeout=2)
264
+ except Exception:
265
+ print(error_msg)
266
+ sys.exit()
267
+
268
+ running_config: dict = response.json()
269
+ if config.model_dump() != running_config:
270
+ print(error_msg)
271
+ sys.exit()
272
+
273
+
274
+ def _request_hardpy(url: str) -> None:
275
+ try:
276
+ response = requests.get(url, timeout=2)
277
+ except Exception:
278
+ print("HardPy operator panel is not running.")
279
+ sys.exit()
280
+ try:
281
+ status: dict = response.json().get("status", "ERROR")
282
+ except ValueError:
283
+ print(f"Hardpy internal error: {response}.")
284
+ sys.exit()
285
+ print(f"HardPy status: {status}.")
286
+
287
+
210
288
  if __name__ == "__main__":
211
289
  cli()
hardpy/cli/template.py CHANGED
@@ -138,8 +138,6 @@ log_cli_format = %%(asctime)s [%%(levelname)s] %%(message)s
138
138
  log_cli_date_format = %H:%M:%S
139
139
  addopts = --hardpy-pt
140
140
  --hardpy-db-url http://{}:{}@{}:{}/
141
- --hardpy-sh {}
142
- --hardpy-sp {}
143
141
  """
144
142
 
145
143
  test_1_py = """import pytest
@@ -207,8 +205,6 @@ class TemplateGenerator:
207
205
  self._config.database.password,
208
206
  self._config.database.host,
209
207
  self._config.database.port,
210
- self._config.socket.host,
211
- self._config.socket.port,
212
208
  )
213
209
 
214
210
  @property
hardpy/common/config.py CHANGED
@@ -42,14 +42,6 @@ class FrontendConfig(BaseModel):
42
42
  port: int = 8000
43
43
 
44
44
 
45
- class SocketConfig(BaseModel):
46
- """Socket configuration."""
47
-
48
- model_config = ConfigDict(extra="forbid")
49
-
50
- host: str = "localhost"
51
- port: int = 6525
52
-
53
45
  class StandCloudConfig(BaseModel):
54
46
  """StandCloud configuration."""
55
47
 
@@ -58,16 +50,16 @@ class StandCloudConfig(BaseModel):
58
50
  address: str = ""
59
51
  connection_only: bool = False
60
52
 
61
- class HardpyConfig(BaseModel):
53
+ class HardpyConfig(BaseModel, extra="allow"):
62
54
  """HardPy configuration."""
63
55
 
64
56
  model_config = ConfigDict(extra="forbid")
65
57
 
66
58
  title: str = "HardPy TOML config"
67
59
  tests_dir: str = "tests"
60
+ tests_name: str = ""
68
61
  database: DatabaseConfig = DatabaseConfig()
69
62
  frontend: FrontendConfig = FrontendConfig()
70
- socket: SocketConfig = SocketConfig()
71
63
  stand_cloud: StandCloudConfig = StandCloudConfig()
72
64
 
73
65
 
@@ -81,14 +73,13 @@ class ConfigManager:
81
73
  def init_config( # noqa: PLR0913
82
74
  cls,
83
75
  tests_dir: str,
76
+ tests_name: str,
84
77
  database_user: str,
85
78
  database_password: str,
86
79
  database_host: str,
87
80
  database_port: int,
88
81
  frontend_host: str,
89
82
  frontend_port: int,
90
- socket_host: str,
91
- socket_port: int,
92
83
  sc_address: str = "",
93
84
  sc_connection_only: bool = False,
94
85
  ) -> None:
@@ -96,26 +87,24 @@ class ConfigManager:
96
87
 
97
88
  Args:
98
89
  tests_dir (str): Tests directory.
90
+ tests_name (str): Tests suite name.
99
91
  database_user (str): Database user name.
100
92
  database_password (str): Database password.
101
93
  database_host (str): Database host.
102
94
  database_port (int): Database port.
103
95
  frontend_host (str): Operator panel host.
104
96
  frontend_port (int): Operator panel port.
105
- socket_host (str): Socket host.
106
- socket_port (int): Socket port.
107
97
  sc_address (str): StandCloud address.
108
98
  sc_connection_only (bool): StandCloud check availability.
109
99
  """
110
100
  cls.obj.tests_dir = str(tests_dir)
101
+ cls.obj.tests_name = tests_name
111
102
  cls.obj.database.user = database_user
112
103
  cls.obj.database.password = database_password
113
104
  cls.obj.database.host = database_host
114
105
  cls.obj.database.port = database_port
115
106
  cls.obj.frontend.host = frontend_host
116
107
  cls.obj.frontend.port = frontend_port
117
- cls.obj.socket.host = socket_host
118
- cls.obj.socket.port = socket_port
119
108
  cls.obj.stand_cloud.address = sc_address
120
109
  cls.obj.stand_cloud.connection_only = sc_connection_only
121
110
 
@@ -128,6 +117,8 @@ class ConfigManager:
128
117
  """
129
118
  if not cls.obj.stand_cloud.address:
130
119
  del cls.obj.stand_cloud
120
+ if not cls.obj.tests_name:
121
+ del cls.obj.tests_name
131
122
  config_str = tomli_w.dumps(cls.obj.model_dump())
132
123
  with Path.open(parent_dir / "hardpy.toml", "w") as file:
133
124
  file.write(config_str)
@@ -149,13 +140,18 @@ class ConfigManager:
149
140
  return None
150
141
  try:
151
142
  with Path.open(toml_path / "hardpy.toml", "rb") as f:
152
- cls.obj = HardpyConfig(**tomli.load(f))
153
- return cls.obj # noqa: TRY300
154
- except tomli.TOMLDecodeError:
155
- logger.exception("Error parsing TOML")
143
+ toml_data = tomli.load(f)
144
+ except tomli.TOMLDecodeError as exc:
145
+ msg = f"Error parsing TOML: {exc}"
146
+ logger.exception(msg)
147
+ return None
148
+
149
+ try:
150
+ cls.obj = HardpyConfig(**toml_data)
156
151
  except ValidationError:
157
152
  logger.exception("Error parsing TOML")
158
- return None
153
+ return None
154
+ return cls.obj
159
155
 
160
156
  @classmethod
161
157
  def get_config(cls) -> HardpyConfig:
@@ -1,11 +1,12 @@
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 hardpy.common.stand_cloud.connector import StandCloudConnector
4
+ from hardpy.common.stand_cloud.connector import StandCloudAPIMode, StandCloudConnector
5
5
  from hardpy.common.stand_cloud.exception import StandCloudError
6
6
  from hardpy.common.stand_cloud.registration import login, logout
7
7
 
8
8
  __all__ = [
9
+ "StandCloudAPIMode",
9
10
  "StandCloudConnector",
10
11
  "StandCloudError",
11
12
  "login",
@@ -4,19 +4,12 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  from datetime import datetime, timedelta, timezone
7
+ from enum import Enum
7
8
  from logging import getLogger
8
9
  from typing import TYPE_CHECKING, NamedTuple
9
10
 
10
- from oauthlib.oauth2.rfc6749.errors import (
11
- InvalidGrantError,
12
- MissingTokenError,
13
- TokenExpiredError,
14
- )
15
- from requests.exceptions import (
16
- ConnectionError as RequestConnectionError,
17
- HTTPError,
18
- InvalidURL,
19
- )
11
+ from oauthlib.oauth2.rfc6749.errors import OAuth2Error
12
+ from requests.exceptions import RequestException
20
13
  from requests_oauth2client import ApiClient, BearerToken
21
14
  from requests_oauth2client.tokens import ExpiredAccessToken
22
15
  from requests_oauthlib import OAuth2Session
@@ -42,24 +35,41 @@ class StandCloudURL(NamedTuple):
42
35
  par: str
43
36
  auth: str
44
37
 
38
+
39
+ class StandCloudAPIMode(str, Enum):
40
+ """StandCloud API mode.
41
+
42
+ HARDPY for test stand, integration for third-party service.
43
+ """
44
+
45
+ HARDPY = "hardpy"
46
+ INTEGRATION = "integration"
47
+
48
+
45
49
  class StandCloudConnector:
46
50
  """StandCloud API connector."""
47
51
 
48
52
  def __init__(
49
53
  self,
50
54
  addr: str,
55
+ api_mode: StandCloudAPIMode = StandCloudAPIMode.HARDPY,
56
+ api_version: int = 1,
51
57
  ) -> None:
52
58
  """Create StandCLoud loader.
53
59
 
54
60
  Args:
55
- addr (str | None, optional): StandCloud address.
56
- The option only for development and debug. Defaults to True.
61
+ addr (str): StandCloud service name.
62
+ api_mode (StandCloudAPIMode): StandCloud API mode,
63
+ hardpy for test stand, integration for third-party service.
64
+ Default: StandCloudAPIMode.HARDPY.
65
+ api_version (int): StandCloud API version.
66
+ Default: 1.
57
67
  """
58
68
  https_prefix = "https://"
59
69
  auth_addr = addr + "/auth"
60
70
 
61
71
  self._url: StandCloudURL = StandCloudURL(
62
- api=https_prefix + addr + self._get_service_name(addr) + "/api/v1",
72
+ api=https_prefix + addr + f"/{api_mode}/api/v{api_version}",
63
73
  token=https_prefix + auth_addr + "/api/oidc/token",
64
74
  par=https_prefix + auth_addr + "/api/oidc/pushed-authorization-request",
65
75
  auth=https_prefix + auth_addr + "/api/oidc/authorization",
@@ -98,13 +108,11 @@ class StandCloudConnector:
98
108
  try:
99
109
  resp = api.get(verify=self._verify_ssl)
100
110
  except ExpiredAccessToken as exc:
101
- raise StandCloudError(str(exc))
102
- except TokenExpiredError as exc:
103
- raise StandCloudError(exc.description)
104
- except InvalidGrantError as exc:
105
- raise StandCloudError(exc.description)
106
- except HTTPError as exc:
107
- raise StandCloudError(exc.strerror) # type: ignore
111
+ raise StandCloudError(str(exc)) from exc
112
+ except OAuth2Error as exc:
113
+ raise StandCloudError(exc.description) from exc
114
+ except RequestException as exc:
115
+ raise StandCloudError(exc.strerror) from exc # type: ignore
108
116
 
109
117
  return resp
110
118
 
@@ -192,15 +200,10 @@ class StandCloudConnector:
192
200
  verify=False,
193
201
  **extra,
194
202
  )
195
- except InvalidGrantError as exc:
196
- raise StandCloudError(exc.description)
197
- except RequestConnectionError as exc:
198
- raise StandCloudError(exc.strerror) # type: ignore
199
- except MissingTokenError as exc:
200
- raise StandCloudError(exc.description)
201
- except InvalidURL:
202
- msg = "Authentication URL is not available"
203
- raise StandCloudError(msg)
203
+ except OAuth2Error as exc:
204
+ raise StandCloudError(exc.description) from exc
205
+ except RequestException as exc:
206
+ raise StandCloudError(exc.strerror) from exc # type: ignore
204
207
  self._token_update(ret) # type: ignore
205
208
 
206
209
  return ApiClient(self._url.api + "/" + endpoint, session=session, timeout=10)
@@ -216,12 +219,3 @@ class StandCloudConnector:
216
219
  expires_at=expires_at,
217
220
  expires_in=expires_in,
218
221
  )
219
-
220
- def _get_service_name(self, addr: str) -> str:
221
- addr_parts = addr.split(".")
222
- number_of_parts = 3
223
- service_position_in_address = 1
224
- if isinstance(addr_parts, list) and len(addr_parts) >= number_of_parts:
225
- return "/" + addr_parts[service_position_in_address]
226
- msg = f"Invalid StandCloud address: {addr}"
227
- raise StandCloudError(msg)
@@ -18,9 +18,9 @@ app.state.pytest_wrp = PyTestWrapper()
18
18
 
19
19
 
20
20
  class Status(str, Enum):
21
- """Pytest run status.
21
+ """HardPy status.
22
22
 
23
- Statuses, that can be returned by HardPy to frontend.
23
+ Statuses, that can be returned by HardPy API.
24
24
  """
25
25
 
26
26
  STOPPED = "stopped"
@@ -31,6 +31,16 @@ class Status(str, Enum):
31
31
  ERROR = "error"
32
32
 
33
33
 
34
+ @app.get("/api/hardpy_config")
35
+ def hardpy_config() -> dict:
36
+ """Get config of HardPy.
37
+
38
+ Returns:
39
+ dict: HardPy config
40
+ """
41
+ return app.state.pytest_wrp.get_config()
42
+
43
+
34
44
  @app.get("/api/start")
35
45
  def start_pytest() -> dict:
36
46
  """Start pytest subprocess.
@@ -68,6 +78,18 @@ def collect_pytest() -> dict:
68
78
  return {"status": Status.BUSY}
69
79
 
70
80
 
81
+ @app.get("/api/status")
82
+ def status() -> dict:
83
+ """Get pytest subprocess status.
84
+
85
+ Returns:
86
+ dict[str, RunStatus]: run status
87
+ """
88
+ is_running = app.state.pytest_wrp.is_running()
89
+ status = Status.BUSY if is_running else Status.READY
90
+ return {"status": status}
91
+
92
+
71
93
  @app.get("/api/couch")
72
94
  def couch_connection() -> dict:
73
95
  """Get couchdb connection string.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "files": {
3
3
  "main.css": "/static/css/main.e8a862f1.css",
4
- "main.js": "/static/js/main.8a7d8f7d.js",
4
+ "main.js": "/static/js/main.fb8b84a3.js",
5
5
  "blueprint-icons-all-paths-loader.js": "/static/js/blueprint-icons-all-paths-loader.0aa89747.chunk.js",
6
6
  "blueprint-icons-split-paths-by-size-loader.js": "/static/js/blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js",
7
7
  "static/js/808.ce070002.chunk.js": "/static/js/808.ce070002.chunk.js",
@@ -21,7 +21,7 @@
21
21
  "static/media/logo_smol.png": "/static/media/logo_smol.5b16f92447a4a9e80331.png",
22
22
  "index.html": "/index.html",
23
23
  "main.e8a862f1.css.map": "/static/css/main.e8a862f1.css.map",
24
- "main.8a7d8f7d.js.map": "/static/js/main.8a7d8f7d.js.map",
24
+ "main.fb8b84a3.js.map": "/static/js/main.fb8b84a3.js.map",
25
25
  "blueprint-icons-all-paths-loader.0aa89747.chunk.js.map": "/static/js/blueprint-icons-all-paths-loader.0aa89747.chunk.js.map",
26
26
  "blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js.map": "/static/js/blueprint-icons-split-paths-by-size-loader.52a072d3.chunk.js.map",
27
27
  "808.ce070002.chunk.js.map": "/static/js/808.ce070002.chunk.js.map",
@@ -31,6 +31,6 @@
31
31
  },
32
32
  "entrypoints": [
33
33
  "static/css/main.e8a862f1.css",
34
- "static/js/main.8a7d8f7d.js"
34
+ "static/js/main.fb8b84a3.js"
35
35
  ]
36
36
  }
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>HardPy Operator Panel</title><script defer="defer" src="/static/js/main.8a7d8f7d.js"></script><link href="/static/css/main.e8a862f1.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>HardPy Operator Panel</title><script defer="defer" src="/static/js/main.fb8b84a3.js"></script><link href="/static/css/main.e8a862f1.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>