diff --git a/booking-app/app/api/safety_training_users/route.ts b/booking-app/app/api/safety_training_users/route.ts index 834e1b08..9eef0051 100644 --- a/booking-app/app/api/safety_training_users/route.ts +++ b/booking-app/app/api/safety_training_users/route.ts @@ -33,7 +33,6 @@ export async function GET(request: NextRequest) { range: range, fields: "values", }); - console.log("emails", response.data.values); const logEntry = { logName: process.env.NEXT_PUBLIC_GCP_LOG_NAME + "/safety-training", diff --git a/booking-app/app/api/syncCalendars/route.ts b/booking-app/app/api/syncCalendars/route.ts index 224d66e8..2deb47b3 100644 --- a/booking-app/app/api/syncCalendars/route.ts +++ b/booking-app/app/api/syncCalendars/route.ts @@ -1,13 +1,21 @@ -import { Booking, MediaServices } from "@/components/src/types"; - -import { NextResponse } from "next/server"; +import { toFirebaseTimestampFromString } from "@/components/src/client/utils/serverDate"; import { TableNames } from "@/components/src/policy"; -import { Timestamp } from "@firebase/firestore"; +import { Booking, MediaServices } from "@/components/src/types"; import admin from "@/lib/firebase/server/firebaseAdmin"; import { getCalendarClient } from "@/lib/googleClient"; -import { toFirebaseTimestampFromString } from "@/components/src/client/utils/serverDate"; +import { Timestamp } from "@firebase/firestore"; +import { NextResponse } from "next/server"; const db = admin.firestore(); +const areRoomIdsSame = (roomIds1: string, roomIds2: string): boolean => { + // Trim and split the room IDs, then sort both arrays + const sortedRoomIds1 = roomIds1.split(',').map(id => id.trim()).sort(); + const sortedRoomIds2 = roomIds2.split(',').map(id => id.trim()).sort(); + + // Compare the two arrays + return sortedRoomIds1.length === sortedRoomIds2.length && + sortedRoomIds1.every((id, index) => id === sortedRoomIds2[index]); +}; const createBookingWithDefaults = ( partialBooking: Partial, ): Booking => { @@ -49,17 +57,41 @@ const createBookingWithDefaults = ( ...partialBooking, }; }; -const findNyuEmail = (event: any): string => { + +const findGuestEmail = (event: any): string => { const attendees = event.attendees || []; - const nyuEmail = attendees.find( - (attendee: any) => attendee.email && attendee.email.endsWith("@nyu.edu"), + const guestEmail = attendees.find((attendee: any) => + attendee.email && !attendee.email.endsWith('@group.calendar.google.com') ); - return nyuEmail ? nyuEmail.email : ""; + return guestEmail ? guestEmail.email : ""; }; +const findRoomIds = (event: any, resources: any[]): string => { + const attendees = event.attendees || []; + const roomIds = new Set(); + + // Add the roomId of the current resource + const currentResource = resources.find(r => r.calendarId === event.organizer.email); + if (currentResource) { + roomIds.add(currentResource.roomId); + } + + // Add other room IDs + attendees.forEach((attendee: any) => { + const resource = resources.find(r => r.calendarId === attendee.email); + if (resource) { + roomIds.add(resource.roomId); + } + }); + + // Convert to array, sort numerically, and join + return Array.from(roomIds) + .sort((a, b) => parseInt(a) - parseInt(b)) + .join(','); +}; + export async function POST(request: Request) { try { const calendar = await getCalendarClient(); - // Fetch all calendar IDs from the Resource table const resourcesSnapshot = await db.collection("resources").get(); const resources = resourcesSnapshot.docs.map(doc => ({ id: doc.id, @@ -68,110 +100,111 @@ export async function POST(request: Request) { })); let totalNewBookings = 0; + let totalUpdatedBookings = 0; let targetBookings = 0; + + const now = new Date(); + const threeMonthsAgo = new Date(now.getFullYear(), now.getMonth() - 1, 1); + const threeMonthsLater = new Date(now.getFullYear(), now.getMonth() + 4, 0); + const timeMin = threeMonthsAgo.toISOString(); + const timeMax = threeMonthsLater.toISOString(); + for (const resource of resources) { try { - // Fetch events for each calendar let pageToken: string | undefined; - const now = new Date(); - const threeMonthsAgo = new Date(now.getFullYear(), now.getMonth(), 1); - const threeMonthsLater = new Date( - now.getFullYear(), - now.getMonth() + 4, - 0, - ); - const timeMin = threeMonthsAgo.toISOString(); - const timeMax = threeMonthsLater.toISOString(); - const events = await calendar.events.list({ - calendarId: resource.calendarId, - timeMin: timeMin, - timeMax: timeMax, - maxResults: 500, // Maximum allowed by Google Calendar API - singleEvents: true, - orderBy: "startTime", - pageToken: pageToken, - }); - - for (const event of events.data.items || []) { - const bookingRef = db - .collection("bookings") - .where("calendarEventId", "==", event.id); - const bookingSnapshot = await bookingRef.get(); - const nyuEmail = findNyuEmail(event); - if (bookingSnapshot.empty && nyuEmail) { - targetBookings++; - console.log("calendarEventId", event.id); - console.log("title", event.summary); - } + do { + const events = await calendar.events.list({ + calendarId: resource.calendarId, + timeMin: timeMin, + timeMax: timeMax, + maxResults: 500, + singleEvents: true, + orderBy: "startTime", + pageToken: pageToken, + }); - if (bookingSnapshot.empty && nyuEmail) { - // Create a new booking - const calendarEventId = event.id; - const newBooking = createBookingWithDefaults({ - title: event.summary || "", - description: event.description || "", - email: nyuEmail || "", - startDate: toFirebaseTimestampFromString( - event.start?.dateTime, - ) as Timestamp, - endDate: toFirebaseTimestampFromString( - event.end?.dateTime, - ) as Timestamp, - calendarEventId: calendarEventId || "", - equipmentCheckedOut: true, - roomId: resource.roomId, - mediaServices: MediaServices.CHECKOUT_EQUIPMENT, - }); - console.log("newBooking", newBooking); - const bookingDocRef = await db - .collection(TableNames.BOOKING) - .add(newBooking); - - console.log(`New Booking created with ID: ${bookingDocRef.id}`); - - const newBookingStatus = { - calendarEventId: calendarEventId, - email: nyuEmail, - requestedAt: admin.firestore.FieldValue.serverTimestamp(), - firstApprovedAt: admin.firestore.FieldValue.serverTimestamp(), - finalApprovedAt: admin.firestore.FieldValue.serverTimestamp(), - }; - console.log("newBookingStatus", newBookingStatus); - const statusDocRef = await db - .collection(TableNames.BOOKING_STATUS) - .add(newBookingStatus); - console.log( - `New BookingStatus created with ID: ${statusDocRef.id}`, - ); - - totalNewBookings++; + for (const event of events.data.items || []) { + const bookingRef = db + .collection("bookings") + .where("calendarEventId", "==", event.id); + const bookingSnapshot = await bookingRef.get(); + const guestEmail = findGuestEmail(event); + const roomIds = findRoomIds(event, resources); + + if (bookingSnapshot.empty && guestEmail) { + targetBookings++; + console.log("calendarEventId", event.id); + console.log("title", event.summary); + + const calendarEventId = event.id; + const newBooking = createBookingWithDefaults({ + title: event.summary || "", + description: event.description || "", + email: guestEmail, + startDate: toFirebaseTimestampFromString(event.start?.dateTime) as Timestamp, + endDate: toFirebaseTimestampFromString(event.end?.dateTime) as Timestamp, + calendarEventId: calendarEventId || "", + roomId: roomIds, + mediaServices: MediaServices.CHECKOUT_EQUIPMENT, + }); + + console.log("newBooking", newBooking); + const bookingDocRef = await db + .collection(TableNames.BOOKING) + .add(newBooking); + + console.log(`New Booking created with ID: ${bookingDocRef.id}`); + + const newBookingStatus = { + calendarEventId: calendarEventId, + email: guestEmail, + requestedAt: admin.firestore.FieldValue.serverTimestamp(), + firstApprovedAt: admin.firestore.FieldValue.serverTimestamp(), + finalApprovedAt: admin.firestore.FieldValue.serverTimestamp(), + }; + console.log("newBookingStatus", newBookingStatus); + const statusDocRef = await db + .collection(TableNames.BOOKING_STATUS) + .add(newBookingStatus); + console.log(`New BookingStatus created with ID: ${statusDocRef.id}`); + + totalNewBookings++; + } else if (!bookingSnapshot.empty) { + // Update existing booking if roomIds contains multiple rooms and is different from the existing roomId + const existingBooking = bookingSnapshot.docs[0]; + const existingData = existingBooking.data() as Booking; + + if (roomIds.includes(',') && !areRoomIdsSame(roomIds, existingData.roomId)) { + console.log("roomIds",roomIds) + console.log("existingData.roomId",existingData.roomId) + await existingBooking.ref.update({ roomId: roomIds }); + console.log(`Updated roomId for Booking ID: ${existingBooking.id}`); + totalUpdatedBookings++; + } + } } pageToken = events.data.nextPageToken; - } - while (pageToken); - console.log("targetBookings", targetBookings); + } while (pageToken); } catch (error) { - console.error( - `Error processing calendar ${resource.calendarId}:`, - error, - ); - // Continue with the next calendar + console.error(`Error processing calendar ${resource.calendarId}:`, error); } } + console.log("targetBookings", targetBookings); + console.log("totalNewBookings", totalNewBookings); + console.log("totalUpdatedBookings", totalUpdatedBookings); + return NextResponse.json( - { - message: `${totalNewBookings} new bookings have been synchronized.`, + { + message: `${totalNewBookings} new bookings have been synchronized. ${totalUpdatedBookings} existing bookings have been updated with multiple rooms.` }, - { status: 200 }, + { status: 200 } ); } catch (error) { console.error("Error syncing calendars:", error); return NextResponse.json( - { - error: "An error occurred while syncing calendars.", - }, - { status: 500 }, + { error: "An error occurred while syncing calendars." }, + { status: 500 } ); } -} +} \ No newline at end of file diff --git a/booking-app/components/src/client/utils/serverDate.ts b/booking-app/components/src/client/utils/serverDate.ts index 9e7e1072..a6509489 100644 --- a/booking-app/components/src/client/utils/serverDate.ts +++ b/booking-app/components/src/client/utils/serverDate.ts @@ -30,12 +30,6 @@ export const serverFormatDate = ( timeZone, }); - console.log("Input:", input); - console.log("Parsed Date:", date.toISOString()); - console.log("Zoned Date:", zonedDate.toString()); - console.log("Formatted Result:", formattedResult); - console.log("Timezone:", timeZone); - return formattedResult; } catch (error) { console.error("Error formatting date:", error, "Input:", input); diff --git a/booking-app/lib/googleClient.ts b/booking-app/lib/googleClient.ts index 2da94dcd..bababfa5 100644 --- a/booking-app/lib/googleClient.ts +++ b/booking-app/lib/googleClient.ts @@ -19,23 +19,16 @@ const refreshAccessTokenIfNeeded = async (oauth2Client) => { try { const { credentials } = await oauth2Client.refreshAccessToken(); oauth2Client.setCredentials(credentials); - console.log("Access token refreshed successfully"); - console.log( - "New token expiry:", - new Date(oauth2Client.credentials.expiry_date) - ); } catch (error) { console.error("Error refreshing access token:", error); throw error; } } else { - console.log("Using existing access token"); } }; const getAuthenticatedClient = async () => { if (!cachedOAuth2Client) { - console.log("Creating new OAuth2 client"); cachedOAuth2Client = createOAuth2Client(); cachedOAuth2Client.setCredentials({ refresh_token: process.env.GOOGLE_REFRESH_TOKEN,