diff --git a/apps/api/src/routes/v1/rest/studyRooms/+endpoint.ts b/apps/api/src/routes/v1/rest/studyRooms/+endpoint.ts index 535dcb8f..745c7b90 100644 --- a/apps/api/src/routes/v1/rest/studyRooms/+endpoint.ts +++ b/apps/api/src/routes/v1/rest/studyRooms/+endpoint.ts @@ -3,7 +3,7 @@ import { studyLocations } from "libs/uc-irvine-lib/src/spaces"; import { ZodError } from "zod"; import { aggregateStudyRooms } from "./lib"; -import { Query, QuerySchema } from "./schema"; +import { QuerySchema } from "./schema"; export const GET = createHandler(async (event, context, res) => { const headers = event.headers; @@ -13,8 +13,12 @@ export const GET = createHandler(async (event, context, res) => { const parsedQuery = QuerySchema.parse(query); if (!studyLocations[parsedQuery.location]) { return res.createErrorResult(404, `Location ${parsedQuery.location} not found`, requestId); - } - const studyRooms = await aggregateStudyRooms(parsedQuery.location, parsedQuery.start, parsedQuery.end) + } + const studyRooms = await aggregateStudyRooms( + parsedQuery.location, + parsedQuery.start, + parsedQuery.end, + ); return res.createOKResult(studyRooms, headers, requestId); } catch (e) { if (e instanceof ZodError) { diff --git a/apps/api/src/routes/v1/rest/studyRooms/{id}/+endpoint.ts b/apps/api/src/routes/v1/rest/studyRooms/{id}/+endpoint.ts index 186dd154..07a476e2 100644 --- a/apps/api/src/routes/v1/rest/studyRooms/{id}/+endpoint.ts +++ b/apps/api/src/routes/v1/rest/studyRooms/{id}/+endpoint.ts @@ -4,17 +4,19 @@ import { ZodError } from "zod"; import { aggregateStudyRooms } from "../lib"; -import { Query, QuerySchema } from "./schema"; +import { QuerySchema } from "./schema"; export const GET = createHandler(async (event, context, res) => { const headers = event.headers; const query = event.queryStringParameters; const requestId = context.awsRequestId; const { id } = event.pathParameters ?? {}; - if (id == null) return res.createErrorResult(400, "Location not provided", requestId); try { switch (id) { - case "all": + case null: + case undefined: + return res.createErrorResult(400, "Location not provided", requestId); + case "all": { const parsedQuery = QuerySchema.parse(query); return res.createOKResult( await Promise.all( @@ -25,8 +27,15 @@ export const GET = createHandler(async (event, context, res) => { headers, requestId, ); - default: - return res.createErrorResult(400, "Invalid endpoint", requestId); + } + default: { + if (studyLocations[id]) { + const parsedQuery = QuerySchema.parse(query); + const studyRooms = await aggregateStudyRooms(id, parsedQuery.start, parsedQuery.end); + return res.createOKResult(studyRooms, headers, requestId); + } + return res.createErrorResult(400, `Location ${id} not found`, requestId); + } } } catch (e) { if (e instanceof ZodError) { diff --git a/apps/docs/docs/developers-guide/rest-api/reference/study-rooms.md b/apps/docs/docs/developers-guide/rest-api/reference/study-rooms.md index 407be640..317952d1 100644 --- a/apps/docs/docs/developers-guide/rest-api/reference/study-rooms.md +++ b/apps/docs/docs/developers-guide/rest-api/reference/study-rooms.md @@ -12,7 +12,7 @@ The study rooms endpoint allows users to get information and availability of stu ## Query parameters -#### `location` string Required +#### `location` string The location of the study rooms to query. Five locations are available to query: @@ -38,7 +38,11 @@ The end date of time slots to query. YYYY-MM-DD format. ```bash -curl "https://api-next.peterportal.org/v1/rest/studyRooms?location=Science&start=2024-04-26&end=2024-04-30"" +curl "https://api-next.peterportal.org/v1/rest/studyRooms/Science?start=2024-04-26&end=2024-04-30" +``` + +```bash +curl "https://api-next.peterportal.org/v1/rest/studyRooms?location=Science&start=2024-04-26&end=2024-04-30" ``` diff --git a/libs/uc-irvine-lib/src/spaces/index.ts b/libs/uc-irvine-lib/src/spaces/index.ts index 4015d109..dc104057 100644 --- a/libs/uc-irvine-lib/src/spaces/index.ts +++ b/libs/uc-irvine-lib/src/spaces/index.ts @@ -27,15 +27,9 @@ export async function getStudySpaces(lid: string, start: string, end: string) { const headers = { Referer: `${LIB_SPACE_URL}?lid=${lid}`, }; - const params = new URLSearchParams(); - params.append("lid", lid); - params.append("gid", "0"); - params.append("start", start); - params.append("end", end); - params.append("pageSize", "18"); // pageSize doesn't seem to affect query but is required return await fetch(LIB_SPACE_AVAILABILITY_URL, { method: "POST", headers: headers, - body: params, + body: new URLSearchParams({ lid, gid: "0", start, end, pageSize: "18" }), }).then((res) => res.json()); } diff --git a/tools/study-room-scraper/src/index.ts b/tools/study-room-scraper/src/index.ts index aa882f72..9c01f1eb 100644 --- a/tools/study-room-scraper/src/index.ts +++ b/tools/study-room-scraper/src/index.ts @@ -1,12 +1,12 @@ import { PrismaClient } from "@libs/db"; -import type { StudyLocation, StudyRoom } from "@peterportal-api/types"; +import type { StudyRoom } from "@peterportal-api/types"; import { scrapeStudyLocations } from "./study-room-scraper"; const prisma = new PrismaClient(); async function main() { - const studyLocations: { [id: string]: StudyLocation } = await scrapeStudyLocations(); + const studyLocations = await scrapeStudyLocations(); const studyLocationInfo = Object.values(studyLocations).map((location) => { return prisma.studyLocation.create({ data: { diff --git a/tools/study-room-scraper/src/study-room-scraper.ts b/tools/study-room-scraper/src/study-room-scraper.ts index 29f586d2..3794d243 100644 --- a/tools/study-room-scraper/src/study-room-scraper.ts +++ b/tools/study-room-scraper/src/study-room-scraper.ts @@ -1,14 +1,12 @@ import type { StudyRoom, StudyLocation } from "@peterportal-api/types"; -import { load } from "cheerio"; +import { load, Cheerio, Element, CheerioAPI } from "cheerio"; import fetch from "cross-fetch"; import { studyLocations, getStudySpaces } from "libs/uc-irvine-lib/src/spaces"; import * as winston from "winston"; const ROOM_SPACE_URL = "https://spaces.lib.uci.edu/space"; -type StudyLocations = { - [id: string]: StudyLocation; -}; +type StudyLocations = Record; const logger = winston.createLogger({ level: "info", @@ -20,27 +18,33 @@ const logger = winston.createLogger({ transports: [new winston.transports.Console()], }); +function processGML(descriptionHeader: Cheerio, $: CheerioAPI): string { + let descriptionText = ""; + descriptionHeader.find("p").each(function () { + let paraText = $(this).text().trim(); + if (paraText.includes("\n")) { + paraText = paraText.replaceAll("\n", ", "); + if (!paraText.endsWith(":")) { + paraText += ". "; + } + } + descriptionText += paraText + " "; + }); + descriptionText = descriptionText.replace(/\s{2,}/g, " ").trim(); + descriptionText = descriptionText.replace(/\s+,/g, ","); + descriptionText = descriptionText.replace(/\.\s*\./g, "."); + descriptionText = descriptionText.replace(".,", "."); + return descriptionText; +} + function processDescription( - descriptionHeader: cheerio.Cheerio, + descriptionHeader: Cheerio, location: string, - $: cheerio.Root, + $: CheerioAPI, ): string { let descriptionText = ""; if (location === "Grunigen Medical Library") { - descriptionHeader.find("p").each(function () { - let paraText = $(this).text().trim(); - if (paraText.includes("\n")) { - paraText = paraText.replaceAll("\n", ", "); - if (!paraText.endsWith(":")) { - paraText += ". "; - } - } - descriptionText += paraText + " "; - }); - descriptionText = descriptionText.replace(/\s{2,}/g, " ").trim(); - descriptionText = descriptionText.replace(/\s+,/g, ","); - descriptionText = descriptionText.replace(/\.\s*\./g, "."); - descriptionText = descriptionText.replace(".,", "."); + descriptionText = processGML(descriptionHeader, $); } else { const descriptionParts: string[] = []; descriptionHeader.contents().each((_, content) => { @@ -164,8 +168,3 @@ export async function scrapeStudyLocations(): Promise { } return studyLocationsMap; } -getRoomInfo("44696"); -getRoomInfo("116383"); -getRoomInfo("117634"); -getRoomInfo("120645"); -getRoomInfo("51792");