zebra-day 2.0.0__py3-none-any.whl → 2.1.4__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 (101) hide show
  1. zebra_day/__init__.py +7 -2
  2. zebra_day/_version.py +1 -0
  3. zebra_day/cli/__init__.py +80 -30
  4. zebra_day/cli/cognito.py +15 -9
  5. zebra_day/cli/gui.py +21 -16
  6. zebra_day/cli/printer.py +34 -27
  7. zebra_day/cli/template.py +19 -15
  8. zebra_day/cmd_mgr.py +3 -6
  9. zebra_day/docs/gx420d-gx430d-ug-en.pdf +0 -0
  10. zebra_day/docs/hardware_config_guide.md +149 -0
  11. zebra_day/docs/programatic_guide.md +181 -0
  12. zebra_day/docs/qln420_zebra_manual.pdf +0 -0
  13. zebra_day/docs/uid_screed_light.md +38 -0
  14. zebra_day/docs/zd620-zd420-ug-en.pdf +0 -0
  15. zebra_day/docs/zebra_day_ui_guide.md +194 -0
  16. zebra_day/etc/printer_config.json +7 -1
  17. zebra_day/etc/printer_config.template.json +3 -17
  18. zebra_day/etc/tmp_printers139.json +10 -0
  19. zebra_day/etc/tmp_printers147.json +10 -0
  20. zebra_day/etc/tmp_printers34.json +10 -0
  21. zebra_day/etc/tmp_printers389.json +10 -0
  22. zebra_day/etc/tmp_printers398.json +10 -0
  23. zebra_day/etc/tmp_printers437.json +10 -0
  24. zebra_day/etc/tmp_printers439.json +10 -0
  25. zebra_day/etc/tmp_printers440.json +10 -0
  26. zebra_day/etc/tmp_printers508.json +10 -0
  27. zebra_day/etc/tmp_printers543.json +10 -0
  28. zebra_day/etc/tmp_printers835.json +10 -0
  29. zebra_day/etc/tmp_printers842.json +10 -0
  30. zebra_day/etc/tmp_printers931.json +10 -0
  31. zebra_day/etc/tmp_printers969.json +10 -0
  32. zebra_day/exceptions.py +1 -1
  33. zebra_day/files/corners_smallTube_preview.png +0 -0
  34. zebra_day/files/test_png_2897.png +0 -0
  35. zebra_day/files/test_png_31690.png +0 -0
  36. zebra_day/files/test_png_33804.png +0 -0
  37. zebra_day/files/test_png_34737.png +0 -0
  38. zebra_day/files/test_png_4161.png +0 -0
  39. zebra_day/files/test_png_44748.png +0 -0
  40. zebra_day/files/test_png_4635.png +0 -0
  41. zebra_day/files/test_png_56349.png +0 -0
  42. zebra_day/files/test_png_5936.png +0 -0
  43. zebra_day/files/test_png_64110.png +0 -0
  44. zebra_day/files/test_png_64891.png +0 -0
  45. zebra_day/files/test_png_69002.png +0 -0
  46. zebra_day/files/test_png_70065.png +0 -0
  47. zebra_day/files/test_png_72366.png +0 -0
  48. zebra_day/files/test_png_77793.png +0 -0
  49. zebra_day/files/test_png_9572.png +0 -0
  50. zebra_day/imgs/.hold +0 -0
  51. zebra_day/imgs/bar_ltpurp.png +0 -0
  52. zebra_day/imgs/bar_purp.png +0 -0
  53. zebra_day/imgs/bar_purp3.png +0 -0
  54. zebra_day/imgs/bar_red.png +0 -0
  55. zebra_day/imgs/legacy/UBC_gantt_chart.png +0 -0
  56. zebra_day/imgs/legacy/gx420d_network_config.png +0 -0
  57. zebra_day/imgs/legacy/gx420d_printer_config.png +0 -0
  58. zebra_day/imgs/legacy/ngrok.png +0 -0
  59. zebra_day/imgs/legacy/printer_details.png +0 -0
  60. zebra_day/imgs/legacy/quick_start_test_label.png +0 -0
  61. zebra_day/imgs/legacy/quick_start_test_label2.png +0 -0
  62. zebra_day/imgs/legacy/zd620_network_config.png +0 -0
  63. zebra_day/imgs/legacy/zd620_printer_config.png +0 -0
  64. zebra_day/imgs/legacy/zday_quick_gui.png +0 -0
  65. zebra_day/imgs/legacy/zebra_day_alt_css_dog.png +0 -0
  66. zebra_day/imgs/legacy/zebra_day_alt_css_flower.png +0 -0
  67. zebra_day/imgs/legacy/zebra_day_alt_css_main.png +0 -0
  68. zebra_day/imgs/legacy/zebra_day_available_zpl_templates.png +0 -0
  69. zebra_day/imgs/legacy/zebra_day_bkup_pconfig.png +0 -0
  70. zebra_day/imgs/legacy/zebra_day_home.png +0 -0
  71. zebra_day/imgs/legacy/zebra_day_manual_print.png +0 -0
  72. zebra_day/imgs/legacy/zebra_day_printer_fleet_json.png +0 -0
  73. zebra_day/imgs/legacy/zebra_day_quick_ex.png +0 -0
  74. zebra_day/imgs/legacy/zebra_day_zpl_template_IRLa.png +0 -0
  75. zebra_day/imgs/legacy/zebra_day_zpl_template_IRLb.png +0 -0
  76. zebra_day/imgs/ui_api_docs.png +0 -0
  77. zebra_day/imgs/ui_config.png +0 -0
  78. zebra_day/imgs/ui_dashboard.png +0 -0
  79. zebra_day/imgs/ui_print_request.png +0 -0
  80. zebra_day/imgs/ui_printers.png +0 -0
  81. zebra_day/imgs/ui_templates.png +0 -0
  82. zebra_day/logging_config.py +4 -9
  83. zebra_day/mkcert.py +157 -0
  84. zebra_day/paths.py +1 -2
  85. zebra_day/print_mgr.py +165 -145
  86. zebra_day/templates/modern/config.html +7 -0
  87. zebra_day/templates/modern/print_request.html +61 -3
  88. zebra_day/web/__init__.py +1 -1
  89. zebra_day/web/app.py +21 -16
  90. zebra_day/web/auth.py +17 -15
  91. zebra_day/web/middleware.py +8 -5
  92. zebra_day/web/routers/__init__.py +0 -1
  93. zebra_day/web/routers/api.py +192 -43
  94. zebra_day/web/routers/ui.py +31 -33
  95. zebra_day/zpl_renderer.py +45 -34
  96. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/METADATA +76 -67
  97. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/RECORD +101 -29
  98. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/WHEEL +0 -0
  99. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/entry_points.txt +0 -0
  100. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/licenses/LICENSE +0 -0
  101. {zebra_day-2.0.0.dist-info → zebra_day-2.1.4.dist-info}/top_level.txt +0 -0
