zebra-day 0.0.37__py3-none-any.whl → 1.0.2__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 (181) hide show
  1. zebra_day/__init__.py +35 -0
  2. zebra_day/bin/__init__.py +0 -0
  3. zebra_day/bin/fetch_zebra_config.py +15 -0
  4. zebra_day/bin/generate_coord_grid_zpl.py +50 -0
  5. zebra_day/bin/print_zpl_from_file.py +21 -0
  6. zebra_day/bin/probe_new_label_dimensions.py +75 -0
  7. zebra_day/bin/scan_for_networed_zebra_printers_arp_scan.sh +1 -0
  8. zebra_day/bin/scan_for_networed_zebra_printers_curl.sh +30 -0
  9. zebra_day/bin/zserve.py +701 -259
  10. zebra_day/cli/__init__.py +240 -0
  11. zebra_day/cli/cognito.py +121 -0
  12. zebra_day/cli/gui.py +255 -0
  13. zebra_day/cli/printer.py +168 -0
  14. zebra_day/cli/template.py +176 -0
  15. zebra_day/cmd_mgr.py +35 -0
  16. zebra_day/etc/Monoid-Regular-HalfTight-Dollar-0-1-l.ttf +0 -0
  17. zebra_day/etc/label_styles/blank.zpl +0 -0
  18. zebra_day/etc/label_styles/cornersStripOf4Squares_1inX1in.zpl +55 -0
  19. zebra_day/etc/label_styles/corners_1inX2in.zpl +28 -0
  20. zebra_day/etc/label_styles/corners_20cmX30cm.zpl +6 -0
  21. zebra_day/etc/label_styles/corners_smallTube.zpl +7 -0
  22. zebra_day/etc/label_styles/corners_unspecifiedDimensions.zpl +15 -0
  23. zebra_day/etc/label_styles/generic_2inX1in.zpl +21 -0
  24. zebra_day/etc/label_styles/plate_1inX0.25in.zpl +9 -0
  25. zebra_day/etc/label_styles/plate_1inX0.25inHD.zpl +9 -0
  26. zebra_day/etc/label_styles/smallTubeWdotHD_prod.zpl +8 -0
  27. zebra_day/etc/label_styles/smallTubeWdot_corners.zpl +7 -0
  28. zebra_day/etc/label_styles/smallTubeWdot_prod.zpl +8 -0
  29. zebra_day/etc/label_styles/smallTubeWdot_prodAlt1.zpl +6 -0
  30. zebra_day/etc/label_styles/smallTubeWdot_prodAlt1b.zpl +3 -0
  31. zebra_day/etc/label_styles/smallTubeWdot_prodV2.zpl +8 -0
  32. zebra_day/etc/label_styles/smallTubeWdot_reagent.zpl +29 -0
  33. zebra_day/etc/label_styles/stripOf4Squares_1inX1in.zpl +32 -0
  34. zebra_day/etc/label_styles/test_800dX800dCoordinateArray.zpl +1 -0
  35. zebra_day/etc/label_styles/tmps/.hold +0 -0
  36. zebra_day/etc/label_styles/tmps/tmp_zpl_templates.here +0 -0
  37. zebra_day/etc/label_styles/tube_20mmX30mmA.zpl +7 -0
  38. zebra_day/etc/label_styles/tube_2inX0.3in.zpl +15 -0
  39. zebra_day/etc/label_styles/tube_2inX0.5in.zpl +15 -0
  40. zebra_day/etc/label_styles/tube_2inX0.5inHD.zpl +15 -0
  41. zebra_day/etc/label_styles/tube_2inX1in.zpl +25 -0
  42. zebra_day/etc/label_styles/tube_2inX1inHD.zpl +22 -0
  43. zebra_day/etc/label_styles/tube_2inX1inHDv3.zpl +21 -0
  44. zebra_day/etc/old_printer_config/.hold +0 -0
  45. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.022846_printer_config.json +1 -0
  46. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.033657_printer_config.json +1 -0
  47. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.039597_printer_config.json +3 -0
  48. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.047295_printer_config.json +1 -0
  49. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.055804_printer_config.json +1 -0
  50. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.061337_printer_config.json +3 -0
  51. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.073326_printer_config.json +1 -0
  52. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.081950_printer_config.json +1 -0
  53. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.088251_printer_config.json +3 -0
  54. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.096501_printer_config.json +1 -0
  55. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.104767_printer_config.json +1 -0
  56. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.110364_printer_config.json +3 -0
  57. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.118239_printer_config.json +1 -0
  58. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.125950_printer_config.json +1 -0
  59. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.349866_printer_config.json +1 -0
  60. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.361085_printer_config.json +3 -0
  61. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.558323_printer_config.json +1 -0
  62. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.565756_printer_config.json +3 -0
  63. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.739070_printer_config.json +16 -0
  64. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.753796_printer_config.json +1 -0
  65. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.760201_printer_config.json +3 -0
  66. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.768747_printer_config.json +1 -0
  67. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.775312_printer_config.json +3 -0
  68. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.782533_printer_config.json +1 -0
  69. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.789287_printer_config.json +1 -0
  70. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.794230_printer_config.json +3 -0
  71. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.800021_printer_config.json +5 -0
  72. zebra_day/etc/printer_config.json +16 -0
  73. zebra_day/etc/printer_config.template.json +16 -0
  74. zebra_day/etc/tmp_printers0.json +5 -0
  75. zebra_day/etc/tmp_printers374.json +5 -0
  76. zebra_day/etc/tmp_printers383.json +5 -0
  77. zebra_day/etc/tmp_printers450.json +5 -0
  78. zebra_day/etc/tmp_printers504.json +5 -0
  79. zebra_day/etc/tmp_printers608.json +5 -0
  80. zebra_day/etc/tmp_printers657.json +5 -0
  81. zebra_day/etc/tmp_printers838.json +5 -0
  82. zebra_day/etc/tmp_printers839.json +5 -0
  83. zebra_day/etc/tmp_printers933.json +5 -0
  84. zebra_day/etc/tmp_printers957.json +5 -0
  85. zebra_day/exceptions.py +88 -0
  86. zebra_day/files/.hold +0 -0
  87. zebra_day/files/hold +0 -0
  88. zebra_day/files/test_png_17696.png +0 -0
  89. zebra_day/files/test_png_23477.png +0 -0
  90. zebra_day/files/test_png_28157.png +0 -0
  91. zebra_day/files/test_png_35832.png +0 -0
  92. zebra_day/files/test_png_36400.png +0 -0
  93. zebra_day/files/test_png_40816.png +0 -0
  94. zebra_day/files/test_png_49564.png +0 -0
  95. zebra_day/files/test_png_53848.png +0 -0
  96. zebra_day/files/test_png_62542.png +0 -0
  97. zebra_day/files/test_png_91597.png +0 -0
  98. zebra_day/files/test_png_93633.png +0 -0
  99. zebra_day/files/tmpbjo3k7q1.png +0 -0
  100. zebra_day/files/tmpigtr4pwy.png +0 -0
  101. zebra_day/files/zpl_label_tube_2inX1in_2026-02-01_01:51:24.370964.png +0 -0
  102. zebra_day/logging_config.py +74 -0
  103. zebra_day/logs/.hold +0 -0
  104. zebra_day/logs/print_requests.log +2 -0
  105. zebra_day/paths.py +143 -0
  106. zebra_day/print_mgr.py +487 -103
  107. zebra_day/static/datschund.css +140 -0
  108. zebra_day/static/datschund.png +0 -0
  109. zebra_day/static/daylily.png +0 -0
  110. zebra_day/static/favicon.svg +20 -0
  111. zebra_day/static/general.css +99 -0
  112. zebra_day/static/js/zebra_modern.js +172 -0
  113. zebra_day/static/lsmc.css +354 -0
  114. zebra_day/static/moon.jpeg +0 -0
  115. zebra_day/static/oakland.css +197 -0
  116. zebra_day/static/petrichor.css +150 -0
  117. zebra_day/static/popday_daylily.css +140 -0
  118. zebra_day/static/style.css +183 -0
  119. zebra_day/static/triangles.css +122 -0
  120. zebra_day/static/tron.css +277 -0
  121. zebra_day/static/zebra_modern.css +771 -0
  122. zebra_day/static/zebras.css +176 -0
  123. zebra_day/templates/base.html +36 -0
  124. zebra_day/templates/bpr.html +72 -0
  125. zebra_day/templates/build_new_config.html +36 -0
  126. zebra_day/templates/build_print_request.html +32 -0
  127. zebra_day/templates/chg_ui_style.html +19 -0
  128. zebra_day/templates/edit_template.html +128 -0
  129. zebra_day/templates/edit_zpl.html +37 -0
  130. zebra_day/templates/index.html +82 -0
  131. zebra_day/templates/legacy/base.html +37 -0
  132. zebra_day/templates/legacy/bpr.html +72 -0
  133. zebra_day/templates/legacy/build_new_config.html +36 -0
  134. zebra_day/templates/legacy/build_print_request.html +32 -0
  135. zebra_day/templates/legacy/chg_ui_style.html +19 -0
  136. zebra_day/templates/legacy/edit_template.html +128 -0
  137. zebra_day/templates/legacy/edit_zpl.html +37 -0
  138. zebra_day/templates/legacy/index.html +82 -0
  139. zebra_day/templates/legacy/list_prior_configs.html +24 -0
  140. zebra_day/templates/legacy/print_result.html +30 -0
  141. zebra_day/templates/legacy/printer_details.html +25 -0
  142. zebra_day/templates/legacy/printer_status.html +70 -0
  143. zebra_day/templates/legacy/save_result.html +17 -0
  144. zebra_day/templates/legacy/send_print_request.html +34 -0
  145. zebra_day/templates/legacy/simple_print.html +94 -0
  146. zebra_day/templates/legacy/view_pstation_json.html +29 -0
  147. zebra_day/templates/list_prior_configs.html +24 -0
  148. zebra_day/templates/modern/base.html +98 -0
  149. zebra_day/templates/modern/config.html +141 -0
  150. zebra_day/templates/modern/dashboard.html +160 -0
  151. zebra_day/templates/modern/print_request.html +141 -0
  152. zebra_day/templates/modern/print_result.html +88 -0
  153. zebra_day/templates/modern/printer_detail.html +117 -0
  154. zebra_day/templates/modern/printers.html +133 -0
  155. zebra_day/templates/modern/save_result.html +46 -0
  156. zebra_day/templates/modern/template_editor.html +172 -0
  157. zebra_day/templates/modern/templates.html +122 -0
  158. zebra_day/templates/print_result.html +30 -0
  159. zebra_day/templates/printer_details.html +25 -0
  160. zebra_day/templates/printer_status.html +70 -0
  161. zebra_day/templates/save_result.html +17 -0
  162. zebra_day/templates/send_print_request.html +34 -0
  163. zebra_day/templates/simple_print.html +94 -0
  164. zebra_day/templates/view_pstation_json.html +29 -0
  165. zebra_day/web/__init__.py +9 -0
  166. zebra_day/web/app.py +171 -0
  167. zebra_day/web/auth.py +172 -0
  168. zebra_day/web/middleware.py +159 -0
  169. zebra_day/web/routers/__init__.py +2 -0
  170. zebra_day/web/routers/api.py +163 -0
  171. zebra_day/web/routers/ui.py +1051 -0
  172. zebra_day/zpl_renderer.py +273 -0
  173. zebra_day-1.0.2.dist-info/METADATA +786 -0
  174. zebra_day-1.0.2.dist-info/RECORD +179 -0
  175. {zebra_day-0.0.37.dist-info → zebra_day-1.0.2.dist-info}/WHEEL +1 -1
  176. zebra_day-1.0.2.dist-info/entry_points.txt +4 -0
  177. zebra_day/bin/te.py +0 -905
  178. zebra_day-0.0.37.dist-info/METADATA +0 -1177
  179. zebra_day-0.0.37.dist-info/RECORD +0 -10
  180. {zebra_day-0.0.37.dist-info → zebra_day-1.0.2.dist-info/licenses}/LICENSE +0 -0
  181. {zebra_day-0.0.37.dist-info → zebra_day-1.0.2.dist-info}/top_level.txt +0 -0
