zebra-day 1.0.2__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.
Files changed (86) hide show
  1. zebra_day/cli/gui.py +89 -6
  2. zebra_day/etc/printer_config.json +2 -14
  3. zebra_day/etc/printer_config.template.json +17 -9
  4. zebra_day/etc/tmp_printers120.json +10 -0
  5. zebra_day/etc/tmp_printers145.json +10 -0
  6. zebra_day/etc/tmp_printers207.json +10 -0
  7. zebra_day/etc/tmp_printers469.json +10 -0
  8. zebra_day/etc/tmp_printers485.json +10 -0
  9. zebra_day/etc/tmp_printers531.json +10 -0
  10. zebra_day/etc/tmp_printers540.json +10 -0
  11. zebra_day/etc/tmp_printers542.json +10 -0
  12. zebra_day/etc/tmp_printers552.json +10 -0
  13. zebra_day/etc/tmp_printers715.json +10 -0
  14. zebra_day/etc/tmp_printers972.json +10 -0
  15. zebra_day/files/blank_preview.png +0 -0
  16. zebra_day/files/corners_20cmX30cm_preview.png +0 -0
  17. zebra_day/files/generic_2inX1in_preview.png +0 -0
  18. zebra_day/files/test_png_12020.png +0 -0
  19. zebra_day/files/test_png_12352.png +0 -0
  20. zebra_day/files/test_png_15472.png +0 -0
  21. zebra_day/files/test_png_24493.png +0 -0
  22. zebra_day/files/test_png_30069.png +0 -0
  23. zebra_day/files/test_png_47791.png +0 -0
  24. zebra_day/files/test_png_47799.png +0 -0
  25. zebra_day/files/test_png_55588.png +0 -0
  26. zebra_day/files/test_png_58809.png +0 -0
  27. zebra_day/files/test_png_67242.png +0 -0
  28. zebra_day/files/test_png_89893.png +0 -0
  29. zebra_day/files/tube_20mmX30mmA_preview.png +0 -0
  30. zebra_day/print_mgr.py +136 -80
  31. zebra_day/templates/modern/config_backups.html +59 -0
  32. zebra_day/templates/modern/config_editor.html +95 -0
  33. zebra_day/templates/modern/config_new.html +93 -0
  34. zebra_day/templates/modern/print_request.html +9 -5
  35. zebra_day/templates/modern/printer_detail.html +161 -34
  36. zebra_day/templates/modern/printers.html +17 -6
  37. zebra_day/templates/modern/template_editor.html +7 -4
  38. zebra_day/web/app.py +84 -7
  39. zebra_day/web/routers/api.py +155 -5
  40. zebra_day/web/routers/ui.py +155 -570
  41. {zebra_day-1.0.2.dist-info → zebra_day-2.0.0.dist-info}/METADATA +74 -13
  42. {zebra_day-1.0.2.dist-info → zebra_day-2.0.0.dist-info}/RECORD +46 -57
  43. zebra_day/bin/fetch_zebra_config.py +0 -15
  44. zebra_day/bin/generate_coord_grid_zpl.py +0 -50
  45. zebra_day/bin/print_zpl_from_file.py +0 -21
  46. zebra_day/bin/probe_new_label_dimensions.py +0 -75
  47. zebra_day/bin/scan_for_networed_zebra_printers.py +0 -23
  48. zebra_day/bin/scan_for_networed_zebra_printers_arp_scan.sh +0 -1
  49. zebra_day/bin/scan_for_networed_zebra_printers_curl.sh +0 -30
  50. zebra_day/bin/zserve.py +0 -1062
  51. zebra_day/templates/base.html +0 -36
  52. zebra_day/templates/bpr.html +0 -72
  53. zebra_day/templates/build_new_config.html +0 -36
  54. zebra_day/templates/build_print_request.html +0 -32
  55. zebra_day/templates/chg_ui_style.html +0 -19
  56. zebra_day/templates/edit_template.html +0 -128
  57. zebra_day/templates/edit_zpl.html +0 -37
  58. zebra_day/templates/index.html +0 -82
  59. zebra_day/templates/legacy/base.html +0 -37
  60. zebra_day/templates/legacy/bpr.html +0 -72
  61. zebra_day/templates/legacy/build_new_config.html +0 -36
  62. zebra_day/templates/legacy/build_print_request.html +0 -32
  63. zebra_day/templates/legacy/chg_ui_style.html +0 -19
  64. zebra_day/templates/legacy/edit_template.html +0 -128
  65. zebra_day/templates/legacy/edit_zpl.html +0 -37
  66. zebra_day/templates/legacy/index.html +0 -82
  67. zebra_day/templates/legacy/list_prior_configs.html +0 -24
  68. zebra_day/templates/legacy/print_result.html +0 -30
  69. zebra_day/templates/legacy/printer_details.html +0 -25
  70. zebra_day/templates/legacy/printer_status.html +0 -70
  71. zebra_day/templates/legacy/save_result.html +0 -17
  72. zebra_day/templates/legacy/send_print_request.html +0 -34
  73. zebra_day/templates/legacy/simple_print.html +0 -94
  74. zebra_day/templates/legacy/view_pstation_json.html +0 -29
  75. zebra_day/templates/list_prior_configs.html +0 -24
  76. zebra_day/templates/print_result.html +0 -30
  77. zebra_day/templates/printer_details.html +0 -25
  78. zebra_day/templates/printer_status.html +0 -70
  79. zebra_day/templates/save_result.html +0 -17
  80. zebra_day/templates/send_print_request.html +0 -34
  81. zebra_day/templates/simple_print.html +0 -94
  82. zebra_day/templates/view_pstation_json.html +0 -29
  83. {zebra_day-1.0.2.dist-info → zebra_day-2.0.0.dist-info}/WHEEL +0 -0
  84. {zebra_day-1.0.2.dist-info → zebra_day-2.0.0.dist-info}/entry_points.txt +0 -0
  85. {zebra_day-1.0.2.dist-info → zebra_day-2.0.0.dist-info}/licenses/LICENSE +0 -0
  86. {zebra_day-1.0.2.dist-info → zebra_day-2.0.0.dist-info}/top_level.txt +0 -0
