diff --git a/README.md b/README.md index 41558ce..127b6b9 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ To kill the npm process do `CTRL + C` in your terminal. To kill the firebase emulators run `fg` to bring the process to the foreground, then do `CTRL + C` # Links -Main site: [irishrailtracker.web.app](https://irishrailtracker.web.app/) +Deployed site: [irishrailtracker.web.app](https://irishrailtracker.web.app/) Jira: [trainenthusiasts.atlassian.net](https://trainenthusiasts.atlassian.net/jira/software/projects/TE/boards/1) diff --git a/functions/index.js b/functions/index.js index 5cc024f..1e7e4db 100644 --- a/functions/index.js +++ b/functions/index.js @@ -18,7 +18,7 @@ exports.getStationData = functions.https.onRequest((request, response) => { // fetch the "stations" collection admin.firestore().collection('stations').get().then((snapshot) => { if (snapshot.empty) { - response.send({data: "Error fetching station data from the database"}) + response.status(404).send({data: "Error fetching station data from the database"}) return; } // iterate through each of the collection's documents @@ -32,39 +32,69 @@ exports.getStationData = functions.https.onRequest((request, response) => { // function to populate the Firestore database with station data from Irish Rail exports.postStationData = functions.https.onRequest((request, response) => { + // helper functon to parse station JSON objects + function parseJSON(result) { + let jsonStr = JSON.stringify(result); + let jsonObj = JSON.parse(jsonStr); + let jsonData = jsonObj.ArrayOfObjStation.objStation; + return jsonData; + } + + // helper function to write to the database + function batchWriteDB(request, response, db, jsonData, dartCodes, stationTypeCode) { + response.set('Access-Control-Allow-Origin', '*'); + response.set('Access-Control-Allow-Credentials', 'true'); + + cors(request, response, () => { + var batchWrite = db.batch(); + jsonData.forEach((doc) => { + // append if the dartCodes hashset is empty or the current station is not present + if (dartCodes.size == 0 || !dartCodes.has(doc["StationCode"][0])) { + doc["StationType"] = [stationTypeCode] + var docID = db.collection('stations').doc(doc["StationCode"][0]) + batchWrite.set(docID, doc); + } + }) + batchWrite.commit() + }) + } + response.set('Access-Control-Allow-Origin', '*'); response.set('Access-Control-Allow-Credentials', 'true'); - cors(request, response, () => { - axios.get('http://api.irishrail.ie/realtime/realtime.asmx/getAllStationsXML') - .then((res) => { + // fetch dart stations and classify as dart stations + axios.get('http://api.irishrail.ie/realtime/realtime.asmx/getAllStationsXML_WithStationType?StationType=D').then(res => { // XML to JSON parseString(res.data, function(err, result) { - let jsonStr = JSON.stringify(result); - let jsonObj = JSON.parse(jsonStr); - let jsonData = jsonObj.ArrayOfObjStation.objStation; + let jsonData = parseJSON(result) - // batch delete all of the "stations" collection's documents - var db = admin.firestore(); + // batch delete all of the station collection's documents + var db = admin.firestore() admin.firestore().collection('stations').get().then((snapshot) => { - var batchDelete = db.batch(); + var batchDelete = db.batch() snapshot.forEach(doc => { - batchDelete.delete(doc.ref); - }); + batchDelete.delete(doc.ref) + }) batchDelete.commit().then(function() { - // batch write all station JSON objects to the "stations" collection - var batchWrite = db.batch(); + // store all dart codes into a hashset + // compare these with the station call with code "all" to avoid duplicates + let dartCodes = new Set() + batchWriteDB(request, response, db, jsonData, dartCodes, "Dart") + // populate the dartCodes hashset 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); - }); + dartCodes.add(doc["StationCode"][0]) + }) - batchWrite.commit().then(function () { - response.send({data: "Successfully fetched and uploaded station data from Irish Rail"}); - }); + // fetch all train stations + axios.get('http://api.irishrail.ie/realtime/realtime.asmx/getAllStationsXML_WithStationType?StationType=A').then(res => { + parseString(res.data, function(err, result) { + let jsonData = parseJSON(result) + batchWriteDB(request, response, db, jsonData, dartCodes, "Train") + response.send({data: "Successfully fetched and upload station data from Irish Rail"}) + }) + }) }) }) }) @@ -96,12 +126,12 @@ exports.getLiveTrainData = functions.https.onRequest((request, response) => { // function to populate the Firestore database with live train data from Irish Rail exports.postLiveTrainData = functions.https.onRequest((request, response) => { - // helper function to parse train objects + // helper function to parse train JSON objects function parseJSON(result) { - let jsonStr = JSON.stringify(result); - let jsonObj = JSON.parse(jsonStr); - let jsonData = jsonObj.ArrayOfObjTrainPositions.objTrainPositions; - return jsonData; + let jsonStr = JSON.stringify(result); + let jsonObj = JSON.parse(jsonStr); + let jsonData = jsonObj.ArrayOfObjTrainPositions.objTrainPositions; + return jsonData; } // helper function to write to the database @@ -126,7 +156,7 @@ exports.postLiveTrainData = functions.https.onRequest((request, response) => { response.set('Access-Control-Allow-Origin', '*'); response.set('Access-Control-Allow-Credentials', 'true'); cors(request, response, () => { - // fetch mainland trains + // fetch mainland trains and classify as trains axios.get('https://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML_WithTrainType?TrainType=M').then(res => { // XML to JSON parseString(res.data, function(err, result) { @@ -141,20 +171,20 @@ exports.postLiveTrainData = functions.https.onRequest((request, response) => { }); batchDelete.commit().then(function() { - // batch write all station JSON objects to the liveTrainData collection - batchWriteDB(request, response, db, jsonData, "M"); + // batch write all train JSON objects to the liveTrainData collection + batchWriteDB(request, response, db, jsonData, "Train"); - // fetch suburban trains + // fetch suburban trains and classify as trains axios.get('https://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML_WithTrainType?TrainType=S').then(res => { parseString(res.data, function(err, result) { let jsonData = parseJSON(result) - batchWriteDB(request, response, db, jsonData, "S"); + batchWriteDB(request, response, db, jsonData, "Train"); - // fetch dart trains + // fetch dart trains and classify as darts axios.get('https://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML_WithTrainType?TrainType=D').then(res => { parseString(res.data, function(err, result) { let jsonData = parseJSON(result) - batchWriteDB(request, response, db, jsonData, "D"); + batchWriteDB(request, response, db, jsonData, "Dart"); response.send({data: "Successfully fetched and uploaded live train data from Irish Rail"}); }) }) diff --git a/functions/test/index.test.js b/functions/test/index.test.js index ce172dc..60a5dcd 100644 --- a/functions/test/index.test.js +++ b/functions/test/index.test.js @@ -32,6 +32,7 @@ describe('Firebase cloud function tests', function() { expect(result.body.data[0]).haveOwnProperty('StationLongitude'); expect(result.body.data[0]).haveOwnProperty('StationCode'); expect(result.body.data[0]).haveOwnProperty('StationId'); + expect(result.body.data[0]).haveOwnProperty('StationType'); }), this.timeout(100000); diff --git a/src/assets/base.css b/src/assets/base.css deleted file mode 100644 index 71dc55a..0000000 --- a/src/assets/base.css +++ /dev/null @@ -1,74 +0,0 @@ -/* color palette from */ -:root { - --vt-c-white: #ffffff; - --vt-c-white-soft: #f8f8f8; - --vt-c-white-mute: #f2f2f2; - - --vt-c-black: #181818; - --vt-c-black-soft: #222222; - --vt-c-black-mute: #282828; - - --vt-c-indigo: #2c3e50; - - --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); - --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); - --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); - --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); - - --vt-c-text-light-1: var(--vt-c-indigo); - --vt-c-text-light-2: rgba(60, 60, 60, 0.66); - --vt-c-text-dark-1: var(--vt-c-white); - --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); -} - -/* semantic color variables for this project */ -:root { - --color-background: var(--vt-c-white); - --color-background-soft: var(--vt-c-white-soft); - --color-background-mute: var(--vt-c-white-mute); - - --color-border: var(--vt-c-divider-light-2); - --color-border-hover: var(--vt-c-divider-light-1); - - --color-heading: var(--vt-c-text-light-1); - --color-text: var(--vt-c-text-light-1); - - --section-gap: 160px; -} - -@media (prefers-color-scheme: dark) { - :root { - --color-background: var(--vt-c-black); - --color-background-soft: var(--vt-c-black-soft); - --color-background-mute: var(--vt-c-black-mute); - - --color-border: var(--vt-c-divider-dark-2); - --color-border-hover: var(--vt-c-divider-dark-1); - - --color-heading: var(--vt-c-text-dark-1); - --color-text: var(--vt-c-text-dark-2); - } -} - -*, -*::before, -*::after { - box-sizing: border-box; - margin: 0; - position: relative; - font-weight: normal; -} - -body { - min-height: 100vh; - color: var(--color-text); - background: var(--color-background); - transition: color 0.5s, background-color 0.5s; - line-height: 1.6; - font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, - Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; - font-size: 15px; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} diff --git a/src/assets/logo.svg b/src/assets/logo.svg deleted file mode 100644 index bc826fe..0000000 --- a/src/assets/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/assets/main.css b/src/assets/main.css deleted file mode 100644 index e8667cd..0000000 --- a/src/assets/main.css +++ /dev/null @@ -1,35 +0,0 @@ -@import './base.css'; - -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - - font-weight: normal; -} - -a, -.green { - text-decoration: none; - color: hsla(160, 100%, 37%, 1); - transition: 0.4s; -} - -@media (hover: hover) { - a:hover { - background-color: hsla(160, 100%, 37%, 0.2); - } -} - -@media (min-width: 1024px) { - body { - display: flex; - place-items: center; - } - - #app { - display: grid; - grid-template-columns: 1fr 1fr; - padding: 0 2rem; - } -} diff --git a/src/assets/station.png b/src/assets/station.png new file mode 100644 index 0000000..871fba1 Binary files /dev/null and b/src/assets/station.png differ diff --git a/src/components/BarChart.vue b/src/components/BarChart.vue index ce6773f..6a43682 100644 --- a/src/components/BarChart.vue +++ b/src/components/BarChart.vue @@ -1,6 +1,6 @@ + - +} + \ No newline at end of file diff --git a/src/components/Navbar.vue b/src/components/Navbar.vue index 429422a..3d47421 100644 --- a/src/components/Navbar.vue +++ b/src/components/Navbar.vue @@ -33,9 +33,7 @@ \ No newline at end of file diff --git a/src/components/SidebarPanel.vue b/src/components/SidebarPanel.vue deleted file mode 100644 index 48a1f5c..0000000 --- a/src/components/SidebarPanel.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - - - diff --git a/src/components/StationSidebar.vue b/src/components/StationSidebar.vue new file mode 100644 index 0000000..cb9db7c --- /dev/null +++ b/src/components/StationSidebar.vue @@ -0,0 +1,66 @@ + + + + + \ No newline at end of file diff --git a/src/components/TrainSidebar.vue b/src/components/TrainSidebar.vue new file mode 100644 index 0000000..1d0d341 --- /dev/null +++ b/src/components/TrainSidebar.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/src/components/icons/IconCommunity.vue b/src/components/icons/IconCommunity.vue deleted file mode 100644 index 2dc8b05..0000000 --- a/src/components/icons/IconCommunity.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/src/components/icons/IconDocumentation.vue b/src/components/icons/IconDocumentation.vue deleted file mode 100644 index 6d4791c..0000000 --- a/src/components/icons/IconDocumentation.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/src/components/icons/IconEcosystem.vue b/src/components/icons/IconEcosystem.vue deleted file mode 100644 index c3a4f07..0000000 --- a/src/components/icons/IconEcosystem.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/src/components/icons/IconSupport.vue b/src/components/icons/IconSupport.vue deleted file mode 100644 index 7452834..0000000 --- a/src/components/icons/IconSupport.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/src/components/icons/IconTooling.vue b/src/components/icons/IconTooling.vue deleted file mode 100644 index 660598d..0000000 --- a/src/components/icons/IconTooling.vue +++ /dev/null @@ -1,19 +0,0 @@ - - diff --git a/src/components/pieChart.vue b/src/components/pieChart.vue index c6f9de6..21ef2c1 100644 --- a/src/components/pieChart.vue +++ b/src/components/pieChart.vue @@ -1,47 +1,55 @@ \ No newline at end of file diff --git a/src/pages/InsightsPage.vue b/src/pages/InsightsPage.vue index 3772ae4..a7e75b6 100644 --- a/src/pages/InsightsPage.vue +++ b/src/pages/InsightsPage.vue @@ -1,35 +1,46 @@ diff --git a/src/store/store.js b/src/store/store.js index c882f8a..260206d 100644 --- a/src/store/store.js +++ b/src/store/store.js @@ -5,9 +5,11 @@ export const store = reactive({ latestTrain: {}, earliestTrain: {}, orderedTrains: [], - selectedDataMap: {}, + selectedTrain: {}, + selectedStation: {}, rawData: {}, - display: false, + displaySelectedTrain: false, + displayedSelectedStation: false, setInsights(insights) { this.insights = insights @@ -33,11 +35,19 @@ export const store = reactive({ this.orderedTrains = unorderedTrains }, - setSelectedDataMap(selectedDataMap) { - this.selectedDataMap = selectedDataMap + setSelectedTrain(selectedTrain) { + this.selectedTrain = selectedTrain + }, + + setSelectedStation(selectedStation) { + this.selectedStation = selectedStation }, - setDisplay(bool) { - this.display = bool + setDisplaySelectedTrain(bool) { + this.displaySelectedTrain = bool + }, + + setDisplaySelectedStation(bool) { + this.displaySelectedStation = bool } }) \ No newline at end of file