zebra_day/print_mgr.py CHANGED
@@ -1,18 +1,55 @@
1
- import os
2
- import sys
3
- import socket
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.
11
+ """
12
+ from __future__ import annotations
13
+
4
14
  import datetime
5
15
  import json
6
- import requests
16
+ import os
17
+ import shutil
18
+ import socket
19
+ import subprocess
20
+ import sys
21
+ import time
22
+ from pathlib import Path
23
+
7
24
  from importlib.resources import files
8
25
 
26
+ from zebra_day.logging_config import get_logger
27
+ from zebra_day import paths as xdg
28
+
29
+ _log = get_logger(__name__)
30
+
31
+
9
32
 
10
33
  def get_current_date():
34
+ """
35
+ get the current datetime
36
+ """
37
+
11
38
  current_date = datetime.date.today()
12
39
  formatted_date = current_date.strftime("%Y-%m-%d")
13
40
  return formatted_date
14
41
 
15
- def send_zpl_code(zpl_code, printer_ip, printer_port=9100):
42
+
43
+ def send_zpl_code(zpl_code, printer_ip, printer_port=9100, is_test=False):
44
+ """
45
+ The bit which passes the zpl to the specified printer.
46
+ Port is more or less hard coded upstream from here fwiw
47
+ """
48
+
49
+ # In the case we are testing only, return None
50
+ if is_test:
51
+ return None
52
+
16
53
  # Create a socket object
17
54
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
18
55
  timeout = 5
@@ -23,83 +60,271 @@ def send_zpl_code(zpl_code, printer_ip, printer_port=9100):
23
60
  sock.connect((printer_ip, printer_port))
24
61
 
25
62
  # Send the ZPL code as raw bytes
26
- sock.sendall(zpl_code.encode())
27
- print("ZPL code sent successfully to the printer!")
28
-
63
+ # ... the zebra printer will not throw an error if the request
64
+ # content is incorrect, or for any reason except to reject request to the wrong port.
65
+ return_code = sock.sendall(zpl_code.encode())
66
+ if return_code in [None]:
67
+ _log.info("ZPL code sent successfully to printer %s:%d", printer_ip, printer_port)
68
+ else:
69
+ raise Exception(f"\n\nPrint request to {printer_ip}:{printer_port} did not return None, but instead: {return_code} ... zpl: {zpl_code}\n")
70
+
29
71
  except ConnectionError as e:
30
- print(f"Error connecting to the printer: {e}")
72
+ raise Exception(f"Error connecting to the printer: {printer_ip} on port {printer_port} \n\n\t"+str(e))
31
73
 
32
74
  finally:
33
75
  # Close the socket connection
34
76
  sock.close()
35
77
 
36
- # Due to some bizarre packaging thing I havent been able to puzzle out, when pip installed from pypy, included files are not found and you need to fetch this objec via:
37
- ## import zebra_day.print_mgr as zdpm
38
- ## z=zdpm.zplo()
78
+ """
79
+ 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
80
+ used when creating a new zpl() class. If absent, a minimal viable json
81
+ object is created in memory, which needs to be populated (via a few methods
82
+ below, or manually if you'd like) before you can do very much.
83
+
84
+
85
+
86
+ """
87
+
39
88
 
40
89
  class zpl:
90
+ """
91
+ The primary class. Instantiate with:
92
+ from zebra_day import print_mgr as zd
93
+ zd_pm = zd.zpl()
94
+ """
95
+
96
+ def __init__(self, json_config: str | None = None):
97
+ """
98
+ Initialize the class.
99
+
100
+ Args:
101
+ json_config: Path to printer config JSON. If not specified,
102
+ uses XDG config path or falls back to package path.
103
+ """
104
+ # Ensure label styles directories exist
105
+ xdg.get_label_drafts_dir() # Creates tmps/ too
106
+
107
+ # Determine config file location (XDG first, then package fallback)
108
+ xdg_config = xdg.get_printer_config_path()
109
+ pkg_config = Path(str(files('zebra_day'))) / "etc" / "printer_config.json"
110
+
111
+ if json_config:
112
+ jcfg = Path(json_config) if not json_config.startswith('/') else Path(json_config)
113
+ elif xdg_config.exists():
114
+ jcfg = xdg_config
115
+ elif pkg_config.exists():
116
+ jcfg = pkg_config
117
+ else:
118
+ jcfg = xdg_config # Will create new config here
119
+
120
+ if jcfg.exists():
121
+ self.load_printer_json(str(jcfg), relative=False)
122
+ else:
123
+ self.create_new_printers_json_with_single_test_printer(str(jcfg))
41
124
 
42
- def __init__(self, debug=0,json_config='zebra_day/etc/printer_config.json'):
43
125
 
44
- self.load_printer_json(data_text = files('zebra_day').joinpath(json_config).read_text())
45
- self.debug = False if debug in [0,'0'] else True
126
+ def probe_zebra_printers_add_to_printers_json(self, ip_stub="192.168.1", scan_wait="0.25",lab="scan-results", relative=False):
127
+ """
128
+ Scan the network for zebra printers
129
+ NOTE! this should work with no dependencies on a MAC
130
+ UBUNTU requires system wide net-tools (for arp)
131
+ Others... well, this may not work
46
132
 
133
+ ---
134
+ Requires:
135
+ curl is pretty standard, arp seems less so
136
+ arp
137
+ ---
138
+
139
+ ip_stub = all 255 possibilities will be probed beneath this
140
+ stub provided
47
141
 
48
- def probe_zebra_printers_add_to_printers_json(self, ip_stub="192.168.1", scan_wait="0.25",lab="scan-results"):
142
+ can_wait = seconds to re-try probing until moving on. 0.25
143
+ default may be too squick
144
+
145
+ lab = code for the lab key to add/update to given finding
146
+ new printers. Existing printers will be over written.
147
+ """
49
148
 
50
149
  if lab not in self.printers['labs']:
51
150
  self.printers['labs'][lab] = {}
52
151
 
53
- self.printers['labs'][lab]["Download-Label-png"] = { "ip_address": "dl_png", "label_zpl_styles": ["test_2inX1in"],"print_method": "generate png", "model" : "na", "serial" : "na"}
54
-
55
- res = os.popen(f"zebra_day/bin/scan_for_networed_zebra_printers_curl.sh {ip_stub} {scan_wait}")
56
- for i in res.readlines():
57
- ii = i.rstrip()
58
- sl = ii.split('|')
152
+ self.printers['labs'][lab]["Download-Label-png"] = {
153
+ "ip_address": "dl_png",
154
+ "label_zpl_styles": ["tube_2inX1in"],
155
+ "print_method": "generate png",
156
+ "model": "na",
157
+ "serial": "na",
158
+ "arp_data": ""
159
+ }
160
+
161
+ # Run scanner script using subprocess instead of os.popen
162
+ script_path = Path(str(files('zebra_day'))) / "bin" / "scan_for_networed_zebra_printers_curl.sh"
163
+ result = subprocess.run(
164
+ [str(script_path), ip_stub, scan_wait],
165
+ capture_output=True,
166
+ text=True,
167
+ check=False
168
+ )
169
+
170
+ for line in result.stdout.splitlines():
171
+ line = line.rstrip()
172
+ sl = line.split('|')
59
173
  if len(sl) > 1:
60
174
  zp = sl[0]
61
175
  ip = sl[1]
62
176
  model = sl[2]
63
177
  serial = sl[3]
64
178
  status = sl[4]
65
- if ip not in self.printers['labs'][lab]:
66
- self.printers['labs'][lab][ip] = {"ip_address" : ip, "label_zpl_styles" : ["blank_0inX0in", "test_2inX1in","tube_2inX1in", "plate_1inX0.25in", "tube_2inX0.3in"], "print_method" : "unk", "model" : model, "serial" : serial} # The label formats set here are the installed defaults
67
-
68
- self.save_printer_json()
179
+ arp_response = sl[5]
69
180
 
181
+ if ip not in self.printers['labs'][lab]:
182
+ # The label formats set here are the installed defaults
183
+ self.printers['labs'][lab][ip] = {
184
+ "ip_address": ip,
185
+ "label_zpl_styles": ["tube_2inX1in", "plate_1inX0.25in", "tube_2inX0.3in"],
186
+ "print_method": "unk",
187
+ "model": model,
188
+ "serial": serial,
189
+ "arp_data": arp_response
190
+ }
191
+
192
+ self.save_printer_json(self.printers_filename, relative=False)
193
+
194
+
195
+ def save_printer_json(self, json_filename: str = "/etc/printer_config.json", relative: bool = True) -> None:
196
+ """
197
+ Save the current self.printers to the specified JSON file.
198
+
199
+ Creates a backup of the previous config in the backups directory.
200
+
201
+ Args:
202
+ json_filename: Path to save the config to
203
+ relative: If True, path is relative to package directory
204
+ """
205
+ # Resolve the target path
206
+ if relative:
207
+ json_path = Path(str(files('zebra_day'))) / json_filename.lstrip('/')
208
+ else:
209
+ json_path = Path(json_filename)
210
+
211
+ # Create backup if file exists
212
+ if hasattr(self, 'printers_filename') and Path(self.printers_filename).exists():
213
+ backup_dir = xdg.get_config_backups_dir()
214
+ rec_date = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
215
+ backup_path = backup_dir / f"{rec_date}_printer_config.json"
216
+ try:
217
+ shutil.copy2(self.printers_filename, backup_path)
218
+ _log.debug("Backup created: %s", backup_path)
219
+ except OSError as e:
220
+ _log.warning("Failed to create backup: %s", e)
221
+
222
+ # Save the config
223
+ json_path.parent.mkdir(parents=True, exist_ok=True)
224
+ with open(json_path, 'w') as json_file:
225
+ json.dump(self.printers, json_file, indent=4)
70
226
 
71
- # USING SELF.PRINTERS
72
- def save_printer_json(self, json_filename="zebra_day/etc/printer_config.json"):
73
- rec_date = str(datetime.datetime.now()).replace(' ','_')
74
- bkup_pconfig_fn = f"zebra_day/etc/old_printer_config/{rec_date}_printer_config.json"
75
-
76
- os.system(f"cp {json_filename} {bkup_pconfig_fn}")
227
+ self.load_printer_json(str(json_path), relative=False)
77
228
 
78
- with open(json_filename, 'w') as json_file:
79
- json.dump(self.printers, json_file, indent=4)
80
- self.load_printer_json(json_filename)
81
229
 
230
+ def load_printer_json(self, json_file=f"etc/printer_config.json", relative=True):
231
+ """
232
+ Loads printer json from a specified file, saves it to the active json.
233
+ If specified file does not exist, it is created with the base
234
+ printers json
235
+
236
+ json_file = path to file
237
+ """
238
+ if relative:
239
+ json_file = f"{str(files('zebra_day'))}/{json_file}"
240
+ else:
241
+ pass
242
+
243
+ _log.debug("Loading printer config from: %s", json_file)
82
244
 
83
- def load_printer_json(self, json_file="zebra_day/etc/printer_config.json"):
84
245
  if not os.path.exists(json_file):
85
- os.system("""echo '{ "labs" : {} }' >>""" + json_file)
246
+ 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}""")
86
247
  fh = open(json_file)