@@ -96,9 +96,14 @@
96
96
  <a href="/" class="btn btn-outline">
97
97
  <i class="fas fa-arrow-left"></i> Cancel
98
98
  </a>
99
- <button type="submit" class="btn btn-primary btn-lg" onclick="showLoading('Sending print request...')">
100
- <i class="fas fa-print"></i> Print Label
101
- </button>
99
+ <div class="d-flex" style="gap: var(--spacing-md);">
100
+ <button type="button" class="btn btn-secondary btn-lg" onclick="downloadPNG()">
101
+ <i class="fas fa-download"></i> Download PNG
102
+ </button>
103
+ <button type="submit" class="btn btn-primary btn-lg" onclick="showLoading('Sending print request...')">
104
+ <i class="fas fa-print"></i> Print Label
105
+ </button>
106
+ </div>
102
107
  </div>
103
108
  </div>
104
109
  </form>
@@ -140,6 +145,59 @@
140
145
  updatePrinters();
141
146
  }
142
147
  });
148
+
149
+ async function downloadPNG() {
150
+ var form = document.getElementById('print-form');
151
+ var template = form.querySelector('select[name="label_zpl_style"]').value;
152
+
153
+ if (!template) {
154
+ showToast('error', 'Error', 'Please select a label template');
155
+ return;
156
+ }
157
+
158
+ showLoading('Rendering PNG...');
159
+
160
+ try {
161
+ var response = await fetch('/api/v1/render/png', {
162
+ method: 'POST',
163
+ headers: {'Content-Type': 'application/json'},
164
+ body: JSON.stringify({
165
+ template: template,
166
+ uid_barcode: form.querySelector('input[name="uid_barcode"]').value || '',
167
+ alt_a: form.querySelector('input[name="alt_a"]').value || '',
168
+ alt_b: form.querySelector('input[name="alt_b"]').value || '',
169
+ alt_c: form.querySelector('input[name="alt_c"]').value || '',
170
+ alt_d: form.querySelector('input[name="alt_d"]').value || '',
171
+ alt_e: form.querySelector('input[name="alt_e"]').value || '',
172
+ alt_f: form.querySelector('input[name="alt_f"]').value || ''
173
+ })
174
+ });
175
+
176
+ hideLoading();
177
+
178
+ if (!response.ok) {
179
+ var error = await response.json();
180
+ showToast('error', 'Render Failed', error.detail || 'Unknown error');
181
+ return;
182
+ }
183
+
184
+ // Download the PNG file
185
+ var blob = await response.blob();
186
+ var url = window.URL.createObjectURL(blob);
187
+ var a = document.createElement('a');
188
+ a.href = url;
189
+ a.download = template + '_label.png';
190
+ document.body.appendChild(a);
191
+ a.click();
192
+ window.URL.revokeObjectURL(url);
193
+ a.remove();
194
+
195
+ showToast('success', 'Success', 'PNG downloaded');
196
+ } catch (e) {
197
+ hideLoading();
198
+ showToast('error', 'Error', 'Failed to render PNG: ' + e.message);
199
+ }
200
+ }
143
201
  </script>
