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
zebra_day/print_mgr.py CHANGED
@@ -1,14 +1,15 @@
1
1
  """
2
- Primary zebra_day module. Primary functions: consistent and clear management
3
- of 1+ networked zebra printers, automated discovery of printers on a
4
- network. Clear formulation and delivery of ZPL strings to destination
5
- printers. Management of zpl template files, which may have format value
6
- components for inserting data on the fly. (elsewhere, a simple ui on
7
- top of this).
8
-
9
- This module is primarily focused on print request and package config mgmt.
10
- See 'cmd_mgr' for interacting with zebras printer config capabilities.
2
+ Primary zebra_day module. Primary functions: consistent and clear management
3
+ of 1+ networked zebra printers, automated discovery of printers on a
4
+ network. Clear formulation and delivery of ZPL strings to destination
5
+ printers. Management of zpl template files, which may have format value
6
+ components for inserting data on the fly. (elsewhere, a simple ui on
7
+ top of this).
8
+
9
+ This module is primarily focused on print request and package config mgmt.
10
+ See 'cmd_mgr' for interacting with zebras printer config capabilities.
11
11
  """
12
+
12
13
  from __future__ import annotations
13
14
 
14
15
  import datetime
@@ -17,20 +18,18 @@ import os
17
18
  import shutil
18
19
  import socket
19
20
  import subprocess
20
- import sys
21
21
  import time
22
- from pathlib import Path
23
-
24
22
  from importlib.resources import files
23
+ from pathlib import Path
24
+ from typing import Literal
25
25
 
26
- from zebra_day.logging_config import get_logger
27
- from zebra_day import paths as xdg
28
26
  import zebra_day.cmd_mgr as zdcm
27
+ from zebra_day import paths as xdg
28
+ from zebra_day.logging_config import get_logger
29
29
 
30
30
  _log = get_logger(__name__)
31
31
 
32
32
 
33
-
34
33
  def get_current_date():
35
34
  """
36
35
  get the current datetime
@@ -46,11 +45,11 @@ def send_zpl_code(zpl_code, printer_ip, printer_port=9100, is_test=False):
46
45
  The bit which passes the zpl to the specified printer.
47
46
  Port is more or less hard coded upstream from here fwiw
48
47
  """
49
-
48
+
50
49
  # In the case we are testing only, return None
51
50
  if is_test:
52
51
  return None
53
-
52
+
54
53
  # Create a socket object
55
54
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
56
55
  timeout = 5
@@ -64,18 +63,23 @@ def send_zpl_code(zpl_code, printer_ip, printer_port=9100, is_test=False):
64
63
  # ... the zebra printer will not throw an error if the request
65
64
  # content is incorrect, or for any reason except to reject request to the wrong port.
66
65
  return_code = sock.sendall(zpl_code.encode())
67
- if return_code in [None]:
66
+ if return_code in [None]:
68
67
  _log.info("ZPL code sent successfully to printer %s:%d", printer_ip, printer_port)
69
68
  else:
70
- raise Exception(f"\n\nPrint request to {printer_ip}:{printer_port} did not return None, but instead: {return_code} ... zpl: {zpl_code}\n")
71
-
69
+ raise Exception(
70
+ f"\n\nPrint request to {printer_ip}:{printer_port} did not return None, but instead: {return_code} ... zpl: {zpl_code}\n"
71
+ )
72
+
72
73
  except ConnectionError as e:
73
- raise Exception(f"Error connecting to the printer: {printer_ip} on port {printer_port} \n\n\t"+str(e))
74
+ raise Exception(
75
+ f"Error connecting to the printer: {printer_ip} on port {printer_port} \n\n\t" + str(e)
76
+ ) from e
74
77
 
75
78
  finally:
76
79
  # Close the socket connection
77
80
  sock.close()
78
81
 
82
+
79
83
  """
80
84
  The zpl.printers object is critical part of zebra_day. There is an in memory js on which can be stored to an active use json file. This active use file is
81
85
  used when creating a new zpl() class. If absent, a minimal viable json
@@ -93,7 +97,7 @@ class zpl:
93
97
  from zebra_day import print_mgr as zd
94
98
  zd_pm = zd.zpl()
95
99
  """
96
-
100
+
97
101
  def __init__(self, json_config: str | None = None):
