openvibers/openvibe.tours Commits

Stored Wikivoyage places follow the parser; {{unesco}} and rail badges keep their words (#45)

OP OpenVibers committed 11 days ago · 074e596d38 · 4 files, +17 −6

A scan of every city summary and place description found text the parser had cut: Suzhou's
"gardens form a." ({{unesco}}), "the and Lines" ({{rint|losangeles|b}}), and a place stored
before unit templates were read ("Covering , the court complex", {{acre|107}}). {{unesco}} reads
"UNESCO World Heritage Site", a rail badge its letter, and the places job writes a Wikivoyage place
it already has again, so stored text follows the parser (its version, wv-text-1, makes the next
boot do it).

Changes

ddiff --git a/server/jobs/places.js b/server/jobs/places.js
iindex 52af7cc..92a2785 100644
---- a/server/jobs/places.js
++++ b/server/jobs/places.js
@@@ -14,9 +14,9 @@ const { createOverpass } = require('../sources/osm');
const { parseCityPage } = require('../sources/wikivoyage');
const { stableId } = require('../store');
--// A run under older rules is not fresh (jobs/index.js): name-en-1 stores OpenStreetMap's English names, so the first
--// boot with it refreshes the cities marked by migration 0004.
--const PLACES_VERSION = 'name-en-1';
++// A run under older rules is not fresh (jobs/index.js): name-en-1 stored OpenStreetMap's English names; wv-text-1
++// rewrites the stored Wikivoyage places' text with the current parser.
++const PLACES_VERSION = 'wv-text-1';
const R = 6_371_000;
function distanceM(a, b) {
@@@ -50,11 +50,16 @@ function createPlacesJob({ config, s, store, fetchImpl, log = console }) {
const name = String(listing.name || '').trim();
if (!name) continue;
const key = name.toLowerCase();
-- if (existing.some((p) => p.name.toLowerCase() === key || distanceM(p, listing) <= 120)) continue;
++ const osmId = stableId('wv', `${city.id}:${key}:${listing.lat}:${listing.lon}`);
++ // A Wikivoyage place already stored is written again, so its text follows the parser (a description stored
++ // before {{acre|107}} was read kept "Covering , the court complex", 2026-11-02); any other place near or
++ // of the same name still wins.
++ const same = existing.some((p) => p.osm_type === 'wikivoyage' && p.osm_id === osmId);
++ if (!same && existing.some((p) => p.name.toLowerCase() === key || distanceM(p, listing) <= 120)) continue;
const place = {
cityId: city.id,
osmType: 'wikivoyage',
-- osmId: stableId('wv', `${city.id}:${key}:${listing.lat}:${listing.lon}`),
++ osmId,
name,
kind: 'attraction',
lat: Number(listing.lat),
ddiff --git a/server/sources/wikivoyage.js b/server/sources/wikivoyage.js
iindex 0829d1d..8aa8262 100644
---- a/server/sources/wikivoyage.js
++++ b/server/sources/wikivoyage.js
@@@ -46,6 +46,9 @@ function inlineTemplate(inner) {
// Wikivoyage's unit templates: {{km2|82,403}} is "82,403 km²" ("…and covers ." for Chongqing, 2026-11-02), {{m|28}}
// is "28 m".
if (UNIT_TEMPLATES[name] && pos[0]) return `${number(pos[0])} ${UNIT_TEMPLATES[name]}`;
++ if (name === 'unesco') return 'UNESCO World Heritage Site';
++ // A rail line's badge ({{rint|losangeles|b}}): its letter or number, as the badge reads.
++ if ((name === 'rint' || name === 'rail-interchange') && pos[1]) return String(pos[1]).toUpperCase();
if (name === 'lang' || name === 'lang-zh') return pos[pos.length - 1] || '';
if (name === 'nowrap' || name === 'nobr' || name === 'small' || name === 'iata' || name === 'icao') return pos[0] || '';
if (name === 'w' || name === 'wp') return pos[1] || pos[0] || '';
ddiff --git a/test/place-names.test.js b/test/place-names.test.js
iindex 292021f..091bed7 100644
---- a/test/place-names.test.js
++++ b/test/place-names.test.js
@@@ -56,7 +56,7 @@ const node = (tags) => ({ type: 'node', id: 9001, lat: 29.56, lon: 106.58, tags:
const runs = await t.ctx.store.latestJobRuns();
const places = runs.find((r) => r.job === 'places');
const counts = typeof places.counts === 'string' ? JSON.parse(places.counts) : places.counts;
-- assert.strictEqual(counts.version, 'name-en-1');
++ assert.strictEqual(counts.version, require('../server/jobs/places').createPlacesJob({ config: t.config, s: t.ctx.s, store: t.ctx.store }).version);
});
await check('the migration marks a city by a name with no Latin letter, not by an accent', async () => {
ddiff --git a/test/units.test.js b/test/units.test.js
iindex 97736f5..846faf9 100644
---- a/test/units.test.js
++++ b/test/units.test.js
@@@ -19,6 +19,9 @@ check('stripWiki keeps the last character, follows links and removes templates,
assert.strictEqual(stripWiki("[[Tokyo/Asakusa|Asakusa]]'s '''Senso-ji''' is [[Buddhist]].", {}), "Asakusa's Senso-ji is Buddhist.");
assert.strictEqual(stripWiki('A {{m|28}} statue<ref>source</ref>'), 'A 28 m statue', 'a unit template keeps its measure');
assert.strictEqual(stripWiki('It covers {{km2|82,403}}.'), 'It covers 82,403 km².');
++ assert.strictEqual(stripWiki('A group of gardens form a {{unesco}}.'), 'A group of gardens form a UNESCO World Heritage Site.');
++ assert.strictEqual(stripWiki('the {{rint|losangeles|b}} and {{rint|losangeles|d}} Lines'), 'the B and D Lines');
++ assert.strictEqual(stripWiki('Covering {{acre|107}}, the court complex'), 'Covering 107 acres, the court complex');
// Inline templates that carry words are kept as text (Chongqing's lead ended "covers ." on 2026-11-02).
assert.strictEqual(stripWiki('It covers {{convert|82400|km2|sqmi}}.'), 'It covers 82,400 km².');
assert.strictEqual(stripWiki('{{convert|20|-|25|km}} away, {{convert|5|to|10|mi}}'), '20–25 km away, 5 to 10 mi');