const db = require("./dbAccess.services"); const clean = require("../utils/sanitizer.utils"); const pis = require("../services/pis.services"); import { logger } from "../utils/logger.utils"; async function findByHeadcodeToday(headcode) { const sanitizedHeadcode = clean.removeNonAlphanumeric(headcode).toUpperCase(); logger.debug( `trainServiceServices.findByHeadcode: Searching for headcode ${sanitizedHeadcode}` ); const now = new Date(); const dayMap = ["su", "m", "t", "w", "th", "f", "s"]; const shortDay = dayMap[now.getDay()]; // Fetch short day from map const query = { headcode: sanitizedHeadcode, scheduleStartDate: { $lte: now }, scheduleEndDate: { $gte: now }, daysRun: { $in: [shortDay] }, }; const queryData = await db.query("timetable", query); let trainData = await parseTrains(queryData); let preparedData = []; for (let trainService in trainData) { if (pis.supported.includes(trainService?.operator)) { // Search for PIS Code for each service const tiplocList = await getPublicStops(trainService?.stops); //console.log(tiplocList.length); console.log(tiplocList); if (tiplocList.length) { const pisDetail = await pis.findByTiplocArray(tiplocList); trainService["pis"] = pisDetail?.[0]?.["code"] ?? "None"; } else if (trainService?.operator === "GW") { trainService["pis"] = "0015"; // Not in Service code // '0015' is a string becuase 0015 is not a valid number.. } } preparedData.push(trainService); } return preparedData; } async function findByHeadcode(date, headcode) { const sanitizedHeadcode = clean.removeNonAlphanumeric(headcode).toUpperCase(); logger.debug( `trainServiceServices.findByHeadcode: Searching for headcode ${sanitizedHeadcode}` ); let searchDate; if (date === "now") { searchDate = new Date(); } else { searchDate = new Date(date); } searchDate.setHours(12, 0, 0); // Set to midday to avoid any timezone issues const dayMap = ["su", "m", "t", "w", "th", "f", "s"]; const shortDay = dayMap[searchDate.getDay()]; // Fetch short day from map const query = { headcode: sanitizedHeadcode, scheduleStartDate: { $lte: searchDate }, scheduleEndDate: { $gte: searchDate }, daysRun: { $in: [shortDay] }, }; const pipeline = [ { $match: query, }, { $project: { operator: 1, stops: { $concatArrays: [ [{ $first: "$stops" }], [{ $arrayElemAt: ["$stops", -1] }], ], }, trainUid: 1, stpIndicator: 1, }, }, ]; const queryData = await db.queryAggregate("timetable", pipeline); let filteredData = filterServices(queryData); return await filteredData; } async function findByTrainUid(uid, date = new Date()) { let queryDate; if (date === "now") { queryDate = new Date(); } else { queryDate = date; } const query = { trainUid: uid, scheduleStartDate: { $lte: queryDate }, scheduleEndDate: { $gte: queryDate }, }; const queryData = await db.query("timetable", query); if (queryData.length === 0) { return []; } let services; services = await filterServices(queryData); console.log(services); let publicStops; if (pis.supported.includes(services[0]?.operator)) { publicStops = await getPublicStops(services[0]?.stops); if (publicStops.length) { const pisCode = await pis.findByTiplocArray(publicStops); services[0].pis = pisCode[0]?.code; } else if (services[0]?.operator === "GW" && !publicStops.length) { services[0].pis = "0015"; } } return services[0]; } module.exports = { findByHeadcodeToday, findByHeadcode, findByTrainUid, }; /* Internal Functions, not to be exported */ /* Accepts the 'stops' array from a db query and produces an array of only public stops as TIPLOCs. */ async function getPublicStops(data) { let tiplocList = []; for (const publicStop in data) { if (data[publicStop]["isPublic"]) { tiplocList.push(data[publicStop]["tiploc"]); } } return tiplocList; } /* Takes a single days data from a headcode query and requeries using the trainUid, required to ensure any cancellations are accounted for */ async function parseTrains(data) { let trainUids = []; for (const i of data) { const trainUid = i["trainUid"]; if (!trainUids.includes(trainUid)) { trainUids.push(trainUid); } } let parsedData = []; for (const i in trainUids) { const result = await findByTrainUid(trainUids[i]); parsedData.push(result); } return parsedData; } async function filterServices(data) { let stpIndicators = {}, filteredServices = []; for (const serviceDetail of data) { const trainUid = serviceDetail["trainUid"]; const stpIndicator = serviceDetail["stpIndicator"]; if (!stpIndicators[trainUid]) { stpIndicators[trainUid] = { hasC: false, hasN: false, hasO: false, hasP: false, }; } if (stpIndicator === "C") { stpIndicators[trainUid].hasC = true; } if (stpIndicator === "N") { stpIndicators[trainUid].hasN = true; } if (stpIndicator === "O") { stpIndicators[trainUid].hasO = true; } if (stpIndicator === "P") { stpIndicators[trainUid].hasP = true; } } let preparedData; for (const serviceDetail of data) { const trainUid = serviceDetail["trainUid"]; const thisStpIndicators = stpIndicators[trainUid]; const stpIndicator = serviceDetail["stpIndicator"]; if (stpIndicator === "C") { break; } if (stpIndicator === "N" && !thisStpIndicators.hasC) { filteredServices.push(serviceDetail); } else if ( stpIndicator === "O" && !thisStpIndicators.hasC && !thisStpIndicators.hasN ) { filteredServices.push(serviceDetail); } else if ( stpIndicator === "P" && !thisStpIndicators.hasC && !thisStpIndicators.hasN && !thisStpIndicators.hasO ) { filteredServices.push(serviceDetail); } } return filteredServices; }