Files
bambuddy/backend/app/schemas/print_log.py
maziggy bebdb38e41 feat(print-log): per-row classification editor + fix silent-drop in GET
(#1687 part 4, reported by @IndividualGhost1905)

  Reporter clarified after part 1 shipped that point 2 wasn't about
  archive `tags` (which describe the model — home decor, toys), but
  about failure-cause classification on the *log* row itself:
  spaghetti, jam, bed-adhesion, etc. Different surface, different
  lifetime.

  The data field he wanted already existed. PrintLogEntry.failure_reason
  is a String(100); the Failure Analysis widget already groups by it;
  the Archive Edit modal already mirrors archive.failure_reason into
  the most recent log entry (archives.py:1421, shipped with #1444).
  The only gaps were:

  1. The GET endpoint silently dropped failure_reason (and archive_id
     and created_by_id) from PrintLogEntrySchema construction even
     when set in the DB — so the Print Log table couldn't render what
     the Failure Analysis widget grouped by. Fixed independently of
     the editor; regression test added.

  2. Orphan log entries (no archive — dispatch errors, aborts before
     archive creation, manual entries) had no edit path at all because
     the Archive Edit modal cannot reach them. The new endpoint is
     the only way to classify those rows.

  Changes:

  - Backend: new PATCH /print-log/{entry_id} taking
    {failure_reason, status}, gated on require_ownership_permission(
    ARCHIVES_UPDATE_ALL, ARCHIVES_UPDATE_OWN) — same ownership shape
    as the per-row DELETE. Validates against the same 11-key failure
    vocabulary and 5-key status set the Archive Edit modal uses;
    unknown values return 400 rather than getting stored as raw text
    (the i18n layer maps the value back through the vocabulary,
    unrecognised values would render as literal strings).
    Empty-string failure_reason stores back as NULL so the column's
    nullable=True intent is preserved end-to-end. GET endpoint now
    surfaces failure_reason, archive_id, created_by_id.

  - Frontend: FAILURE_REASON_KEYS moved to an export from
    EditArchiveModal.tsx so the new editor reuses the exact same
    vocabulary — backend and frontend stay in lockstep. Pencil icon
    beside the existing trash icon on every Print Log row, opens a
    compact two-field modal (status + failure reason). Save
    invalidates print-log and archives-stats query keys so the
    Failure Analysis widget reflects the re-classification on the
    same response cycle. Failure reason rendered as a sub-label under
    the status badge, matching PrintLogTable.tsx's convention.

  - i18n: 10 new keys (editEntryTitle, editEntryDescription,
    entryUpdated, entryUpdateFailed, archives.permission.noEdit, plus
    a 5-key statuses block) translated across all 11 locales. No
    English fallbacks.

  - Wiki: features/print-log.md gains per-row actions section,
    updated permissions table, PATCH/single-DELETE endpoint docs.
2026-06-09 11:21:03 +02:00

45 lines
1.3 KiB
Python

from datetime import datetime
from pydantic import BaseModel
class PrintLogEntrySchema(BaseModel):
id: int
archive_id: int | None = None
print_name: str | None = None
printer_name: str | None = None
printer_id: int | None = None
status: str
started_at: datetime | None = None
completed_at: datetime | None = None
duration_seconds: int | None = None
filament_type: str | None = None
filament_color: str | None = None
filament_used_grams: float | None = None
cost: float | None = None
energy_kwh: float | None = None
energy_cost: float | None = None
failure_reason: str | None = None
thumbnail_path: str | None = None
created_by_id: int | None = None
created_by_username: str | None = None
created_at: datetime
class PrintLogResponse(BaseModel):
items: list[PrintLogEntrySchema]
total: int
class PrintLogEntryUpdate(BaseModel):
"""Per-row classification edits (#1687 part 4 — IndividualGhost1905).
Lets the user set ``failure_reason`` (and re-classify ``status``) directly
on a Print Log row, including on orphan entries that have no archive to
edit through. The Failure Analysis widget already groups by
``PrintLogEntry.failure_reason``, so this just plugs the editor gap.
"""
failure_reason: str | None = None
status: str | None = None