diff --git a/README.md b/README.md
index 69490937..b2df6481 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,9 @@ A summary of the libraries we use are listed below.
It's responsible for installing, uninstalling, and keeping track of the app's dependencies.
`npm install --global pnpm`
+3. Make sure to have `docker` installed, which will allow you to run the local postgres database
+ required for backend functions. You can install it from [the official website here](https://www.docker.com/get-started/).
+
## Developing
1. Clone the ZotMeal repository or your fork.
@@ -44,19 +47,25 @@ A summary of the libraries we use are listed below.
`nvm use`
3. Navigate to the root directory and install the dependencies.
`cd ZotMeal && pnpm install`
-4. Create a .env based on the .env.example
-5. To start a local Postgres container database run the `start-database.sh` script. This will automatically set a test database.
-6. Run `turbo db:generate`
-7. Start the local development servers for expo and server.
- `pnpm dev`
+4. To start a local Postgres container database run the `docker compose up` in the root directory.
+ This will automatically set up and run a test database using docker.
+
+5. Create a .env based on the .env.example using `localhost`
+
+6. Run `pnpm db:push` to push the schema to the docker database.
+
+7. Start the local development servers for expo and server with `pnpm dev`.
The tRPC procedures are available on ?input={field: value}
-8. View the local website at and/or with the [Expo Go mobile app](https://expo.dev/client).
+8. View the local website at and/or with the [Expo Go mobile app](https://expo.dev/client).
As you make changes to the Expo application, those changes will be automatically
reflected on the local website as well as the mobile app.
-9. To add a new package run `turbo gen workspace` and follow the prompts
## Testing
Run `turbo test` at the root of the project.
+
+## Adding Workspaces
+
+To add a new package run `turbo gen workspace` and follow the prompts
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 389ff6b2..bb3be47a 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,28 +24,19 @@ import type { Event } from "@zotmeal/db";
import { useMenuStore } from "~/utils";
+import { useEvents } from "../eventsContext";
+
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 { events } = useEvents()
+ const eventData = events[Number(id)]!
return (
<>
-
-
{testData.title}
+
+
{eventData.title}
@@ -90,13 +81,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 +115,7 @@ export default function Event() {
borderTopLeftRadius={0}
borderTopRightRadius={0}
>
- {testData.longDescription}
+ {eventData.longDescription}
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 ff10cd6b..5780ad63 100644
--- a/apps/expo/src/app/events/index.tsx
+++ b/apps/expo/src/app/events/index.tsx
@@ -1,36 +1,49 @@
+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";
-// 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;
+
+// Events Component
export default function Events() {
- // const { data, error } = api.event.get.useQuery({});
+ const { events, setEvents } = useEvents();
+
+ 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) => (
+ {events.map((event: Event, index: number) => (
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
diff --git a/packages/api/src/schedules/getWeekInfo.ts b/packages/api/src/schedules/getWeekInfo.ts
index a620f6cf..ecfd0209 100644
--- a/packages/api/src/schedules/getWeekInfo.ts
+++ b/packages/api/src/schedules/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();