#142: fix stale filter count cutoff, tighten comments (review fixes)
This commit is contained in:
@@ -19,8 +19,7 @@ type LaneReporter interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ownerWindow is the staleness boundary the Series list's "not checked in
|
// ownerWindow is the staleness boundary the Series list's "not checked in
|
||||||
// 12h" filter compares against, and the same twelve hours the landing page's
|
// 12h" filter compares against. Declared once; later admin tickets read it.
|
||||||
// figures share. Declared once; later admin tickets read it.
|
|
||||||
const ownerWindow = 12 * time.Hour
|
const ownerWindow = 12 * time.Hour
|
||||||
|
|
||||||
// adminView is the shared shell data for an administrative page and the roster
|
// adminView is the shared shell data for an administrative page and the roster
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ func (h *Handler) seriesListView(r *http.Request) (seriesListView, error) {
|
|||||||
for i, a := range data.Rows {
|
for i, a := range data.Rows {
|
||||||
view.Rows = append(view.Rows, seriesRow(a, i, now))
|
view.Rows = append(view.Rows, seriesRow(a, i, now))
|
||||||
}
|
}
|
||||||
view.Filters, err = h.seriesFilterOptions(filter)
|
view.Filters, err = h.seriesFilterOptions(filter, sf.Cutoff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return seriesListView{}, err
|
return seriesListView{}, err
|
||||||
}
|
}
|
||||||
@@ -181,11 +181,12 @@ func (h *Handler) seriesListView(r *http.Request) (seriesListView, error) {
|
|||||||
// count, one SeriesShapes pass per filter summed in Go — the shipped surface
|
// count, one SeriesShapes pass per filter summed in Go — the shipped surface
|
||||||
// offers eight grouped passes, not a single stats query (#140). The counts
|
// offers eight grouped passes, not a single stats query (#140). The counts
|
||||||
// are library-wide because the select sits next to the Site narrowing and
|
// are library-wide because the select sits next to the Site narrowing and
|
||||||
// must not shift as the owner narrows the list itself.
|
// must not shift as the owner narrows the list itself. Cutoff travels with
|
||||||
func (h *Handler) seriesFilterOptions(selected string) ([]seriesFilterOption, error) {
|
// the stale filter, or its count would always be zero.
|
||||||
|
func (h *Handler) seriesFilterOptions(selected string, cutoff int64) ([]seriesFilterOption, error) {
|
||||||
out := make([]seriesFilterOption, 0, len(seriesFilterOrder))
|
out := make([]seriesFilterOption, 0, len(seriesFilterOrder))
|
||||||
for _, name := range seriesFilterOrder {
|
for _, name := range seriesFilterOrder {
|
||||||
shapes, err := h.store.SeriesShapes(store.SeriesFilter{Name: name})
|
shapes, err := h.store.SeriesShapes(store.SeriesFilter{Name: name, Cutoff: cutoff})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -203,9 +204,8 @@ func (h *Handler) seriesFilterOptions(selected string) ([]seriesFilterOption, er
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// seriesRow formats one store row for the template. The note chips are the
|
// seriesRow shapes one store row for the template, capping its chips at two
|
||||||
// row's own hygiene facts, capped at two with a +N tail; attention marks a
|
// plus a +N tail; attention marks a row that carries any.
|
||||||
// row that carries any.
|
|
||||||
func seriesRow(a store.AdminSeries, i int, now time.Time) seriesRowView {
|
func seriesRow(a store.AdminSeries, i int, now time.Time) seriesRowView {
|
||||||
row := seriesRowView{
|
row := seriesRowView{
|
||||||
Key: a.Key(),
|
Key: a.Key(),
|
||||||
@@ -273,13 +273,10 @@ func checkedAge(now time.Time, ts int64) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pagerRange is the pager's "1–50 of 120" line. Zero rows reads as "0 of 0":
|
// pagerRange is the pager's "1–50 of 120" line. The template renders the
|
||||||
// the empty state carries the page then, and a window number for a list that
|
// pager only over rows (the empty state replaces it), so it is never asked
|
||||||
// rendered nothing would be a number nothing points at.
|
// to describe an empty list.
|
||||||
func pagerRange(total, rows, page int) string {
|
func pagerRange(total, rows, page int) string {
|
||||||
if rows == 0 {
|
|
||||||
return "0 of 0"
|
|
||||||
}
|
|
||||||
from := (page-1)*seriesPageSize + 1
|
from := (page-1)*seriesPageSize + 1
|
||||||
return fmt.Sprintf("%d–%d of %d", from, from+rows-1, total)
|
return fmt.Sprintf("%d–%d of %d", from, from+rows-1, total)
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-3
@@ -1939,15 +1939,16 @@ func TestSeriesListFilterWiring(t *testing.T) {
|
|||||||
t.Fatalf("open %s: %v", dsn, err)
|
t.Fatalf("open %s: %v", dsn, err)
|
||||||
}
|
}
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
seedSeriesRow(t, st, db, seriesRowSeed{key: "asura:healthy", url: "https://asurascans.com/comics/healthy", cover: "aaa", checkedAt: 9000, latestNum: floatPtr(10), bookmarks: 1})
|
seedSeriesRow(t, st, db, seriesRowSeed{key: "asura:healthy", url: "https://asurascans.com/comics/healthy", cover: "aaa", checkedAt: time.Now().UnixMilli(), latestNum: floatPtr(10), bookmarks: 1})
|
||||||
seedSeriesRow(t, st, db, seriesRowSeed{key: "asura:nocover", url: "https://asurascans.com/comics/nocover", checkedAt: 9000, latestNum: floatPtr(3), bookmarks: 1})
|
seedSeriesRow(t, st, db, seriesRowSeed{key: "asura:nocover", url: "https://asurascans.com/comics/nocover", checkedAt: time.Now().UnixMilli(), latestNum: floatPtr(3), bookmarks: 1})
|
||||||
|
seedSeriesRow(t, st, db, seriesRowSeed{key: "asura:stale", url: "https://asurascans.com/comics/stale", cover: "bbb", checkedAt: time.Now().Add(-24 * time.Hour).UnixMilli(), latestNum: floatPtr(4), bookmarks: 1})
|
||||||
srv := newRouter(st, testConfig(), nil)
|
srv := newRouter(st, testConfig(), nil)
|
||||||
|
|
||||||
body := adminSeriesPage(t, srv, st, "?filter=no_cover")
|
body := adminSeriesPage(t, srv, st, "?filter=no_cover")
|
||||||
if !strings.Contains(body, "Title of asura:nocover") {
|
if !strings.Contains(body, "Title of asura:nocover") {
|
||||||
t.Errorf("no_cover list misses its row:\n%s", body)
|
t.Errorf("no_cover list misses its row:\n%s", body)
|
||||||
}
|
}
|
||||||
if strings.Contains(body, "Title of asura:healthy") {
|
if strings.Contains(body, "Title of asura:healthy") || strings.Contains(body, "Title of asura:stale") {
|
||||||
t.Errorf("no_cover list renders a covered row:\n%s", body)
|
t.Errorf("no_cover list renders a covered row:\n%s", body)
|
||||||
}
|
}
|
||||||
if !strings.Contains(body, "1 series") || !strings.Contains(body, "No cover") {
|
if !strings.Contains(body, "1 series") || !strings.Contains(body, "No cover") {
|
||||||
@@ -1959,6 +1960,11 @@ func TestSeriesListFilterWiring(t *testing.T) {
|
|||||||
if !strings.Contains(body, `<option value="no_cover" selected>`) {
|
if !strings.Contains(body, `<option value="no_cover" selected>`) {
|
||||||
t.Errorf("the no_cover option is not selected:\n%s", body)
|
t.Errorf("the no_cover option is not selected:\n%s", body)
|
||||||
}
|
}
|
||||||
|
// The stale option's count is cutoff-dependent: the row read passes the
|
||||||
|
// 12h boundary, and so must the aggregate that numbers the select.
|
||||||
|
if !strings.Contains(body, "Not checked in 12h (1)") {
|
||||||
|
t.Errorf("the stale option lacks its cutoff-dependent count:\n%s", body)
|
||||||
|
}
|
||||||
|
|
||||||
// An unknown filter value is the absent All case: both rows, no error.
|
// An unknown filter value is the absent All case: both rows, no error.
|
||||||
body = adminSeriesPage(t, srv, st, "?filter=bogus")
|
body = adminSeriesPage(t, srv, st, "?filter=bogus")
|
||||||
|
|||||||
Reference in New Issue
Block a user