From 823a6c882decab40f84a860e8a034d6f0235a2a4 Mon Sep 17 00:00:00 2001 From: Bryan Silva Date: Sat, 18 May 2024 20:46:44 -0700 Subject: [PATCH 1/4] Added scrapeCampusDish function --- packages/api/src/events/services/scrape.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/api/src/events/services/scrape.ts b/packages/api/src/events/services/scrape.ts index 776ad447..b0cac23b 100644 --- a/packages/api/src/events/services/scrape.ts +++ b/packages/api/src/events/services/scrape.ts @@ -1,9 +1,10 @@ import axios from "axios"; import * as cheerio from "cheerio"; -import type { Event } from "@zotmeal/db"; +import type { Event, Drizzle } from "@zotmeal/db"; import { EventSchema } from "@zotmeal/db"; import { getRestaurantId, parseEventDate } from "@zotmeal/utils"; +import { upsertEvents } from "./event"; import { logger } from "../../../logger"; @@ -115,3 +116,19 @@ export async function scrapeEvents(html: string): Promise { } return null; } + +// scrapes all events from campusDish and upserts them into the db +export async function scrapeCampusDishEvents(db: Drizzle): Promise { + const html = await getHTML( + "https://uci-campusdish-com.translate.goog/api/events?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=wapp", + ); + const events = await scrapeEvents(html); + + if (!events) { + throw new Error("Could not retrieve campus dish events"); + } + + const upsertedEvents = await upsertEvents(db, events); + + return upsertedEvents +} \ No newline at end of file From bf7b236a758abda92b7b505feed490d1bc1d7925 Mon Sep 17 00:00:00 2001 From: Bryan Silva Date: Sun, 19 May 2024 01:13:31 -0700 Subject: [PATCH 2/4] Connected events to main events page --- apps/expo/src/app/events/event/[id].tsx | 30 ++--- apps/expo/src/app/events/index.tsx | 138 +++++++++++++---------- packages/api/src/services/getWeekInfo.ts | 5 + 3 files changed, 93 insertions(+), 80 deletions(-) diff --git a/apps/expo/src/app/events/event/[id].tsx b/apps/expo/src/app/events/event/[id].tsx index 389ff6b2..2a3c1ca4 100644 --- a/apps/expo/src/app/events/event/[id].tsx +++ b/apps/expo/src/app/events/event/[id].tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useContext } from "react"; import { Stack, useGlobalSearchParams } from "expo-router"; import { CalendarClock, @@ -24,26 +24,18 @@ import type { Event } from "@zotmeal/db"; import { useMenuStore } from "~/utils"; +import { EventContext } from ".."; + export default function Event() { const { id } = useGlobalSearchParams(); const { selectedRestaurant } = useMenuStore(); - - const testData = { - title: "Test Event", - start: new Date("2022-01-01 12:00:00"), - end: new Date(), - image: - "https://uci.campusdish.com/-/media/Feature/Articles/DefaultEventImage.ashx?mh=350&mw=350&hash=B788068F09F0E38D1D19756934E293E4C1379BBF", - shortDescription: "This is a test event with a short description!", - longDescription: `This is a long description of the event. It's so long that it wraps around multiple lines. It's a very long description, but it's also very interesting. You should definitely read it.`, - restaurantId: "3314", - } satisfies Event; + const eventData = useContext(EventContext) return ( <> -

{testData.title}

+

{eventData.title}