87
248
  self.printers_filename = json_file
88
249
  self.printers = json.load(fh)
250
+ # self.save_printer_json() <--- use the save_printer_json call after calling this. Else, recursion.
251
+
89
252
 
253
+ def create_new_printers_json_with_single_test_printer(self, fn=None):
254
+ """
255
+ Create a new printers json with just the png printer defined
256
+ """
90
257
 
91
- def clear_printers_json(self, json_file="zebra_day/etc/printer_config.json"):
92
- os.system(f"""echo '{{"labs" : {{}} }}' > {json_file}""")
93
- fh = open(json_file)
94
- self.printers_filename = json_file
95
- self.printers = json.load(fh)
96
258
 
259
+ if fn in [None]:
260
+ fn = str(files('zebra_day'))+"/etc/printer_config.json"
261
+
262
+ if not hasattr(self, 'printers'):
263
+ self.printers = {}
264
+ self.printers_filename = fn
265
+
266
+ jdat = None
267
+ with open(f"{str(files('zebra_day'))}/etc/printer_config.template.json", 'r') as file:
268
+ jdat = json.load(file)
269
+
270
+ self.printers = jdat
271
+
272
+ self.save_printer_json(fn, relative=False)
273
+
274
+
275
+ def clear_printers_json(self, json_file: str = "/etc/printer_config.json") -> None:
276
+ """
277
+ Reset printers JSON to empty minimal structure.
278
+
279
+ Args:
280
+ json_file: Path to the config file (relative to package)
281
+ """
282
+ json_path = Path(str(files('zebra_day'))) / json_file.lstrip('/')
283
+
284
+ # Write empty config using pathlib
285
+ empty_config = {"labs": {}}
286
+ json_path.parent.mkdir(parents=True, exist_ok=True)
287
+ with open(json_path, 'w') as f:
288
+ json.dump(empty_config, f, indent=4)
289
+
290
+ self.printers_filename = str(json_path)
291
+ self.printers = empty_config
292
+
293
+ self.save_printer_json(str(json_path), relative=False)
294
+
295
+ def replace_printer_json_from_template(self) -> None:
296
+ """
297
+ Replace the active printer config with the default template.
298
+
299
+ Copies the template JSON to the active config location.
300
+ """
301
+ pkg_path = Path(str(files('zebra_day')))
302
+ template_path = pkg_path / "etc" / "printer_config.template.json"
303
+ target_path = pkg_path / "etc" / "printer_config.json"
304
+
305
+ # Copy template to active config using shutil
306
+ shutil.copy2(template_path, target_path)
307
+
308
+ with open(target_path) as fh:
309
+ self.printers = json.load(fh)
310
+ self.printers_filename = str(target_path)
311
+
312
+ self.save_printer_json(self.printers_filename, relative=False)
97
313
 