144
202
  {% endblock %}
145
203
 
zebra_day/web/__init__.py CHANGED
@@ -3,7 +3,7 @@ zebra_day web application module.
3
3
 
4
4
  FastAPI-based web interface for managing Zebra printers and label templates.
5
5
  """
6
+
6
7
  from zebra_day.web.app import create_app
7
8
 
8
9
  __all__ = ["create_app"]
9
-
zebra_day/web/app.py CHANGED
@@ -3,20 +3,21 @@ FastAPI application factory for zebra_day.
3
3
 
4
4
  This module provides the main FastAPI application for the zebra_day web interface.
5
5
  """
6
+
6
7
  from __future__ import annotations
7
8
 
8
9
  import os
9
10
  import subprocess
11
+ from importlib.resources import files
10
12
  from pathlib import Path
11
- from typing import Literal, Optional
13
+ from typing import Literal
12
14
 
13
- from fastapi import FastAPI, Request
15
+ from fastapi import FastAPI
14
16
  from fastapi.staticfiles import StaticFiles
15
17
  from fastapi.templating import Jinja2Templates
16
- from importlib.resources import files
17
18
 
18
- from zebra_day.logging_config import get_logger
19
19
  from zebra_day import paths as xdg
20
+ from zebra_day.logging_config import get_logger
20
21
  from zebra_day.web.middleware import RequestLoggingMiddleware, print_rate_limiter
21
22
 
22
23
  _log = get_logger(__name__)
@@ -39,7 +40,7 @@ def create_app(
39
40
  *,
40
41
  debug: bool = False,
41
42
  css_theme: str = "lsmc.css",
42
- auth: Optional[Literal["none", "cognito"]] = None,
43
+ auth: Literal["none", "cognito"] | None = None,
43
44
  ) -> FastAPI:
44
45
  """
45
46
  Create and configure the FastAPI application.
@@ -76,7 +77,7 @@ def create_app(
76
77
  from zebra_day.web.auth import CognitoAuthMiddleware, setup_cognito_auth
77
78
 
78
79
  cognito_auth = setup_cognito_auth(app)
79
- app.add_middleware(CognitoAuthMiddleware, cognito_auth=cognito_auth)
80
+ app.add_middleware(CognitoAuthMiddleware, cognito_auth=cognito_auth) # type: ignore[arg-type]
80
81
  app.state.cognito_auth = cognito_auth
81
82
  app.state.auth_mode = "cognito"
82
83
  _log.info("Cognito authentication middleware enabled")
@@ -96,9 +97,14 @@ def create_app(
96
97
  app.mount("/static", StaticFiles(directory=str(_STATIC_PATH)), name="static")
97
98
 
98
99
  # Also mount package directories that need to be served
99
- files_dir = _PKG_PATH / "files"
100
- if files_dir.exists():
101
- app.mount("/files", StaticFiles(directory=str(files_dir)), name="files")
100
+ # Package files directory (for templates, previews generated in-package)
101
+ pkg_files_dir = _PKG_PATH / "files"
102
+ pkg_files_dir.mkdir(parents=True, exist_ok=True)
103
+ app.mount("/files", StaticFiles(directory=str(pkg_files_dir)), name="files")
104
+
105
+ # XDG generated files directory (for PNG downloads from dl_png printer)
106
+ xdg_generated_dir = xdg.get_generated_files_dir()
107
+ app.mount("/generated", StaticFiles(directory=str(xdg_generated_dir)), name="generated")
102
108
 
103
109
  etc_dir = _PKG_PATH / "etc"
104
110
  if etc_dir.exists():
@@ -109,7 +115,7 @@ def create_app(
109
115
  app.state.templates = templates
110
116
 
111
117
  # Register routers
112
- from zebra_day.web.routers import ui, api
118
+ from zebra_day.web.routers import api, ui
113
119
 
114
120
  app.include_router(ui.router)
115
121
  app.include_router(api.router, prefix="/api/v1", tags=["api"])
@@ -141,7 +147,7 @@ def create_app(
141
147
  return app
142
148
 
143
149
 
144
- def get_default_cert_paths() -> tuple[Optional[Path], Optional[Path]]:
150
+ def get_default_cert_paths() -> tuple[Path | None, Path | None]:
145
151
  """
