@finbheara/archive-client 0.11.0 → 0.13.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 +43 -14
- package/dist/chunks/{index-7hw19tw1.js → index-1ty89s8d.js} +510 -327
- package/dist/cli.js +1 -1
- package/dist/index.js +5 -5
- package/dist/types/dancers-names.d.ts +1 -1
- package/dist/types/dancers.d.ts +14 -8
- package/dist/types/index.d.ts +1 -1
- package/dist/types/schools.d.ts +9 -0
- package/dist/types/stream.d.ts +1 -6
- package/package.json +1 -1
|
@@ -71,9 +71,16 @@ import { Age, DANCE_UP_YEARS, birthYearsMeet } from "@finbheara/domain/age";
|
|
|
71
71
|
import { createIndex, indexSchema, personEncoder } from "@finbheara/domain/noun-index";
|
|
72
72
|
import domainPkg from "@finbheara/domain/package.json";
|
|
73
73
|
import { dancerName } from "@finbheara/domain/person";
|
|
74
|
-
import { populationIn, populationRelation } from "@finbheara/domain/title";
|
|
75
74
|
import {
|
|
75
|
+
compTitle,
|
|
76
|
+
populationIn,
|
|
77
|
+
populationRelation
|
|
78
|
+
} from "@finbheara/domain/title";
|
|
79
|
+
import {
|
|
80
|
+
composeNames,
|
|
76
81
|
foldNameKey,
|
|
82
|
+
nameBlockKeys,
|
|
83
|
+
nameKinship,
|
|
77
84
|
surnameBlock
|
|
78
85
|
} from "@finbheara/names/normalize";
|
|
79
86
|
import namesPkg from "@finbheara/names/package.json";
|
|
@@ -87,6 +94,7 @@ var NAME_LEVELS2 = [
|
|
|
87
94
|
...NAME_LEVEL_COLUMNS,
|
|
88
95
|
"initial",
|
|
89
96
|
"middle",
|
|
97
|
+
"form",
|
|
90
98
|
"surname",
|
|
91
99
|
"like"
|
|
92
100
|
];
|
|
@@ -339,286 +347,23 @@ function notOnTheFloor(placePrinted, atts, withdrawn) {
|
|
|
339
347
|
return withdrawn !== null && DID_NOT_TABULATE.has(withdrawn);
|
|
340
348
|
}
|
|
341
349
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
feature: "dancers",
|
|
360
|
-
needs: ["results:results.name_order_resolved"],
|
|
361
|
-
since: 260,
|
|
362
|
-
calls: "dancers.resolve, dancers.search, dancers.get (the dancers index)"
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
feature: "schools",
|
|
366
|
-
needs: ["school-registry.jsonl", "school-redirects.jsonl"],
|
|
367
|
-
since: 260,
|
|
368
|
-
calls: "schools.resolve, a dancer question with a school (the schools index)"
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
feature: "feises",
|
|
372
|
-
needs: [
|
|
373
|
-
"results:feises.date_lo",
|
|
374
|
-
"results:feises.date_hi",
|
|
375
|
-
"results:feises.feis_instance_id",
|
|
376
|
-
"results:feises.feis_brand_id",
|
|
377
|
-
"results:competitions.feis_region",
|
|
378
|
-
"feis-redirects.jsonl"
|
|
379
|
-
],
|
|
380
|
-
since: 265,
|
|
381
|
-
calls: "feises.find, feises.series, and comps and registrations through them (the feises index)"
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
feature: "comps",
|
|
385
|
-
needs: [
|
|
386
|
-
"results:competitions.feis_level",
|
|
387
|
-
"results:competitions.round",
|
|
388
|
-
"results:feises.feis_brand_id"
|
|
389
|
-
],
|
|
390
|
-
since: 265,
|
|
391
|
-
calls: "comps.resolve, comps.syllabus, comps.syllabusFor, registrations.resolve (the comps index)"
|
|
392
|
-
},
|
|
393
|
-
{
|
|
394
|
-
feature: "comps.dance",
|
|
395
|
-
needs: ["results:competitions.dance"],
|
|
396
|
-
since: 270,
|
|
397
|
-
calls: "the `dance` a comp of comps.resolve, comps.syllabus and comps.syllabusFor carries: null, never refused, on a release without it"
|
|
398
|
-
},
|
|
399
|
-
{
|
|
400
|
-
feature: "schools.lineage",
|
|
401
|
-
needs: ["school-lineage.jsonl"],
|
|
402
|
-
since: 184,
|
|
403
|
-
calls: "the `lineage` school evidence of dancers.resolve and dancers.search: no school is joined by lineage, never refused, on a release without it"
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
feature: "results.stream",
|
|
407
|
-
needs: [
|
|
408
|
-
"results:competitions.round",
|
|
409
|
-
"results:competitions.feis_level",
|
|
410
|
-
"results:competitions.level_printed",
|
|
411
|
-
"results:competitions.age_printed",
|
|
412
|
-
"results:results.yob_printed",
|
|
413
|
-
"results:results.primary_source",
|
|
414
|
-
"results:results.platform_dancer_id",
|
|
415
|
-
"results:results.relative_place",
|
|
416
|
-
"results:feises.date_lo",
|
|
417
|
-
"results:feises.date_hi"
|
|
418
|
-
],
|
|
419
|
-
since: 255,
|
|
420
|
-
calls: "results.stream, results.entries (scopes transcribed, platform, all)"
|
|
421
|
-
},
|
|
422
|
-
{
|
|
423
|
-
feature: "majors.stream",
|
|
424
|
-
needs: [
|
|
425
|
-
"majors:competitions.round",
|
|
426
|
-
"majors:competitions.feis_level",
|
|
427
|
-
"majors:results.yob_printed",
|
|
428
|
-
"majors:results.primary_source",
|
|
429
|
-
"majors:results.relative_place",
|
|
430
|
-
"majors:feises.date_lo"
|
|
431
|
-
],
|
|
432
|
-
since: 255,
|
|
433
|
-
calls: "results.stream, results.entries (scope majors)"
|
|
434
|
-
},
|
|
435
|
-
{
|
|
436
|
-
feature: "results.printed_age",
|
|
437
|
-
needs: ["results:results.printed_age", "majors:results.printed_age"],
|
|
438
|
-
since: 271,
|
|
439
|
-
calls: "the `printed_age` a row of results.stream and results.entries carries: null, never refused, on a release without it"
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
feature: "majors.tabs",
|
|
443
|
-
needs: ["majors:tabs.address", "majors:tabs.true_round"],
|
|
444
|
-
since: 215,
|
|
445
|
-
calls: "majors.tabulated"
|
|
446
|
-
},
|
|
447
|
-
{
|
|
448
|
-
feature: "feises.dates",
|
|
449
|
-
needs: ["results:feises.feis_id", "results:feises.date_lo", "results:feises.date_hi"],
|
|
450
|
-
since: 245,
|
|
451
|
-
calls: "feises.dates"
|
|
452
|
-
},
|
|
453
|
-
{
|
|
454
|
-
feature: "dancers.records",
|
|
455
|
-
needs: [
|
|
456
|
-
"results:dancers.display",
|
|
457
|
-
"results:dancers.appearances",
|
|
458
|
-
"results:dancers.birth_lo",
|
|
459
|
-
"results:dancers.birth_hi",
|
|
460
|
-
"results:dancers.birth_source",
|
|
461
|
-
"results:dancers.birth_conflict",
|
|
462
|
-
"results:dancers.genders"
|
|
463
|
-
],
|
|
464
|
-
since: 215,
|
|
465
|
-
calls: "dancers.records"
|
|
466
|
-
},
|
|
467
|
-
{
|
|
468
|
-
feature: "dancers.years",
|
|
469
|
-
needs: [
|
|
470
|
-
"results:dancer_years.region",
|
|
471
|
-
"results:dancer_years.region_source",
|
|
472
|
-
"results:dancer_years.nationals",
|
|
473
|
-
"results:dancer_years.nationals_source",
|
|
474
|
-
"results:dancer_years.geohash"
|
|
475
|
-
],
|
|
476
|
-
since: 245,
|
|
477
|
-
calls: "dancers.years"
|
|
478
|
-
},
|
|
479
|
-
{
|
|
480
|
-
feature: "teams",
|
|
481
|
-
needs: [
|
|
482
|
-
...[
|
|
483
|
-
"slug",
|
|
484
|
-
"address",
|
|
485
|
-
"event",
|
|
486
|
-
"year",
|
|
487
|
-
"card",
|
|
488
|
-
"comp_no",
|
|
489
|
-
"title",
|
|
490
|
-
"category",
|
|
491
|
-
"hands",
|
|
492
|
-
"letter",
|
|
493
|
-
"label",
|
|
494
|
-
"name_printed",
|
|
495
|
-
"school_printed",
|
|
496
|
-
"school_loc",
|
|
497
|
-
"school_id",
|
|
498
|
-
"place",
|
|
499
|
-
"place_printed",
|
|
500
|
-
"tie",
|
|
501
|
-
"competitors",
|
|
502
|
-
"atts"
|
|
503
|
-
].map((c) => `table:team_results.${c}`),
|
|
504
|
-
...[
|
|
505
|
-
"team",
|
|
506
|
-
"kind",
|
|
507
|
-
"event_year",
|
|
508
|
-
"year",
|
|
509
|
-
"comp_no",
|
|
510
|
-
"title",
|
|
511
|
-
"team_index",
|
|
512
|
-
"seq",
|
|
513
|
-
"school_id",
|
|
514
|
-
"school_print_materialized"
|
|
515
|
-
].map((c) => `table:roster_teams.${c}`),
|
|
516
|
-
...["team", "seq", "name_printed", "name", "yob", "dancer_id"].map((c) => `table:roster_members.${c}`),
|
|
517
|
-
...[
|
|
518
|
-
"event_year",
|
|
519
|
-
"comp_no",
|
|
520
|
-
"team",
|
|
521
|
-
"result_address",
|
|
522
|
-
"school_id",
|
|
523
|
-
"join_kind",
|
|
524
|
-
"via_split",
|
|
525
|
-
"roster_teams",
|
|
526
|
-
"result_teams",
|
|
527
|
-
"roster_index",
|
|
528
|
-
"result_letter"
|
|
529
|
-
].map((c) => `table:team_result_rosters.${c}`),
|
|
530
|
-
"table:event_years.event_year",
|
|
531
|
-
"table:event_years.event",
|
|
532
|
-
"table:event_years.year"
|
|
533
|
-
],
|
|
534
|
-
since: 260,
|
|
535
|
-
calls: "teams.results, teams.rosters, teams.resultRosters (the teams index)"
|
|
536
|
-
},
|
|
537
|
-
{
|
|
538
|
-
feature: "teams.fields",
|
|
539
|
-
needs: [
|
|
540
|
-
"table:team_results.age",
|
|
541
|
-
"table:team_results.gender",
|
|
542
|
-
"table:team_results.printed_status",
|
|
543
|
-
"table:roster_teams.dance",
|
|
544
|
-
"table:roster_teams.school_print_as_of",
|
|
545
|
-
"table:roster_members.gender"
|
|
546
|
-
],
|
|
547
|
-
since: 266,
|
|
548
|
-
calls: "the fields teams.results (age, gender, printed_status) and teams.rosters (dance, school_printed_as_of, a member's gender) carry where present: absent, never refused, on a release without them"
|
|
549
|
-
},
|
|
550
|
-
{
|
|
551
|
-
feature: "events",
|
|
552
|
-
needs: [
|
|
553
|
-
"event_year",
|
|
554
|
-
"event",
|
|
555
|
-
"year",
|
|
556
|
-
"date_lo",
|
|
557
|
-
"date_hi",
|
|
558
|
-
"resolution",
|
|
559
|
-
"source",
|
|
560
|
-
"date_printed",
|
|
561
|
-
"place",
|
|
562
|
-
"place_printed"
|
|
563
|
-
].map((c) => `table:event_years.${c}`),
|
|
564
|
-
since: 260,
|
|
565
|
-
calls: "events.years (the events index)"
|
|
566
|
-
},
|
|
567
|
-
{
|
|
568
|
-
feature: "staff",
|
|
569
|
-
needs: [
|
|
570
|
-
"results:adjudicators",
|
|
571
|
-
"results:feis_panels",
|
|
572
|
-
"school-staff.jsonl",
|
|
573
|
-
"school-teachers.jsonl",
|
|
574
|
-
"school-registry.jsonl"
|
|
575
|
-
],
|
|
576
|
-
since: 260,
|
|
577
|
-
calls: "staff.teacher, staff.adjudicator (the staff index)"
|
|
578
|
-
}
|
|
579
|
-
];
|
|
580
|
-
async function hasNeed(probe, n) {
|
|
581
|
-
const t = /^table:([A-Za-z_]+)(?:\.([A-Za-z_]+))?$/.exec(n);
|
|
582
|
-
if (t !== null) {
|
|
583
|
-
if (probe.tableColumns === undefined)
|
|
584
|
-
return false;
|
|
585
|
-
const cols = await probe.tableColumns(t[1]);
|
|
586
|
-
return t[2] === undefined ? cols.size > 0 : cols.has(t[2]);
|
|
587
|
-
}
|
|
588
|
-
const m = /^(results|majors):([A-Za-z_]+)(?:\.([A-Za-z_]+))?$/.exec(n);
|
|
589
|
-
if (m === null)
|
|
590
|
-
return probe.publishes(n);
|
|
591
|
-
const cols = columnsOf2(await probe.db(m[1]), m[2]);
|
|
592
|
-
return m[3] === undefined ? cols.size > 0 : cols.has(m[3]);
|
|
593
|
-
}
|
|
594
|
-
async function capabilityOf2(probe, f) {
|
|
595
|
-
const missing = [];
|
|
596
|
-
for (const n of f.needs)
|
|
597
|
-
if (!await hasNeed(probe, n))
|
|
598
|
-
missing.push(n);
|
|
599
|
-
return { ...f, state: missing.length === 0 ? "full" : "missing", missing };
|
|
600
|
-
}
|
|
601
|
-
function needsRelease2(tag, c) {
|
|
602
|
-
return new Error(`archive-client: ${tag} is too old for ${c.feature} (${c.calls}): it lacks ${c.missing.join(", ")}. Open a release of cut ${c.since} or later`);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
var ENTRY_RANK = new Map([
|
|
606
|
-
["final", 0],
|
|
607
|
-
["", 1],
|
|
608
|
-
["recall", 2],
|
|
609
|
-
["standings", 3],
|
|
610
|
-
["unplaced", 4],
|
|
611
|
-
["qual", 5],
|
|
612
|
-
["qual-a", 5],
|
|
613
|
-
["qual-b", 5]
|
|
614
|
-
]);
|
|
615
|
-
function entryRank2(section) {
|
|
616
|
-
const known = ENTRY_RANK.get(section);
|
|
617
|
-
if (known !== undefined)
|
|
618
|
-
return known;
|
|
619
|
-
if (/^solo(?:-r\d+)?$/.test(section))
|
|
620
|
-
return 6;
|
|
621
|
-
return 9;
|
|
350
|
+
var ENTRY_RANK = new Map([
|
|
351
|
+
["final", 0],
|
|
352
|
+
["", 1],
|
|
353
|
+
["recall", 2],
|
|
354
|
+
["standings", 3],
|
|
355
|
+
["unplaced", 4],
|
|
356
|
+
["qual", 5],
|
|
357
|
+
["qual-a", 5],
|
|
358
|
+
["qual-b", 5]
|
|
359
|
+
]);
|
|
360
|
+
function entryRank2(section) {
|
|
361
|
+
const known = ENTRY_RANK.get(section);
|
|
362
|
+
if (known !== undefined)
|
|
363
|
+
return known;
|
|
364
|
+
if (/^solo(?:-r\d+)?$/.test(section))
|
|
365
|
+
return 6;
|
|
366
|
+
return 9;
|
|
622
367
|
}
|
|
623
368
|
var COLUMNS = `r.address AS address, r.competition AS competition, c.event AS event,
|
|
624
369
|
c.year AS year, c.tier AS tier, c.comp_no AS comp_no, c.title AS title, c.category AS category,
|
|
@@ -632,13 +377,9 @@ var COLUMNS = `r.address AS address, r.competition AS competition, c.event AS ev
|
|
|
632
377
|
r.yob_printed AS yob_printed, r.points_printed AS points_printed,
|
|
633
378
|
r.platform_dancer_id AS platform_dancer_id, r.primary_source AS primary_source,
|
|
634
379
|
r.file AS file, r.line AS line`;
|
|
635
|
-
var printedAgeColumn = (carried) => carried ? "r.printed_age AS printed_age" : "NULL AS printed_age";
|
|
636
|
-
var streamColumns = (db) => ({
|
|
637
|
-
printedAge: columnsOf2(db, "results").has("printed_age")
|
|
638
|
-
});
|
|
639
380
|
var TRANSCRIBED = "('transcript', 'manifested')";
|
|
640
381
|
var scopeDb = (s) => s === "majors" ? "majors" : "results";
|
|
641
|
-
function streamSql(q
|
|
382
|
+
function streamSql(q) {
|
|
642
383
|
const where = [];
|
|
643
384
|
const args = [];
|
|
644
385
|
const list = (col, v, nocase = false) => {
|
|
@@ -679,7 +420,7 @@ function streamSql(q, carried = { printedAge: false }) {
|
|
|
679
420
|
if (q.primary === true)
|
|
680
421
|
where.push("(r.primary_source IS NULL OR r.primary_source = '')");
|
|
681
422
|
return {
|
|
682
|
-
sql: `SELECT ${COLUMNS}
|
|
423
|
+
sql: `SELECT ${COLUMNS} FROM results r JOIN competitions c ON c.file = r.file AND c.line = r.competition_line${where.length === 0 ? "" : ` WHERE ${where.join(" AND ")}`} ORDER BY r.file, r.line`,
|
|
683
424
|
args
|
|
684
425
|
};
|
|
685
426
|
}
|
|
@@ -702,13 +443,12 @@ function rowOf(r, dates) {
|
|
|
702
443
|
withdrawn: text(withdrawn),
|
|
703
444
|
qualifier: text(rest.qualifier),
|
|
704
445
|
primary_source: text(rest.primary_source),
|
|
705
|
-
printed_age: text(rest.printed_age),
|
|
706
446
|
dancer_scope: r.dancer_id === null ? null : r.dancer_id >= FEIS_LOCAL_ID_FLOOR2 ? "per-release" : "stable"
|
|
707
447
|
};
|
|
708
448
|
}
|
|
709
449
|
async function* streamRows(db, q) {
|
|
710
450
|
const dates = feisDates(db);
|
|
711
|
-
const { sql, args } = streamSql(q
|
|
451
|
+
const { sql, args } = streamSql(q);
|
|
712
452
|
const stmt = db.prepare(sql);
|
|
713
453
|
try {
|
|
714
454
|
for (const r of stmt.iterate(...args))
|
|
@@ -719,7 +459,7 @@ async function* streamRows(db, q) {
|
|
|
719
459
|
}
|
|
720
460
|
async function* entryRows(db, q) {
|
|
721
461
|
const dates = feisDates(db);
|
|
722
|
-
const { sql, args } = streamSql(q
|
|
462
|
+
const { sql, args } = streamSql(q);
|
|
723
463
|
const stmt = db.prepare(sql);
|
|
724
464
|
let file = null;
|
|
725
465
|
let held = new Map;
|
|
@@ -904,7 +644,7 @@ var PERSON = { table: "person", value: "id", valueType: "INTEGER PRIMARY KEY" };
|
|
|
904
644
|
var ASKED = ["surname_key", "nn4", "blk_surname"];
|
|
905
645
|
var dancersIndex = {
|
|
906
646
|
name: "dancers",
|
|
907
|
-
version: "
|
|
647
|
+
version: "5",
|
|
908
648
|
inputs: ["results"],
|
|
909
649
|
keyParts: () => [...PACKAGE_KEY_PARTS],
|
|
910
650
|
says: "every published dancer print at the names package's keys, its dancers, schools and years",
|
|
@@ -916,6 +656,8 @@ var dancersIndex = {
|
|
|
916
656
|
out.run("CREATE TABLE dancer_school(dancer_id INTEGER NOT NULL, school_id TEXT, print TEXT NOT NULL, rows INTEGER NOT NULL, year_lo INTEGER, year_hi INTEGER)");
|
|
917
657
|
out.run("CREATE TABLE dancer_population(dancer_id INTEGER NOT NULL, population TEXT NOT NULL, rows INTEGER NOT NULL, PRIMARY KEY(dancer_id, population)) WITHOUT ROWID");
|
|
918
658
|
out.run("CREATE TABLE dancer_regional(dancer_id INTEGER NOT NULL, year INTEGER NOT NULL, region TEXT NOT NULL, regional INTEGER NOT NULL, PRIMARY KEY(dancer_id, year)) WITHOUT ROWID");
|
|
659
|
+
out.run("CREATE TABLE person_block(block TEXT NOT NULL, person INTEGER NOT NULL)");
|
|
660
|
+
const putBlock = out.prepare("INSERT INTO person_block VALUES (?, ?)");
|
|
919
661
|
const src = rel.db("results");
|
|
920
662
|
const groups = src.query(`SELECT r.name_printed AS p, r.name_order_resolved AS o, r.dancer_id AS d,
|
|
921
663
|
r.school_printed AS sp, r.school_id AS s,
|
|
@@ -937,6 +679,8 @@ var dancersIndex = {
|
|
|
937
679
|
id = ++next;
|
|
938
680
|
names.add(name, id);
|
|
939
681
|
personOf.set(reading, id);
|
|
682
|
+
for (const b of nameBlockKeys(name))
|
|
683
|
+
putBlock.run(b, id);
|
|
940
684
|
}
|
|
941
685
|
personOf.set(k, id);
|
|
942
686
|
}
|
|
@@ -1012,6 +756,7 @@ var dancersIndex = {
|
|
|
1012
756
|
for (const s of schema.indexes)
|
|
1013
757
|
if (ASKED.some((c) => s.endsWith(`(${c})`)))
|
|
1014
758
|
out.run(s);
|
|
759
|
+
out.run("CREATE INDEX person_block_block ON person_block(block)");
|
|
1015
760
|
out.run("CREATE INDEX print_dancer_person ON print_dancer(person)");
|
|
1016
761
|
out.run("CREATE INDEX print_dancer_dancer ON print_dancer(dancer_id)");
|
|
1017
762
|
out.run("CREATE INDEX dancer_school_dancer ON dancer_school(dancer_id)");
|
|
@@ -1031,6 +776,7 @@ var NAME_WEIGHT = {
|
|
|
1031
776
|
nn4: 3,
|
|
1032
777
|
initial: 2,
|
|
1033
778
|
middle: 1.5,
|
|
779
|
+
form: 1.25,
|
|
1034
780
|
surname: 1,
|
|
1035
781
|
like: 1
|
|
1036
782
|
};
|
|
@@ -1171,7 +917,56 @@ class DancerResolver2 {
|
|
|
1171
917
|
if (level !== null)
|
|
1172
918
|
hits.set(value, level);
|
|
1173
919
|
}
|
|
1174
|
-
|
|
920
|
+
const exact = await this.answer(db, q, hits);
|
|
921
|
+
if (exact.arm !== "none")
|
|
922
|
+
return exact;
|
|
923
|
+
const born = await this.answer(db, q, hits, true, { birthSlack: true });
|
|
924
|
+
if (born.arm !== "none")
|
|
925
|
+
return { ...born, widened: "birth-conflict" };
|
|
926
|
+
const forms = this.formsOf(db, names, asked, hits);
|
|
927
|
+
if (forms.size === 0)
|
|
928
|
+
return exact;
|
|
929
|
+
const all = new Map(hits);
|
|
930
|
+
for (const id of forms.keys())
|
|
931
|
+
all.set(id, "form");
|
|
932
|
+
const formed = await this.answer(db, q, all, true, { birthSlack: true, forms });
|
|
933
|
+
return formed.arm === "none" ? exact : { ...formed, widened: "name-form" };
|
|
934
|
+
}
|
|
935
|
+
formsOf(db, names, asked, hits) {
|
|
936
|
+
const out = new Map;
|
|
937
|
+
const keys = nameBlockKeys(asked);
|
|
938
|
+
const persons = new Set;
|
|
939
|
+
if (keys.length > 0)
|
|
940
|
+
for (const r of db.query(`SELECT DISTINCT person FROM person_block WHERE block IN (${keys.map(() => "?").join(",")})`).all(...keys))
|
|
941
|
+
persons.add(r.person);
|
|
942
|
+
const rows = new Map;
|
|
943
|
+
for (const { value, row } of names.rows({ surname_key: [asked.surnameKey()] }, [
|
|
944
|
+
"print",
|
|
945
|
+
"reading"
|
|
946
|
+
]))
|
|
947
|
+
rows.set(value, row);
|
|
948
|
+
const ids = [...persons].filter((id) => !hits.has(id) && !rows.has(id));
|
|
949
|
+
for (let i = 0;i < ids.length; i += 500) {
|
|
950
|
+
const chunk = ids.slice(i, i + 500);
|
|
951
|
+
for (const r of db.query(`SELECT id, print, reading FROM person WHERE id IN (${chunk.map(() => "?").join(",")})`).all(...chunk))
|
|
952
|
+
rows.set(r.id, { print: r.print, reading: r.reading });
|
|
953
|
+
}
|
|
954
|
+
for (const [id, row] of rows) {
|
|
955
|
+
if (hits.has(id))
|
|
956
|
+
continue;
|
|
957
|
+
const c = ENCODER.decode(row);
|
|
958
|
+
if (!c.isPerson())
|
|
959
|
+
continue;
|
|
960
|
+
const composed = composeNames(asked, c);
|
|
961
|
+
if (composed.composition !== "conflict" && composed.composition !== "silent") {
|
|
962
|
+
out.set(id, [...composed.via]);
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
const kin = nameKinship(asked, c);
|
|
966
|
+
if (kin.relation !== "different")
|
|
967
|
+
out.set(id, [...kin.via]);
|
|
968
|
+
}
|
|
969
|
+
return out;
|
|
1175
970
|
}
|
|
1176
971
|
async search(q) {
|
|
1177
972
|
if (q.name !== undefined) {
|
|
@@ -1222,7 +1017,7 @@ class DancerResolver2 {
|
|
|
1222
1017
|
schools: schoolsOf(db, dancerId)
|
|
1223
1018
|
};
|
|
1224
1019
|
}
|
|
1225
|
-
async answer(db, q, hits, narrow = true) {
|
|
1020
|
+
async answer(db, q, hits, narrow = true, widen = {}) {
|
|
1226
1021
|
const asOf = yearOf(q.asOf);
|
|
1227
1022
|
const age = q.age === undefined ? undefined : this.readAge(q.age, asOf);
|
|
1228
1023
|
const population = q.population === undefined ? undefined : readPopulation2(q.population);
|
|
@@ -1231,7 +1026,7 @@ class DancerResolver2 {
|
|
|
1231
1026
|
let lineageIds = [];
|
|
1232
1027
|
let school;
|
|
1233
1028
|
if (q.school !== undefined) {
|
|
1234
|
-
const s = await this.rel.schools.resolve(q.school);
|
|
1029
|
+
const s = await this.rel.schools.resolve(q.school, { year: askedYear(q, asOf) });
|
|
1235
1030
|
schoolIds = s.candidates.map((c) => c.school_id);
|
|
1236
1031
|
const lineage = await this.lineage();
|
|
1237
1032
|
if (lineage === null)
|
|
@@ -1251,9 +1046,11 @@ class DancerResolver2 {
|
|
|
1251
1046
|
unlinked.set(r.print, (unlinked.get(r.print) ?? 0) + r.n);
|
|
1252
1047
|
continue;
|
|
1253
1048
|
}
|
|
1254
|
-
const e = byDancer.get(r.d) ?? { level, prints: [] };
|
|
1049
|
+
const e = byDancer.get(r.d) ?? { level, prints: [], via: new Set };
|
|
1255
1050
|
e.level = finer(e.level, level);
|
|
1256
1051
|
e.prints.push({ print: r.print, rows: r.n, level });
|
|
1052
|
+
for (const v of widen.forms?.get(person) ?? [])
|
|
1053
|
+
e.via.add(v);
|
|
1257
1054
|
byDancer.set(r.d, e);
|
|
1258
1055
|
}
|
|
1259
1056
|
const dq = db.query("SELECT * FROM dancer WHERE dancer_id = ?");
|
|
@@ -1264,7 +1061,15 @@ class DancerResolver2 {
|
|
|
1264
1061
|
continue;
|
|
1265
1062
|
const schools = schoolsOf(db, id);
|
|
1266
1063
|
const latest = latestSchool(schools, asOf);
|
|
1267
|
-
|
|
1064
|
+
let ageEv = ageEvidence(d, age?.age ?? null);
|
|
1065
|
+
let widenedBirth = false;
|
|
1066
|
+
if (widen.birthSlack && ageEv === "contradicts" && d.birth_conflict !== 0) {
|
|
1067
|
+
const wider = ageEvidence(d, age?.age ?? null, 1);
|
|
1068
|
+
if (wider !== "contradicts") {
|
|
1069
|
+
ageEv = wider;
|
|
1070
|
+
widenedBirth = true;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1268
1073
|
const pop = populationOf(db, id);
|
|
1269
1074
|
const popEv = populationEvidence(pop, population?.read ?? null);
|
|
1270
1075
|
const schoolEv = schoolIds === null ? "unasked" : schoolIds.length === 0 ? "unresolved" : await this.schoolEvidence(schools, schoolIds, lineageIds, asOf);
|
|
@@ -1285,8 +1090,10 @@ class DancerResolver2 {
|
|
|
1285
1090
|
age: ageEv,
|
|
1286
1091
|
school: schoolEv,
|
|
1287
1092
|
population: popEv,
|
|
1288
|
-
region: regionEv
|
|
1093
|
+
region: regionEv,
|
|
1094
|
+
...e.level === "form" ? { form: [...e.via] } : {}
|
|
1289
1095
|
},
|
|
1096
|
+
...widenedBirth ? { widened: "birth-conflict" } : {},
|
|
1290
1097
|
prints: e.prints.sort((a, b) => b.rows - a.rows || (a.print < b.print ? -1 : 1)),
|
|
1291
1098
|
rows: d.rows,
|
|
1292
1099
|
year_lo: d.year_lo,
|
|
@@ -1313,18 +1120,54 @@ class DancerResolver2 {
|
|
|
1313
1120
|
return false;
|
|
1314
1121
|
});
|
|
1315
1122
|
const year = askedYear(q, asOf);
|
|
1316
|
-
|
|
1317
|
-
|
|
1123
|
+
const band = age?.age?.birthYears() ?? null;
|
|
1124
|
+
const adultBy = adultBornBy(year);
|
|
1125
|
+
const adultAsked = adultBy !== null && adultCategory(q.age, age?.age ?? null);
|
|
1126
|
+
const returningOf = (c) => adultAsked && c.birth.hi !== null && c.birth.hi <= adultBy ? c.birth.lo : null;
|
|
1127
|
+
let line = null;
|
|
1128
|
+
if (schoolIds !== null && schoolIds.length > 0) {
|
|
1318
1129
|
const lineage = await this.lineage();
|
|
1319
|
-
|
|
1320
|
-
|
|
1130
|
+
line = lineage === null ? new Set(schoolIds) : schoolLine(lineage, schoolIds, year);
|
|
1131
|
+
}
|
|
1132
|
+
const formFacts = new Map;
|
|
1133
|
+
if (narrow && widen.forms !== undefined && line !== null) {
|
|
1134
|
+
const next = [];
|
|
1135
|
+
for (const c of kept) {
|
|
1136
|
+
if (c.evidence.nameLevel === "form") {
|
|
1137
|
+
const fact = await this.schoolRefutes(schoolsOf(db, c.dancer_id), line, year, q.school, returningOf(c));
|
|
1138
|
+
if (fact !== null) {
|
|
1139
|
+
formFacts.set(c.dancer_id, fact);
|
|
1140
|
+
excluded.push({ candidate: c, why: "name-form" });
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
next.push(c);
|
|
1145
|
+
}
|
|
1146
|
+
kept = next;
|
|
1147
|
+
}
|
|
1148
|
+
const askedName = dancerName(q.name, q.order === "unknown" ? undefined : q.order);
|
|
1149
|
+
const byInitials = initialsOnly2(askedName);
|
|
1150
|
+
let narrowing;
|
|
1151
|
+
if (narrow && kept.length > 1 && line !== null) {
|
|
1321
1152
|
const slack = q.age !== undefined && !("yob" in q.age) ? DANCE_UP_YEARS : 0;
|
|
1322
1153
|
const open = [];
|
|
1323
1154
|
const held = [];
|
|
1324
1155
|
const refuted = [];
|
|
1325
1156
|
const bySchool = new Set;
|
|
1157
|
+
const givenFact = (c) => {
|
|
1158
|
+
if (byInitials || c.evidence.nameLevel !== "initial")
|
|
1159
|
+
return false;
|
|
1160
|
+
const fact = otherGiven(db, c.dancer_id, askedName);
|
|
1161
|
+
if (fact === null)
|
|
1162
|
+
return false;
|
|
1163
|
+
refuted.push({ dancer_id: c.dancer_id, why: "given", fact });
|
|
1164
|
+
bySchool.add(c.dancer_id);
|
|
1165
|
+
return true;
|
|
1166
|
+
};
|
|
1326
1167
|
const schoolFact = async (c) => {
|
|
1327
|
-
|
|
1168
|
+
if (givenFact(c))
|
|
1169
|
+
return true;
|
|
1170
|
+
const fact = await this.schoolRefutes(schoolsOf(db, c.dancer_id), line, year, q.school, returningOf(c));
|
|
1328
1171
|
if (fact === null)
|
|
1329
1172
|
return false;
|
|
1330
1173
|
refuted.push({ dancer_id: c.dancer_id, why: "school", fact });
|
|
@@ -1345,10 +1188,10 @@ class DancerResolver2 {
|
|
|
1345
1188
|
refuted.push({
|
|
1346
1189
|
dancer_id: c.dancer_id,
|
|
1347
1190
|
why: x.why,
|
|
1348
|
-
fact: contradictionFact(x, age, slack)
|
|
1191
|
+
fact: formFacts.get(c.dancer_id) ?? contradictionFact(x, age, slack)
|
|
1349
1192
|
});
|
|
1350
1193
|
}
|
|
1351
|
-
|
|
1194
|
+
narrowing = {
|
|
1352
1195
|
open: [...open.map((c) => c.dancer_id), ...held],
|
|
1353
1196
|
heldByDanceUp: held,
|
|
1354
1197
|
refuted,
|
|
@@ -1357,7 +1200,10 @@ class DancerResolver2 {
|
|
|
1357
1200
|
if (open.length === 1 && held.length === 0) {
|
|
1358
1201
|
for (const c of kept)
|
|
1359
1202
|
if (bySchool.has(c.dancer_id))
|
|
1360
|
-
excluded.push({
|
|
1203
|
+
excluded.push({
|
|
1204
|
+
candidate: c,
|
|
1205
|
+
why: refuted.find((r) => r.dancer_id === c.dancer_id)?.why === "given" ? "given" : "school"
|
|
1206
|
+
});
|
|
1361
1207
|
kept = open;
|
|
1362
1208
|
}
|
|
1363
1209
|
}
|
|
@@ -1387,8 +1233,8 @@ class DancerResolver2 {
|
|
|
1387
1233
|
};
|
|
1388
1234
|
}
|
|
1389
1235
|
}
|
|
1390
|
-
if (preferred === undefined && kept.length > 1 &&
|
|
1391
|
-
const openIds = new Set(
|
|
1236
|
+
if (preferred === undefined && kept.length > 1 && narrowing !== undefined) {
|
|
1237
|
+
const openIds = new Set(narrowing.open);
|
|
1392
1238
|
const hit = kept.filter((c) => openIds.has(c.dancer_id) && c.year_hi !== null && c.year_hi >= year - 1);
|
|
1393
1239
|
if (hit.length === 1) {
|
|
1394
1240
|
const c = hit[0];
|
|
@@ -1410,7 +1256,7 @@ class DancerResolver2 {
|
|
|
1410
1256
|
...population !== undefined ? { population } : {},
|
|
1411
1257
|
...oireachtas !== undefined ? { oireachtas } : {},
|
|
1412
1258
|
...school !== undefined ? { school } : {},
|
|
1413
|
-
...
|
|
1259
|
+
...narrowing !== undefined ? byInitials ? { initials: narrowing } : { fullName: narrowing } : {}
|
|
1414
1260
|
};
|
|
1415
1261
|
}
|
|
1416
1262
|
async readOireachtas(db, o, asOf) {
|
|
@@ -1463,20 +1309,30 @@ class DancerResolver2 {
|
|
|
1463
1309
|
}
|
|
1464
1310
|
return joined ? "lineage" : earlier ? "moved" : "none";
|
|
1465
1311
|
}
|
|
1466
|
-
async schoolRefutes(schools, line, year, asked) {
|
|
1312
|
+
async schoolRefutes(schools, line, year, asked, returning = null) {
|
|
1467
1313
|
const rows = schools.filter((s) => s.year_lo === null || s.year_lo <= year);
|
|
1468
1314
|
if (rows.length === 0)
|
|
1469
1315
|
return null;
|
|
1470
1316
|
const seen = new Set;
|
|
1317
|
+
const kept = [];
|
|
1318
|
+
let youth = 0;
|
|
1471
1319
|
for (const s of rows) {
|
|
1472
1320
|
const own = await this.schoolIdsOf(s);
|
|
1473
1321
|
if (own.length === 0 || own.some((id) => line.has(id)))
|
|
1474
1322
|
return null;
|
|
1323
|
+
if (returning !== null && youthRow(s, returning)) {
|
|
1324
|
+
youth++;
|
|
1325
|
+
continue;
|
|
1326
|
+
}
|
|
1327
|
+
kept.push(s);
|
|
1475
1328
|
for (const id of own)
|
|
1476
1329
|
seen.add(id);
|
|
1477
1330
|
}
|
|
1478
|
-
|
|
1479
|
-
|
|
1331
|
+
if (kept.length === 0)
|
|
1332
|
+
return null;
|
|
1333
|
+
const last = latestSchool(kept, year);
|
|
1334
|
+
const set = youth > 0 ? ` (${youth} youth row(s) set aside for an adult-category question)` : "";
|
|
1335
|
+
return `every school row to ${year} reads ${[...seen].sort().join(", ")} (the latest ${last?.school_id ?? last?.print} in ${last?.year_hi}), none of them ${asked ?? "the asked school"} or a school its line joined by ${year}${set}`;
|
|
1480
1336
|
}
|
|
1481
1337
|
async schoolIdsOf(s) {
|
|
1482
1338
|
const key = s.school_id ?? s.print;
|
|
@@ -1515,6 +1371,40 @@ class DancerResolver2 {
|
|
|
1515
1371
|
return done(Age.from(c.age, { year: c.year }));
|
|
1516
1372
|
}
|
|
1517
1373
|
}
|
|
1374
|
+
function adultBornBy(year) {
|
|
1375
|
+
const a = Age.from("adult", { year });
|
|
1376
|
+
return a.isAge() ? a.birthYears().to : null;
|
|
1377
|
+
}
|
|
1378
|
+
function adultCategory(asked, read) {
|
|
1379
|
+
if (read?.isAge() && read.isAdult())
|
|
1380
|
+
return true;
|
|
1381
|
+
if (asked === undefined || !("band" in asked))
|
|
1382
|
+
return false;
|
|
1383
|
+
const t = compTitle(asked.band).age;
|
|
1384
|
+
return t?.isAge() === true && t.isAdult();
|
|
1385
|
+
}
|
|
1386
|
+
function youthRow(s, bornLo) {
|
|
1387
|
+
if (bornLo === null || s.year_hi === null)
|
|
1388
|
+
return false;
|
|
1389
|
+
const by = adultBornBy(s.year_hi);
|
|
1390
|
+
return by !== null && bornLo > by;
|
|
1391
|
+
}
|
|
1392
|
+
function otherGiven(db, dancerId, asked) {
|
|
1393
|
+
const r = db.query("SELECT names_printed FROM dancer WHERE dancer_id = ?").get(dancerId);
|
|
1394
|
+
if (r === null)
|
|
1395
|
+
return null;
|
|
1396
|
+
for (const print of JSON.parse(r.names_printed)) {
|
|
1397
|
+
const p = dancerName(print);
|
|
1398
|
+
if (!p.isPerson() || (p.givenFields()[0]?.length ?? 0) < 2)
|
|
1399
|
+
continue;
|
|
1400
|
+
if (nameLevel2(asked, p) !== null)
|
|
1401
|
+
continue;
|
|
1402
|
+
if (nameKinship(asked, p).relation !== "different")
|
|
1403
|
+
continue;
|
|
1404
|
+
return `printed "${print}", another given name than the asked one, and met it only through an initial`;
|
|
1405
|
+
}
|
|
1406
|
+
return null;
|
|
1407
|
+
}
|
|
1518
1408
|
function birthOf(d) {
|
|
1519
1409
|
return {
|
|
1520
1410
|
lo: d.birth_lo,
|
|
@@ -1582,12 +1472,12 @@ function regionEvidence(db, dancerId, region, year) {
|
|
|
1582
1472
|
return { word: "contradicts", region: at(latest) };
|
|
1583
1473
|
return { word: latest.region === region ? "same" : "moved", region: at(latest) };
|
|
1584
1474
|
}
|
|
1585
|
-
function ageEvidence(d, asked) {
|
|
1475
|
+
function ageEvidence(d, asked, slack = 0) {
|
|
1586
1476
|
if (asked === null)
|
|
1587
1477
|
return "unasked";
|
|
1588
1478
|
if (d.birth_lo === null && d.birth_hi === null)
|
|
1589
1479
|
return "unknown";
|
|
1590
|
-
const own = Age.fromYOB(d.birth_lo, d.birth_hi);
|
|
1480
|
+
const own = Age.fromYOB(d.birth_lo === null ? null : d.birth_lo - slack, d.birth_hi === null ? null : d.birth_hi + slack);
|
|
1591
1481
|
if (!own.isAge())
|
|
1592
1482
|
return "unknown";
|
|
1593
1483
|
if (own.fitsIn(asked))
|
|
@@ -1620,7 +1510,7 @@ function contradictionFact(x, age, slack) {
|
|
|
1620
1510
|
import { Age as Age2 } from "@finbheara/domain/age";
|
|
1621
1511
|
import {
|
|
1622
1512
|
codePopulation,
|
|
1623
|
-
compTitle,
|
|
1513
|
+
compTitle as compTitle2,
|
|
1624
1514
|
matchTitle,
|
|
1625
1515
|
populationIn as populationIn2,
|
|
1626
1516
|
secondIn,
|
|
@@ -1689,7 +1579,7 @@ function compOf(r, codes = []) {
|
|
|
1689
1579
|
const stated = r.gender_source === "level" ? "" : populationIn2(r.gender ?? "");
|
|
1690
1580
|
const pop = stated || populationIn2(r.title ?? "") || (r.platform === "ifeis" && codes.length > 0 ? codePopulation(r.code, codes) : "") || populationIn2(r.gender ?? "");
|
|
1691
1581
|
const second = [r.title, r.level_printed, r.dance_printed].some((t) => t != null && secondIn(t));
|
|
1692
|
-
const round = r.round ?? (r.title === null ? null :
|
|
1582
|
+
const round = r.round ?? (r.title === null ? null : compTitle2(r.title).round?.toString() ?? null);
|
|
1693
1583
|
return {
|
|
1694
1584
|
competition: r.competition,
|
|
1695
1585
|
feis_id: r.feis_id,
|
|
@@ -1950,7 +1840,7 @@ class CompResolver2 {
|
|
|
1950
1840
|
if (q.title === undefined || q.title.trim() === "")
|
|
1951
1841
|
return answer("unique", "prior-year", hits, { from, check: "unchecked" });
|
|
1952
1842
|
const m = matchTitle(q.title, [entryOf(hit)]);
|
|
1953
|
-
const r = titleRelation(m.title,
|
|
1843
|
+
const r = titleRelation(m.title, compTitle2(hit.title ?? "", {}));
|
|
1954
1844
|
const check = {
|
|
1955
1845
|
title: q.title,
|
|
1956
1846
|
agrees: m.isSingular(),
|
|
@@ -2222,22 +2112,28 @@ var encoderOf = (r) => schoolEncoder({ reader: r.reader });
|
|
|
2222
2112
|
var TABLE = "school_name";
|
|
2223
2113
|
var schoolsIndex = {
|
|
2224
2114
|
name: "schools",
|
|
2225
|
-
version: "
|
|
2115
|
+
version: "3",
|
|
2226
2116
|
inputs: ["results", "school-registry.jsonl", "school-redirects.jsonl"],
|
|
2227
2117
|
says: "every published school print and its ids, read at the school reader's slots",
|
|
2228
2118
|
keyParts: (rel) => [`reader=${rel.schoolReader().id}`],
|
|
2229
2119
|
build(rel, out) {
|
|
2230
2120
|
const choice = rel.schoolReader();
|
|
2231
2121
|
const encoder = encoderOf(choice);
|
|
2232
|
-
out.run("CREATE TABLE print_id(print TEXT NOT NULL, school_id TEXT NOT NULL, rows INTEGER NOT NULL, PRIMARY KEY(print, school_id)) WITHOUT ROWID");
|
|
2122
|
+
out.run("CREATE TABLE print_id(print TEXT NOT NULL, school_id TEXT NOT NULL, rows INTEGER NOT NULL, year_lo INTEGER, year_hi INTEGER, PRIMARY KEY(print, school_id)) WITHOUT ROWID");
|
|
2233
2123
|
out.run("CREATE TABLE registry(school_id TEXT PRIMARY KEY, display TEXT NOT NULL, regions TEXT NOT NULL, places TEXT NOT NULL)");
|
|
2234
2124
|
out.run("CREATE TABLE redirect(old_id TEXT PRIMARY KEY, new_id TEXT NOT NULL, kind TEXT NOT NULL)");
|
|
2235
2125
|
const schema = indexSchema3(encoder, { table: TABLE });
|
|
2236
2126
|
out.run(schema.table);
|
|
2237
|
-
const
|
|
2238
|
-
const
|
|
2127
|
+
const src = rel.db("results");
|
|
2128
|
+
const lined = src.query("PRAGMA table_info(results)").all().some((c) => c.name === "competition_line");
|
|
2129
|
+
const pairs = src.query(lined ? `SELECT trim(r.school_printed) AS p, r.school_id AS s, count(*) AS n,
|
|
2130
|
+
min(c.year) AS lo, max(c.year) AS hi
|
|
2131
|
+
FROM results r LEFT JOIN competitions c ON c.file = r.file AND c.line = r.competition_line
|
|
2132
|
+
WHERE r.school_id IS NOT NULL GROUP BY 1, 2 ORDER BY 1, 2` : `SELECT trim(school_printed) AS p, school_id AS s, count(*) AS n, NULL AS lo, NULL AS hi
|
|
2133
|
+
FROM results WHERE school_id IS NOT NULL GROUP BY 1, 2 ORDER BY 1, 2`).all();
|
|
2134
|
+
const putPair = out.prepare("INSERT INTO print_id VALUES (?, ?, ?, ?, ?)");
|
|
2239
2135
|
for (const r of pairs)
|
|
2240
|
-
putPair.run(r.p, r.s, r.n);
|
|
2136
|
+
putPair.run(r.p, r.s, r.n, r.lo, r.hi);
|
|
2241
2137
|
out.run("CREATE INDEX print_id_school ON print_id(school_id)");
|
|
2242
2138
|
const names = createIndex3(out, encoder, {
|
|
2243
2139
|
table: TABLE
|
|
@@ -2282,6 +2178,23 @@ class SchoolResolver2 {
|
|
|
2282
2178
|
};
|
|
2283
2179
|
const answer = (candidates, extra = {}) => {
|
|
2284
2180
|
let cs = candidates.sort((a, b) => b.rows - a.rows || (a.school_id < b.school_id ? -1 : 1));
|
|
2181
|
+
let year;
|
|
2182
|
+
if (q.year !== undefined && cs.length > 1 && cs.every((c) => c.years !== undefined)) {
|
|
2183
|
+
const y = q.year;
|
|
2184
|
+
const span = (c) => c.years;
|
|
2185
|
+
let kept = cs.filter((c) => {
|
|
2186
|
+
const { lo, hi } = span(c);
|
|
2187
|
+
return lo !== null && hi !== null && lo <= y && y <= hi;
|
|
2188
|
+
});
|
|
2189
|
+
if (kept.length === 0) {
|
|
2190
|
+
const before = cs.filter((c) => span(c).hi !== null && span(c).hi < y);
|
|
2191
|
+
const last = Math.max(...before.map((c) => span(c).hi));
|
|
2192
|
+
kept = before.filter((c) => span(c).hi === last);
|
|
2193
|
+
}
|
|
2194
|
+
year = { asked: y, narrowed: kept.length > 0 && kept.length < cs.length };
|
|
2195
|
+
if (kept.length > 0)
|
|
2196
|
+
cs = kept;
|
|
2197
|
+
}
|
|
2285
2198
|
let region;
|
|
2286
2199
|
if (q.region !== undefined) {
|
|
2287
2200
|
const kept = cs.filter((c) => c.regions.length === 0 || c.regions.includes(q.region));
|
|
@@ -2295,6 +2208,7 @@ class SchoolResolver2 {
|
|
|
2295
2208
|
arm: cs.length === 0 ? "none" : cs.length === 1 ? "unique" : "ambiguous",
|
|
2296
2209
|
candidates: cs,
|
|
2297
2210
|
...region !== undefined ? { region } : {},
|
|
2211
|
+
...year !== undefined ? { year } : {},
|
|
2298
2212
|
...extra
|
|
2299
2213
|
};
|
|
2300
2214
|
};
|
|
@@ -2318,15 +2232,27 @@ class SchoolResolver2 {
|
|
|
2318
2232
|
return answer([byId(live, "redirect")], { redirected: { from: a, to: live } });
|
|
2319
2233
|
const byPrints = (prints, by) => {
|
|
2320
2234
|
const ids = new Map;
|
|
2321
|
-
const q2 = db.query("SELECT school_id, rows FROM print_id WHERE print = ?");
|
|
2235
|
+
const q2 = db.query("SELECT school_id, rows, year_lo AS lo, year_hi AS hi FROM print_id WHERE print = ?");
|
|
2322
2236
|
for (const p of prints)
|
|
2323
2237
|
for (const r of q2.all(p)) {
|
|
2324
|
-
const e = ids.get(r.school_id) ?? {
|
|
2238
|
+
const e = ids.get(r.school_id) ?? {
|
|
2239
|
+
rows: 0,
|
|
2240
|
+
prints: new Set,
|
|
2241
|
+
lo: null,
|
|
2242
|
+
hi: null
|
|
2243
|
+
};
|
|
2325
2244
|
e.rows += r.rows;
|
|
2326
2245
|
e.prints.add(p);
|
|
2246
|
+
if (r.lo !== null && (e.lo === null || r.lo < e.lo))
|
|
2247
|
+
e.lo = r.lo;
|
|
2248
|
+
if (r.hi !== null && (e.hi === null || r.hi > e.hi))
|
|
2249
|
+
e.hi = r.hi;
|
|
2327
2250
|
ids.set(r.school_id, e);
|
|
2328
2251
|
}
|
|
2329
|
-
return [...ids].map(([id, e]) =>
|
|
2252
|
+
return [...ids].map(([id, e]) => ({
|
|
2253
|
+
...candidate(id, by, e.rows, [...e.prints].sort()),
|
|
2254
|
+
years: { lo: e.lo, hi: e.hi }
|
|
2255
|
+
}));
|
|
2330
2256
|
};
|
|
2331
2257
|
const exact = byPrints([a], "print");
|
|
2332
2258
|
if (exact.length > 0)
|
|
@@ -2732,7 +2658,7 @@ import {
|
|
|
2732
2658
|
import { join as join3 } from "path";
|
|
2733
2659
|
|
|
2734
2660
|
import { Age as Age3 } from "@finbheara/domain/age";
|
|
2735
|
-
import { compTitle as
|
|
2661
|
+
import { compTitle as compTitle3 } from "@finbheara/domain/title";
|
|
2736
2662
|
function youngest2(answers) {
|
|
2737
2663
|
const key = (a) => {
|
|
2738
2664
|
const iv = Age3.from(a.age, { year: null }).interval();
|
|
@@ -2812,7 +2738,7 @@ class RegistrationResolver2 {
|
|
|
2812
2738
|
const printed = first !== undefined ? [] : comps.flatMap((c, i) => {
|
|
2813
2739
|
const code = row.codes[i];
|
|
2814
2740
|
const t = row.titles?.[code];
|
|
2815
|
-
const a = t === undefined ? null :
|
|
2741
|
+
const a = t === undefined ? null : compTitle3(t).age;
|
|
2816
2742
|
return a?.isAge() === true && a.kind !== "yob" ? [{ age: a.ag1, year: c.feis.year, code }] : [];
|
|
2817
2743
|
});
|
|
2818
2744
|
const byTitle = youngest2(printed);
|
|
@@ -3022,6 +2948,263 @@ function defaultRemote() {
|
|
|
3022
2948
|
return installed ?? fallbackRemote2(ghRemote2, httpsRemote2());
|
|
3023
2949
|
}
|
|
3024
2950
|
|
|
2951
|
+
function columnsOf2(db, table) {
|
|
2952
|
+
return new Set(db.query("SELECT name FROM pragma_table_info(?)").all(table).map((r) => r.name));
|
|
2953
|
+
}
|
|
2954
|
+
var FEATURES2 = [
|
|
2955
|
+
{
|
|
2956
|
+
feature: "results.level",
|
|
2957
|
+
needs: ["results:competitions.feis_level"],
|
|
2958
|
+
since: 215,
|
|
2959
|
+
calls: "results.query, majors.results"
|
|
2960
|
+
},
|
|
2961
|
+
{
|
|
2962
|
+
feature: "majors.order",
|
|
2963
|
+
needs: ["majors:competitions.seq"],
|
|
2964
|
+
since: 260,
|
|
2965
|
+
calls: "majors.competitions"
|
|
2966
|
+
},
|
|
2967
|
+
{
|
|
2968
|
+
feature: "dancers",
|
|
2969
|
+
needs: ["results:results.name_order_resolved"],
|
|
2970
|
+
since: 260,
|
|
2971
|
+
calls: "dancers.resolve, dancers.search, dancers.get (the dancers index)"
|
|
2972
|
+
},
|
|
2973
|
+
{
|
|
2974
|
+
feature: "schools",
|
|
2975
|
+
needs: ["school-registry.jsonl", "school-redirects.jsonl"],
|
|
2976
|
+
since: 260,
|
|
2977
|
+
calls: "schools.resolve, a dancer question with a school (the schools index)"
|
|
2978
|
+
},
|
|
2979
|
+
{
|
|
2980
|
+
feature: "feises",
|
|
2981
|
+
needs: [
|
|
2982
|
+
"results:feises.date_lo",
|
|
2983
|
+
"results:feises.date_hi",
|
|
2984
|
+
"results:feises.feis_instance_id",
|
|
2985
|
+
"results:feises.feis_brand_id",
|
|
2986
|
+
"results:competitions.feis_region",
|
|
2987
|
+
"feis-redirects.jsonl"
|
|
2988
|
+
],
|
|
2989
|
+
since: 265,
|
|
2990
|
+
calls: "feises.find, feises.series, and comps and registrations through them (the feises index)"
|
|
2991
|
+
},
|
|
2992
|
+
{
|
|
2993
|
+
feature: "comps",
|
|
2994
|
+
needs: [
|
|
2995
|
+
"results:competitions.feis_level",
|
|
2996
|
+
"results:competitions.round",
|
|
2997
|
+
"results:feises.feis_brand_id"
|
|
2998
|
+
],
|
|
2999
|
+
since: 265,
|
|
3000
|
+
calls: "comps.resolve, comps.syllabus, comps.syllabusFor, registrations.resolve (the comps index)"
|
|
3001
|
+
},
|
|
3002
|
+
{
|
|
3003
|
+
feature: "comps.dance",
|
|
3004
|
+
needs: ["results:competitions.dance"],
|
|
3005
|
+
since: 270,
|
|
3006
|
+
calls: "the `dance` a comp of comps.resolve, comps.syllabus and comps.syllabusFor carries: null, never refused, on a release without it"
|
|
3007
|
+
},
|
|
3008
|
+
{
|
|
3009
|
+
feature: "schools.lineage",
|
|
3010
|
+
needs: ["school-lineage.jsonl"],
|
|
3011
|
+
since: 184,
|
|
3012
|
+
calls: "the `lineage` school evidence of dancers.resolve and dancers.search: no school is joined by lineage, never refused, on a release without it"
|
|
3013
|
+
},
|
|
3014
|
+
{
|
|
3015
|
+
feature: "results.stream",
|
|
3016
|
+
needs: [
|
|
3017
|
+
"results:competitions.round",
|
|
3018
|
+
"results:competitions.feis_level",
|
|
3019
|
+
"results:competitions.level_printed",
|
|
3020
|
+
"results:competitions.age_printed",
|
|
3021
|
+
"results:results.yob_printed",
|
|
3022
|
+
"results:results.primary_source",
|
|
3023
|
+
"results:results.platform_dancer_id",
|
|
3024
|
+
"results:results.relative_place",
|
|
3025
|
+
"results:feises.date_lo",
|
|
3026
|
+
"results:feises.date_hi"
|
|
3027
|
+
],
|
|
3028
|
+
since: 255,
|
|
3029
|
+
calls: "results.stream, results.entries (scopes transcribed, platform, all)"
|
|
3030
|
+
},
|
|
3031
|
+
{
|
|
3032
|
+
feature: "majors.stream",
|
|
3033
|
+
needs: [
|
|
3034
|
+
"majors:competitions.round",
|
|
3035
|
+
"majors:competitions.feis_level",
|
|
3036
|
+
"majors:results.yob_printed",
|
|
3037
|
+
"majors:results.primary_source",
|
|
3038
|
+
"majors:results.relative_place",
|
|
3039
|
+
"majors:feises.date_lo"
|
|
3040
|
+
],
|
|
3041
|
+
since: 255,
|
|
3042
|
+
calls: "results.stream, results.entries (scope majors)"
|
|
3043
|
+
},
|
|
3044
|
+
{
|
|
3045
|
+
feature: "majors.tabs",
|
|
3046
|
+
needs: ["majors:tabs.address", "majors:tabs.true_round"],
|
|
3047
|
+
since: 215,
|
|
3048
|
+
calls: "majors.tabulated"
|
|
3049
|
+
},
|
|
3050
|
+
{
|
|
3051
|
+
feature: "feises.dates",
|
|
3052
|
+
needs: ["results:feises.feis_id", "results:feises.date_lo", "results:feises.date_hi"],
|
|
3053
|
+
since: 245,
|
|
3054
|
+
calls: "feises.dates"
|
|
3055
|
+
},
|
|
3056
|
+
{
|
|
3057
|
+
feature: "dancers.records",
|
|
3058
|
+
needs: [
|
|
3059
|
+
"results:dancers.display",
|
|
3060
|
+
"results:dancers.appearances",
|
|
3061
|
+
"results:dancers.birth_lo",
|
|
3062
|
+
"results:dancers.birth_hi",
|
|
3063
|
+
"results:dancers.birth_source",
|
|
3064
|
+
"results:dancers.birth_conflict",
|
|
3065
|
+
"results:dancers.genders"
|
|
3066
|
+
],
|
|
3067
|
+
since: 215,
|
|
3068
|
+
calls: "dancers.records"
|
|
3069
|
+
},
|
|
3070
|
+
{
|
|
3071
|
+
feature: "dancers.years",
|
|
3072
|
+
needs: [
|
|
3073
|
+
"results:dancer_years.region",
|
|
3074
|
+
"results:dancer_years.region_source",
|
|
3075
|
+
"results:dancer_years.nationals",
|
|
3076
|
+
"results:dancer_years.nationals_source",
|
|
3077
|
+
"results:dancer_years.geohash"
|
|
3078
|
+
],
|
|
3079
|
+
since: 245,
|
|
3080
|
+
calls: "dancers.years"
|
|
3081
|
+
},
|
|
3082
|
+
{
|
|
3083
|
+
feature: "teams",
|
|
3084
|
+
needs: [
|
|
3085
|
+
...[
|
|
3086
|
+
"slug",
|
|
3087
|
+
"address",
|
|
3088
|
+
"event",
|
|
3089
|
+
"year",
|
|
3090
|
+
"card",
|
|
3091
|
+
"comp_no",
|
|
3092
|
+
"title",
|
|
3093
|
+
"category",
|
|
3094
|
+
"hands",
|
|
3095
|
+
"letter",
|
|
3096
|
+
"label",
|
|
3097
|
+
"name_printed",
|
|
3098
|
+
"school_printed",
|
|
3099
|
+
"school_loc",
|
|
3100
|
+
"school_id",
|
|
3101
|
+
"place",
|
|
3102
|
+
"place_printed",
|
|
3103
|
+
"tie",
|
|
3104
|
+
"competitors",
|
|
3105
|
+
"atts"
|
|
3106
|
+
].map((c) => `table:team_results.${c}`),
|
|
3107
|
+
...[
|
|
3108
|
+
"team",
|
|
3109
|
+
"kind",
|
|
3110
|
+
"event_year",
|
|
3111
|
+
"year",
|
|
3112
|
+
"comp_no",
|
|
3113
|
+
"title",
|
|
3114
|
+
"team_index",
|
|
3115
|
+
"seq",
|
|
3116
|
+
"school_id",
|
|
3117
|
+
"school_print_materialized"
|
|
3118
|
+
].map((c) => `table:roster_teams.${c}`),
|
|
3119
|
+
...["team", "seq", "name_printed", "name", "yob", "dancer_id"].map((c) => `table:roster_members.${c}`),
|
|
3120
|
+
...[
|
|
3121
|
+
"event_year",
|
|
3122
|
+
"comp_no",
|
|
3123
|
+
"team",
|
|
3124
|
+
"result_address",
|
|
3125
|
+
"school_id",
|
|
3126
|
+
"join_kind",
|
|
3127
|
+
"via_split",
|
|
3128
|
+
"roster_teams",
|
|
3129
|
+
"result_teams",
|
|
3130
|
+
"roster_index",
|
|
3131
|
+
"result_letter"
|
|
3132
|
+
].map((c) => `table:team_result_rosters.${c}`),
|
|
3133
|
+
"table:event_years.event_year",
|
|
3134
|
+
"table:event_years.event",
|
|
3135
|
+
"table:event_years.year"
|
|
3136
|
+
],
|
|
3137
|
+
since: 260,
|
|
3138
|
+
calls: "teams.results, teams.rosters, teams.resultRosters (the teams index)"
|
|
3139
|
+
},
|
|
3140
|
+
{
|
|
3141
|
+
feature: "teams.fields",
|
|
3142
|
+
needs: [
|
|
3143
|
+
"table:team_results.age",
|
|
3144
|
+
"table:team_results.gender",
|
|
3145
|
+
"table:team_results.printed_status",
|
|
3146
|
+
"table:roster_teams.dance",
|
|
3147
|
+
"table:roster_teams.school_print_as_of",
|
|
3148
|
+
"table:roster_members.gender"
|
|
3149
|
+
],
|
|
3150
|
+
since: 266,
|
|
3151
|
+
calls: "the fields teams.results (age, gender, printed_status) and teams.rosters (dance, school_printed_as_of, a member's gender) carry where present: absent, never refused, on a release without them"
|
|
3152
|
+
},
|
|
3153
|
+
{
|
|
3154
|
+
feature: "events",
|
|
3155
|
+
needs: [
|
|
3156
|
+
"event_year",
|
|
3157
|
+
"event",
|
|
3158
|
+
"year",
|
|
3159
|
+
"date_lo",
|
|
3160
|
+
"date_hi",
|
|
3161
|
+
"resolution",
|
|
3162
|
+
"source",
|
|
3163
|
+
"date_printed",
|
|
3164
|
+
"place",
|
|
3165
|
+
"place_printed"
|
|
3166
|
+
].map((c) => `table:event_years.${c}`),
|
|
3167
|
+
since: 260,
|
|
3168
|
+
calls: "events.years (the events index)"
|
|
3169
|
+
},
|
|
3170
|
+
{
|
|
3171
|
+
feature: "staff",
|
|
3172
|
+
needs: [
|
|
3173
|
+
"results:adjudicators",
|
|
3174
|
+
"results:feis_panels",
|
|
3175
|
+
"school-staff.jsonl",
|
|
3176
|
+
"school-teachers.jsonl",
|
|
3177
|
+
"school-registry.jsonl"
|
|
3178
|
+
],
|
|
3179
|
+
since: 260,
|
|
3180
|
+
calls: "staff.teacher, staff.adjudicator (the staff index)"
|
|
3181
|
+
}
|
|
3182
|
+
];
|
|
3183
|
+
async function hasNeed(probe, n) {
|
|
3184
|
+
const t = /^table:([A-Za-z_]+)(?:\.([A-Za-z_]+))?$/.exec(n);
|
|
3185
|
+
if (t !== null) {
|
|
3186
|
+
if (probe.tableColumns === undefined)
|
|
3187
|
+
return false;
|
|
3188
|
+
const cols = await probe.tableColumns(t[1]);
|
|
3189
|
+
return t[2] === undefined ? cols.size > 0 : cols.has(t[2]);
|
|
3190
|
+
}
|
|
3191
|
+
const m = /^(results|majors):([A-Za-z_]+)(?:\.([A-Za-z_]+))?$/.exec(n);
|
|
3192
|
+
if (m === null)
|
|
3193
|
+
return probe.publishes(n);
|
|
3194
|
+
const cols = columnsOf2(await probe.db(m[1]), m[2]);
|
|
3195
|
+
return m[3] === undefined ? cols.size > 0 : cols.has(m[3]);
|
|
3196
|
+
}
|
|
3197
|
+
async function capabilityOf2(probe, f) {
|
|
3198
|
+
const missing = [];
|
|
3199
|
+
for (const n of f.needs)
|
|
3200
|
+
if (!await hasNeed(probe, n))
|
|
3201
|
+
missing.push(n);
|
|
3202
|
+
return { ...f, state: missing.length === 0 ? "full" : "missing", missing };
|
|
3203
|
+
}
|
|
3204
|
+
function needsRelease2(tag, c) {
|
|
3205
|
+
return new Error(`archive-client: ${tag} is too old for ${c.feature} (${c.calls}): it lacks ${c.missing.join(", ")}. Open a release of cut ${c.since} or later`);
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3025
3208
|
function tagDir(root, tag) {
|
|
3026
3209
|
if (parseArchiveTag2(tag) === null)
|
|
3027
3210
|
throw new Error(`archive-client: "${tag}" is not a release tag (archive-YYYY.MM.DD[-N[.k]])`);
|
|
@@ -3540,4 +3723,4 @@ function releases2(opts = {}) {
|
|
|
3540
3723
|
return new ReleaseManager2(opts);
|
|
3541
3724
|
}
|
|
3542
3725
|
|
|
3543
|
-
export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, NO_CALENDAR2, FeisFinder2,
|
|
3726
|
+
export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, NO_CALENDAR2, FeisFinder2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, initialsOnly2, 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 };
|