@finbheara/archive-client 0.7.0 → 0.8.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 +15 -4
- package/dist/chunks/{index-4ph8n4ga.js → index-mg6dw0y5.js} +288 -231
- package/dist/cli.js +2 -2
- package/dist/index.js +3 -3
- package/dist/types/dancers.d.ts +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -234,7 +234,17 @@ level of the names package: the print, the key, the folded key, the tokens in an
|
|
|
234
234
|
(a middle name one print omits: `nameRelation`). `age` is how the dancer's published birth window
|
|
235
235
|
meets the asked age (`Age.fitsIn`, `overlaps`): `fits`, `overlaps`, `contradicts`, `unknown`.
|
|
236
236
|
`school` is how the dancer's schools meet the asked one: `same` (the latest school as of `asOf`),
|
|
237
|
-
`
|
|
237
|
+
`lineage` (the latest school is one the release's lineage joins to the asked one), `moved` (an
|
|
238
|
+
earlier one is the asked one), `none`, `unresolved` (the print reached no school).
|
|
239
|
+
|
|
240
|
+
THE LINEAGE is the release's `school-lineage.jsonl`: every line joins its `id` and its `other`,
|
|
241
|
+
whatever its direction (`precedes`, `succeeds`) or shape (a rename, a split, a merger, an absorb,
|
|
242
|
+
a relation), so a file carrying one end of an edge reads as one carrying both. The whole line
|
|
243
|
+
joins: every school reached through any number of edges, in either direction (the two halves of a
|
|
244
|
+
split are joined through their predecessor, the schools of a merger through their successor). An
|
|
245
|
+
earlier row at a joined school alone is no match. `r.school.lineage` lists the ids joined to the
|
|
246
|
+
asked ones. A release that publishes no lineage (feature `schools.lineage`) joins
|
|
247
|
+
nothing and its answer carries no `lineage`, never refused.
|
|
238
248
|
|
|
239
249
|
`population` is how the dancer's published majors population meets the asked one: `same`,
|
|
240
250
|
`contradicts`, `both`, `unknown`, `unasked`. A dancer's population is read only from its rows at
|
|
@@ -251,12 +261,13 @@ majors solo competitions and in none of the asked one's (listed in `excluded`, `
|
|
|
251
261
|
`"population"`, and a candidate contradicting both says `age`). A dancer published in both, or in
|
|
252
262
|
neither, stays. A population word that names no one population (`mixed`) narrows nothing, and the
|
|
253
263
|
answer says so (`r.population.note`). The school is evidence and order, never a reason
|
|
254
|
-
to drop anyone: candidates whose latest school is the asked one
|
|
255
|
-
danced for it, then the rest. A school row meets the asked school by its id, else by its print
|
|
264
|
+
to drop anyone: candidates whose latest school is the asked one, or one the lineage joins to it,
|
|
265
|
+
come first, then those that ever danced for it, then the rest. A school row meets the asked school by its id, else by its print
|
|
256
266
|
(one `rel.schools` resolves to the asked ids), so a latest print the release
|
|
257
267
|
filed under no id is still `same`. `unique` is one candidate left after the age, never a best
|
|
258
268
|
guess promoted. Where the school singles out one candidate, `preferred` names it with the reason,
|
|
259
|
-
for a caller to weigh, and `arm` stays as counted.
|
|
269
|
+
for a caller to weigh, and `arm` stays as counted. A `lineage` match ranks with `same`: one of
|
|
270
|
+
each singles out neither. The `score` orders candidates and is no
|
|
260
271
|
probability.
|
|
261
272
|
|
|
262
273
|
A FEIS-LOCAL ID (`FEIS_LOCAL_ID_FLOOR`, 2^34, and up) is valid in its release only: a candidate and a result row
|
|
@@ -106,6 +106,215 @@ function nameLevel2(q, c) {
|
|
|
106
106
|
return null;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
import { readFileSync } from "fs";
|
|
110
|
+
var SCHEMA = `
|
|
111
|
+
CREATE TABLE feis(
|
|
112
|
+
feis_id TEXT PRIMARY KEY, source TEXT NOT NULL, token TEXT NOT NULL, host_id TEXT NOT NULL,
|
|
113
|
+
name TEXT NOT NULL, date_lo TEXT, date_hi TEXT, date_printed TEXT, year INTEGER,
|
|
114
|
+
location TEXT, instance_id TEXT, brand_id TEXT, regions TEXT NOT NULL,
|
|
115
|
+
competitions INTEGER NOT NULL, rows INTEGER NOT NULL, captures INTEGER NOT NULL
|
|
116
|
+
);
|
|
117
|
+
CREATE TABLE redirect(old_id TEXT PRIMARY KEY, new_id TEXT NOT NULL, kind TEXT NOT NULL);
|
|
118
|
+
`;
|
|
119
|
+
var INDEXES = [
|
|
120
|
+
"CREATE INDEX feis_host ON feis(host_id COLLATE NOCASE)",
|
|
121
|
+
"CREATE INDEX feis_token ON feis(token)",
|
|
122
|
+
"CREATE INDEX feis_source ON feis(source)",
|
|
123
|
+
"CREATE INDEX feis_instance ON feis(instance_id)",
|
|
124
|
+
"CREATE INDEX feis_brand ON feis(brand_id)",
|
|
125
|
+
"CREATE INDEX feis_year ON feis(year)"
|
|
126
|
+
];
|
|
127
|
+
function readJsonl(path) {
|
|
128
|
+
return readFileSync(path, "utf8").split(`
|
|
129
|
+
`).filter((l) => l.trim() !== "").map((l) => JSON.parse(l));
|
|
130
|
+
}
|
|
131
|
+
var feisesIndex = {
|
|
132
|
+
name: "feises",
|
|
133
|
+
version: "2",
|
|
134
|
+
inputs: ["results", "feis-redirects.jsonl"],
|
|
135
|
+
says: "every feis by id, platform, code, series and name, with its counts and regions",
|
|
136
|
+
build(rel, out) {
|
|
137
|
+
out.run(SCHEMA);
|
|
138
|
+
const src = rel.db("results");
|
|
139
|
+
const counts = new Map;
|
|
140
|
+
for (const r of src.query("SELECT event, count(*) AS n, json_group_array(DISTINCT feis_region) AS regions FROM competitions GROUP BY event").all())
|
|
141
|
+
counts.set(r.event, {
|
|
142
|
+
comps: r.n,
|
|
143
|
+
regions: new Set(JSON.parse(r.regions).filter((x) => x !== null))
|
|
144
|
+
});
|
|
145
|
+
const rows = new Map;
|
|
146
|
+
for (const r of src.query("SELECT c.event AS event, sum(r.n) AS n FROM (SELECT competition, count(*) AS n FROM results GROUP BY competition) r JOIN (SELECT DISTINCT competition, event FROM competitions) c ON c.competition = r.competition GROUP BY c.event").all())
|
|
147
|
+
rows.set(r.event, r.n);
|
|
148
|
+
const put = out.prepare("INSERT OR IGNORE INTO feis VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
149
|
+
const captures = new Map;
|
|
150
|
+
const feisRows = src.query("SELECT feis_id, source, host_id, name_printed, date_lo, date_hi, date_printed, year, location_printed, feis_instance_id, feis_brand_id FROM feises ORDER BY file, line").all();
|
|
151
|
+
for (const f of feisRows)
|
|
152
|
+
captures.set(f.feis_id, (captures.get(f.feis_id) ?? 0) + 1);
|
|
153
|
+
for (const f of feisRows) {
|
|
154
|
+
const c = counts.get(f.feis_id);
|
|
155
|
+
const colon = f.feis_id.indexOf(":");
|
|
156
|
+
put.run(f.feis_id, f.source, colon === -1 ? f.source : f.feis_id.slice(0, colon), f.host_id, f.name_printed, f.date_lo, f.date_hi, f.date_printed, f.year, f.location_printed, f.feis_instance_id, f.feis_brand_id, JSON.stringify([...c?.regions ?? []].sort()), c?.comps ?? 0, rows.get(f.feis_id) ?? 0, captures.get(f.feis_id) ?? 0);
|
|
157
|
+
}
|
|
158
|
+
const red = out.prepare("INSERT OR REPLACE INTO redirect VALUES (?, ?, ?)");
|
|
159
|
+
for (const r of readJsonl(rel.path("feis-redirects.jsonl")))
|
|
160
|
+
red.run(r.old_id, r.new_id, r.kind);
|
|
161
|
+
for (const s of INDEXES)
|
|
162
|
+
out.run(s);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
var NO_CALENDAR2 = "the release holds a feis-year once its results are captured and carries no calendar of scheduled feiseanna";
|
|
166
|
+
var globToLike = (g) => g.replace(/[\\%_]/g, (c) => `\\${c}`).replace(/\*/g, "%").replace(/\?/g, "_");
|
|
167
|
+
|
|
168
|
+
class FeisFinder2 {
|
|
169
|
+
rel;
|
|
170
|
+
constructor(rel) {
|
|
171
|
+
this.rel = rel;
|
|
172
|
+
}
|
|
173
|
+
async dates() {
|
|
174
|
+
await this.rel.fetch("results");
|
|
175
|
+
await this.rel.require("feises.dates");
|
|
176
|
+
const dates = this.rel.db("results").query("SELECT feis_id, min(date_lo) AS date_lo, max(date_hi) AS date_hi FROM feises GROUP BY feis_id ORDER BY feis_id").all();
|
|
177
|
+
return { tag: this.rel.tag, dates };
|
|
178
|
+
}
|
|
179
|
+
async find(q) {
|
|
180
|
+
const db = await this.rel.indexDb("feises");
|
|
181
|
+
const where = [];
|
|
182
|
+
const args = [];
|
|
183
|
+
let redirected;
|
|
184
|
+
if (q.platform !== undefined) {
|
|
185
|
+
where.push("(source = ? OR token = ?)");
|
|
186
|
+
args.push(q.platform, q.platform);
|
|
187
|
+
}
|
|
188
|
+
if (q.code !== undefined) {
|
|
189
|
+
where.push("host_id = ? COLLATE NOCASE");
|
|
190
|
+
args.push(q.code);
|
|
191
|
+
}
|
|
192
|
+
if (q.id !== undefined) {
|
|
193
|
+
let id = q.id;
|
|
194
|
+
const seen = new Set;
|
|
195
|
+
for (;; ) {
|
|
196
|
+
const r = db.query("SELECT new_id FROM redirect WHERE old_id = ?").get(id);
|
|
197
|
+
if (r === null || seen.has(r.new_id))
|
|
198
|
+
break;
|
|
199
|
+
seen.add(id);
|
|
200
|
+
id = r.new_id;
|
|
201
|
+
}
|
|
202
|
+
if (id !== q.id)
|
|
203
|
+
redirected = { from: q.id, to: id };
|
|
204
|
+
where.push("(feis_id = ? OR instance_id = ? OR brand_id = ?)");
|
|
205
|
+
args.push(id, id, id);
|
|
206
|
+
}
|
|
207
|
+
if (q.name !== undefined) {
|
|
208
|
+
where.push("name LIKE ? ESCAPE '\\'");
|
|
209
|
+
args.push(globToLike(q.name));
|
|
210
|
+
}
|
|
211
|
+
if (q.year !== undefined) {
|
|
212
|
+
where.push("year = ?");
|
|
213
|
+
args.push(q.year);
|
|
214
|
+
}
|
|
215
|
+
const sql = `SELECT * FROM feis${where.length > 0 ? ` WHERE ${where.join(" AND ")}` : ""} ORDER BY date_lo, feis_id`;
|
|
216
|
+
const feises = db.query(sql).all(...args).map((r) => ({
|
|
217
|
+
...r,
|
|
218
|
+
regions: JSON.parse(r.regions)
|
|
219
|
+
}));
|
|
220
|
+
return { tag: this.rel.tag, feises, ...redirected !== undefined ? { redirected } : {} };
|
|
221
|
+
}
|
|
222
|
+
async codeStem(token, stem) {
|
|
223
|
+
const db = await this.rel.indexDb("feises");
|
|
224
|
+
const like = db.query("SELECT host_id, year, brand_id FROM feis WHERE token = ? AND host_id LIKE ? ESCAPE '\\' COLLATE NOCASE").all(token, `${stem.replace(/[\\%_]/g, (c) => `\\${c}`)}__`);
|
|
225
|
+
const fits = like.filter((f) => {
|
|
226
|
+
const d = /(\d{2})$/.exec(f.host_id);
|
|
227
|
+
return d !== null && f.host_id.slice(0, -2).toLowerCase() === stem.toLowerCase() && f.year !== null && f.year % 100 === Number(d[1]) && f.brand_id !== null;
|
|
228
|
+
});
|
|
229
|
+
return [...new Set(fits.map((f) => f.brand_id))].sort();
|
|
230
|
+
}
|
|
231
|
+
async seriesOf(brand) {
|
|
232
|
+
const db = await this.rel.indexDb("feises");
|
|
233
|
+
const rows = db.query("SELECT * FROM feis WHERE brand_id = ? ORDER BY year, date_lo, feis_id").all(brand);
|
|
234
|
+
const byYear = new Map;
|
|
235
|
+
for (const r of rows) {
|
|
236
|
+
const i = byYear.get(r.year);
|
|
237
|
+
if (i === undefined)
|
|
238
|
+
byYear.set(r.year, {
|
|
239
|
+
year: r.year,
|
|
240
|
+
feis_ids: [r.feis_id],
|
|
241
|
+
date_lo: r.date_lo,
|
|
242
|
+
name: r.name
|
|
243
|
+
});
|
|
244
|
+
else
|
|
245
|
+
i.feis_ids.push(r.feis_id);
|
|
246
|
+
}
|
|
247
|
+
const instances = [...byYear.values()];
|
|
248
|
+
const newest = rows[rows.length - 1];
|
|
249
|
+
return {
|
|
250
|
+
series_id: brand,
|
|
251
|
+
name: newest?.name ?? "",
|
|
252
|
+
host: newest?.source ?? "",
|
|
253
|
+
hosts: [...new Set(rows.map((r) => r.source))].sort(),
|
|
254
|
+
codes: [...new Set(rows.map((r) => r.host_id))].sort(),
|
|
255
|
+
years: instances.map((i) => i.year).filter((y) => y !== null),
|
|
256
|
+
instances
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
async series(q) {
|
|
260
|
+
const asked = q.trim();
|
|
261
|
+
const answer = async (brands, via, more = {}) => {
|
|
262
|
+
const candidates = [];
|
|
263
|
+
for (const b of brands)
|
|
264
|
+
candidates.push(await this.seriesOf(b));
|
|
265
|
+
const arm = candidates.length === 0 ? "none" : candidates.length === 1 ? "unique" : "ambiguous";
|
|
266
|
+
const u = arm === "unique" ? candidates[0] : null;
|
|
267
|
+
return {
|
|
268
|
+
tag: this.rel.tag,
|
|
269
|
+
asked,
|
|
270
|
+
arm,
|
|
271
|
+
via: candidates.length === 0 ? null : via,
|
|
272
|
+
series_id: u?.series_id ?? null,
|
|
273
|
+
name: u?.name ?? null,
|
|
274
|
+
host: u?.host ?? null,
|
|
275
|
+
years: u?.years ?? [],
|
|
276
|
+
instances: u?.instances ?? [],
|
|
277
|
+
next: null,
|
|
278
|
+
...u !== null ? { next_why: NO_CALENDAR2 } : {},
|
|
279
|
+
candidates,
|
|
280
|
+
...arm === "ambiguous" ? { why: `${candidates.length} series answer "${asked}": ${brands.join(", ")}` } : {},
|
|
281
|
+
...more
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
if (asked === "")
|
|
285
|
+
return answer([], null, { why: "no series was asked" });
|
|
286
|
+
const byId = await this.find({ id: asked });
|
|
287
|
+
if (byId.feises.length > 0) {
|
|
288
|
+
const brands = [...new Set(byId.feises.map((f) => f.brand_id))];
|
|
289
|
+
const filed = brands.filter((b) => b !== null).sort();
|
|
290
|
+
const redirected = byId.redirected !== undefined ? { redirected: byId.redirected } : {};
|
|
291
|
+
if (filed.length === 0)
|
|
292
|
+
return answer([], null, {
|
|
293
|
+
...redirected,
|
|
294
|
+
why: `the release files ${byId.feises.map((f) => f.feis_id).join(", ")} under no series`
|
|
295
|
+
});
|
|
296
|
+
return answer(filed, "id", redirected);
|
|
297
|
+
}
|
|
298
|
+
const code = /^([^:\s]+):(.*?)(\d{2})?$/.exec(asked);
|
|
299
|
+
if (code !== null) {
|
|
300
|
+
const [, token, stem] = code;
|
|
301
|
+
const brands = stem === "" ? [] : await this.codeStem(token, stem);
|
|
302
|
+
if (brands.length > 0)
|
|
303
|
+
return answer(brands, "code-stem");
|
|
304
|
+
}
|
|
305
|
+
const glob = /[*?]/.test(asked) ? asked : `*${asked}*`;
|
|
306
|
+
const byName = await this.find({ name: glob });
|
|
307
|
+
const brands = [
|
|
308
|
+
...new Set(byName.feises.map((f) => f.brand_id).filter((b) => b !== null))
|
|
309
|
+
].sort();
|
|
310
|
+
if (brands.length > 0)
|
|
311
|
+
return answer(brands, "name");
|
|
312
|
+
return answer([], null, {
|
|
313
|
+
why: byName.feises.length > 0 ? `the feises named "${asked}" are filed under no series` : `the release holds no feis or series "${asked}"`
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
109
318
|
var ENTRY_RANK = new Map([
|
|
110
319
|
["final", 0],
|
|
111
320
|
["", 1],
|
|
@@ -375,6 +584,7 @@ class MajorsFinder2 {
|
|
|
375
584
|
}
|
|
376
585
|
}
|
|
377
586
|
|
|
587
|
+
var LINEAGE_FILE = "school-lineage.jsonl";
|
|
378
588
|
function nameOrderOf(s) {
|
|
379
589
|
return s === "surname-first" || s === "given-first" || s === "unknown" ? s : undefined;
|
|
380
590
|
}
|
|
@@ -506,11 +716,17 @@ var AGE_WEIGHT = {
|
|
|
506
716
|
};
|
|
507
717
|
var SCHOOL_RANK = {
|
|
508
718
|
same: 0,
|
|
719
|
+
lineage: 0,
|
|
509
720
|
moved: 1,
|
|
510
721
|
none: 2,
|
|
511
722
|
unresolved: 2,
|
|
512
723
|
unasked: 2
|
|
513
724
|
};
|
|
725
|
+
var PREFERRED_WHY = {
|
|
726
|
+
same: "whose latest school is",
|
|
727
|
+
lineage: "whose latest school the release's lineage joins to",
|
|
728
|
+
moved: "that ever danced for"
|
|
729
|
+
};
|
|
514
730
|
var POPULATION_WEIGHT = {
|
|
515
731
|
same: 1,
|
|
516
732
|
both: 0,
|
|
@@ -520,6 +736,7 @@ var POPULATION_WEIGHT = {
|
|
|
520
736
|
};
|
|
521
737
|
var SCHOOL_WEIGHT = {
|
|
522
738
|
same: 2,
|
|
739
|
+
lineage: 2,
|
|
523
740
|
moved: 1,
|
|
524
741
|
none: 0,
|
|
525
742
|
unresolved: 0,
|
|
@@ -535,9 +752,30 @@ function idChunks(ids) {
|
|
|
535
752
|
class DancerResolver2 {
|
|
536
753
|
rel;
|
|
537
754
|
printIds = new Map;
|
|
755
|
+
lineageOf;
|
|
538
756
|
constructor(rel) {
|
|
539
757
|
this.rel = rel;
|
|
540
758
|
}
|
|
759
|
+
lineage() {
|
|
760
|
+
this.lineageOf ??= (async () => {
|
|
761
|
+
if ((await this.rel.capability("schools.lineage")).state !== "full")
|
|
762
|
+
return null;
|
|
763
|
+
await this.rel.fetch(LINEAGE_FILE);
|
|
764
|
+
const out = new Map;
|
|
765
|
+
const join = (a, b) => {
|
|
766
|
+
const s = out.get(a) ?? new Set;
|
|
767
|
+
s.add(b);
|
|
768
|
+
out.set(a, s);
|
|
769
|
+
};
|
|
770
|
+
for (const r of readJsonl(this.rel.path(LINEAGE_FILE)))
|
|
771
|
+
if (r.id !== r.other) {
|
|
772
|
+
join(r.id, r.other);
|
|
773
|
+
join(r.other, r.id);
|
|
774
|
+
}
|
|
775
|
+
return out;
|
|
776
|
+
})();
|
|
777
|
+
return this.lineageOf;
|
|
778
|
+
}
|
|
541
779
|
async records(q = {}) {
|
|
542
780
|
await this.rel.fetch("results");
|
|
543
781
|
await this.rel.require("dancers.records");
|
|
@@ -643,11 +881,27 @@ class DancerResolver2 {
|
|
|
643
881
|
const age = q.age === undefined ? undefined : this.readAge(q.age, asOf);
|
|
644
882
|
const population = q.population === undefined ? undefined : readPopulation2(q.population);
|
|
645
883
|
let schoolIds = null;
|
|
884
|
+
let lineageIds = [];
|
|
646
885
|
let school;
|
|
647
886
|
if (q.school !== undefined) {
|
|
648
887
|
const s = await this.rel.schools.resolve(q.school);
|
|
649
888
|
schoolIds = s.candidates.map((c) => c.school_id);
|
|
650
|
-
|
|
889
|
+
const lineage = await this.lineage();
|
|
890
|
+
if (lineage === null)
|
|
891
|
+
school = { arm: s.arm, ids: schoolIds };
|
|
892
|
+
else {
|
|
893
|
+
const asked = new Set(schoolIds);
|
|
894
|
+
const reached = new Set(schoolIds);
|
|
895
|
+
const todo = [...schoolIds];
|
|
896
|
+
for (let id = todo.pop();id !== undefined; id = todo.pop())
|
|
897
|
+
for (const o of lineage.get(id) ?? [])
|
|
898
|
+
if (!reached.has(o)) {
|
|
899
|
+
reached.add(o);
|
|
900
|
+
todo.push(o);
|
|
901
|
+
}
|
|
902
|
+
lineageIds = [...reached].filter((id) => !asked.has(id)).sort();
|
|
903
|
+
school = { arm: s.arm, ids: schoolIds, lineage: lineageIds };
|
|
904
|
+
}
|
|
651
905
|
}
|
|
652
906
|
const byDancer = new Map;
|
|
653
907
|
const unlinked = new Map;
|
|
@@ -674,7 +928,7 @@ class DancerResolver2 {
|
|
|
674
928
|
const ageEv = ageEvidence(d, age?.age ?? null);
|
|
675
929
|
const pop = populationOf(db, id);
|
|
676
930
|
const popEv = populationEvidence(pop, population?.read ?? null);
|
|
677
|
-
const schoolEv = schoolIds === null ? "unasked" : schoolIds.length === 0 ? "unresolved" : await this.schoolEvidence(schools, schoolIds, asOf);
|
|
931
|
+
const schoolEv = schoolIds === null ? "unasked" : schoolIds.length === 0 ? "unresolved" : await this.schoolEvidence(schools, schoolIds, lineageIds, asOf);
|
|
678
932
|
all.push({
|
|
679
933
|
dancer_id: id,
|
|
680
934
|
display: d.display,
|
|
@@ -706,15 +960,17 @@ class DancerResolver2 {
|
|
|
706
960
|
});
|
|
707
961
|
let preferred;
|
|
708
962
|
if (kept.length > 1 && school !== undefined)
|
|
709
|
-
for (const
|
|
710
|
-
const hit = kept.filter((c) => SCHOOL_RANK[c.evidence.school] ===
|
|
963
|
+
for (const rank of [SCHOOL_RANK.same, SCHOOL_RANK.moved]) {
|
|
964
|
+
const hit = kept.filter((c) => SCHOOL_RANK[c.evidence.school] === rank);
|
|
711
965
|
if (hit.length === 0)
|
|
712
966
|
continue;
|
|
713
|
-
if (hit.length === 1)
|
|
967
|
+
if (hit.length === 1) {
|
|
968
|
+
const c = hit[0];
|
|
714
969
|
preferred = {
|
|
715
|
-
dancer_id:
|
|
716
|
-
why: `the only candidate ${
|
|
970
|
+
dancer_id: c.dancer_id,
|
|
971
|
+
why: `the only candidate ${PREFERRED_WHY[c.evidence.school]} ${q.school}`
|
|
717
972
|
};
|
|
973
|
+
}
|
|
718
974
|
break;
|
|
719
975
|
}
|
|
720
976
|
return {
|
|
@@ -730,29 +986,33 @@ class DancerResolver2 {
|
|
|
730
986
|
...school !== undefined ? { school } : {}
|
|
731
987
|
};
|
|
732
988
|
}
|
|
733
|
-
async schoolEvidence(schools, ids, asOf) {
|
|
989
|
+
async schoolEvidence(schools, ids, lineage, asOf) {
|
|
734
990
|
const rows = schools.filter((s) => asOf === null || s.year_lo === null || s.year_lo <= asOf);
|
|
735
|
-
const
|
|
991
|
+
const idsOf = async (s) => {
|
|
736
992
|
if (s.school_id !== null)
|
|
737
|
-
return
|
|
993
|
+
return [s.school_id];
|
|
738
994
|
let read = this.printIds.get(s.print);
|
|
739
995
|
if (read === undefined) {
|
|
740
996
|
read = (await this.rel.schools.resolve(s.print)).candidates.map((c) => c.school_id);
|
|
741
997
|
this.printIds.set(s.print, read);
|
|
742
998
|
}
|
|
743
|
-
return read
|
|
999
|
+
return read;
|
|
744
1000
|
};
|
|
745
1001
|
const top = (s) => asOf !== null && s.year_hi !== null ? Math.min(s.year_hi, asOf) : s.year_hi ?? -1;
|
|
746
1002
|
const latest = Math.max(-1, ...rows.map(top));
|
|
747
1003
|
let earlier = false;
|
|
1004
|
+
let joined = false;
|
|
748
1005
|
for (const s of rows) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
if (
|
|
752
|
-
|
|
753
|
-
|
|
1006
|
+
const own = await idsOf(s);
|
|
1007
|
+
const isLatest = top(s) === latest;
|
|
1008
|
+
if (own.some((id) => ids.includes(id))) {
|
|
1009
|
+
if (isLatest)
|
|
1010
|
+
return "same";
|
|
1011
|
+
earlier = true;
|
|
1012
|
+
} else if (isLatest && own.some((id) => lineage.includes(id)))
|
|
1013
|
+
joined = true;
|
|
754
1014
|
}
|
|
755
|
-
return earlier ? "moved" : "none";
|
|
1015
|
+
return joined ? "lineage" : earlier ? "moved" : "none";
|
|
756
1016
|
}
|
|
757
1017
|
readAge(a, asOf) {
|
|
758
1018
|
const done = (age, note) => {
|
|
@@ -865,7 +1125,7 @@ var LEVEL = Object.freeze({
|
|
|
865
1125
|
600: "PC",
|
|
866
1126
|
700: "OC"
|
|
867
1127
|
});
|
|
868
|
-
var
|
|
1128
|
+
var SCHEMA2 = `
|
|
869
1129
|
CREATE TABLE comp(
|
|
870
1130
|
competition TEXT PRIMARY KEY, feis_id TEXT NOT NULL, brand_id TEXT, year INTEGER,
|
|
871
1131
|
code TEXT NOT NULL, code_fold TEXT NOT NULL, title TEXT, age TEXT, gender TEXT,
|
|
@@ -874,7 +1134,7 @@ CREATE TABLE comp(
|
|
|
874
1134
|
gender_source TEXT, level_printed TEXT, dance_printed TEXT, dance TEXT
|
|
875
1135
|
);
|
|
876
1136
|
`;
|
|
877
|
-
var
|
|
1137
|
+
var INDEXES2 = [
|
|
878
1138
|
"CREATE INDEX comp_feis_code ON comp(feis_id, code_fold)",
|
|
879
1139
|
"CREATE INDEX comp_brand_year ON comp(brand_id, year)"
|
|
880
1140
|
];
|
|
@@ -893,7 +1153,7 @@ var compsIndex = {
|
|
|
893
1153
|
inputs: ["results"],
|
|
894
1154
|
says: "every competition by its feis and its own code, with its series and year",
|
|
895
1155
|
build(rel, out) {
|
|
896
|
-
out.run(
|
|
1156
|
+
out.run(SCHEMA2);
|
|
897
1157
|
const src = rel.db("results");
|
|
898
1158
|
const brand = new Map;
|
|
899
1159
|
for (const f of src.query("SELECT feis_id, feis_brand_id, year FROM feises ORDER BY file, line").all())
|
|
@@ -909,7 +1169,7 @@ var compsIndex = {
|
|
|
909
1169
|
const f = brand.get(c.event);
|
|
910
1170
|
put.run(c.competition, c.event, f?.brand ?? null, c.year ?? f?.year ?? null, c.comp_no.trim(), fold(c.comp_no), c.title, c.age, c.gender, c.feis_level, c.round, c.source, captures.get(c.competition) ?? 1, c.file, c.line, c.age_source, c.age_printed, c.gender_source, c.level_printed, c.dance_printed, c.dance);
|
|
911
1171
|
}
|
|
912
|
-
for (const s of
|
|
1172
|
+
for (const s of INDEXES2)
|
|
913
1173
|
out.run(s);
|
|
914
1174
|
}
|
|
915
1175
|
};
|
|
@@ -1199,215 +1459,6 @@ class CompResolver2 {
|
|
|
1199
1459
|
}
|
|
1200
1460
|
}
|
|
1201
1461
|
|
|
1202
|
-
import { readFileSync } from "fs";
|
|
1203
|
-
var SCHEMA2 = `
|
|
1204
|
-
CREATE TABLE feis(
|
|
1205
|
-
feis_id TEXT PRIMARY KEY, source TEXT NOT NULL, token TEXT NOT NULL, host_id TEXT NOT NULL,
|
|
1206
|
-
name TEXT NOT NULL, date_lo TEXT, date_hi TEXT, date_printed TEXT, year INTEGER,
|
|
1207
|
-
location TEXT, instance_id TEXT, brand_id TEXT, regions TEXT NOT NULL,
|
|
1208
|
-
competitions INTEGER NOT NULL, rows INTEGER NOT NULL, captures INTEGER NOT NULL
|
|
1209
|
-
);
|
|
1210
|
-
CREATE TABLE redirect(old_id TEXT PRIMARY KEY, new_id TEXT NOT NULL, kind TEXT NOT NULL);
|
|
1211
|
-
`;
|
|
1212
|
-
var INDEXES2 = [
|
|
1213
|
-
"CREATE INDEX feis_host ON feis(host_id COLLATE NOCASE)",
|
|
1214
|
-
"CREATE INDEX feis_token ON feis(token)",
|
|
1215
|
-
"CREATE INDEX feis_source ON feis(source)",
|
|
1216
|
-
"CREATE INDEX feis_instance ON feis(instance_id)",
|
|
1217
|
-
"CREATE INDEX feis_brand ON feis(brand_id)",
|
|
1218
|
-
"CREATE INDEX feis_year ON feis(year)"
|
|
1219
|
-
];
|
|
1220
|
-
function readJsonl(path) {
|
|
1221
|
-
return readFileSync(path, "utf8").split(`
|
|
1222
|
-
`).filter((l) => l.trim() !== "").map((l) => JSON.parse(l));
|
|
1223
|
-
}
|
|
1224
|
-
var feisesIndex = {
|
|
1225
|
-
name: "feises",
|
|
1226
|
-
version: "2",
|
|
1227
|
-
inputs: ["results", "feis-redirects.jsonl"],
|
|
1228
|
-
says: "every feis by id, platform, code, series and name, with its counts and regions",
|
|
1229
|
-
build(rel, out) {
|
|
1230
|
-
out.run(SCHEMA2);
|
|
1231
|
-
const src = rel.db("results");
|
|
1232
|
-
const counts = new Map;
|
|
1233
|
-
for (const r of src.query("SELECT event, count(*) AS n, json_group_array(DISTINCT feis_region) AS regions FROM competitions GROUP BY event").all())
|
|
1234
|
-
counts.set(r.event, {
|
|
1235
|
-
comps: r.n,
|
|
1236
|
-
regions: new Set(JSON.parse(r.regions).filter((x) => x !== null))
|
|
1237
|
-
});
|
|
1238
|
-
const rows = new Map;
|
|
1239
|
-
for (const r of src.query("SELECT c.event AS event, sum(r.n) AS n FROM (SELECT competition, count(*) AS n FROM results GROUP BY competition) r JOIN (SELECT DISTINCT competition, event FROM competitions) c ON c.competition = r.competition GROUP BY c.event").all())
|
|
1240
|
-
rows.set(r.event, r.n);
|
|
1241
|
-
const put = out.prepare("INSERT OR IGNORE INTO feis VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
1242
|
-
const captures = new Map;
|
|
1243
|
-
const feisRows = src.query("SELECT feis_id, source, host_id, name_printed, date_lo, date_hi, date_printed, year, location_printed, feis_instance_id, feis_brand_id FROM feises ORDER BY file, line").all();
|
|
1244
|
-
for (const f of feisRows)
|
|
1245
|
-
captures.set(f.feis_id, (captures.get(f.feis_id) ?? 0) + 1);
|
|
1246
|
-
for (const f of feisRows) {
|
|
1247
|
-
const c = counts.get(f.feis_id);
|
|
1248
|
-
const colon = f.feis_id.indexOf(":");
|
|
1249
|
-
put.run(f.feis_id, f.source, colon === -1 ? f.source : f.feis_id.slice(0, colon), f.host_id, f.name_printed, f.date_lo, f.date_hi, f.date_printed, f.year, f.location_printed, f.feis_instance_id, f.feis_brand_id, JSON.stringify([...c?.regions ?? []].sort()), c?.comps ?? 0, rows.get(f.feis_id) ?? 0, captures.get(f.feis_id) ?? 0);
|
|
1250
|
-
}
|
|
1251
|
-
const red = out.prepare("INSERT OR REPLACE INTO redirect VALUES (?, ?, ?)");
|
|
1252
|
-
for (const r of readJsonl(rel.path("feis-redirects.jsonl")))
|
|
1253
|
-
red.run(r.old_id, r.new_id, r.kind);
|
|
1254
|
-
for (const s of INDEXES2)
|
|
1255
|
-
out.run(s);
|
|
1256
|
-
}
|
|
1257
|
-
};
|
|
1258
|
-
var NO_CALENDAR2 = "the release holds a feis-year once its results are captured and carries no calendar of scheduled feiseanna";
|
|
1259
|
-
var globToLike = (g) => g.replace(/[\\%_]/g, (c) => `\\${c}`).replace(/\*/g, "%").replace(/\?/g, "_");
|
|
1260
|
-
|
|
1261
|
-
class FeisFinder2 {
|
|
1262
|
-
rel;
|
|
1263
|
-
constructor(rel) {
|
|
1264
|
-
this.rel = rel;
|
|
1265
|
-
}
|
|
1266
|
-
async dates() {
|
|
1267
|
-
await this.rel.fetch("results");
|
|
1268
|
-
await this.rel.require("feises.dates");
|
|
1269
|
-
const dates = this.rel.db("results").query("SELECT feis_id, min(date_lo) AS date_lo, max(date_hi) AS date_hi FROM feises GROUP BY feis_id ORDER BY feis_id").all();
|
|
1270
|
-
return { tag: this.rel.tag, dates };
|
|
1271
|
-
}
|
|
1272
|
-
async find(q) {
|
|
1273
|
-
const db = await this.rel.indexDb("feises");
|
|
1274
|
-
const where = [];
|
|
1275
|
-
const args = [];
|
|
1276
|
-
let redirected;
|
|
1277
|
-
if (q.platform !== undefined) {
|
|
1278
|
-
where.push("(source = ? OR token = ?)");
|
|
1279
|
-
args.push(q.platform, q.platform);
|
|
1280
|
-
}
|
|
1281
|
-
if (q.code !== undefined) {
|
|
1282
|
-
where.push("host_id = ? COLLATE NOCASE");
|
|
1283
|
-
args.push(q.code);
|
|
1284
|
-
}
|
|
1285
|
-
if (q.id !== undefined) {
|
|
1286
|
-
let id = q.id;
|
|
1287
|
-
const seen = new Set;
|
|
1288
|
-
for (;; ) {
|
|
1289
|
-
const r = db.query("SELECT new_id FROM redirect WHERE old_id = ?").get(id);
|
|
1290
|
-
if (r === null || seen.has(r.new_id))
|
|
1291
|
-
break;
|
|
1292
|
-
seen.add(id);
|
|
1293
|
-
id = r.new_id;
|
|
1294
|
-
}
|
|
1295
|
-
if (id !== q.id)
|
|
1296
|
-
redirected = { from: q.id, to: id };
|
|
1297
|
-
where.push("(feis_id = ? OR instance_id = ? OR brand_id = ?)");
|
|
1298
|
-
args.push(id, id, id);
|
|
1299
|
-
}
|
|
1300
|
-
if (q.name !== undefined) {
|
|
1301
|
-
where.push("name LIKE ? ESCAPE '\\'");
|
|
1302
|
-
args.push(globToLike(q.name));
|
|
1303
|
-
}
|
|
1304
|
-
if (q.year !== undefined) {
|
|
1305
|
-
where.push("year = ?");
|
|
1306
|
-
args.push(q.year);
|
|
1307
|
-
}
|
|
1308
|
-
const sql = `SELECT * FROM feis${where.length > 0 ? ` WHERE ${where.join(" AND ")}` : ""} ORDER BY date_lo, feis_id`;
|
|
1309
|
-
const feises = db.query(sql).all(...args).map((r) => ({
|
|
1310
|
-
...r,
|
|
1311
|
-
regions: JSON.parse(r.regions)
|
|
1312
|
-
}));
|
|
1313
|
-
return { tag: this.rel.tag, feises, ...redirected !== undefined ? { redirected } : {} };
|
|
1314
|
-
}
|
|
1315
|
-
async codeStem(token, stem) {
|
|
1316
|
-
const db = await this.rel.indexDb("feises");
|
|
1317
|
-
const like = db.query("SELECT host_id, year, brand_id FROM feis WHERE token = ? AND host_id LIKE ? ESCAPE '\\' COLLATE NOCASE").all(token, `${stem.replace(/[\\%_]/g, (c) => `\\${c}`)}__`);
|
|
1318
|
-
const fits = like.filter((f) => {
|
|
1319
|
-
const d = /(\d{2})$/.exec(f.host_id);
|
|
1320
|
-
return d !== null && f.host_id.slice(0, -2).toLowerCase() === stem.toLowerCase() && f.year !== null && f.year % 100 === Number(d[1]) && f.brand_id !== null;
|
|
1321
|
-
});
|
|
1322
|
-
return [...new Set(fits.map((f) => f.brand_id))].sort();
|
|
1323
|
-
}
|
|
1324
|
-
async seriesOf(brand) {
|
|
1325
|
-
const db = await this.rel.indexDb("feises");
|
|
1326
|
-
const rows = db.query("SELECT * FROM feis WHERE brand_id = ? ORDER BY year, date_lo, feis_id").all(brand);
|
|
1327
|
-
const byYear = new Map;
|
|
1328
|
-
for (const r of rows) {
|
|
1329
|
-
const i = byYear.get(r.year);
|
|
1330
|
-
if (i === undefined)
|
|
1331
|
-
byYear.set(r.year, {
|
|
1332
|
-
year: r.year,
|
|
1333
|
-
feis_ids: [r.feis_id],
|
|
1334
|
-
date_lo: r.date_lo,
|
|
1335
|
-
name: r.name
|
|
1336
|
-
});
|
|
1337
|
-
else
|
|
1338
|
-
i.feis_ids.push(r.feis_id);
|
|
1339
|
-
}
|
|
1340
|
-
const instances = [...byYear.values()];
|
|
1341
|
-
const newest = rows[rows.length - 1];
|
|
1342
|
-
return {
|
|
1343
|
-
series_id: brand,
|
|
1344
|
-
name: newest?.name ?? "",
|
|
1345
|
-
host: newest?.source ?? "",
|
|
1346
|
-
hosts: [...new Set(rows.map((r) => r.source))].sort(),
|
|
1347
|
-
codes: [...new Set(rows.map((r) => r.host_id))].sort(),
|
|
1348
|
-
years: instances.map((i) => i.year).filter((y) => y !== null),
|
|
1349
|
-
instances
|
|
1350
|
-
};
|
|
1351
|
-
}
|
|
1352
|
-
async series(q) {
|
|
1353
|
-
const asked = q.trim();
|
|
1354
|
-
const answer = async (brands, via, more = {}) => {
|
|
1355
|
-
const candidates = [];
|
|
1356
|
-
for (const b of brands)
|
|
1357
|
-
candidates.push(await this.seriesOf(b));
|
|
1358
|
-
const arm = candidates.length === 0 ? "none" : candidates.length === 1 ? "unique" : "ambiguous";
|
|
1359
|
-
const u = arm === "unique" ? candidates[0] : null;
|
|
1360
|
-
return {
|
|
1361
|
-
tag: this.rel.tag,
|
|
1362
|
-
asked,
|
|
1363
|
-
arm,
|
|
1364
|
-
via: candidates.length === 0 ? null : via,
|
|
1365
|
-
series_id: u?.series_id ?? null,
|
|
1366
|
-
name: u?.name ?? null,
|
|
1367
|
-
host: u?.host ?? null,
|
|
1368
|
-
years: u?.years ?? [],
|
|
1369
|
-
instances: u?.instances ?? [],
|
|
1370
|
-
next: null,
|
|
1371
|
-
...u !== null ? { next_why: NO_CALENDAR2 } : {},
|
|
1372
|
-
candidates,
|
|
1373
|
-
...arm === "ambiguous" ? { why: `${candidates.length} series answer "${asked}": ${brands.join(", ")}` } : {},
|
|
1374
|
-
...more
|
|
1375
|
-
};
|
|
1376
|
-
};
|
|
1377
|
-
if (asked === "")
|
|
1378
|
-
return answer([], null, { why: "no series was asked" });
|
|
1379
|
-
const byId = await this.find({ id: asked });
|
|
1380
|
-
if (byId.feises.length > 0) {
|
|
1381
|
-
const brands = [...new Set(byId.feises.map((f) => f.brand_id))];
|
|
1382
|
-
const filed = brands.filter((b) => b !== null).sort();
|
|
1383
|
-
const redirected = byId.redirected !== undefined ? { redirected: byId.redirected } : {};
|
|
1384
|
-
if (filed.length === 0)
|
|
1385
|
-
return answer([], null, {
|
|
1386
|
-
...redirected,
|
|
1387
|
-
why: `the release files ${byId.feises.map((f) => f.feis_id).join(", ")} under no series`
|
|
1388
|
-
});
|
|
1389
|
-
return answer(filed, "id", redirected);
|
|
1390
|
-
}
|
|
1391
|
-
const code = /^([^:\s]+):(.*?)(\d{2})?$/.exec(asked);
|
|
1392
|
-
if (code !== null) {
|
|
1393
|
-
const [, token, stem] = code;
|
|
1394
|
-
const brands = stem === "" ? [] : await this.codeStem(token, stem);
|
|
1395
|
-
if (brands.length > 0)
|
|
1396
|
-
return answer(brands, "code-stem");
|
|
1397
|
-
}
|
|
1398
|
-
const glob = /[*?]/.test(asked) ? asked : `*${asked}*`;
|
|
1399
|
-
const byName = await this.find({ name: glob });
|
|
1400
|
-
const brands = [
|
|
1401
|
-
...new Set(byName.feises.map((f) => f.brand_id).filter((b) => b !== null))
|
|
1402
|
-
].sort();
|
|
1403
|
-
if (brands.length > 0)
|
|
1404
|
-
return answer(brands, "name");
|
|
1405
|
-
return answer([], null, {
|
|
1406
|
-
why: byName.feises.length > 0 ? `the feises named "${asked}" are filed under no series` : `the release holds no feis or series "${asked}"`
|
|
1407
|
-
});
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
1462
|
import { Database } from "bun:sqlite";
|
|
1412
1463
|
import { createHash as createHash2 } from "crypto";
|
|
1413
1464
|
import { existsSync, mkdirSync, readdirSync, renameSync, rmSync, statSync } from "fs";
|
|
@@ -2517,6 +2568,12 @@ var FEATURES2 = [
|
|
|
2517
2568
|
since: 270,
|
|
2518
2569
|
calls: "the `dance` a comp of comps.resolve, comps.syllabus and comps.syllabusFor carries: null, never refused, on a release without it"
|
|
2519
2570
|
},
|
|
2571
|
+
{
|
|
2572
|
+
feature: "schools.lineage",
|
|
2573
|
+
needs: ["school-lineage.jsonl"],
|
|
2574
|
+
since: 184,
|
|
2575
|
+
calls: "the `lineage` school evidence of dancers.resolve and dancers.search: no school is joined by lineage, never refused, on a release without it"
|
|
2576
|
+
},
|
|
2520
2577
|
{
|
|
2521
2578
|
feature: "results.stream",
|
|
2522
2579
|
needs: [
|
|
@@ -3229,4 +3286,4 @@ function releases2(opts = {}) {
|
|
|
3229
3286
|
return new ReleaseManager2(opts);
|
|
3230
3287
|
}
|
|
3231
3288
|
|
|
3232
|
-
export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, nameOrderOf, DancerResolver2, readPopulation2, canonicalOf2, CompResolver2,
|
|
3289
|
+
export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, NO_CALENDAR2, FeisFinder2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, nameOrderOf, DancerResolver2, readPopulation2, canonicalOf2, CompResolver2, 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-mg6dw0y5.js";
|
|
8
8
|
import"./chunks/index-hne1wadw.js";
|
|
9
9
|
import"./chunks/index-hghdy980.js";
|
|
10
10
|
|
|
@@ -178,7 +178,7 @@ function printDancers(a, say) {
|
|
|
178
178
|
if (a.age !== undefined)
|
|
179
179
|
say(` age asked: ${a.age.read ?? "(unread)"}${a.age.birth !== null ? ` born ${a.age.birth.from ?? "?"}-${a.age.birth.to ?? "?"}` : ""}${a.age.note !== undefined ? ` (${a.age.note})` : ""}`);
|
|
180
180
|
if (a.school !== undefined)
|
|
181
|
-
say(` school asked: ${a.school.arm} ${a.school.ids.join(", ") || "-"}`);
|
|
181
|
+
say(` school asked: ${a.school.arm} ${a.school.ids.join(", ") || "-"}${a.school.lineage !== undefined && a.school.lineage.length > 0 ? ` (lineage: ${a.school.lineage.join(", ")})` : ""}`);
|
|
182
182
|
if (a.population !== undefined)
|
|
183
183
|
say(` population asked: ${a.population.read ?? "(unread)"}${a.population.note !== undefined ? ` (${a.population.note})` : ""}`);
|
|
184
184
|
for (const c of a.candidates)
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
unzstdTo2,
|
|
7
7
|
NAME_LEVELS2,
|
|
8
8
|
nameLevel2,
|
|
9
|
+
NO_CALENDAR2,
|
|
10
|
+
FeisFinder2,
|
|
9
11
|
entryRank2,
|
|
10
12
|
FEIS_LOCAL_ID_FLOOR2,
|
|
11
13
|
ResultFinder2,
|
|
@@ -14,8 +16,6 @@ import {
|
|
|
14
16
|
readPopulation2,
|
|
15
17
|
canonicalOf2,
|
|
16
18
|
CompResolver2,
|
|
17
|
-
NO_CALENDAR2,
|
|
18
|
-
FeisFinder2,
|
|
19
19
|
StaffFinder2,
|
|
20
20
|
SchoolResolver2,
|
|
21
21
|
TeamFinder2,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
openCached2,
|
|
46
46
|
ReleaseManager2,
|
|
47
47
|
releases2
|
|
48
|
-
} from "./chunks/index-
|
|
48
|
+
} from "./chunks/index-mg6dw0y5.js";
|
|
49
49
|
import {
|
|
50
50
|
cacheRoot2,
|
|
51
51
|
repoOf2
|
package/dist/types/dancers.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface DancerQuery {
|
|
|
24
24
|
}
|
|
25
25
|
export type AgeEvidence = "fits" | "overlaps" | "contradicts" | "unknown" | "unasked";
|
|
26
26
|
export type PopulationEvidence = "same" | "contradicts" | "both" | "unknown" | "unasked";
|
|
27
|
-
export type SchoolEvidence = "same" | "moved" | "none" | "unresolved" | "unasked";
|
|
27
|
+
export type SchoolEvidence = "same" | "lineage" | "moved" | "none" | "unresolved" | "unasked";
|
|
28
28
|
export interface DancerSchool {
|
|
29
29
|
school_id: string | null;
|
|
30
30
|
print: string;
|
|
@@ -98,6 +98,7 @@ export interface DancerAnswer {
|
|
|
98
98
|
school?: {
|
|
99
99
|
arm: string;
|
|
100
100
|
ids: string[];
|
|
101
|
+
lineage?: string[];
|
|
101
102
|
};
|
|
102
103
|
refused?: string;
|
|
103
104
|
}
|
|
@@ -163,7 +164,9 @@ export interface DancerYear {
|
|
|
163
164
|
export declare class DancerResolver {
|
|
164
165
|
private readonly rel;
|
|
165
166
|
private readonly printIds;
|
|
167
|
+
private lineageOf;
|
|
166
168
|
constructor(rel: Release);
|
|
169
|
+
private lineage;
|
|
167
170
|
records(q?: {
|
|
168
171
|
ids?: readonly number[];
|
|
169
172
|
}): Promise<{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finbheara/archive-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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",
|