98
- def replace_printer_json_from_template(self):
99
- os.system('cp zebra_day/etc/printer_config.template.json zebra_day/etc/printer_config.json')
100
314
 
101
315
 
102
316
  def get_valid_label_styles_for_lab(self,lab=None):
317
+ """
318
+ The intention for this method was to confirm a template
319
+ being requested for use in printing to some printer
320
+ was 'allowed' by checking with that printers printer json
321
+ for the array of valid templates.
322
+
323
+ This was a huge PITA in testing, could be re-enabled at some point
324
+
325
+ It is used once, but prints a warning only.
326
+ """
327
+
103
328
  unique_labels = set()
104
329
 
105
330
  for printer in self.printers['labs'][lab]:
@@ -110,11 +335,25 @@ class zpl:
110
335
  return result
111
336
 
112
337
 
338
+ # Given these inputs, format them in to the specified zpl template and
339
+ # prepare a string to send to a printer
113
340
  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):
114
-
115
- zpl_file = f"zebra_day/etc/label_styles/{label_zpl_style}.zpl"
341
+ """
342
+ Produce a ZPL string using the specified zpl template file, and
343
+ formatting in the values, where appropriate.
344
+
345
+ label_zpl_style = filename, minus the .zpl which keys to the .zpl file.
346
+ (note, NOT the full file name. This shoudlbe changed
347
+ to full file paths at some point)
348
+
349
+ uid_barcode and alt_a -to- alt_f, are the allowed format keys in
350
+ the zpl templates. They may be used in any way. uid_barcode
351
+ just differntiates one.
352
+ """
353
+
354
+ zpl_file = str(files('zebra_day'))+f"/etc/label_styles/{label_zpl_style}.zpl"
116
355
  if not os.path.exists(zpl_file):