98
102
  """
99
103
  Initialize the class.
@@ -107,10 +111,10 @@ class zpl:
107
111
 
108
112
  # Determine config file location (XDG first, then package fallback)
109
113
  xdg_config = xdg.get_printer_config_path()
110
- pkg_config = Path(str(files('zebra_day'))) / "etc" / "printer_config.json"
114
+ pkg_config = Path(str(files("zebra_day"))) / "etc" / "printer_config.json"
111
115
 
112
116
  if json_config:
113
- jcfg = Path(json_config) if not json_config.startswith('/') else Path(json_config)
117
+ jcfg = Path(json_config) if not json_config.startswith("/") else Path(json_config)
114
118
  elif xdg_config.exists():
115
119
  jcfg = xdg_config
116
120
  elif pkg_config.exists():
@@ -123,8 +127,9 @@ class zpl:
123
127
  else:
124
128
  self.create_new_printers_json_with_single_test_printer(str(jcfg))
125
129
 
126
-
127
- def probe_zebra_printers_add_to_printers_json(self, ip_stub="192.168.1", scan_wait="0.25", lab="scan-results", relative=False):
130
+ def probe_zebra_printers_add_to_printers_json(
131
+ self, ip_stub="192.168.1", scan_wait="0.25", lab="default", relative=False
132
+ ):
128
133
  """
129
134
  Scan the network for zebra printers.
130
135
 
@@ -147,33 +152,18 @@ class zpl:
147
152
  self.printers["schema_version"] = "2.0.0"
148
153
 
149
154
  # Initialize lab with v2 structure if not exists
