CourseInsights / src / datasetProcessor / DatasetCollector.ts
DatasetCollector.ts
Raw
import { InsightError } from "../controller/IInsightFacade";
import { DatasetBuilding, DatasetRoom, DatasetSection, GeoResponse } from "./DatasetSection";
import * as http from "node:http";

export default class DatasetCollector {
	public collectDataset(section: any): DatasetSection {
		const { id, Course, Title, Professor, Subject, Avg, Pass, Fail, Audit, Section } = section;
		let { Year } = section;
		if (Section === "overall") Year = "1900";
		return {
			uuid: String(id),
			id: Course,
			title: Title,
			instructor: Professor,
			dept: Subject,

			year: Number(Year),
			avg: Avg,
			pass: Pass,
			fail: Fail,
			audit: Audit,
		} as DatasetSection;
	}

	public collectDatasetBuilding(address: string, geo: GeoResponse, trTree: any): DatasetBuilding {
		const title = this.collectElementByClass(trTree, "views-field-title")[0];
		const a = this.collectElementByTag(title, "a")[0];
		const fullname = this.collectText(a);
		const href = this.collectHref(a);
		const code = this.collectElementByClass(trTree, "views-field-field-building-code")[0];
		const shortname = this.collectText(code);
		const lat = geo.lat;
		const lon = geo.lon;

		return {
			fullname,
			shortname,
			address,
			href,

			lat,
			lon,
		} as DatasetBuilding;
	}

	public collectDatasetRoom(trTree: any, building: DatasetBuilding): DatasetRoom {
		const fullname = building.fullname;
		const shortname = building.shortname;
		const address = building.address;
		const lat = building.lat;
		const lon = building.lon;

		const roomNumber = this.collectElementByClass(trTree, "views-field-field-room-number")[0];
		const a = this.collectElementByTag(roomNumber, "a")[0];
		const href = this.collectHref(a);
		const number = this.collectText(a);
		const name = shortname + "_" + number;

		const roomType = this.collectElementByClass(trTree, "views-field-field-room-type")[0];
		const type = this.collectText(roomType);

		const roomFurniture = this.collectElementByClass(trTree, "views-field-field-room-furniture")[0];
		const furniture = this.collectText(roomFurniture);

		const roomCapacity = this.collectElementByClass(trTree, "views-field-field-room-capacity")[0];
		const seats = Number(this.collectText(roomCapacity));

		return {
			fullname,
			shortname,
			address,
			href,

			lat,
			lon,

			number,
			name,
			type,
			furniture,

			seats,
		} as DatasetRoom;
	}

	public collectElementByTag(content: any, value: string): any {
		const elements = [];

		if (content.tagName === value) {
			elements.push(content);
		}
		if (content.childNodes) {
			for (const childNode of content.childNodes) {
				const res = this.collectElementByTag(childNode, value);
				elements.push(...res);
			}
		}

		return elements;
	}

	public collectElementByClass(content: any, value: string): any {
		const elements = [];

		const attrCheck = content.attrs?.filter((attr: any) => attr.name === "class");
		if (attrCheck?.length) {
			const classList = new Set(attrCheck[0].value.split(" "));
			if (classList.has(value)) {
				elements.push(content);
			}
		}

		if (content.childNodes) {
			for (const childNode of content.childNodes) {
				const res = this.collectElementByClass(childNode, value);
				elements.push(...res);
			}
		}

		return elements;
	}

	public collectText(content: any): string {
		for (const childNode of content.childNodes) {
			if (childNode.nodeName === "#text") {
				return String(childNode.value).trim();
			}
		}
		throw new InsightError(`collectText`);
	}

	public collectHref(content: any): string {
		return content.attrs?.find((attr: any) => attr.name === "href")?.value;
	}

	// adapted from https://blog.logrocket.com/5-ways-make-http-requests-node-js/
	public async collectGeoResponse(address: string): Promise<GeoResponse> {
		const code = encodeURIComponent(address);
		return new Promise((resolve) => {
			http
				.get(`http://cs310.students.cs.ubc.ca:11316/api/v1/project_team285/${code}`, (res) => {
					const data: any[] = [];
					res.on("data", (chunk) => {
						data.push(chunk);
					});
					res.on("end", () => {
						try {
							resolve(JSON.parse(Buffer.concat(data).toString()));
						} catch (_err) {
							resolve({ error: "collectGeoResponse" } as GeoResponse);
						}
					});
				})
				.on("error", (err) => {
					resolve({ error: err.message } as GeoResponse);
				});
		});
	}
}