feat(scripts): add speaker matcher with wikilink replacement
This commit is contained in:
parent
2aad5ac211
commit
f6391b0258
|
|
@ -0,0 +1,38 @@
|
|||
const { matchAttendeeToPersons, loadPersons } = require('./person-matcher.js');
|
||||
|
||||
function matchSpeakers(speakers, personsList) {
|
||||
const persons = personsList || loadPersons();
|
||||
const map = new Map();
|
||||
for (const speaker of speakers) {
|
||||
const attendee = { name: speaker.name, email: speaker.email || '' };
|
||||
const match = matchAttendeeToPersons(attendee, persons);
|
||||
if (match.matched) {
|
||||
const personName = match.file.replace('.md', '');
|
||||
map.set(speaker.name, `[[00 Kontext/Personen/${personName}]]`);
|
||||
} else {
|
||||
map.set(speaker.name, null);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
function escapeRegex(s) {
|
||||
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function replaceSpeakerNames(text, map) {
|
||||
const entries = [...map.entries()]
|
||||
.filter(([_, link]) => link !== null)
|
||||
.sort((a, b) => b[0].length - a[0].length);
|
||||
|
||||
if (entries.length === 0) return text;
|
||||
|
||||
const pattern = entries.map(([name]) => escapeRegex(name)).join('|');
|
||||
const re = new RegExp(pattern, 'g');
|
||||
|
||||
return text.replace(re, (match) => {
|
||||
return map.get(match) || match;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { matchSpeakers, replaceSpeakerNames };
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { matchSpeakers, replaceSpeakerNames } = require('../lib/speaker-matcher.js');
|
||||
|
||||
describe('matchSpeakers', () => {
|
||||
const persons = [
|
||||
{ file: 'Christian Kauer (KRAH).md', fm: { vorname: 'Christian', nachname: 'Kauer', email: 'c.kauer@krah-gruppe.de' } },
|
||||
{ file: 'Frank Herberg.md', fm: { vorname: 'Frank', nachname: 'Herberg', email: 'f.herberg@krah-gruppe.de' } },
|
||||
{ file: 'Stefan Theile-Ochel.md', fm: { vorname: 'Stefan', nachname: 'Theile-Ochel', email: 's.theile-ochel@krah-gruppe.de' } }
|
||||
];
|
||||
|
||||
it('matches by email when available', () => {
|
||||
const speakers = [{ name: 'Christian Kauer', email: 'c.kauer@krah-gruppe.de' }];
|
||||
const map = matchSpeakers(speakers, persons);
|
||||
assert.equal(map.get('Christian Kauer'), '[[00 Kontext/Personen/Christian Kauer (KRAH)]]');
|
||||
});
|
||||
|
||||
it('matches by full name when email missing', () => {
|
||||
const speakers = [{ name: 'Frank Herberg' }];
|
||||
const map = matchSpeakers(speakers, persons);
|
||||
assert.equal(map.get('Frank Herberg'), '[[00 Kontext/Personen/Frank Herberg]]');
|
||||
});
|
||||
|
||||
it('returns null wikilink for unmatched speaker', () => {
|
||||
const speakers = [{ name: 'Unknown Person' }];
|
||||
const map = matchSpeakers(speakers, persons);
|
||||
assert.equal(map.get('Unknown Person'), null);
|
||||
});
|
||||
|
||||
it('handles multiple speakers in one call', () => {
|
||||
const speakers = [
|
||||
{ name: 'Christian Kauer' },
|
||||
{ name: 'Frank Herberg' },
|
||||
{ name: 'Foo Bar' }
|
||||
];
|
||||
const map = matchSpeakers(speakers, persons);
|
||||
assert.equal(map.size, 3);
|
||||
assert.ok(map.get('Christian Kauer'));
|
||||
assert.equal(map.get('Foo Bar'), null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('replaceSpeakerNames', () => {
|
||||
it('replaces speaker names with wikilinks in text', () => {
|
||||
const map = new Map([
|
||||
['Christian Kauer', '[[00 Kontext/Personen/Christian Kauer (KRAH)]]'],
|
||||
['Frank Herberg', '[[00 Kontext/Personen/Frank Herberg]]']
|
||||
]);
|
||||
const text = 'Christian Kauer: Hallo. Frank Herberg: Hi Christian Kauer.';
|
||||
const out = replaceSpeakerNames(text, map);
|
||||
assert.ok(out.includes('[[00 Kontext/Personen/Christian Kauer (KRAH)]]'));
|
||||
assert.ok(out.includes('[[00 Kontext/Personen/Frank Herberg]]'));
|
||||
assert.ok(!out.includes('Christian Kauer:'));
|
||||
});
|
||||
|
||||
it('skips unmatched speakers (null wikilink)', () => {
|
||||
const map = new Map([['Unknown', null]]);
|
||||
const out = replaceSpeakerNames('Unknown: Hello.', map);
|
||||
assert.equal(out, 'Unknown: Hello.');
|
||||
});
|
||||
|
||||
it('does longest-name first to avoid partial overlaps', () => {
|
||||
const map = new Map([
|
||||
['Christian Kauer', '[[Christian Kauer]]'],
|
||||
['Christian', '[[Christian Hermann]]']
|
||||
]);
|
||||
const out = replaceSpeakerNames('Christian Kauer hat gesprochen.', map);
|
||||
assert.ok(out.includes('[[Christian Kauer]] hat gesprochen.'));
|
||||
assert.ok(!out.includes('[[Christian Hermann]]'));
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue