Skip to content

Commit 1efdbab

Browse files
cubehouseclaude
andauthored
fix(europapark): fall back to analyticsName for empty-name shows (#226)
Some shows publish an empty public `name` with the title only in `analyticsName` (e.g. Rulantica show 133 'TALENT ACADEMY on Stage', status preview). getParkEntities()'s `if (!poi.name) return` guard dropped them before they became entities, so they never reached the collector's moderation queue. Status was never the gate — preview/draft shows with a name are already ingested; an empty name was the only block. Resolve an effective name as `poi.name || poi.analyticsName` and skip only when both are empty. Surfaces 1 entity in today's live feed. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4236b39 commit 1efdbab

2 files changed

Lines changed: 65 additions & 4 deletions

File tree

src/parks/europapark/__tests__/europapark.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,56 @@ describe('_isWaitsGlitch', () => {
269269
expect(probe.probe(waits, entities)).toBe(true);
270270
});
271271
});
272+
273+
// ── Show name resolution ────────────────────────────────────────────────────
274+
// Some upstream shows (e.g. Rulantica show 133 "TALENT ACADEMY on Stage")
275+
// arrive with an empty public `name` while the title sits in `analyticsName`.
276+
// Without a fallback they are dropped by the `if (!name) return` guard and
277+
// never reach the entity list / moderation queue.
278+
class EntityProbe extends EuropaPark {
279+
private readonly _pois: any[];
280+
constructor(pois: any[]) {
281+
super();
282+
this._pois = pois;
283+
}
284+
override async getPOIs(): Promise<any> {
285+
return this._pois;
286+
}
287+
}
288+
289+
describe('getParkEntities show name fallback', () => {
290+
const pois = [
291+
{
292+
id: 747,
293+
type: 'showlocation',
294+
name: 'Stage at Skip Strand',
295+
scopes: ['rulantica'],
296+
latitude: 48.26,
297+
longitude: 7.74,
298+
shows: [
299+
{id: 133, name: '', analyticsName: 'TALENT ACADEMY on Stage'},
300+
{id: 134, name: 'The secret of the vikings', analyticsName: 'internal label'},
301+
{id: 135, name: '', analyticsName: ''},
302+
],
303+
},
304+
];
305+
306+
test('falls back to analyticsName when a show has an empty name', async () => {
307+
const entities = await new EntityProbe(pois).getParkEntities();
308+
const show133 = entities.find((e) => e.id === 'shows_133');
309+
expect(show133).toBeDefined();
310+
expect(show133!.name).toBe('TALENT ACADEMY on Stage');
311+
expect(show133!.entityType).toBe('SHOW');
312+
});
313+
314+
test('keeps the public name when one is present', async () => {
315+
const entities = await new EntityProbe(pois).getParkEntities();
316+
const show134 = entities.find((e) => e.id === 'shows_134');
317+
expect(show134!.name).toBe('The secret of the vikings');
318+
});
319+
320+
test('still skips a show with neither name nor analyticsName', async () => {
321+
const entities = await new EntityProbe(pois).getParkEntities();
322+
expect(entities.find((e) => e.id === 'shows_135')).toBeUndefined();
323+
});
324+
});

src/parks/europapark/europapark.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import {TagBuilder} from '../../tags/index.js';
1919
type EuropaParkPOI = {
2020
id: number;
2121
name: string;
22+
// Internal label; sometimes the only place a not-yet-published show's title
23+
// lives while its public `name` is still empty (e.g. Rulantica show 133).
24+
analyticsName?: string;
2225
type: string;
2326
subtype?: string;
2427
queueing?: boolean;
@@ -38,6 +41,7 @@ type EuropaParkPOI = {
3841
type EuropaParkShow = {
3942
id: number;
4043
name: string;
44+
analyticsName?: string;
4145
duration?: number;
4246
code?: number;
4347
latitude?: number;
@@ -351,7 +355,11 @@ class EuropaParkBase extends Destination {
351355
const entities: EuropaParkEntity[] = [];
352356

353357
const addPoiData = (poi: EuropaParkPOI & {entityType?: string}): void => {
354-
if (!poi.name) return;
358+
// Some shows (e.g. Rulantica 133 "TALENT ACADEMY on Stage") publish an
359+
// empty public `name` with the title only in `analyticsName`. Fall back
360+
// to it so the entity still surfaces; skip only when both are empty.
361+
const name = poi.name || poi.analyticsName;
362+
if (!name) return;
355363

356364
const poiEntityTypes = ['attraction', 'showlocation', 'shows', 'pois'];
357365
const entityType = poi.entityType ?? poi.type;
@@ -380,7 +388,7 @@ class EuropaParkBase extends Destination {
380388
if (poi.queueing) return;
381389

382390
// Skip queue map pointers
383-
if (poi.name.indexOf('Queue - ') === 0) return;
391+
if (name.indexOf('Queue - ') === 0) return;
384392

385393
// Map old vs new entity type strings to id prefix
386394
let idPrefix: string;
@@ -396,14 +404,14 @@ class EuropaParkBase extends Destination {
396404
(entityType === 'shows' || entityType === 'showlocation') ? 'SHOW' : 'ATTRACTION';
397405

398406
// Look for a virtual-queue companion (queueing:true, name contains this ride's name)
399-
const nameLower = poi.name.toLowerCase();
407+
const nameLower = name.toLowerCase();
400408
const vQueueData = poiData.find((x) => {
401409
return x.queueing === true && x.name.toLowerCase().indexOf(nameLower) > 0;
402410
});
403411

404412
entities.push({
405413
id: `${idPrefix}_${poi.id}`,
406-
name: poi.name,
414+
name,
407415
entityType: finalEntityType,
408416
scopes: poi.scopes || [],
409417
code: poi.code,

0 commit comments

Comments
 (0)