@finbheara/archive-client 0.18.0 → 0.20.1
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 +48 -1
- package/dist/chunks/{index-tr14vc1c.js → index-6028k52f.js} +97 -2
- package/dist/cli.js +1 -1
- package/dist/index.js +3 -1
- package/dist/types/assets.d.ts +5 -0
- package/dist/types/dancer-level.d.ts +24 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/results.d.ts +2 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -80,6 +80,7 @@ c.features; // [{ feature: "dancers", state: "full" | "missing", missing: [...
|
|
|
80
80
|
| feature | the calls | from cut |
|
|
81
81
|
| --- | --- | --- |
|
|
82
82
|
| `results.level` | `results.query`, `majors.results` | 215 |
|
|
83
|
+
| `results.dancer-level` | `results.dancerLevel` | 270 |
|
|
83
84
|
| `dancers` | `dancers.resolve`, `search`, `get` | 260 |
|
|
84
85
|
| `schools` | `schools.resolve`, a dancer question with a school | 260 |
|
|
85
86
|
| `staff` | `staff.teacher`, `staff.adjudicator` | 260 |
|
|
@@ -100,7 +101,10 @@ A spec is `pinned` (the default: the user-local pin, else the latest), `latest`,
|
|
|
100
101
|
|
|
101
102
|
EVERY FILE IS CHECKED before it lands: the downloaded bytes against the host's own sha256 digest,
|
|
102
103
|
and the content against the manifest (a database's decompressed bytes against
|
|
103
|
-
`databases.<name>.sha256`, a
|
|
104
|
+
`databases.<name>.sha256`, a side file's decompressed bytes against `sideFiles.<name>.sha256`, a
|
|
105
|
+
parquet file against `parquet.files[<name>].sha256`). A side file the manifest names under
|
|
106
|
+
`sideFiles` is published zstd'd (`<name>.zst`, or the `file` it states) and lands plain under its
|
|
107
|
+
own name. A mismatch
|
|
104
108
|
refuses and leaves nothing in place. A file nothing vouches for is kept and its receipt says so
|
|
105
109
|
(`strict: true` refuses it). A database is decompressed in process, streamed, never through a
|
|
106
110
|
`zstd` binary.
|
|
@@ -448,6 +452,49 @@ A row carries the prints and the ids that stand for them: the address, the compe
|
|
|
448
452
|
and age, the place, the name and school as printed with the dancer and school ids. The majors are
|
|
449
453
|
read from `majors.sqlite`, fetched on first use.
|
|
450
454
|
|
|
455
|
+
### What level a dancer dances
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
await rel.results.dancerLevel({ dancer_id: 1001, year: 2025 });
|
|
459
|
+
// { level: 200, grades: { advancement_range: { lo: 300, hi: 400 },
|
|
460
|
+
// level_range: { lo: 200, hi: 400 }, oireachtas_level: true } }
|
|
461
|
+
await rel.results.dancerLevel({ dancer_id: 1002 }); // its latest year: { level: 600 }
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
No single number is a grades dancer's level. Grades are won PER DANCE: a dancer can be Novice in
|
|
465
|
+
the reel and Prizewinner in the slip jig at once. Only a championship (600 preliminary, 700 open)
|
|
466
|
+
is a level of the dancer. A competition's `feis_level` is the competition's rung. A set, a set
|
|
467
|
+
dance, a treble reel or a special prints a rung that its entrants need not hold. So the answer
|
|
468
|
+
reads:
|
|
469
|
+
|
|
470
|
+
- **the year**: the competition's calendar year. When it is omitted, the latest year in which the
|
|
471
|
+
dancer has a rated feis row.
|
|
472
|
+
- **a rated row**: a feis row (tier `T4`, a platform's) of the dancer, on the floor (no printed
|
|
473
|
+
absence, no `DNT`), whose competition publishes a `feis_level`.
|
|
474
|
+
- **a championship row**: a rated row at 600 or 700. A 700 whose title or level field also names
|
|
475
|
+
the preliminary (`PC and OC`, `Prelim and Champ`) joins the two, and it reads 600 here.
|
|
476
|
+
- **a graded row**: a rated row at 100 to 500 whose `dance` is one of the six graded dances (reel,
|
|
477
|
+
light jig, single jig, slip jig, treble jig, hornpipe) and whose title, level or dance field names
|
|
478
|
+
no special (`special`, `spcl`, `trophy`). Sets, set dances, treble reels and specials are never
|
|
479
|
+
read.
|
|
480
|
+
|
|
481
|
+
| field | meaning |
|
|
482
|
+
| --- | --- |
|
|
483
|
+
| `level` | for a championship dancer (any championship row that year), the LOWEST championship rung danced that year. Otherwise the lowest graded rung danced that year. Null where the year holds neither |
|
|
484
|
+
| `grades` | present only for a grades dancer: no championship row that year, and at least one graded row |
|
|
485
|
+
| `grades.advancement_range` | `{ lo, hi }`, the lowest and highest rung over the reel, slip jig, treble jig and hornpipe, the dances a dancer advances in. Null where none of them was danced |
|
|
486
|
+
| `grades.level_range` | `{ lo, hi }` over all six graded dances |
|
|
487
|
+
| `grades.oireachtas_level` | `true` where the dancer danced a championship solo at a regional Oireachtas that year: a row on the floor at a tier `T3` event, the same calendar year, whose competition's `category` is `Solo`. A grade championship that the release files as `Solo` counts as well (one regional, in one year, prints ten) |
|
|
488
|
+
|
|
489
|
+
Only feis rows set the level. A dancer with majors rows and no feis row in the year (tier `T4`) gets `level` null, whatever they danced at the majors.
|
|
490
|
+
|
|
491
|
+
A dance that was not danced that year takes no part: each range covers what was danced. An adult
|
|
492
|
+
grade row is read like any other, since the adult ladder also grades per dance. A release older
|
|
493
|
+
than the published `dance` column is refused by name (`results.dancer-level`).
|
|
494
|
+
|
|
495
|
+
Each call reads the dancer's rows once. The release does not index the dancer id, so one call
|
|
496
|
+
scans the results table, as `results.query` does (about half a second on a full release).
|
|
497
|
+
|
|
451
498
|
## Every row, streamed
|
|
452
499
|
|
|
453
500
|
```ts
|
|
@@ -27,6 +27,17 @@ function planAsset2(name, manifest) {
|
|
|
27
27
|
contentSha256: manifest?.databases?.[name]?.sha256 ?? null
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
+
const side = manifest?.sideFiles?.[name];
|
|
31
|
+
if (side !== undefined) {
|
|
32
|
+
const file = side.file ?? `${name}.zst`;
|
|
33
|
+
return {
|
|
34
|
+
asked: name,
|
|
35
|
+
published: file === name ? [name] : [file, name],
|
|
36
|
+
local: name,
|
|
37
|
+
decompress: true,
|
|
38
|
+
contentSha256: side.sha256 ?? null
|
|
39
|
+
};
|
|
40
|
+
}
|
|
30
41
|
return {
|
|
31
42
|
asked: name,
|
|
32
43
|
published: [name],
|
|
@@ -366,6 +377,69 @@ function notOnTheFloor(placePrinted, atts, withdrawn) {
|
|
|
366
377
|
return withdrawn !== null && DID_NOT_TABULATE.has(withdrawn);
|
|
367
378
|
}
|
|
368
379
|
|
|
380
|
+
import { GRADE_DANCES } from "@finbheara/domain/dance-types";
|
|
381
|
+
var ADVANCEMENT_DANCES2 = Object.freeze([
|
|
382
|
+
"reel",
|
|
383
|
+
"slip-jig",
|
|
384
|
+
"treble-jig",
|
|
385
|
+
"hornpipe"
|
|
386
|
+
]);
|
|
387
|
+
var SPECIAL_WORDS = Object.freeze(["special", "spcl", "trophy"]);
|
|
388
|
+
var JOINED_WORDS = Object.freeze([
|
|
389
|
+
"prelim",
|
|
390
|
+
"pc and oc",
|
|
391
|
+
"pc/oc",
|
|
392
|
+
"pc & oc",
|
|
393
|
+
"pc-oc",
|
|
394
|
+
"pc/c",
|
|
395
|
+
"pr\xEDomh",
|
|
396
|
+
"priomh"
|
|
397
|
+
]);
|
|
398
|
+
var MAJORS_SOURCES = new Set(["transcript", "manifested"]);
|
|
399
|
+
var has = (t, words) => words.some((w) => t.includes(w));
|
|
400
|
+
function range(levels) {
|
|
401
|
+
if (levels.length === 0)
|
|
402
|
+
return null;
|
|
403
|
+
return { lo: Math.min(...levels), hi: Math.max(...levels) };
|
|
404
|
+
}
|
|
405
|
+
var isRated = (r) => r.tier === "T4" && !MAJORS_SOURCES.has(r.source) && r.feis_level !== null;
|
|
406
|
+
function dancerLevelOf(db, tag, q) {
|
|
407
|
+
const all = db.query(`SELECT c.year AS year, c.tier AS tier, c.source AS source, c.feis_level AS feis_level,
|
|
408
|
+
c.dance AS dance, c.category AS category,
|
|
409
|
+
lower(coalesce(c.title, '') || ' | ' || coalesce(c.level_printed, '') || ' | ' ||
|
|
410
|
+
coalesce(c.dance_printed, '')) AS t,
|
|
411
|
+
r.place_printed AS place_printed, r.atts AS atts, r.withdrawn AS withdrawn
|
|
412
|
+
FROM results r JOIN competitions c ON c.file = r.file AND c.line = r.competition_line
|
|
413
|
+
WHERE r.dancer_id = ? AND c.tier IN ('T3', 'T4')`).all(q.dancer_id).filter((r) => !notOnTheFloor(r.place_printed, r.atts, r.withdrawn));
|
|
414
|
+
let year = q.year ?? null;
|
|
415
|
+
if (year === null) {
|
|
416
|
+
for (const r of all)
|
|
417
|
+
if (isRated(r) && r.year !== null && (year === null || r.year > year))
|
|
418
|
+
year = r.year;
|
|
419
|
+
}
|
|
420
|
+
const answer = { tag, asked: q, dancer_id: q.dancer_id, year, level: null };
|
|
421
|
+
if (year === null)
|
|
422
|
+
return answer;
|
|
423
|
+
const rows = all.filter((r) => r.year === year);
|
|
424
|
+
const rated = rows.filter(isRated);
|
|
425
|
+
const champs = rated.filter((r) => r.feis_level >= 600).map((r) => r.feis_level === 700 && has(r.t, JOINED_WORDS) ? 600 : r.feis_level);
|
|
426
|
+
if (champs.length > 0)
|
|
427
|
+
return { ...answer, level: Math.min(...champs) };
|
|
428
|
+
const graded = rated.filter((r) => r.feis_level >= 100 && r.feis_level <= 500 && r.dance !== null && GRADE_DANCES.includes(r.dance) && !has(r.t, SPECIAL_WORDS));
|
|
429
|
+
const levelRange = range(graded.map((r) => r.feis_level));
|
|
430
|
+
if (levelRange === null)
|
|
431
|
+
return answer;
|
|
432
|
+
return {
|
|
433
|
+
...answer,
|
|
434
|
+
level: levelRange.lo,
|
|
435
|
+
grades: {
|
|
436
|
+
advancement_range: range(graded.filter((r) => ADVANCEMENT_DANCES2.includes(r.dance)).map((r) => r.feis_level)),
|
|
437
|
+
level_range: levelRange,
|
|
438
|
+
oireachtas_level: rows.some((r) => r.tier === "T3" && r.category === "Solo")
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
369
443
|
var ENTRY_RANK = new Map([
|
|
370
444
|
["final", 0],
|
|
371
445
|
["", 1],
|
|
@@ -565,6 +639,11 @@ class ResultFinder2 {
|
|
|
565
639
|
await this.rel.require("results.level");
|
|
566
640
|
return queryResults(this.rel, "results", q);
|
|
567
641
|
}
|
|
642
|
+
async dancerLevel(q) {
|
|
643
|
+
await this.rel.fetch("results");
|
|
644
|
+
await this.rel.require("results.dancer-level");
|
|
645
|
+
return dancerLevelOf(this.rel.db("results"), this.rel.tag, q);
|
|
646
|
+
}
|
|
568
647
|
async streamDb(q) {
|
|
569
648
|
const db = scopeDb(q.scope);
|
|
570
649
|
await this.rel.fetch(db);
|
|
@@ -3077,6 +3156,21 @@ var FEATURES2 = [
|
|
|
3077
3156
|
since: 215,
|
|
3078
3157
|
calls: "results.query, majors.results"
|
|
3079
3158
|
},
|
|
3159
|
+
{
|
|
3160
|
+
feature: "results.dancer-level",
|
|
3161
|
+
needs: [
|
|
3162
|
+
"results:competitions.feis_level",
|
|
3163
|
+
"results:competitions.dance",
|
|
3164
|
+
"results:competitions.level_printed",
|
|
3165
|
+
"results:competitions.dance_printed",
|
|
3166
|
+
"results:competitions.category",
|
|
3167
|
+
"results:results.place_printed",
|
|
3168
|
+
"results:results.atts",
|
|
3169
|
+
"results:results.withdrawn"
|
|
3170
|
+
],
|
|
3171
|
+
since: 270,
|
|
3172
|
+
calls: "results.dancerLevel"
|
|
3173
|
+
},
|
|
3080
3174
|
{
|
|
3081
3175
|
feature: "majors.order",
|
|
3082
3176
|
needs: ["majors:competitions.seq"],
|
|
@@ -3746,7 +3840,8 @@ class ReleaseManager2 {
|
|
|
3746
3840
|
copyFileSync(src, raw);
|
|
3747
3841
|
}
|
|
3748
3842
|
const base = name.replace(/\.sqlite(\.zst)?$/, "");
|
|
3749
|
-
const
|
|
3843
|
+
const side = Object.entries(manifest.sideFiles ?? {}).find(([k, f]) => (f.file ?? `${k}.zst`) === name)?.[0];
|
|
3844
|
+
const asked = base !== name && isDatabase(base) ? base : side ?? name;
|
|
3750
3845
|
const plan = planAsset2(asked, manifest);
|
|
3751
3846
|
const landed = await land(raw, { name, digest: null }, plan, join4(dest, plan.local), this.opts, log);
|
|
3752
3847
|
receipt.files[plan.local] = landed;
|
|
@@ -3842,4 +3937,4 @@ function releases2(opts = {}) {
|
|
|
3842
3937
|
return new ReleaseManager2(opts);
|
|
3843
3938
|
}
|
|
3844
3939
|
|
|
3845
|
-
export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, NO_CALENDAR2, FeisFinder2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, LINEAGE_FILE2, linksOfLineage2, releaseSchoolGraph2, readsSpanishNames2, SchoolResolver2, initialsOnly2, nameOrderOf, DancerResolver2, readPopulation2, syllabusSourceOf2, canonicalOf2, CompResolver2, StaffFinder2, 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 };
|
|
3940
|
+
export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, NO_CALENDAR2, FeisFinder2, ADVANCEMENT_DANCES2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, LINEAGE_FILE2, linksOfLineage2, releaseSchoolGraph2, readsSpanishNames2, SchoolResolver2, initialsOnly2, nameOrderOf, DancerResolver2, readPopulation2, syllabusSourceOf2, canonicalOf2, CompResolver2, StaffFinder2, 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
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
nameLevel2,
|
|
9
9
|
NO_CALENDAR2,
|
|
10
10
|
FeisFinder2,
|
|
11
|
+
ADVANCEMENT_DANCES2,
|
|
11
12
|
entryRank2,
|
|
12
13
|
FEIS_LOCAL_ID_FLOOR2,
|
|
13
14
|
ResultFinder2,
|
|
@@ -51,7 +52,7 @@ import {
|
|
|
51
52
|
openCached2,
|
|
52
53
|
ReleaseManager2,
|
|
53
54
|
releases2
|
|
54
|
-
} from "./chunks/index-
|
|
55
|
+
} from "./chunks/index-6028k52f.js";
|
|
55
56
|
import {
|
|
56
57
|
cacheRoot2,
|
|
57
58
|
repoOf2
|
|
@@ -66,6 +67,7 @@ import {
|
|
|
66
67
|
pickTag2
|
|
67
68
|
} from "./chunks/index-hghdy980.js";
|
|
68
69
|
export {
|
|
70
|
+
ADVANCEMENT_DANCES2 as ADVANCEMENT_DANCES,
|
|
69
71
|
ARCHIVE_TAG2 as ARCHIVE_TAG,
|
|
70
72
|
CompResolver2 as CompResolver,
|
|
71
73
|
DATABASES2 as DATABASES,
|
package/dist/types/assets.d.ts
CHANGED
|
@@ -14,6 +14,11 @@ export interface ReleaseManifest {
|
|
|
14
14
|
table?: string;
|
|
15
15
|
}>;
|
|
16
16
|
};
|
|
17
|
+
sideFiles?: Record<string, {
|
|
18
|
+
file?: string;
|
|
19
|
+
bytes?: number;
|
|
20
|
+
sha256?: string;
|
|
21
|
+
}>;
|
|
17
22
|
[k: string]: unknown;
|
|
18
23
|
}
|
|
19
24
|
export declare const DATABASES: readonly ["results", "majors"];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
export declare const ADVANCEMENT_DANCES: readonly string[];
|
|
3
|
+
export interface DancerLevelQuery {
|
|
4
|
+
dancer_id: number;
|
|
5
|
+
year?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface LevelRange {
|
|
8
|
+
lo: number;
|
|
9
|
+
hi: number;
|
|
10
|
+
}
|
|
11
|
+
export interface DancerGrades {
|
|
12
|
+
advancement_range: LevelRange | null;
|
|
13
|
+
level_range: LevelRange;
|
|
14
|
+
oireachtas_level: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface DancerLevelAnswer {
|
|
17
|
+
tag: string;
|
|
18
|
+
asked: DancerLevelQuery;
|
|
19
|
+
dancer_id: number;
|
|
20
|
+
year: number | null;
|
|
21
|
+
level: number | null;
|
|
22
|
+
grades?: DancerGrades;
|
|
23
|
+
}
|
|
24
|
+
export declare function dancerLevelOf(db: Database, tag: string, q: DancerLevelQuery): DancerLevelAnswer;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { type AssetPlan, DATABASES, type DatabaseName, type LandedFile, type Rec
|
|
|
2
2
|
export { sha256File, unzstdTo } from "./bytes.js";
|
|
3
3
|
export { type AgeAsked, type AgeEvidence, type DancerAnswer, type DancerBulkRecord, type DancerCandidate, type DancerQuery, type DancerRecord, DancerResolver, type DancerRegion, type DancerSchool, type DancerSearchQuery, type DancerYear, type ExcludedCandidate, FEIS_LOCAL_ID_FLOOR, type InitialsRefuted, type Narrowing, initialsOnly, type OireachtasAsked, type PopulationEvidence, type RegionEvidence, type SchoolEvidence, readPopulation, } from "./dancers.js";
|
|
4
4
|
export { NAME_LEVELS, type NameLevel, nameLevel } from "./dancers-names.js";
|
|
5
|
+
export { ADVANCEMENT_DANCES, type DancerGrades, type DancerLevelAnswer, type DancerLevelQuery, type LevelRange, } from "./dancer-level.js";
|
|
5
6
|
export { type Comp, type CompAnswer, type CompDance, type CompKind, type CompQuery, CompResolver, type CompSource, type FeisPlace, type SyllabusAnswer, type SyllabusForAnswer, type SyllabusSource, type TitleCheck, canonicalOf, syllabusSourceOf, } from "./comps.js";
|
|
6
7
|
export { type ClientOptions, cacheRoot, repoOf } from "./config.js";
|
|
7
8
|
export { type Feis, type FeisAnswer, FeisFinder, type FeisQuery, NO_CALENDAR, type Series, type SeriesAnswer, type SeriesInstance, type SeriesNext, } from "./feises.js";
|
package/dist/types/results.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DancerLevelAnswer, type DancerLevelQuery } from "./dancer-level.js";
|
|
1
2
|
import type { Release } from "./release.js";
|
|
2
3
|
import { type EntryRow, type StreamQuery, type StreamRow } from "./stream.js";
|
|
3
4
|
export declare const FEIS_LOCAL_ID_FLOOR = 17179869184;
|
|
@@ -44,6 +45,7 @@ export declare class ResultFinder {
|
|
|
44
45
|
private readonly rel;
|
|
45
46
|
constructor(rel: Release);
|
|
46
47
|
query(q: ResultQuery): Promise<ResultAnswer>;
|
|
48
|
+
dancerLevel(q: DancerLevelQuery): Promise<DancerLevelAnswer>;
|
|
47
49
|
private streamDb;
|
|
48
50
|
stream(q: StreamQuery): AsyncGenerator<StreamRow>;
|
|
49
51
|
entries(q: StreamQuery): AsyncGenerator<EntryRow>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finbheara/archive-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
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",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"prepublishOnly": "bun scripts/build.ts && bun ../../tools/checks/archive-client-tarball.ts"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@finbheara/domain": "0.
|
|
48
|
-
"@finbheara/names": "0.
|
|
47
|
+
"@finbheara/domain": "0.57.1",
|
|
48
|
+
"@finbheara/names": "0.29.0",
|
|
49
49
|
"hyparquet": "1.31.2"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|