@@ -90,13 +82,13 @@ export default function Event() { - {format(testData.start.toString(), "LLL do p")} -{" "} - {format(testData.end.toString(), "LLL do p")} + {format(eventData.start.toString(), "LLL do p")} -{" "} + {format(eventData.end.toString(), "LLL do p")} - {testData.shortDescription} + {eventData.shortDescription}
@@ -124,7 +116,7 @@ export default function Event() { borderTopLeftRadius={0} borderTopRightRadius={0} > - {testData.longDescription} + {eventData.longDescription} diff --git a/apps/expo/src/app/events/index.tsx b/apps/expo/src/app/events/index.tsx index ff10cd6b..e8421485 100644 --- a/apps/expo/src/app/events/index.tsx +++ b/apps/expo/src/app/events/index.tsx @@ -1,4 +1,5 @@ -import { Link } from "expo-router"; +import { createContext, useEffect, useState } from "react"; +import { Link, Stack } from "expo-router"; import { format } from "date-fns"; import { H3, Image, ScrollView, Text, YStack } from "tamagui"; @@ -6,31 +7,44 @@ import type { Event } from "@zotmeal/db"; import { RestaurantTabs } from "~/components"; -// import { api } from "~/utils/api"; +import { api } from "~/utils/api"; + +// Create a context for events, default value is a test event +const testData: Event = { + start: new Date("2022-01-01 12:00:00"), + end: new Date(), + title: "Test Event", + shortDescription: "This is a test event", + longDescription: `This is a long description of the event. It's so long that it wraps + around multiple lines. It's a very long description, but it's also + very interesting. You should definitely read it.`, + image: + "https://uci.campusdish.com/-/media/Feature/Articles/DefaultEventImage.ashx?mh=350&mw=350&hash=B788068F09F0E38D1D19756934E293E4C1379BBF", + restaurantId: "3314", +} satisfies Event; + +export const EventContext = createContext(testData) + +// Events Component export default function Events() { - // const { data, error } = api.event.get.useQuery({}); + const [events, setEvents] = useState([]); + + const eventsQuery = api.event.get.useQuery({}); - const testData: Event[] = Array(5).fill({ - start: new Date("2022-01-01 12:00:00"), - end: new Date(), - title: "Test Event", - shortDescription: "This is a test event", - longDescription: `This is a long description of the event. It's so long that it wraps - around multiple lines. It's a very long description, but it's also - very interesting. You should definitely read it.`, - image: - "https://uci.campusdish.com/-/media/Feature/Articles/DefaultEventImage.ashx?mh=350&mw=350&hash=B788068F09F0E38D1D19756934E293E4C1379BBF", - restaurantId: "3314", - } satisfies Event) as Event[]; + useEffect(() => { + if (eventsQuery?.data) { + setEvents(eventsQuery.data); + } + }, [eventsQuery?.data]); - // if (!data) { - // return Loading...; - // } + if (eventsQuery?.isLoading) { + return Loading...; + } - // if (error) { - // return Error: {error.message}; - // } + if (eventsQuery?.isError) { + return Error: {eventsQuery.error.message}; + } return ( - {testData.map((event: Event, index: number) => ( - - ( + + - + + + +

{event.title}

+ + {format(event.start.toString(), "LLL do p")} -{" "} + {format(event.end.toString(), "LLL do p")} +
-

{event.title}

- - {format(event.start.toString(), "LLL do p")} -{" "} - {format(event.end.toString(), "LLL do p")} - -
- + + ))}
diff --git a/packages/api/src/services/getWeekInfo.ts b/packages/api/src/services/getWeekInfo.ts index a620f6cf..ecfd0209 100644 --- a/packages/api/src/services/getWeekInfo.ts +++ b/packages/api/src/services/getWeekInfo.ts @@ -4,6 +4,7 @@ import { z } from "zod"; import type { Drizzle } from "@zotmeal/db"; import { RestaurantSchema } from "@zotmeal/db"; import { DateRegex } from "@zotmeal/validators"; +import { scrapeCampusDishEvents } from "../events"; import type { UpdateDailyParams } from "./updateDaily"; import { logger } from "../../logger"; @@ -24,6 +25,10 @@ export async function getWeekInfo( const { date: dateString, restaurant } = params; const startDate = new Date(dateString); + // Scrape and insert new events into db + const eventResults = await scrapeCampusDishEvents(db) + + // Update menus for each day const results = await Promise.allSettled( Array.from({ length: NUM_DAYS_UPDATE }).map((_, i) => { const insertDate = new Date(); From 51125c478dbefcedef4f85a8c16260851c9400cc Mon Sep 17 00:00:00 2001 From: Bryan Silva Date: Sun, 19 May 2024 17:54:02 -0700 Subject: [PATCH 3/4] Fixed events images --- apps/expo/src/app/events/index.tsx | 83 +++++++++++++++--------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/apps/expo/src/app/events/index.tsx b/apps/expo/src/app/events/index.tsx index e8421485..92310478 100644 --- a/apps/expo/src/app/events/index.tsx +++ b/apps/expo/src/app/events/index.tsx @@ -35,6 +35,9 @@ export default function Events() { useEffect(() => { if (eventsQuery?.data) { setEvents(eventsQuery.data); + eventsQuery.data.forEach((event) => { + console.log(event.image) + }) } }, [eventsQuery?.data]); @@ -54,52 +57,50 @@ export default function Events() { }} > {events.map((event: Event, index: number) => ( - - + - - - -

{event.title}

- - {format(event.start.toString(), "LLL do p")} -{" "} - {format(event.end.toString(), "LLL do p")} - +
- -
+

{event.title}

+ + {format(event.start.toString(), "LLL do p")} -{" "} + {format(event.end.toString(), "LLL do p")} + + + ))} From 85ab758f1a3357cb72413de157b323bcefe4dc97 Mon Sep 17 00:00:00 2001 From: Bryan Silva Date: Sun, 19 May 2024 19:34:05 -0700 Subject: [PATCH 4/4] Added events context to dynamic routes --- apps/expo/src/app/events/_layout.tsx | 18 ++++++++++++++++ apps/expo/src/app/events/event/[id].tsx | 11 +++++----- apps/expo/src/app/events/eventsContext.tsx | 24 ++++++++++++++++++++++ apps/expo/src/app/events/index.tsx | 12 ++++------- 4 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 apps/expo/src/app/events/_layout.tsx create mode 100644 apps/expo/src/app/events/eventsContext.tsx diff --git a/apps/expo/src/app/events/_layout.tsx b/apps/expo/src/app/events/_layout.tsx new file mode 100644 index 00000000..0b8d0277 --- /dev/null +++ b/apps/expo/src/app/events/_layout.tsx @@ -0,0 +1,18 @@ +import { Stack } from "expo-router" +import { EventsProvider } from "./eventsContext" + +export default function EventsLayout() { + return ( + + + + + + + ) +} \ No newline at end of file diff --git a/apps/expo/src/app/events/event/[id].tsx b/apps/expo/src/app/events/event/[id].tsx index 2a3c1ca4..bb3be47a 100644 --- a/apps/expo/src/app/events/event/[id].tsx +++ b/apps/expo/src/app/events/event/[id].tsx @@ -24,20 +24,19 @@ import type { Event } from "@zotmeal/db"; import { useMenuStore } from "~/utils"; -import { EventContext } from ".."; +import { useEvents } from "../eventsContext"; export default function Event() { const { id } = useGlobalSearchParams(); const { selectedRestaurant } = useMenuStore(); - const eventData = useContext(EventContext) + const { events } = useEvents() + const eventData = events[Number(id)]! return ( <> - +

{eventData.title}

diff --git a/apps/expo/src/app/events/eventsContext.tsx b/apps/expo/src/app/events/eventsContext.tsx new file mode 100644 index 00000000..877a71db --- /dev/null +++ b/apps/expo/src/app/events/eventsContext.tsx @@ -0,0 +1,24 @@ +import React, { createContext, useState, useContext, ReactNode } from 'react'; +import type { Event } from "@zotmeal/db" + +interface EventsContextProps { + events: Event[]; + setEvents: React.Dispatch>; +} + +const EventsContext = createContext({events: [], setEvents: () => {}}); + +export function EventsProvider(props: { children: ReactNode }) { + const [events, setEvents] = useState([]); + + return ( + + {props.children} + + ); +}; + +export function useEvents() { + const context = useContext(EventsContext); + return context; + }; \ No newline at end of file diff --git a/apps/expo/src/app/events/index.tsx b/apps/expo/src/app/events/index.tsx index 92310478..5780ad63 100644 --- a/apps/expo/src/app/events/index.tsx +++ b/apps/expo/src/app/events/index.tsx @@ -1,9 +1,10 @@ -import { createContext, useEffect, useState } from "react"; -import { Link, Stack } from "expo-router"; +import { useEffect} from "react"; +import { Link } from "expo-router"; import { format } from "date-fns"; import { H3, Image, ScrollView, Text, YStack } from "tamagui"; import type { Event } from "@zotmeal/db"; +import { useEvents } from "./eventsContext"; import { RestaurantTabs } from "~/components"; @@ -24,20 +25,15 @@ const testData: Event = { restaurantId: "3314", } satisfies Event; -export const EventContext = createContext(testData) - // Events Component export default function Events() { - const [events, setEvents] = useState([]); + const { events, setEvents } = useEvents(); const eventsQuery = api.event.get.useQuery({}); useEffect(() => { if (eventsQuery?.data) { setEvents(eventsQuery.data); - eventsQuery.data.forEach((event) => { - console.log(event.image) - }) } }, [eventsQuery?.data]);