150
- if lab not in self.printers['labs']:
151
- self.printers['labs'][lab] = {
152
- "lab_name": lab,
155
+ if lab not in self.printers["labs"]:
156
+ self.printers["labs"][lab] = {
157
+ "lab_name": lab.replace("-", " ").title(),
153
158
  "available_locations": [],
154
- "printers": {}
159
+ "printers": {},
155
160
  }
156
161
 
157
162
  # Ensure lab has printers sub-object (migration from v1)
158
- if "printers" not in self.printers['labs'][lab]:
159
- self.printers['labs'][lab]["printers"] = {}
160
- self.printers['labs'][lab].setdefault("lab_name", lab)
161
- self.printers['labs'][lab].setdefault("available_locations", [])
162
-
163
- # Add the virtual PNG printer
164
- self.printers['labs'][lab]["printers"]["Download-Label-png"] = {
165
- "ip_address": "dl_png",
166
- "printer_name": "Download Label as PNG",
167
- "lab_location": None,
168
- "manufacturer": "virtual",
169
- "model": "na",
170
- "serial": "na",
171
- "label_zpl_styles": ["tube_2inX1in"],
172
- "default_label_style": "tube_2inX1in",
173
- "print_method": "generate png",
174
- "arp_data": "",
175
- "notes": ""
176
- }
163
+ if "printers" not in self.printers["labs"][lab]:
164
+ self.printers["labs"][lab]["printers"] = {}
165
+ self.printers["labs"][lab].setdefault("lab_name", lab.replace("-", " ").title())
166
+ self.printers["labs"][lab].setdefault("available_locations", [])
177
167
 
178
168
  # Scan network for Zebra printers using pure Python
179
169
  wait_time = float(scan_wait) if scan_wait else 0.25
@@ -183,6 +173,7 @@ class zpl:
183
173
  try:
184
174
  # Try to connect to ZPL port (9100)
185
175
  import socket
176
+
186
177
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
187
178
  sock.settimeout(wait_time)
188
179
  result = sock.connect_ex((ip, 9100))
@@ -200,46 +191,51 @@ class zpl:
200
191
 
201
192
  # Parse model from config
202
193
  if "MODEL" in config:
203
- for line in config.split('\n'):
194
+ for line in config.split("\n"):
204
195
  if "MODEL" in line.upper():
205
- parts = line.split(':')
196
+ parts = line.split(":")
206
197
  if len(parts) > 1:
207
198
  model = parts[1].strip()
208
199
  break
209
200
 
210
201
  # Parse serial from config
211
202
  if "SERIAL" in config.upper():
212
- for line in config.split('\n'):
203
+ for line in config.split("\n"):
213
204
  if "SERIAL" in line.upper():
214
- parts = line.split(':')
205
+ parts = line.split(":")
215
206
  if len(parts) > 1:
216
207
  serial = parts[1].strip()
217
208
  break
218
209
  except Exception:
219
210
  pass # Use defaults if we can't query printer
220
211
 
221
- if ip not in self.printers['labs'][lab]["printers"]:
212
+ if ip not in self.printers["labs"][lab]["printers"]:
222
213
  # The label formats set here are the installed defaults
223
- self.printers['labs'][lab]["printers"][ip] = {
214
+ self.printers["labs"][lab]["printers"][ip] = {
224
215
  "ip_address": ip,
225
216
  "printer_name": None, # User can set friendly name later
226
217
  "lab_location": None, # User can set location later
227
218
  "manufacturer": "zebra",
228
219
  "model": model,
229
220
  "serial": serial,
230
- "label_zpl_styles": ["tube_2inX1in", "plate_1inX0.25in", "tube_2inX0.3in"],
221
+ "label_zpl_styles": [
222
+ "tube_2inX1in",
223
+ "plate_1inX0.25in",
224
+ "tube_2inX0.3in",
225
+ ],
231
226
  "default_label_style": "tube_2inX1in", # Default to first style
232
227
  "print_method": "socket",
233
228
  "arp_data": "",
234
- "notes": ""
229
+ "notes": "",
235
230
  }
236
231
  except Exception:
237
232
  pass # Skip unreachable IPs
238
233
 
239
234
  self.save_printer_json(self.printers_filename, relative=False)
240
235
 
241
-
242
- def save_printer_json(self, json_filename: str = "/etc/printer_config.json", relative: bool = True) -> None:
236
+ def save_printer_json(
237
+ self, json_filename: str = "/etc/printer_config.json", relative: bool = True
238
+ ) -> None:
243
239
  """
244
240
  Save the current self.printers to the specified JSON file.
245
241
 
@@ -251,12 +247,12 @@ class zpl:
251
247
  """
252
248
  # Resolve the target path
253
249
  if relative:
254
- json_path = Path(str(files('zebra_day'))) / json_filename.lstrip('/')
250
+ json_path = Path(str(files("zebra_day"))) / json_filename.lstrip("/")
255
251
  else:
256
252
  json_path = Path(json_filename)
257
253
 
258
254
  # Create backup if file exists
259
- if hasattr(self, 'printers_filename') and Path(self.printers_filename).exists():
255
+ if hasattr(self, "printers_filename") and Path(self.printers_filename).exists():
260
256
  backup_dir = xdg.get_config_backups_dir()
261
257
  rec_date = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
262
258
  backup_path = backup_dir / f"{rec_date}_printer_config.json"
@@ -268,56 +264,54 @@ class zpl:
268
264
 
269
265
  # Save the config
270
266
  json_path.parent.mkdir(parents=True, exist_ok=True)
271
- with open(json_path, 'w') as json_file:
267
+ with open(json_path, "w") as json_file:
272
268
  json.dump(self.printers, json_file, indent=4)
273
269
 
274
270
  self.load_printer_json(str(json_path), relative=False)
275
271
 
276
-
277
- def load_printer_json(self, json_file=f"etc/printer_config.json", relative=True):
272
+ def load_printer_json(self, json_file="etc/printer_config.json", relative=True):
278
273
  """
279
274
  Loads printer json from a specified file, saves it to the active json.
280
275
  If specified file does not exist, it is created with the base
281
276
  printers json
282
-
277
+
283
278
  json_file = path to file
284
279
  """
285
280
  if relative:
286
281
  json_file = f"{str(files('zebra_day'))}/{json_file}"
287
282
  else:
288
283
  pass
289
-
284
+
290
285
  _log.debug("Loading printer config from: %s", json_file)
291
286
 
292
287
  if not os.path.exists(json_file):
293
- raise Exception(f"""The file specified does not exist. Consider specifying the default 'etc/printer_config.json , provided: {json_file}, which had {str(files('zebra_day'))} prefixed to it', for {json_file}""")
288
+ raise Exception(
289
+ f"""The file specified does not exist. Consider specifying the default 'etc/printer_config.json , provided: {json_file}, which had {str(files("zebra_day"))} prefixed to it', for {json_file}"""
290
+ )
294
291
  fh = open(json_file)
295
292
  self.printers_filename = json_file
296
293
  self.printers = json.load(fh)
297
294
  # self.save_printer_json() <--- use the save_printer_json call after calling this. Else, recursion.
298
-
299
295
 
300
296
  def create_new_printers_json_with_single_test_printer(self, fn=None):
301
297
  """
302
298
  Create a new printers json with just the png printer defined
303
299
  """
304
300
 
305
-
306
301
  if fn in [None]:
307
- fn = str(files('zebra_day'))+"/etc/printer_config.json"
308
-
309
- if not hasattr(self, 'printers'):
302
+ fn = str(files("zebra_day")) + "/etc/printer_config.json"
303
+
304
+ if not hasattr(self, "printers"):
310
305
  self.printers = {}
311
306
  self.printers_filename = fn
312
307
 
313
308
  jdat = None
314
- with open(f"{str(files('zebra_day'))}/etc/printer_config.template.json", 'r') as file:
309
+ with open(f"{str(files('zebra_day'))}/etc/printer_config.template.json") as file:
315
310
  jdat = json.load(file)
316
-
311
+
317
312
  self.printers = jdat
318
-
319
- self.save_printer_json(fn, relative=False)
320
313
 
314
+ self.save_printer_json(fn, relative=False)
321
315
 
322
316
  def clear_printers_json(self, json_file: str = "/etc/printer_config.json") -> None:
323
317
  """
@@ -326,15 +320,12 @@ class zpl:
326
320
  Args:
327
321
  json_file: Path to the config file (relative to package)
328
322
  """
329
- json_path = Path(str(files('zebra_day'))) / json_file.lstrip('/')
323
+ json_path = Path(str(files("zebra_day"))) / json_file.lstrip("/")
330
324
 
331
325
  # Write empty config with v2 schema
332
- empty_config = {
333
- "schema_version": "2.0.0",
334
- "labs": {}
335
- }
326
+ empty_config = {"schema_version": "2.0.0", "labs": {}}
336
327
  json_path.parent.mkdir(parents=True, exist_ok=True)
337
- with open(json_path, 'w') as f:
328
+ with open(json_path, "w") as f:
338
329
  json.dump(empty_config, f, indent=4)
339
330
 
340
331
  self.printers_filename = str(json_path)
@@ -348,7 +339,7 @@ class zpl:
348
339
 
349
340
  Copies the template JSON to the active config location.
350
341
  """
351
- pkg_path = Path(str(files('zebra_day')))
342
+ pkg_path = Path(str(files("zebra_day")))
352
343
  template_path = pkg_path / "etc" / "printer_config.template.json"
353
344
  target_path = pkg_path / "etc" / "printer_config.json"
354
345
 
@@ -361,8 +352,6 @@ class zpl:
361
352
 
362
353
  self.save_printer_json(self.printers_filename, relative=False)
363
354
 
364
-
365
-
366
355
  def get_valid_label_styles_for_lab(self, lab=None):
367
356
  """
368
357
  Get all unique label styles available for printers in a lab.
@@ -378,18 +367,27 @@ class zpl:
378
367
  unique_labels = set()
379
368
 
380
369
  # Access printers via nested 'printers' key (v2 schema)
381
- lab_printers = self.printers['labs'][lab].get('printers', {})
382
- for printer_id, printer_data in lab_printers.items():
383
- for style in printer_data.get('label_zpl_styles', []):
370
+ lab_printers = self.printers["labs"][lab].get("printers", {})
371
+ for _printer_id, printer_data in lab_printers.items():
372
+ for style in printer_data.get("label_zpl_styles", []):
384
373
  unique_labels.add(style)
385
374
 
386
375
  result = list(unique_labels)
387
376
  return result
388
377
 
389
-
390
378
  # Given these inputs, format them in to the specified zpl template and
391
379
  # prepare a string to send to a printer
392
- def formulate_zpl(self,uid_barcode=None, alt_a=None, alt_b=None, alt_c=None, alt_d=None, alt_e=None, alt_f=None, label_zpl_style=None):
380
+ def formulate_zpl(
381
+ self,
382
+ uid_barcode=None,
383
+ alt_a=None,
384
+ alt_b=None,
385
+ alt_c=None,
386
+ alt_d=None,
387
+ alt_e=None,
388
+ alt_f=None,
389
+ label_zpl_style=None,
390
+ ):
393
391
  """
394
392
  Produce a ZPL string using the specified zpl template file, and
395
393
  formatting in the values, where appropriate.
@@ -402,21 +400,30 @@ class zpl:
402
400
  the zpl templates. They may be used in any way. uid_barcode
403
401
  just differntiates one.
404
402
  """
405
-
406
- zpl_file = str(files('zebra_day'))+f"/etc/label_styles/{label_zpl_style}.zpl"
403
+
404
+ zpl_file = str(files("zebra_day")) + f"/etc/label_styles/{label_zpl_style}.zpl"
407
405
  if not os.path.exists(zpl_file):
408
- zpl_file = str(files('zebra_day'))+f"/etc/label_styles/tmps/{label_zpl_style}.zpl"
406
+ zpl_file = str(files("zebra_day")) + f"/etc/label_styles/tmps/{label_zpl_style}.zpl"
409
407
  if not os.path.exists(zpl_file):
410
- raise Exception(f"ZPL File : {zpl_file} does not exist in the TOPLEVEL or TMPS zebra_day/etc/label_styles dir.")
408
+ raise Exception(
409
+ f"ZPL File : {zpl_file} does not exist in the TOPLEVEL or TMPS zebra_day/etc/label_styles dir."
410
+ )
411
411
 
412
- with open(zpl_file, 'r') as file:
412
+ with open(zpl_file) as file:
413
413
  content = file.read()
414
- zpl_string = content.format(uid_barcode=uid_barcode, alt_a=alt_a, alt_b=alt_b, alt_c=alt_c, alt_d=alt_d, alt_e=alt_e, alt_f=alt_f, label_zpl_style=label_zpl_style)
414
+ zpl_string = content.format(
415
+ uid_barcode=uid_barcode,
416
+ alt_a=alt_a,
417
+ alt_b=alt_b,
418
+ alt_c=alt_c,
419
+ alt_d=alt_d,
420
+ alt_e=alt_e,
421
+ alt_f=alt_f,
422
+ label_zpl_style=label_zpl_style,
423
+ )
415
424
 
416
425
  return zpl_string
417
426
 
418
-
419
-
420
427
  def generate_label_png(self, zpl_string=None, png_fn=None, relative=False):
421
428
  """
422
429
  Generate a PNG image from ZPL string using local renderer.
@@ -436,10 +443,10 @@ class zpl:
436
443
  from zebra_day.zpl_renderer import render_zpl_to_png
437
444
 
438
445
  if relative:
439
- png_fn = str(files('zebra_day')) + '/' + png_fn
446
+ png_fn = str(files("zebra_day")) + "/" + png_fn
440
447
 
441
448
  if zpl_string is None or png_fn is None:
442
- raise ValueError('ERROR: zpl_string and png_fn may not be None.')
449
+ raise ValueError("ERROR: zpl_string and png_fn may not be None.")
443
450
 
444
451
  try:
445
452
  result = render_zpl_to_png(zpl_string, png_fn)
@@ -448,18 +455,29 @@ class zpl:
448
455
  except Exception as e:
449
456
  _log.error("Failed to convert ZPL to image: %s", e)
450
457
  raise
451
-
452
458
 
453
- def print_raw_zpl(self,zpl_content,printer_ip, port=9100):
459
+ def print_raw_zpl(self, zpl_content, printer_ip, port=9100):
454
460
  """
455
461
  For use when no use of the printer mapping config json is needed. This assumes you know which IP is your desired printer. The spcified zpl_content will be sent to that IP+port.
456
462
  """
457
463
  send_zpl_code(zpl_content, printer_ip, printer_port=port)
458
464
 
459
-
460
-
461
-
462
- def print_zpl(self, lab=None, printer_name=None, uid_barcode='', alt_a='', alt_b='', alt_c='', alt_d='', alt_e='', alt_f='', label_zpl_style=None, client_ip='pkg', print_n=1, zpl_content=None):
465
+ def print_zpl(
466
+ self,
467
+ lab=None,
468
+ printer_name=None,
469
+ uid_barcode="",
470
+ alt_a="",
471
+ alt_b="",
472
+ alt_c="",
473
+ alt_d="",
474
+ alt_e="",
475
+ alt_f="",
476
+ label_zpl_style=None,
477
+ client_ip="pkg",
478
+ print_n=1,
479
+ zpl_content=None,
480
+ ):
463
481
  """
464
482
  The main print method. Accepts info to determine the desired
465
483
  printer IP and to request the desired ZPL string to be sent
@@ -479,63 +497,68 @@ class zpl:
479
497
  if print_n < 1:
480
498
  raise Exception(f"\n\nprint_n < 1 , specified {print_n}")
481
499
 
482
- rec_date = str(datetime.datetime.now()).replace(' ', '_')
483
500
  print_n = int(print_n)
484
501
 
485
- if printer_name in ['', 'None', None] and lab in [None, '', 'None']:
486
- raise Exception(f"lab and printer_name are both required to route a zebra print request, the following was what was received: lab:{lab} & printer_name:{printer_name}")
502
+ if printer_name in ["", "None", None] and lab in [None, "", "None"]:
503
+ raise Exception(
504
+ f"lab and printer_name are both required to route a zebra print request, the following was what was received: lab:{lab} & printer_name:{printer_name}"
505
+ )
487
506
 
488
507
  # Access printer via nested 'printers' key (v2 schema)
489
- printer_data = self.printers['labs'][lab]['printers'][printer_name]
508
+ printer_data = self.printers["labs"][lab]["printers"][printer_name]
490
509
 
491
- if label_zpl_style in [None, '', 'None']:
510
+ if label_zpl_style in [None, "", "None"]:
492
511
  # Use default_label_style if set, otherwise fall back to first in list
493
- label_zpl_style = printer_data.get('default_label_style') or printer_data['label_zpl_styles'][0]
494
- elif label_zpl_style not in printer_data['label_zpl_styles']:
512
+ label_zpl_style = (
513
+ printer_data.get("default_label_style") or printer_data["label_zpl_styles"][0]
514
+ )
515
+ elif label_zpl_style not in printer_data["label_zpl_styles"]:
495
516
  _log.warning(
496
517
  "ZPL style '%s' is not valid for %s/%s. Valid styles: %s",
497
- label_zpl_style, lab, printer_name,
498
- printer_data['label_zpl_styles']
518
+ label_zpl_style,
519
+ lab,
520
+ printer_name,
521
+ printer_data["label_zpl_styles"],
499
522
  )
500
523
 
501
524
  printer_ip = printer_data["ip_address"]
502
525
 
503
- zpl_string = ''
526
+ zpl_string = ""
504
527
  if zpl_content in [None]:
505
- zpl_string = self.formulate_zpl(uid_barcode=uid_barcode, alt_a=alt_a, alt_b=alt_b, alt_c=alt_c, alt_d=alt_d, alt_e=alt_e, alt_f=alt_f, label_zpl_style=label_zpl_style)
528
+ zpl_string = self.formulate_zpl(
529
+ uid_barcode=uid_barcode,
530
+ alt_a=alt_a,
531
+ alt_b=alt_b,
532
+ alt_c=alt_c,
533
+ alt_d=alt_d,
534
+ alt_e=alt_e,
535
+ alt_f=alt_f,
536
+ label_zpl_style=label_zpl_style,
537
+ )
506
538
  else:
507
539
  zpl_string = zpl_content
508
540
 
509
541
  # Log print request to file (using pathlib, not shell)
510
542
  log_file = xdg.get_logs_dir() / "print_requests.log"
511
543
  log_entry = f"{lab}\t{printer_name}\t{uid_barcode}\t{label_zpl_style}\t{printer_ip}\t{print_n}\t{client_ip}\t{zpl_content}\n"
512
- with open(log_file, 'a') as f:
544
+ with open(log_file, "a") as f:
513
545
  f.write(log_entry)
514
546
 
515
- ret_s = None
516
- if printer_ip in ['dl_png']:
517
- png_fn = str(xdg.get_generated_files_dir() / f"zpl_label_{label_zpl_style}_{rec_date}.png")
518
- ret_s = self.generate_label_png(zpl_string, png_fn, False)
547
+ # Send to printer
548
+ for _ in range(print_n):
549
+ send_zpl_code(zpl_string, printer_ip)
519
550
 
520
- else:
521
- pn = 1
522
- while pn <= print_n:
523
- send_zpl_code(zpl_string, printer_ip)
524
- pn += 1
525
-
526
- ret_s = zpl_string
527
-
528
- return ret_s
551
+ return zpl_string
529
552
 
530
553
 
531
554
  def _get_local_ip() -> str:
532
555
  """Get the local IP address of this machine."""
533
556
  ipcmd = r"""(ip addr show | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' || ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1') 2>/dev/null"""
534
557
  result = subprocess.run(ipcmd, shell=True, capture_output=True, text=True)
535
- return result.stdout.strip().split('\n')[0] if result.stdout.strip() else "127.0.0.1"
558
+ return result.stdout.strip().split("\n")[0] if result.stdout.strip() else "127.0.0.1"
536
559
 
537
560
 
538
- def _parse_auth_args() -> str:
561
+ def _parse_auth_args() -> Literal["none", "cognito"]:
539
562
  """Parse --auth CLI argument.
540
563
 
541
564
  Returns:
@@ -552,7 +575,8 @@ def _parse_auth_args() -> str:
552
575
  help="Authentication mode: 'none' (public, default) or 'cognito' (AWS Cognito)",
553
576
  )
554
577
  args, _ = parser.parse_known_args()
555
- return args.auth
578
+ auth_mode: Literal["none", "cognito"] = args.auth
579
+ return auth_mode
556
580
 
557
581
 
558
582
  def zday_start() -> None:
@@ -571,14 +595,13 @@ def zday_start() -> None:
571
595
  zday_start --auth cognito # Enable Cognito authentication
572
596
  """
573
597
  import warnings
598
+
574
599
  warnings.warn(
575
600
  "zday_start is deprecated. Use 'zday gui start' instead.",
576
601
  DeprecationWarning,
577
602
  stacklevel=2,
578
603
  )
579
- _log.warning(
580
- "DEPRECATED: zday_start is deprecated. Use 'zday gui start' instead."
581
- )
604
+ _log.warning("DEPRECATED: zday_start is deprecated. Use 'zday gui start' instead.")
582
605
 
583
606
  from zebra_day.web.app import run_server
584
607
 
@@ -606,6 +629,7 @@ def main() -> None:
606
629
  zday_quickstart --auth cognito # Enable Cognito authentication
607
630
  """
608
631
  import warnings
632
+
609
633
  warnings.warn(
610
634
  "zday_quickstart is deprecated. Use 'zday bootstrap' then 'zday gui start' instead.",
611
635
  DeprecationWarning,
@@ -621,7 +645,7 @@ def main() -> None:
621
645
  auth_mode = _parse_auth_args()
622
646
 
623
647
  ip = _get_local_ip()
624
- ip_root = ".".join(ip.split('.')[:-1])
648
+ ip_root = ".".join(ip.split(".")[:-1])
625
649
 
626
650
  _log.info("IP detected: %s ... using IP root: %s", ip, ip_root)
627
651
  _log.info("Scanning for zebra printers on this network (may take a few minutes)...")
@@ -632,8 +656,7 @@ def main() -> None:
632
656
 
633
657
  _log.info("Zebra Printer Scan Complete. Results: %s", zp.printers)
634
658
  _log.info(
635
- "Starting zebra_day web GUI at %s:8118 (auth=%s). "
636
- "Press Ctrl+C to shut down.",
659
+ "Starting zebra_day web GUI at %s:8118 (auth=%s). Press Ctrl+C to shut down.",
637
660
  ip,
638
661
  auth_mode,
639
662
  )
@@ -642,10 +665,7 @@ def main() -> None:
642
665
  run_server(host="0.0.0.0", port=8118, reload=False, auth=auth_mode)
643
666
 
644
667
  _log.info("EXITING ZDAY QUICKSTART")
645
- _log.info(
646
- "If the web GUI did not run, check if a service is already running at %s:8118",
647
- ip
648
- )
668
+ _log.info("If the web GUI did not run, check if a service is already running at %s:8118", ip)
649
669
 
650
670
 
651
671
  if __name__ == "__main__":
@@ -660,5 +680,5 @@ if __name__ == "__zday_start__":
660
680
  """
661
681
  entry point for zday_start
662
682
  """
663
-
683
+
664
684
  zday_start()
@@ -81,6 +81,13 @@
81
81
  <i class="fas fa-external-link-alt"></i> View Full
82
82
  </a>
83
83
  </div>
84
+
85
+ <!-- Config File Path -->
86
+ <div class="mb-md" style="padding: 0.75rem 1rem; background: var(--color-gray-700); border-radius: var(--radius-sm); font-family: 'JetBrains Mono', monospace; font-size: 0.875rem;">
87
+ <span class="text-muted"><i class="fas fa-file-alt"></i> Loaded from:</span>
88
+ <code style="color: var(--color-highlight); margin-left: 0.5rem;">{{ config_file_path | default('Unknown') }}</code>
89
+ </div>
90
+
84
91
  {% if config_summary %}
85
92
  <div class="grid grid-4 mb-lg">
86
93
  <div class="stat-card">