Files
minstrel/internal/api/admin_coverage.go
T
bvandeusenandClaude Opus 5.5 08a2c06b85 docs: api comments say what the endpoints do, not how they came to be (#6277)
Rewrites the internal/api comment blocks to rule 32: task, milestone and
rule ids gone, the stories behind them restated as the constraint they
left, stale references corrected (the stream secret is wired from config,
the timezone handler already refreshes the scheduler, the Android app is
the native client, the design docs cited are not in the repo). Also
rewraps comment lines over 80 columns left by the earlier batches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:23:58 -04:00

85 lines
3.1 KiB
Go

package api
import (
"context"
"net/http"
"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
"git.fabledsword.com/bvandeusen/minstrel/internal/library"
)
// coverageRollupResp is the wire shape for GET /api/admin/library/coverage.
// All fields are non-negative counts. with_art + pending + settled = total;
// pending_no_mbid is a subset of pending.
type coverageRollupResp struct {
Total int64 `json:"total"`
WithArt int64 `json:"with_art"`
Pending int64 `json:"pending"`
Settled int64 `json:"settled"`
PendingNoMbid int64 `json:"pending_no_mbid"`
}
// handleGetLibraryCoverage implements GET /api/admin/library/coverage.
// Surfaces a library-wide rollup of albums.cover_art_source for the admin
// dashboard gauge. Always 200; zeros on an empty library.
func (h *handlers) handleGetLibraryCoverage(w http.ResponseWriter, r *http.Request) {
q := dbq.New(h.pool)
row, err := q.GetAlbumCoverageRollup(r.Context())
if err != nil {
writeErrWithLog(w, h.logger, "admin: get coverage rollup", apierror.InternalMsg("lookup failed", err))
return
}
writeJSON(w, http.StatusOK, coverageRollupResp{
Total: row.Total,
WithArt: row.WithArt,
Pending: row.Pending,
Settled: row.Settled,
PendingNoMbid: row.PendingNoMbid,
})
}
// fingerprintCoverageResp is the wire shape for GET
// /api/admin/library/fingerprints. fingerprinted + rejected + pending = total.
// Missing tracks are not counted: there is no file to fingerprint. Enabled
// travels with the counts because with fingerprinting off pending never
// shrinks, and a gauge that implies progress would be promising work nothing is
// doing.
type fingerprintCoverageResp struct {
Total int64 `json:"total"`
Fingerprinted int64 `json:"fingerprinted"`
Rejected int64 `json:"rejected"`
Pending int64 `json:"pending"`
Enabled bool `json:"enabled"`
}
// fingerprintCoverage reads the gauge against the current settings: a print at
// another length counts as pending, because the backfill will re-derive it.
func (h *handlers) fingerprintCoverage(ctx context.Context) (fingerprintCoverageResp, error) {
cfg := h.fingerprintSettings.Get()
row, err := library.FingerprintCoverage(ctx, h.pool, cfg)
if err != nil {
return fingerprintCoverageResp{}, err
}
return fingerprintCoverageResp{
Total: row.Total,
Fingerprinted: row.Fingerprinted,
Rejected: row.Rejected,
Pending: row.Pending,
Enabled: cfg.Enabled,
}, nil
}
// handleGetFingerprintCoverage implements GET /api/admin/library/fingerprints:
// how far the fingerprint backfill has got. The backfill is its own
// worker spanning many passes, with no scan run to attach a tally to, so its
// progress is read live here. Always 200; zeros on an empty library.
func (h *handlers) handleGetFingerprintCoverage(w http.ResponseWriter, r *http.Request) {
cov, err := h.fingerprintCoverage(r.Context())
if err != nil {
writeErrWithLog(w, h.logger, "admin: get fingerprint coverage", apierror.InternalMsg("lookup failed", err))
return
}
writeJSON(w, http.StatusOK, cov)
}