diff --git a/scripts/lib/speaker-matcher.js b/scripts/lib/speaker-matcher.js new file mode 100644 index 0000000..5125857 --- /dev/null +++ b/scripts/lib/speaker-matcher.js @@ -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 }; diff --git a/scripts/test/speaker-matcher.test.js b/scripts/test/speaker-matcher.test.js new file mode 100644 index 0000000..4b2b3ee --- /dev/null +++ b/scripts/test/speaker-matcher.test.js @@ -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]]')); + }); +});