zebra-day 0.0.37__py3-none-any.whl → 2.0.0__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/__init__.py +0 -0
- zebra_day/cli/__init__.py +240 -0
- zebra_day/cli/cognito.py +121 -0
- zebra_day/cli/gui.py +338 -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/Monoid-Regular-HalfTight-Dollar-0-1-l.ttf +0 -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/generic_2inX1in.zpl +21 -0
- zebra_day/etc/label_styles/plate_1inX0.25in.zpl +9 -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/.hold +0 -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.3in.zpl +15 -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_2inX1in.zpl +25 -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/.hold +0 -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/2026-02-01_01:51:29.739070_printer_config.json +16 -0
- 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 +4 -0
- zebra_day/etc/printer_config.template.json +24 -0
- zebra_day/etc/tmp_printers0.json +5 -0
- zebra_day/etc/tmp_printers120.json +10 -0
- zebra_day/etc/tmp_printers145.json +10 -0
- zebra_day/etc/tmp_printers207.json +10 -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_printers469.json +10 -0
- zebra_day/etc/tmp_printers485.json +10 -0
- zebra_day/etc/tmp_printers504.json +5 -0
- zebra_day/etc/tmp_printers531.json +10 -0
- zebra_day/etc/tmp_printers540.json +10 -0
- zebra_day/etc/tmp_printers542.json +10 -0
- zebra_day/etc/tmp_printers552.json +10 -0
- zebra_day/etc/tmp_printers608.json +5 -0
- zebra_day/etc/tmp_printers657.json +5 -0
- zebra_day/etc/tmp_printers715.json +10 -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/etc/tmp_printers972.json +10 -0
- zebra_day/exceptions.py +88 -0
- zebra_day/files/.hold +0 -0
- zebra_day/files/blank_preview.png +0 -0
- zebra_day/files/corners_20cmX30cm_preview.png +0 -0
- zebra_day/files/generic_2inX1in_preview.png +0 -0
- zebra_day/files/hold +0 -0
- zebra_day/files/test_png_12020.png +0 -0
- zebra_day/files/test_png_12352.png +0 -0
- zebra_day/files/test_png_15472.png +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_24493.png +0 -0
- zebra_day/files/test_png_28157.png +0 -0
- zebra_day/files/test_png_30069.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_47791.png +0 -0
- zebra_day/files/test_png_47799.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_55588.png +0 -0
- zebra_day/files/test_png_58809.png +0 -0
- zebra_day/files/test_png_62542.png +0 -0
- zebra_day/files/test_png_67242.png +0 -0
- zebra_day/files/test_png_89893.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/tube_20mmX30mmA_preview.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 +557 -117
- zebra_day/static/datschund.css +140 -0
- 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 +197 -0
- zebra_day/static/petrichor.css +150 -0
- zebra_day/static/popday_daylily.css +140 -0
- zebra_day/static/style.css +183 -0
- zebra_day/static/triangles.css +122 -0
- zebra_day/static/tron.css +277 -0
- zebra_day/static/zebra_modern.css +771 -0
- zebra_day/static/zebras.css +176 -0
- zebra_day/templates/modern/base.html +98 -0
- zebra_day/templates/modern/config.html +141 -0
- zebra_day/templates/modern/config_backups.html +59 -0
- zebra_day/templates/modern/config_editor.html +95 -0
- zebra_day/templates/modern/config_new.html +93 -0
- zebra_day/templates/modern/dashboard.html +160 -0
- zebra_day/templates/modern/print_request.html +145 -0
- zebra_day/templates/modern/print_result.html +88 -0
- zebra_day/templates/modern/printer_detail.html +244 -0
- zebra_day/templates/modern/printers.html +144 -0
- zebra_day/templates/modern/save_result.html +46 -0
- zebra_day/templates/modern/template_editor.html +175 -0
- zebra_day/templates/modern/templates.html +122 -0
- zebra_day/web/__init__.py +9 -0
- zebra_day/web/app.py +248 -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 +313 -0
- zebra_day/web/routers/ui.py +636 -0
- zebra_day/zpl_renderer.py +273 -0
- zebra_day-2.0.0.dist-info/METADATA +847 -0
- zebra_day-2.0.0.dist-info/RECORD +168 -0
- {zebra_day-0.0.37.dist-info → zebra_day-2.0.0.dist-info}/WHEEL +1 -1
- zebra_day-2.0.0.dist-info/entry_points.txt +4 -0
- zebra_day/bin/scan_for_networed_zebra_printers.py +0 -23
- zebra_day/bin/te.py +0 -905
- zebra_day/bin/zserve.py +0 -620
- zebra_day-0.0.37.dist-info/METADATA +0 -1177
- zebra_day-0.0.37.dist-info/RECORD +0 -10
- {zebra_day-0.0.37.dist-info → zebra_day-2.0.0.dist-info/licenses}/LICENSE +0 -0
- {zebra_day-0.0.37.dist-info → zebra_day-2.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Versioned JSON API router for zebra_day.
|
|
3
|
+
|
|
4
|
+
Provides programmatic access to printer management and label printing.
|
|
5
|
+
All endpoints return JSON and are prefixed with /api/v1/.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Optional, List, Dict, Any
|
|
10
|
+
|
|
11
|
+
from fastapi import APIRouter, HTTPException, Request
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
|
|
14
|
+
router = APIRouter()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# ----- Request/Response Models -----
|
|
18
|
+
|
|
19
|
+
class PrintRequest(BaseModel):
|
|
20
|
+
"""Request model for printing a label."""
|
|
21
|
+
lab: str = Field(..., description="Lab identifier")
|
|
22
|
+
printer: str = Field(..., description="Printer name")
|
|
23
|
+
label_zpl_style: Optional[str] = Field(None, description="ZPL template name")
|
|
24
|
+
uid_barcode: str = Field("", description="UID for barcode")
|
|
25
|
+
alt_a: str = Field("", description="Alternative field A")
|
|
26
|
+
alt_b: str = Field("", description="Alternative field B")
|
|
27
|
+
alt_c: str = Field("", description="Alternative field C")
|
|
28
|
+
alt_d: str = Field("", description="Alternative field D")
|
|
29
|
+
alt_e: str = Field("", description="Alternative field E")
|
|
30
|
+
alt_f: str = Field("", description="Alternative field F")
|
|
31
|
+
copies: int = Field(1, ge=1, le=100, description="Number of copies")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PrintResponse(BaseModel):
|
|
35
|
+
"""Response model for print request."""
|
|
36
|
+
success: bool
|
|
37
|
+
message: str
|
|
38
|
+
png_url: Optional[str] = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class PrinterInfo(BaseModel):
|
|
42
|
+
"""Printer information model (v2.0.0 schema)."""
|
|
43
|
+
id: str = Field(..., description="Printer identifier/key in JSON")
|
|
44
|
+
ip_address: str
|
|
45
|
+
printer_name: Optional[str] = Field(None, description="User-friendly display name")
|
|
46
|
+
lab_location: Optional[str] = Field(None, description="Location within the lab")
|
|
47
|
+
manufacturer: str = Field("zebra", description="Printer manufacturer")
|
|
48
|
+
model: str
|
|
49
|
+
serial: str
|
|
50
|
+
label_zpl_styles: List[str]
|
|
51
|
+
default_label_style: Optional[str] = Field(None, description="Default label style to use when none specified")
|
|
52
|
+
print_method: str
|
|
53
|
+
notes: Optional[str] = Field("", description="Optional notes")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class LabInfo(BaseModel):
|
|
57
|
+
"""Lab information model (v2.0.0 schema)."""
|
|
58
|
+
id: str = Field(..., description="Lab identifier/key in JSON")
|
|
59
|
+
lab_name: str = Field(..., description="Human-readable lab name")
|
|
60
|
+
available_locations: List[str] = Field(default_factory=list, description="Valid location options for printers")
|
|
61
|
+
printers: List[PrinterInfo]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class LabPrinters(BaseModel):
|
|
65
|
+
"""Lab and its printers (deprecated, use LabInfo)."""
|
|
66
|
+
lab: str
|
|
67
|
+
printers: List[PrinterInfo]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ----- Endpoints -----
|
|
71
|
+
|
|
72
|
+
@router.get("/labs", response_model=List[str])
|
|
73
|
+
async def list_labs(request: Request) -> List[str]:
|
|
74
|
+
"""List all available labs."""
|
|
75
|
+
zp = request.app.state.zp
|
|
76
|
+
return list(zp.printers.get("labs", {}).keys())
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@router.get("/labs/{lab}", response_model=LabInfo)
|
|
80
|
+
async def get_lab(request: Request, lab: str) -> LabInfo:
|
|
81
|
+
"""Get lab details including available locations and printers."""
|
|
82
|
+
zp = request.app.state.zp
|
|
83
|
+
labs = zp.printers.get("labs", {})
|
|
84
|
+
|
|
85
|
+
if lab not in labs:
|
|
86
|
+
raise HTTPException(status_code=404, detail=f"Lab '{lab}' not found")
|
|
87
|
+
|
|
88
|
+
lab_data = labs[lab]
|
|
89
|
+
lab_printers = lab_data.get("printers", {})
|
|
90
|
+
|
|
91
|
+
printers = []
|
|
92
|
+
for printer_id, info in lab_printers.items():
|
|
93
|
+
printers.append(
|
|
94
|
+
PrinterInfo(
|
|
95
|
+
id=printer_id,
|
|
96
|
+
ip_address=info.get("ip_address", ""),
|
|
97
|
+
printer_name=info.get("printer_name"),
|
|
98
|
+
lab_location=info.get("lab_location"),
|
|
99
|
+
manufacturer=info.get("manufacturer", "zebra"),
|
|
100
|
+
model=info.get("model", ""),
|
|
101
|
+
serial=info.get("serial", ""),
|
|
102
|
+
label_zpl_styles=info.get("label_zpl_styles", []),
|
|
103
|
+
default_label_style=info.get("default_label_style"),
|
|
104
|
+
print_method=info.get("print_method", ""),
|
|
105
|
+
notes=info.get("notes", ""),
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return LabInfo(
|
|
110
|
+
id=lab,
|
|
111
|
+
lab_name=lab_data.get("lab_name", lab),
|
|
112
|
+
available_locations=lab_data.get("available_locations", []),
|
|
113
|
+
printers=printers,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@router.get("/labs/{lab}/printers", response_model=List[PrinterInfo])
|
|
118
|
+
async def list_printers(request: Request, lab: str) -> List[PrinterInfo]:
|
|
119
|
+
"""List all printers in a lab."""
|
|
120
|
+
zp = request.app.state.zp
|
|
121
|
+
labs = zp.printers.get("labs", {})
|
|
122
|
+
|
|
123
|
+
if lab not in labs:
|
|
124
|
+
raise HTTPException(status_code=404, detail=f"Lab '{lab}' not found")
|
|
125
|
+
|
|
126
|
+
# Access printers via nested 'printers' key (v2 schema)
|
|
127
|
+
lab_printers = labs[lab].get("printers", {})
|
|
128
|
+
|
|
129
|
+
printers = []
|
|
130
|
+
for printer_id, info in lab_printers.items():
|
|
131
|
+
printers.append(
|
|
132
|
+
PrinterInfo(
|
|
133
|
+
id=printer_id,
|
|
134
|
+
ip_address=info.get("ip_address", ""),
|
|
135
|
+
printer_name=info.get("printer_name"),
|
|
136
|
+
lab_location=info.get("lab_location"),
|
|
137
|
+
manufacturer=info.get("manufacturer", "zebra"),
|
|
138
|
+
model=info.get("model", ""),
|
|
139
|
+
serial=info.get("serial", ""),
|
|
140
|
+
label_zpl_styles=info.get("label_zpl_styles", []),
|
|
141
|
+
default_label_style=info.get("default_label_style"),
|
|
142
|
+
print_method=info.get("print_method", ""),
|
|
143
|
+
notes=info.get("notes", ""),
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
return printers
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@router.get("/templates", response_model=List[str])
|
|
150
|
+
async def list_templates(request: Request) -> List[str]:
|
|
151
|
+
"""List all available ZPL templates."""
|
|
152
|
+
from pathlib import Path
|
|
153
|
+
|
|
154
|
+
pkg_path = request.app.state.pkg_path
|
|
155
|
+
styles_dir = pkg_path / "etc" / "label_styles"
|
|
156
|
+
|
|
157
|
+
templates = []
|
|
158
|
+
if styles_dir.exists():
|
|
159
|
+
for f in styles_dir.iterdir():
|
|
160
|
+
if f.is_file() and f.suffix == ".zpl":
|
|
161
|
+
templates.append(f.stem)
|
|
162
|
+
|
|
163
|
+
# Also include drafts
|
|
164
|
+
tmps_dir = styles_dir / "tmps"
|
|
165
|
+
if tmps_dir.exists():
|
|
166
|
+
for f in tmps_dir.iterdir():
|
|
167
|
+
if f.is_file() and f.suffix == ".zpl":
|
|
168
|
+
templates.append(f.stem)
|
|
169
|
+
|
|
170
|
+
return sorted(templates)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@router.post("/print", response_model=PrintResponse)
|
|
174
|
+
async def print_label(request: Request, print_req: PrintRequest) -> PrintResponse:
|
|
175
|
+
"""Send a print request to a printer."""
|
|
176
|
+
zp = request.app.state.zp
|
|
177
|
+
rate_limiter = request.app.state.print_rate_limiter
|
|
178
|
+
client_ip = request.client.host if request.client else "unknown"
|
|
179
|
+
|
|
180
|
+
# Check rate limit
|
|
181
|
+
allowed, reason = await rate_limiter.acquire(client_ip)
|
|
182
|
+
if not allowed:
|
|
183
|
+
raise HTTPException(status_code=429, detail=reason)
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
result = zp.print_zpl(
|
|
187
|
+
lab=print_req.lab,
|
|
188
|
+
printer_name=print_req.printer,
|
|
189
|
+
label_zpl_style=print_req.label_zpl_style,
|
|
190
|
+
uid_barcode=print_req.uid_barcode,
|
|
191
|
+
alt_a=print_req.alt_a,
|
|
192
|
+
alt_b=print_req.alt_b,
|
|
193
|
+
alt_c=print_req.alt_c,
|
|
194
|
+
alt_d=print_req.alt_d,
|
|
195
|
+
alt_e=print_req.alt_e,
|
|
196
|
+
alt_f=print_req.alt_f,
|
|
197
|
+
print_n=print_req.copies,
|
|
198
|
+
client_ip=client_ip,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Check if result is a PNG file path
|
|
202
|
+
if result and ".png" in str(result):
|
|
203
|
+
png_name = str(result).split("/")[-1]
|
|
204
|
+
return PrintResponse(
|
|
205
|
+
success=True,
|
|
206
|
+
message="PNG generated successfully",
|
|
207
|
+
png_url=f"/files/{png_name}",
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return PrintResponse(success=True, message="Print request sent successfully")
|
|
211
|
+
except Exception as e:
|
|
212
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
213
|
+
finally:
|
|
214
|
+
rate_limiter.release()
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@router.get("/config")
|
|
218
|
+
async def get_config(request: Request) -> Dict[str, Any]:
|
|
219
|
+
"""Get the current printer configuration."""
|
|
220
|
+
zp = request.app.state.zp
|
|
221
|
+
return zp.printers
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# ----- Lab Settings Endpoints -----
|
|
225
|
+
|
|
226
|
+
class LabUpdateRequest(BaseModel):
|
|
227
|
+
"""Request model for updating lab settings."""
|
|
228
|
+
lab_name: Optional[str] = Field(None, description="Human-readable lab name")
|
|
229
|
+
available_locations: Optional[List[str]] = Field(None, description="List of valid locations")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class PrinterUpdateRequest(BaseModel):
|
|
233
|
+
"""Request model for updating printer settings."""
|
|
234
|
+
printer_name: Optional[str] = Field(None, description="User-friendly display name")
|
|
235
|
+
lab_location: Optional[str] = Field(None, description="Location within the lab")
|
|
236
|
+
notes: Optional[str] = Field(None, description="Optional notes")
|
|
237
|
+
label_zpl_styles: Optional[List[str]] = Field(None, description="Allowed ZPL styles")
|
|
238
|
+
default_label_style: Optional[str] = Field(None, description="Default label style to use when none specified")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@router.patch("/labs/{lab}", response_model=LabInfo)
|
|
242
|
+
async def update_lab(request: Request, lab: str, update: LabUpdateRequest) -> LabInfo:
|
|
243
|
+
"""Update lab settings (lab_name, available_locations)."""
|
|
244
|
+
zp = request.app.state.zp
|
|
245
|
+
labs = zp.printers.get("labs", {})
|
|
246
|
+
|
|
247
|
+
if lab not in labs:
|
|
248
|
+
raise HTTPException(status_code=404, detail=f"Lab '{lab}' not found")
|
|
249
|
+
|
|
250
|
+
lab_data = labs[lab]
|
|
251
|
+
|
|
252
|
+
if update.lab_name is not None:
|
|
253
|
+
lab_data["lab_name"] = update.lab_name
|
|
254
|
+
if update.available_locations is not None:
|
|
255
|
+
lab_data["available_locations"] = update.available_locations
|
|
256
|
+
|
|
257
|
+
# Save changes
|
|
258
|
+
zp.save_printer_json(zp.printers_filename, relative=False)
|
|
259
|
+
|
|
260
|
+
# Return updated lab info
|
|
261
|
+
return await get_lab(request, lab)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@router.patch("/labs/{lab}/printers/{printer_id}")
|
|
265
|
+
async def update_printer(
|
|
266
|
+
request: Request, lab: str, printer_id: str, update: PrinterUpdateRequest
|
|
267
|
+
) -> PrinterInfo:
|
|
268
|
+
"""Update printer settings (printer_name, lab_location, notes)."""
|
|
269
|
+
zp = request.app.state.zp
|
|
270
|
+
labs = zp.printers.get("labs", {})
|
|
271
|
+
|
|
272
|
+
if lab not in labs:
|
|
273
|
+
raise HTTPException(status_code=404, detail=f"Lab '{lab}' not found")
|
|
274
|
+
|
|
275
|
+
lab_printers = labs[lab].get("printers", {})
|
|
276
|
+
if printer_id not in lab_printers:
|
|
277
|
+
raise HTTPException(status_code=404, detail=f"Printer '{printer_id}' not found in lab '{lab}'")
|
|
278
|
+
|
|
279
|
+
printer_data = lab_printers[printer_id]
|
|
280
|
+
|
|
281
|
+
if update.printer_name is not None:
|
|
282
|
+
printer_data["printer_name"] = update.printer_name if update.printer_name else None
|
|
283
|
+
if update.lab_location is not None:
|
|
284
|
+
printer_data["lab_location"] = update.lab_location if update.lab_location else None
|
|
285
|
+
if update.notes is not None:
|
|
286
|
+
printer_data["notes"] = update.notes
|
|
287
|
+
if update.label_zpl_styles is not None:
|
|
288
|
+
printer_data["label_zpl_styles"] = update.label_zpl_styles
|
|
289
|
+
if update.default_label_style is not None:
|
|
290
|
+
# Validate that the style exists in label_zpl_styles (if it's not empty string)
|
|
291
|
+
if update.default_label_style and update.default_label_style not in printer_data.get("label_zpl_styles", []):
|
|
292
|
+
raise HTTPException(
|
|
293
|
+
status_code=400,
|
|
294
|
+
detail=f"Default label style '{update.default_label_style}' must be one of: {printer_data.get('label_zpl_styles', [])}"
|
|
295
|
+
)
|
|
296
|
+
printer_data["default_label_style"] = update.default_label_style if update.default_label_style else None
|
|
297
|
+
|
|
298
|
+
# Save changes
|
|
299
|
+
zp.save_printer_json(zp.printers_filename, relative=False)
|
|
300
|
+
|
|
301
|
+
return PrinterInfo(
|
|
302
|
+
id=printer_id,
|
|
303
|
+
ip_address=printer_data.get("ip_address", ""),
|
|
304
|
+
printer_name=printer_data.get("printer_name"),
|
|
305
|
+
lab_location=printer_data.get("lab_location"),
|
|
306
|
+
manufacturer=printer_data.get("manufacturer", "zebra"),
|
|
307
|
+
model=printer_data.get("model", ""),
|
|
308
|
+
serial=printer_data.get("serial", ""),
|
|
309
|
+
label_zpl_styles=printer_data.get("label_zpl_styles", []),
|
|
310
|
+
default_label_style=printer_data.get("default_label_style"),
|
|
311
|
+
print_method=printer_data.get("print_method", ""),
|
|
312
|
+
notes=printer_data.get("notes", ""),
|
|
313
|
+
)
|