117
- zpl_file = f"zebra_day/etc/label_styles/tmps/{label_zpl_style}.zpl"
356
+ zpl_file = str(files('zebra_day'))+f"/etc/label_styles/tmps/{label_zpl_style}.zpl"
118
357
  if not os.path.exists(zpl_file):
119
358
  raise Exception(f"ZPL File : {zpl_file} does not exist in the TOPLEVEL or TMPS zebra_day/etc/label_styles dir.")
120
359
 
@@ -125,48 +364,102 @@ class zpl:
125
364
  return zpl_string
126
365
 
127
366
 
128
- def generate_label_png(self,zpl_string=None, png_fn=None):
367
+
368
+ def generate_label_png(self, zpl_string=None, png_fn=None, relative=False):
369
+ """
370
+ Generate a PNG image from ZPL string using local renderer.
371
+
372
+ This uses a local ZPL renderer (Pillow + zint-bindings) instead of
373
+ the external Labelary API, enabling offline operation and avoiding
374
+ rate limits.
375
+
376
+ Args:
377
+ zpl_string: The ZPL code to render
378
+ png_fn: Output filename for the PNG
379
+ relative: If True, treat png_fn as relative to package directory
380
+
381
+ Returns:
382
+ Path to the generated PNG file
383
+ """
384
+ from zebra_day.zpl_renderer import render_zpl_to_png
385
+
386
+ if relative:
387
+ png_fn = str(files('zebra_day')) + '/' + png_fn
388
+
389
+ if zpl_string is None or png_fn is None:
390
+ raise ValueError('ERROR: zpl_string and png_fn may not be None.')
391
+
392
+ try:
393
+ result = render_zpl_to_png(zpl_string, png_fn)
394
+ _log.info("Label image saved as %s", result)
395
+ return result
396
+ except Exception as e:
397
+ _log.error("Failed to convert ZPL to image: %s", e)
398
+ raise
399
+
400
+
401
+ def print_raw_zpl(self,zpl_content,printer_ip, port=9100):
402
+ """
403
+ 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.
404
+ """
405
+ send_zpl_code(zpl_content, printer_ip, printer_port=port)
406
+
407
+
408
+
409
+
410
+ 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):
411
+ """
412
+ The main print method. Accepts info to determine the desired
413
+ printer IP and to request the desired ZPL string to be sent
414
+ to the printer.
415
+
416
+ lab = top level key in self.printers['labs']
417
+ printer_name = key for printer info (ie: ip_address) needed
418
+ to satisfy print requests.
419
+ label_zpl_style = template code, see above for addl deets
420
+ client_ip = optional, this is logged with print request info
421
+ print_n = integer, > 0
422
+ zpl_content = DO NOT USE -- hacky way to directly pass a zpl
423
+ string to a printer. to do: write a cleaner
424
+ string+ip method of printing.
425
+ """
426
+
427
+ if print_n < 1:
428
+ raise Exception(f"\n\nprint_n < 1 , specified {print_n}")
129
429
 
