Update data collection functions by train type

This commit is contained in:
Conor McNamara
2023-03-03 18:49:01 +00:00
parent c0ea15f58a
commit b0b5ecd36a
2 changed files with 94 additions and 114 deletions

View File

@ -52,47 +52,40 @@ exports.getStationData = functions.https.onRequest((request, response) => {
}); });
}) })
// function to populate the Firestore database with station data from Irish Rail // helper function to fetch data from Irish Rail given a train type (mainland, suburban, dart)
exports.postStationData = functions.https.onRequest((request, response) => { function callIrishRail(request, response, db, trainTypeCode) {
response.set('Access-Control-Allow-Origin', '*');
response.set('Access-Control-Allow-Credentials', 'true');
cors(request, response, () => { cors(request, response, () => {
axios.get('http://api.irishrail.ie/realtime/realtime.asmx/getAllStationsXML') let url = 'https://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML_WithTrainType?TrainType=' + trainTypeCode
axios.get(url)
.then((res) => { .then((res) => {
var batchWrite = db.batch();
// XML to JSON // XML to JSON
parseString(res.data, function(err, result) { parseString(res.data, function(err, result) {
let jsonStr = JSON.stringify(result); let jsonStr = JSON.stringify(result);
let jsonObj = JSON.parse(jsonStr); let jsonObj = JSON.parse(jsonStr);
let jsonData = jsonObj.ArrayOfObjStation.objStation; let jsonData = jsonObj.ArrayOfObjTrainPositions.objTrainPositions;
// batch delete all of the "stations" collection's documents jsonData.forEach((doc) => {
var db = admin.firestore(); // ignore trains with longitudes or latitudes equal zero
admin.firestore().collection('stations').get().then((snapshot) => { if (!(doc["TrainLongitude"] == 0 || doc["TrainLatitude"] == 0)) {
var batchDelete = db.batch(); doc["TrainType"] = [trainTypeCode]
snapshot.forEach(doc => { // set the train's code as the document ID
batchDelete.delete(doc.ref); var docID = db.collection('liveTrainData').doc(doc["TrainCode"][0]);
}); batchWrite.set(docID, doc);
}
});
batchDelete.commit().then(function() { batchWrite.commit()
// batch write all station JSON objects to the "stations" collection .catch((error) => {
var batchWrite = db.batch(); return false;
jsonData.forEach((doc) => {
// set the station's ID as the document ID
var docID = db.collection('stations').doc(doc["StationCode"][0]);
batchWrite.set(docID, doc);
});
batchWrite.commit().then(function () {
response.send({data: "Successfully fetched and uploaded station data from Irish Rail"});
});
})
}) })
}) })
}) })
.catch((error) => {
return false;
})
}) })
}) }
// function to populate the Firestore database with live train data from Irish Rail // function to populate the Firestore database with live train data from Irish Rail
exports.postLiveTrainData = functions.https.onRequest((request, response) => { exports.postLiveTrainData = functions.https.onRequest((request, response) => {
@ -100,44 +93,23 @@ exports.postLiveTrainData = functions.https.onRequest((request, response) => {
response.set('Access-Control-Allow-Credentials', 'true'); response.set('Access-Control-Allow-Credentials', 'true');
cors(request, response, () => { cors(request, response, () => {
axios.get('http://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML') // batch delete all of the "liveTrainData" collections's documents
.then((res) => { var db = admin.firestore();
// XML to JSON admin.firestore().collection('liveTrainData').get().then((snapshot) => {
parseString(res.data, function(err, result) { var batchDelete = db.batch();
let jsonStr = JSON.stringify(result); snapshot.forEach(doc => {
let jsonObj = JSON.parse(jsonStr); batchDelete.delete(doc.ref);
let jsonData = jsonObj.ArrayOfObjTrainPositions.objTrainPositions; });
// batch delete all of the "liveTrainData" collections's documents // fetch data using codes M (mainland), S (suburban), D (dart)
var db = admin.firestore(); batchDelete.commit().then(function() {
admin.firestore().collection('liveTrainData').get().then((snapshot) => { if (callIrishRail(request, response, db, "M") == false ||
var batchDelete = db.batch(); callIrishRail(request, response, db, "S") == false ||
snapshot.forEach(doc => { callIrishRail(request, response, db, "D") == false) {
batchDelete.delete(doc.ref); response.send({data: "Error fetching data from the IrishRail API"});
}); }
response.send({data: "Successfully fetched and uploaded live train data from Irish Rail"});
batchDelete.commit().then(function() { })
// batch write all train JSON objects to the "liveTrainData" collection })
var batchWrite = db.batch(); })
jsonData.forEach((doc) => {
// ignore trains with longitudes or latitudes equal zero
if (!(doc["TrainLongitude"] == 0 || doc["TrainLatitude"] == 0)) {
// set the train's code as the document ID
var docID = db.collection('liveTrainData').doc(doc["TrainCode"][0]);
batchWrite.set(docID, doc);
}
});
batchWrite.commit().then(function () {
response.send({data: "Successfully fetched and uploaded live train data from Irish Rail"});
});
})
})
})
})
.catch((error) => {;
response.send({data: "Error fetching data from the IrishRail API"});
})
});
}) })

View File

@ -1,11 +1,14 @@
<template> <template>
<h2>Insights:</h2> <h2>Insights:</h2>
<p>Total number of trains: {{ this.numTrains }}</p> <p>Total number of trains: {{ this.insights["totalNumTrains"] }}</p>
<p>Number of actively running trains: {{ this.numRunningTrains }}</p> <p>Number of actively running trains: {{ this.insights["numRunningTrains"] }}</p>
<p>Percentage late: {{ this.percentageLate }}%</p> <p>Percentage late: {{ this.insights["percentageLate"] }}%</p>
<p>Percentage early or ontime: {{ this.percentageEarly }}%</p> <p>Percentage early or ontime: {{ this.insights["percentageNotLate"] }}%</p>
<p v-if="this.latestTrain['TrainCode']">Latest train: {{ this.latestTrain["TrainCode"][0] }}, {{ this.latestTrain["Direction"][0] }}, {{ this.latestTime }} mins late</p> <p v-if="this.latestTrain['TrainCode']">Latest train: {{ this.latestTrain["TrainCode"][0] }}, {{ this.latestTrain["Direction"][0] }}, {{ this.insights["latestTime"] }} mins late</p>
<p v-if="this.earliestTrain['TrainCode']">Earliest train: {{ this.earliestTrain["TrainCode"][0] }}, {{ this.earliestTrain["Direction"][0] }}, {{ this.earliestTime * -1 }} mins early</p> <p v-if="this.earliestTrain['TrainCode']">Earliest train: {{ this.earliestTrain["TrainCode"][0] }}, {{ this.earliestTrain["Direction"][0] }}, {{ this.insights["earliestTime"] * -1 }} mins early</p>
<p>Mainland: {{ this.insights["numMainland"] }}</p>
<p>Suburban: {{ this.insights["numSuburban"] }}</p>
<p>Darts: {{ this.insights["numDart"] }}</p>
<button @click="getLiveTrainData">Fetch Data</button> <button @click="getLiveTrainData">Fetch Data</button>
<button @click="postLiveTrainData">Populate Database</button> <button @click="postLiveTrainData">Populate Database</button>
@ -31,16 +34,17 @@
</div> </div>
</transition> </transition>
<ol-map :loadTilesWhileAnimating="true" :loadTilesWhileInteracting="true" style="height: 100vh; width: 100vw"> <ol-map :loadTilesWhileAnimating="true" :loadTilesWhileInteracting="true" style="height: 100vh; width: 100vw">
<ol-view ref="view" :center="center" :rotation="rotation" :zoom="zoom" :projection="projection" /> <ol-view ref="view" :center="center" :rotation="rotation" :zoom="zoom" :projection="projection" />
<ol-tile-layer> <ol-tile-layer>
<ol-source-osm /> <ol-source-osm />
</ol-tile-layer> </ol-tile-layer>
<template v-for="coordinate, i in coordinates" :position="inline-block"> <template v-for="coordinate, i in coordinates" :position="inline-block">
<!-- overlay offset is the size of the image so that it is centered--> <!-- overlay offset is the size of the image so that it is centered-->
<ol-overlay :position="coordinate" :positioning="center-center" :offset="[-14,-16]"> <ol-overlay :position="coordinate" :positioning="center-center" :offset="[-14,-16]">
<div class="overlay-content" @click="getSelectedTrain(i)"> <div class="overlay-content" @click="getSelectedTrain(i)">
<img src="../assets/red-train-solid.png" class="trainMapIcon" alt="Train Icon">
<img v-if="isTrainLate(i)" src="../assets/red-train-solid.png" class="trainMapIcon" alt="Train Icon"> <img v-if="isTrainLate(i)" src="../assets/red-train-solid.png" class="trainMapIcon" alt="Train Icon">
<img v-else src="../assets/green-train-solid.png" class="trainMapIcon" alt="Train Icon"> <img v-else src="../assets/green-train-solid.png" class="trainMapIcon" alt="Train Icon">
</div> </div>
@ -85,15 +89,9 @@ export default {
selectedDataMap: {}, selectedDataMap: {},
display: false, display: false,
numTrains: 0, insights: {},
numRunningTrains: 0,
numLateRunningTrains: 0,
latestTrain: {}, latestTrain: {},
earliestTrain: {}, earliestTrain: {},
percentageEarly: 0,
percentageLate: 0,
latestTime: 0,
earliestTime: 0,
} }
}, },
@ -126,33 +124,44 @@ export default {
getData().then((response) => { getData().then((response) => {
try { try {
this.dbLiveTrainData = response.data; this.dbLiveTrainData = response.data;
console.log(this.dbLiveTrainData) this.insights = {"numRunningTrains": 0,
"numLateRunningTrains": 0,
this.numRunningTrains = 0; "numMainland": 0,
this.numTrains = 0; "numSuburban": 0,
this.numLateRunningTrains = 0; "numDart": 0}
var latest = null var latest = null
var currLatestTime = 0
var earliest = null var earliest = null
var currEarliestTime = 0 var currLatestTime = null
var currEarliestTime = null
// create an array of coordinates and hashmap with the key-values {index: JSON obj} // create an array of coordinates and hashmap with the key-values {index: JSON obj}
for(var i=0; i<this.dbLiveTrainData.length; i++) { for(var i=0; i<this.dbLiveTrainData.length; i++) {
this.coordinates[i] = ref(fromLonLat([this.dbLiveTrainData[i]["TrainLongitude"][0], this.dbLiveTrainData[i]["TrainLatitude"][0]])) let train = this.dbLiveTrainData[i];
this.allDataMap[i] = this.dbLiveTrainData[i]; this.coordinates[i] = ref(fromLonLat([train["TrainLongitude"][0], train["TrainLatitude"][0]]))
this.allDataMap[i] = train;
if (train["TrainType"][0] == "M") {
this.insights["numMainland"] += 1;
}
else if (train["TrainType"][0] == "S") {
this.insights["numSuburban"] += 1;
}
else if (train["TrainType"][0] == "D") {
this.insights["numDart"] += 1;
}
// check if the train is running // check if the train is running
if (this.dbLiveTrainData[i]["TrainStatus"][0] == "R") { if (this.dbLiveTrainData[i]["TrainStatus"][0] == "R") {
this.numRunningTrains += 1; this.insights["numRunningTrains"] += 1;
let publicMessage = train["PublicMessage"][0];
let publicMessage = this.dbLiveTrainData[i]["PublicMessage"][0]; let startTimeStr = publicMessage.indexOf("(");
let startTimeStr = publicMessage.indexOf("(")
// late // late
if (publicMessage[startTimeStr+1] != "-" && publicMessage[startTimeStr+1] != "0") { if (publicMessage[startTimeStr+1] != "-" && publicMessage[startTimeStr+1] != "0") {
this.numLateRunningTrains += 1; this.insights["numLateRunningTrains"] += 1;
if (!latest) { if (!latest) {
latest = this.dbLiveTrainData[i]; latest = train;
} }
let timeEnd = publicMessage.indexOf(" ", startTimeStr+1); let timeEnd = publicMessage.indexOf(" ", startTimeStr+1);
@ -160,34 +169,34 @@ export default {
// new latest train // new latest train
if (num > currLatestTime) { if (num > currLatestTime) {
latest = this.dbLiveTrainData[i] latest = train
currLatestTime = num currLatestTime = num
} }
} }
// early or ontime // early or ontime
else { else {
if (!earliest) { if (!earliest) {
earliest = this.dbLiveTrainData[i]; earliest = train;
} }
let timeEnd = publicMessage.indexOf(" ", startTimeStr+1); let timeEnd = publicMessage.indexOf(" ", startTimeStr+1);
let num = parseInt(publicMessage.substring(startTimeStr+1, timeEnd)) let num = parseInt(publicMessage.substring(startTimeStr+1, timeEnd))
// new earliest train, negative as early trains defined as negative x mins late // new earliest train, negative as early trains defined as negative x mins late
if (num < currEarliestTime) { if (num < currEarliestTime) {
earliest = this.dbLiveTrainData[i] earliest = train
currEarliestTime = num currEarliestTime = num
} }
} }
} }
} }
this.percentageLate = ((this.numLateRunningTrains / this.numRunningTrains) * 100).toFixed(2); this.insights["percentageLate"] = ((this.insights["numLateRunningTrains"] / this.insights["numRunningTrains"]) * 100).toFixed(2);
this.percentageEarly = 100 - this.percentageLate; this.insights["percentageNotLate"] = (100 - this.insights["percentageLate"]).toFixed(2);
this.numTrains = Object.keys(this.allDataMap).length; this.insights["totalNumTrains"] = Object.keys(this.allDataMap).length;
this.insights["latestTime"] = currLatestTime;
this.insights["earliestTime"] = currEarliestTime
this.latestTrain = latest; this.latestTrain = latest;
this.earliestTrain = earliest; this.earliestTrain = earliest;
this.latestTime = currLatestTime;
this.earliestTime = currEarliestTime;
loader.hide(); loader.hide();
} }
catch (error) { catch (error) {
@ -224,7 +233,7 @@ export default {
return false; return false;
}, },
// ---------------- TESTING ---------------- // used for mobile
postLiveTrainData() { postLiveTrainData() {
const functions = getFunctions(app); const functions = getFunctions(app);
let host = window.location.hostname let host = window.location.hostname
@ -232,12 +241,10 @@ export default {
connectFunctionsEmulator(functions, host, 5001); connectFunctionsEmulator(functions, host, 5001);
} }
const postData = httpsCallable(functions, 'postLiveTrainData'); const postData = httpsCallable(functions, 'postLiveTrainData');
postData().then((response) => { postData().then((response) => {
this.getLiveTrainData() this.getLiveTrainData()
}) })
} }
// ---------------- TESTING ----------------
} }
} }
</script> </script>
@ -351,6 +358,7 @@ export default {
float: right; float: right;
right: 0%; right: 0%;
top: 0%; top: 0%;
width:100%; width:100%;
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;