@finbheara/archive-client 0.3.0 → 0.4.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 +41 -6
- package/dist/chunks/{index-kp811fb0.js → index-v8e0qe9f.js} +347 -9
- package/dist/cli.js +65 -1
- package/dist/index.js +7 -1
- package/dist/types/cli.d.ts +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/indexes.d.ts +7 -1
- package/dist/types/parquet.d.ts +4 -0
- package/dist/types/release.d.ts +3 -0
- package/dist/types/schema.d.ts +1 -0
- package/dist/types/teams.d.ts +105 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Open a versioned release of an Irish dance results dataset and work with it locally: download,
|
|
4
4
|
verify, decompress and cache it by tag, keep a user-local cache of releases, and resolve feises,
|
|
5
|
-
schools, competitions, registration lists, dancers, results, majors, teachers
|
|
6
|
-
adjudicators against one. A command line, `idq`, is a thin wrapper over the same calls.
|
|
5
|
+
schools, competitions, registration lists, dancers, results, majors, teams, event dates, teachers
|
|
6
|
+
and adjudicators against one. A command line, `idq`, is a thin wrapper over the same calls.
|
|
7
7
|
|
|
8
8
|
Bun 1.4.2 or later (the databases open through `bun:sqlite`). MIT.
|
|
9
9
|
|
|
@@ -20,7 +20,7 @@ type declarations (`.d.ts`), so a TypeScript project needs Bun's types (`"types"
|
|
|
20
20
|
`bunx idq <verb>`.
|
|
21
21
|
|
|
22
22
|
The version is 0.x while the API settles: a minor bump may break, so pin a minor
|
|
23
|
-
(`"@finbheara/archive-client": "0.
|
|
23
|
+
(`"@finbheara/archive-client": "0.4.x"`).
|
|
24
24
|
|
|
25
25
|
## Configuration
|
|
26
26
|
|
|
@@ -85,6 +85,8 @@ c.features; // [{ feature: "dancers", state: "full" | "missing", missing: [...
|
|
|
85
85
|
| `staff` | `staff.teacher`, `staff.adjudicator` | 260 |
|
|
86
86
|
| `majors.order` | `majors.competitions` | 260 |
|
|
87
87
|
| `feises`, `comps` | `feises.find`, `feises.series`, `comps.*`, `registrations.resolve` | 265 |
|
|
88
|
+
| `teams` | `teams.results`, `teams.rosters`, `teams.resultRosters` | 260 |
|
|
89
|
+
| `events` | `events.years` | 260 |
|
|
88
90
|
|
|
89
91
|
A spec is `pinned` (the default: the user-local pin, else the latest), `latest`, a cut number
|
|
90
92
|
(`42`, or `42.1` for a fraction) or a tag (`archive-YYYY.MM.DD-N`).
|
|
@@ -138,8 +140,9 @@ outside the cache root.
|
|
|
138
140
|
|
|
139
141
|
The release databases carry a primary key per table and nothing more, so lookups build side
|
|
140
142
|
indexes, one SQLite file per index per tag, each keyed on its builder's version and the sha256 of
|
|
141
|
-
every input it read. A file whose key is stale is rebuilt, never served.
|
|
142
|
-
caller may hand its own (`builders` option, an `IndexBuilder`
|
|
143
|
+
every input it read. A file whose key is stale is rebuilt, never served. These ship here, and a
|
|
144
|
+
caller may hand its own (`builders` option, an `IndexBuilder`, with an optional async `load` step
|
|
145
|
+
whose answer its `build` receives):
|
|
143
146
|
|
|
144
147
|
- `schools`: every published school print with the ids its rows carry, filed into a
|
|
145
148
|
`@finbheara/domain` `schoolEncoder` noun index, with the registry and the retired ids
|
|
@@ -151,6 +154,9 @@ caller may hand its own (`builders` option, an `IndexBuilder`):
|
|
|
151
154
|
- `staff` (keyed the same way): teachers and adjudicators by name (a `roleEncoder` noun index) and TD number: the
|
|
152
155
|
schools' staff lists and TD links, the adjudicator register, and the seats each sat
|
|
153
156
|
- `comps`: every competition by its feis and the feis's own code, with its series and year
|
|
157
|
+
- `teams`: the team results, the roster teams with their members, and how they join, by
|
|
158
|
+
event-year
|
|
159
|
+
- `events`: every event-year's dates and place
|
|
154
160
|
|
|
155
161
|
## Feises
|
|
156
162
|
|
|
@@ -189,7 +195,8 @@ print exactly as the release published it, then the print read by `@finbheara/do
|
|
|
189
195
|
reader at its filing key (`sn3`) and at its real words. `ambiguous` and `none` are answers: a
|
|
190
196
|
best guess is never promoted to `unique`. `region` drops only a candidate the registry places in
|
|
191
197
|
other regions, and keeps one it places nowhere. A print the reader refuses (a placeholder)
|
|
192
|
-
answers `none` with the reason.
|
|
198
|
+
answers `none` with the reason. An id (or a retired id followed) answers with every print its
|
|
199
|
+
rows were published under and their rows, so it agrees with each print lookup of that school.
|
|
193
200
|
|
|
194
201
|
Every answer carries the release tag it came from.
|
|
195
202
|
|
|
@@ -269,6 +276,32 @@ A row carries the prints and the ids that stand for them: the address, the compe
|
|
|
269
276
|
and age, the place, the name and school as printed with the dancer and school ids. The majors are
|
|
270
277
|
read from `majors.sqlite`, fetched on first use.
|
|
271
278
|
|
|
279
|
+
## Teams and event-years
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
const r = await rel.teams.results({ event: "<event>", year: 2024 });
|
|
283
|
+
r.results; // [{ address, competition, event, year, comp_no, title, category, hands, letter, label,
|
|
284
|
+
// school_printed, school_id, place, place_printed, tie, competitors, atts, ... }]
|
|
285
|
+
const t = await rel.teams.rosters({ event: "<event>", year: 2024 });
|
|
286
|
+
t.teams; // [{ team, kind, event, year, comp_no, title, team_index, seq, school_id, school_printed,
|
|
287
|
+
// members: [{ seq, name_printed, name, yob, dancer_id, dancer_scope }] }]
|
|
288
|
+
const j = await rel.teams.resultRosters({ event: "<event>", year: 2024 });
|
|
289
|
+
j.joins; // [{ team, result_address, join_kind, via_split, roster_index, result_letter, ... }]
|
|
290
|
+
const e = await rel.events.years({ event: "<event>" });
|
|
291
|
+
e.years; // [{ event, year, date_lo, date_hi, resolution, source, date_printed, place, place_printed }]
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
A query names an event (case-blind), a year, both, or neither (every row). Everything comes from
|
|
295
|
+
ONE release: a roster member's `dancer_id` is the same release's id as the results', so a caller
|
|
296
|
+
never joins team records of one release to dancer ids of another. A roster is a sheet of a school's
|
|
297
|
+
teams (`kind: "rosterTeam"`) or a result card's own line-up (`lineup`). `join_kind` says how a
|
|
298
|
+
roster team met the results (`exact`, `card`, `branch`) or why it did not (`no-result`,
|
|
299
|
+
`no-school`, `no-roster`, `multi-team-unjoined`), and `result_address` is null where none joined.
|
|
300
|
+
An event-year's dates are a window (`date_lo`, `date_hi`) with how fine it is (`resolution`:
|
|
301
|
+
`day`, `month`, `calendar`) and what it is read from (`source`), and `place` is a place-tree node
|
|
302
|
+
where the release knows one. A field the source left empty is null. The rows are read once per
|
|
303
|
+
release into the `teams` and `events` indexes.
|
|
304
|
+
|
|
272
305
|
## Teachers and adjudicators
|
|
273
306
|
|
|
274
307
|
```ts
|
|
@@ -393,6 +426,8 @@ and the competitions still resolve.
|
|
|
393
426
|
[--population girls|boys] [--rows]
|
|
394
427
|
idq results [--release <spec>] [--dancer <id>] [--feis <id>] [--comp <c>] [--school <s>] [--year <y>] [--limit <n>]
|
|
395
428
|
idq majors [--release <spec>] [--event <e>] [--year <y>] [--comp <c>] [--dancer <id>] [--rows]
|
|
429
|
+
idq teams [--release <spec>] [--event <e>] [--year <y>] [--rosters | --joins]
|
|
430
|
+
idq events [--release <spec>] [--event <e>] [--year <y>]
|
|
396
431
|
idq teacher [--release <spec>] <name|TD number>
|
|
397
432
|
idq adjudicator [--release <spec>] <name|TD number>
|
|
398
433
|
idq comp [--release <spec>] <feis> <code> [--year y] [--title t]
|
|
@@ -1459,7 +1459,7 @@ var encoderOf = (r) => schoolEncoder({ reader: r.reader });
|
|
|
1459
1459
|
var TABLE = "school_name";
|
|
1460
1460
|
var schoolsIndex = {
|
|
1461
1461
|
name: "schools",
|
|
1462
|
-
version: "
|
|
1462
|
+
version: "2",
|
|
1463
1463
|
inputs: ["results", "school-registry.jsonl", "school-redirects.jsonl"],
|
|
1464
1464
|
says: "every published school print and its ids, read at the school reader's slots",
|
|
1465
1465
|
keyParts: (rel) => [`reader=${rel.schoolReader().id}`],
|
|
@@ -1475,6 +1475,7 @@ var schoolsIndex = {
|
|
|
1475
1475
|
const putPair = out.prepare("INSERT INTO print_id VALUES (?, ?, ?)");
|
|
1476
1476
|
for (const r of pairs)
|
|
1477
1477
|
putPair.run(r.p, r.s, r.n);
|
|
1478
|
+
out.run("CREATE INDEX print_id_school ON print_id(school_id)");
|
|
1478
1479
|
const names = createIndex3(out, encoder, {
|
|
1479
1480
|
table: TABLE
|
|
1480
1481
|
});
|
|
@@ -1534,9 +1535,13 @@ class SchoolResolver2 {
|
|
|
1534
1535
|
...extra
|
|
1535
1536
|
};
|
|
1536
1537
|
};
|
|
1538
|
+
const byId = (id, by) => {
|
|
1539
|
+
const rs = db.query("SELECT print, rows FROM print_id WHERE school_id = ? ORDER BY print").all(id);
|
|
1540
|
+
return candidate(id, by, rs.reduce((n, r) => n + r.rows, 0), rs.map((r) => r.print));
|
|
1541
|
+
};
|
|
1537
1542
|
const a = asked.trim();
|
|
1538
1543
|
if (reg.get(a) !== null)
|
|
1539
|
-
return answer([
|
|
1544
|
+
return answer([byId(a, "id")]);
|
|
1540
1545
|
let live = a;
|
|
1541
1546
|
const seen = new Set;
|
|
1542
1547
|
for (;; ) {
|
|
@@ -1547,7 +1552,7 @@ class SchoolResolver2 {
|
|
|
1547
1552
|
live = r.new_id;
|
|
1548
1553
|
}
|
|
1549
1554
|
if (live !== a)
|
|
1550
|
-
return answer([
|
|
1555
|
+
return answer([byId(live, "redirect")], { redirected: { from: a, to: live } });
|
|
1551
1556
|
const byPrints = (prints, by) => {
|
|
1552
1557
|
const ids = new Map;
|
|
1553
1558
|
const q2 = db.query("SELECT school_id, rows FROM print_id WHERE print = ?");
|
|
@@ -1581,8 +1586,240 @@ class SchoolResolver2 {
|
|
|
1581
1586
|
}
|
|
1582
1587
|
}
|
|
1583
1588
|
|
|
1589
|
+
import { parquetMetadata, parquetReadObjects, parquetSchema } from "hyparquet";
|
|
1590
|
+
var COMPRESSORS = {
|
|
1591
|
+
ZSTD: (input) => new Uint8Array(Bun.zstdDecompressSync(input))
|
|
1592
|
+
};
|
|
1593
|
+
var tableAsset = (table) => `${table}.parquet`;
|
|
1594
|
+
function plain(v) {
|
|
1595
|
+
if (typeof v === "bigint")
|
|
1596
|
+
return v >= BigInt(Number.MIN_SAFE_INTEGER) && v <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(v) : v;
|
|
1597
|
+
if (Array.isArray(v))
|
|
1598
|
+
return v.map(plain);
|
|
1599
|
+
if (v !== null && typeof v === "object" && !(v instanceof Date)) {
|
|
1600
|
+
const o = {};
|
|
1601
|
+
for (const [k, x] of Object.entries(v))
|
|
1602
|
+
o[k] = plain(x);
|
|
1603
|
+
return o;
|
|
1604
|
+
}
|
|
1605
|
+
return v;
|
|
1606
|
+
}
|
|
1607
|
+
async function readParquet(path, columns) {
|
|
1608
|
+
const file = await Bun.file(path).arrayBuffer();
|
|
1609
|
+
const rows = await parquetReadObjects({
|
|
1610
|
+
file,
|
|
1611
|
+
compressors: COMPRESSORS,
|
|
1612
|
+
...columns !== undefined ? { columns: [...columns] } : {}
|
|
1613
|
+
});
|
|
1614
|
+
return rows.map((r) => plain(r));
|
|
1615
|
+
}
|
|
1616
|
+
async function parquetColumns(path) {
|
|
1617
|
+
const meta = parquetMetadata(await Bun.file(path).arrayBuffer());
|
|
1618
|
+
return new Set(parquetSchema(meta).children.map((c) => c.element.name));
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
var str = (v) => typeof v === "string" && v !== "" ? v : null;
|
|
1622
|
+
var num = (v) => typeof v === "number" ? v : null;
|
|
1623
|
+
var strs = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
1624
|
+
var scope = (id) => id === null ? null : id >= FEIS_LOCAL_ID_FLOOR2 ? "per-release" : "stable";
|
|
1625
|
+
var TEAM_TABLES = [
|
|
1626
|
+
"team_results",
|
|
1627
|
+
"roster_teams",
|
|
1628
|
+
"roster_members",
|
|
1629
|
+
"team_result_rosters",
|
|
1630
|
+
"event_years"
|
|
1631
|
+
];
|
|
1632
|
+
var TEAMS_SCHEMA = `
|
|
1633
|
+
CREATE TABLE event_year(event_year TEXT PRIMARY KEY, event TEXT NOT NULL, year INTEGER NOT NULL);
|
|
1634
|
+
CREATE TABLE result(address TEXT PRIMARY KEY, competition TEXT, event TEXT, year INTEGER,
|
|
1635
|
+
card TEXT, comp_no TEXT, title TEXT, category TEXT, hands INTEGER, letter TEXT, label TEXT,
|
|
1636
|
+
name_printed TEXT, school_printed TEXT, school_loc TEXT, school_id TEXT, place INTEGER,
|
|
1637
|
+
place_printed TEXT, tie INTEGER, competitors INTEGER, atts TEXT, ord INTEGER);
|
|
1638
|
+
CREATE TABLE roster_team(team TEXT PRIMARY KEY, kind TEXT, event TEXT, year INTEGER,
|
|
1639
|
+
comp_no TEXT, title TEXT, team_index TEXT, seq INTEGER, school_id TEXT, school_printed TEXT,
|
|
1640
|
+
ord INTEGER);
|
|
1641
|
+
CREATE TABLE roster_member(team TEXT, seq INTEGER, name_printed TEXT, name TEXT, yob INTEGER,
|
|
1642
|
+
dancer_id INTEGER, ord INTEGER);
|
|
1643
|
+
CREATE TABLE result_roster(team TEXT, event TEXT, year INTEGER, comp_no TEXT, school_id TEXT,
|
|
1644
|
+
result_address TEXT, join_kind TEXT, via_split INTEGER, roster_teams INTEGER,
|
|
1645
|
+
result_teams INTEGER, roster_index TEXT, result_letter TEXT, ord INTEGER);
|
|
1646
|
+
`;
|
|
1647
|
+
var TEAMS_INDEXES = [
|
|
1648
|
+
"CREATE INDEX result_ey ON result(event COLLATE NOCASE, year)",
|
|
1649
|
+
"CREATE INDEX result_year ON result(year)",
|
|
1650
|
+
"CREATE INDEX roster_team_ey ON roster_team(event COLLATE NOCASE, year)",
|
|
1651
|
+
"CREATE INDEX roster_team_year ON roster_team(year)",
|
|
1652
|
+
"CREATE INDEX roster_member_team ON roster_member(team)",
|
|
1653
|
+
"CREATE INDEX result_roster_ey ON result_roster(event COLLATE NOCASE, year)",
|
|
1654
|
+
"CREATE INDEX result_roster_year ON result_roster(year)"
|
|
1655
|
+
];
|
|
1656
|
+
async function readTables(rel, tables) {
|
|
1657
|
+
const out = {};
|
|
1658
|
+
for (const t of tables) {
|
|
1659
|
+
await rel.fetch(tableAsset(t));
|
|
1660
|
+
out[t] = await readParquet(rel.path(tableAsset(t)));
|
|
1661
|
+
}
|
|
1662
|
+
return out;
|
|
1663
|
+
}
|
|
1664
|
+
var teamsIndex = {
|
|
1665
|
+
name: "teams",
|
|
1666
|
+
version: "1",
|
|
1667
|
+
inputs: TEAM_TABLES.map(tableAsset),
|
|
1668
|
+
says: "the team results, the roster teams with their members, and how they join, by event-year",
|
|
1669
|
+
load: (rel) => readTables(rel, TEAM_TABLES),
|
|
1670
|
+
build(_rel, out, loaded) {
|
|
1671
|
+
const t = loaded;
|
|
1672
|
+
out.run(TEAMS_SCHEMA);
|
|
1673
|
+
const ey = new Map;
|
|
1674
|
+
const putEy = out.prepare("INSERT OR IGNORE INTO event_year VALUES (?, ?, ?)");
|
|
1675
|
+
for (const r of t.event_years) {
|
|
1676
|
+
const k = str(r.event_year);
|
|
1677
|
+
const event = str(r.event);
|
|
1678
|
+
const year = num(r.year);
|
|
1679
|
+
if (k === null || event === null || year === null)
|
|
1680
|
+
continue;
|
|
1681
|
+
ey.set(k, { event, year });
|
|
1682
|
+
putEy.run(k, event, year);
|
|
1683
|
+
}
|
|
1684
|
+
const putR = out.prepare("INSERT OR IGNORE INTO result VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
1685
|
+
let ord = 0;
|
|
1686
|
+
for (const r of t.team_results)
|
|
1687
|
+
putR.run(str(r.address), str(r.slug), str(r.event), num(r.year), str(r.card), str(r.comp_no), str(r.title), str(r.category), num(r.hands), str(r.letter), str(r.label), str(r.name_printed), str(r.school_printed), str(r.school_loc), str(r.school_id), num(r.place), str(r.place_printed), num(r.tie), num(r.competitors), JSON.stringify(strs(r.atts)), ord++);
|
|
1688
|
+
const putT = out.prepare("INSERT OR IGNORE INTO roster_team VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
1689
|
+
for (const r of t.roster_teams) {
|
|
1690
|
+
const e = ey.get(str(r.event_year) ?? "");
|
|
1691
|
+
putT.run(str(r.team), str(r.kind), e?.event ?? null, num(r.year) ?? e?.year ?? null, str(r.comp_no), str(r.title), str(r.team_index), num(r.seq), str(r.school_id), str(r.school_print_materialized), ord++);
|
|
1692
|
+
}
|
|
1693
|
+
const putM = out.prepare("INSERT INTO roster_member VALUES (?, ?, ?, ?, ?, ?, ?)");
|
|
1694
|
+
for (const r of t.roster_members)
|
|
1695
|
+
putM.run(str(r.team), num(r.seq), typeof r.name_printed === "string" ? r.name_printed : "", str(r.name), num(r.yob), num(r.dancer_id), ord++);
|
|
1696
|
+
const putJ = out.prepare("INSERT INTO result_roster VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
1697
|
+
for (const r of t.team_result_rosters) {
|
|
1698
|
+
const e = ey.get(str(r.event_year) ?? "");
|
|
1699
|
+
putJ.run(str(r.team), e?.event ?? null, e?.year ?? null, str(r.comp_no), str(r.school_id), str(r.result_address), str(r.join_kind), num(r.via_split) === 1 ? 1 : 0, num(r.roster_teams), num(r.result_teams), str(r.roster_index), str(r.result_letter), ord++);
|
|
1700
|
+
}
|
|
1701
|
+
for (const s of TEAMS_INDEXES)
|
|
1702
|
+
out.run(s);
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
var EVENTS_SCHEMA = `CREATE TABLE event_year(event TEXT NOT NULL, year INTEGER NOT NULL,
|
|
1706
|
+
date_lo TEXT, date_hi TEXT, resolution TEXT, source TEXT, date_printed TEXT, place TEXT,
|
|
1707
|
+
place_printed TEXT, PRIMARY KEY(event, year))`;
|
|
1708
|
+
var eventsIndex = {
|
|
1709
|
+
name: "events",
|
|
1710
|
+
version: "1",
|
|
1711
|
+
inputs: [tableAsset("event_years")],
|
|
1712
|
+
says: "every event-year: its dates (and how fine they are) and its place",
|
|
1713
|
+
load: (rel) => readTables(rel, ["event_years"]),
|
|
1714
|
+
build(_rel, out, loaded) {
|
|
1715
|
+
const rows = loaded.event_years ?? [];
|
|
1716
|
+
out.run(EVENTS_SCHEMA);
|
|
1717
|
+
const put = out.prepare("INSERT OR IGNORE INTO event_year VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
1718
|
+
for (const r of rows) {
|
|
1719
|
+
const event = str(r.event);
|
|
1720
|
+
const year = num(r.year);
|
|
1721
|
+
if (event === null || year === null)
|
|
1722
|
+
continue;
|
|
1723
|
+
put.run(event, year, str(r.date_lo), str(r.date_hi), str(r.resolution), str(r.source), JSON.stringify(strs(r.date_printed)), str(r.place), JSON.stringify(strs(r.place_printed)));
|
|
1724
|
+
}
|
|
1725
|
+
out.run("CREATE INDEX event_year_event ON event_year(event COLLATE NOCASE)");
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1728
|
+
function whereOf(q, prefix = "") {
|
|
1729
|
+
const where = [];
|
|
1730
|
+
const args = [];
|
|
1731
|
+
if (q.event !== undefined) {
|
|
1732
|
+
where.push(`${prefix}event = ? COLLATE NOCASE`);
|
|
1733
|
+
args.push(q.event);
|
|
1734
|
+
}
|
|
1735
|
+
if (q.year !== undefined) {
|
|
1736
|
+
where.push(`${prefix}year = ?`);
|
|
1737
|
+
args.push(q.year);
|
|
1738
|
+
}
|
|
1739
|
+
return { sql: where.length === 0 ? "" : ` WHERE ${where.join(" AND ")}`, args };
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
class TeamFinder2 {
|
|
1743
|
+
rel;
|
|
1744
|
+
constructor(rel) {
|
|
1745
|
+
this.rel = rel;
|
|
1746
|
+
}
|
|
1747
|
+
db() {
|
|
1748
|
+
return this.rel.indexDb("teams");
|
|
1749
|
+
}
|
|
1750
|
+
async results(q = {}) {
|
|
1751
|
+
const db = await this.db();
|
|
1752
|
+
const w = whereOf(q);
|
|
1753
|
+
const rows = db.query(`SELECT address, competition, event, year, card, comp_no, title, category, hands, letter,
|
|
1754
|
+
label, name_printed, school_printed, school_loc, school_id, place, place_printed, tie,
|
|
1755
|
+
competitors, atts
|
|
1756
|
+
FROM result${w.sql} ORDER BY year, event, competition, place IS NULL, place, ord`).all(...w.args);
|
|
1757
|
+
return {
|
|
1758
|
+
tag: this.rel.tag,
|
|
1759
|
+
asked: q,
|
|
1760
|
+
results: rows.map((r) => ({ ...r, atts: JSON.parse(r.atts) }))
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
async rosters(q = {}) {
|
|
1764
|
+
const db = await this.db();
|
|
1765
|
+
const w = whereOf(q, "t.");
|
|
1766
|
+
const teams = db.query(`SELECT team, kind, event, year, comp_no, title, team_index, seq, school_id, school_printed
|
|
1767
|
+
FROM roster_team t${w.sql} ORDER BY year, event, ord`).all(...w.args);
|
|
1768
|
+
const members = new Map;
|
|
1769
|
+
for (const m of db.query(`SELECT m.team AS team, m.seq AS seq, m.name_printed AS name_printed, m.name AS name,
|
|
1770
|
+
m.yob AS yob, m.dancer_id AS dancer_id
|
|
1771
|
+
FROM roster_member m JOIN roster_team t ON t.team = m.team${w.sql}
|
|
1772
|
+
ORDER BY m.team, m.seq, m.ord`).all(...w.args)) {
|
|
1773
|
+
const { team, ...rest } = m;
|
|
1774
|
+
let list = members.get(team);
|
|
1775
|
+
if (list === undefined)
|
|
1776
|
+
members.set(team, list = []);
|
|
1777
|
+
list.push({ ...rest, dancer_scope: scope(rest.dancer_id) });
|
|
1778
|
+
}
|
|
1779
|
+
return {
|
|
1780
|
+
tag: this.rel.tag,
|
|
1781
|
+
asked: q,
|
|
1782
|
+
teams: teams.map((t) => ({ ...t, members: members.get(t.team) ?? [] }))
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
async resultRosters(q = {}) {
|
|
1786
|
+
const db = await this.db();
|
|
1787
|
+
const w = whereOf(q);
|
|
1788
|
+
const rows = db.query(`SELECT team, event, year, comp_no, school_id, result_address, join_kind, via_split,
|
|
1789
|
+
roster_teams, result_teams, roster_index, result_letter
|
|
1790
|
+
FROM result_roster${w.sql} ORDER BY year, event, ord`).all(...w.args);
|
|
1791
|
+
return {
|
|
1792
|
+
tag: this.rel.tag,
|
|
1793
|
+
asked: q,
|
|
1794
|
+
joins: rows.map((r) => ({ ...r, via_split: r.via_split === 1 }))
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
class EventFinder2 {
|
|
1800
|
+
rel;
|
|
1801
|
+
constructor(rel) {
|
|
1802
|
+
this.rel = rel;
|
|
1803
|
+
}
|
|
1804
|
+
async years(q = {}) {
|
|
1805
|
+
const db = await this.rel.indexDb("events");
|
|
1806
|
+
const w = whereOf(q);
|
|
1807
|
+
const rows = db.query(`SELECT event, year, date_lo, date_hi, resolution, source, date_printed, place, place_printed
|
|
1808
|
+
FROM event_year${w.sql} ORDER BY year, event`).all(...w.args);
|
|
1809
|
+
return {
|
|
1810
|
+
tag: this.rel.tag,
|
|
1811
|
+
asked: q,
|
|
1812
|
+
years: rows.map((r) => ({
|
|
1813
|
+
...r,
|
|
1814
|
+
date_printed: JSON.parse(r.date_printed),
|
|
1815
|
+
place_printed: JSON.parse(r.place_printed)
|
|
1816
|
+
}))
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1584
1821
|
function defaultBuilders2() {
|
|
1585
|
-
return [schoolsIndex, feisesIndex, compsIndex, dancersIndex, staffIndex];
|
|
1822
|
+
return [schoolsIndex, feisesIndex, compsIndex, dancersIndex, staffIndex, teamsIndex, eventsIndex];
|
|
1586
1823
|
}
|
|
1587
1824
|
var indexDir = (rel) => join(rel.dir, "index");
|
|
1588
1825
|
var indexPath2 = (rel, name) => join(indexDir(rel), `${name}.sqlite`);
|
|
@@ -1618,6 +1855,8 @@ function buildIndex2(rel, b, o) {
|
|
|
1618
1855
|
const key = indexKey2(rel, b);
|
|
1619
1856
|
if (!o.force && keyOf(out) === key)
|
|
1620
1857
|
return out;
|
|
1858
|
+
if (b.load !== undefined && o.loaded === undefined)
|
|
1859
|
+
throw new Error(`archive-client: index "${b.name}" reads its inputs first: build it with ensureIndex (or rel.index)`);
|
|
1621
1860
|
mkdirSync(indexDir(rel), { recursive: true });
|
|
1622
1861
|
const tmp = `${out}.building.${process.pid}`;
|
|
1623
1862
|
rmSync(tmp, { force: true });
|
|
@@ -1627,7 +1866,7 @@ function buildIndex2(rel, b, o) {
|
|
|
1627
1866
|
db.run("PRAGMA journal_mode = OFF");
|
|
1628
1867
|
db.run("PRAGMA synchronous = OFF");
|
|
1629
1868
|
db.run("CREATE TABLE meta(k TEXT PRIMARY KEY, v TEXT NOT NULL)");
|
|
1630
|
-
db.transaction(() => b.build(rel, db))();
|
|
1869
|
+
db.transaction(() => b.build(rel, db, o.loaded))();
|
|
1631
1870
|
const put = db.prepare("INSERT INTO meta(k, v) VALUES (?, ?)");
|
|
1632
1871
|
put.run("key", key);
|
|
1633
1872
|
put.run("builder", b.name);
|
|
@@ -1644,6 +1883,12 @@ function buildIndex2(rel, b, o) {
|
|
|
1644
1883
|
o.log?.(`index ${rel.tag} ${b.name}: built in ${((performance.now() - t0) / 1000).toFixed(1)} s`);
|
|
1645
1884
|
return out;
|
|
1646
1885
|
}
|
|
1886
|
+
async function ensureIndex2(rel, b, o) {
|
|
1887
|
+
if (!o.force && isCurrent2(rel, b))
|
|
1888
|
+
return indexPath2(rel, b.name);
|
|
1889
|
+
const loaded = b.load === undefined ? undefined : await b.load(rel);
|
|
1890
|
+
return buildIndex2(rel, b, { ...o, loaded });
|
|
1891
|
+
}
|
|
1647
1892
|
function listIndexes2(rel) {
|
|
1648
1893
|
const dir = indexDir(rel);
|
|
1649
1894
|
if (!existsSync(dir))
|
|
@@ -2053,6 +2298,81 @@ var FEATURES2 = [
|
|
|
2053
2298
|
since: 265,
|
|
2054
2299
|
calls: "comps.resolve, comps.syllabus, comps.syllabusFor, registrations.resolve (the comps index)"
|
|
2055
2300
|
},
|
|
2301
|
+
{
|
|
2302
|
+
feature: "teams",
|
|
2303
|
+
needs: [
|
|
2304
|
+
...[
|
|
2305
|
+
"slug",
|
|
2306
|
+
"address",
|
|
2307
|
+
"event",
|
|
2308
|
+
"year",
|
|
2309
|
+
"card",
|
|
2310
|
+
"comp_no",
|
|
2311
|
+
"title",
|
|
2312
|
+
"category",
|
|
2313
|
+
"hands",
|
|
2314
|
+
"letter",
|
|
2315
|
+
"label",
|
|
2316
|
+
"name_printed",
|
|
2317
|
+
"school_printed",
|
|
2318
|
+
"school_loc",
|
|
2319
|
+
"school_id",
|
|
2320
|
+
"place",
|
|
2321
|
+
"place_printed",
|
|
2322
|
+
"tie",
|
|
2323
|
+
"competitors",
|
|
2324
|
+
"atts"
|
|
2325
|
+
].map((c) => `table:team_results.${c}`),
|
|
2326
|
+
...[
|
|
2327
|
+
"team",
|
|
2328
|
+
"kind",
|
|
2329
|
+
"event_year",
|
|
2330
|
+
"year",
|
|
2331
|
+
"comp_no",
|
|
2332
|
+
"title",
|
|
2333
|
+
"team_index",
|
|
2334
|
+
"seq",
|
|
2335
|
+
"school_id",
|
|
2336
|
+
"school_print_materialized"
|
|
2337
|
+
].map((c) => `table:roster_teams.${c}`),
|
|
2338
|
+
...["team", "seq", "name_printed", "name", "yob", "dancer_id"].map((c) => `table:roster_members.${c}`),
|
|
2339
|
+
...[
|
|
2340
|
+
"event_year",
|
|
2341
|
+
"comp_no",
|
|
2342
|
+
"team",
|
|
2343
|
+
"result_address",
|
|
2344
|
+
"school_id",
|
|
2345
|
+
"join_kind",
|
|
2346
|
+
"via_split",
|
|
2347
|
+
"roster_teams",
|
|
2348
|
+
"result_teams",
|
|
2349
|
+
"roster_index",
|
|
2350
|
+
"result_letter"
|
|
2351
|
+
].map((c) => `table:team_result_rosters.${c}`),
|
|
2352
|
+
"table:event_years.event_year",
|
|
2353
|
+
"table:event_years.event",
|
|
2354
|
+
"table:event_years.year"
|
|
2355
|
+
],
|
|
2356
|
+
since: 260,
|
|
2357
|
+
calls: "teams.results, teams.rosters, teams.resultRosters (the teams index)"
|
|
2358
|
+
},
|
|
2359
|
+
{
|
|
2360
|
+
feature: "events",
|
|
2361
|
+
needs: [
|
|
2362
|
+
"event_year",
|
|
2363
|
+
"event",
|
|
2364
|
+
"year",
|
|
2365
|
+
"date_lo",
|
|
2366
|
+
"date_hi",
|
|
2367
|
+
"resolution",
|
|
2368
|
+
"source",
|
|
2369
|
+
"date_printed",
|
|
2370
|
+
"place",
|
|
2371
|
+
"place_printed"
|
|
2372
|
+
].map((c) => `table:event_years.${c}`),
|
|
2373
|
+
since: 260,
|
|
2374
|
+
calls: "events.years (the events index)"
|
|
2375
|
+
},
|
|
2056
2376
|
{
|
|
2057
2377
|
feature: "staff",
|
|
2058
2378
|
needs: [
|
|
@@ -2067,6 +2387,13 @@ var FEATURES2 = [
|
|
|
2067
2387
|
}
|
|
2068
2388
|
];
|
|
2069
2389
|
async function hasNeed(probe, n) {
|
|
2390
|
+
const t = /^table:([A-Za-z_]+)(?:\.([A-Za-z_]+))?$/.exec(n);
|
|
2391
|
+
if (t !== null) {
|
|
2392
|
+
if (probe.tableColumns === undefined)
|
|
2393
|
+
return false;
|
|
2394
|
+
const cols = await probe.tableColumns(t[1]);
|
|
2395
|
+
return t[2] === undefined ? cols.size > 0 : cols.has(t[2]);
|
|
2396
|
+
}
|
|
2070
2397
|
const m = /^(results|majors):([A-Za-z_]+)(?:\.([A-Za-z_]+))?$/.exec(n);
|
|
2071
2398
|
if (m === null)
|
|
2072
2399
|
return probe.publishes(n);
|
|
@@ -2138,6 +2465,8 @@ class Release2 {
|
|
|
2138
2465
|
staff;
|
|
2139
2466
|
comps;
|
|
2140
2467
|
registrations;
|
|
2468
|
+
teams;
|
|
2469
|
+
events;
|
|
2141
2470
|
opts;
|
|
2142
2471
|
handles = new Map;
|
|
2143
2472
|
remoteAssets = null;
|
|
@@ -2158,6 +2487,8 @@ class Release2 {
|
|
|
2158
2487
|
this.staff = new StaffFinder2(this);
|
|
2159
2488
|
this.comps = new CompResolver2(this);
|
|
2160
2489
|
this.registrations = new RegistrationResolver2(this, this.comps, opts.dancerResolver);
|
|
2490
|
+
this.teams = new TeamFinder2(this);
|
|
2491
|
+
this.events = new EventFinder2(this);
|
|
2161
2492
|
}
|
|
2162
2493
|
receipt() {
|
|
2163
2494
|
return readReceipt(this.dir) ?? { tag: this.tag, source: "unknown", files: {} };
|
|
@@ -2202,7 +2533,14 @@ class Release2 {
|
|
|
2202
2533
|
await this.fetch(name);
|
|
2203
2534
|
return this.db(name);
|
|
2204
2535
|
},
|
|
2205
|
-
publishes: (asset) => this.publishes(asset)
|
|
2536
|
+
publishes: (asset) => this.publishes(asset),
|
|
2537
|
+
tableColumns: async (table) => {
|
|
2538
|
+
const asset = tableAsset(table);
|
|
2539
|
+
if (!await this.publishes(asset))
|
|
2540
|
+
return new Set;
|
|
2541
|
+
await this.fetch(asset);
|
|
2542
|
+
return parquetColumns(this.path(asset));
|
|
2543
|
+
}
|
|
2206
2544
|
};
|
|
2207
2545
|
checked = new Map;
|
|
2208
2546
|
async capability(feature) {
|
|
@@ -2299,7 +2637,7 @@ class Release2 {
|
|
|
2299
2637
|
if (FEATURES2.some((f) => f.feature === b.name))
|
|
2300
2638
|
await this.require(b.name);
|
|
2301
2639
|
await this.fetch(...b.inputs);
|
|
2302
|
-
return
|
|
2640
|
+
return ensureIndex2(this, b, { force: o.force ?? false, log: this.opts.log });
|
|
2303
2641
|
}
|
|
2304
2642
|
async indexDb(name) {
|
|
2305
2643
|
const key = `index:${name}`;
|
|
@@ -2518,7 +2856,7 @@ class ReleaseManager2 {
|
|
|
2518
2856
|
await rel.fetch(...b.inputs);
|
|
2519
2857
|
built.push({
|
|
2520
2858
|
name,
|
|
2521
|
-
path:
|
|
2859
|
+
path: await ensureIndex2(rel, b, { force: o.force ?? false, log: this.opts.log })
|
|
2522
2860
|
});
|
|
2523
2861
|
}
|
|
2524
2862
|
rel.close();
|
|
@@ -2591,4 +2929,4 @@ function releases2(opts = {}) {
|
|
|
2591
2929
|
return new ReleaseManager2(opts);
|
|
2592
2930
|
}
|
|
2593
2931
|
|
|
2594
|
-
export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, nameOrderOf, DancerResolver2, readPopulation2, canonicalOf2, CompResolver2, NO_CALENDAR2, FeisFinder2, StaffFinder2, SchoolResolver2, defaultBuilders2, indexPath2, indexKey2, isCurrent2, buildIndex2, listIndexes2, readPin2, youngest2, dancerResolverOf2, RegistrationResolver2, ghRemote2, httpsRemote2, fallbackRemote2, setRemote2, columnsOf2, FEATURES2, capabilityOf2, needsRelease2, resolveTag2, Release2, openRelease2, openCached2, ReleaseManager2, releases2 };
|
|
2932
|
+
export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, nameOrderOf, DancerResolver2, readPopulation2, canonicalOf2, CompResolver2, NO_CALENDAR2, FeisFinder2, StaffFinder2, SchoolResolver2, TeamFinder2, EventFinder2, defaultBuilders2, indexPath2, indexKey2, isCurrent2, buildIndex2, ensureIndex2, listIndexes2, readPin2, youngest2, dancerResolverOf2, RegistrationResolver2, ghRemote2, httpsRemote2, fallbackRemote2, setRemote2, columnsOf2, FEATURES2, capabilityOf2, needsRelease2, resolveTag2, Release2, openRelease2, openCached2, ReleaseManager2, releases2 };
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
nameOrderOf,
|
|
5
5
|
openRelease2,
|
|
6
6
|
releases2
|
|
7
|
-
} from "./chunks/index-
|
|
7
|
+
} from "./chunks/index-v8e0qe9f.js";
|
|
8
8
|
import"./chunks/index-hne1wadw.js";
|
|
9
9
|
import"./chunks/index-hghdy980.js";
|
|
10
10
|
|
|
@@ -50,6 +50,12 @@ var USAGE = `usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]
|
|
|
50
50
|
majors [--release <spec>] [--event <e>] [--year <y>] [--comp <competition>] [--dancer <id>]
|
|
51
51
|
the majors: every event and its years; an event's
|
|
52
52
|
competitions; a competition's, an event's or a dancer's rows
|
|
53
|
+
teams [--release <spec>] [--event <e>] [--year <y>] [--rosters | --joins]
|
|
54
|
+
the team result rows of an event-year; with --rosters its
|
|
55
|
+
roster teams and their dancers; with --joins how each roster
|
|
56
|
+
team meets the results
|
|
57
|
+
events [--release <spec>] [--event <e>] [--year <y>]
|
|
58
|
+
each event-year's dates and place
|
|
53
59
|
teacher [--release <spec>] <name|TD number>
|
|
54
60
|
the staff lists' people: where they taught, the register,
|
|
55
61
|
where they sat
|
|
@@ -313,6 +319,14 @@ async function resolveVerb(rel, what, args, p, say, stdin) {
|
|
|
313
319
|
throw new Error("resolve comp|dancer|registrations");
|
|
314
320
|
}
|
|
315
321
|
}
|
|
322
|
+
function eventYearOf(p) {
|
|
323
|
+
const event = p.flags.get("--event");
|
|
324
|
+
const year = p.flags.get("--year");
|
|
325
|
+
return {
|
|
326
|
+
...event !== undefined ? { event } : {},
|
|
327
|
+
...year !== undefined ? { year: Number(year) } : {}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
316
330
|
async function main(argv, io = {}) {
|
|
317
331
|
const say = io.say ?? ((s) => console.log(s));
|
|
318
332
|
const log = io.log ?? ((s) => console.error(s));
|
|
@@ -630,6 +644,56 @@ async function main(argv, io = {}) {
|
|
|
630
644
|
rel.close();
|
|
631
645
|
}
|
|
632
646
|
}
|
|
647
|
+
case "teams": {
|
|
648
|
+
const rel = await openRelease2(spec, { ...opts, assets: [] });
|
|
649
|
+
try {
|
|
650
|
+
const q = eventYearOf(p);
|
|
651
|
+
if (p.bools.has("--rosters")) {
|
|
652
|
+
const a = await rel.teams.rosters(q);
|
|
653
|
+
out(a, () => {
|
|
654
|
+
for (const t of a.teams) {
|
|
655
|
+
say(` ${t.year} ${t.event ?? "?"} ${t.comp_no ?? "-"} ${t.team_index ?? "-"} ${t.school_id ?? "(no id)"} "${t.school_printed ?? ""}" ${t.title ?? ""} [${t.kind}]`);
|
|
656
|
+
for (const m of t.members)
|
|
657
|
+
say(` ${m.seq} ${m.dancer_id ?? "-"} ${m.name_printed}${m.yob === null ? "" : ` (${m.yob})`}`);
|
|
658
|
+
}
|
|
659
|
+
say(`${a.teams.length} roster team(s) in ${a.tag}`);
|
|
660
|
+
});
|
|
661
|
+
return a.teams.length === 0 ? 1 : 0;
|
|
662
|
+
}
|
|
663
|
+
if (p.bools.has("--joins")) {
|
|
664
|
+
const a = await rel.teams.resultRosters(q);
|
|
665
|
+
out(a, () => {
|
|
666
|
+
for (const j of a.joins)
|
|
667
|
+
say(` ${j.year ?? "?"} ${j.event ?? "?"} ${j.comp_no ?? "-"} ${j.team} ${j.join_kind} ${j.result_address ?? "-"} roster ${j.roster_index ?? "-"} result ${j.result_letter ?? "-"}`);
|
|
668
|
+
say(`${a.joins.length} join(s) in ${a.tag}`);
|
|
669
|
+
});
|
|
670
|
+
return a.joins.length === 0 ? 1 : 0;
|
|
671
|
+
}
|
|
672
|
+
const a = await rel.teams.results(q);
|
|
673
|
+
out(a, () => {
|
|
674
|
+
for (const r of a.results)
|
|
675
|
+
say(` ${r.address} ${r.place_printed ?? "-"} ${r.label ?? "-"} ${r.school_id ?? "(no id)"} "${r.school_printed ?? ""}" ${r.title ?? ""}${r.atts.length > 0 ? ` [${r.atts.join(",")}]` : ""}`);
|
|
676
|
+
say(`${a.results.length} team result(s) in ${a.tag}`);
|
|
677
|
+
});
|
|
678
|
+
return a.results.length === 0 ? 1 : 0;
|
|
679
|
+
} finally {
|
|
680
|
+
rel.close();
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
case "events": {
|
|
684
|
+
const rel = await openRelease2(spec, { ...opts, assets: [] });
|
|
685
|
+
try {
|
|
686
|
+
const a = await rel.events.years(eventYearOf(p));
|
|
687
|
+
out(a, () => {
|
|
688
|
+
for (const y of a.years)
|
|
689
|
+
say(` ${y.event} ${y.year} ${y.date_lo ?? "?"}..${y.date_hi ?? "?"} (${y.resolution ?? "-"}, ${y.source ?? "-"}) ${y.place ?? "-"}${y.place_printed.length > 0 ? ` "${y.place_printed.join("; ")}"` : ""}`);
|
|
690
|
+
say(`${a.years.length} event-year(s) in ${a.tag}`);
|
|
691
|
+
});
|
|
692
|
+
return a.years.length === 0 ? 1 : 0;
|
|
693
|
+
} finally {
|
|
694
|
+
rel.close();
|
|
695
|
+
}
|
|
696
|
+
}
|
|
633
697
|
case "teacher":
|
|
634
698
|
case "adjudicator": {
|
|
635
699
|
if (rest.length === 0) {
|
package/dist/index.js
CHANGED
|
@@ -17,11 +17,14 @@ import {
|
|
|
17
17
|
FeisFinder2,
|
|
18
18
|
StaffFinder2,
|
|
19
19
|
SchoolResolver2,
|
|
20
|
+
TeamFinder2,
|
|
21
|
+
EventFinder2,
|
|
20
22
|
defaultBuilders2,
|
|
21
23
|
indexPath2,
|
|
22
24
|
indexKey2,
|
|
23
25
|
isCurrent2,
|
|
24
26
|
buildIndex2,
|
|
27
|
+
ensureIndex2,
|
|
25
28
|
listIndexes2,
|
|
26
29
|
readPin2,
|
|
27
30
|
youngest2,
|
|
@@ -41,7 +44,7 @@ import {
|
|
|
41
44
|
openCached2,
|
|
42
45
|
ReleaseManager2,
|
|
43
46
|
releases2
|
|
44
|
-
} from "./chunks/index-
|
|
47
|
+
} from "./chunks/index-v8e0qe9f.js";
|
|
45
48
|
import {
|
|
46
49
|
cacheRoot2,
|
|
47
50
|
repoOf2
|
|
@@ -60,6 +63,7 @@ export {
|
|
|
60
63
|
CompResolver2 as CompResolver,
|
|
61
64
|
DATABASES2 as DATABASES,
|
|
62
65
|
DancerResolver2 as DancerResolver,
|
|
66
|
+
EventFinder2 as EventFinder,
|
|
63
67
|
FEATURES2 as FEATURES,
|
|
64
68
|
FEIS_LOCAL_ID_FLOOR2 as FEIS_LOCAL_ID_FLOOR,
|
|
65
69
|
FeisFinder2 as FeisFinder,
|
|
@@ -72,6 +76,7 @@ export {
|
|
|
72
76
|
ResultFinder2 as ResultFinder,
|
|
73
77
|
SchoolResolver2 as SchoolResolver,
|
|
74
78
|
StaffFinder2 as StaffFinder,
|
|
79
|
+
TeamFinder2 as TeamFinder,
|
|
75
80
|
buildIndex2 as buildIndex,
|
|
76
81
|
cacheRoot2 as cacheRoot,
|
|
77
82
|
canonicalOf2 as canonicalOf,
|
|
@@ -81,6 +86,7 @@ export {
|
|
|
81
86
|
cutOf2 as cutOf,
|
|
82
87
|
dancerResolverOf2 as dancerResolverOf,
|
|
83
88
|
defaultBuilders2 as defaultBuilders,
|
|
89
|
+
ensureIndex2 as ensureIndex,
|
|
84
90
|
fallbackRemote2 as fallbackRemote,
|
|
85
91
|
ghRemote2 as ghRemote,
|
|
86
92
|
httpsRemote2 as httpsRemote,
|
package/dist/types/cli.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import type { OpenOptions } from "./release.js";
|
|
3
|
-
export declare const USAGE = "usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]\n\n config repo <owner/name> set the user-local repository the releases come from\n list [--remote] the cached releases (size, files, indexes, the pin), and\n with --remote the published tags\n fetch [--release <spec>] [--assets results,majors,<file>...]\n download, verify and cache a release (default: results)\n pin <spec> the user-local pin: what a bare open (spec \"pinned\") opens\n unpin remove the pin (a bare open is then \"latest\")\n current the pin, and whether it is cached\n add <dir> --tag <tag> add a release built locally (its MANIFEST.json and files)\n to the cache under its tag\n index [--release <spec>] [<name>...] [--force]\n build the per-tag indexes (default: every one)\n verify [--release <spec>] re-hash every cached file against its receipt\n capabilities [--release <spec>] what the release carries of what the client reads, from\n its own schema: each feature full or missing (its calls\n refuse, naming the first cut that publishes it)\n prune [--keep N] [--dry-run] remove all but the N newest cached releases (default 1)\n and the pinned one\n rm <tag> [--dry-run] remove one cached release (not the pinned one)\n drop-indexes [<tag>] [--dry-run] remove the index files (of one release, or of every one)\n feis [--release <spec>] [--platform <p>] [--code <c>] [--id <id>] [--name <glob>] [--year <y>]\n the feises a query names\n series [--release <spec>] <name|host code|feis id>\n the series a name, a host code (fweb:ABC) or any feis id\n names: its years, hosts, codes and next instance\n school [--release <spec>] <print|school_id> [--region <code>]\n a school print or id to the published schools\n dancer [--release <spec>] <name> | --surname <s> | --like <glob> | --id <dancer_id>\n [--order surname-first|given-first] [--school <print|id>]\n [--yob <y> | --band <U10> [--year <y>] | --comp <competition>] [--as-of <date>]\n [--population girls|boys] [--rows]\n the dancers a name reaches, each with its evidence\n (name level, age, school, population) and the arm; the results of\n the one dancer where one answers (or --rows for each)\n results [--release <spec>] [--dancer <id>] [--feis <id>] [--comp <competition>]\n [--school <print|id>] [--year <y>] [--limit <n>]\n the published result rows, prints and ids\n majors [--release <spec>] [--event <e>] [--year <y>] [--comp <competition>] [--dancer <id>]\n the majors: every event and its years; an event's\n competitions; a competition's, an event's or a dancer's rows\n teacher [--release <spec>] <name|TD number>\n the staff lists' people: where they taught, the register,\n where they sat\n adjudicator [--release <spec>] <name|TD number>\n the register's and the panels' people: the same answer\n comp [--release <spec>] <feis> <code> [--year <y>] [--title <t>]\n a feis's own comp code to the comp it names, through that\n feis's syllabus (or the prior year's)\n syllabus [--release <spec>] <feis> [year]\n one feis-year's comps, in printed order\n syllabus [--release <spec>] --for <series> [year]\n a series' syllabus for a year: that year's where held,\n else the newest, saying which and what it is read from\n resolve comp [<feis> <code>] [--year <y>] [--title <t>]\n resolve dancer [<name>] [--school <s>] [--comp <feis>/<code> | --yob <y> | --band <b> --year <y>]\n [--order <o>] [--as-of <date>] [--population girls|boys]\n resolve registrations\n JSONL out, one answer per line; with no positional the\n questions are JSONL on stdin, one per line (a comp:\n {feis, code, year?, title?}, a dancer: {name, school?, age?,\n population?}, a registration: {name, school?, codes[], feis,\n year?, population?})\n\n <spec>: pinned (the default) | latest | a cut (42, 42.1) | a tag (archive-YYYY.MM.DD-N)";
|
|
3
|
+
export declare const USAGE = "usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]\n\n config repo <owner/name> set the user-local repository the releases come from\n list [--remote] the cached releases (size, files, indexes, the pin), and\n with --remote the published tags\n fetch [--release <spec>] [--assets results,majors,<file>...]\n download, verify and cache a release (default: results)\n pin <spec> the user-local pin: what a bare open (spec \"pinned\") opens\n unpin remove the pin (a bare open is then \"latest\")\n current the pin, and whether it is cached\n add <dir> --tag <tag> add a release built locally (its MANIFEST.json and files)\n to the cache under its tag\n index [--release <spec>] [<name>...] [--force]\n build the per-tag indexes (default: every one)\n verify [--release <spec>] re-hash every cached file against its receipt\n capabilities [--release <spec>] what the release carries of what the client reads, from\n its own schema: each feature full or missing (its calls\n refuse, naming the first cut that publishes it)\n prune [--keep N] [--dry-run] remove all but the N newest cached releases (default 1)\n and the pinned one\n rm <tag> [--dry-run] remove one cached release (not the pinned one)\n drop-indexes [<tag>] [--dry-run] remove the index files (of one release, or of every one)\n feis [--release <spec>] [--platform <p>] [--code <c>] [--id <id>] [--name <glob>] [--year <y>]\n the feises a query names\n series [--release <spec>] <name|host code|feis id>\n the series a name, a host code (fweb:ABC) or any feis id\n names: its years, hosts, codes and next instance\n school [--release <spec>] <print|school_id> [--region <code>]\n a school print or id to the published schools\n dancer [--release <spec>] <name> | --surname <s> | --like <glob> | --id <dancer_id>\n [--order surname-first|given-first] [--school <print|id>]\n [--yob <y> | --band <U10> [--year <y>] | --comp <competition>] [--as-of <date>]\n [--population girls|boys] [--rows]\n the dancers a name reaches, each with its evidence\n (name level, age, school, population) and the arm; the results of\n the one dancer where one answers (or --rows for each)\n results [--release <spec>] [--dancer <id>] [--feis <id>] [--comp <competition>]\n [--school <print|id>] [--year <y>] [--limit <n>]\n the published result rows, prints and ids\n majors [--release <spec>] [--event <e>] [--year <y>] [--comp <competition>] [--dancer <id>]\n the majors: every event and its years; an event's\n competitions; a competition's, an event's or a dancer's rows\n teams [--release <spec>] [--event <e>] [--year <y>] [--rosters | --joins]\n the team result rows of an event-year; with --rosters its\n roster teams and their dancers; with --joins how each roster\n team meets the results\n events [--release <spec>] [--event <e>] [--year <y>]\n each event-year's dates and place\n teacher [--release <spec>] <name|TD number>\n the staff lists' people: where they taught, the register,\n where they sat\n adjudicator [--release <spec>] <name|TD number>\n the register's and the panels' people: the same answer\n comp [--release <spec>] <feis> <code> [--year <y>] [--title <t>]\n a feis's own comp code to the comp it names, through that\n feis's syllabus (or the prior year's)\n syllabus [--release <spec>] <feis> [year]\n one feis-year's comps, in printed order\n syllabus [--release <spec>] --for <series> [year]\n a series' syllabus for a year: that year's where held,\n else the newest, saying which and what it is read from\n resolve comp [<feis> <code>] [--year <y>] [--title <t>]\n resolve dancer [<name>] [--school <s>] [--comp <feis>/<code> | --yob <y> | --band <b> --year <y>]\n [--order <o>] [--as-of <date>] [--population girls|boys]\n resolve registrations\n JSONL out, one answer per line; with no positional the\n questions are JSONL on stdin, one per line (a comp:\n {feis, code, year?, title?}, a dancer: {name, school?, age?,\n population?}, a registration: {name, school?, codes[], feis,\n year?, population?})\n\n <spec>: pinned (the default) | latest | a cut (42, 42.1) | a tag (archive-YYYY.MM.DD-N)";
|
|
4
4
|
export interface Parsed {
|
|
5
5
|
positional: string[];
|
|
6
6
|
flags: Map<string, string>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { NAME_LEVELS, type NameLevel, nameLevel } from "./dancers-names.js";
|
|
|
5
5
|
export { type Comp, type CompAnswer, type CompKind, type CompQuery, CompResolver, type CompSource, type FeisPlace, type SyllabusAnswer, type SyllabusForAnswer, type SyllabusSource, type TitleCheck, canonicalOf, } from "./comps.js";
|
|
6
6
|
export { type ClientOptions, cacheRoot, repoOf } from "./config.js";
|
|
7
7
|
export { type Feis, type FeisAnswer, FeisFinder, type FeisQuery, NO_CALENDAR, type Series, type SeriesAnswer, type SeriesInstance, type SeriesNext, } from "./feises.js";
|
|
8
|
-
export { type IndexBuilder, type IndexFile, buildIndex, defaultBuilders, indexKey, indexPath, isCurrent, listIndexes, } from "./indexes.js";
|
|
8
|
+
export { type IndexBuilder, type IndexFile, buildIndex, defaultBuilders, ensureIndex, indexKey, indexPath, isCurrent, listIndexes, } from "./indexes.js";
|
|
9
9
|
export { type CachedRelease, type ReleaseList, ReleaseManager, type RemovalPlan, releases, } from "./manager.js";
|
|
10
10
|
export { type Registered, type Sat, type StaffAnswer, StaffFinder, type StaffPerson, type Taught, type TdLink, } from "./people.js";
|
|
11
11
|
export { type Pin, readPin } from "./pin.js";
|
|
@@ -15,4 +15,5 @@ export { type MajorCompetition, type MajorEvent, MajorsFinder, type ResultAnswer
|
|
|
15
15
|
export { type DancerAnswerLike, type DancerQueryLike, type DancerResolverLike, type DancerUnavailable, type RegistrationRow, type ResolvedDancer, RegistrationResolver, type ResolvedRegistration, dancerResolverOf, youngest, } from "./registrations.js";
|
|
16
16
|
export { type SchoolAnswer, type SchoolCandidate, type SchoolQuery, SchoolResolver, type SchoolRung, } from "./schools.js";
|
|
17
17
|
export { type Capability, FEATURES, type Feature, type SchemaProbe, capabilityOf, columnsOf, needsRelease, } from "./schema.js";
|
|
18
|
+
export { EventFinder, type EventYear, type EventYearQuery, type ResultRoster, type RosterMember, type RosterTeam, TeamFinder, type TeamResult, } from "./teams.js";
|
|
18
19
|
export { ARCHIVE_TAG, type ArchiveTag, type ReleaseSpec, compareTagsNewestFirst, cutOf, parseArchiveTag, parseReleaseSpec, pickTag, renderArchiveTag, } from "./tag.js";
|
package/dist/types/indexes.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ export interface IndexBuilder {
|
|
|
6
6
|
readonly inputs: readonly string[];
|
|
7
7
|
readonly says: string;
|
|
8
8
|
keyParts?(rel: Release): string[];
|
|
9
|
-
|
|
9
|
+
load?(rel: Release): Promise<unknown>;
|
|
10
|
+
build(rel: Release, out: Database, loaded?: unknown): void;
|
|
10
11
|
}
|
|
11
12
|
export declare function defaultBuilders(): IndexBuilder[];
|
|
12
13
|
export declare const indexDir: (rel: {
|
|
@@ -21,7 +22,12 @@ export declare function isCurrent(rel: Release, b: IndexBuilder): boolean;
|
|
|
21
22
|
export declare function buildIndex(rel: Release, b: IndexBuilder, o: {
|
|
22
23
|
force: boolean;
|
|
23
24
|
log?: (line: string) => void;
|
|
25
|
+
loaded?: unknown;
|
|
24
26
|
}): string;
|
|
27
|
+
export declare function ensureIndex(rel: Release, b: IndexBuilder, o: {
|
|
28
|
+
force: boolean;
|
|
29
|
+
log?: (line: string) => void;
|
|
30
|
+
}): Promise<string>;
|
|
25
31
|
export interface IndexFile {
|
|
26
32
|
name: string;
|
|
27
33
|
path: string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const tableAsset: (table: string) => string;
|
|
2
|
+
export declare function plain(v: unknown): unknown;
|
|
3
|
+
export declare function readParquet(path: string, columns?: readonly string[]): Promise<Record<string, unknown>[]>;
|
|
4
|
+
export declare function parquetColumns(path: string): Promise<Set<string>>;
|
package/dist/types/release.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { MajorsFinder, ResultFinder } from "./results.js";
|
|
|
12
12
|
import { type Capability } from "./schema.js";
|
|
13
13
|
import { type SchoolReaderChoice, SchoolResolver } from "./schools.js";
|
|
14
14
|
import { type ReleaseSpec } from "./tag.js";
|
|
15
|
+
import { EventFinder, TeamFinder } from "./teams.js";
|
|
15
16
|
export interface OpenOptions extends ClientOptions {
|
|
16
17
|
assets?: readonly string[];
|
|
17
18
|
remote?: Remote;
|
|
@@ -41,6 +42,8 @@ export declare class Release {
|
|
|
41
42
|
readonly staff: StaffFinder;
|
|
42
43
|
readonly comps: CompResolver;
|
|
43
44
|
readonly registrations: RegistrationResolver;
|
|
45
|
+
readonly teams: TeamFinder;
|
|
46
|
+
readonly events: EventFinder;
|
|
44
47
|
private readonly opts;
|
|
45
48
|
private readonly handles;
|
|
46
49
|
private remoteAssets;
|
package/dist/types/schema.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface Capability extends Feature {
|
|
|
14
14
|
export interface SchemaProbe {
|
|
15
15
|
db(name: string): Promise<Database>;
|
|
16
16
|
publishes(asset: string): Promise<boolean>;
|
|
17
|
+
tableColumns?(table: string): Promise<Set<string>>;
|
|
17
18
|
}
|
|
18
19
|
export declare function hasNeed(probe: SchemaProbe, n: string): Promise<boolean>;
|
|
19
20
|
export declare function capabilityOf(probe: SchemaProbe, f: Feature): Promise<Capability>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { IndexBuilder } from "./indexes.js";
|
|
2
|
+
import type { Release } from "./release.js";
|
|
3
|
+
export interface EventYearQuery {
|
|
4
|
+
event?: string;
|
|
5
|
+
year?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface TeamResult {
|
|
8
|
+
address: string;
|
|
9
|
+
competition: string;
|
|
10
|
+
event: string;
|
|
11
|
+
year: number;
|
|
12
|
+
card: string | null;
|
|
13
|
+
comp_no: string | null;
|
|
14
|
+
title: string | null;
|
|
15
|
+
category: string | null;
|
|
16
|
+
hands: number | null;
|
|
17
|
+
letter: string | null;
|
|
18
|
+
label: string | null;
|
|
19
|
+
name_printed: string | null;
|
|
20
|
+
school_printed: string | null;
|
|
21
|
+
school_loc: string | null;
|
|
22
|
+
school_id: string | null;
|
|
23
|
+
place: number | null;
|
|
24
|
+
place_printed: string | null;
|
|
25
|
+
tie: number | null;
|
|
26
|
+
competitors: number | null;
|
|
27
|
+
atts: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface RosterMember {
|
|
30
|
+
seq: number;
|
|
31
|
+
name_printed: string;
|
|
32
|
+
name: string | null;
|
|
33
|
+
yob: number | null;
|
|
34
|
+
dancer_id: number | null;
|
|
35
|
+
dancer_scope: "per-release" | "stable" | null;
|
|
36
|
+
}
|
|
37
|
+
export interface RosterTeam {
|
|
38
|
+
team: string;
|
|
39
|
+
kind: string;
|
|
40
|
+
event: string | null;
|
|
41
|
+
year: number;
|
|
42
|
+
comp_no: string | null;
|
|
43
|
+
title: string | null;
|
|
44
|
+
team_index: string | null;
|
|
45
|
+
seq: number | null;
|
|
46
|
+
school_id: string | null;
|
|
47
|
+
school_printed: string | null;
|
|
48
|
+
members: RosterMember[];
|
|
49
|
+
}
|
|
50
|
+
export interface ResultRoster {
|
|
51
|
+
team: string;
|
|
52
|
+
event: string | null;
|
|
53
|
+
year: number | null;
|
|
54
|
+
comp_no: string | null;
|
|
55
|
+
school_id: string | null;
|
|
56
|
+
result_address: string | null;
|
|
57
|
+
join_kind: string;
|
|
58
|
+
via_split: boolean;
|
|
59
|
+
roster_teams: number | null;
|
|
60
|
+
result_teams: number | null;
|
|
61
|
+
roster_index: string | null;
|
|
62
|
+
result_letter: string | null;
|
|
63
|
+
}
|
|
64
|
+
export interface EventYear {
|
|
65
|
+
event: string;
|
|
66
|
+
year: number;
|
|
67
|
+
date_lo: string | null;
|
|
68
|
+
date_hi: string | null;
|
|
69
|
+
resolution: string | null;
|
|
70
|
+
source: string | null;
|
|
71
|
+
date_printed: string[];
|
|
72
|
+
place: string | null;
|
|
73
|
+
place_printed: string[];
|
|
74
|
+
}
|
|
75
|
+
export declare const teamsIndex: IndexBuilder;
|
|
76
|
+
export declare const eventsIndex: IndexBuilder;
|
|
77
|
+
export declare class TeamFinder {
|
|
78
|
+
private readonly rel;
|
|
79
|
+
constructor(rel: Release);
|
|
80
|
+
private db;
|
|
81
|
+
results(q?: EventYearQuery): Promise<{
|
|
82
|
+
tag: string;
|
|
83
|
+
asked: EventYearQuery;
|
|
84
|
+
results: TeamResult[];
|
|
85
|
+
}>;
|
|
86
|
+
rosters(q?: EventYearQuery): Promise<{
|
|
87
|
+
tag: string;
|
|
88
|
+
asked: EventYearQuery;
|
|
89
|
+
teams: RosterTeam[];
|
|
90
|
+
}>;
|
|
91
|
+
resultRosters(q?: EventYearQuery): Promise<{
|
|
92
|
+
tag: string;
|
|
93
|
+
asked: EventYearQuery;
|
|
94
|
+
joins: ResultRoster[];
|
|
95
|
+
}>;
|
|
96
|
+
}
|
|
97
|
+
export declare class EventFinder {
|
|
98
|
+
private readonly rel;
|
|
99
|
+
constructor(rel: Release);
|
|
100
|
+
years(q?: EventYearQuery): Promise<{
|
|
101
|
+
tag: string;
|
|
102
|
+
asked: EventYearQuery;
|
|
103
|
+
years: EventYear[];
|
|
104
|
+
}>;
|
|
105
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finbheara/archive-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@finbheara/domain": "0.49.0",
|
|
48
|
-
"@finbheara/names": "0.22.0"
|
|
48
|
+
"@finbheara/names": "0.22.0",
|
|
49
|
+
"hyparquet": "1.31.2"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"typescript": "7.0.2"
|