From 9ead6bde4cef265f624ee06d81e3ff5225b86445 Mon Sep 17 00:00:00 2001 From: Vamshi Surabhi Date: Sun, 27 Oct 2024 21:18:16 +0530 Subject: [PATCH 1/5] event attendees in a separate table --- .../src/services/google-calendar.ts | 81 +++++++++++++++---- 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/ndc-duckduckapi/src/services/google-calendar.ts b/ndc-duckduckapi/src/services/google-calendar.ts index 94c8bf8..a80ce39 100644 --- a/ndc-duckduckapi/src/services/google-calendar.ts +++ b/ndc-duckduckapi/src/services/google-calendar.ts @@ -45,11 +45,10 @@ CREATE TABLE IF NOT EXISTS calendar_events ( status VARCHAR, location VARCHAR, recurring_event_id VARCHAR, - recurrence JSON, + recurrence JSON, transparency VARCHAR, visibility VARCHAR, ical_uid VARCHAR, - attendees JSON, reminders JSON, conference_data JSON, color_id VARCHAR, @@ -66,6 +65,23 @@ CREATE TABLE IF NOT EXISTS calendar_events ( is_all_day BOOLEAN ); +CREATE TABLE IF NOT EXISTS calendar_attendees ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + event_id VARCHAR, + email VARCHAR, + display_name VARCHAR, + organizer BOOLEAN, + self BOOLEAN, + resource BOOLEAN, + optional BOOLEAN, + response_status VARCHAR, + comment TEXT, + additional_guests INTEGER, + FOREIGN KEY (event_id) REFERENCES calendar_events(id) ON DELETE CASCADE +); + +COMMENT ON TABLE calendar_attendees IS 'This table contains attendees of a calendar event'; + COMMENT ON TABLE calendar_events IS 'This table contains events from google calendar. While querying this table keep the following in mind. 1) The title of the event is stored in the summary field. 2) typically add a filter to remove cancelled events by checking status != ''cancelled'' '; CREATE TABLE IF NOT EXISTS sync_state ( @@ -199,7 +215,7 @@ export class SyncManager { this.auth = new OAuth2Client({ clientId: this.credentials.client_id, clientSecret: this.credentials.client_secret, - eagerRefreshThresholdMillis: 1000 // 60 seconds + eagerRefreshThresholdMillis: 1000 // 60 seconds }); } else { this.auth = new OAuth2Client(); @@ -208,16 +224,16 @@ export class SyncManager { this.auth.setCredentials({ access_token: this.credentials.access_token, refresh_token: this.credentials.refresh_token, - expiry_date: Date.now() + 3000 + expiry_date: Date.now() + 3000 // expiry_date: this.credentials.expires_in ? Date.now() + this.credentials.expires_in * 1000 : undefined }); this.calendar = google.calendar({ version: "v3", auth: this.auth }); - + this.auth.on('tokens', (tokens) => { this.saveCredentials({ - access_token: tokens?.access_token, - refresh_token: this.credentials.refresh_token, + access_token: tokens?.access_token, + refresh_token: this.credentials.refresh_token, expires_in: tokens?.expiry_date ? ((tokens.expiry_date - Date.now()) / 1000) : undefined }); console.log('GoogleCalendarLoader.Auth:: ' + 'Refreshed token saved.'); @@ -248,7 +264,7 @@ export class SyncManager { } async initialize(): Promise { - + // Test access to calendar by fetching one event this.loaderState.state = "Testing google-calendar access..."; console.log('GoogleCalendarLoader.Initialize:: ' + this.loaderState.state); @@ -259,7 +275,7 @@ export class SyncManager { console.log('GoogleCalendarLoader.Initialize:: ' + this.loaderState.state); return this.loaderState.state; } - + debugLog("Initializing sync manager..."); this.loaderState.state = "Running"; this.startPeriodicSync(); @@ -268,7 +284,7 @@ export class SyncManager { await this.cleanup(); process.exit(0); }); - + return this.loaderState.state; } @@ -346,7 +362,7 @@ export class SyncManager { this.startPeriodicSync(); } else { console.log('GoogleCalendarLoader:: ' + "Unable to process events. " + error); - throw error; + throw error; } } } @@ -355,7 +371,7 @@ export class SyncManager { events: calendar_v3.Schema$Event[], calendarId: string, ): Promise { - + for (const event of events) { const formattedEvent = this.formatEventData(event, calendarId, new Date().toISOString()); if (event.status === "cancelled") { @@ -449,7 +465,7 @@ export class SyncManager { transparency: event.transparency || null, visibility: event.visibility || null, ical_uid: event.iCalUID || "", - attendees: event.attendees ? JSON.stringify(event.attendees) : null, + attendees: event.attendees || null, reminders: event.reminders ? JSON.stringify(event.reminders) : null, conference_data: event.conferenceData ? JSON.stringify(event.conferenceData) @@ -476,6 +492,40 @@ export class SyncManager { }; } + private async insertAttendees(eventId: string, attendees: calendar_v3.Schema$EventAttendee[]): Promise { + if (!attendees || attendees.length === 0) return; + + for (const attendee of attendees) { + const stmt = ` + INSERT INTO calendar_attendees ( + event_id, + email, + display_name, + organizer, + self, + resource, + optional, + response_status, + comment, + additional_guests + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `; + + await asyncDBAll(stmt, + eventId, + attendee.email || null, + attendee.displayName || null, + attendee.organizer || false, + attendee.self || false, + attendee.resource || false, + attendee.optional || false, + attendee.responseStatus || null, + attendee.comment || null, + attendee.additionalGuests || null + ); + } + } + private async insertEventsBatch(events: CalendarEvent[]): Promise { if (events.length === 0) return; @@ -555,7 +605,10 @@ export class SyncManager { const result: any = await asyncDBAll(stmt, ...values); console.log('GoogleCalendarLoader:: ' + "Rows inserted: " + result.length); - + if (event.id && event.attendees) { + await this.insertAttendees(event.id, attendees); + } + } catch (error) { debugLog(values); console.error(`Error inserting event:`, error); From 9e56e2f0dfa374f0b4b3fd9a52791ba55271b219 Mon Sep 17 00:00:00 2001 From: Vamshi Surabhi Date: Sun, 27 Oct 2024 21:37:17 +0530 Subject: [PATCH 2/5] type fixes --- ndc-duckduckapi/src/services/google-calendar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndc-duckduckapi/src/services/google-calendar.ts b/ndc-duckduckapi/src/services/google-calendar.ts index a80ce39..f9286d5 100644 --- a/ndc-duckduckapi/src/services/google-calendar.ts +++ b/ndc-duckduckapi/src/services/google-calendar.ts @@ -115,7 +115,7 @@ interface CalendarEvent { transparency: string | null; visibility: string | null; ical_uid: string; - attendees: string | null; + attendees: calendar_v3.Schema$EventAttendee[] | null; reminders: string | null; conference_data: string | null; color_id: string | null; From 285512e8495fe2f3789c454da63410870f1dc4f1 Mon Sep 17 00:00:00 2001 From: Vamshi Surabhi Date: Sun, 27 Oct 2024 21:39:00 +0530 Subject: [PATCH 3/5] type fixes --- ndc-duckduckapi/src/services/google-calendar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndc-duckduckapi/src/services/google-calendar.ts b/ndc-duckduckapi/src/services/google-calendar.ts index f9286d5..0c11b4a 100644 --- a/ndc-duckduckapi/src/services/google-calendar.ts +++ b/ndc-duckduckapi/src/services/google-calendar.ts @@ -606,7 +606,7 @@ export class SyncManager { const result: any = await asyncDBAll(stmt, ...values); console.log('GoogleCalendarLoader:: ' + "Rows inserted: " + result.length); if (event.id && event.attendees) { - await this.insertAttendees(event.id, attendees); + await this.insertAttendees(event.id, event.attendees); } } catch (error) { From f813bb18c108390d4ddb825f67ec205829282d3c Mon Sep 17 00:00:00 2001 From: Vamshi Surabhi Date: Sun, 27 Oct 2024 22:01:00 +0530 Subject: [PATCH 4/5] more fixes --- .../src/services/google-calendar.ts | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/ndc-duckduckapi/src/services/google-calendar.ts b/ndc-duckduckapi/src/services/google-calendar.ts index 0c11b4a..8a179aa 100644 --- a/ndc-duckduckapi/src/services/google-calendar.ts +++ b/ndc-duckduckapi/src/services/google-calendar.ts @@ -65,8 +65,10 @@ CREATE TABLE IF NOT EXISTS calendar_events ( is_all_day BOOLEAN ); -CREATE TABLE IF NOT EXISTS calendar_attendees ( - id INTEGER PRIMARY KEY AUTOINCREMENT, +CREATE SEQUENCE event_attendee_id START 1; + +CREATE TABLE IF NOT EXISTS event_attendees ( + id INTEGER PRIMARY KEY DEFAULT nextval('event_attendee_id'), event_id VARCHAR, email VARCHAR, display_name VARCHAR, @@ -77,10 +79,10 @@ CREATE TABLE IF NOT EXISTS calendar_attendees ( response_status VARCHAR, comment TEXT, additional_guests INTEGER, - FOREIGN KEY (event_id) REFERENCES calendar_events(id) ON DELETE CASCADE + FOREIGN KEY (event_id) REFERENCES calendar_events(id) ); -COMMENT ON TABLE calendar_attendees IS 'This table contains attendees of a calendar event'; +COMMENT ON TABLE event_attendees IS 'This table contains attendees of a calendar event'; COMMENT ON TABLE calendar_events IS 'This table contains events from google calendar. While querying this table keep the following in mind. 1) The title of the event is stored in the summary field. 2) typically add a filter to remove cancelled events by checking status != ''cancelled'' '; @@ -387,6 +389,8 @@ export class SyncManager { else { // Delete this single event try { + const attendees = await asyncDBAll("DELETE FROM event_attendees WHERE event_id = ?", event.id); + debugLog("Deleted event attendees: " + JSON.stringify(attendees)); const result = await asyncDBAll("DELETE FROM calendar_events WHERE id = ?", event.id); debugLog("Deleted event: " + JSON.stringify(result)); } catch (error) { @@ -497,7 +501,7 @@ export class SyncManager { for (const attendee of attendees) { const stmt = ` - INSERT INTO calendar_attendees ( + INSERT INTO event_attendees ( event_id, email, display_name, @@ -623,6 +627,14 @@ export class SyncManager { try { await asyncDBRun("BEGIN TRANSACTION"); + // First delete attendees for all events in this calendar + const clearAttendees: any = await asyncDBAll(` + DELETE FROM event_attendees + WHERE event_id IN ( + SELECT id FROM calendar_events WHERE calendar_id = ? + )`, calendarId); + console.log('GoogleCalendarLoader:: ' + "Deleted attendees before full sync: " + clearAttendees.length); + const clearEvents: any = await asyncDBAll("DELETE FROM calendar_events WHERE calendar_id = ?", calendarId); console.log('GoogleCalendarLoader:: ' + "Truncated rows before full sync: " + clearEvents.length); From 5cb3646b4f0d8b29efb7604224b5a95932434978 Mon Sep 17 00:00:00 2001 From: Vamshi Surabhi Date: Sun, 27 Oct 2024 23:13:13 +0530 Subject: [PATCH 5/5] fixes --- ndc-duckduckapi/src/services/google-calendar.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ndc-duckduckapi/src/services/google-calendar.ts b/ndc-duckduckapi/src/services/google-calendar.ts index 8a179aa..6dab4a3 100644 --- a/ndc-duckduckapi/src/services/google-calendar.ts +++ b/ndc-duckduckapi/src/services/google-calendar.ts @@ -65,7 +65,7 @@ CREATE TABLE IF NOT EXISTS calendar_events ( is_all_day BOOLEAN ); -CREATE SEQUENCE event_attendee_id START 1; +CREATE SEQUENCE IF NOT EXISTS event_attendee_id START 1; CREATE TABLE IF NOT EXISTS event_attendees ( id INTEGER PRIMARY KEY DEFAULT nextval('event_attendee_id'), @@ -537,7 +537,7 @@ export class SyncManager { for (const event of events) { try { // Create placeholders only for this chunk - const placeholders = Array(31).fill("?").join(","); + const placeholders = Array(30).fill("?").join(","); const stmt = ` INSERT OR REPLACE INTO calendar_events ( id, @@ -556,7 +556,6 @@ export class SyncManager { transparency, visibility, ical_uid, - attendees, reminders, conference_data, color_id, @@ -590,7 +589,6 @@ export class SyncManager { event.transparency, event.visibility, event.ical_uid, - event.attendees, event.reminders, event.conference_data, event.color_id,