Skip to content

Add admin timelapse GIF export of round status - #181

Merged
dbaseqp merged 3 commits into
dbaseqp:mainfrom
tire-fire:feat/timelapse-export
Nov 7, 2026
Merged

dbaseqp merged 3 commits into
dbaseqp:mainfrom
tire-fire:feat/timelapse-export

Conversation

@tire-fire

@tire-fire tire-fire commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Adds an Export Timelapse button to the admin engine page. It downloads an animated GIF of the service status heatmap, with one frame per round, so you can watch how every active team's services went up and down over the whole event.

How it works

  • GET /api/engine/export/timelapse (admin only) loads the round, team, service and result columns for the active teams with one query (GetCheckResults). It skips the error and debug text.
  • The new www/timelapse package lays out the image and encodes it with image/gif:
    • Rows are teams and columns are services. Both are sorted in the same order as the /graphs heatmap, using golang.org/x/text/collate, which matches the browser's localeCompare.
    • Each cell shows the same up, down and unknown icons as the /graphs status heatmap (static/assets/services/*.png), loaded from those files so the two can't drift apart. Their colors and the 55° service-label angle match /graphs.
    • Text uses the Go fonts (anti-aliased Go Regular for labels, Go Bold for the round number) so rotated service names stay readable.
    • Every frame after the first holds only the region that changed, with unchanged pixels transparent. This keeps the file and memory small.
    • Long events are sampled evenly down to 300 frames, always keeping the first and last round. Each frame's title shows the real round number.
  • Limits:
    • Team and service names are cut to 32 characters, so one very long name can't blow up the image.
    • Images over 2M pixels are refused with an error rather than drawn.
  • All validation runs before any response bytes are written, so errors come back as JSON. The button uses fetch and shows the error message instead of saving a broken file.

Dependencies

  • golang.org/x/image for the Go fonts, TrueType rendering and label rotation.
  • golang.org/x/text, already an indirect dependency, is now a direct one.

Tests

  • www/timelapse:
    • decodes the GIF with image/gif, composites the frames, and checks every cell's color in every round, plus the frame delays and the loop count;
    • a rapid property test with random names and statuses that also checks nothing is drawn on the image edge (no clipped labels);
    • sampling, size limits, name shortening, and writer errors.
  • www/api: the sort order against a list taken from localeCompare.
  • tests/integration: GetCheckResults ordering and team filtering.

@dbaseqp
dbaseqp merged commit 6a51d6d into dbaseqp:main Nov 7, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants