feat(scripts): add backfill-series-id script for jour-fixe agendas
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
15499cf190
commit
0679aa013f
|
|
@ -0,0 +1,55 @@
|
|||
// scripts/backfill-series-id.js
|
||||
const { readFileSync, writeFileSync } = require('node:fs');
|
||||
const { resolve } = require('node:path');
|
||||
const { getCalendarEvents } = require('./lib/o365-calendar.js');
|
||||
|
||||
function upsertFrontmatterField(md, key, value) {
|
||||
const fmMatch = md.match(/^---\n([\s\S]*?)\n---/);
|
||||
if (!fmMatch) return md;
|
||||
const fm = fmMatch[1];
|
||||
const lineRegex = new RegExp(`^${key}:.*$`, 'm');
|
||||
let newFm;
|
||||
if (lineRegex.test(fm)) {
|
||||
newFm = fm.replace(lineRegex, `${key}: ${value}`);
|
||||
} else {
|
||||
newFm = `${fm}\n${key}: ${value}`;
|
||||
}
|
||||
return md.replace(/^---\n[\s\S]*?\n---/, `---\n${newFm}\n---`);
|
||||
}
|
||||
|
||||
async function backfill(agendaPath, titleRegex, daysAhead = 30) {
|
||||
const events = await getCalendarEvents(daysAhead);
|
||||
const re = new RegExp(titleRegex, 'i');
|
||||
const match = events.find(e => re.test(e.title) && e.isRecurring);
|
||||
if (!match) {
|
||||
throw new Error(`no recurring event matching /${titleRegex}/ in next ${daysAhead} days`);
|
||||
}
|
||||
// Need raw seriesMasterId — re-fetch single event by id (parseEventToMeeting drops it)
|
||||
const { getEventById } = require('./lib/o365-calendar.js');
|
||||
const raw = await getEventById(match.id);
|
||||
const seriesId = raw.seriesMasterId;
|
||||
if (!seriesId) {
|
||||
throw new Error(`event ${match.id} has no seriesMasterId`);
|
||||
}
|
||||
const md = readFileSync(agendaPath, 'utf-8');
|
||||
const updated = upsertFrontmatterField(md, 'o365_series_id', seriesId);
|
||||
writeFileSync(agendaPath, updated, 'utf-8');
|
||||
return seriesId;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2) {
|
||||
console.error('Usage: node backfill-series-id.js <agenda-path> <title-regex>');
|
||||
process.exit(2);
|
||||
}
|
||||
const [agendaPath, titleRegex] = args;
|
||||
const id = await backfill(resolve(agendaPath), titleRegex);
|
||||
console.log(`set o365_series_id=${id} in ${agendaPath}`);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch(e => { console.error(e.stack || e.message); process.exit(1); });
|
||||
}
|
||||
|
||||
module.exports = { upsertFrontmatterField, backfill };
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// scripts/test/backfill-series-id.test.js
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { upsertFrontmatterField } = require('../backfill-series-id.js');
|
||||
|
||||
describe('upsertFrontmatterField', () => {
|
||||
it('inserts new field when missing', () => {
|
||||
const md = `---\ntags: [jour-fixe]\nserie: IT Team\nrhythmus: wöchentlich\n---\n\n# Agenda\n`;
|
||||
const out = upsertFrontmatterField(md, 'o365_series_id', 'AAMkSeries123');
|
||||
assert.ok(out.includes('o365_series_id: AAMkSeries123'));
|
||||
assert.ok(out.includes('serie: IT Team'));
|
||||
assert.ok(out.endsWith('# Agenda\n'));
|
||||
});
|
||||
|
||||
it('replaces existing field value', () => {
|
||||
const md = `---\nserie: X\no365_series_id: OLD\n---\n\nbody\n`;
|
||||
const out = upsertFrontmatterField(md, 'o365_series_id', 'NEW');
|
||||
assert.ok(out.includes('o365_series_id: NEW'));
|
||||
assert.ok(!out.includes('OLD'));
|
||||
});
|
||||
|
||||
it('returns content unchanged when no frontmatter present', () => {
|
||||
const md = `# No frontmatter\n`;
|
||||
const out = upsertFrontmatterField(md, 'o365_series_id', 'X');
|
||||
assert.equal(out, md);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue