strom-research 1.26.0-beta.1 → 1.26.0-beta.2
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/CHANGELOG.cs.md +8 -1
- package/CHANGELOG.de.md +11 -1
- package/CHANGELOG.md +9 -1
- package/CHANGELOG.pl.md +10 -1
- package/dist/brief/places.js +25 -3
- package/dist/commands/placeid.js +37 -6
- package/dist/core/placeid.js +221 -37
- package/dist/core/placesview.js +1 -0
- package/dist/core/schema.js +2 -0
- package/dist/core/tree.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.cs.md
CHANGED
|
@@ -7,7 +7,7 @@ Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou n
|
|
|
7
7
|
|
|
8
8
|
## 1.26.0
|
|
9
9
|
|
|
10
|
-
*Připravuje se: bety do 1.26.0-beta.
|
|
10
|
+
*Připravuje se: bety do 1.26.0-beta.2. Oddíl bude úplný s vydáním.*
|
|
11
11
|
|
|
12
12
|
- **Brána týdne předplatného už neukončí běh kvůli jedné nepřečtené odpovědi.** Zeptá se znovu, pak čtvrt hodiny počká
|
|
13
13
|
a řekne, co dostala. Nepřihlášený agent se ohlásí už před prvním sezením i s postupem přihlášení; výchozí složka
|
|
@@ -21,6 +21,13 @@ Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou n
|
|
|
21
21
|
vrátit. Souhrn sezení o místech začíná tím, co se udělalo – ztotožněno, ověřeno, s otázkou, nedohledáno – a jedno
|
|
22
22
|
sezení vezme víc míst sousedních oblastí. Z „v Lhotě v čp. 12“ už nezůstane místo „v Lhotě v“:
|
|
23
23
|
místem je obec, jak ji výzkum píše jinde, i když ji věta skloňuje. Každý název se do záznamu místa zapíše jen jednou.
|
|
24
|
+
- **Fronta míst se po vyřízení míst vyprázdní.** Místo ztotožněné, ověřené, předané k otázce nebo nedohledané odejde z
|
|
25
|
+
fronty se všemi svými tvary, i s tím, který fakta mezitím píší jinak; místa, která sezení už udělalo, se znovu neberou
|
|
26
|
+
a počet čekajících sedí. Co ve frontě nechala dřívější verze, se odebere samo (jen vyřízená místa, krok jde vrátit);
|
|
27
|
+
kontrola vyžádaná u místa, které čeká na ztotožnění, zůstane jako kontrola. Každý krok nad místy jde vrátit vlastním
|
|
28
|
+
číslem, i když čeká jiný (také odpovědi člověka). Chybné jméno v záznamu místa — překlep ze záznamu, který už žádný
|
|
29
|
+
fakt nepíše, jméno jiného místa — jde odebrat i s důvodem (`strom place names L… --drop`); sezení nad místy vidí další
|
|
30
|
+
jména záznamu s jazykem a roky; kontrola může uvést, o jaký zdroj se opřela; příslušnost jde zadat i k jednomu roku.
|
|
24
31
|
- V rozporech úpravy z aplikace už číslo domu, které značku má, nedostane značku podruhé (polsky „nr domu nr domu 12“),
|
|
25
32
|
a pomlčka v titulku je podle jazyka výzkumu.
|
|
26
33
|
|
package/CHANGELOG.de.md
CHANGED
|
@@ -7,7 +7,7 @@ Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Ins
|
|
|
7
7
|
|
|
8
8
|
## 1.26.0
|
|
9
9
|
|
|
10
|
-
*In Vorbereitung: die Betas bis 1.26.0-beta.
|
|
10
|
+
*In Vorbereitung: die Betas bis 1.26.0-beta.2. Der Abschnitt ist mit dem Release vollständig.*
|
|
11
11
|
|
|
12
12
|
- **Das Tor der Abo-Woche beendet einen Lauf nicht mehr wegen einer unlesbaren Antwort.** Es fragt erneut, wartet dann
|
|
13
13
|
eine Viertelstunde und nennt, was es erhielt. Ein nicht angemeldeter Agent wird schon vor der ersten Sitzung gemeldet,
|
|
@@ -25,6 +25,16 @@ Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Ins
|
|
|
25
25
|
benachbarter Gebiete. Aus „in Lhota in Nr. 12“ bleibt kein Ort „in Lhota in“ mehr:
|
|
26
26
|
Ort ist die Gemeinde, wie die Forschung sie sonst schreibt, auch wenn der Satz sie beugt. Jeder Name steht nur einmal
|
|
27
27
|
im Eintrag eines Ortes.
|
|
28
|
+
- **Die Warteschlange der Orte leert sich, wenn die Orte erledigt sind.** Ein Ort, der bestimmt, geprüft, erfragt oder
|
|
29
|
+
nicht gefunden wurde, verlässt die Warteschlange mit allen seinen Formen, auch mit einer, die die Fakten inzwischen
|
|
30
|
+
anders schreiben; Orte, die eine Sitzung schon erledigt hat, werden nicht erneut genommen, und die Zahl des Wartenden
|
|
31
|
+
stimmt. Was eine frühere Version in der Warteschlange ließ, wird von selbst entfernt (nur erledigte Orte, der Schritt
|
|
32
|
+
lässt sich rückgängig machen); eine Prüfung, die für einen Ort verlangt wird, der auf die Bestimmung wartet, bleibt
|
|
33
|
+
als Prüfung. Jeder Schritt bei den Orten lässt sich mit seiner eigenen Nummer rückgängig machen, auch wenn ein anderer
|
|
34
|
+
wartet (auch die Antworten der Person). Ein falscher Name im Datensatz eines Ortes — ein Versehen eines Eintrags, das
|
|
35
|
+
kein Faktum mehr schreibt, der Name eines anderen Ortes — lässt sich mit Begründung entfernen (`strom place names L…
|
|
36
|
+
--drop`); die Sitzung über die Orte sieht die weiteren Namen des Datensatzes mit Sprache und Jahren; eine Prüfung kann
|
|
37
|
+
die Quelle nennen, auf die sie sich stützt; eine Zugehörigkeit lässt sich auch für ein einzelnes Jahr angeben.
|
|
28
38
|
- In den Widersprüchen einer Änderung aus der App bekommt eine Hausnummer, die ihre Bezeichnung schon trägt, sie nicht
|
|
29
39
|
ein zweites Mal (polnisch „nr domu nr domu 12“), und der Gedankenstrich im Titel richtet sich nach der Sprache der
|
|
30
40
|
Forschung.
|
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,7 @@ The changes of each version in short. The full notes and the install lines are o
|
|
|
7
7
|
|
|
8
8
|
## 1.26.0
|
|
9
9
|
|
|
10
|
-
*In preparation: the betas up to 1.26.0-beta.
|
|
10
|
+
*In preparation: the betas up to 1.26.0-beta.2. The section is complete with the release.*
|
|
11
11
|
|
|
12
12
|
- **The gate of the subscription's week no longer ends a run over one answer it cannot read.** It asks again, then
|
|
13
13
|
waits a quarter of an hour and says what it got. An agent not logged in is said before the first session, with how to
|
|
@@ -23,6 +23,14 @@ The changes of each version in short. The full notes and the install lines are o
|
|
|
23
23
|
located — and takes more places of neighbouring areas at once. "in Lhota in no. 12" no longer leaves "in Lhota in" as
|
|
24
24
|
a place: the place is the municipality as the research writes it elsewhere, also where the sentence declined it. Each
|
|
25
25
|
name goes into a place's record once.
|
|
26
|
+
- **The queue of places empties as the places are done.** A place identified, checked, asked about or not located leaves
|
|
27
|
+
the queue with every form of it, also one the facts write otherwise since; the places a session already did are no
|
|
28
|
+
longer taken again, and the count of what waits is right. What an earlier version left in the queue is taken out by
|
|
29
|
+
itself (only places dealt with, the step can be undone); a check asked of a place waiting to be identified is kept as
|
|
30
|
+
the check. Each step on places is undone by its own number, also while another waits (the person's answers among
|
|
31
|
+
them). A wrong name of a place's record — a slip of a record no fact writes any more, another place's name — can be
|
|
32
|
+
taken off with the reason (`strom place names L… --drop`); the session on the places sees the record's other names
|
|
33
|
+
with their language and years; a check can name the reference it stood on; a jurisdiction can be given for one year.
|
|
26
34
|
- In the conflicts of an edit from the app a house that says it is one already no longer gets its marker twice ("nr domu nr
|
|
27
35
|
domu 12" in Polish), and the title's dash is the one of the research's language.
|
|
28
36
|
|
package/CHANGELOG.pl.md
CHANGED
|
@@ -7,7 +7,7 @@ Zmiany każdej wersji w skrócie. Pełne informacje o wydaniach i polecenia inst
|
|
|
7
7
|
|
|
8
8
|
## 1.26.0
|
|
9
9
|
|
|
10
|
-
*W przygotowaniu: bety do 1.26.0-beta.
|
|
10
|
+
*W przygotowaniu: bety do 1.26.0-beta.2. Sekcja będzie kompletna z wydaniem.*
|
|
11
11
|
|
|
12
12
|
- **Warunek tygodnia subskrypcji nie kończy już przebiegu z powodu jednej nieodczytanej odpowiedzi.** Pyta ponownie,
|
|
13
13
|
potem czeka kwadrans i podaje, co otrzymał. Niezalogowany agent jest zgłaszany już przed pierwszą sesją, ze sposobem
|
|
@@ -23,6 +23,15 @@ Zmiany każdej wersji w skrócie. Pełne informacje o wydaniach i polecenia inst
|
|
|
23
23
|
odnaleziono – a jedna sesja bierze więcej miejsc z sąsiednich obszarów. Z „w Lhocie w nr 12” nie zostaje już
|
|
24
24
|
miejsce „w Lhocie w”: miejscem jest miejscowość tak, jak badanie zapisuje ją gdzie indziej, także gdy zdanie ją
|
|
25
25
|
odmienia. Każda nazwa trafia do rekordu miejsca tylko raz.
|
|
26
|
+
- **Kolejka miejsc opróżnia się, gdy miejsca są załatwione.** Miejsce zidentyfikowane, sprawdzone, przekazane jako
|
|
27
|
+
pytanie albo nieustalone opuszcza kolejkę ze wszystkimi swoimi formami, także z tą, którą fakty zapisują teraz
|
|
28
|
+
inaczej; miejsca, które sesja już zrobiła, nie są brane ponownie, a liczba oczekujących się zgadza. To, co w kolejce
|
|
29
|
+
zostawiła wcześniejsza wersja, usuwa się samo (tylko załatwione miejsca, krok można cofnąć); sprawdzenie zlecone dla
|
|
30
|
+
miejsca czekającego na identyfikację zostaje jako sprawdzenie. Każdy krok przy miejscach można cofnąć jego własnym
|
|
31
|
+
numerem, także gdy czeka inny (również odpowiedzi osoby). Błędną nazwę w rekordzie miejsca — pomyłkę z zapisu, której
|
|
32
|
+
żaden fakt już nie zapisuje, nazwę innego miejsca — można usunąć z uzasadnieniem (`strom place names L… --drop`);
|
|
33
|
+
sesja nad miejscami widzi pozostałe nazwy rekordu z językiem i latami; sprawdzenie może podać źródło, na którym się
|
|
34
|
+
oparło; przynależność można podać także dla jednego roku.
|
|
26
35
|
- W sprzecznościach zmian z aplikacji numer domu, który ma już oznaczenie, nie dostaje go po raz drugi („nr domu nr
|
|
27
36
|
domu 12”), a myślnik w tytule zależy od języka badania.
|
|
28
37
|
|
package/dist/brief/places.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { langName } from "../core/lang.js";
|
|
6
6
|
import { match } from "../cli/registry.js";
|
|
7
7
|
import { synopsis } from "./sheet.js";
|
|
8
|
-
import { factForms, personsHand, PLACES_IDENTIFY_ORIGIN, queueBatches, readPlacesFile, recordIndex } from "../core/placeid.js";
|
|
8
|
+
import { factForms, otherNames, personsHand, PLACES_IDENTIFY_ORIGIN, queueBatches, readPlacesFile, recordIndex } from "../core/placeid.js";
|
|
9
9
|
import { historyOn } from "../core/pastevents.js";
|
|
10
10
|
import { othersRecords } from "../core/placeadopt.js";
|
|
11
11
|
import { foldText } from "../core/text.js";
|
|
@@ -24,6 +24,24 @@ function span(ys) {
|
|
|
24
24
|
const b = Math.max(...ys);
|
|
25
25
|
return a === b ? String(a) : `${a}–${b}`;
|
|
26
26
|
}
|
|
27
|
+
/** A name as the agent writes it: name[@lang][:from-to]. */
|
|
28
|
+
function nameSaid(n) {
|
|
29
|
+
const years = n.from === undefined && n.to === undefined ? "" : n.from === n.to ? `:${n.from}` : `:${n.from ?? ""}-${n.to ?? ""}`;
|
|
30
|
+
return `${n.name}${n.lang ? `@${n.lang}` : ""}${years}`;
|
|
31
|
+
}
|
|
32
|
+
/** A record's names: today's, its other names (another language, old names — with their language and years), the forms of the facts. */
|
|
33
|
+
function recordNames(p) {
|
|
34
|
+
const rest = otherNames(p);
|
|
35
|
+
const alt = rest.filter((n) => n.kind !== "form");
|
|
36
|
+
const forms = rest.filter((n) => n.kind === "form");
|
|
37
|
+
return [
|
|
38
|
+
nameSaid(p.names[0]),
|
|
39
|
+
alt.length ? `other names ${alt.map(nameSaid).join(", ")}` : "",
|
|
40
|
+
forms.length ? `forms ${forms.map((n) => n.name).join(", ")}` : "",
|
|
41
|
+
]
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.join("; ");
|
|
44
|
+
}
|
|
27
45
|
/** One place of the batch: its form, the facts, the people, the archives of its sources, the record of it. */
|
|
28
46
|
function placeLine(tree, form, f, entry, p, others) {
|
|
29
47
|
const areas = f
|
|
@@ -40,7 +58,7 @@ function placeLine(tree, form, f, entry, p, others) {
|
|
|
40
58
|
f ? `${f.facts} fact${f.facts === 1 ? "" : "s"} ${span(f.years)}` : "",
|
|
41
59
|
persons.length ? `people ${persons.join(" ")}${f.persons.size > persons.length ? " …" : ""}` : "",
|
|
42
60
|
areas.length ? `sources from ${areas.join(", ")}` : "",
|
|
43
|
-
p ? `record ${p.id}: ${p
|
|
61
|
+
p ? `record ${p.id}: ${recordNames(p)}` : "no record yet",
|
|
44
62
|
p?.parent ? `in ${p.parent}` : "",
|
|
45
63
|
p?.jurisdictions.length ? p.jurisdictions.map((j) => `${j.kind} ${j.name}${j.from || j.to ? ` ${j.from ?? "…"}–${j.to ?? "…"}` : ""}`).join(", ") : "",
|
|
46
64
|
p?.coords ? `on the map ${p.coords.lat.toFixed(4)}, ${p.coords.lon.toFixed(4)}${personsHand(p) ? " (the person's — never moved)" : ""}` : "",
|
|
@@ -89,8 +107,11 @@ export function placesIdentifySections(tree, task, alone, settings) {
|
|
|
89
107
|
"",
|
|
90
108
|
"The record of a place:",
|
|
91
109
|
"- its first name is the official name of today in the language of the country it lies in now (a village now in Poland: Polish; the German name is historical) — the names of the facts and the old names go in as --form and --alt name@lang:from-to (an old name: from-to its years; one year: name@lang:1844)",
|
|
110
|
+
"- the record's other names above are known already (name@language:years): today's name in another language may be among them — look it up by that name before calling it not found, and set it first with --name when the reference confirms it",
|
|
111
|
+
"- a name of the record found wrong (another place's name, a slip of a record no fact writes any more): strom place names L… --drop \"<name>\" --why \"what shows it is wrong\" — never left in with a note that it is wrong",
|
|
92
112
|
"- what is found of a place goes into its record, also where the coordinates are right already: strom place verify L… --ok --why \"…\" --alt … --form … (never only into the history)",
|
|
93
|
-
"- a jurisdiction found wrong (another parish): strom place verify L… --ok --why \"what the records say\" --drop-jurisdiction parish:<name> --jurisdiction parish:<right one>:from-to",
|
|
113
|
+
"- a jurisdiction found wrong (another parish): strom place verify L… --ok --why \"what the records say\" --drop-jurisdiction parish:<name> --jurisdiction parish:<right one>:from-to (one year: parish:<name>:1899)",
|
|
114
|
+
"- the reference a check stood on: strom place verify L… --ok --why \"…\" --source-title \"…\" --source-url …",
|
|
94
115
|
"- a form of the facts that is a place with a record already (\"Nové Lhoty\" of Lhoty): strom place identify L… --form \"<form>\" — never a second record; two records of one place: strom place merge L… --into L… --reason \"…\"",
|
|
95
116
|
"",
|
|
96
117
|
"Three degrees, never guessed:",
|
|
@@ -115,6 +136,7 @@ export function placesIdentifySections(tree, task, alone, settings) {
|
|
|
115
136
|
cmd(["place", "verify"]),
|
|
116
137
|
cmd(["place", "rewrite"]),
|
|
117
138
|
cmd(["place", "merge"]),
|
|
139
|
+
cmd(["place", "names"]),
|
|
118
140
|
cmd(["place", "show"]),
|
|
119
141
|
...(history ? [cmd(["history", "map"])] : []),
|
|
120
142
|
].filter(Boolean);
|
package/dist/commands/placeid.js
CHANGED
|
@@ -8,7 +8,7 @@ import { UsageError } from "../core/errors.js";
|
|
|
8
8
|
import { listOpt } from "../core/records.js";
|
|
9
9
|
import { refuseInArchive } from "../core/state.js";
|
|
10
10
|
import { isAgent } from "../core/which.js";
|
|
11
|
-
import { answerPlaces, cancelPlaces, factForms, identifyEstimate, identifyPlace, parseCandidate, parseJurisdiction, PLACE_TAKES, placeState, proposePlace, queueBatches, queueOrder, queuePlaces, raisePlacesTask, readPlacesFile, recordIndex, rewriteForm, undoPlaces, unlocatePlace, verifyPlace, identifyingNow, mergePlaces, parseAltName, parseDrop, placeIdOf, PLACE_FLAGS, REQUEST_MAX, } from "../core/placeid.js";
|
|
11
|
+
import { answerPlaces, cancelPlaces, dropPlaceNames, factForms, identifyEstimate, identifyPlace, parseCandidate, parseJurisdiction, PLACE_TAKES, placeState, proposePlace, queueBatches, queueOrder, queuePlaces, raisePlacesTask, readPlacesFile, recordIndex, rewriteForm, undoPlaces, unlocatePlace, verifyPlace, identifyingNow, mergePlaces, parseAltName, parseDrop, placeIdOf, PLACE_FLAGS, REQUEST_MAX, } from "../core/placeid.js";
|
|
12
12
|
import { actsAsArchive } from "../core/state.js";
|
|
13
13
|
function str(v) {
|
|
14
14
|
return typeof v === "string" && v.trim() ? v.trim() : undefined;
|
|
@@ -24,7 +24,7 @@ const DETAIL_OPTIONS = [
|
|
|
24
24
|
{ name: "form", type: "string", multiple: true, value: "<place>", description: "another form of the facts that is the same place (kept among the record's names as a form, out of the queue; repeatable)" },
|
|
25
25
|
{ name: "kind", type: "string", value: "<kind>", description: "village, town, part of a municipality, …" },
|
|
26
26
|
{ name: "parent", type: "string", value: "<L…>", description: "the place it is part of" },
|
|
27
|
-
{ name: "jurisdiction", type: "string", multiple: true, value: "<kind:name[:from-to]>", description: 'e.g. "parish:Lhotov:1784-" or "district:Lhotov" (repeatable)' },
|
|
27
|
+
{ name: "jurisdiction", type: "string", multiple: true, value: "<kind:name[:from-to]>", description: 'e.g. "parish:Lhotov:1784-", "district:Lhotov" or one year "district:Lhotov:1899" (repeatable)' },
|
|
28
28
|
{ name: "drop-jurisdiction", type: "string", multiple: true, value: "<kind:name>", description: "a jurisdiction of the record found wrong: taken off, --why says why (repeatable)" },
|
|
29
29
|
];
|
|
30
30
|
register({
|
|
@@ -121,6 +121,8 @@ register({
|
|
|
121
121
|
{ name: "ok", type: "boolean", description: "the records agree with the coordinates" },
|
|
122
122
|
{ name: "undecided", type: "string", value: "<why>", description: "it cannot be decided, and why" },
|
|
123
123
|
{ name: "why", type: "string", value: "<text>", description: "with --ok: on what (kept with the check); with --drop-jurisdiction: why it is wrong" },
|
|
124
|
+
{ name: "source-title", type: "string", value: "<title>", description: "the reference the check stands on (a gazetteer, a register of places)" },
|
|
125
|
+
{ name: "source-url", type: "string", value: "<url>", description: "its address" },
|
|
124
126
|
...DETAIL_OPTIONS,
|
|
125
127
|
],
|
|
126
128
|
args: [{ name: "place", description: "place ID (L0012) or the place as the facts write it", required: true }],
|
|
@@ -134,7 +136,12 @@ register({
|
|
|
134
136
|
refuseInArchive(tree, "place verify", ctx.settings.agent(tree.config).value);
|
|
135
137
|
if (!!opts.ok === !!opts.undecided)
|
|
136
138
|
throw new UsageError("--ok or --undecided \"<why>\"", { hint: `strom place verify ${args[0] ?? "L…"} --ok --why "on what" (names found: --alt, --form)` });
|
|
137
|
-
const
|
|
139
|
+
const title = str(opts["source-title"]);
|
|
140
|
+
const url = str(opts["source-url"]);
|
|
141
|
+
if (url && !title)
|
|
142
|
+
throw new UsageError("--source-url with its --source-title");
|
|
143
|
+
const source = title ? { source: { title, ...(url ? { url } : {}) } } : {};
|
|
144
|
+
const r = verifyPlace(tree, args[0], opts.ok ? { ok: true, ...(str(opts.why) ? { why: str(opts.why) } : {}), ...source } : { ok: false, why: String(opts.undecided), ...source }, detailsOf(opts));
|
|
138
145
|
raisePlacesTask(tree, actsAsArchive(tree));
|
|
139
146
|
return stepResult(tree, r);
|
|
140
147
|
},
|
|
@@ -185,11 +192,35 @@ register({
|
|
|
185
192
|
propose: !!opts.propose,
|
|
186
193
|
...(str(opts.why) ? { why: str(opts.why) } : {}),
|
|
187
194
|
});
|
|
195
|
+
raisePlacesTask(tree, actsAsArchive(tree));
|
|
188
196
|
return {
|
|
189
197
|
text: lines(written(tree), r.left ? `left: ${counted(r.left, "fact")} (their house says another thing — never written over)` : undefined),
|
|
190
198
|
data: { place: r.place, changed: r.changed, left: r.left, asked: r.asked },
|
|
191
199
|
};
|
|
192
200
|
},
|
|
201
|
+
}, {
|
|
202
|
+
path: ["place", "names"],
|
|
203
|
+
summary: "Take a wrong name off a place record (a slip of a record kept as a form, another place's name), with the reason",
|
|
204
|
+
group: "sources",
|
|
205
|
+
tree: true,
|
|
206
|
+
writes: true,
|
|
207
|
+
description: "Never today's name (the first: --name of strom place verify or identify sets another) and never a name a fact still writes for the place (strom place rewrite first). Other names are added with --alt and --form of strom place identify or verify.",
|
|
208
|
+
args: [{ name: "place", description: "place ID (L0012)", required: true }],
|
|
209
|
+
options: [
|
|
210
|
+
{ name: "drop", type: "string", multiple: true, value: "<name>", description: "the name to take off, as the record has it" },
|
|
211
|
+
{ name: "why", type: "string", value: "<text>", description: "why it is wrong (kept with the step)" },
|
|
212
|
+
],
|
|
213
|
+
examples: ['strom place names L0002 --drop "v Lhotě v" --why "a slip of the register; the facts write Lhota now"'],
|
|
214
|
+
run(ctx, { args, opts }) {
|
|
215
|
+
const tree = ctx.tree();
|
|
216
|
+
refuseInArchive(tree, "place names", ctx.settings.agent(tree.config).value);
|
|
217
|
+
const r = dropPlaceNames(tree, args[0], listOpt(opts.drop), String(opts.why ?? ""));
|
|
218
|
+
raisePlacesTask(tree, actsAsArchive(tree));
|
|
219
|
+
return {
|
|
220
|
+
text: lines(`${r.place.id}: ${r.dropped.map((n) => n.name).join(", ")} taken off`, `undo (the person's): strom place undo ${r.undo}`, written(tree)),
|
|
221
|
+
data: { place: r.place, dropped: r.dropped, undo: r.undo },
|
|
222
|
+
};
|
|
223
|
+
},
|
|
193
224
|
}, {
|
|
194
225
|
path: ["place", "queue"],
|
|
195
226
|
summary: "Queue places to be identified (or their coordinates checked) — or show the queue",
|
|
@@ -304,12 +335,12 @@ register({
|
|
|
304
335
|
},
|
|
305
336
|
}, {
|
|
306
337
|
path: ["place", "undo"],
|
|
307
|
-
summary: "Undo the person's
|
|
338
|
+
summary: "Undo a step on places by its id (the person's answers, names taken off, the queue tidied)",
|
|
308
339
|
group: "sources",
|
|
309
340
|
tree: true,
|
|
310
341
|
writes: true,
|
|
311
|
-
person: "Undoing
|
|
312
|
-
args: [{ name: "step", description: "the step's id (the
|
|
342
|
+
person: "Undoing a step on places is the person's own",
|
|
343
|
+
args: [{ name: "step", description: "the step's id (the step prints it; each of the last 20 steps once)", required: true }],
|
|
313
344
|
examples: ["strom place undo u1a2b3c"],
|
|
314
345
|
run(ctx, { args }) {
|
|
315
346
|
const tree = ctx.tree();
|
package/dist/core/placeid.js
CHANGED
|
@@ -41,12 +41,30 @@ export function readPlacesFile(tree) {
|
|
|
41
41
|
for (const [k, v] of Object.entries(f?.queue ?? {}))
|
|
42
42
|
if (v && typeof v.form === "string" && v.form.trim())
|
|
43
43
|
queue[k] = v;
|
|
44
|
-
|
|
44
|
+
const undos = Array.isArray(f?.undos) ? f.undos.filter((u) => u && typeof u.id === "string") : [];
|
|
45
|
+
return { queue, ...(f?.undo ? { undo: f.undo } : {}), ...(undos.length ? { undos } : {}), ...(f?.converted ? { converted: f.converted } : {}), ...(typeof f?.repaired === "number" ? { repaired: f.repaired } : {}) };
|
|
45
46
|
}
|
|
46
47
|
catch {
|
|
47
48
|
return { queue: {} };
|
|
48
49
|
}
|
|
49
50
|
}
|
|
51
|
+
/** How many steps on places can be undone, each by its own ID (the oldest go first). */
|
|
52
|
+
export const UNDO_KEPT = 20;
|
|
53
|
+
/** A step to be undone kept: the newest; the one before it waits among the others, never lost to it. */
|
|
54
|
+
function keepUndo(file, u) {
|
|
55
|
+
if (file.undo)
|
|
56
|
+
file.undos = [...(file.undos ?? []), file.undo].slice(-(UNDO_KEPT - 1));
|
|
57
|
+
file.undo = u;
|
|
58
|
+
return u.id;
|
|
59
|
+
}
|
|
60
|
+
/** A new step's undo ID: never one waiting already (two steps in one millisecond). */
|
|
61
|
+
function undoId(file) {
|
|
62
|
+
const taken = new Set([file.undo?.id, ...(file.undos ?? []).map((u) => u.id)]);
|
|
63
|
+
let t = Date.now();
|
|
64
|
+
while (taken.has(`u${t.toString(36)}`))
|
|
65
|
+
t++;
|
|
66
|
+
return `u${t.toString(36)}`;
|
|
67
|
+
}
|
|
50
68
|
function fileContent(f) {
|
|
51
69
|
return stringifyCanonical(f);
|
|
52
70
|
}
|
|
@@ -96,8 +114,12 @@ export function factForms(tree) {
|
|
|
96
114
|
* okres Horní"); none when two do — a place two records could be belongs to none until its writing is set right.
|
|
97
115
|
*/
|
|
98
116
|
export function recordIndex(tree) {
|
|
117
|
+
return recordIndexOf(tree.list("place"));
|
|
118
|
+
}
|
|
119
|
+
/** recordIndex over the given records (the records as a step leaves them). */
|
|
120
|
+
function recordIndexOf(places) {
|
|
99
121
|
const byName = new Map();
|
|
100
|
-
for (const p of
|
|
122
|
+
for (const p of places) {
|
|
101
123
|
if (p.retracted)
|
|
102
124
|
continue;
|
|
103
125
|
for (const k of new Set(p.names.map((n) => foldText(n.name))))
|
|
@@ -297,7 +319,17 @@ export function queuePlaces(tree, req, by = "person") {
|
|
|
297
319
|
skipped.push({ form: w.form, why: "running" });
|
|
298
320
|
continue;
|
|
299
321
|
}
|
|
300
|
-
|
|
322
|
+
const had = [w.key, ...others].find((k) => file.queue[k]);
|
|
323
|
+
if (had) {
|
|
324
|
+
const e = file.queue[had];
|
|
325
|
+
// queued to be looked for, and on the map since: the check asked now takes its place (the tidying would take
|
|
326
|
+
// the old entry out as identified, and the person's check with it)
|
|
327
|
+
if (req.check && w.place?.coords && !e.check) {
|
|
328
|
+
const { back: _b, ...rest } = e;
|
|
329
|
+
file.queue[had] = { ...rest, at, by, check: true };
|
|
330
|
+
queued.push(e.form);
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
301
333
|
skipped.push({ form: w.form, why: "queued" });
|
|
302
334
|
continue;
|
|
303
335
|
}
|
|
@@ -512,7 +544,7 @@ function setFirst(p, today, forms) {
|
|
|
512
544
|
}
|
|
513
545
|
/** "<kind>:<name>" of a jurisdiction to take off. */
|
|
514
546
|
export function parseDrop(s) {
|
|
515
|
-
const m = /^([^:]+):(.+?)(?::\d{0,4}-\d{0,4})?$/u.exec(s.normalize("NFC").trim());
|
|
547
|
+
const m = /^([^:]+):(.+?)(?::\d{0,4}-\d{0,4}|:\d{3,4})?$/u.exec(s.normalize("NFC").trim());
|
|
516
548
|
if (!m || !JURISDICTIONS.includes(m[1].trim()))
|
|
517
549
|
throw new UsageError(`--drop-jurisdiction "${s}": "<kind>:<name>", kind one of ${JURISDICTIONS.join(", ")}`, { code: "place.jurisdiction" });
|
|
518
550
|
return { kind: m[1].trim(), name: m[2].trim() };
|
|
@@ -609,10 +641,68 @@ function originUnknown(p) {
|
|
|
609
641
|
export function personsHand(p) {
|
|
610
642
|
return !!p.coords && (!p.locating || p.locating.by === "person");
|
|
611
643
|
}
|
|
612
|
-
/**
|
|
613
|
-
|
|
644
|
+
/**
|
|
645
|
+
* A step done on a place takes out of the queue every entry that leads to it now — the forms of its facts, and any
|
|
646
|
+
* form the queue keeps whose writing is a name of the record as the step leaves it (the facts written otherwise since,
|
|
647
|
+
* a rewrite, a merge) — and only those. `keys`: forms the step names itself. `only`: which entries go (all by default).
|
|
648
|
+
*/
|
|
649
|
+
function unqueue(tree, file, changed, keys, only = () => true) {
|
|
650
|
+
const ids = new Set(changed.filter((p) => !p.retracted).map((p) => p.id));
|
|
651
|
+
const byId = new Set(changed.map((p) => p.id));
|
|
652
|
+
const recordOf = recordIndexOf([...tree.list("place").filter((p) => !byId.has(p.id)), ...changed]);
|
|
653
|
+
for (const [k, e] of Object.entries(file.queue)) {
|
|
654
|
+
const p = recordOf(e.form);
|
|
655
|
+
if ((keys.includes(k) && only(e, changed[0])) || (p && ids.has(p.id) && only(e, p)))
|
|
656
|
+
delete file.queue[k];
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* A queue entry whose place was dealt with after it was queued (identified, checked, asked, not located) — what an
|
|
661
|
+
* earlier strom left in the queue when the facts' form of it was written otherwise before the step. An entry the
|
|
662
|
+
* person put back (`back`) is never taken out so.
|
|
663
|
+
*/
|
|
664
|
+
export function settledBy(p, e) {
|
|
665
|
+
if (e.back)
|
|
666
|
+
return false;
|
|
667
|
+
const l = p.locating;
|
|
668
|
+
const asked = !!l?.question && l.question.at >= e.at;
|
|
669
|
+
// checked after it was queued: the time of the check (an earlier strom kept only the day of a check found right — a
|
|
670
|
+
// later day; the same day, or a check undecided of no time, is not known to come after)
|
|
671
|
+
if (e.check)
|
|
672
|
+
return !!p.coords && (asked || (l?.checked ? l.checked >= e.at : !!l?.verified && l.verified > e.at.slice(0, 10)));
|
|
673
|
+
// a place on the map is never queued to be looked for (queuePlaces): on the map since, it was identified
|
|
674
|
+
return !!p.coords || asked || (!!p.unlocated && (l?.at ?? "") >= e.at);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* The entries of the queue whose place was dealt with already (settledBy) taken out — nothing else: an entry of no
|
|
678
|
+
* record, of a record two places bear, or of a place still to be done stays. One operation; while the person has no
|
|
679
|
+
* step to undo, `strom place undo` puts them back (and they stay then). Inside the tree's lock; the forms taken out.
|
|
680
|
+
*/
|
|
681
|
+
export function settleQueue(tree) {
|
|
682
|
+
const file = readPlacesFile(tree);
|
|
683
|
+
if (!Object.keys(file.queue).length)
|
|
684
|
+
return [];
|
|
685
|
+
const recordOf = recordIndex(tree);
|
|
686
|
+
const gone = {};
|
|
687
|
+
for (const [k, e] of Object.entries(file.queue)) {
|
|
688
|
+
const p = recordOf(e.form);
|
|
689
|
+
if (p && settledBy(p, e))
|
|
690
|
+
gone[k] = e;
|
|
691
|
+
}
|
|
692
|
+
const keys = Object.keys(gone);
|
|
693
|
+
if (!keys.length)
|
|
694
|
+
return [];
|
|
614
695
|
for (const k of keys)
|
|
615
696
|
delete file.queue[k];
|
|
697
|
+
const undo = keepUndo(file, { id: undoId(file), at: now(), places: {}, queue: gone });
|
|
698
|
+
const forms = keys.map((k) => gone[k].form);
|
|
699
|
+
write(tree, [], file, {
|
|
700
|
+
op: "place.settle",
|
|
701
|
+
targets: [],
|
|
702
|
+
reason: "queue entries of places dealt with already, left by an earlier version; nothing else taken out",
|
|
703
|
+
summary: `out of the queue, dealt with already: ${forms.length === 1 ? `place "${forms[0]}"` : `${forms.length} places (${forms.join(", ").slice(0, 600)})`} — undo: strom place undo ${undo}`,
|
|
704
|
+
});
|
|
705
|
+
return forms;
|
|
616
706
|
}
|
|
617
707
|
/**
|
|
618
708
|
* A place identified surely (K2.2, K2.4): its coordinates, names, parent and jurisdictions written into its record (made
|
|
@@ -663,7 +753,7 @@ export function identifyPlace(tree, ref, input) {
|
|
|
663
753
|
p.locating = locating;
|
|
664
754
|
p.updated = now();
|
|
665
755
|
const file = readPlacesFile(tree);
|
|
666
|
-
unqueue(file, [...t.keys, ...more]);
|
|
756
|
+
unqueue(tree, file, [p], [...t.keys, ...more]);
|
|
667
757
|
write(tree, [p], file, {
|
|
668
758
|
op: "place.identify",
|
|
669
759
|
targets: [p.id],
|
|
@@ -700,7 +790,7 @@ export function unlocatePlace(tree, ref, why, needs, by = "research") {
|
|
|
700
790
|
delete p.locating.needs;
|
|
701
791
|
p.updated = now();
|
|
702
792
|
const file = readPlacesFile(tree);
|
|
703
|
-
unqueue(file, t.keys);
|
|
793
|
+
unqueue(tree, file, [p], t.keys);
|
|
704
794
|
write(tree, [p], file, { op: "place.unlocated", targets: [p.id], summary: `${p.id} ${p.names[0].name} not located: ${w.slice(0, 80)}` });
|
|
705
795
|
return p;
|
|
706
796
|
});
|
|
@@ -757,7 +847,7 @@ export function proposePlace(tree, ref, q) {
|
|
|
757
847
|
p.locating = { ...(p.locating ?? originUnknown(p)), question };
|
|
758
848
|
p.updated = now();
|
|
759
849
|
const file = readPlacesFile(tree);
|
|
760
|
-
unqueue(file, t.keys);
|
|
850
|
+
unqueue(tree, file, [p], t.keys);
|
|
761
851
|
write(tree, [p], file, { op: "place.propose", targets: [p.id], summary: `${p.id} ${p.names[0].name}: a question for the person (${q.kind}${q.candidates ? `, ${q.candidates.length} candidates` : ""})` });
|
|
762
852
|
return p;
|
|
763
853
|
});
|
|
@@ -779,18 +869,21 @@ export function verifyPlace(tree, ref, result, details = {}) {
|
|
|
779
869
|
throw new UsageError('why the jurisdiction is wrong: --why "…" (what the records or the reference say)', { code: "place.why" });
|
|
780
870
|
const { more, dropped, today } = applyDetails(tree, p, t, details, forms);
|
|
781
871
|
const base = p.locating ?? { by: "person", at: p.updated };
|
|
872
|
+
const { verifiedSource: _s, ...kept } = base;
|
|
873
|
+
const source = result.source ? { verifiedSource: { title: text(result.source.title, 300, "--source-title"), ...(result.source.url ? { url: result.source.url.trim() } : {}) } } : {};
|
|
782
874
|
if (result.ok) {
|
|
783
|
-
const { checkWhy: _c, verifiedWhy: _w, ...rest } =
|
|
784
|
-
|
|
875
|
+
const { checkWhy: _c, verifiedWhy: _w, ...rest } = kept;
|
|
876
|
+
const at = now();
|
|
877
|
+
p.locating = { ...rest, verified: at.slice(0, 10), checked: at, ...(why ? { verifiedWhy: why } : {}), ...source };
|
|
785
878
|
}
|
|
786
879
|
else {
|
|
787
880
|
if (!why)
|
|
788
881
|
throw new UsageError('why it cannot be decided: --undecided "…"', { code: "place.why" });
|
|
789
|
-
p.locating = { ...
|
|
882
|
+
p.locating = { ...kept, checkWhy: why, checked: now(), ...source };
|
|
790
883
|
}
|
|
791
884
|
p.updated = now();
|
|
792
885
|
const file = readPlacesFile(tree);
|
|
793
|
-
unqueue(file, [...t.keys, ...more]);
|
|
886
|
+
unqueue(tree, file, [p], [...t.keys, ...more]);
|
|
794
887
|
write(tree, [p], file, {
|
|
795
888
|
op: "place.verify",
|
|
796
889
|
targets: [p.id],
|
|
@@ -860,7 +953,11 @@ export function mergePlaces(tree, dupRef, intoRef, reason) {
|
|
|
860
953
|
.filter((x) => !x.retracted && x.parent === dup.id && x.id !== into.id)
|
|
861
954
|
.map((x) => ({ ...structuredClone(x), parent: into.id, updated: t }));
|
|
862
955
|
const gone = { ...structuredClone(dup), retracted: { at: t, reason: `the same place as ${into.id}: ${why}` }, mergedInto: into.id, updated: t };
|
|
863
|
-
|
|
956
|
+
// the queue's forms of the duplicate lead to the one record now: out where it was dealt with already
|
|
957
|
+
const file = readPlacesFile(tree);
|
|
958
|
+
const was = JSON.stringify(file.queue);
|
|
959
|
+
unqueue(tree, file, [into, gone, ...parts], [], (e, p) => settledBy(p, e));
|
|
960
|
+
write(tree, [into, gone, ...parts], JSON.stringify(file.queue) !== was ? file : undefined, {
|
|
864
961
|
op: "place.merge",
|
|
865
962
|
targets: [into.id, dup.id, ...parts.map((x) => x.id)].sort(),
|
|
866
963
|
summary: `${dup.id} ${dup.names[0].name} merged into ${into.id} ${into.names[0].name}${parts.length ? ` (its parts ${parts.map((x) => x.id).join(", ")} with it)` : ""}`,
|
|
@@ -953,7 +1050,11 @@ export function rewriteForm(tree, form, to, opts) {
|
|
|
953
1050
|
throw new UsageError('why the facts write it so: --reason "…"', { code: "place.why" });
|
|
954
1051
|
const { records, done, left: gone } = applyChanges(tree, changes, "before");
|
|
955
1052
|
place.updated = now();
|
|
956
|
-
|
|
1053
|
+
// the form's entry of the queue leads to the record now: out where the place was dealt with already
|
|
1054
|
+
const file = readPlacesFile(tree);
|
|
1055
|
+
const was = JSON.stringify(file.queue);
|
|
1056
|
+
unqueue(tree, file, [place], [], (e, x) => settledBy(x, e));
|
|
1057
|
+
write(tree, [place, ...records], JSON.stringify(file.queue) !== was ? file : undefined, {
|
|
957
1058
|
op: "place.rewrite",
|
|
958
1059
|
targets: [place.id, ...records.map((r) => r.id)].sort(),
|
|
959
1060
|
summary: `the facts' place "${form}" → ${name}${to.house ? ` (house ${to.house})` : ""}: ${done.length} facts in ${records.length} records${left.length ? `, ${left.length} left (another house)` : ""}`,
|
|
@@ -1057,12 +1158,12 @@ export function answerPlaces(tree, ids, take, opts = {}) {
|
|
|
1057
1158
|
const file = readPlacesFile(tree);
|
|
1058
1159
|
const before = new Map(places.map((p) => [p.id, tree.get(p.id)]));
|
|
1059
1160
|
const undo = {
|
|
1060
|
-
id:
|
|
1161
|
+
id: undoId(file),
|
|
1061
1162
|
at: now(),
|
|
1062
1163
|
places: Object.fromEntries(places.map((p) => [p.id, { before: before.get(p.id), after: p.updated }])),
|
|
1063
1164
|
...(done.length ? { facts: done } : {}),
|
|
1064
1165
|
};
|
|
1065
|
-
file
|
|
1166
|
+
keepUndo(file, undo);
|
|
1066
1167
|
write(tree, [...places, ...records], file, {
|
|
1067
1168
|
op: "place.answer",
|
|
1068
1169
|
targets: [...places.map((p) => p.id), ...records.map((r) => r.id)].sort(),
|
|
@@ -1073,15 +1174,16 @@ export function answerPlaces(tree, ids, take, opts = {}) {
|
|
|
1073
1174
|
});
|
|
1074
1175
|
}
|
|
1075
1176
|
/**
|
|
1076
|
-
*
|
|
1077
|
-
*
|
|
1177
|
+
* A step on places undone by its ID (the person's answers, a whole batch of them; names taken off; the queue tidied —
|
|
1178
|
+
* each of the last UNDO_KEPT once): each place as it was where nothing changed it since, the facts' places back where
|
|
1179
|
+
* still so, the queue's entries back. Inside the tree's lock.
|
|
1078
1180
|
*/
|
|
1079
1181
|
export function undoPlaces(tree, id) {
|
|
1080
1182
|
return tree.withTreeLock(() => {
|
|
1081
1183
|
const file = readPlacesFile(tree);
|
|
1082
|
-
const u = file.undo;
|
|
1083
|
-
if (!u
|
|
1084
|
-
throw new UsageError(
|
|
1184
|
+
const u = file.undo?.id === id ? file.undo : file.undos?.find((x) => x.id === id);
|
|
1185
|
+
if (!u)
|
|
1186
|
+
throw new UsageError(`nothing to undo of that step (each step on places once, the last ${UNDO_KEPT})`, { code: "places.undo.later" });
|
|
1085
1187
|
const restored = [];
|
|
1086
1188
|
const left = [];
|
|
1087
1189
|
const records = [];
|
|
@@ -1096,13 +1198,42 @@ export function undoPlaces(tree, id) {
|
|
|
1096
1198
|
restored.push(pid);
|
|
1097
1199
|
}
|
|
1098
1200
|
if (left.length)
|
|
1099
|
-
throw new UsageError(`changed since: ${left.join(", ")} — the step is not undone
|
|
1201
|
+
throw new UsageError(`changed since: ${left.join(", ")} — the step is not undone (a later step on the same place is undone first)`, {
|
|
1202
|
+
code: "places.undo.later",
|
|
1203
|
+
params: { places: left.join(", ") },
|
|
1204
|
+
});
|
|
1205
|
+
// an earlier step still to be undone that left a place as this undo puts it back: it knows the place by its new time
|
|
1206
|
+
for (const r of records) {
|
|
1207
|
+
const was = u.places[r.id].before.updated;
|
|
1208
|
+
for (const x of [file.undo, ...(file.undos ?? [])])
|
|
1209
|
+
if (x && x !== u && x.places[r.id]?.after === was)
|
|
1210
|
+
x.places[r.id] = { ...x.places[r.id], after: r.updated };
|
|
1211
|
+
}
|
|
1100
1212
|
const back = applyChanges(tree, u.facts ?? [], "after");
|
|
1101
|
-
|
|
1213
|
+
// the queue's entries the step took out: back, and kept there (never taken out by the tidying again)
|
|
1214
|
+
let queued = 0;
|
|
1215
|
+
for (const [k, e] of Object.entries(u.queue ?? {})) {
|
|
1216
|
+
if (file.queue[k])
|
|
1217
|
+
continue;
|
|
1218
|
+
file.queue[k] = { ...e, back: true };
|
|
1219
|
+
queued++;
|
|
1220
|
+
}
|
|
1221
|
+
// out of the steps to be undone; the one before it the newest again
|
|
1222
|
+
if (file.undo === u) {
|
|
1223
|
+
const before = file.undos?.pop();
|
|
1224
|
+
if (before)
|
|
1225
|
+
file.undo = before;
|
|
1226
|
+
else
|
|
1227
|
+
delete file.undo;
|
|
1228
|
+
}
|
|
1229
|
+
else
|
|
1230
|
+
file.undos = file.undos.filter((x) => x !== u);
|
|
1231
|
+
if (!file.undos?.length)
|
|
1232
|
+
delete file.undos;
|
|
1102
1233
|
write(tree, [...records, ...back.records], file, {
|
|
1103
1234
|
op: "place.undo",
|
|
1104
1235
|
targets: [...restored, ...back.records.map((r) => r.id)].sort(),
|
|
1105
|
-
summary: `
|
|
1236
|
+
summary: `a step on places undone (${u.id}): ${restored.length} places${back.done.length ? `, ${back.done.length} facts' places back` : ""}${queued ? `, ${queued} back in the queue` : ""}`,
|
|
1106
1237
|
});
|
|
1107
1238
|
return { restored, left };
|
|
1108
1239
|
});
|
|
@@ -1160,6 +1291,8 @@ export function raisePlacesTask(tree, archive) {
|
|
|
1160
1291
|
if (tree.dryRun || archive)
|
|
1161
1292
|
return undefined;
|
|
1162
1293
|
return tree.withTreeLock(() => {
|
|
1294
|
+
// what an earlier version left in the queue of places dealt with already: out first, so the count is right
|
|
1295
|
+
settleQueue(tree);
|
|
1163
1296
|
const file = readPlacesFile(tree);
|
|
1164
1297
|
const forms = factForms(tree);
|
|
1165
1298
|
const recordOf = recordIndex(tree);
|
|
@@ -1220,6 +1353,55 @@ export function raisePlacesTask(tree, archive) {
|
|
|
1220
1353
|
return task.id;
|
|
1221
1354
|
});
|
|
1222
1355
|
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Names of a place record found wrong (a slip of a record kept as a form, another place's name) taken off, with the
|
|
1358
|
+
* reason: never today's name (the first), never a name a fact of the tree still writes for this place (set the facts
|
|
1359
|
+
* right first: strom place rewrite). One operation; while the person has no step to undo, `strom place undo` puts them
|
|
1360
|
+
* back. Inside the tree's lock.
|
|
1361
|
+
*/
|
|
1362
|
+
export function dropPlaceNames(tree, ref, names, reason) {
|
|
1363
|
+
return tree.withTreeLock(() => {
|
|
1364
|
+
const id = placeIdOf(ref);
|
|
1365
|
+
const was = id ? tree.get(id) : undefined;
|
|
1366
|
+
if (!was || was.type !== "place" || was.retracted)
|
|
1367
|
+
throw new UsageError(`no place ${ref}: a place ID (L0012)`, { code: "place.unknown", hint: "strom place list" });
|
|
1368
|
+
const why = text(reason, 1000, "--why");
|
|
1369
|
+
if (!why)
|
|
1370
|
+
throw new UsageError('why the name is wrong: --why "…"', { code: "place.why" });
|
|
1371
|
+
if (!names.length)
|
|
1372
|
+
throw new UsageError('which name: --drop "<name>"', { code: "place.name" });
|
|
1373
|
+
const p = structuredClone(was);
|
|
1374
|
+
const forms = factForms(tree);
|
|
1375
|
+
const recordOf = recordIndex(tree);
|
|
1376
|
+
const dropped = [];
|
|
1377
|
+
for (const raw of names) {
|
|
1378
|
+
const n = raw.normalize("NFC").trim();
|
|
1379
|
+
const key = foldText(n);
|
|
1380
|
+
// the very writing first, else any of the same letters (any accent)
|
|
1381
|
+
const exact = p.names.findIndex((x) => x.name === n);
|
|
1382
|
+
const hit = exact >= 0 ? exact : p.names.findIndex((x) => foldText(x.name) === key);
|
|
1383
|
+
if (hit < 0)
|
|
1384
|
+
throw new UsageError(`${p.id} has no name "${n}" (it has ${p.names.map((x) => x.name).join(", ")})`, { code: "place.name" });
|
|
1385
|
+
if (hit === 0)
|
|
1386
|
+
throw new UsageError(`"${n}" is the first name of ${p.id} — today's name is set with --name (strom place verify ${p.id} --ok --name "…")`, { code: "place.name" });
|
|
1387
|
+
const f = forms.get(foldText(p.names[hit].name));
|
|
1388
|
+
if (f && recordOf(f.form)?.id === p.id && !p.names.some((x, i) => i !== hit && foldText(x.name) === foldText(p.names[hit].name)))
|
|
1389
|
+
throw new UsageError(`${f.facts === 1 ? "a fact" : `${f.facts} facts`} of the tree still write "${f.form}" — set them right first: strom place rewrite "${f.form}" --to ${p.id} --reason "…"`, { code: "place.form" });
|
|
1390
|
+
dropped.push(p.names[hit]);
|
|
1391
|
+
p.names.splice(hit, 1);
|
|
1392
|
+
}
|
|
1393
|
+
p.updated = now();
|
|
1394
|
+
const file = readPlacesFile(tree);
|
|
1395
|
+
const undo = keepUndo(file, { id: undoId(file), at: now(), places: { [p.id]: { before: was, after: p.updated } } });
|
|
1396
|
+
write(tree, [p], file, {
|
|
1397
|
+
op: "place.names",
|
|
1398
|
+
targets: [p.id],
|
|
1399
|
+
reason: why,
|
|
1400
|
+
summary: `${p.id} ${p.names[0].name}: name${dropped.length === 1 ? "" : "s"} taken off as wrong: ${dropped.map((x) => `${x.name}${x.lang ? `@${x.lang}` : ""}`).join(", ")} — ${why.slice(0, 80)} — undo: strom place undo ${undo}`,
|
|
1401
|
+
});
|
|
1402
|
+
return { place: p, dropped, undo };
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1223
1405
|
/**
|
|
1224
1406
|
* The other names history's map is told of a place (strom history map --also) written among its record's names too —
|
|
1225
1407
|
* the record is where the app and the identification read them. Nothing where the facts' place has no one record.
|
|
@@ -1361,19 +1543,17 @@ export function repairPlaces(tree) {
|
|
|
1361
1543
|
if (!records.length)
|
|
1362
1544
|
return [];
|
|
1363
1545
|
file.repaired = PLACES_REPAIR;
|
|
1364
|
-
|
|
1365
|
-
file
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
};
|
|
1370
|
-
}
|
|
1546
|
+
const undo = keepUndo(file, {
|
|
1547
|
+
id: undoId(file),
|
|
1548
|
+
at: now(),
|
|
1549
|
+
places: Object.fromEntries(records.map((p) => [p.id, { before: tree.get(p.id), after: p.updated }])),
|
|
1550
|
+
});
|
|
1371
1551
|
write(tree, records, file, {
|
|
1372
1552
|
op: "place.repair",
|
|
1373
1553
|
targets: records.map((p) => p.id).sort(),
|
|
1374
1554
|
// a name read from its syntax, or written twice, is no name lost: the data check takes the step as reasoned
|
|
1375
1555
|
reason: "what an earlier version wrote into the place records set right, nothing lost (undo kept)",
|
|
1376
|
-
summary: `place records set right (${records.length}): ${said.join(" · ").slice(0, 1500)}
|
|
1556
|
+
summary: `place records set right (${records.length}): ${said.join(" · ").slice(0, 1500)} — undo: strom place undo ${undo}`,
|
|
1377
1557
|
});
|
|
1378
1558
|
return said;
|
|
1379
1559
|
}
|
|
@@ -1423,11 +1603,15 @@ export function keepPlacesTask(tree) {
|
|
|
1423
1603
|
tree.withTreeLock(() => repairPlaces(tree));
|
|
1424
1604
|
return raisePlacesTask(tree, false);
|
|
1425
1605
|
}
|
|
1426
|
-
/** The jurisdiction of "<kind>:<name>[:from-to]". */
|
|
1606
|
+
/** The jurisdiction of "<kind>:<name>[:from-to]" — one year (":1899"): known so in that year, from and to alike. */
|
|
1427
1607
|
export function parseJurisdiction(s) {
|
|
1428
|
-
const m = /^([^:]+):([^:]+?)(?::(\d{3,4})?-(\d{3,4})
|
|
1608
|
+
const m = /^([^:]+):([^:]+?)(?::(?:(\d{3,4})?-(\d{3,4})?|(\d{3,4})))?$/u.exec(s.normalize("NFC").trim());
|
|
1429
1609
|
const kind = m?.[1]?.trim();
|
|
1430
1610
|
if (!m || !kind || !JURISDICTIONS.includes(kind))
|
|
1431
|
-
throw new UsageError(`--jurisdiction "${s}": "<kind>:<name>[:from-to]", kind one of ${JURISDICTIONS.join(", ")}`, { code: "place.jurisdiction" });
|
|
1432
|
-
|
|
1611
|
+
throw new UsageError(`--jurisdiction "${s}": "<kind>:<name>[:from-to]" or one year "<kind>:<name>:<year>", kind one of ${JURISDICTIONS.join(", ")}`, { code: "place.jurisdiction" });
|
|
1612
|
+
const from = m[3] ?? m[5];
|
|
1613
|
+
const to = m[4] ?? m[5];
|
|
1614
|
+
if (from && to && Number(from) > Number(to))
|
|
1615
|
+
throw new UsageError(`--jurisdiction "${s}": the years ${from}-${to} go backwards`, { code: "place.jurisdiction" });
|
|
1616
|
+
return { kind: kind, name: m[2].trim(), recordsets: [], ...(from ? { from: Number(from) } : {}), ...(to ? { to: Number(to) } : {}) };
|
|
1433
1617
|
}
|
package/dist/core/placesview.js
CHANGED
|
@@ -60,6 +60,7 @@ function view(k, p) {
|
|
|
60
60
|
...(why ? { why } : {}),
|
|
61
61
|
...(loc?.source ? { source: loc.source } : {}),
|
|
62
62
|
...(loc?.verified ? { verified: loc.verified } : {}),
|
|
63
|
+
...(loc?.verifiedSource ? { verifiedSource: loc.verifiedSource } : {}),
|
|
63
64
|
...(loc?.checkWhy ? { checkWhy: loc.checkWhy } : {}),
|
|
64
65
|
...(!p.coords && p.unlocated && loc?.needs ? { needs: loc.needs } : {}),
|
|
65
66
|
...(q && (state === "proposed" || state === "conflict") ? { waiting: p.id } : {}),
|
package/dist/core/schema.js
CHANGED
|
@@ -78,7 +78,9 @@ export const SCHEMAS = {
|
|
|
78
78
|
source: { t: "object", of: { title: S({ req: true, max: 300 }), url: S({ max: 2000 }) } },
|
|
79
79
|
verified: S(),
|
|
80
80
|
verifiedWhy: S({ max: 1000 }),
|
|
81
|
+
verifiedSource: { t: "object", of: { title: S({ req: true, max: 300 }), url: S({ max: 2000 }) } },
|
|
81
82
|
checkWhy: S({ max: 1000 }),
|
|
83
|
+
checked: S(),
|
|
82
84
|
needs: S({ max: 300 }),
|
|
83
85
|
context: { t: "number", int: true, min: 0 },
|
|
84
86
|
kept: { t: "object", of: { lat: { t: "number", req: true, min: -90, max: 90 }, lon: { t: "number", req: true, min: -180, max: 180 } } },
|
package/dist/core/tree.js
CHANGED
|
@@ -31,7 +31,7 @@ export const TREE_FILE = "strom.json";
|
|
|
31
31
|
export const SEAL_FOREIGN = "seal.foreign";
|
|
32
32
|
// between releases the candidate being made (a bridge run from the sources says what it is: the Strom app goes by it);
|
|
33
33
|
// at a release the release's own (package.json)
|
|
34
|
-
export const VERSION = "1.26.0-beta.
|
|
34
|
+
export const VERSION = "1.26.0-beta.2";
|
|
35
35
|
const GITIGNORE = `# Strom Research — local state and large files are not versioned
|
|
36
36
|
.strom/
|
|
37
37
|
/media/
|