@finbheara/archive-client 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -249,6 +249,10 @@ A FEIS-LOCAL ID (`FEIS_LOCAL_ID_FLOOR`, 2^34, and up) is valid in its release on
249
249
  carrying one say `scope: "per-release"` (`dancer_scope` on a row), so a caller never stores one
250
250
  across releases. Every answer carries the release tag.
251
251
 
252
+ A MAJORS ID (under the floor) is kept across releases on a best-effort basis only: a merge
253
+ retires one, a split mints one, and a renumbered source page can hand one to another dancer. A
254
+ caller that joins ids across releases checks the name and the rows it expects.
255
+
252
256
  ## Results and the majors
253
257
 
254
258
  ```ts
@@ -284,6 +288,8 @@ c.source; // "syllabus" | "prior-year" | "title"
284
288
  c.kind; // "championship" | "grade" | "unrated"
285
289
  c.level; // "OC" (FF, BG, AB, NV, PW, PC, OC), c.feis_level 700
286
290
  c.age; // "U10", c.population "mixed", c.canonical "710OC", c.title, c.competition
291
+ c.age_source; // "printed" | "title" | "code" | "merged" | null, c.age_printed "U10" as printed
292
+ c.second; // true for a second chance or a supplementary comp beside the main one
287
293
  c.from; // { feis_ids: ["fweb:ABC25"], year: 2025 } where an earlier year answered
288
294
  c.check; // "unchecked", or what the caller's `title` says about the earlier comp
289
295
  c.syllabus_source; // "results": what the syllabus the answer read is read from
@@ -326,6 +332,21 @@ QuickFeis numbers its own and-over championships (`15 & over` prelim `616PC`, `1
326
332
  `719OC`). An and-over code is then the same string as an `Under N + 1` one, and `age` (`O18`)
327
333
  tells them apart. An adult band, a range and a grade render none.
328
334
 
335
+ `age` is the release's reading, and `age_source` says where it was read. A `merged` comp (a
336
+ platform's comps merged into one, `710OCM` with no `710OC` beside it) prints one member's band,
337
+ so its `age` is the union of its members' (`U12` for `710OCM - U10 Open Champion` where the next
338
+ open the feis prints is `713OCM`), and `age_printed` keeps the platform's own field (`U10`). A
339
+ joined code at the bottom of its series stays open below (`710OC/711OC` with no open under 10
340
+ beside it is `U11/11yr`, every age under 11), as a younger dancer dances up into it.
341
+
342
+ `population` is the sex the release states, else a population word in the title, else the
343
+ code's sex suffix read with every code its feis printed (`codePopulation` from
344
+ `@finbheara/domain/title`: `720OCB` beside `720OCG` is boys and girls, `512M` beside `512` is
345
+ boys, a lone `713OCM` says nothing), else the release's `mixed` for a grade or a preliminary
346
+ championship. `second` is read from the words of the title and the platform's level and dance
347
+ fields (`secondIn`: `2nd Chance`, `2nd Chc`, `Second Chance`, `Supplementary Prelim`, `2nd Open
348
+ Champ`), and never from a code's digit, which marks a round's half at some hosts (`610PC2H`).
349
+
329
350
  ## Registrations
330
351
 
331
352
  ```ts
@@ -655,9 +655,11 @@ function ageEvidence(d, asked) {
655
655
 
656
656
  import { Age as Age2 } from "@finbheara/domain/age";
657
657
  import {
658
+ codePopulation,
658
659
  compTitle,
659
660
  matchTitle,
660
661
  populationIn as populationIn2,
662
+ secondIn,
661
663
  titleRelation
662
664
  } from "@finbheara/domain/title";
663
665
  var LEVEL = Object.freeze({
@@ -674,17 +676,25 @@ CREATE TABLE comp(
674
676
  competition TEXT PRIMARY KEY, feis_id TEXT NOT NULL, brand_id TEXT, year INTEGER,
675
677
  code TEXT NOT NULL, code_fold TEXT NOT NULL, title TEXT, age TEXT, gender TEXT,
676
678
  feis_level INTEGER, round TEXT, platform TEXT NOT NULL, captures INTEGER NOT NULL,
677
- file TEXT NOT NULL, line INTEGER NOT NULL
679
+ file TEXT NOT NULL, line INTEGER NOT NULL, age_source TEXT, age_printed TEXT,
680
+ gender_source TEXT, level_printed TEXT, dance_printed TEXT
678
681
  );
679
682
  `;
