|
|
@ -1,5 +1,7 @@ |
|
|
|
const csv = require("csv-parse"); |
|
|
|
const axios = require("axios"); |
|
|
|
const path = require("path"); |
|
|
|
const fs = require("fs").promises; |
|
|
|
const { snakeToCamelCase, unzipFile } = require("../../util"); |
|
|
|
|
|
|
|
/** |
|
|
@ -17,15 +19,65 @@ const { snakeToCamelCase, unzipFile } = require("../../util"); |
|
|
|
* @property {string} directionId Route identifier inside the line. |
|
|
|
* @property {string} departureTime Theoretical time at which the |
|
|
|
* vehicle will depart the stop (HH:MM:SS format). |
|
|
|
* @property {string} isTheorical (sic) Whether the arrival time is only |
|
|
|
* a theoretical information. |
|
|
|
* @property {string} isTheorical (sic) True if this is only the planned |
|
|
|
* passing time, false if this is real-time information. |
|
|
|
* @property {string} delaySec Number of seconds before the vehicle arrives |
|
|
|
* at the station. |
|
|
|
* at the station (only if isTheorical is false). |
|
|
|
* @property {string} destArCode Unique network identifier for the final |
|
|
|
* stop of this trip. |
|
|
|
* stop of this trip (only if isTheorical is false). |
|
|
|
*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* Wrap a passing-fetching function to use a filesystem-based cache. |
|
|
|
* |
|
|
|
* @param {function} func Fetching function to wrap. |
|
|
|
* @param {string} cachePath Path to the file to use as a cache (will be |
|
|
|
* overwritten, may be non-existing). |
|
|
|
* @return {function} Wrapped function. |
|
|
|
*/ |
|
|
|
const makeCached = (func, cachePath) => { |
|
|
|
return async function *() { |
|
|
|
try { |
|
|
|
const cacheRaw = await fs.readFile(cachePath, {encoding: "utf8"}); |
|
|
|
const cache = JSON.parse(cacheRaw); |
|
|
|
|
|
|
|
if (Date.now() < cache.timing.nextUpdate) { |
|
|
|
yield cache.timing; |
|
|
|
|
|
|
|
for (const passing of cache.passings) { |
|
|
|
yield passing; |
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
// Ignore missing cache file
|
|
|
|
if (err.code !== 'ENOENT') { |
|
|
|
throw err; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const passings = func(); |
|
|
|
const newCache = { |
|
|
|
timing: (await passings.next()).value, |
|
|
|
passings: [], |
|
|
|
}; |
|
|
|
|
|
|
|
yield newCache.timing; |
|
|
|
|
|
|
|
for await (const passing of passings) { |
|
|
|
newCache.passings.push(passing); |
|
|
|
yield passing; |
|
|
|
} |
|
|
|
|
|
|
|
fs.writeFile(cachePath, JSON.stringify(newCache)); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
const cacheDir = path.join(__dirname, "..", "..", "..", "cache"); |
|
|
|
|
|
|
|
const realtimeEndpoint = "http://data.montpellier3m.fr/node/10732/download"; |
|
|
|
const realtimeCachePath = path.join(cacheDir, "realtime.json"); |
|
|
|
|
|
|
|
/** |
|
|
|
* Fetch real time passings of vehicles across the network. |
|
|
@ -54,9 +106,10 @@ const fetchRealtime = async function *() { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
exports.fetchRealtime = fetchRealtime; |
|
|
|
exports.fetchRealtime = makeCached(fetchRealtime, realtimeCachePath); |
|
|
|
|
|
|
|
const theoreticalEndpoint = "http://data.montpellier3m.fr/node/10731/download"; |
|
|
|
const theoreticalCachePath = path.join(cacheDir, "theoretical.json"); |
|
|
|
|
|
|
|
/** |
|
|
|
* Fetch theoretical passings for the current day across the network. |
|
|
@ -101,4 +154,4 @@ const fetchTheoretical = async function *() { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
exports.fetchTheoretical = fetchTheoretical; |
|
|
|
exports.fetchTheoretical = makeCached(fetchTheoretical, theoreticalCachePath); |
|
|
|