move api logic to parent class ApiBasedRepositoryLoader

This commit is contained in:
2025-01-21 13:33:28 -08:00
parent 900d649409
commit 59af309bec
2 changed files with 272 additions and 259 deletions

View File

@@ -0,0 +1,268 @@
import { GetterSetterRepository } from "../repositories/GetterSetterRepository";
import { IEta, IRoute, IShuttle, IStop, ISystem } from "../entities/entities";
const systemIdsToSupport = ["263"];
const baseUrl = "https://passiogo.com/mapGetData.php";
export class ApiBasedRepositoryLoader {
constructor(
protected repository: GetterSetterRepository,
) {
}
protected async fetchAndUpdateSystemData() {
const params = {
getSystems: "2",
};
const query = new URLSearchParams(params).toString();
const response = await fetch(`${baseUrl}?${query}`);
const json = await response.json()
if (typeof json.all === "object") {
// filter down to supported systems
const filteredSystems = json.all.filter((jsonSystem: any) => systemIdsToSupport.includes(jsonSystem.id));
await Promise.all(filteredSystems.map(async (system: any) => {
const constructedSystem: ISystem = {
id: system.id,
name: system.fullname,
};
await this.repository.addOrUpdateSystem(constructedSystem);
}));
}
}
protected async fetchAndUpdateRouteDataForExistingSystems() {
const systems = await this.repository.getSystems();
await Promise.all(systems.map(async (system) => {
const params = {
getRoutes: "2",
};
const formDataJsonObject = {
"systemSelected0": system.id,
"amount": "1",
}
const formData = new FormData();
formData.set("json", JSON.stringify(formDataJsonObject));
const query = new URLSearchParams(params).toString();
const response = await fetch(`${baseUrl}?${query}`, {
method: "POST",
body: formData,
});
const json = await response.json();
if (typeof json.all === "object") {
await Promise.all(json.all.map(async (jsonRoute: any) => {
const constructedRoute: IRoute = {
name: jsonRoute.name,
color: jsonRoute.color,
id: jsonRoute.myid,
polylineCoordinates: [],
systemId: system.id,
};
await this.repository.addOrUpdateRoute(constructedRoute);
}))
}
}));
}
protected async fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystems() {
// Fetch from the API
// Pass JSON output into two different methods to update repository
const systems = await this.repository.getSystems();
await Promise.all(systems.map(async (system: ISystem) => {
const params = {
getStops: "2",
};
const formDataJsonObject = {
"s0": system.id,
"sA": 1
};
const formData = new FormData();
formData.set("json", JSON.stringify(formDataJsonObject));
const query = new URLSearchParams(params).toString();
const response = await fetch(`${baseUrl}?${query}`, {
method: "POST",
body: formData,
});
const json = await response.json();
await this.updateStopDataForSystemAndApiResponse(system, json);
await this.updateOrderedStopDataForExistingStops(json);
await this.updatePolylineDataForExistingRoutesAndApiResponse(json);
}));
}
protected async fetchAndUpdateShuttleDataForExistingSystems() {
const systems = await this.repository.getSystems();
await Promise.all(systems.map(async (system: ISystem) => {
const params = {
getBuses: "2"
};
const formDataJsonObject = {
"s0": system.id,
"sA": "1"
};
const formData = new FormData();
formData.set("json", JSON.stringify(formDataJsonObject));
const query = new URLSearchParams(params).toString();
const response = await fetch(`${baseUrl}?${query}`, {
method: "POST",
body: formData,
});
const json = await response.json();
if (json.buses && json.buses["-1"] === undefined) {
const jsonBuses = Object.values(json.buses).map((busesArr: any) => {
return busesArr[0];
});
await Promise.all(jsonBuses.map(async (jsonBus: any) => {
const constructedShuttle: IShuttle = {
name: jsonBus.bus,
coordinates: {
latitude: parseFloat(jsonBus.latitude),
longitude: parseFloat(jsonBus.longitude),
},
routeId: jsonBus.routeId,
systemId: system.id,
id: `${jsonBus.busId}`
}
await this.repository.addOrUpdateShuttle(constructedShuttle);
}))
}
}));
}
protected async fetchAndUpdateEtaDataForExistingOrderedStops() {
// TODO implement once I figure out how to associate ETA data with shuttles
const systems = await this.repository.getSystems()
await Promise.all(systems.map(async (system: ISystem) => {
const stops = await this.repository.getStopsBySystemId(system.id);
await Promise.all(stops.map(async (stop) => {
const params = {
eta: "3",
stopIds: stop.id,
};
const query = new URLSearchParams(params).toString();
const response = await fetch(`${baseUrl}?${query}`, {
method: "GET",
});
const json = await response.json();
if (json.ETAs && json.ETAs[stop.id]) {
// Continue with the parsing
json.ETAs[stop.id].forEach((jsonEta: any) => {
// Update cache
const shuttleId: string = jsonEta.busId;
const eta: IEta = {
secondsRemaining: jsonEta.secondsSpent,
shuttleId: `${shuttleId}`,
stopId: stop.id,
millisecondsSinceEpoch: Date.now(),
};
this.repository.addOrUpdateEta(eta);
});
}
}));
}))
}
protected async updateStopDataForSystemAndApiResponse(system: ISystem, json: any) {
if (json.stops) {
const jsonStops = Object.values(json.stops);
await Promise.all(jsonStops.map(async (stop: any) => {
const constructedStop: IStop = {
name: stop.name,
id: stop.id,
systemId: system.id,
coordinates: {
latitude: parseFloat(stop.latitude),
longitude: parseFloat(stop.longitude),
},
};
await this.repository.addOrUpdateStop(constructedStop);
}));
}
}
protected async updateOrderedStopDataForExistingStops(json: any) {
if (json.routes) {
await Promise.all(Object.keys(json.routes).map(async (routeId) => {
const jsonOrderedStopData: any[][] = json.routes[routeId].slice(2);
// The API may return the same stop twice to indicate that
// the route runs in a loop
// If the API does not do this, assume the route is one-way
// To account for this, check if ordered stop already exists in repo
// If it does, write to that stop instead
for (let index = 0; index < jsonOrderedStopData.length; index++) {
const orderedStopDataArray = jsonOrderedStopData[index];
const stopId = orderedStopDataArray[1];
let constructedOrderedStop = await this.repository.getOrderedStopByRouteAndStopId(routeId, stopId)
if (constructedOrderedStop === null) {
constructedOrderedStop = {
routeId,
stopId,
position: index + 1,
};
}
if (index >= 1) {
constructedOrderedStop.previousStop = {
routeId,
stopId: jsonOrderedStopData[index - 1][1],
position: index,
};
}
if (index < jsonOrderedStopData.length - 1) {
constructedOrderedStop.nextStop = {
routeId,
stopId: jsonOrderedStopData[index + 1][1],
position: index + 2,
};
}
await this.repository.addOrUpdateOrderedStop(constructedOrderedStop);
}
}));
}
}
protected async updatePolylineDataForExistingRoutesAndApiResponse(json: any) {
if (json.routePoints) {
await Promise.all(Object.keys(json.routePoints).map(async (routeId) => {
const routePoints = json.routePoints[routeId][0];
const existingRoute = await this.repository.getRouteById(routeId);
if (!existingRoute) return;
existingRoute.polylineCoordinates = routePoints.map((point: any) => {
return {
latitude: parseFloat(point.lat),
longitude: parseFloat(point.lng),
};
});
await this.repository.addOrUpdateRoute(existingRoute);
}))
}
}
}

