fix(dn-batches): add uploaded_at so ordering works

PocketBase's created is empty on every dn_batches record, making the
board's -created sort a no-op and the tick's oldest-first meaningless.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
DEls 2026-09-17 20:53:23 +02:00
parent b1ce964f39
commit 06311c6de2
5 changed files with 100 additions and 2 deletions

View file

@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""
Adds dn_batches.uploaded_at and backfills it.
PocketBase's own `created` is an empty string on every dn_batches record —
the collection was created without autodate — so the board's "-created"
sort is a no-op and the tick's "oldest first" means nothing. The rest of
this codebase already works around the same quirk (delivery_notes sorts by
date_received), so an explicit field is the consistent fix.
Backfill uses the batch's earliest dn_batch_notes date where one exists.
Batches with nothing to go on are LEFT EMPTY rather than given an invented
timestamp; they sort last.
Idempotent.
"""
import sys
from pb_env import PB, http, superuser_token
def get_all(token, coll, fields=None, filt=None):
out, page = [], 1
while True:
q = f"?page={page}&perPage=200"
if fields:
q += f"&fields={fields}"
if filt:
q += f"&filter={filt}"
code, resp = http("GET", f"/api/collections/{coll}/records{q}", token=token)
if code != 200:
sys.exit(f"GET {coll} failed: {code} {resp}")
out.extend(resp["items"])
if page >= resp["totalPages"]:
return out
page += 1
def main():
token = superuser_token()
print(f"\n→ dn_batches on {PB}")
code, coll = http("GET", "/api/collections/dn_batches", token=token)
if code != 200:
sys.exit(f"GET collection failed: {code} {coll}")
fields = list(coll.get("fields", []))
if any(f.get("name") == "uploaded_at" for f in fields):
print(" • uploaded_at already exists")
else:
code, resp = http(
"PATCH",
f"/api/collections/{coll['id']}",
{"fields": fields + [{"name": "uploaded_at", "type": "date", "required": False}]},
token,
)
if code != 200:
sys.exit(f"PATCH failed: {code} {resp}")
print(" ✓ added uploaded_at")
notes = get_all(token, "dn_batch_notes", "batch,date")
earliest = {}
for n in notes:
d = (n.get("date") or "").strip()
b = n.get("batch")
if not d or not b:
continue
if b not in earliest or d < earliest[b]:
earliest[b] = d
batches = get_all(token, "dn_batches", "id,uploaded_at")
filled = skipped = blank = 0
for b in batches:
if (b.get("uploaded_at") or "").strip():
skipped += 1
continue
d = earliest.get(b["id"])
if not d:
blank += 1
continue
code, resp = http(
"PATCH",
f"/api/collections/dn_batches/records/{b['id']}",
{"uploaded_at": d},
token,
)
if code != 200:
sys.exit(f"PATCH {b['id']} failed: {code} {resp}")
filled += 1
print(f" backfilled {filled}, already set {skipped}, left empty {blank}")
if __name__ == "__main__":
main()
print("\nDone.")

View file

@ -180,6 +180,7 @@ export async function POST(request: Request) {
source: "web",
uploaded_by: "web-upload",
status: "inbox",
uploaded_at: new Date().toISOString(),
page_count: 0,
notes_detected: 0,
notes_promoted: 0,

View file

@ -38,7 +38,7 @@ export async function POST(request: Request) {
candidates = await pb.collection("dn_batches").getFullList({
filter: `status='inbox'`,
fields: "id",
sort: "created",
sort: "uploaded_at",
});
} catch (err) {
console.error("[dn-tick] list failed", err);

View file

@ -17,7 +17,7 @@ export default async function InboxPage({
const batches = await pb.collection("dn_batches").getFullList({
expand: "site",
sort: "-created",
sort: "-uploaded_at",
...filterOpt(siteFilter(scope)),
});

View file

@ -381,6 +381,8 @@ export interface DnBatchRecord extends RecordModel {
claimed_at: string;
/** Last error message from n8n. Empty when the batch is healthy. */
last_error: string;
/** Set explicitly at upload — PocketBase's `created` is unusable here. */
uploaded_at: string;
expand?: { site?: SiteRecord };
}