@@ -2,9 +2,7 @@
2
2
  UI router for zebra_day web interface.
3
3
 
4
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
5
+ All routes use the modern UI design with responsive layouts.
8
6
  """
9
7
  from __future__ import annotations
10
8
 
@@ -29,7 +27,7 @@ router = APIRouter()
29
27
 
30
28
 
31
29
  def get_template_context(request: Request, **kwargs) -> dict:
32
- """Build common template context for legacy templates."""
30
+ """Build common template context for templates."""
33
31
  return {
34
32
  "request": request,
35
33
  "css_theme": f"static/{request.app.state.css_theme}",
@@ -73,7 +71,8 @@ def get_templates_list(pkg_path: Path) -> tuple[list, list]:
73
71
  def get_stats(zp, pkg_path: Path) -> dict:
74
72
  """Calculate dashboard statistics."""
75
73
  labs = zp.printers.get("labs", {})
76
- total_printers = sum(len(printers) for printers in labs.values())
74
+ # Count printers via nested 'printers' key (v2 schema)
75
+ total_printers = sum(len(lab_data.get("printers", {})) for lab_data in labs.values())
77
76
  stable, draft = get_templates_list(pkg_path)
78
77
 
79
78
  # Count backup files
@@ -141,15 +140,23 @@ async def modern_printers_by_lab(request: Request, lab: str):
141
140
  if lab not in zp.printers.get("labs", {}):
142
141
  raise HTTPException(status_code=404, detail=f"Lab '{lab}' not found")
143
142
 
143
+ lab_data = zp.printers["labs"][lab]
144
+ lab_printers = lab_data.get("printers", {})
145
+
144
146
  printers = []
145
- for name, info in zp.printers["labs"][lab].items():
147
+ for name, info in lab_printers.items():
146
148
  printers.append({
147
- "name": name,
149
+ "id": name,
150
+ "name": info.get("printer_name") or name, # Display name or fallback to ID
151
+ "printer_name": info.get("printer_name"),
148
152
  "ip_address": info.get("ip_address", ""),
153
+ "lab_location": info.get("lab_location"),
154
+ "manufacturer": info.get("manufacturer", "zebra"),
149
155
  "model": info.get("model", ""),
150
156
  "serial": info.get("serial", ""),
151
157
  "label_zpl_styles": info.get("label_zpl_styles", []),
152
158
  "status": "online" if info.get("ip_address") else "unknown",
159
+ "notes": info.get("notes", ""),
153
160
  })
154
161
 
155
162
  ip_root = ".".join(request.app.state.local_ip.split(".")[:-1])
@@ -160,23 +167,29 @@ async def modern_printers_by_lab(request: Request, lab: str):
160
167
  labs=list(zp.printers.get("labs", {}).keys()),
161
168
  printers=printers,
162
169
  lab=lab,
170
+ lab_name=lab_data.get("lab_name", lab),
171
+ available_locations=lab_data.get("available_locations", []),
163
172
  ip_root=ip_root,
164
173
  )
165
174
  return templates.TemplateResponse("modern/printers.html", context)
166
175
 
167
176
 
168
- @router.get("/printers/{lab}/{printer_name}", response_class=HTMLResponse)
169
- async def modern_printer_detail(request: Request, lab: str, printer_name: str):
177
+ @router.get("/printers/{lab}/{printer_id}", response_class=HTMLResponse)
178
+ async def modern_printer_detail(request: Request, lab: str, printer_id: str):
170
179
  """Modern printer detail page."""
171
180
  zp = request.app.state.zp
172
181
  templates = request.app.state.templates
173
182
 
174
183
  if lab not in zp.printers.get("labs", {}):
175
184
  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
185
 
179
- printer_info = zp.printers["labs"][lab][printer_name]
186
+ lab_data = zp.printers["labs"][lab]
187
+ lab_printers = lab_data.get("printers", {})
188
+
189
+ if printer_id not in lab_printers:
190
+ raise HTTPException(status_code=404, detail=f"Printer '{printer_id}' not found")
191
+
192
+ printer_info = lab_printers[printer_id]
180
193
 
181
194
  # Try to get printer configuration
182
195
  printer_config = ""
@@ -190,8 +203,11 @@ async def modern_printer_detail(request: Request, lab: str, printer_name: str):
190
203
  context = get_modern_context(
191
204
  request,
192
205
  active_page="printers",
193
- printer_name=printer_name,
206
+ printer_id=printer_id,
207
+ printer_name=printer_info.get("printer_name") or printer_id,
194
208
  lab=lab,
209
+ lab_name=lab_data.get("lab_name", lab),
210
+ available_locations=lab_data.get("available_locations", []),
195
211
  printer_info=printer_info,
196
212
  printer_config=printer_config,
197
213
  )
@@ -278,6 +294,53 @@ async def modern_template_edit(
278
294
  return templates.TemplateResponse("modern/template_editor.html", context)
279
295
 
280
296
 
297
+ @router.get("/templates/preview")
298
+ async def modern_template_preview(
299
+ request: Request,
300
+ filename: str,
301
+ dtype: str = "",
302
+ ):
303
+ """Generate a PNG preview of a ZPL template.
304
+
305
+ Returns the PNG image directly or redirects to the generated file.
306
+ """
307
+ zp = request.app.state.zp
308
+ pkg_path = request.app.state.pkg_path
309
+
310
+ # Find the template file
311
+ if dtype:
312
+ filepath = pkg_path / "etc" / "label_styles" / dtype / filename
313
+ else:
314
+ # Try with .zpl extension if not provided
315
+ if not filename.endswith(".zpl"):
316
+ filepath = pkg_path / "etc" / "label_styles" / f"{filename}.zpl"
317
+ else:
318
+ filepath = pkg_path / "etc" / "label_styles" / filename
319
+
320
+ if not filepath.exists():
321
+ raise HTTPException(status_code=404, detail=f"Template '{filename}' not found")
322
+
323
+ try:
324
+ # Read template content
325
+ zpl_content = filepath.read_text()
326
+
327
+ # Generate PNG preview
328
+ output_dir = pkg_path / "files"
329
+ output_dir.mkdir(parents=True, exist_ok=True)
330
+
331
+ # Use template name for output file
332
+ template_name = filepath.stem
333
+ output_path = output_dir / f"{template_name}_preview.png"
334
+
335
+ result = zp.generate_label_png(zpl_content, str(output_path), False)
336
+
337
+ # Return redirect to the generated file
338
+ return RedirectResponse(url=f"/files/{template_name}_preview.png", status_code=303)
339
+
340
+ except Exception as e:
341
+ raise HTTPException(status_code=500, detail=f"Preview generation failed: {e}")
342
+
343
+
281
344
  @router.get("/config", response_class=HTMLResponse)
282
345
  async def modern_config(request: Request):
283
346
  """Modern configuration page."""
@@ -309,426 +372,76 @@ async def modern_config(request: Request):
309
372
 
310
373
  @router.get("/config/view", response_class=HTMLResponse)
311
374
  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."""
375
+ """View printer configuration JSON."""
482
376
  zp = request.app.state.zp
483
377
  templates = request.app.state.templates
484
378
 
485
- labs = list(zp.printers.get("labs", {}).keys())
379
+ config_json = json.dumps(zp.printers, indent=4)
486
380
 
487
381
  context = get_template_context(
488
382
  request,
489
- title="Zebra Day - Home",
490
- labs=labs,
383
+ title="View Configuration",
384
+ config_json=config_json,
385
+ mode="view",
491
386
  )
492
- return templates.TemplateResponse("legacy/index.html", context)
387
+ return templates.TemplateResponse("modern/config_editor.html", context)
493
388
 
494
389
 
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."""
390
+ @router.get("/config/edit", response_class=HTMLResponse)
391
+ async def modern_config_edit(request: Request, error_msg: Optional[str] = None):
392
+ """Edit printer configuration JSON."""
664
393
  zp = request.app.state.zp
665
394
  templates = request.app.state.templates
666
395
 
667
- config_data = json.dumps(zp.printers, indent=4)
396
+ config_json = json.dumps(zp.printers, indent=4)
668
397
 
669
398
  context = get_template_context(
670
399
  request,
671
- title="View Printer Config JSON",
672
- config_data=config_data,
400
+ title="Edit Configuration",
401
+ config_json=config_json,
402
+ mode="edit",
673
403
  error_msg=error_msg,
674
404
  )
675
- return templates.TemplateResponse("legacy/view_pstation_json.html", context)
405
+ return templates.TemplateResponse("modern/config_editor.html", context)
676
406
 
677
407
 
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."""
408
+ @router.post("/config/save")
409
+ async def modern_config_save(request: Request, json_data: str = Form(...)):
410
+ """Save edited printer configuration JSON."""
681
411
  zp = request.app.state.zp