View File

@@ -1,9 +1,8 @@
import { GetterSetterRepository } from "../repositories/GetterSetterRepository";
import { IEta, IRoute, IShuttle, IStop, ISystem } from "../entities/entities";
import { ApiBasedRepositoryLoader } from "./ApiBasedRepositoryLoader";
const timeout = 10000;
const systemIdsToSupport = ["263"];
const baseUrl = "https://passiogo.com/mapGetData.php";
// Ideas to break this into smaller pieces in the future:
// Have one repository data loader running for each supported system
@@ -19,13 +18,14 @@ const baseUrl = "https://passiogo.com/mapGetData.php";
// - OrderedStops: reload every few minutes
// - Systems: reload once a day
export class TimedApiBasedRepositoryLoader {
export class TimedApiBasedRepositoryLoader extends ApiBasedRepositoryLoader {
private shouldBeRunning: boolean = false;
private timer: any;
constructor(
private repository: GetterSetterRepository,
repository: GetterSetterRepository,
) {
super(repository);
this.startFetchDataAndUpdate = this.startFetchDataAndUpdate.bind(this);
}
@@ -64,259 +64,4 @@ export class TimedApiBasedRepositoryLoader {
this.timer = setTimeout(this.startFetchDataAndUpdate, timeout);
}
private async fetchAndUpdateSystemData() {
const params = {
getSystems: "2",
};
const query = new URLSearchParams(params).toString();
const response = await fetch(`${baseUrl}?${query}`);
const json = await response.json()
if (typeof json.all === "object") {
// filter down to supported systems
const filteredSystems = json.all.filter((jsonSystem: any) => systemIdsToSupport.includes(jsonSystem.id));
await Promise.all(filteredSystems.map(async (system: any) => {
const constructedSystem: ISystem = {
id: system.id,
name: system.fullname,
};
await this.repository.addOrUpdateSystem(constructedSystem);
}));
}
}
private async fetchAndUpdateRouteDataForExistingSystems() {
const systems = await this.repository.getSystems();
await Promise.all(systems.map(async (system) => {
const params = {
getRoutes: "2",
};
const formDataJsonObject = {
"systemSelected0": system.id,
"amount": "1",
}
const formData = new FormData();
formData.set("json", JSON.stringify(formDataJsonObject));
const query = new URLSearchParams(params).toString();
const response = await fetch(`${baseUrl}?${query}`, {
method: "POST",
body: formData,
});
const json = await response.json();
if (typeof json.all === "object") {
await Promise.all(json.all.map(async (jsonRoute: any) => {
const constructedRoute: IRoute = {
name: jsonRoute.name,
color: jsonRoute.color,
id: jsonRoute.myid,
polylineCoordinates: [],
systemId: system.id,
};
await this.repository.addOrUpdateRoute(constructedRoute);
}))
}
}));
}
private async fetchAndUpdateStopAndPolylineDataForRoutesInExistingSystems() {
// Fetch from the API
// Pass JSON output into two different methods to update repository
const systems = await this.repository.getSystems();
await Promise.all(systems.map(async (system: ISystem) => {
const params = {
getStops: "2",
};
const formDataJsonObject = {
"s0": system.id,
"sA": 1
};
const formData = new FormData();
formData.set("json", JSON.stringify(formDataJsonObject));
const query = new URLSearchParams(params).toString();
const response = await fetch(`${baseUrl}?${query}`, {
method: "POST",
body: formData,
});
const json = await response.json();
await this.updateStopDataForSystemAndApiResponse(system, json);
await this.updateOrderedStopDataForExistingStops(json);
await this.updatePolylineDataForExistingRoutesAndApiResponse(json);
}));
}
private async fetchAndUpdateShuttleDataForExistingSystems() {
const systems = await this.repository.getSystems();
await Promise.all(systems.map(async (system: ISystem) => {
const params = {
getBuses: "2"
};
const formDataJsonObject = {
"s0": system.id,
"sA": "1"
};
const formData = new FormData();
formData.set("json", JSON.stringify(formDataJsonObject));
const query = new URLSearchParams(params).toString();
const response = await fetch(`${baseUrl}?${query}`, {
method: "POST",
body: formData,
});
const json = await response.json();
if (json.buses && json.buses["-1"] === undefined) {
const jsonBuses = Object.values(json.buses).map((busesArr: any) => {
return busesArr[0];
});
await Promise.all(jsonBuses.map(async (jsonBus: any) => {
const constructedShuttle: IShuttle = {
name: jsonBus.bus,
coordinates: {
latitude: parseFloat(jsonBus.latitude),
longitude: parseFloat(jsonBus.longitude),
},
routeId: jsonBus.routeId,
systemId: system.id,
id: `${jsonBus.busId}`
}
await this.repository.addOrUpdateShuttle(constructedShuttle);
}))
}
}));
}
private async fetchAndUpdateEtaDataForExistingOrderedStops() {
// TODO implement once I figure out how to associate ETA data with shuttles
const systems = await this.repository.getSystems()
await Promise.all(systems.map(async (system: ISystem) => {
const stops = await this.repository.getStopsBySystemId(system.id);
await Promise.all(stops.map(async (stop) => {
const params = {
eta: "3",
stopIds: stop.id,
};
const query = new URLSearchParams(params).toString();
const response = await fetch(`${baseUrl}?${query}`, {
method: "GET",
});
const json = await response.json();
if (json.ETAs && json.ETAs[stop.id]) {
// Continue with the parsing
json.ETAs[stop.id].forEach((jsonEta: any) => {
// Update cache
const shuttleId: string = jsonEta.busId;
const eta: IEta = {
secondsRemaining: jsonEta.secondsSpent,
shuttleId: `${shuttleId}`,
stopId: stop.id,
millisecondsSinceEpoch: Date.now(),
};
this.repository.addOrUpdateEta(eta);
});
}
}));
}))
}
private async updateStopDataForSystemAndApiResponse(system: ISystem, json: any) {
if (json.stops) {
const jsonStops = Object.values(json.stops);
await Promise.all(jsonStops.map(async (stop: any) => {
const constructedStop: IStop = {
name: stop.name,
id: stop.id,
systemId: system.id,
coordinates: {
latitude: parseFloat(stop.latitude),
longitude: parseFloat(stop.longitude),
},
};
await this.repository.addOrUpdateStop(constructedStop);
}));
}
}
private async updateOrderedStopDataForExistingStops(json: any) {
if (json.routes) {
await Promise.all(Object.keys(json.routes).map(async (routeId) => {
const jsonOrderedStopData: any[][] = json.routes[routeId].slice(2);
// The API may return the same stop twice to indicate that
// the route runs in a loop
// If the API does not do this, assume the route is one-way
// To account for this, check if ordered stop already exists in repo
// If it does, write to that stop instead
for (let index = 0; index < jsonOrderedStopData.length; index++) {
const orderedStopDataArray = jsonOrderedStopData[index];
const stopId = orderedStopDataArray[1];
let constructedOrderedStop = await this.repository.getOrderedStopByRouteAndStopId(routeId, stopId)
if (constructedOrderedStop === null) {
constructedOrderedStop = {
routeId,
stopId,
position: index + 1,
};
}
if (index >= 1) {
constructedOrderedStop.previousStop = {
routeId,
stopId: jsonOrderedStopData[index - 1][1],
position: index,
};
}
if (index < jsonOrderedStopData.length - 1) {
constructedOrderedStop.nextStop = {
routeId,
stopId: jsonOrderedStopData[index + 1][1],
position: index + 2,
};
}
await this.repository.addOrUpdateOrderedStop(constructedOrderedStop);
}
}));
}
}
private async updatePolylineDataForExistingRoutesAndApiResponse(json: any) {
if (json.routePoints) {
await Promise.all(Object.keys(json.routePoints).map(async (routeId) => {
const routePoints = json.routePoints[routeId][0];
const existingRoute = await this.repository.getRouteById(routeId);
if (!existingRoute) return;
existingRoute.polylineCoordinates = routePoints.map((point: any) => {
return {
latitude: parseFloat(point.lat),
longitude: parseFloat(point.lng),
};
});
await this.repository.addOrUpdateRoute(existingRoute);
}))
}
}
}