diff --git a/.claude/settings.local.json b/.claude/settings.local.json index e77944f..bcd46b8 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -1,7 +1,10 @@ { "permissions": { "allow": [ - "WebSearch" + "WebSearch", + "mcp__context7__resolve-library-id", + "mcp__context7__get-library-docs", + "Bash(npm test)" ], "deny": [], "ask": [] diff --git a/events.yml b/events.yml index 33e65dc..a0327bf 100644 --- a/events.yml +++ b/events.yml @@ -28,6 +28,7 @@ prod: titlePrefix: '#Brekkie' showEditLink: true - schedule: + enabled: false rate: cron(0 7 ? * FRI *) name: 'coles-dinner' description: 'Coles Dinner' @@ -41,6 +42,7 @@ prod: titlePrefix: '#Dinner' showEditLink: true - schedule: + enabled: false rate: cron(0 7 1-7 * ? *) name: 'something-different-1' description: 'Something Different 1' @@ -54,6 +56,7 @@ prod: titlePrefix: '#Dinner' showEditLink: true - schedule: + enabled: false rate: cron(0 7 15-21 * ? *) name: 'something-different-2' description: 'Something Different 2' @@ -80,6 +83,7 @@ prod: titlePrefix: '#JamaicanDish' showEditLink: true - schedule: + enabled: false rate: cron(0 7 22-28 * ? *) name: 'jamaican-dish-2' description: 'Jamaican Dish 2' @@ -147,7 +151,7 @@ prod: titlePrefix: '#CoupleSkills' - schedule: enabled: true - rate: cron(0 10 ? * 2/2 *) + rate: cron(0 10 ? * FRI-SAT *) name: 'PillowTalk' description: 'Pillow Talk' input: @@ -229,7 +233,7 @@ prod: titlePrefix: '#BetterSelf' showEditLink: true - schedule: - rate: cron(0 10 ? * SUN-THUR *) + rate: cron(0 7 ? * MON-FRI *) name: 'notifyer-mantras' description: 'Mantras | Sydney | AEDT' input: @@ -243,7 +247,7 @@ prod: titlePrefix: '#mantras' showEditLink: true - schedule: - rate: cron(0 10 ? * SUN-THUR *) + rate: cron(0 7 ? * SAT,SUN *) name: 'notifyer-quotes' description: 'Quotes | Sydney | AEDT' input: diff --git a/lib/onenote.js b/lib/onenote.js index d948901..22f6e83 100644 --- a/lib/onenote.js +++ b/lib/onenote.js @@ -4,7 +4,24 @@ const GraphOneNoteService = require('./graph-onenote-service'); const localStorage = require('./store'); const Chance = require('chance'); -const chance = new Chance(); + +/** + * Calculate day of year with leap year support + * @param {Date} date - Date to calculate from (defaults to today) + * @returns {number} Day of year (1-366 for leap years) + */ +function getDayOfYear(date = new Date()) { + const start = new Date(date.getFullYear(), 0, 1); + const diff = date - start; + const oneDay = 1000 * 60 * 60 * 24; + return Math.floor(diff / oneDay) + 1; +} + +/** + * seed chance with the day of the year for deterministic randomization + * that hopefully better distributes randomness across old and new notes in a section + */ +const chance = (process.env.NODE_ENV === 'production') ? new Chance(getDayOfYear()) : new Chance(); const graphService = new GraphOneNoteService(); @@ -142,7 +159,7 @@ async function getNotePreview(note) { try { const response = await graphService.getPagePreview(id) - + return { title, preview: response, @@ -165,10 +182,10 @@ async function getNoteContents(url) { try { const pageId = extractPageIdFromUrl(url) const response = await graphService.getPageContent(pageId) - + // Log page ID for debugging console.log('Retrieved content for page ID:', pageId) - + return response } catch (error) { console.error('getNoteContents', error) @@ -191,13 +208,13 @@ function extractPageIdFromUrl(url) { */ function logRecentNote(note, sectionHandle) { let notes = localStorage.getItem(`recent_${sectionHandle}`, true) || [] - + // Ensure notes is always an array if (!Array.isArray(notes)) { console.warn(`Expected array for recent_${sectionHandle}, got:`, typeof notes, notes) notes = [] } - + const recent = Number(process.env.RECENT_NOTE_LENGTH) || 7 console.log('recent', recent) if (notes.length === recent) { @@ -248,7 +265,7 @@ function extractFirstImage(htmlContent) { width: firstImg.getAttribute('width') || null, height: firstImg.getAttribute('height') || null } - + return result } @@ -302,5 +319,5 @@ module.exports = { setNoteSection, extractFirstImage, downloadImage, - getImageSize + getImageSize, } diff --git a/tests/onenote.test.js b/tests/onenote.test.js new file mode 100644 index 0000000..c076142 --- /dev/null +++ b/tests/onenote.test.js @@ -0,0 +1,69 @@ +const Chance = require('chance'); + +// Import the getDayOfYear function from the actual module +const { getDayOfYear } = require('../lib/onenote'); + +describe('getDayOfYear function', () => { + // Test regular year + test('calculates correct day of year for a non-leap year', () => { + const testDate = new Date(2023, 0, 15); // January 15, 2023 + expect(getDayOfYear(testDate)).toBe(15); + }); + + // Test leap year + test('calculates correct day of year for a leap year', () => { + const testDate = new Date(2024, 1, 29); // February 29, 2024 (leap year) + expect(getDayOfYear(testDate)).toBe(60); + }); + + // Test edge cases + test('first day of the year returns 1', () => { + const testDate = new Date(2023, 0, 1); + expect(getDayOfYear(testDate)).toBe(1); + }); + + test('last day of a non-leap year returns 365', () => { + const testDate = new Date(2023, 11, 31); + expect(getDayOfYear(testDate)).toBe(365); + }); + + test('last day of a leap year returns 366', () => { + const testDate = new Date(2024, 11, 31); + expect(getDayOfYear(testDate)).toBe(366); + }); +}); + +describe('Seeded Chance Randomness', () => { + test('same seed produces same random results', () => { + const seed = getDayOfYear(new Date('2023-01-15')); + const chance1 = new Chance(seed); + const chance2 = new Chance(seed); + + // Test multiple random generations + for (let i = 0; i < 10; i++) { + expect(chance1.natural()).toBe(chance2.natural()); + expect(chance1.pickone([1, 2, 3])).toBe(chance2.pickone([1, 2, 3])); + } + }); + + test('different dates produce different seeds', () => { + const chance1 = new Chance(getDayOfYear(new Date('2023-01-15'))); + const chance2 = new Chance(getDayOfYear(new Date('2023-02-15'))); + + // While not guaranteed, the probability of these being exactly the same + // for multiple generations is extremely low + const results1 = new Set(); + const results2 = new Set(); + + for (let i = 0; i < 100; i++) { + results1.add(chance1.natural()); + results2.add(chance2.natural()); + } + + expect(results1.size).toBeGreaterThan(1); + expect(results2.size).toBeGreaterThan(1); + + // Ensure they're different sets + expect(Array.from(results1)).not.toEqual(Array.from(results2)); + }); +}); \ No newline at end of file