412
+ pkg_path = request.app.state.pkg_path
682
413
 
683
414
  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
- )
415
+ # Validate JSON
416
+ new_config = json.loads(json_data)
690
417
 
691
- try:
692
- # Backup and save
693
- zp.save_printer_json()
418
+ # Backup current config
419
+ json_file = pkg_path / "etc" / "printer_config.json"
420
+ if json_file.exists():
421
+ bkup_dir = pkg_path / "etc" / "old_printer_config"
422
+ bkup_dir.mkdir(parents=True, exist_ok=True)
423
+ rec_date = str(datetime.now()).replace(" ", "_")
424
+ bkup_file = bkup_dir / f"printer_config.{rec_date}.json"
425
+ bkup_file.write_text(json_file.read_text())
694
426
 
695
- # Write new config
696
- with open(zp.printers_filename, "w") as f:
697
- json.dump(data, f, indent=4)
427
+ # Save new config
428
+ json_file.write_text(json.dumps(new_config, indent=4))
698
429
 
699
430
  # Reload config
700
431
  zp.load_printer_json(json_file=zp.printers_filename, relative=False)
701
432
 
702
- return RedirectResponse(url="/legacy", status_code=303)
433
+ return RedirectResponse(url="/config", status_code=303)
703
434
 
704
- except Exception as e:
435
+ except json.JSONDecodeError as e:
705
436
  return RedirectResponse(
706
- url=f"/legacy/view_pstation_json?error_msg=Error+saving:+{str(e)}",
707
- status_code=303,
437
+ url=f"/config/edit?error_msg=Invalid JSON: {e}",
438
+ status_code=303
708
439
  )