146
152
  Get default certificate paths from XDG config directory.
147
153
 
@@ -163,8 +169,8 @@ def run_server(
163
169
  port: int = 8118,
164
170
  reload: bool = False,
165
171
  auth: Literal["none", "cognito"] = "none",
166
- ssl_certfile: Optional[str] = None,
167
- ssl_keyfile: Optional[str] = None,
172
+ ssl_certfile: str | None = None,
173
+ ssl_keyfile: str | None = None,
168
174
  ):
169
175
  """
170
176
  Run the FastAPI server using uvicorn.
@@ -228,7 +234,7 @@ def run_server(
228
234
  )
229
235
 
230
236
  # Build uvicorn config
231
- uvicorn_kwargs = {
237
+ uvicorn_kwargs: dict[str, str | int | bool | None] = {
232
238
  "host": host,
233
239
  "port": port,
234
240
  "reload": reload,
@@ -244,5 +250,4 @@ def run_server(
244
250
 
245
251
  _log.info("Starting server at %s://%s:%d", protocol, host, port)
246
252
 
247
- uvicorn.run("zebra_day.web.app:create_app", **uvicorn_kwargs)
248
-
253
+ uvicorn.run("zebra_day.web.app:create_app", **uvicorn_kwargs) # type: ignore[arg-type]
zebra_day/web/auth.py CHANGED
@@ -5,10 +5,10 @@ Provides optional Cognito authentication support via the daylily-cognito library
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
- import os
9
- from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional
8
+ from collections.abc import Callable
9
+ from typing import TYPE_CHECKING, Any
10
10
 
11
- from fastapi import Depends, HTTPException, Request, status
11
+ from fastapi import HTTPException, Request, status
12
12
  from starlette.middleware.base import BaseHTTPMiddleware
13
13
  from starlette.responses import Response
14
14
 
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
20
20
  _log = get_logger(__name__)
21
21
 
22
22
  # Endpoints that should never require authentication
23
- PUBLIC_PATHS: List[str] = [
23
+ PUBLIC_PATHS: list[str] = [
24
24
  "/healthz",
25
25
  "/readyz",
26
26
  "/docs",
@@ -30,7 +30,7 @@ PUBLIC_PATHS: List[str] = [
30
30
 
31
31
  # Try to import daylily-cognito components
32
32
  _COGNITO_AVAILABLE = False
33
- _COGNITO_IMPORT_ERROR: Optional[str] = None
33
+ _COGNITO_IMPORT_ERROR: str | None = None
34
34
 
35
35
  try:
36
36
  from daylily_cognito import CognitoAuth, CognitoConfig, create_auth_dependency
@@ -38,9 +38,9 @@ try:
38
38
  _COGNITO_AVAILABLE = True
39
39
  except ImportError as e:
40
40
  _COGNITO_IMPORT_ERROR = str(e)
41
- CognitoAuth = None # type: ignore[misc, assignment]
42
- CognitoConfig = None # type: ignore[misc, assignment]
43
- create_auth_dependency = None # type: ignore[misc, assignment]
41
+ CognitoAuth = None
42
+ CognitoConfig = None
43
+ create_auth_dependency = None
44
44
 
45
45
 
46
46
  def is_cognito_available() -> bool:
@@ -48,7 +48,7 @@ def is_cognito_available() -> bool:
48
48
  return _COGNITO_AVAILABLE
49
49
 
50
50
 
51
- def get_cognito_import_error() -> Optional[str]:
51
+ def get_cognito_import_error() -> str | None:
52
52
  """Get the import error message if daylily-cognito is not available."""
53
53
  return _COGNITO_IMPORT_ERROR
54
54
 
@@ -59,7 +59,7 @@ class CognitoAuthMiddleware(BaseHTTPMiddleware):
59
59
  Exempts health check endpoints and other public paths.
60
60
  """
61
61
 
62
- def __init__(self, app: "FastAPI", cognito_auth: Any) -> None:
62
+ def __init__(self, app: FastAPI, cognito_auth: Any) -> None:
63
63
  super().__init__(app)
64
64
  self.cognito_auth = cognito_auth
65
65
  self.get_current_user = create_auth_dependency(cognito_auth, optional=False)
@@ -70,11 +70,13 @@ class CognitoAuthMiddleware(BaseHTTPMiddleware):
70
70
 
71
71
  # Allow public endpoints without authentication
72
72
  if any(path.startswith(public) for public in PUBLIC_PATHS):
73
- return await call_next(request)
73
+ response = await call_next(request)
74
+ return response # type: ignore[no-any-return]
74
75
 
75
76
  # Allow static files without authentication
76
77
  if path.startswith("/static") or path.startswith("/files") or path.startswith("/etc"):
77
- return await call_next(request)
78
+ response = await call_next(request)
79
+ return response # type: ignore[no-any-return]
78
80
 
79
81
  # Check for Authorization header
80
82
  auth_header = request.headers.get("Authorization")
@@ -116,10 +118,11 @@ class CognitoAuthMiddleware(BaseHTTPMiddleware):
116
118
  headers={"WWW-Authenticate": "Bearer"},
117
119
  )
118
120
 
119
- return await call_next(request)
121
+ response = await call_next(request)
122
+ return response # type: ignore[no-any-return]
120
123
 
121
124
 
122
- def setup_cognito_auth(app: "FastAPI") -> Any:
125
+ def setup_cognito_auth(app: FastAPI) -> Any:
123
126
  """Configure Cognito authentication for the FastAPI app.
124
127
 
125
128
  Reads configuration from environment variables:
@@ -169,4 +172,3 @@ def setup_cognito_auth(app: "FastAPI") -> Any:
169
172
  )
170
173
 
171
174
  return cognito_auth
172
-
@@ -3,12 +3,13 @@ Middleware for the zebra_day FastAPI application.
3
3
 
4
4
  Provides request logging and rate limiting functionality.
5
5
  """
6
+
6
7
  from __future__ import annotations
7
8
 
8
9
  import asyncio
9
10
  import time
10
11
  from collections import defaultdict
11
- from typing import Callable
12
+ from collections.abc import Callable
12
13
 
13
14
  from fastapi import Request, Response
14
15
  from starlette.middleware.base import BaseHTTPMiddleware
@@ -33,7 +34,7 @@ class RequestLoggingMiddleware(BaseHTTPMiddleware):
33
34
  client_ip = request.client.host if request.client else "unknown"
34
35
  method = request.method
35
36
  path = request.url.path
36
- query = str(request.query_params) if request.query_params else ""
37
+ str(request.query_params) if request.query_params else ""
37
38
 
38
39
  # Extract relevant parameters for print operations
39
40
  lab = request.query_params.get("lab", "")
@@ -86,7 +87,7 @@ class RequestLoggingMiddleware(BaseHTTPMiddleware):
86
87
  else:
87
88
  _log.info("Request completed", extra=log_context)
88
89
 
89
- return response
90
+ return response # type: ignore[no-any-return]
90
91
 
91
92
 
92
93
  class PrintRateLimiter:
@@ -136,7 +137,10 @@ class PrintRateLimiter:
136
137
 
137
138
  # Check rate limit
138
139
  if len(self._request_times[client_ip]) >= self.max_requests:
139
- return False, f"Rate limit exceeded: {self.max_requests} requests per {self.window_seconds}s"
140
+ return (
141
+ False,
142
+ f"Rate limit exceeded: {self.max_requests} requests per {self.window_seconds}s",
143
+ )
140
144
 
141
145
  # Try to acquire semaphore (non-blocking check)
142
146
  if self._semaphore.locked() and self._semaphore._value == 0:
@@ -156,4 +160,3 @@ class PrintRateLimiter:
156
160
 
157
161
  # Global rate limiter instance
158
162
  print_rate_limiter = PrintRateLimiter()
159
-
@@ -1,2 +1 @@
1
1
  """FastAPI routers for zebra_day web application."""
2
-