130
- if zpl_string in [None] or png_fn in [None]:
131
- raise Exception('ERROR: zpl_string and png_fn may not be None.')
132
-
133
- # Labelary API URL
134
- labelary_url = "http://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/"
135
-
136
- # Create a POST request to the Labelary API
137
- response = requests.post(labelary_url, data=zpl_string)
138
-
139
- # Check if the request was successful
140
- if response.status_code == 200:
141
- # Save the image to a file
142
- with open(png_fn, "wb") as f:
143
- f.write(response.content)
144
- print(f"Image saved as {png_fn}")
145
- else:
146
- print(f"Failed to convert ZPL to image. Status code: {response.status_code}")
147
-
148
- return png_fn
149
-
150
-
151
- 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):
152
430
  rec_date = str(datetime.datetime.now()).replace(' ','_')
153
431
  print_n = int(print_n)
154
432
 
433
+ if printer_name in ['','None',None] and lab in [None,'','None']:
434
+ 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}")
435
+
155
436
  if label_zpl_style in [None,'','None']:
156
437
  label_zpl_style = self.printers['labs'][lab][printer_name]['label_zpl_styles'][0] # If a style is not specified, assume the first
157
438
  elif label_zpl_style not in self.printers['labs'][lab][printer_name]['label_zpl_styles']:
158
- print(f"\n\nWARNING:::\nZPL style: {label_zpl_style} is not valid for {lab} {printer_name} ... {self.printers['labs'][lab][printer_name]['label_zpl_styles']}")
439
+ _log.warning(
440
+ "ZPL style '%s' is not valid for %s/%s. Valid styles: %s",
441
+ label_zpl_style, lab, printer_name,
442
+ self.printers['labs'][lab][printer_name]['label_zpl_styles']
443
+ )
159
444
 
160
445
  printer_ip = self.printers['labs'][lab][printer_name]["ip_address"]
161
446
 
162
- 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)
447
+ zpl_string = ''
448
+ if zpl_content in [None]:
449
+ 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)
450
+ else:
451
+ zpl_string = zpl_content
163
452
 
164
- os.system(f"echo '{lab}\t{printer_name}\t{uid_barcode}\t{label_zpl_style}\t{printer_ip}\t{print_n}\t{client_ip}' >> zebra_day/logs/print_requests.log")
453
+ # Log print request to file (using pathlib, not shell)
454
+ log_file = xdg.get_logs_dir() / "print_requests.log"
455
+ 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"
456
+ with open(log_file, 'a') as f:
457
+ f.write(log_entry)
165
458
 
166
459
  ret_s = None
167
460
  if printer_ip in ['dl_png']:
168
- png_fn = f"zebra_day/files/zpl_label_{label_zpl_style}_{rec_date}.png"
169
- ret_s = self.generate_label_png(zpl_string, png_fn)
461
+ png_fn = str(xdg.get_generated_files_dir() / f"zpl_label_{label_zpl_style}_{rec_date}.png")
462
+ ret_s = self.generate_label_png(zpl_string, png_fn, False)
170
463
 
171
464
  else:
172
465
  pn = 1
@@ -176,49 +469,140 @@ class zpl:
176
469
 
177
470
  ret_s = zpl_string
178
471
 
179
- if self.debug:
180
- print(f"\nZPL STRING :: {zpl_string}\n")
181
-
182
472
  return ret_s
183
473
 
184
474
 
