hiski-mcp 0.1.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.
@@ -0,0 +1,162 @@
1
+ import * as cheerio from "cheerio";
2
+ import seurakuntaData from "./data/seurakunnat.json" with { type: "json" };
3
+ /** Parses Hiski year coverage text such as "1848, 1854-1856" into ranges. */
4
+ export function parseVuosivalit(teksti) {
5
+ return teksti
6
+ .split(",")
7
+ .map((osa) => osa.replace(/\s+/g, ""))
8
+ .filter((osa) => osa.length > 0)
9
+ .map((osa) => {
10
+ const [alku, loppu = alku] = osa.split("-").map(Number);
11
+ return { alku: alku, loppu: loppu };
12
+ });
13
+ }
14
+ export const KIRJAT = ["kastetut", "vihityt", "haudatut", "smuutt", "umuutt"];
15
+ /** All Hiski parishes, generated by scripts/paivita-seurakunnat.ts. */
16
+ export const SEURAKUNNAT = seurakuntaData;
17
+ const KOODI_LINKISTA = /^\/hiski\?fi\+(\d{4})$/;
18
+ /**
19
+ * Parses the book list Hiski returns when every parish is selected at once.
20
+ * Each row holds the full parish name and the indexed years per book.
21
+ */
22
+ export function parseSeurakuntaluettelo(html) {
23
+ const $ = cheerio.load(html);
24
+ const tulos = [];
25
+ $("tr").each((_, rivi) => {
26
+ const solut = $(rivi).children("td");
27
+ const linkki = solut.first().find("a");
28
+ const koodi = KOODI_LINKISTA.exec(linkki.attr("href") ?? "")?.[1];
29
+ if (!koodi)
30
+ return;
31
+ const koko_nimi = linkki.text().trim();
32
+ const erotin = koko_nimi.indexOf(" - ");
33
+ const vuodet = Object.fromEntries(KIRJAT.map((kirja, i) => [kirja, parseVuosivalit(solut.eq(i + 1).text())]));
34
+ tulos.push({
35
+ koodi,
36
+ nimi: erotin >= 0 ? koko_nimi.slice(0, erotin) : koko_nimi,
37
+ ...(erotin >= 0 && { rinnakkaisnimi: koko_nimi.slice(erotin + 3) }),
38
+ koko_nimi,
39
+ vuodet,
40
+ });
41
+ });
42
+ return tulos;
43
+ }
44
+ /** Lowercases, strips diacritics (ä→a, ö→o, å→a) and collapses whitespace. */
45
+ export function normalisoi(s) {
46
+ return s.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase().replace(/\s+/g, " ").trim();
47
+ }
48
+ /** How well a parish matches a query; lower is better. */
49
+ var Taso;
50
+ (function (Taso) {
51
+ Taso[Taso["Koodi"] = 0] = "Koodi";
52
+ Taso[Taso["Tasmaa"] = 1] = "Tasmaa";
53
+ Taso[Taso["Alkaa"] = 2] = "Alkaa";
54
+ Taso[Taso["SananAlku"] = 3] = "SananAlku";
55
+ Taso[Taso["Sisaltaa"] = 4] = "Sisaltaa";
56
+ Taso[Taso["Vartalo"] = 5] = "Vartalo";
57
+ })(Taso || (Taso = {}));
58
+ const sanat = (s) => s.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
59
+ const yhteinenAlku = (a, b) => {
60
+ let i = 0;
61
+ while (i < a.length && i < b.length && a[i] === b[i])
62
+ i++;
63
+ return i;
64
+ };
65
+ /**
66
+ * Finnish parish names are often in the genitive ("Turun", "Kristiinankaupungin"),
67
+ * so a query word matches a name word when they share most of their beginning.
68
+ */
69
+ const vartaloTasmaa = (hakusana, nimisana) => yhteinenAlku(hakusana, nimisana) >= Math.max(3, hakusana.length - 3);
70
+ /** Smaller when name words share a longer beginning and a similar length. */
71
+ function vartaloEtaisyys(hakusanat, nimisanat) {
72
+ let summa = 0;
73
+ for (const h of hakusanat) {
74
+ summa += Math.min(...nimisanat.map((n) => h.length - yhteinenAlku(h, n) + Math.abs(h.length - n.length)));
75
+ }
76
+ return summa;
77
+ }
78
+ function luokittele(haku, seurakunta) {
79
+ const taso = (t) => ({ taso: t, etaisyys: 0 });
80
+ if (/^\d{1,4}$/.test(haku)) {
81
+ return haku.padStart(4, "0") === seurakunta.koodi ? taso(Taso.Koodi) : undefined;
82
+ }
83
+ const nimet = [seurakunta.nimi, seurakunta.rinnakkaisnimi, seurakunta.koko_nimi]
84
+ .filter((n) => n !== undefined)
85
+ .map(normalisoi);
86
+ if (nimet.some((n) => n === haku))
87
+ return taso(Taso.Tasmaa);
88
+ if (nimet.some((n) => n.startsWith(haku)))
89
+ return taso(Taso.Alkaa);
90
+ const nimisanat = sanat(nimet.join(" "));
91
+ const hakusanat = sanat(haku);
92
+ if (hakusanat.length === 0)
93
+ return undefined;
94
+ if (hakusanat.every((h) => nimisanat.some((n) => n.startsWith(h))))
95
+ return taso(Taso.SananAlku);
96
+ if (nimet.some((n) => n.includes(haku)))
97
+ return taso(Taso.Sisaltaa);
98
+ if (hakusanat.every((h) => nimisanat.some((n) => vartaloTasmaa(h, n)))) {
99
+ return { taso: Taso.Vartalo, etaisyys: vartaloEtaisyys(hakusanat, nimisanat) };
100
+ }
101
+ return undefined;
102
+ }
103
+ function etsiOsumat(haku, lista) {
104
+ const normalisoitu = normalisoi(haku);
105
+ if (!normalisoitu)
106
+ return [];
107
+ const osumat = lista.flatMap((seurakunta) => {
108
+ const luokitus = luokittele(normalisoitu, seurakunta);
109
+ return luokitus ? [{ seurakunta, ...luokitus }] : [];
110
+ });
111
+ // Loose stem matches are only useful when nothing better was found.
112
+ const parempia = osumat.some((o) => o.taso < Taso.Vartalo);
113
+ return osumat
114
+ .filter((o) => !parempia || o.taso < Taso.Vartalo)
115
+ .sort((a, b) => a.taso - b.taso ||
116
+ a.etaisyys - b.etaisyys ||
117
+ a.seurakunta.nimi.localeCompare(b.seurakunta.nimi, "fi"));
118
+ }
119
+ /** Finds parishes by code, name or parallel name, best matches first. */
120
+ export function etsiSeurakunta(haku, lista = SEURAKUNNAT) {
121
+ return etsiOsumat(haku, lista).map((o) => o.seurakunta);
122
+ }
123
+ const MAX_EHDOKKAITA = 10;
124
+ /** A parish name that did not resolve to exactly one parish. */
125
+ export class SeurakuntaVirhe extends Error {
126
+ syote;
127
+ ehdokkaat;
128
+ constructor(syote, ehdokkaat) {
129
+ super(ehdokkaat.length === 0
130
+ ? `Seurakuntaa "${syote}" ei löytynyt.`
131
+ : `Seurakunta "${syote}" ei ole yksiselitteinen. Ehdokkaat: ${ehdokkaat
132
+ .map((s) => `${s.koodi} ${s.koko_nimi}`)
133
+ .join("; ")}`);
134
+ this.syote = syote;
135
+ this.ehdokkaat = ehdokkaat;
136
+ this.name = "SeurakuntaVirhe";
137
+ }
138
+ }
139
+ function ratkaiseYksi(syote) {
140
+ const osumat = etsiOsumat(syote, SEURAKUNNAT);
141
+ const tarkat = osumat.filter((o) => o.taso <= Taso.Tasmaa);
142
+ if (tarkat.length === 1)
143
+ return tarkat[0].seurakunta;
144
+ // A lone stem match is only a guess, so it is offered as a candidate instead.
145
+ if (tarkat.length === 0 && osumat.length === 1 && osumat[0].taso < Taso.Vartalo) {
146
+ return osumat[0].seurakunta;
147
+ }
148
+ throw new SeurakuntaVirhe(syote, osumat.slice(0, MAX_EHDOKKAITA).map((o) => o.seurakunta));
149
+ }
150
+ /**
151
+ * Resolves parish codes or names to parishes, dropping duplicates.
152
+ * Throws SeurakuntaVirhe for an unknown or ambiguous entry.
153
+ */
154
+ export function ratkaiseSeurakunnat(syotteet) {
155
+ const tulos = new Map();
156
+ for (const syote of syotteet) {
157
+ const seurakunta = ratkaiseYksi(syote);
158
+ tulos.set(seurakunta.koodi, seurakunta);
159
+ }
160
+ return [...tulos.values()];
161
+ }
162
+ //# sourceMappingURL=parishes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parishes.js","sourceRoot":"","sources":["../src/parishes.ts"],"names":[],"mappings":"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"}
package/dist/server.js ADDED
@@ -0,0 +1,39 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { HiskiClient } from "./client.js";
3
+ import { virheViesti } from "./muotoilu.js";
4
+ import { OHJEET } from "./ohjeet.js";
5
+ import { TYOKALUT } from "./tyokalut.js";
6
+ import { SERVER_NAME, SERVER_VERSION } from "./version.js";
7
+ /** Creates the Hiski MCP server; the client is injectable for tests. */
8
+ export function luoPalvelin(hiski = new HiskiClient()) {
9
+ const palvelin = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, { instructions: OHJEET });
10
+ for (const tyokalu of TYOKALUT)
11
+ rekisteroi(palvelin, tyokalu, hiski);
12
+ return palvelin;
13
+ }
14
+ function rekisteroi(palvelin, tyokalu, hiski) {
15
+ palvelin.registerTool(tyokalu.nimi, {
16
+ title: tyokalu.otsikko,
17
+ description: tyokalu.kuvaus,
18
+ inputSchema: tyokalu.skeema,
19
+ annotations: {
20
+ title: tyokalu.otsikko,
21
+ readOnlyHint: true,
22
+ destructiveHint: false,
23
+ idempotentHint: true,
24
+ openWorldHint: tyokalu.verkko,
25
+ },
26
+ }, async (args, extra) => {
27
+ try {
28
+ const vastaus = await tyokalu.kasittele(args, { hiski, signal: extra.signal });
29
+ return { content: [{ type: "text", text: JSON.stringify(vastaus) }] };
30
+ }
31
+ catch (virhe) {
32
+ return {
33
+ isError: true,
34
+ content: [{ type: "text", text: virheViesti(virhe) }],
35
+ };
36
+ }
37
+ });
38
+ }
39
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAgB,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3D,wEAAwE;AACxE,MAAM,UAAU,WAAW,CAAC,QAAqB,IAAI,WAAW,EAAE;IAChE,MAAM,QAAQ,GAAG,IAAI,SAAS,CAC5B,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,EAC9C,EAAE,YAAY,EAAE,MAAM,EAAE,CACzB,CAAC;IACF,KAAK,MAAM,OAAO,IAAI,QAAQ;QAAE,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IACrE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,UAAU,CAAC,QAAmB,EAAE,OAAgB,EAAE,KAAkB;IAC3E,QAAQ,CAAC,YAAY,CACnB,OAAO,CAAC,IAAI,EACZ;QACE,KAAK,EAAE,OAAO,CAAC,OAAO;QACtB,WAAW,EAAE,OAAO,CAAC,MAAM;QAC3B,WAAW,EAAE,OAAO,CAAC,MAAM;QAC3B,WAAW,EAAE;YACX,KAAK,EAAE,OAAO,CAAC,OAAO;YACtB,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,OAAO,CAAC,MAAM;SAC9B;KACF,EACD,KAAK,EAAE,IAAI,EAAE,KAAK,EAA2B,EAAE;QAC7C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;YAC/E,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QACxE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;aACtD,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,125 @@
1
+ import * as cheerio from "cheerio";
2
+ import { HISKI_URL } from "./hakutulos.js";
3
+ import { parseVuosivalit } from "./parishes.js";
4
+ import { siisti } from "./teksti.js";
5
+ const EI_LOYTYNYT = "Seurakuntaa ei löytynyt";
6
+ const KIRJALINKKI = /^\/hiski\?fi\+(\d{4})\+([a-z]+)$/;
7
+ const SEURAKUNTALINKKI = /^\/hiski\?fi\+(\d{4})$/;
8
+ const HAETTAVAT_KIRJAT = new Set(["kastetut", "vihityt", "haudatut", "smuutt", "umuutt", "kaikki"]);
9
+ const LISATIEDOT = "Lisätietoja seurakunnasta";
10
+ /** The maps that end the parish page. */
11
+ const KARTAT = /<TABLE WIDTH="100%">/i;
12
+ /** Parses a Hiski parish page (/hiski?fi+SRK). */
13
+ export function parseSeurakuntasivu(html) {
14
+ if (html.includes(EI_LOYTYNYT))
15
+ return { tila: "ei_loytynyt" };
16
+ const $ = cheerio.load(html);
17
+ const nimi = siisti($("h2").first().text());
18
+ if (!nimi)
19
+ throw new Error("Seurakuntasivua ei tunnistettu");
20
+ let koodi;
21
+ const kirjat = [];
22
+ $("li > a").each((_, a) => {
23
+ const osat = KIRJALINKKI.exec($(a).attr("href") ?? "");
24
+ if (!osat || !HAETTAVAT_KIRJAT.has(osat[2]))
25
+ return;
26
+ koodi ??= osat[1];
27
+ // The years follow the link in the same list item: "Kastetut</A> (1695-1718, 1720-1911)".
28
+ const vuodet = /\(([^)]*)\)/.exec($(a).parent().text())?.[1] ?? "";
29
+ kirjat.push({ kirja: osat[2], vuodet: parseVuosivalit(vuodet) });
30
+ });
31
+ if (!koodi)
32
+ throw new Error("Seurakuntasivua ei tunnistettu");
33
+ // The only links to a single other parish are the neighbours. (The parser
34
+ // moves their list out of its heading's paragraph, so the heading is no anchor.)
35
+ const naapurit = $("a")
36
+ .get()
37
+ .flatMap((a) => {
38
+ const naapuri = SEURAKUNTALINKKI.exec($(a).attr("href") ?? "")?.[1];
39
+ const naapurinNimi = siisti($(a).text());
40
+ return naapuri && naapuri !== koodi && naapurinNimi
41
+ ? [{ koodi: naapuri, nimi: naapurinNimi }]
42
+ : [];
43
+ });
44
+ const lisatiedot = $("a")
45
+ .filter((_, a) => siisti($(a).text()) === LISATIEDOT)
46
+ .attr("href");
47
+ const huomautus = lueHuomautus(html);
48
+ return {
49
+ tila: "ok",
50
+ koodi,
51
+ nimi,
52
+ kirjat,
53
+ naapurit,
54
+ ...(huomautus && { huomautus }),
55
+ ...(lisatiedot && { lisatiedot_url: new URL(lisatiedot, HISKI_URL).href }),
56
+ };
57
+ }
58
+ /**
59
+ * Reads the free text Hiski prints between the info link and the maps, such
60
+ * as the note that ceded Karelia's records are mostly in the Karelia database.
61
+ */
62
+ function lueHuomautus(html) {
63
+ const alku = html.indexOf(LISATIEDOT);
64
+ if (alku < 0)
65
+ return undefined;
66
+ const loppu = html.slice(alku).search(KARTAT);
67
+ const alue = html.slice(html.indexOf("</A>", alku) + 4, loppu < 0 ? undefined : alku + loppu);
68
+ const $ = cheerio.load(alue, null, false);
69
+ const teksti = siisti($.root().text());
70
+ if (!teksti)
71
+ return undefined;
72
+ const linkit = $("a")
73
+ .get()
74
+ .map((a) => ({
75
+ teksti: siisti($(a).text()) ?? "",
76
+ url: new URL($(a).attr("href") ?? "", HISKI_URL).href,
77
+ }));
78
+ return { teksti, ...(linkit.length > 0 && { linkit }) };
79
+ }
80
+ const OTSIKKO = /<H1>([^<]*)<\/H1>/i;
81
+ /** Text of an HTML fragment, whitespace collapsed. */
82
+ const tekstiksi = (html) => siisti(cheerio.load(html, null, false).root().text());
83
+ /**
84
+ * Parses a parish info page (mini-pgsql.php). The page is loose text between
85
+ * headings, so it is split at <H2> headings; only the sections that help
86
+ * interpret search results are kept (not archive listings or clergy).
87
+ */
88
+ export function parseSeurakuntatiedot(html) {
89
+ const otsikko = OTSIKKO.exec(html);
90
+ if (!otsikko)
91
+ throw new Error("Seurakunnan lisätietosivua ei tunnistettu");
92
+ if (otsikko[1].includes("Ei löytynyt"))
93
+ return { tila: "ei_loytynyt" };
94
+ const [johdanto = "", ...osiot] = html.slice(otsikko.index + otsikko[0].length).split(/<H2>/i);
95
+ // The introduction runs from the province line to the first map image.
96
+ const [maakuntarivi = "", ...kappaleet] = johdanto.split(/<IMG/i)[0].split(/<P>/i);
97
+ const maakunta = /Maakunta:\s*\d*\s*(.*)/.exec(tekstiksi(maakuntarivi) ?? "")?.[1];
98
+ const historia = kappaleet.map(tekstiksi).filter((k) => k !== undefined);
99
+ let kylat = [];
100
+ let vanhatNimet = [];
101
+ for (const osio of osiot) {
102
+ const [nimi = "", sisalto = ""] = osio.split(/<\/H2>/i);
103
+ if (siisti(nimi) === "Kylät") {
104
+ kylat = (tekstiksi(sisalto) ?? "")
105
+ .split(",")
106
+ .map((kyla) => siisti(kyla))
107
+ .filter((kyla) => kyla !== undefined);
108
+ }
109
+ else if (siisti(nimi) === "Vanhat nimet") {
110
+ const $ = cheerio.load(sisalto, null, false);
111
+ vanhatNimet = $("li")
112
+ .get()
113
+ .map((li) => siisti($(li).text()))
114
+ .filter((n) => n !== undefined);
115
+ }
116
+ }
117
+ return {
118
+ tila: "ok",
119
+ ...(maakunta && { maakunta }),
120
+ ...(historia.length > 0 && { historia }),
121
+ ...(kylat.length > 0 && { kylat }),
122
+ ...(vanhatNimet.length > 0 && { vanhat_nimet: vanhatNimet }),
123
+ };
124
+ }
125
+ //# sourceMappingURL=seurakuntasivu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seurakuntasivu.js","sourceRoot":"","sources":["../src/seurakuntasivu.ts"],"names":[],"mappings":"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"}
package/dist/solu.js ADDED
@@ -0,0 +1,18 @@
1
+ import { siisti } from "./teksti.js";
2
+ /**
3
+ * Reads a table cell from a Hiski page: its text without links, with the
4
+ * parenthesised <SMALL> field comments separated out.
5
+ */
6
+ export function jasennaSolu($, solu) {
7
+ const kopio = solu.clone();
8
+ kopio.children("a").remove();
9
+ const kommentit = kopio
10
+ .children("small")
11
+ .remove()
12
+ .map((_, small) => siisti(siisti($(small).text())?.replace(/^\((.*)\)$/, "$1")))
13
+ .get()
14
+ .filter((k) => k !== undefined);
15
+ const arvo = siisti(kopio.text());
16
+ return { ...(arvo && { arvo }), kommentit };
17
+ }
18
+ //# sourceMappingURL=solu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solu.js","sourceRoot":"","sources":["../src/solu.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,CAAa,EAAE,IAAsB;IAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IAC3B,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7B,MAAM,SAAS,GAAG,KAAK;SACpB,QAAQ,CAAC,OAAO,CAAC;SACjB,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC;SAC/E,GAAG,EAAE;SACL,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAClC,OAAO,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,187 @@
1
+ import * as cheerio from "cheerio";
2
+ import { HISKI_URL } from "./hakutulos.js";
3
+ import { jasennaSolu } from "./solu.js";
4
+ import { siisti } from "./teksti.js";
5
+ const VIRHE = "Virhe parametrissa!";
6
+ const HENKILON_KENTAT = ["ammatti", "etunimi", "patronyymi", "sukunimi", "ika"];
7
+ /** Sets the cells' values to the given fields; "mies.kyla" sets a nested field. */
8
+ const arvot = (...kentat) => (tapahtuma, solut) => kentat.forEach((kentta, i) => {
9
+ const { arvo, kommentit } = solut[i] ?? { kommentit: [] };
10
+ if (!arvo && kommentit.length === 0)
11
+ return;
12
+ // Nested objects are only created for data, so an empty person is left out.
13
+ const polku = kentta.split(".");
14
+ const nimi = polku.pop();
15
+ const kohde = polku.reduce((olio, osa) => (olio[osa] ??= {}), tapahtuma);
16
+ if (arvo)
17
+ kohde[nimi] = arvo;
18
+ if (kommentit.length > 0)
19
+ (kohde.kenttakommentit ??= {})[nimi] = kommentit;
20
+ });
21
+ /** Reads a person row: occupation, first name, patronymic, surname and age. */
22
+ const henkilo = (kentta) => (tapahtuma, solut) => arvot(...HENKILON_KENTAT.map((k) => `${kentta}.${k}`))(tapahtuma, solut);
23
+ const IKA = /^(\S+) v\. (\S+) kk (\S+) vko (\S+) pv$/;
24
+ const IKAYKSIKOT = ["vuodet", "kuukaudet", "viikot", "paivat"];
25
+ /** Reads the cause of death and the age "28 v. - kk - vko - pv" ("-" is empty). */
26
+ const kuolinsyyJaIka = (tapahtuma, solut) => {
27
+ arvot("kuolinsyy")(tapahtuma, solut);
28
+ const { arvo: teksti, kommentit } = solut[1] ?? { kommentit: [] };
29
+ if (kommentit.length > 0)
30
+ (tapahtuma.kenttakommentit ??= {}).ika = kommentit;
31
+ if (!teksti)
32
+ return;
33
+ const osat = IKA.exec(teksti);
34
+ if (!osat) {
35
+ tapahtuma.ika_teksti = teksti;
36
+ return;
37
+ }
38
+ const ika = {};
39
+ IKAYKSIKOT.forEach((yksikko, i) => {
40
+ const arvo = osat[i + 1];
41
+ if (arvo && arvo !== "-")
42
+ ika[yksikko] = arvo;
43
+ });
44
+ if (Object.keys(ika).length > 0)
45
+ tapahtuma.ika = ika;
46
+ };
47
+ /** Which fields each row fills, keyed by the row's heading path, per book. */
48
+ const RIVIT = {
49
+ kastetut: {
50
+ "Syntynyt / Kastettu": arvot("syntynyt", "kastettu"),
51
+ "Kylä / Talo": arvot("kyla", "talo"),
52
+ Isä: henkilo("isa"),
53
+ Äiti: henkilo("aiti"),
54
+ Lapsi: arvot("lapsi"),
55
+ },
56
+ vihityt: {
57
+ "Kuulutettu / Vihitty": arvot("kuulutettu", "vihitty"),
58
+ "Mies › Kylä / Talo": arvot("mies.kyla", "mies.talo"),
59
+ "Mies › Nimi": henkilo("mies"),
60
+ "Vaimo › Kylä / Talo": arvot("vaimo.kyla", "vaimo.talo"),
61
+ "Vaimo › Nimi": henkilo("vaimo"),
62
+ },
63
+ haudatut: {
64
+ "Kuollut / Haudattu": arvot("kuollut", "haudattu"),
65
+ "Kylä / Talo": arvot("kyla", "talo"),
66
+ Vainaja: henkilo("henkilo"),
67
+ "Kuolinsyy / Ikä": kuolinsyyJaIka,
68
+ Omainen: henkilo("omainen"),
69
+ "Omainen › jatko": arvot("omainen.lisatieto"),
70
+ },
71
+ // The headings name the direction: this parish is "Minne" for a move in
72
+ // and "Mistä" for a move out.
73
+ smuutt: {
74
+ "Lähtöpäivä / Saapumispäivä": arvot("lahtopaiva", "saapumispaiva"),
75
+ "Minne: Kylä / Talo": arvot("kyla", "talo"),
76
+ Mistä: arvot("toinen_paikka"),
77
+ Henkilö: henkilo("henkilo"),
78
+ },
79
+ umuutt: {
80
+ "Lähtöpäivä / Saapumispäivä": arvot("lahtopaiva", "saapumispaiva"),
81
+ "Mistä: Kylä / Talo": arvot("kyla", "talo"),
82
+ Minne: arvot("toinen_paikka"),
83
+ Henkilö: henkilo("henkilo"),
84
+ },
85
+ };
86
+ /** Parses a Hiski event page (/hiski?fi+SRK+KIRJA+N). */
87
+ export function parseTapahtumasivu(html) {
88
+ if (html.includes(VIRHE))
89
+ return { tila: "virhe" };
90
+ const $ = cheerio.load(html);
91
+ const taulukko = $("table[border=4]").first();
92
+ if (taulukko.length === 0)
93
+ return { tila: "ei_loytynyt" };
94
+ const lomake = (nimi) => $(`input[name="${nimi}" i]`).attr("value");
95
+ const koodi = lomake("srk");
96
+ const kirja = lomake("kirja");
97
+ const nimi = siisti($("h2").first().text());
98
+ const pysyva = $("a")
99
+ .filter((_, a) => /^\/hiski\?fi\+t\d+$/.test($(a).attr("href") ?? ""))
100
+ .attr("href");
101
+ if (!koodi || !onTunnettuKirja(kirja) || !nimi || !pysyva) {
102
+ throw new Error("Tapahtumasivua ei tunnistettu");
103
+ }
104
+ const rivit = lueRivit($, taulukko);
105
+ const kommentit = [
106
+ ...lueKommentit($, rivit, "Alkup. komm.", "alkup"),
107
+ ...lueKommentit($, rivit, "Oma komm.", "oma"),
108
+ ];
109
+ const linkit = rivit
110
+ .filter((r) => r.otsake[0] === "Linkit")
111
+ .flatMap((r) => r.solut.find("a").get())
112
+ .map((a) => ({
113
+ teksti: siisti($(a).text()) ?? "",
114
+ url: new URL($(a).attr("href") ?? "", HISKI_URL).href,
115
+ }));
116
+ const tapahtuma = {
117
+ seurakunta: { koodi, nimi },
118
+ kirja,
119
+ pysyva_linkki: new URL(pysyva, HISKI_URL).href,
120
+ };
121
+ for (const rivi of rivit) {
122
+ const kasittelija = RIVIT[kirja][rivi.otsake.join(" › ")];
123
+ kasittelija?.(tapahtuma, rivi.solut.get().map((s) => jasennaSolu($, $(s))));
124
+ }
125
+ if (kommentit.length > 0)
126
+ tapahtuma.kommentit = kommentit;
127
+ if (linkit.length > 0)
128
+ tapahtuma.linkit = linkit;
129
+ return { tila: "ok", tapahtuma: tapahtuma };
130
+ }
131
+ const onTunnettuKirja = (kirja) => kirja !== undefined && Object.hasOwn(RIVIT, kirja);
132
+ /** A cell holding only a <SMALL> heading, e.g. <TD><SMALL>Isä</SMALL>. */
133
+ function onOtsakesolu($, solu) {
134
+ const small = $(solu).children("small");
135
+ const teksti = siisti($(solu).text());
136
+ return small.length === 1 && teksti === siisti(small.text()) && !teksti?.startsWith("(");
137
+ }
138
+ /**
139
+ * Reads the rows with their heading path. A heading cell spanning several
140
+ * rows (Mies, Vaimo, Omainen) becomes the first part of the path of each row
141
+ * it spans; a spanned row without a heading of its own gets "jatko".
142
+ */
143
+ function lueRivit($, taulukko) {
144
+ const rivit = [];
145
+ let yla;
146
+ taulukko.find("tr").each((_, tr) => {
147
+ const solut = $(tr).children("td");
148
+ let otsakkeita = 0;
149
+ while (otsakkeita < solut.length && onOtsakesolu($, solut[otsakkeita]))
150
+ otsakkeita++;
151
+ const otsakkeet = solut.slice(0, otsakkeita).get();
152
+ const omat = otsakkeet.map((s) => siisti($(s).text()));
153
+ const ylaotsake = yla && yla.riveja > 0 ? yla.otsake : undefined;
154
+ const ensimmainen = otsakkeet[0];
155
+ const ulottuu = Number($(ensimmainen).attr("rowspan") ?? 1);
156
+ if (ensimmainen && ulottuu > 1) {
157
+ yla = { otsake: omat[0], riveja: ulottuu - 1 };
158
+ rivit.push({ otsake: omat, solut: solut.slice(otsakkeet.length) });
159
+ return;
160
+ }
161
+ if (ylaotsake)
162
+ yla.riveja--;
163
+ const otsake = ylaotsake ? [ylaotsake, ...(omat.length > 0 ? omat : ["jatko"])] : omat;
164
+ rivit.push({ otsake, solut: solut.slice(otsakkeet.length) });
165
+ });
166
+ return rivit;
167
+ }
168
+ /**
169
+ * Splits a comment cell such as
170
+ * `<FONT>ALKUPKOMM:</FONT> Död 17.3.1850<BR><FONT>VV:</FONT> \K2.<BR>`
171
+ * into comments, skipping empty ones.
172
+ */
173
+ function lueKommentit($, rivit, otsake, tyyppi) {
174
+ const rivi = rivit.find((r) => r.otsake[0] === otsake);
175
+ if (!rivi)
176
+ return [];
177
+ return (rivi.solut.first().html() ?? "").split(/<br\s*\/?>/i).flatMap((osa) => {
178
+ const pala = cheerio.load(osa, null, false);
179
+ const alikentta = siisti(pala("font").first().text())?.replace(/:$/, "");
180
+ pala("font").remove();
181
+ const teksti = siisti(pala.text());
182
+ if (!teksti)
183
+ return [];
184
+ return [{ tyyppi, alikentta: alikentta ?? tyyppi.toUpperCase(), teksti }];
185
+ });
186
+ }
187
+ //# sourceMappingURL=tapahtumasivu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tapahtumasivu.js","sourceRoot":"","sources":["../src/tapahtumasivu.ts"],"names":[],"mappings":"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"}
package/dist/teksti.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Normalizes text from Hiski pages: whitespace (including the non-breaking
3
+ * spaces Hiski uses for empty cells) is collapsed and trimmed, and empty text
4
+ * becomes undefined.
5
+ */
6
+ export function siisti(teksti) {
7
+ const tulos = teksti?.replace(/\s+/g, " ").trim();
8
+ return tulos || undefined;
9
+ }
10
+ //# sourceMappingURL=teksti.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teksti.js","sourceRoot":"","sources":["../src/teksti.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,MAA0B;IAC/C,MAAM,KAAK,GAAG,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,OAAO,KAAK,IAAI,SAAS,CAAC;AAC5B,CAAC"}