"""Exports one event's final board as a PNG. Reads the live Redis canvas rather than replaying the history like gif.py - once placement is over the canvas endpoint serves a wiped board, but Redis still holds the real final one (see tasks.py). """ from io import BytesIO from events.models import Event from .canvas import read_canvas from .gif import _render_frame from .models import PlaceSettings def build_final_png(event: Event) -> bytes: """Renders `event`'s current board as a PNG, upscaled like gif.py. Redis only pads up to the last written pixel, so a short canvas is padded with white (and a longer one, e.g. from before a shrink, cut) to the current grid dimensions.""" place_settings = PlaceSettings.current(event) width, height = place_settings.width, place_settings.height size = width * height canvas = read_canvas(event.path, width, height)[:size].ljust(size, b"\0") buffer = BytesIO() _render_frame(bytearray(canvas), width, height).save(buffer, format="PNG") return buffer.getvalue()