import { InsightDatasetKind, InsightError, NotFoundError } from "../controller/IInsightFacade"; import InsightFacade from "../controller/InsightFacade"; import JSZip from "jszip"; import DatasetPersister from "./DatasetPersister"; import { DatasetSection, DatasetRoom, DatasetBuilding } from "./DatasetSection"; import DatasetValidator from "./DatasetValidator"; import DatasetParser from "./DatasetParser"; export default class DatasetProcessor { private insightFacade: InsightFacade; constructor(insightFacade: InsightFacade) { this.insightFacade = insightFacade; } public async processAddDataset(id: string, content: string, kind: InsightDatasetKind): Promise { if (!new DatasetValidator(this.insightFacade).validateDataset(id, content, kind)) { throw new InsightError("validateDataset"); } try { const zip = await JSZip().loadAsync(content, { base64: true }); let dataset: DatasetSection[] | DatasetRoom[]; if (kind === InsightDatasetKind.Sections) { dataset = await this.processAddDatasetSection(zip); } else { dataset = await this.processAddDatasetRoom(zip); } if (dataset.length === 0) { throw new InsightError("no valid dataset sections|rooms"); } this.insightFacade.datasetIds.push(id); this.insightFacade.insights.push({ id, kind, numRows: dataset.length }); this.insightFacade.datasets.set(id, dataset); const persister = new DatasetPersister(); await Promise.all([ persister.persistWriteDataset("data/_datasetIds.json", this.insightFacade.datasetIds), persister.persistWriteDataset("data/_insightDatasets.json", this.insightFacade.insights), persister.persistWriteDataset(`data/${id}.json`, dataset), ]); return this.insightFacade.datasetIds; } catch (err) { throw new InsightError(`processAddDataset ${err}`); } } public async processAddDatasetSection(zip: JSZip): Promise { try { if (!zip.files["courses/"]) { throw new InsightError("zip is not courses/ folder"); } const fileContents: string[] = await Promise.all( Object.values(zip.folder("courses")!.files).map(async (file) => file.async("text")) ); return new DatasetParser(this.insightFacade).parseDataset(fileContents); } catch (err) { throw new InsightError(`processAddDatasetSection ${err}`); } } public async processAddDatasetRoom(zip: JSZip): Promise { try { const index = zip.files["index.htm"]; if (!index) { throw new InsightError("zip doesn't have index.htm file"); } if (!zip.files["campus/discover/buildings-and-classrooms/"]) { throw new InsightError("zip doesn't have campus/discover/buildings-and-classrooms/ folder"); } const fileContent: string = await index.async("text"); const parser = new DatasetParser(this.insightFacade); const buildings: DatasetBuilding[] = await parser.parseBuilding(fileContent); const rooms: DatasetRoom[] = []; await Promise.all( buildings.map(async (building) => { const path = building.href.substring(2); const htmFile = zip.files[path]; if (!htmFile) { return; } const roomFile = await htmFile.async("text"); const datasetRooms: DatasetRoom[] = parser.parseRoom(roomFile, building); rooms.push(...datasetRooms); }) ); return rooms; } catch (err) { throw new InsightError(`processAddDatasetRoom ${err}`); } } public async processRemoveDataset(id: string): Promise { const validator = new DatasetValidator(this.insightFacade); if (!validator.validateId(id)) { throw new InsightError("validateId"); } if (!validator.validateRemoval(id)) { throw new NotFoundError("validateRemoval"); } try { this.insightFacade.datasetIds = this.insightFacade.datasetIds.filter((iD) => iD !== id); this.insightFacade.insights = this.insightFacade.insights.filter((insight) => insight.id !== id); this.insightFacade.datasets.delete(id); const persister = new DatasetPersister(); await Promise.all([ persister.persistWriteDataset("data/_datasetIds.json", this.insightFacade.datasetIds), persister.persistWriteDataset("data/_insightDatasets.json", this.insightFacade.insights), persister.persistDeleteDataset("data/" + id + ".json"), ]); return id; } catch (err) { throw new InsightError(`processRemoveDataset ${err}`); //maybe unnecessary, only for safety } } }