709
440
 
710
441
 
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."""
442
+ @router.get("/config/backups", response_class=HTMLResponse)
443
+ async def modern_config_backups(request: Request):
444
+ """List prior config files."""
732
445
  templates = request.app.state.templates
733
446
  pkg_path = request.app.state.pkg_path
734
447
  bkup_dir = pkg_path / "etc" / "old_printer_config"
@@ -741,15 +454,15 @@ async def legacy_list_prior_printer_config_files(request: Request):
741
454
 
742
455
  context = get_template_context(
743
456
  request,
744
- title="Prior Printer Config Files",
457
+ title="Configuration Backups",
745
458
  backup_files=backup_files,
746
459
  )
747
- return templates.TemplateResponse("legacy/list_prior_configs.html", context)
460
+ return templates.TemplateResponse("modern/config_backups.html", context)
748
461
 
749
462
 
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."""
463
+ @router.get("/config/new", response_class=HTMLResponse)
464
+ async def modern_config_new(request: Request):
465
+ """Build new config page."""
753
466
  zp = request.app.state.zp
754
467
  templates = request.app.state.templates
755
468
 
@@ -757,107 +470,49 @@ async def legacy_build_new_printers_config_json(request: Request):
757
470
 
758
471
  context = get_template_context(
759
472
  request,
760
- title="Scan Network for Printers",
473
+ title="New Configuration",
761
474
  ip_root=ip_root,
762
475
  labs=list(zp.printers.get("labs", {}).keys()),
763
476
  )
764
- return templates.TemplateResponse("legacy/build_new_config.html", context)
477
+ return templates.TemplateResponse("modern/config_new.html", context)
765
478
 
766
479
 
767
- @router.get("/legacy/probe_zebra_printers_add_to_printers_json")
768
- async def legacy_probe_zebra_printers(
480
+ @router.get("/config/reset")
481
+ async def modern_config_reset(request: Request):
482
+ """Reset printer config from template."""
483
+ zp = request.app.state.zp
484
+ zp.replace_printer_json_from_template()
485
+ time.sleep(1.2)
486
+ return RedirectResponse(url="/config", status_code=303)
487
+
488
+
489
+ @router.get("/config/clear")
490
+ async def modern_config_clear(request: Request):
491
+ """Clear the printer configuration JSON."""
492
+ zp = request.app.state.zp
493
+ zp.clear_printers_json()
494
+ time.sleep(1.2)
495
+ return RedirectResponse(url="/config", status_code=303)
496
+
497
+
498
+ @router.get("/config/scan", response_class=HTMLResponse)
499
+ async def modern_config_scan(
769
500
  request: Request,
770
501
  ip_stub: str = "192.168.1",
771
502
  scan_wait: str = "0.25",
772
503
  lab: str = "scan-results",
773
504
  ):
774
- """Legacy probe network for Zebra printers and add to config."""
505
+ """Scan network for printers."""
775
506
  zp = request.app.state.zp
776
507
  zp.probe_zebra_printers_add_to_printers_json(
777
508
  ip_stub=ip_stub, scan_wait=scan_wait, lab=lab
778
509
  )
779
510
  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)