185
- # Due to some bizarre packaging thing I havent been able to puzzle out, when pip installed from pypy, included files are not found and you need to fetch this objec via:
186
- ## import zebra_day.print_mgr as zdpm
187
- ## z=zdpm.zplo()
188
- def zplo():
475
+ def _get_local_ip() -> str:
476
+ """Get the local IP address of this machine."""
477
+ 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"""
478
+ result = subprocess.run(ipcmd, shell=True, capture_output=True, text=True)
479
+ return result.stdout.strip().split('\n')[0] if result.stdout.strip() else "127.0.0.1"
480
+
481
+
482
+ def _parse_auth_args() -> str:
483
+ """Parse --auth CLI argument.
484
+
485
+ Returns:
486
+ Auth mode: "none" or "cognito"
487
+ """
488
+ import argparse
489
+
490
+ parser = argparse.ArgumentParser(add_help=False)
491
+ parser.add_argument(
492
+ "--auth",
493
+ type=str,
494
+ choices=["none", "cognito"],
495
+ default="none",
496
+ help="Authentication mode: 'none' (public, default) or 'cognito' (AWS Cognito)",
497
+ )
498
+ args, _ = parser.parse_known_args()
499
+ return args.auth
500
+
501
+
502
+ def zday_start() -> None:
503
+ """
504
+ Start the zebra_day web UI on 0.0.0.0:8118.
505
+
506
+ .. deprecated::
507
+ Use ``zday gui start`` instead. This command will be removed in v1.0.
508
+
509
+ This offers package utilities in a UI, mostly intended for
510
+ template design, testing, and printer fleet maintenance.
511
+
512
+ Usage:
513
+ zday_start # Start with no authentication
514
+ zday_start --auth none # Explicit no authentication
515
+ zday_start --auth cognito # Enable Cognito authentication
516
+ """
517
+ import warnings
518
+ warnings.warn(
519
+ "zday_start is deprecated. Use 'zday gui start' instead.",
520
+ DeprecationWarning,
521
+ stacklevel=2,
522
+ )
523
+ _log.warning(
524
+ "DEPRECATED: zday_start is deprecated. Use 'zday gui start' instead."
525
+ )
526
+
527
+ from zebra_day.web.app import run_server
528
+
529
+ auth_mode = _parse_auth_args()
530
+ _log.info("Starting zebra_day FastAPI server on 0.0.0.0:8118 (auth=%s)...", auth_mode)
531
+ run_server(host="0.0.0.0", port=8118, reload=False, auth=auth_mode)
532
+
533
+
534
+ def main() -> None:
535
+ """
536
+ Quick start: scan for printers and start the web GUI.
537
+
538
+ .. deprecated::
539
+ Use ``zday bootstrap`` followed by ``zday gui start`` instead.
540
+ This command will be removed in v1.0.
541
+
542
+ If zebra_day has been pip installed, running zday_quickstart
543
+ will first attempt a zebra printer discovery scan of your network,
544
+ create a new printers JSON for what is found, and start
545
+ the zebra_day UI on 0.0.0.0:8118.
546
+
547
+ Usage:
548
+ zday_quickstart # Start with no authentication
549
+ zday_quickstart --auth none # Explicit no authentication
550
+ zday_quickstart --auth cognito # Enable Cognito authentication
551
+ """
552
+ import warnings
553
+ warnings.warn(
554
+ "zday_quickstart is deprecated. Use 'zday bootstrap' then 'zday gui start' instead.",
555
+ DeprecationWarning,
556
+ stacklevel=2,
557
+ )
558
+ _log.warning(
559
+ "DEPRECATED: zday_quickstart is deprecated. Use 'zday bootstrap' then 'zday gui start' instead."
560
+ )
561
+
189
562
  import zebra_day.print_mgr as zdpm
190
- os.chdir(os.path.dirname(zdpm.__file__))
191
- os.chdir('..')
192
- zp = zdpm.zpl()
193
- return zp
563
+ from zebra_day.web.app import run_server
194
564
 
565
+ auth_mode = _parse_auth_args()
195
566
 
196
- def main():
197
- ipcmd = "(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:\
198
- )?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1') 2>/dev/null"
199
- print(ipcmd)
200
- ip = os.popen(ipcmd).readline().rstrip()
567
+ ip = _get_local_ip()
201
568
  ip_root = ".".join(ip.split('.')[:-1])
202
569
 
203
- print(f"\nIP detected: {ip} ... using IP root: {ip_root}\n\n ..... now scanning for zebra printers on this network (which may take a few minutes...)")
204
- os.system('sleep 2.2')
205
- import zebra_day.print_mgr as zdpm
206
- os.chdir(os.path.dirname(zdpm.__file__))
207
- os.chdir('..')
570
+ _log.info("IP detected: %s ... using IP root: %s", ip, ip_root)
571
+ _log.info("Scanning for zebra printers on this network (may take a few minutes)...")
572
+ time.sleep(2.2)
573
+
208
574
  zp = zdpm.zpl()
209
575
  zp.probe_zebra_printers_add_to_printers_json(ip_stub=ip_root)
210
576
 
211
- print(f"\nZebra Printer Scan Complete. Results:" + str(zp.printers) + "\n\n")
212
- print(f'\nNow starting zebra_day web GUI\n\n\n\t\t\t**** THE ZDAY WEB GUI WILL BE ACCESSIBLE VIA THE URL: {ip}:8118 \n\n\n\tThe zday web server will continue running, and not return this shell to a command prompt until it is shut down\n\t.... you may shut down this web service by hitting ctrl+c.\n\n')
213
- os.system('sleep 1.3')
214
-
215
- os.system('python zebra_day/bin/zserve.py')
577
+ _log.info("Zebra Printer Scan Complete. Results: %s", zp.printers)
578
+ _log.info(
579
+ "Starting zebra_day web GUI at %s:8118 (auth=%s). "
580
+ "Press Ctrl+C to shut down.",
581
+ ip,
582
+ auth_mode,
583
+ )
584
+ time.sleep(1.3)
216
585
 
217
- print('\n\n\n ** EXITING ZDAY QUICKSTART **\n\n\t\tif the zday web gui did not run ( if you immediately got the command prompt back, it did not run ), check and see if there is a service already running at {ip}:8118 . Otherwise, check out the zday cherrypy STDOUT emitted just above what you are reading now. Cut&Paste that into chatgpt and see if a solution is presented!')
586
+ run_server(host="0.0.0.0", port=8118, reload=False, auth=auth_mode)
218
587
 
219
- print('fin')
588
+ _log.info("EXITING ZDAY QUICKSTART")
589
+ _log.info(
590
+ "If the web GUI did not run, check if a service is already running at %s:8118",
591
+ ip
592
+ )
220
593
 
221
594
 
222
595
  if __name__ == "__main__":
596
+ """
597
+ entry point for zday_quickstart.
598
+ """
223
599
 
224
600
  main()
601
+
602
+
603
+ if __name__ == "__zday_start__":
604
+ """
605
+ entry point for zday_start
606
+ """
607
+
608
+ zday_start()