680
683
  var INDEXES = [
681
684
  "CREATE INDEX comp_feis_code ON comp(feis_id, code_fold)",
682
685
  "CREATE INDEX comp_brand_year ON comp(brand_id, year)"
683
686
  ];
687
+ var OPTIONAL = [
688
+ "age_source",
689
+ "age_printed",
690
+ "gender_source",
691
+ "level_printed",
692
+ "dance_printed"
693
+ ];
684
694
  var fold = (code) => code.trim().toLowerCase();
685
695
  var compsIndex = {
686
696
  name: "comps",
687
- version: "1",
697
+ version: "2",
688
698
  inputs: ["results"],
689
699
  says: "every competition by its feis and its own code, with its series and year",
690
700
  build(rel, out) {
@@ -697,19 +707,23 @@ var compsIndex = {
697
707
  const captures = new Map;
698
708
  for (const r of src.query("SELECT competition, count(*) AS n FROM competitions GROUP BY competition").all())
699
709
  captures.set(r.competition, r.n);
700
- const put = out.prepare("INSERT OR IGNORE INTO comp VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
701
- for (const c of src.query("SELECT competition, event, year, comp_no, title, age, gender, feis_level, round, source, file, line FROM competitions WHERE comp_no IS NOT NULL AND trim(comp_no) != '' ORDER BY file, line").iterate()) {
710
+ const put = out.prepare("INSERT OR IGNORE INTO comp VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
711
+ const has = new Set(src.query("PRAGMA table_info(competitions)").all().map((r) => r.name));
712
+ const opt = (col) => has.has(col) ? col : `NULL AS ${col}`;
713
+ for (const c of src.query(`SELECT competition, event, year, comp_no, title, age, gender, feis_level, round, source, file, line, ${OPTIONAL.map(opt).join(", ")} FROM competitions WHERE comp_no IS NOT NULL AND trim(comp_no) != '' ORDER BY file, line`).iterate()) {
702
714
  const f = brand.get(c.event);
703
- 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);
715
+ 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);
704
716
  }
705
717
  for (const s of INDEXES)
706
718
  out.run(s);
707
719
  }
708
720
  };
709
- function compOf(r) {
721
+ function compOf(r, codes = []) {
710
722
  const lv = r.feis_level;
711
723
  const kind = lv === null ? "unrated" : lv >= 600 ? "championship" : "grade";
712
- const pop = populationIn2(r.gender ?? "") || populationIn2(r.title ?? "");
724
+ const stated = r.gender_source === "level" ? "" : populationIn2(r.gender ?? "");
725
+ const pop = stated || populationIn2(r.title ?? "") || (r.platform === "ifeis" && codes.length > 0 ? codePopulation(r.code, codes) : "") || populationIn2(r.gender ?? "");
726
+ const second = [r.title, r.level_printed, r.dance_printed].some((t) => t != null && secondIn(t));
713
727
  const round = r.round ?? (r.title === null ? null : compTitle(r.title).round?.toString() ?? null);
714
728
  return {
715
729
  competition: r.competition,
@@ -721,7 +735,10 @@ function compOf(r) {
721
735
  feis_level: lv,
722
736
  level: lv === null ? null : LEVEL[lv] ?? null,
723
737
  age: r.age,
738
+ age_source: r.age_source ?? null,
739
+ age_printed: r.age_printed ?? null,
724
740
  population: pop === "" ? null : pop,
741
+ second,
725
742
  round,
726
743
  canonical: canonicalOf2(lv, r.age),
727
744
  platform: r.platform,
@@ -828,7 +845,12 @@ class CompResolver2 {
828
845
  const marks = feisIds.map(() => "?").join(", ");
829
846
  const sql = `SELECT * FROM comp WHERE feis_id IN (${marks})${code !== undefined ? " AND code_fold = ?" : ""} ORDER BY file, line`;
830
847
  const args = code !== undefined ? [...feisIds, fold(code)] : [...feisIds];
831
- return db.query(sql).all(...args).map(compOf);
848
+ const rows = db.query(sql).all(...args);
849
+ const all = code === undefined ? rows : db.query(`SELECT feis_id, code FROM comp WHERE feis_id IN (${marks})`).all(...feisIds);
850
+ const codes = new Map;
851
+ for (const r of all)
852
+ codes.set(r.feis_id, [...codes.get(r.feis_id) ?? [], r.code]);
853
+ return rows.map((r) => compOf(r, codes.get(r.feis_id) ?? []));
832
854
  }
833
855
  async idsOf(brand, year) {
834
856
  return (await this.ofSeries("", brand, year, "series")).feis_ids;
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  nameOrderOf,
5
5
  openRelease2,
6
6
  releases2
7
- } from "./chunks/index-6e2gb6sk.js";
7
+ } from "./chunks/index-kp811fb0.js";
8
8
  import"./chunks/index-hne1wadw.js";
9
9
  import"./chunks/index-hghdy980.js";
10
10
 
@@ -690,7 +690,7 @@ async function main(argv, io = {}) {
690
690
  out(a, () => {
691
691
  say(`${a.feis.asked} ${a.year ?? "?"} ${a.held ? `held: ${a.feis.feis_ids.join(", ")}` : "not held"} series ${a.feis.brand_id ?? "-"} (years ${a.years.join(", ") || "-"}) in ${a.tag}`);
692
692
  for (const c of a.entries)
693
- say(` ${c.code} ${c.kind} ${c.level ?? "-"} ${c.age ?? "-"} ${c.population ?? "-"} "${c.title ?? ""}"`);
693
+ say(` ${c.code} ${c.kind} ${c.level ?? "-"} ${c.age ?? "-"} ${c.population ?? "-"}${c.second ? " second" : ""} "${c.title ?? ""}"`);
694
694
  say(`${a.entries.length} comp(s)`);
695
695
  });
696
696
  rel.close();
package/dist/index.js CHANGED
@@ -41,7 +41,7 @@ import {
41
41
  openCached2,
42
42
  ReleaseManager2,
43
43
  releases2
44
- } from "./chunks/index-6e2gb6sk.js";
44
+ } from "./chunks/index-kp811fb0.js";
45
45
  import {
46
46
  cacheRoot2,
47
47
  repoOf2
@@ -15,7 +15,10 @@ export interface Comp {
15
15
  feis_level: number | null;
16
16
  level: string | null;
17
17
  age: string | null;
18
+ age_source: string | null;
19
+ age_printed: string | null;
18
20
  population: Population | null;
21
+ second: boolean;
19
22
  round: string | null;
20
23
  canonical: string | null;
21
24
  platform: string;
@@ -107,8 +110,13 @@ interface CompRow {
107
110
  round: string | null;
108
111
  platform: string;
109
112
  captures: number;
113
+ age_source?: string | null;
114
+ age_printed?: string | null;
115
+ gender_source?: string | null;
116
+ level_printed?: string | null;
117
+ dance_printed?: string | null;
110
118
  }
111
- export declare function compOf(r: CompRow): Comp;
119
+ export declare function compOf(r: CompRow, codes?: readonly string[]): Comp;
112
120
  export declare function canonicalOf(level: number | null, age: string | null): string | null;
113
121
  export declare class CompResolver {
114
122
  private readonly rel;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finbheara/archive-client",
3
- "version": "0.2.0",
3
+ "version": "0.3.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",
@@ -44,7 +44,7 @@
44
44
  "prepublishOnly": "bun scripts/build.ts && bun ../../tools/checks/archive-client-tarball.ts"
45
45
  },
46
46
  "dependencies": {
47
- "@finbheara/domain": "0.48.0",
47
+ "@finbheara/domain": "0.49.0",
48
48
  "@finbheara/names": "0.22.0"
49
49
  },
50
50
  "devDependencies": {