511
+ return RedirectResponse(url=f"/printers/{lab}", status_code=303)
857
512
 
858
513
 
859
- @router.get("/legacy/_print_label", response_class=HTMLResponse)
860
- async def legacy_print_label(
514
+ @router.get("/_print_label", response_class=HTMLResponse)
515
+ async def modern_print_label(
861
516
  request: Request,
862
517
  lab: Optional[str] = None,
863
518
  printer: str = "",
@@ -872,7 +527,7 @@ async def legacy_print_label(
872
527
  alt_f: str = "",
873
528
  labSelect: str = "",
874
529
  ):
875
- """Legacy execute print request."""
530
+ """Execute print request - modern UI."""
876
531
  zp = request.app.state.zp
877
532
  templates = request.app.state.templates
878
533
  rate_limiter = request.app.state.print_rate_limiter
@@ -912,53 +567,18 @@ async def legacy_print_label(
912
567
  png_name = str(result).split("/")[-1]
913
568
  png_url = f"/files/{png_name}"
914
569
 
915
- context = get_template_context(
570
+ context = get_modern_context(
916
571
  request,
917
572
  title="Print Result",
918
573
  success=True,
919
574
  full_url=full_url,
920
575
  png_url=png_url,
921
576
  )
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)
577
+ return templates.TemplateResponse("modern/print_result.html", context)
958
578
 
959
579
 
960
- @router.post("/legacy/save", response_class=HTMLResponse)
961
- async def legacy_save_template(
580
+ @router.post("/save", response_class=HTMLResponse)
581
+ async def modern_save_template(
962
582
  request: Request,
963
583
  filename: str = Form(...),
964
584
  content: str = Form(...),
@@ -966,7 +586,7 @@ async def legacy_save_template(
966
586
  lab: str = Form(""),
967
587
  printer: str = Form(""),
968
588
  ):
969
- """Legacy save ZPL template as a new draft file."""
589
+ """Save ZPL template as a new draft file - modern UI."""
970
590
  templates = request.app.state.templates
971
591
  pkg_path = request.app.state.pkg_path
972
592
 
@@ -979,16 +599,16 @@ async def legacy_save_template(
979
599
  temp_filepath = tmps_dir / new_filename
980
600
  temp_filepath.write_text(content)
981
601
 
982
- context = get_template_context(
602
+ context = get_modern_context(
983
603
  request,
984
604
  title="Template Saved",
985
605
  new_filename=new_filename,
986
606
  )
987
- return templates.TemplateResponse("legacy/save_result.html", context)
607
+ return templates.TemplateResponse("modern/save_result.html", context)
988
608
 
989
609
 
990
- @router.post("/legacy/png_renderer")
991
- async def legacy_png_renderer(
610
+ @router.post("/png_renderer")
611
+ async def modern_png_renderer(
992
612
  request: Request,
993
613
  filename: str = Form(...),
994
614
  content: str = Form(...),
@@ -996,7 +616,7 @@ async def legacy_png_renderer(
996
616
  printer: str = Form(""),
997
617
  ftag: str = Form(""),
998
618
  ):
999
- """Legacy render ZPL content to PNG."""
619
+ """Render ZPL content to PNG - modern UI."""
1000
620
  zp = request.app.state.zp
1001
621
  pkg_path = request.app.state.pkg_path
1002
622
 
@@ -1014,38 +634,3 @@ async def legacy_png_renderer(
1014
634
  content=f"files/{Path(png_tmp_f).name}",
1015
635
  media_type="text/plain",
1016
636
  )
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"}