@finbheara/archive-client 0.35.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -103,6 +103,8 @@ c.features; // [{ feature: "dancers", state: "full" | "missing", missing: [...
103
103
  | `calendar` | `calendar.get`, `calendar.list` | 320 |
104
104
  | `calendar.expected` | each calendar stage's `expected`; absent, never refused, without it | 320 |
105
105
  | `calendar.next` | `calendar.next` | 320 |
106
+ | `calendar.missing` | `calendar.missing` | 320 |
107
+ | `calendar.filed` | the `filed` of `calendar.missing`; empty, never refused, without it | 320 |
106
108
  | `tabs`, `panels` | `tabs.query`, `tabs.stream`; `panels.query` | 184 |
107
109
  | `feis-panels` | `feisPanels.query` | 260 |
108
110
  | `staff.adjudicators` | `staff.adjudicators` | 230 |
@@ -772,6 +774,10 @@ const l = await rel.calendar.list({ stage: "entrants", state: "observed", from:
772
774
  const n = await rel.calendar.next({ brand: "plf" });
773
775
  // [{ feis_brand_id, year, run_expected, run_expected_basis, run_expected_n,
774
776
  // from_instance_id, from_date }]: a series' next edition no listing names yet
777
+ const m = await rel.calendar.missing({ stage: "results", kind: "platform-host" });
778
+ // stage, platform, tier, kind; { items: [{ item_id, stage, feis_instance_id, kind,
779
+ // platform, expected, expected_basis, expected_n, grace_days, due, overdue_days,
780
+ // hosts: [{ source, feis_id, url }], ... }], filed: [{ ..., filed }], deferred }
775
781
  ```
776
782
 
777
783
  ONE ROW PER FEIS INSTANCE, on the id its results carry: a feis-series instance (`<brand>.<year>`),
@@ -802,6 +808,20 @@ held years, the last the release's year or the one before, whose next edition is
802
808
  calendar: its year and expected first day. It is never an instance: an id is minted only once a
803
809
  listing names the edition.
804
810
 
811
+ THE MISSING STAGES (`calendar.missing`): a `results` or `entrants` stage no capture has shown whose
812
+ expected day plus its platform's GRACE is before the release's day, the run inside the 365 days
813
+ before it. The grace is the platform's largest gap between two days it was captured on, over the
814
+ 180 days before the release, at least 7 days: an item waits out the platform's slowest scrape
815
+ before it counts; a day on the pooled lag (basis `pooled`), or a platform with no gap of its own,
816
+ waits the pooled grace. A stage whose basis is `unmeasured` is never missing. An item is a calendar
817
+ instance, or a feis-index listing no instance holds (`feis_instance_id` null, `item_id` its first
818
+ occurrence id). `kind` is `platform-host` (a capture platform lists it, so a scrape of it should
819
+ bring it) or `listing-only` (only a calendar listing names it, no capture platform, and its results
820
+ may be published nowhere capture reads). A stage a capture's not-found record defers is not listed:
821
+ `deferred` counts those. A stage already in a filed capture ticket (`calendar.filed`, the publisher's
822
+ filed record) is not in `items` either: `filed` lists it with its ticket (`owner/repo#N`), and it is
823
+ never counted against the threshold a capture epic is drafted over. A worklist, never an error.
824
+
805
825
  ## Teachers and adjudicators
806
826
 
807
827
  ```ts
@@ -981,6 +1001,8 @@ is passed to the dancer resolver as asked (`rel.dancers`, above).
981
1001
  idq calendar [--brand <series>] [--tier <t>] [--from <date>] [--to <date>] [--stage <s>] [--state <s>]
982
1002
  [--terminal results|withdrawn|passed-no-results|none]
983
1003
  idq calendar --next [--brand <series>]
1004
+ idq calendar --missing [--stage results|entrants] [--platform <p>] [--tier <t>]
1005
+ [--kind platform-host|listing-only]
984
1006
  idq teacher [--release <spec>] <name|TD number>
985
1007
  idq adjudicator [--release <spec>] <name|TD number>
986
1008
  idq comp [--release <spec>] <feis> <code> [--year y] [--title t]
@@ -742,6 +742,39 @@ var FEATURES2 = [
742
742
  since: 320,
743
743
  calls: "calendar.next"
744
744
  },
745
+ {
746
+ feature: "calendar.missing",
747
+ needs: [
748
+ "item_id",
749
+ "stage",
750
+ "feis_instance_id",
751
+ "feis_brand_id",
752
+ "tier",
753
+ "name_printed",
754
+ "date_lo",
755
+ "date_hi",
756
+ "occurrence_ids",
757
+ "feis_ids",
758
+ "kind",
759
+ "platform",
760
+ "expected",
761
+ "expected_basis",
762
+ "expected_n",
763
+ "grace_days",
764
+ "due",
765
+ "overdue_days",
766
+ "hosts",
767
+ "deferred"
768
+ ].map((c) => `results:feis_calendar_missing.${c}`),
769
+ since: 320,
770
+ calls: "calendar.missing"
771
+ },
772
+ {
773
+ feature: "calendar.filed",
774
+ needs: ["results:feis_calendar_missing.deferred_kind"],
775
+ since: 320,
776
+ calls: "calendar.missing's `filed` (empty, never refused, without it)"
777
+ },
745
778
  {
746
779
  feature: "upcoming.entrants",
747
780
  needs: [
@@ -3968,6 +4001,55 @@ class CalendarFinder2 {
3968
4001
  }))
3969
4002
  };
3970
4003
  }
4004
+ async missing(q = {}) {
4005
+ await this.rel.fetch("results");
4006
+ await this.rel.require("calendar.missing");
4007
+ const db = this.rel.db("results");
4008
+ const where = [];
4009
+ const args = [];
4010
+ for (const [col, v] of [
4011
+ ["stage", q.stage],
4012
+ ["platform", q.platform],
4013
+ ["tier", q.tier],
4014
+ ["kind", q.kind]
4015
+ ])
4016
+ if (v !== undefined) {
4017
+ where.push(`${col} = ?`);
4018
+ args.push(v);
4019
+ }
4020
+ const rows = db.query(`SELECT * FROM feis_calendar_missing${where.length === 0 ? "" : ` WHERE ${where.join(" AND ")}`}
4021
+ ORDER BY CASE stage WHEN 'results' THEN 0 ELSE 1 END,
4022
+ CASE kind WHEN 'platform-host' THEN 0 ELSE 1 END, platform, due, item_id`).all(...args);
4023
+ const isDeferred = (r) => r.deferred !== null && r.deferred !== undefined;
4024
+ const isFiled = (r) => isDeferred(r) && r.deferred_kind === "filed";
4025
+ const item = (r) => ({
4026
+ item_id: r.item_id,
4027
+ stage: r.stage,
4028
+ feis_instance_id: r.feis_instance_id ?? null,
4029
+ feis_brand_id: r.feis_brand_id ?? null,
4030
+ tier: r.tier,
4031
+ name_printed: r.name_printed ?? null,
4032
+ date_lo: r.date_lo ?? null,
4033
+ date_hi: r.date_hi ?? null,
4034
+ occurrence_ids: json(r.occurrence_ids, []),
4035
+ feis_ids: json(r.feis_ids, []),
4036
+ kind: r.kind,
4037
+ platform: r.platform,
4038
+ expected: r.expected,
4039
+ expected_basis: r.expected_basis,
4040
+ expected_n: r.expected_n === null || r.expected_n === undefined ? null : Number(r.expected_n),
4041
+ grace_days: Number(r.grace_days),
4042
+ due: r.due,
4043
+ overdue_days: Number(r.overdue_days),
4044
+ hosts: json(r.hosts, [])
4045
+ });
4046
+ return {
4047
+ tag: this.rel.tag,
4048
+ items: rows.filter((r) => !isDeferred(r)).map(item),
4049
+ filed: rows.filter(isFiled).map((r) => ({ ...item(r), filed: r.deferred })),
4050
+ deferred: rows.filter((r) => isDeferred(r) && !isFiled(r)).length
4051
+ };
4052
+ }
3971
4053
  async get(id) {
3972
4054
  const db = await this.db();
3973
4055
  const ways = [
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  latestCheck2,
22
22
  within,
23
23
  staleWarning2
24
- } from "./chunks/index-gt7yxxp8.js";
24
+ } from "./chunks/index-hfpcedbh.js";
25
25
  import"./chunks/index-hghdy980.js";
26
26
 
27
27
  var USAGE_HEADER = "usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json] [--live]";
@@ -394,8 +394,19 @@ var VERBS = [
394
394
  },
395
395
  {
396
396
  verb: "calendar",
397
- value: ["--release", "--brand", "--tier", "--from", "--to", "--stage", "--state", "--terminal"],
398
- bool: ["--next"],
397
+ value: [
398
+ "--release",
399
+ "--brand",
400
+ "--tier",
401
+ "--from",
402
+ "--to",
403
+ "--stage",
404
+ "--state",
405
+ "--terminal",
406
+ "--platform",
407
+ "--kind"
408
+ ],
409
+ bool: ["--next", "--missing"],
399
410
  usage: [
400
411
  " calendar [--release <spec>] <instance|feis id|occurrence id>",
401
412
  " one feis instance's stages: announced, syllabus, entrants,",
@@ -408,7 +419,13 @@ var VERBS = [
408
419
  " expected day with its basis and n where the release has them",
409
420
  " calendar --next [--brand <series>]",
410
421
  " each series' next edition no listing names yet: its year",
411
- " and expected first day from the year before (basis brand)"
422
+ " and expected first day from the year before (basis brand)",
423
+ " calendar --missing [--stage results|entrants] [--platform <p>] [--tier T1..T4]",
424
+ " [--kind platform-host|listing-only]",
425
+ " the stages past their expected day plus the platform's",
426
+ " grace, the run in the last 365 days: hosts, urls, expected",
427
+ " day, grace and days overdue; the filed (a capture ticket",
428
+ " names them) and the deferred counted apart"
412
429
  ]
413
430
  },
414
431
  {
@@ -1558,6 +1575,29 @@ async function main(argv, io = {}) {
1558
1575
  const rel = await open(spec, { ...opts, assets: [] });
1559
1576
  try {
1560
1577
  const line = (e) => ` ${e.date_lo ?? "?"} ${e.feis_instance_id} ${e.tier} ${["announced", "syllabus", "entrants", "run", "results"].map((s) => `${s}:${e.stages[s].state === "observed" ? e.stages[s].date ?? "seen" : e.stages[s].state}`).join(" ")} [${e.terminal ?? "ahead"}]${e.name_printed !== null ? ` "${e.name_printed}"` : ""}`;
1578
+ if (p.bools.has("--missing")) {
1579
+ const stage = p.flags.get("--stage");
1580
+ const platform = p.flags.get("--platform");
1581
+ const tier = p.flags.get("--tier");
1582
+ const kind = p.flags.get("--kind");
1583
+ const a = await rel.calendar.missing({
1584
+ ...stage !== undefined ? { stage } : {},
1585
+ ...platform !== undefined ? { platform } : {},
1586
+ ...tier !== undefined ? { tier } : {},
1587
+ ...kind !== undefined ? { kind } : {}
1588
+ });
1589
+ out(a, () => {
1590
+ for (const m of a.items) {
1591
+ say(` ${m.due} ${m.item_id} ${m.stage} ${m.kind} ${m.platform} run ${m.date_lo ?? "?"} expected ${m.expected} (${m.expected_basis}, n ${m.expected_n ?? "-"}) + ${m.grace_days} grace ${m.overdue_days} days overdue${m.name_printed !== null ? ` "${m.name_printed}"` : ""}`);
1592
+ for (const h of m.hosts)
1593
+ say(` ${h.source} ${h.feis_id ?? "-"} ${h.url ?? "-"}`);
1594
+ }
1595
+ for (const m of a.filed)
1596
+ say(` ${m.due} ${m.item_id} ${m.stage} ${m.kind} ${m.platform} run ${m.date_lo ?? "?"} FILED ${m.filed}${m.name_printed !== null ? ` "${m.name_printed}"` : ""}`);
1597
+ say(`${a.items.length} missing, ${a.filed.length} filed, ${a.deferred} deferred, in ${a.tag}`);
1598
+ });
1599
+ return a.items.length === 0 ? 1 : 0;
1600
+ }
1561
1601
  if (p.bools.has("--next")) {
1562
1602
  const brand = p.flags.get("--brand");
1563
1603
  const a = await rel.calendar.next(brand === undefined ? {} : { brand });
package/dist/index.js CHANGED
@@ -80,7 +80,7 @@ import {
80
80
  readLatestCheck2,
81
81
  latestCheck2,
82
82
  staleWarning2
83
- } from "./chunks/index-gt7yxxp8.js";
83
+ } from "./chunks/index-hfpcedbh.js";
84
84
  import {
85
85
  cacheRoot2,
86
86
  repoOf2
@@ -59,6 +59,49 @@ export interface CalendarNextAnswer {
59
59
  tag: string;
60
60
  next: CalendarNext[];
61
61
  }
62
+ export type CalendarMissingStage = "results" | "entrants";
63
+ export type CalendarMissingKind = "platform-host" | "listing-only";
64
+ export interface CalendarMissingHost {
65
+ source: string;
66
+ feis_id: string | null;
67
+ url: string | null;
68
+ }
69
+ export interface CalendarMissing {
70
+ item_id: string;
71
+ stage: CalendarMissingStage;
72
+ feis_instance_id: string | null;
73
+ feis_brand_id: string | null;
74
+ tier: string;
75
+ name_printed: string | null;
76
+ date_lo: string | null;
77
+ date_hi: string | null;
78
+ occurrence_ids: string[];
79
+ feis_ids: string[];
80
+ kind: CalendarMissingKind;
81
+ platform: string;
82
+ expected: string;
83
+ expected_basis: CalendarExpectedBasis;
84
+ expected_n: number | null;
85
+ grace_days: number;
86
+ due: string;
87
+ overdue_days: number;
88
+ hosts: CalendarMissingHost[];
89
+ }
90
+ export interface CalendarMissingQuery {
91
+ stage?: CalendarMissingStage;
92
+ platform?: string;
93
+ tier?: string;
94
+ kind?: CalendarMissingKind;
95
+ }
96
+ export interface CalendarFiled extends CalendarMissing {
97
+ filed: string;
98
+ }
99
+ export interface CalendarMissingAnswer {
100
+ tag: string;
101
+ items: CalendarMissing[];
102
+ filed: CalendarFiled[];
103
+ deferred: number;
104
+ }
62
105
  export interface CalendarGetAnswer {
63
106
  tag: string;
64
107
  asked: string;
@@ -74,6 +117,7 @@ export declare class CalendarFinder {
74
117
  next(q?: {
75
118
  brand?: string;
76
119
  }): Promise<CalendarNextAnswer>;
120
+ missing(q?: CalendarMissingQuery): Promise<CalendarMissingAnswer>;
77
121
  get(id: string): Promise<CalendarGetAnswer>;
78
122
  list(q?: CalendarQuery): Promise<CalendarAnswer>;
79
123
  }
@@ -26,5 +26,5 @@ export { type EntryRow, type StreamCompetition, type StreamQuery, type StreamRow
26
26
  export { type FeisPanelSeat, FeisPanelFinder, PanelFinder, type PanelSeat, type RegisterRow, type TabAnswer, TabFinder, type TabQuery, type TabRow, type TabStreamQuery, } from "./tabs.js";
27
27
  export { EventFinder, type EventYear, type EventYearQuery, type ResultRoster, type RosterMember, type RosterTeam, TeamFinder, type TeamResult, } from "./teams.js";
28
28
  export { type EntrantCandidate, FIELD_STATES, type FieldComp, DEPARTURE_REASONS, type DepartureReason, type FieldState, type HeldOut, type UpcomingDeparture, type UpcomingDepartureAnswer, type UpcomingDepartureQuery, type UpcomingEntrant, type UpcomingEntrantAnswer, type UpcomingEntrantQuery, type UpcomingFeis, type UpcomingFeisAnswer, type UpcomingFeisQuery, type UpcomingFeisState, type UpcomingField, type UpcomingFieldAnswer, type UpcomingFieldQuery, UpcomingFinder, type UpcomingHost, } from "./upcoming.js";
29
- export { CALENDAR_STAGES, CALENDAR_STAGE_STATES, CALENDAR_TERMINALS, type CalendarAnswer, type CalendarEntry, type CalendarExpected, type CalendarExpectedBasis, type CalendarNext, type CalendarNextAnswer, CalendarFinder, type CalendarGetAnswer, type CalendarQuery, type CalendarStage, type CalendarStageState, type CalendarStageValue, type CalendarTerminal, } from "./calendar.js";
29
+ export { CALENDAR_STAGES, CALENDAR_STAGE_STATES, CALENDAR_TERMINALS, type CalendarAnswer, type CalendarEntry, type CalendarExpected, type CalendarExpectedBasis, type CalendarFiled, type CalendarMissing, type CalendarMissingAnswer, type CalendarMissingHost, type CalendarMissingKind, type CalendarMissingQuery, type CalendarMissingStage, type CalendarNext, type CalendarNextAnswer, CalendarFinder, type CalendarGetAnswer, type CalendarQuery, type CalendarStage, type CalendarStageState, type CalendarStageValue, type CalendarTerminal, } from "./calendar.js";
30
30
  export { ARCHIVE_TAG, type ArchiveTag, type ReleaseSpec, compareTagsNewestFirst, cutOf, parseArchiveTag, parseReleaseSpec, pickTag, renderArchiveTag, } from "./tag.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finbheara/archive-client",
3
- "version": "0.35.0",
3
+ "version": "0.37.0",
4
4
  "description": "Open a versioned release of an Irish dance results archive: download, verify, decompress and cache it by tag, keep the cache, and resolve feises and schools against it",
5
5
  "keywords": ["irish-dance", "results", "release", "cache", "sqlite"],
6
6
  "license": "MIT",