zebra-day 0.0.32__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.
- zebra_day/__init__.py +35 -0
- zebra_day/bin/fetch_zebra_config.py +15 -0
- zebra_day/bin/generate_coord_grid_zpl.py +50 -0
- zebra_day/bin/print_zpl_from_file.py +21 -0
- zebra_day/bin/probe_new_label_dimensions.py +75 -0
- zebra_day/bin/scan_for_networed_zebra_printers_curl.sh +2 -1
- zebra_day/bin/zserve.py +701 -259
- zebra_day/cli/__init__.py +240 -0
- zebra_day/cli/cognito.py +121 -0
- zebra_day/cli/gui.py +255 -0
- zebra_day/cli/printer.py +168 -0
- zebra_day/cli/template.py +176 -0
- zebra_day/cmd_mgr.py +35 -0
- zebra_day/etc/label_styles/blank.zpl +0 -0
- zebra_day/etc/label_styles/cornersStripOf4Squares_1inX1in.zpl +55 -0
- zebra_day/etc/label_styles/corners_1inX2in.zpl +28 -0
- zebra_day/etc/label_styles/corners_20cmX30cm.zpl +6 -0
- zebra_day/etc/label_styles/corners_smallTube.zpl +7 -0
- zebra_day/etc/label_styles/corners_unspecifiedDimensions.zpl +15 -0
- zebra_day/etc/label_styles/plate_1inX0.25inHD.zpl +9 -0
- zebra_day/etc/label_styles/smallTubeWdotHD_prod.zpl +8 -0
- zebra_day/etc/label_styles/smallTubeWdot_corners.zpl +7 -0
- zebra_day/etc/label_styles/smallTubeWdot_prod.zpl +8 -0
- zebra_day/etc/label_styles/smallTubeWdot_prodAlt1.zpl +6 -0
- zebra_day/etc/label_styles/smallTubeWdot_prodAlt1b.zpl +3 -0
- zebra_day/etc/label_styles/smallTubeWdot_prodV2.zpl +8 -0
- zebra_day/etc/label_styles/smallTubeWdot_reagent.zpl +29 -0
- zebra_day/etc/label_styles/stripOf4Squares_1inX1in.zpl +32 -0
- zebra_day/etc/label_styles/test_800dX800dCoordinateArray.zpl +1 -0
- zebra_day/etc/label_styles/tmps/tmp_zpl_templates.here +0 -0
- zebra_day/etc/label_styles/tube_20mmX30mmA.zpl +7 -0
- zebra_day/etc/label_styles/tube_2inX0.5in.zpl +15 -0
- zebra_day/etc/label_styles/tube_2inX0.5inHD.zpl +15 -0
- zebra_day/etc/label_styles/tube_2inX1inHD.zpl +22 -0
- zebra_day/etc/label_styles/tube_2inX1inHDv3.zpl +21 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.022846_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.033657_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.039597_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.047295_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.055804_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.061337_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.073326_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.081950_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.088251_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.096501_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.104767_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.110364_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.118239_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.125950_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.349866_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.361085_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.558323_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.565756_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/{2023-10-25_02:19:04.139607_printer_config.json → 2026-02-01_01:51:29.739070_printer_config.json} +4 -3
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.753796_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.760201_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.768747_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.775312_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.782533_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.789287_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.794230_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.800021_printer_config.json +5 -0
- zebra_day/etc/printer_config.json +3 -54
- zebra_day/etc/printer_config.template.json +3 -2
- zebra_day/etc/tmp_printers0.json +5 -0
- zebra_day/etc/tmp_printers374.json +5 -0
- zebra_day/etc/tmp_printers383.json +5 -0
- zebra_day/etc/tmp_printers450.json +5 -0
- zebra_day/etc/tmp_printers504.json +5 -0
- zebra_day/etc/tmp_printers608.json +5 -0
- zebra_day/etc/tmp_printers657.json +5 -0
- zebra_day/etc/tmp_printers838.json +5 -0
- zebra_day/etc/tmp_printers839.json +5 -0
- zebra_day/etc/tmp_printers933.json +5 -0
- zebra_day/etc/tmp_printers957.json +5 -0
- zebra_day/exceptions.py +88 -0
- zebra_day/files/hold +0 -0
- zebra_day/files/test_png_17696.png +0 -0
- zebra_day/files/test_png_23477.png +0 -0
- zebra_day/files/test_png_28157.png +0 -0
- zebra_day/files/test_png_35832.png +0 -0
- zebra_day/files/test_png_36400.png +0 -0
- zebra_day/files/test_png_40816.png +0 -0
- zebra_day/files/test_png_49564.png +0 -0
- zebra_day/files/test_png_53848.png +0 -0
- zebra_day/files/test_png_62542.png +0 -0
- zebra_day/files/test_png_91597.png +0 -0
- zebra_day/files/test_png_93633.png +0 -0
- zebra_day/files/tmpbjo3k7q1.png +0 -0
- zebra_day/files/tmpigtr4pwy.png +0 -0
- zebra_day/files/zpl_label_tube_2inX1in_2026-02-01_01:51:24.370964.png +0 -0
- zebra_day/logging_config.py +74 -0
- zebra_day/logs/.hold +0 -0
- zebra_day/logs/print_requests.log +2 -0
- zebra_day/paths.py +143 -0
- zebra_day/print_mgr.py +489 -103
- zebra_day/static/datschund.css +63 -43
- zebra_day/static/datschund.png +0 -0
- zebra_day/static/daylily.png +0 -0
- zebra_day/static/favicon.svg +20 -0
- zebra_day/static/general.css +99 -0
- zebra_day/static/js/zebra_modern.js +172 -0
- zebra_day/static/lsmc.css +354 -0
- zebra_day/static/moon.jpeg +0 -0
- zebra_day/static/oakland.css +0 -32
- zebra_day/static/popday_daylily.css +1 -1
- zebra_day/static/style.css +39 -0
- zebra_day/static/zebra_modern.css +771 -0
- zebra_day/templates/base.html +36 -0
- zebra_day/templates/bpr.html +72 -0
- zebra_day/templates/build_new_config.html +36 -0
- zebra_day/templates/build_print_request.html +32 -0
- zebra_day/templates/chg_ui_style.html +19 -0
- zebra_day/templates/edit_template.html +128 -0
- zebra_day/templates/edit_zpl.html +37 -0
- zebra_day/templates/index.html +82 -0
- zebra_day/templates/legacy/base.html +37 -0
- zebra_day/templates/legacy/bpr.html +72 -0
- zebra_day/templates/legacy/build_new_config.html +36 -0
- zebra_day/templates/legacy/build_print_request.html +32 -0
- zebra_day/templates/legacy/chg_ui_style.html +19 -0
- zebra_day/templates/legacy/edit_template.html +128 -0
- zebra_day/templates/legacy/edit_zpl.html +37 -0
- zebra_day/templates/legacy/index.html +82 -0
- zebra_day/templates/legacy/list_prior_configs.html +24 -0
- zebra_day/templates/legacy/print_result.html +30 -0
- zebra_day/templates/legacy/printer_details.html +25 -0
- zebra_day/templates/legacy/printer_status.html +70 -0
- zebra_day/templates/legacy/save_result.html +17 -0
- zebra_day/templates/legacy/send_print_request.html +34 -0
- zebra_day/templates/legacy/simple_print.html +94 -0
- zebra_day/templates/legacy/view_pstation_json.html +29 -0
- zebra_day/templates/list_prior_configs.html +24 -0
- zebra_day/templates/modern/base.html +98 -0
- zebra_day/templates/modern/config.html +141 -0
- zebra_day/templates/modern/dashboard.html +160 -0
- zebra_day/templates/modern/print_request.html +141 -0
- zebra_day/templates/modern/print_result.html +88 -0
- zebra_day/templates/modern/printer_detail.html +117 -0
- zebra_day/templates/modern/printers.html +133 -0
- zebra_day/templates/modern/save_result.html +46 -0
- zebra_day/templates/modern/template_editor.html +172 -0
- zebra_day/templates/modern/templates.html +122 -0
- zebra_day/templates/print_result.html +30 -0
- zebra_day/templates/printer_details.html +25 -0
- zebra_day/templates/printer_status.html +70 -0
- zebra_day/templates/save_result.html +17 -0
- zebra_day/templates/send_print_request.html +34 -0
- zebra_day/templates/simple_print.html +94 -0
- zebra_day/templates/view_pstation_json.html +29 -0
- zebra_day/web/__init__.py +9 -0
- zebra_day/web/app.py +171 -0
- zebra_day/web/auth.py +172 -0
- zebra_day/web/middleware.py +159 -0
- zebra_day/web/routers/__init__.py +2 -0
- zebra_day/web/routers/api.py +163 -0
- zebra_day/web/routers/ui.py +1051 -0
- zebra_day/zpl_renderer.py +273 -0
- zebra_day-1.0.2.dist-info/METADATA +786 -0
- zebra_day-1.0.2.dist-info/RECORD +179 -0
- {zebra_day-0.0.32.dist-info → zebra_day-1.0.2.dist-info}/WHEEL +1 -1
- zebra_day-1.0.2.dist-info/entry_points.txt +4 -0
- zebra_day/etc/.blind +0 -1
- zebra_day/etc/current_style.txt +0 -1
- zebra_day/etc/label_styles/test_2inX1in.zpl +0 -22
- zebra_day/etc/label_styles/tmps/labware_2inX1in.na.2023-10-24_12:49:21.045127.zpl +0 -21
- zebra_day/etc/label_styles/tmps/labware_2inX1in.naggg.2023-10-24_16:02:04.704814.8888.2023-10-24_16:02:16.443911.zpl +0 -21
- zebra_day/etc/label_styles/tmps/labware_2inX1in.naggg.2023-10-24_16:02:04.704814.zpl +0 -21
- zebra_day/etc/label_styles/tmps/test_2inX1in.na.2023-10-24_12:45:37.002774.zpl +0 -22
- zebra_day/etc/old_printer_config/2023-10-24_16:06:06.931764_printer_config.json +0 -67
- zebra_day/etc/printer_config.json~ +0 -67
- zebra_day/files/tmp_2olihg4.png +0 -0
- zebra_day/files/tmpveojoyvn.png +0 -0
- zebra_day/files/zpl_label_labware_2inX1in_2023-10-25_02:30:08.093631.png +0 -0
- zebra_day/files/zpl_label_test_2inX1in_2023-10-24_15:54:29.343124.png +0 -0
- zebra_day/files/zpl_label_test_2inX1in_2023-10-24_16:01:45.670132.png +0 -0
- zebra_day/static/beyonce.css +0 -227
- zebra_day/static/datschund_on_moon.css +0 -164
- zebra_day/static/medicalsci.css +0 -144
- zebra_day/static/moar_zebra.css +0 -133
- zebra_day/static/popday.css +0 -140
- zebra_day/static/popday_dark.css +0 -140
- zebra_day/static/popday_dog.css +0 -140
- zebra_day-0.0.32.dist-info/METADATA +0 -14
- zebra_day-0.0.32.dist-info/RECORD +0 -53
- zebra_day-0.0.32.dist-info/entry_points.txt +0 -2
- /zebra_day/{etc/label_styles/blank_0inX0in.zpl → bin/__init__.py} +0 -0
- /zebra_day/etc/label_styles/{labware_2inX1in.zpl → generic_2inX1in.zpl} +0 -0
- {zebra_day-0.0.32.dist-info → zebra_day-1.0.2.dist-info/licenses}/LICENSE +0 -0
- {zebra_day-0.0.32.dist-info → zebra_day-1.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1051 @@
|
|
|
1
|
+
"""
|
|
2
|
+
UI router for zebra_day web interface.
|
|
3
|
+
|
|
4
|
+
Provides HTML endpoints for the web-based management interface.
|
|
5
|
+
Supports dual interfaces:
|
|
6
|
+
- Modern UI: Root routes (/, /printers, /print, /templates, /config)
|
|
7
|
+
- Legacy UI: Routes under /legacy prefix
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import subprocess
|
|
14
|
+
import tempfile
|
|
15
|
+
import time
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
from fastapi import APIRouter, Request, Form, HTTPException
|
|
21
|
+
from fastapi.responses import HTMLResponse, RedirectResponse, Response
|
|
22
|
+
|
|
23
|
+
from zebra_day.logging_config import get_logger
|
|
24
|
+
import zebra_day.cmd_mgr as zdcm
|
|
25
|
+
|
|
26
|
+
_log = get_logger(__name__)
|
|
27
|
+
|
|
28
|
+
router = APIRouter()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_template_context(request: Request, **kwargs) -> dict:
|
|
32
|
+
"""Build common template context for legacy templates."""
|
|
33
|
+
return {
|
|
34
|
+
"request": request,
|
|
35
|
+
"css_theme": f"static/{request.app.state.css_theme}",
|
|
36
|
+
"local_ip": request.app.state.local_ip,
|
|
37
|
+
**kwargs,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_modern_context(request: Request, active_page: str = "", **kwargs) -> dict:
|
|
42
|
+
"""Build common template context for modern templates."""
|
|
43
|
+
return {
|
|
44
|
+
"request": request,
|
|
45
|
+
"active_page": active_page,
|
|
46
|
+
"local_ip": request.app.state.local_ip,
|
|
47
|
+
"version": getattr(request.app.state, "version", "0.7.0"),
|
|
48
|
+
"cache_bust": str(int(time.time())),
|
|
49
|
+
**kwargs,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_templates_list(pkg_path: Path) -> tuple[list, list]:
|
|
54
|
+
"""Get lists of stable and draft templates."""
|
|
55
|
+
styles_dir = pkg_path / "etc" / "label_styles"
|
|
56
|
+
stable_templates = []
|
|
57
|
+
draft_templates = []
|
|
58
|
+
|
|
59
|
+
if styles_dir.exists():
|
|
60
|
+
for f in sorted(styles_dir.iterdir()):
|
|
61
|
+
if f.is_file() and f.suffix == ".zpl":
|
|
62
|
+
stable_templates.append(f.stem)
|
|
63
|
+
|
|
64
|
+
tmps_dir = styles_dir / "tmps"
|
|
65
|
+
if tmps_dir.exists():
|
|
66
|
+
for f in sorted(tmps_dir.iterdir()):
|
|
67
|
+
if f.is_file() and f.suffix == ".zpl":
|
|
68
|
+
draft_templates.append(f.stem)
|
|
69
|
+
|
|
70
|
+
return stable_templates, draft_templates
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_stats(zp, pkg_path: Path) -> dict:
|
|
74
|
+
"""Calculate dashboard statistics."""
|
|
75
|
+
labs = zp.printers.get("labs", {})
|
|
76
|
+
total_printers = sum(len(printers) for printers in labs.values())
|
|
77
|
+
stable, draft = get_templates_list(pkg_path)
|
|
78
|
+
|
|
79
|
+
# Count backup files
|
|
80
|
+
bkup_dir = pkg_path / "etc" / "old_printer_config"
|
|
81
|
+
backups = len(list(bkup_dir.iterdir())) if bkup_dir.exists() else 0
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
"total_labs": len(labs),
|
|
85
|
+
"total_printers": total_printers,
|
|
86
|
+
"online_printers": 0, # Would need to check each printer
|
|
87
|
+
"total_templates": len(stable) + len(draft),
|
|
88
|
+
"backups": backups,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# =============================================================================
|
|
93
|
+
# MODERN UI ROUTES (root level)
|
|
94
|
+
# =============================================================================
|
|
95
|
+
|
|
96
|
+
@router.get("/", response_class=HTMLResponse)
|
|
97
|
+
async def modern_dashboard(request: Request):
|
|
98
|
+
"""Modern dashboard - home page."""
|
|
99
|
+
zp = request.app.state.zp
|
|
100
|
+
templates = request.app.state.templates
|
|
101
|
+
pkg_path = request.app.state.pkg_path
|
|
102
|
+
|
|
103
|
+
labs = zp.printers.get("labs", {})
|
|
104
|
+
stats = get_stats(zp, pkg_path)
|
|
105
|
+
|
|
106
|
+
context = get_modern_context(
|
|
107
|
+
request,
|
|
108
|
+
active_page="dashboard",
|
|
109
|
+
labs=labs,
|
|
110
|
+
stats=stats,
|
|
111
|
+
)
|
|
112
|
+
return templates.TemplateResponse("modern/dashboard.html", context)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@router.get("/printers", response_class=HTMLResponse)
|
|
116
|
+
async def modern_printers(request: Request):
|
|
117
|
+
"""Modern printers list - all labs."""
|
|
118
|
+
zp = request.app.state.zp
|
|
119
|
+
templates = request.app.state.templates
|
|
120
|
+
|
|
121
|
+
labs = list(zp.printers.get("labs", {}).keys())
|
|
122
|
+
ip_root = ".".join(request.app.state.local_ip.split(".")[:-1])
|
|
123
|
+
|
|
124
|
+
context = get_modern_context(
|
|
125
|
+
request,
|
|
126
|
+
active_page="printers",
|
|
127
|
+
labs=labs,
|
|
128
|
+
printers=None,
|
|
129
|
+
lab=None,
|
|
130
|
+
ip_root=ip_root,
|
|
131
|
+
)
|
|
132
|
+
return templates.TemplateResponse("modern/printers.html", context)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@router.get("/printers/{lab}", response_class=HTMLResponse)
|
|
136
|
+
async def modern_printers_by_lab(request: Request, lab: str):
|
|
137
|
+
"""Modern printers list for a specific lab."""
|
|
138
|
+
zp = request.app.state.zp
|
|
139
|
+
templates = request.app.state.templates
|
|
140
|
+
|
|
141
|
+
if lab not in zp.printers.get("labs", {}):
|
|
142
|
+
raise HTTPException(status_code=404, detail=f"Lab '{lab}' not found")
|
|
143
|
+
|
|
144
|
+
printers = []
|
|
145
|
+
for name, info in zp.printers["labs"][lab].items():
|
|
146
|
+
printers.append({
|
|
147
|
+
"name": name,
|
|
148
|
+
"ip_address": info.get("ip_address", ""),
|
|
149
|
+
"model": info.get("model", ""),
|
|
150
|
+
"serial": info.get("serial", ""),
|
|
151
|
+
"label_zpl_styles": info.get("label_zpl_styles", []),
|
|
152
|
+
"status": "online" if info.get("ip_address") else "unknown",
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
ip_root = ".".join(request.app.state.local_ip.split(".")[:-1])
|
|
156
|
+
|
|
157
|
+
context = get_modern_context(
|
|
158
|
+
request,
|
|
159
|
+
active_page="printers",
|
|
160
|
+
labs=list(zp.printers.get("labs", {}).keys()),
|
|
161
|
+
printers=printers,
|
|
162
|
+
lab=lab,
|
|
163
|
+
ip_root=ip_root,
|
|
164
|
+
)
|
|
165
|
+
return templates.TemplateResponse("modern/printers.html", context)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@router.get("/printers/{lab}/{printer_name}", response_class=HTMLResponse)
|
|
169
|
+
async def modern_printer_detail(request: Request, lab: str, printer_name: str):
|
|
170
|
+
"""Modern printer detail page."""
|
|
171
|
+
zp = request.app.state.zp
|
|
172
|
+
templates = request.app.state.templates
|
|
173
|
+
|
|
174
|
+
if lab not in zp.printers.get("labs", {}):
|
|
175
|
+
raise HTTPException(status_code=404, detail=f"Lab '{lab}' not found")
|
|
176
|
+
if printer_name not in zp.printers["labs"][lab]:
|
|
177
|
+
raise HTTPException(status_code=404, detail=f"Printer '{printer_name}' not found")
|
|
178
|
+
|
|
179
|
+
printer_info = zp.printers["labs"][lab][printer_name]
|
|
180
|
+
|
|
181
|
+
# Try to get printer configuration
|
|
182
|
+
printer_config = ""
|
|
183
|
+
ip_addr = printer_info.get("ip_address", "")
|
|
184
|
+
if ip_addr and ip_addr != "dl_png":
|
|
185
|
+
try:
|
|
186
|
+
printer_config = zdcm.ZebraPrinter(ip_addr).get_configuration()
|
|
187
|
+
except Exception as e:
|
|
188
|
+
printer_config = f"Unable to retrieve config: {e}"
|
|
189
|
+
|
|
190
|
+
context = get_modern_context(
|
|
191
|
+
request,
|
|
192
|
+
active_page="printers",
|
|
193
|
+
printer_name=printer_name,
|
|
194
|
+
lab=lab,
|
|
195
|
+
printer_info=printer_info,
|
|
196
|
+
printer_config=printer_config,
|
|
197
|
+
)
|
|
198
|
+
return templates.TemplateResponse("modern/printer_detail.html", context)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@router.get("/print", response_class=HTMLResponse)
|
|
202
|
+
async def modern_print_request(
|
|
203
|
+
request: Request,
|
|
204
|
+
lab: str = "",
|
|
205
|
+
printer: str = "",
|
|
206
|
+
template: str = "",
|
|
207
|
+
):
|
|
208
|
+
"""Modern print request form."""
|
|
209
|
+
zp = request.app.state.zp
|
|
210
|
+
templates = request.app.state.templates
|
|
211
|
+
pkg_path = request.app.state.pkg_path
|
|
212
|
+
|
|
213
|
+
stable_templates, draft_templates = get_templates_list(pkg_path)
|
|
214
|
+
labs_dict = zp.printers.get("labs", {})
|
|
215
|
+
|
|
216
|
+
context = get_modern_context(
|
|
217
|
+
request,
|
|
218
|
+
active_page="print",
|
|
219
|
+
labs=list(labs_dict.keys()),
|
|
220
|
+
labs_dict=json.dumps(labs_dict),
|
|
221
|
+
stable_templates=stable_templates,
|
|
222
|
+
draft_templates=draft_templates,
|
|
223
|
+
selected_lab=lab,
|
|
224
|
+
selected_printer=printer,
|
|
225
|
+
selected_template=template,
|
|
226
|
+
)
|
|
227
|
+
return templates.TemplateResponse("modern/print_request.html", context)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@router.get("/templates", response_class=HTMLResponse)
|
|
231
|
+
async def modern_templates(request: Request):
|
|
232
|
+
"""Modern template management page."""
|
|
233
|
+
templates = request.app.state.templates
|
|
234
|
+
pkg_path = request.app.state.pkg_path
|
|
235
|
+
|
|
236
|
+
stable_templates, draft_templates = get_templates_list(pkg_path)
|
|
237
|
+
|
|
238
|
+
context = get_modern_context(
|
|
239
|
+
request,
|
|
240
|
+
active_page="templates",
|
|
241
|
+
stable_templates=stable_templates,
|
|
242
|
+
draft_templates=draft_templates,
|
|
243
|
+
)
|
|
244
|
+
return templates.TemplateResponse("modern/templates.html", context)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@router.get("/templates/edit", response_class=HTMLResponse)
|
|
248
|
+
async def modern_template_edit(
|
|
249
|
+
request: Request,
|
|
250
|
+
filename: str,
|
|
251
|
+
dtype: str = "",
|
|
252
|
+
):
|
|
253
|
+
"""Modern template editor."""
|
|
254
|
+
zp = request.app.state.zp
|
|
255
|
+
templates = request.app.state.templates
|
|
256
|
+
pkg_path = request.app.state.pkg_path
|
|
257
|
+
|
|
258
|
+
if dtype:
|
|
259
|
+
filepath = pkg_path / "etc" / "label_styles" / dtype / filename
|
|
260
|
+
else:
|
|
261
|
+
filepath = pkg_path / "etc" / "label_styles" / filename
|
|
262
|
+
|
|
263
|
+
if not filepath.exists():
|
|
264
|
+
raise HTTPException(status_code=404, detail=f"Template '{filename}' not found")
|
|
265
|
+
|
|
266
|
+
content = filepath.read_text()
|
|
267
|
+
labs_dict = zp.printers.get("labs", {})
|
|
268
|
+
|
|
269
|
+
context = get_modern_context(
|
|
270
|
+
request,
|
|
271
|
+
active_page="templates",
|
|
272
|
+
filename=filename,
|
|
273
|
+
content=content,
|
|
274
|
+
dtype=dtype,
|
|
275
|
+
labs=list(labs_dict.keys()),
|
|
276
|
+
labs_dict=json.dumps(labs_dict),
|
|
277
|
+
)
|
|
278
|
+
return templates.TemplateResponse("modern/template_editor.html", context)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@router.get("/config", response_class=HTMLResponse)
|
|
282
|
+
async def modern_config(request: Request):
|
|
283
|
+
"""Modern configuration page."""
|
|
284
|
+
zp = request.app.state.zp
|
|
285
|
+
templates = request.app.state.templates
|
|
286
|
+
pkg_path = request.app.state.pkg_path
|
|
287
|
+
|
|
288
|
+
labs = list(zp.printers.get("labs", {}).keys())
|
|
289
|
+
ip_root = ".".join(request.app.state.local_ip.split(".")[:-1])
|
|
290
|
+
|
|
291
|
+
# Build config summary
|
|
292
|
+
stats = get_stats(zp, pkg_path)
|
|
293
|
+
config_summary = {
|
|
294
|
+
"labs": stats["total_labs"],
|
|
295
|
+
"printers": stats["total_printers"],
|
|
296
|
+
"templates": stats["total_templates"],
|
|
297
|
+
"backups": stats["backups"],
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
context = get_modern_context(
|
|
301
|
+
request,
|
|
302
|
+
active_page="config",
|
|
303
|
+
labs=labs,
|
|
304
|
+
ip_root=ip_root,
|
|
305
|
+
config_summary=config_summary,
|
|
306
|
+
)
|
|
307
|
+
return templates.TemplateResponse("modern/config.html", context)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@router.get("/config/view", response_class=HTMLResponse)
|
|
311
|
+
async def modern_config_view(request: Request):
|
|
312
|
+
"""View printer configuration JSON (redirects to legacy for now)."""
|
|
313
|
+
return RedirectResponse(url="/legacy/view_pstation_json", status_code=303)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@router.get("/config/edit", response_class=HTMLResponse)
|
|
317
|
+
async def modern_config_edit(request: Request):
|
|
318
|
+
"""Edit printer configuration JSON (redirects to legacy for now)."""
|
|
319
|
+
return RedirectResponse(url="/legacy/view_pstation_json", status_code=303)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@router.get("/config/backups", response_class=HTMLResponse)
|
|
323
|
+
async def modern_config_backups(request: Request):
|
|
324
|
+
"""List prior config files (redirects to legacy for now)."""
|
|
325
|
+
return RedirectResponse(url="/legacy/list_prior_printer_config_files", status_code=303)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@router.get("/config/new", response_class=HTMLResponse)
|
|
329
|
+
async def modern_config_new(request: Request):
|
|
330
|
+
"""Build new config (redirects to legacy for now)."""
|
|
331
|
+
return RedirectResponse(url="/legacy/build_new_printers_config_json", status_code=303)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
@router.get("/config/scan", response_class=HTMLResponse)
|
|
335
|
+
async def modern_config_scan(
|
|
336
|
+
request: Request,
|
|
337
|
+
ip_stub: str = "192.168.1",
|
|
338
|
+
scan_wait: str = "0.25",
|
|
339
|
+
lab: str = "scan-results",
|
|
340
|
+
):
|
|
341
|
+
"""Scan network for printers."""
|
|
342
|
+
zp = request.app.state.zp
|
|
343
|
+
zp.probe_zebra_printers_add_to_printers_json(
|
|
344
|
+
ip_stub=ip_stub, scan_wait=scan_wait, lab=lab
|
|
345
|
+
)
|
|
346
|
+
time.sleep(2.2)
|
|
347
|
+
return RedirectResponse(url=f"/printers/{lab}", status_code=303)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
@router.get("/_print_label", response_class=HTMLResponse)
|
|
351
|
+
async def modern_print_label(
|
|
352
|
+
request: Request,
|
|
353
|
+
lab: Optional[str] = None,
|
|
354
|
+
printer: str = "",
|
|
355
|
+
printer_ip: str = "",
|
|
356
|
+
label_zpl_style: str = "",
|
|
357
|
+
uid_barcode: str = "",
|
|
358
|
+
alt_a: str = "",
|
|
359
|
+
alt_b: str = "",
|
|
360
|
+
alt_c: str = "",
|
|
361
|
+
alt_d: str = "",
|
|
362
|
+
alt_e: str = "",
|
|
363
|
+
alt_f: str = "",
|
|
364
|
+
labSelect: str = "",
|
|
365
|
+
):
|
|
366
|
+
"""Execute print request - modern UI."""
|
|
367
|
+
zp = request.app.state.zp
|
|
368
|
+
templates = request.app.state.templates
|
|
369
|
+
rate_limiter = request.app.state.print_rate_limiter
|
|
370
|
+
|
|
371
|
+
if lab is None:
|
|
372
|
+
lab = labSelect
|
|
373
|
+
|
|
374
|
+
client_ip = request.client.host if request.client else "unknown"
|
|
375
|
+
|
|
376
|
+
# Check rate limit
|
|
377
|
+
allowed, reason = await rate_limiter.acquire(client_ip)
|
|
378
|
+
if not allowed:
|
|
379
|
+
raise HTTPException(status_code=429, detail=reason)
|
|
380
|
+
|
|
381
|
+
try:
|
|
382
|
+
result = zp.print_zpl(
|
|
383
|
+
lab=lab,
|
|
384
|
+
printer_name=printer,
|
|
385
|
+
label_zpl_style=label_zpl_style,
|
|
386
|
+
uid_barcode=uid_barcode,
|
|
387
|
+
alt_a=alt_a,
|
|
388
|
+
alt_b=alt_b,
|
|
389
|
+
alt_c=alt_c,
|
|
390
|
+
alt_d=alt_d,
|
|
391
|
+
alt_e=alt_e,
|
|
392
|
+
alt_f=alt_f,
|
|
393
|
+
client_ip=client_ip,
|
|
394
|
+
)
|
|
395
|
+
finally:
|
|
396
|
+
rate_limiter.release()
|
|
397
|
+
|
|
398
|
+
# Build the full URL for reference
|
|
399
|
+
full_url = str(request.url)
|
|
400
|
+
|
|
401
|
+
png_url = None
|
|
402
|
+
if result and ".png" in str(result):
|
|
403
|
+
png_name = str(result).split("/")[-1]
|
|
404
|
+
png_url = f"/files/{png_name}"
|
|
405
|
+
|
|
406
|
+
context = get_modern_context(
|
|
407
|
+
request,
|
|
408
|
+
title="Print Result",
|
|
409
|
+
success=True,
|
|
410
|
+
full_url=full_url,
|
|
411
|
+
png_url=png_url,
|
|
412
|
+
)
|
|
413
|
+
return templates.TemplateResponse("modern/print_result.html", context)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
@router.post("/save", response_class=HTMLResponse)
|
|
417
|
+
async def modern_save_template(
|
|
418
|
+
request: Request,
|
|
419
|
+
filename: str = Form(...),
|
|
420
|
+
content: str = Form(...),
|
|
421
|
+
ftag: str = Form("na"),
|
|
422
|
+
lab: str = Form(""),
|
|
423
|
+
printer: str = Form(""),
|
|
424
|
+
):
|
|
425
|
+
"""Save ZPL template as a new draft file - modern UI."""
|
|
426
|
+
templates = request.app.state.templates
|
|
427
|
+
pkg_path = request.app.state.pkg_path
|
|
428
|
+
|
|
429
|
+
rec_date = str(datetime.now()).replace(" ", "_")
|
|
430
|
+
new_filename = filename.replace(".zpl", f".{ftag}.{rec_date}.zpl")
|
|
431
|
+
|
|
432
|
+
tmps_dir = pkg_path / "etc" / "label_styles" / "tmps"
|
|
433
|
+
tmps_dir.mkdir(parents=True, exist_ok=True)
|
|
434
|
+
|
|
435
|
+
temp_filepath = tmps_dir / new_filename
|
|
436
|
+
temp_filepath.write_text(content)
|
|
437
|
+
|
|
438
|
+
context = get_modern_context(
|
|
439
|
+
request,
|
|
440
|
+
title="Template Saved",
|
|
441
|
+
new_filename=new_filename,
|
|
442
|
+
)
|
|
443
|
+
return templates.TemplateResponse("modern/save_result.html", context)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
@router.post("/png_renderer")
|
|
447
|
+
async def modern_png_renderer(
|
|
448
|
+
request: Request,
|
|
449
|
+
filename: str = Form(...),
|
|
450
|
+
content: str = Form(...),
|
|
451
|
+
lab: str = Form(""),
|
|
452
|
+
printer: str = Form(""),
|
|
453
|
+
ftag: str = Form(""),
|
|
454
|
+
):
|
|
455
|
+
"""Render ZPL content to PNG - modern UI."""
|
|
456
|
+
zp = request.app.state.zp
|
|
457
|
+
pkg_path = request.app.state.pkg_path
|
|
458
|
+
|
|
459
|
+
files_dir = pkg_path / "files"
|
|
460
|
+
files_dir.mkdir(parents=True, exist_ok=True)
|
|
461
|
+
|
|
462
|
+
png_tmp_f = tempfile.NamedTemporaryFile(
|
|
463
|
+
suffix=".png", dir=str(files_dir), delete=False
|
|
464
|
+
).name
|
|
465
|
+
|
|
466
|
+
zp.generate_label_png(content, png_fn=png_tmp_f)
|
|
467
|
+
|
|
468
|
+
# Return just the relative path for the img src
|
|
469
|
+
return Response(
|
|
470
|
+
content=f"files/{Path(png_tmp_f).name}",
|
|
471
|
+
media_type="text/plain",
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
# =============================================================================
|
|
476
|
+
# LEGACY UI ROUTES (under /legacy prefix)
|
|
477
|
+
# =============================================================================
|
|
478
|
+
|
|
479
|
+
@router.get("/legacy", response_class=HTMLResponse)
|
|
480
|
+
async def legacy_index(request: Request):
|
|
481
|
+
"""Legacy home page."""
|
|
482
|
+
zp = request.app.state.zp
|
|
483
|
+
templates = request.app.state.templates
|
|
484
|
+
|
|
485
|
+
labs = list(zp.printers.get("labs", {}).keys())
|
|
486
|
+
|
|
487
|
+
context = get_template_context(
|
|
488
|
+
request,
|
|
489
|
+
title="Zebra Day - Home",
|
|
490
|
+
labs=labs,
|
|
491
|
+
)
|
|
492
|
+
return templates.TemplateResponse("legacy/index.html", context)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
@router.get("/legacy/printer_status", response_class=HTMLResponse)
|
|
496
|
+
async def legacy_printer_status(request: Request, lab: str = "scan-results"):
|
|
497
|
+
"""Legacy printer status page for a lab."""
|
|
498
|
+
zp = request.app.state.zp
|
|
499
|
+
templates = request.app.state.templates
|
|
500
|
+
|
|
501
|
+
if lab not in zp.printers.get("labs", {}):
|
|
502
|
+
raise HTTPException(status_code=404, detail=f"Lab '{lab}' not found")
|
|
503
|
+
|
|
504
|
+
printers = []
|
|
505
|
+
for name, info in zp.printers["labs"][lab].items():
|
|
506
|
+
printer_data = {
|
|
507
|
+
"name": name,
|
|
508
|
+
"ip_address": info.get("ip_address", ""),
|
|
509
|
+
"model": info.get("model", ""),
|
|
510
|
+
"serial": info.get("serial", ""),
|
|
511
|
+
"label_zpl_styles": info.get("label_zpl_styles", []),
|
|
512
|
+
"arp_data": info.get("arp_data", ""),
|
|
513
|
+
"status": "unknown",
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
# Try to get printer status (curl check)
|
|
517
|
+
if info.get("ip_address") and info["ip_address"] != "dl_png":
|
|
518
|
+
try:
|
|
519
|
+
result = subprocess.run(
|
|
520
|
+
["curl", "-m", "4", info["ip_address"]],
|
|
521
|
+
capture_output=True,
|
|
522
|
+
text=True,
|
|
523
|
+
timeout=5,
|
|
524
|
+
)
|
|
525
|
+
for line in result.stdout.splitlines():
|
|
526
|
+
if "Status:" in line:
|
|
527
|
+
printer_data["status"] = line.strip()
|
|
528
|
+
break
|
|
529
|
+
except Exception:
|
|
530
|
+
printer_data["status"] = "Unable to connect"
|
|
531
|
+
|
|
532
|
+
printers.append(printer_data)
|
|
533
|
+
|
|
534
|
+
ip_root = ".".join(request.app.state.local_ip.split(".")[:-1])
|
|
535
|
+
|
|
536
|
+
context = get_template_context(
|
|
537
|
+
request,
|
|
538
|
+
title=f"Printer Status - {lab}",
|
|
539
|
+
lab=lab,
|
|
540
|
+
printers=printers,
|
|
541
|
+
ip_root=ip_root,
|
|
542
|
+
labs=list(zp.printers.get("labs", {}).keys()),
|
|
543
|
+
)
|
|
544
|
+
return templates.TemplateResponse("legacy/printer_status.html", context)
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
@router.get("/legacy/simple_print_request", response_class=HTMLResponse)
|
|
548
|
+
async def legacy_simple_print_request(request: Request):
|
|
549
|
+
"""Legacy simple print request form."""
|
|
550
|
+
zp = request.app.state.zp
|
|
551
|
+
templates = request.app.state.templates
|
|
552
|
+
|
|
553
|
+
pkg_path = request.app.state.pkg_path
|
|
554
|
+
styles_dir = pkg_path / "etc" / "label_styles"
|
|
555
|
+
|
|
556
|
+
template_names = []
|
|
557
|
+
if styles_dir.exists():
|
|
558
|
+
for f in sorted(styles_dir.iterdir()):
|
|
559
|
+
if f.is_file() and f.suffix == ".zpl":
|
|
560
|
+
template_names.append(f.stem)
|
|
561
|
+
|
|
562
|
+
labs_and_printers = {
|
|
563
|
+
lab: list(printers.keys())
|
|
564
|
+
for lab, printers in zp.printers.get("labs", {}).items()
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
context = get_template_context(
|
|
568
|
+
request,
|
|
569
|
+
title="Print Label",
|
|
570
|
+
templates=template_names,
|
|
571
|
+
labs=list(zp.printers.get("labs", {}).keys()),
|
|
572
|
+
labs_and_printers=json.dumps(labs_and_printers),
|
|
573
|
+
)
|
|
574
|
+
return templates.TemplateResponse("legacy/simple_print.html", context)
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
@router.get("/legacy/edit_zpl", response_class=HTMLResponse)
|
|
578
|
+
async def legacy_edit_zpl(request: Request):
|
|
579
|
+
"""Legacy list ZPL templates for editing."""
|
|
580
|
+
templates = request.app.state.templates
|
|
581
|
+
pkg_path = request.app.state.pkg_path
|
|
582
|
+
styles_dir = pkg_path / "etc" / "label_styles"
|
|
583
|
+
|
|
584
|
+
stable_templates = []
|
|
585
|
+
draft_templates = []
|
|
586
|
+
|
|
587
|
+
if styles_dir.exists():
|
|
588
|
+
for f in sorted(styles_dir.iterdir()):
|
|
589
|
+
if f.is_file() and f.suffix == ".zpl":
|
|
590
|
+
stable_templates.append(f.name)
|
|
591
|
+
|
|
592
|
+
tmps_dir = styles_dir / "tmps"
|
|
593
|
+
if tmps_dir.exists():
|
|
594
|
+
for f in sorted(tmps_dir.iterdir()):
|
|
595
|
+
if f.is_file() and f.suffix == ".zpl":
|
|
596
|
+
draft_templates.append(f.name)
|
|
597
|
+
|
|
598
|
+
context = get_template_context(
|
|
599
|
+
request,
|
|
600
|
+
title="Edit ZPL Templates",
|
|
601
|
+
stable_templates=stable_templates,
|
|
602
|
+
draft_templates=draft_templates,
|
|
603
|
+
)
|
|
604
|
+
return templates.TemplateResponse("legacy/edit_zpl.html", context)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
@router.get("/legacy/chg_ui_style", response_class=HTMLResponse)
|
|
608
|
+
async def legacy_chg_ui_style(request: Request, css_file: Optional[str] = None):
|
|
609
|
+
"""Legacy change UI style or show available styles."""
|
|
610
|
+
if css_file:
|
|
611
|
+
request.app.state.css_theme = css_file
|
|
612
|
+
return RedirectResponse(url="/legacy", status_code=303)
|
|
613
|
+
|
|
614
|
+
templates = request.app.state.templates
|
|
615
|
+
pkg_path = request.app.state.pkg_path
|
|
616
|
+
static_dir = pkg_path / "static"
|
|
617
|
+
|
|
618
|
+
css_files = []
|
|
619
|
+
if static_dir.exists():
|
|
620
|
+
for f in sorted(static_dir.iterdir()):
|
|
621
|
+
if f.suffix == ".css":
|
|
622
|
+
css_files.append(f.name)
|
|
623
|
+
|
|
624
|
+
context = get_template_context(request, title="Change UI Style", css_files=css_files)
|
|
625
|
+
return templates.TemplateResponse("legacy/chg_ui_style.html", context)
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
@router.get("/legacy/printer_details", response_class=HTMLResponse)
|
|
629
|
+
async def legacy_printer_details(request: Request, printer_name: str, lab: str):
|
|
630
|
+
"""Legacy show detailed printer information."""
|
|
631
|
+
zp = request.app.state.zp
|
|
632
|
+
templates = request.app.state.templates
|
|
633
|
+
|
|
634
|
+
if lab not in zp.printers.get("labs", {}):
|
|
635
|
+
raise HTTPException(status_code=404, detail=f"Lab '{lab}' not found")
|
|
636
|
+
if printer_name not in zp.printers["labs"][lab]:
|
|
637
|
+
raise HTTPException(status_code=404, detail=f"Printer '{printer_name}' not found")
|
|
638
|
+
|
|
639
|
+
printer_info = zp.printers["labs"][lab][printer_name]
|
|
640
|
+
|
|
641
|
+
# Try to get printer configuration
|
|
642
|
+
printer_config = ""
|
|
643
|
+
ip_addr = printer_info.get("ip_address", "")
|
|
644
|
+
if ip_addr and ip_addr != "dl_png":
|
|
645
|
+
try:
|
|
646
|
+
printer_config = zdcm.ZebraPrinter(ip_addr).get_configuration()
|
|
647
|
+
except Exception as e:
|
|
648
|
+
printer_config = f"Unable to retrieve config: {e}"
|
|
649
|
+
|
|
650
|
+
context = get_template_context(
|
|
651
|
+
request,
|
|
652
|
+
title=f"Printer Details - {printer_name}",
|
|
653
|
+
printer_name=printer_name,
|
|
654
|
+
lab=lab,
|
|
655
|
+
printer_info=printer_info,
|
|
656
|
+
printer_config=printer_config,
|
|
657
|
+
)
|
|
658
|
+
return templates.TemplateResponse("legacy/printer_details.html", context)
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
@router.get("/legacy/view_pstation_json", response_class=HTMLResponse)
|
|
662
|
+
async def legacy_view_pstation_json(request: Request, error_msg: Optional[str] = None):
|
|
663
|
+
"""Legacy view and edit printer configuration JSON."""
|
|
664
|
+
zp = request.app.state.zp
|
|
665
|
+
templates = request.app.state.templates
|
|
666
|
+
|
|
667
|
+
config_data = json.dumps(zp.printers, indent=4)
|
|
668
|
+
|
|
669
|
+
context = get_template_context(
|
|
670
|
+
request,
|
|
671
|
+
title="View Printer Config JSON",
|
|
672
|
+
config_data=config_data,
|
|
673
|
+
error_msg=error_msg,
|
|
674
|
+
)
|
|
675
|
+
return templates.TemplateResponse("legacy/view_pstation_json.html", context)
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
@router.post("/legacy/save_pstation_json")
|
|
679
|
+
async def legacy_save_pstation_json(request: Request, json_data: str = Form(...)):
|
|
680
|
+
"""Legacy save edited printer configuration JSON."""
|
|
681
|
+
zp = request.app.state.zp
|
|
682
|
+
|
|
683
|
+
try:
|
|
684
|
+
data = json.loads(json_data)
|
|
685
|
+
except json.JSONDecodeError as e:
|
|
686
|
+
return RedirectResponse(
|
|
687
|
+
url=f"/legacy/view_pstation_json?error_msg=Invalid+JSON:+{str(e)}",
|
|
688
|
+
status_code=303,
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
try:
|
|
692
|
+
# Backup and save
|
|
693
|
+
zp.save_printer_json()
|
|
694
|
+
|
|
695
|
+
# Write new config
|
|
696
|
+
with open(zp.printers_filename, "w") as f:
|
|
697
|
+
json.dump(data, f, indent=4)
|
|
698
|
+
|
|
699
|
+
# Reload config
|
|
700
|
+
zp.load_printer_json(json_file=zp.printers_filename, relative=False)
|
|
701
|
+
|
|
702
|
+
return RedirectResponse(url="/legacy", status_code=303)
|
|
703
|
+
|
|
704
|
+
except Exception as e:
|
|
705
|
+
return RedirectResponse(
|
|
706
|
+
url=f"/legacy/view_pstation_json?error_msg=Error+saving:+{str(e)}",
|
|
707
|
+
status_code=303,
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
@router.get("/legacy/clear_printers_json")
|
|
712
|
+
async def legacy_clear_printers_json(request: Request):
|
|
713
|
+
"""Legacy clear the printer configuration JSON."""
|
|
714
|
+
zp = request.app.state.zp
|
|
715
|
+
zp.clear_printers_json()
|
|
716
|
+
time.sleep(1.2)
|
|
717
|
+
return RedirectResponse(url="/legacy", status_code=303)
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
@router.get("/legacy/reset_pstation_json")
|
|
721
|
+
async def legacy_reset_pstation_json(request: Request):
|
|
722
|
+
"""Legacy reset printer config from template."""
|
|
723
|
+
zp = request.app.state.zp
|
|
724
|
+
zp.replace_printer_json_from_template()
|
|
725
|
+
time.sleep(1.2)
|
|
726
|
+
return RedirectResponse(url="/legacy", status_code=303)
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
@router.get("/legacy/list_prior_printer_config_files", response_class=HTMLResponse)
|
|
730
|
+
async def legacy_list_prior_printer_config_files(request: Request):
|
|
731
|
+
"""Legacy list backed up printer config files."""
|
|
732
|
+
templates = request.app.state.templates
|
|
733
|
+
pkg_path = request.app.state.pkg_path
|
|
734
|
+
bkup_dir = pkg_path / "etc" / "old_printer_config"
|
|
735
|
+
|
|
736
|
+
backup_files = []
|
|
737
|
+
if bkup_dir.exists():
|
|
738
|
+
for f in sorted(bkup_dir.iterdir(), reverse=True):
|
|
739
|
+
if f.is_file():
|
|
740
|
+
backup_files.append(f.name)
|
|
741
|
+
|
|
742
|
+
context = get_template_context(
|
|
743
|
+
request,
|
|
744
|
+
title="Prior Printer Config Files",
|
|
745
|
+
backup_files=backup_files,
|
|
746
|
+
)
|
|
747
|
+
return templates.TemplateResponse("legacy/list_prior_configs.html", context)
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
@router.get("/legacy/build_new_printers_config_json", response_class=HTMLResponse)
|
|
751
|
+
async def legacy_build_new_printers_config_json(request: Request):
|
|
752
|
+
"""Legacy show network scan form."""
|
|
753
|
+
zp = request.app.state.zp
|
|
754
|
+
templates = request.app.state.templates
|
|
755
|
+
|
|
756
|
+
ip_root = ".".join(request.app.state.local_ip.split(".")[:-1])
|
|
757
|
+
|
|
758
|
+
context = get_template_context(
|
|
759
|
+
request,
|
|
760
|
+
title="Scan Network for Printers",
|
|
761
|
+
ip_root=ip_root,
|
|
762
|
+
labs=list(zp.printers.get("labs", {}).keys()),
|
|
763
|
+
)
|
|
764
|
+
return templates.TemplateResponse("legacy/build_new_config.html", context)
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
@router.get("/legacy/probe_zebra_printers_add_to_printers_json")
|
|
768
|
+
async def legacy_probe_zebra_printers(
|
|
769
|
+
request: Request,
|
|
770
|
+
ip_stub: str = "192.168.1",
|
|
771
|
+
scan_wait: str = "0.25",
|
|
772
|
+
lab: str = "scan-results",
|
|
773
|
+
):
|
|
774
|
+
"""Legacy probe network for Zebra printers and add to config."""
|
|
775
|
+
zp = request.app.state.zp
|
|
776
|
+
zp.probe_zebra_printers_add_to_printers_json(
|
|
777
|
+
ip_stub=ip_stub, scan_wait=scan_wait, lab=lab
|
|
778
|
+
)
|
|
779
|
+
time.sleep(2.2)
|
|
780
|
+
return RedirectResponse(url=f"/legacy/printer_status?lab={lab}", status_code=303)
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
@router.get("/legacy/bpr", response_class=HTMLResponse)
|
|
784
|
+
async def legacy_bpr(request: Request):
|
|
785
|
+
"""Legacy build print request - select lab, printer, template."""
|
|
786
|
+
zp = request.app.state.zp
|
|
787
|
+
templates = request.app.state.templates
|
|
788
|
+
pkg_path = request.app.state.pkg_path
|
|
789
|
+
|
|
790
|
+
styles_dir = pkg_path / "etc" / "label_styles"
|
|
791
|
+
|
|
792
|
+
stable_templates = []
|
|
793
|
+
draft_templates = []
|
|
794
|
+
|
|
795
|
+
if styles_dir.exists():
|
|
796
|
+
for f in sorted(styles_dir.iterdir()):
|
|
797
|
+
if f.is_file() and f.suffix == ".zpl":
|
|
798
|
+
stable_templates.append(f.stem)
|
|
799
|
+
|
|
800
|
+
tmps_dir = styles_dir / "tmps"
|
|
801
|
+
if tmps_dir.exists():
|
|
802
|
+
for f in sorted(tmps_dir.iterdir()):
|
|
803
|
+
if f.is_file() and f.suffix == ".zpl":
|
|
804
|
+
draft_templates.append(f.stem)
|
|
805
|
+
|
|
806
|
+
labs_dict = zp.printers.get("labs", {})
|
|
807
|
+
|
|
808
|
+
context = get_template_context(
|
|
809
|
+
request,
|
|
810
|
+
title="Build Print Request",
|
|
811
|
+
labs=list(labs_dict.keys()),
|
|
812
|
+
labs_dict=json.dumps(labs_dict),
|
|
813
|
+
stable_templates=stable_templates,
|
|
814
|
+
draft_templates=draft_templates,
|
|
815
|
+
)
|
|
816
|
+
return templates.TemplateResponse("legacy/bpr.html", context)
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
@router.get("/legacy/send_print_request", response_class=HTMLResponse)
|
|
820
|
+
async def legacy_send_print_request(request: Request):
|
|
821
|
+
"""Legacy send print request - stable templates only."""
|
|
822
|
+
zp = request.app.state.zp
|
|
823
|
+
templates = request.app.state.templates
|
|
824
|
+
|
|
825
|
+
context = get_template_context(
|
|
826
|
+
request,
|
|
827
|
+
title="Send Print Request",
|
|
828
|
+
labs=zp.printers.get("labs", {}),
|
|
829
|
+
)
|
|
830
|
+
return templates.TemplateResponse("legacy/send_print_request.html", context)
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
@router.get("/legacy/build_print_request", response_class=HTMLResponse)
|
|
834
|
+
async def legacy_build_print_request(
|
|
835
|
+
request: Request,
|
|
836
|
+
lab: str = "",
|
|
837
|
+
printer: str = "",
|
|
838
|
+
printer_ip: str = "",
|
|
839
|
+
label_zpl_style: str = "",
|
|
840
|
+
filename: str = "",
|
|
841
|
+
):
|
|
842
|
+
"""Legacy show print request form with pre-filled values."""
|
|
843
|
+
templates = request.app.state.templates
|
|
844
|
+
|
|
845
|
+
if label_zpl_style in ["", "None"] and filename not in ["", "None"]:
|
|
846
|
+
label_zpl_style = filename.replace(".zpl", "")
|
|
847
|
+
|
|
848
|
+
context = get_template_context(
|
|
849
|
+
request,
|
|
850
|
+
title="Build Print Request",
|
|
851
|
+
lab=lab,
|
|
852
|
+
printer=printer,
|
|
853
|
+
printer_ip=printer_ip,
|
|
854
|
+
label_zpl_style=label_zpl_style,
|
|
855
|
+
)
|
|
856
|
+
return templates.TemplateResponse("legacy/build_print_request.html", context)
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
@router.get("/legacy/_print_label", response_class=HTMLResponse)
|
|
860
|
+
async def legacy_print_label(
|
|
861
|
+
request: Request,
|
|
862
|
+
lab: Optional[str] = None,
|
|
863
|
+
printer: str = "",
|
|
864
|
+
printer_ip: str = "",
|
|
865
|
+
label_zpl_style: str = "",
|
|
866
|
+
uid_barcode: str = "",
|
|
867
|
+
alt_a: str = "",
|
|
868
|
+
alt_b: str = "",
|
|
869
|
+
alt_c: str = "",
|
|
870
|
+
alt_d: str = "",
|
|
871
|
+
alt_e: str = "",
|
|
872
|
+
alt_f: str = "",
|
|
873
|
+
labSelect: str = "",
|
|
874
|
+
):
|
|
875
|
+
"""Legacy execute print request."""
|
|
876
|
+
zp = request.app.state.zp
|
|
877
|
+
templates = request.app.state.templates
|
|
878
|
+
rate_limiter = request.app.state.print_rate_limiter
|
|
879
|
+
|
|
880
|
+
if lab is None:
|
|
881
|
+
lab = labSelect
|
|
882
|
+
|
|
883
|
+
client_ip = request.client.host if request.client else "unknown"
|
|
884
|
+
|
|
885
|
+
# Check rate limit
|
|
886
|
+
allowed, reason = await rate_limiter.acquire(client_ip)
|
|
887
|
+
if not allowed:
|
|
888
|
+
raise HTTPException(status_code=429, detail=reason)
|
|
889
|
+
|
|
890
|
+
try:
|
|
891
|
+
result = zp.print_zpl(
|
|
892
|
+
lab=lab,
|
|
893
|
+
printer_name=printer,
|
|
894
|
+
label_zpl_style=label_zpl_style,
|
|
895
|
+
uid_barcode=uid_barcode,
|
|
896
|
+
alt_a=alt_a,
|
|
897
|
+
alt_b=alt_b,
|
|
898
|
+
alt_c=alt_c,
|
|
899
|
+
alt_d=alt_d,
|
|
900
|
+
alt_e=alt_e,
|
|
901
|
+
alt_f=alt_f,
|
|
902
|
+
client_ip=client_ip,
|
|
903
|
+
)
|
|
904
|
+
finally:
|
|
905
|
+
rate_limiter.release()
|
|
906
|
+
|
|
907
|
+
# Build the full URL for reference
|
|
908
|
+
full_url = str(request.url)
|
|
909
|
+
|
|
910
|
+
png_url = None
|
|
911
|
+
if result and ".png" in str(result):
|
|
912
|
+
png_name = str(result).split("/")[-1]
|
|
913
|
+
png_url = f"/files/{png_name}"
|
|
914
|
+
|
|
915
|
+
context = get_template_context(
|
|
916
|
+
request,
|
|
917
|
+
title="Print Result",
|
|
918
|
+
success=True,
|
|
919
|
+
full_url=full_url,
|
|
920
|
+
png_url=png_url,
|
|
921
|
+
)
|
|
922
|
+
return templates.TemplateResponse("legacy/print_result.html", context)
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
@router.get("/legacy/edit", response_class=HTMLResponse)
|
|
926
|
+
async def legacy_edit_template(
|
|
927
|
+
request: Request,
|
|
928
|
+
filename: str,
|
|
929
|
+
dtype: str = "",
|
|
930
|
+
):
|
|
931
|
+
"""Legacy edit a ZPL template file."""
|
|
932
|
+
zp = request.app.state.zp
|
|
933
|
+
templates = request.app.state.templates
|
|
934
|
+
pkg_path = request.app.state.pkg_path
|
|
935
|
+
|
|
936
|
+
if dtype:
|
|
937
|
+
filepath = pkg_path / "etc" / "label_styles" / dtype / filename
|
|
938
|
+
else:
|
|
939
|
+
filepath = pkg_path / "etc" / "label_styles" / filename
|
|
940
|
+
|
|
941
|
+
if not filepath.exists():
|
|
942
|
+
raise HTTPException(status_code=404, detail=f"Template '{filename}' not found")
|
|
943
|
+
|
|
944
|
+
content = filepath.read_text()
|
|
945
|
+
|
|
946
|
+
labs_dict = zp.printers.get("labs", {})
|
|
947
|
+
|
|
948
|
+
context = get_template_context(
|
|
949
|
+
request,
|
|
950
|
+
title=f"Edit: {filename}",
|
|
951
|
+
filename=filename,
|
|
952
|
+
content=content,
|
|
953
|
+
dtype=dtype,
|
|
954
|
+
labs=list(labs_dict.keys()),
|
|
955
|
+
labs_dict=json.dumps(labs_dict),
|
|
956
|
+
)
|
|
957
|
+
return templates.TemplateResponse("legacy/edit_template.html", context)
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
@router.post("/legacy/save", response_class=HTMLResponse)
|
|
961
|
+
async def legacy_save_template(
|
|
962
|
+
request: Request,
|
|
963
|
+
filename: str = Form(...),
|
|
964
|
+
content: str = Form(...),
|
|
965
|
+
ftag: str = Form("na"),
|
|
966
|
+
lab: str = Form(""),
|
|
967
|
+
printer: str = Form(""),
|
|
968
|
+
):
|
|
969
|
+
"""Legacy save ZPL template as a new draft file."""
|
|
970
|
+
templates = request.app.state.templates
|
|
971
|
+
pkg_path = request.app.state.pkg_path
|
|
972
|
+
|
|
973
|
+
rec_date = str(datetime.now()).replace(" ", "_")
|
|
974
|
+
new_filename = filename.replace(".zpl", f".{ftag}.{rec_date}.zpl")
|
|
975
|
+
|
|
976
|
+
tmps_dir = pkg_path / "etc" / "label_styles" / "tmps"
|
|
977
|
+
tmps_dir.mkdir(parents=True, exist_ok=True)
|
|
978
|
+
|
|
979
|
+
temp_filepath = tmps_dir / new_filename
|
|
980
|
+
temp_filepath.write_text(content)
|
|
981
|
+
|
|
982
|
+
context = get_template_context(
|
|
983
|
+
request,
|
|
984
|
+
title="Template Saved",
|
|
985
|
+
new_filename=new_filename,
|
|
986
|
+
)
|
|
987
|
+
return templates.TemplateResponse("legacy/save_result.html", context)
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
@router.post("/legacy/png_renderer")
|
|
991
|
+
async def legacy_png_renderer(
|
|
992
|
+
request: Request,
|
|
993
|
+
filename: str = Form(...),
|
|
994
|
+
content: str = Form(...),
|
|
995
|
+
lab: str = Form(""),
|
|
996
|
+
printer: str = Form(""),
|
|
997
|
+
ftag: str = Form(""),
|
|
998
|
+
):
|
|
999
|
+
"""Legacy render ZPL content to PNG."""
|
|
1000
|
+
zp = request.app.state.zp
|
|
1001
|
+
pkg_path = request.app.state.pkg_path
|
|
1002
|
+
|
|
1003
|
+
files_dir = pkg_path / "files"
|
|
1004
|
+
files_dir.mkdir(parents=True, exist_ok=True)
|
|
1005
|
+
|
|
1006
|
+
png_tmp_f = tempfile.NamedTemporaryFile(
|
|
1007
|
+
suffix=".png", dir=str(files_dir), delete=False
|
|
1008
|
+
).name
|
|
1009
|
+
|
|
1010
|
+
zp.generate_label_png(content, png_fn=png_tmp_f)
|
|
1011
|
+
|
|
1012
|
+
# Return just the relative path for the img src
|
|
1013
|
+
return Response(
|
|
1014
|
+
content=f"files/{Path(png_tmp_f).name}",
|
|
1015
|
+
media_type="text/plain",
|
|
1016
|
+
)
|
|
1017
|
+
|
|
1018
|
+
|
|
1019
|
+
@router.post("/legacy/build_and_send_raw_print_request")
|
|
1020
|
+
async def legacy_build_and_send_raw_print_request(
|
|
1021
|
+
request: Request,
|
|
1022
|
+
lab: str = Form(...),
|
|
1023
|
+
printer: str = Form(...),
|
|
1024
|
+
content: str = Form(...),
|
|
1025
|
+
printer_ip: str = Form(""),
|
|
1026
|
+
label_zpl_style: str = Form(""),
|
|
1027
|
+
filename: str = Form(""),
|
|
1028
|
+
ftag: str = Form(""),
|
|
1029
|
+
):
|
|
1030
|
+
"""Legacy send raw ZPL content to printer."""
|
|
1031
|
+
zp = request.app.state.zp
|
|
1032
|
+
rate_limiter = request.app.state.print_rate_limiter
|
|
1033
|
+
client_ip = request.client.host if request.client else "unknown"
|
|
1034
|
+
|
|
1035
|
+
# Check rate limit
|
|
1036
|
+
allowed, reason = await rate_limiter.acquire(client_ip)
|
|
1037
|
+
if not allowed:
|
|
1038
|
+
raise HTTPException(status_code=429, detail=reason)
|
|
1039
|
+
|
|
1040
|
+
try:
|
|
1041
|
+
zp.print_zpl(
|
|
1042
|
+
lab=lab,
|
|
1043
|
+
printer_name=printer,
|
|
1044
|
+
label_zpl_style=None,
|
|
1045
|
+
zpl_content=content,
|
|
1046
|
+
client_ip=client_ip,
|
|
1047
|
+
)
|
|
1048
|
+
finally:
|
|
1049
|
+
rate_limiter.release()
|
|
1050
|
+
|
|
1051
|
+
return {"status": "sent"}
|