|
| 1 | +"""Best-effort Batch-Dispatcher: validiert + queued pro Item, sammelt Errors.""" |
| 2 | +from __future__ import annotations |
| 3 | + |
| 4 | +import logging |
| 5 | + |
| 6 | +from app.printer_backends.exceptions import ( |
| 7 | + PrinterCoverOpenError, |
| 8 | + PrinterOfflineError, |
| 9 | + SnmpQueryError, |
| 10 | + TapeEmptyError, |
| 11 | + TapeMismatchError, |
| 12 | +) |
| 13 | +from app.schemas.print_batch import BatchError |
| 14 | +from app.schemas.print_request import PrintRequest |
| 15 | +from app.services.lookup_service import LookupFailedError |
| 16 | +from app.services.template_loader import TemplateNotFoundError |
| 17 | + |
| 18 | +_log = logging.getLogger(__name__) |
| 19 | + |
| 20 | +# Per-item errors → collected into BatchError list (best-effort) |
| 21 | +_PER_ITEM_ERRORS: dict[type[Exception], str] = { |
| 22 | + TemplateNotFoundError: "template_not_found", |
| 23 | + LookupFailedError: "integration_lookup_failed", |
| 24 | + TapeMismatchError: "tape_mismatch", |
| 25 | + TapeEmptyError: "tape_empty", |
| 26 | +} |
| 27 | + |
| 28 | +# Hardware preconditions → propagate (caller returns 409) |
| 29 | +_BATCH_FATAL_ERRORS: tuple[type[Exception], ...] = ( |
| 30 | + PrinterCoverOpenError, |
| 31 | + PrinterOfflineError, |
| 32 | + SnmpQueryError, |
| 33 | +) |
| 34 | + |
| 35 | + |
| 36 | +async def dispatch_batch( |
| 37 | + service, # PrintService (duck-typed) |
| 38 | + items: list[PrintRequest], |
| 39 | +) -> tuple[list[str], list[BatchError]]: |
| 40 | + """Queue each item individually. Collect per-item errors. |
| 41 | + Hardware errors propagate.""" |
| 42 | + job_ids: list[str] = [] |
| 43 | + errors: list[BatchError] = [] |
| 44 | + |
| 45 | + for index, item in enumerate(items): |
| 46 | + try: |
| 47 | + job_id = await service.submit_print_job(item) |
| 48 | + job_ids.append(str(job_id)) |
| 49 | + except _BATCH_FATAL_ERRORS: |
| 50 | + raise |
| 51 | + except tuple(_PER_ITEM_ERRORS) as exc: |
| 52 | + code = _PER_ITEM_ERRORS[type(exc)] |
| 53 | + detail = None |
| 54 | + if isinstance(exc, TapeMismatchError): |
| 55 | + detail = {"expected_mm": exc.expected_mm, |
| 56 | + "loaded_mm": exc.loaded_mm} |
| 57 | + errors.append(BatchError( |
| 58 | + index=index, error_code=code, |
| 59 | + error_message=str(exc), error_detail=detail, |
| 60 | + )) |
| 61 | + except Exception as exc: # unknown sync failure |
| 62 | + _log.exception("unexpected error in batch item %d", index) |
| 63 | + errors.append(BatchError( |
| 64 | + index=index, error_code="internal_error", |
| 65 | + error_message=str(exc), |
| 66 | + )) |
| 67 | + |
| 68 | + return job_ids, errors |
0 commit comments