Skip to content

Commit

Permalink
Migrate NRE departures script to JavaScript
Browse files Browse the repository at this point in the history
  • Loading branch information
BenjaminEHowe committed Jul 17, 2024
1 parent 79a9eff commit 0605c8a
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ _site
.jekyll-metadata
vendor
node_modules

# wrangler
.dev.vars
.wrangler
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The easiest way to run Jekyll is using Docker:

### Using Wrangler

To test some of the dynamic aspects of the website (e.g. [Cloudflare Pages Functions](https://developers.cloudflare.com/pages/functions/)), it is necessary to use [Wrangler](https://developers.cloudflare.com/workers/wrangler/).
To test some of the dynamic aspects of the website (e.g. [Cloudflare Pages Functions](https://developers.cloudflare.com/pages/functions/)), it is necessary to use [Wrangler](https://developers.cloudflare.com/workers/wrangler/). You may also need [a local `.dev.vars` file](https://developers.cloudflare.com/pages/functions/bindings/#interact-with-your-secrets-locally) containing secrets.

To set up a local database:

Expand Down
1 change: 1 addition & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ include:
- _redirects

exclude:
- .wrangler
- functions
- functions-src
- sql
Expand Down
110 changes: 110 additions & 0 deletions functions/api/nreDepartures/v1/[[departures]].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { USER_AGENT } from "../../../../functions-src/util";

// TODO: allow these values to be customised through request params
const NUM_SERVICES = 4;
const TIME_OFFSET = 5;

export async function onRequest(context) {
if (context.request.method !== "GET") {
return new Response("Invalid request method", { status: 405 });
}

const crsLocation = context.params.departures[0];
if (!crsLocation.match(CRS_REGEX)) {
return new Response(`Invalid crsLocation: ${crsLocation}`, { status: 400 });
}
const crsFilter = context.params.departures[1];
if (crsFilter && !crsFilter.match(CRS_REGEX)) {
return new Response(`Invalid crsFilter: ${crsFilter}`, { status: 400 });
}

const params = { "timeOffset": TIME_OFFSET };
if (crsFilter) {
params["filterCrs"] = crsFilter;
}
const paramsString = new URLSearchParams(params).toString();
const response = await fetch(new Request(`https://api1.raildata.org.uk/1010-live-departure-board-dep/LDBWS/api/20220120/GetDepBoardWithDetails/${crsLocation}?${paramsString}`, {
headers: {
"user-agent": USER_AGENT,
"x-apikey": context.env.RAILDATA_LIVE_DEPARTURES_API_KEY,
},
}));
const nreDepartures = await response.json();

const data = {};
data["location"] = nreDepartures["locationName"];
if (crsFilter) {
data["filterLocation"] = nreDepartures["filterLocationName"];
}
const tsRe = nreDepartures["generatedAt"].match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).\d*(\+\d{2}:\d{2})$/);
data["generatedAt"] = `${tsRe[1]}-${tsRe[2]}-${tsRe[3]}T${tsRe[4]}:${tsRe[5]}:${tsRe[6]}${tsRe[7]}`;
const services = []
if ("trainServices" in nreDepartures) {
nreDepartures["trainServices"].forEach((service) => {
const thisService = {};
if (crsFilter) {
let allCallingPoints = []
service["subsequentCallingPoints"].forEach((callingPoints => {
console.log(callingPoints["callingPoint"]);
allCallingPoints.push(...callingPoints["callingPoint"]);
}))
let callingPoint = allCallingPoints.filter(cp => cp["crs"] === crsFilter)[0];
if (callingPoint["isCancelled"]) {
return;
}
}
thisService["cancelled"] = service["isCancelled"];
if ("cancelReason" in service) {
if (service["cancelReason"].includes(CANCEL_REASON_PREFIX)) {
thisService["cancelReason"] = `Due to ${service["cancelReason"].substring(CANCEL_REASON_PREFIX.length)}`;
}
}
thisService["etd"] = service["etd"];
thisService["std"] = service["std"];
thisService["timeForSort"] = thisService["std"];
if (thisService["etd"].includes(":")) {
thisService["timeForSort"] = thisService["etd"];
}
thisService["destination"] = service["destination"][0]["locationName"]; // TODO: handle multiple destinations
thisService["length"] = service["length"].toString();
if (thisService["length"] == "0") {
thisService["length"] = "?";
}
thisService["operator"] = service["operator"];
thisService["operatorCode"] = service["operatorCode"];
thisService["platform"] = "?";
if ("platform" in service) {
thisService["platform"] = service["platform"];
}
services.push(thisService);
});
}

let servicesContainsEarlyMorning = false;
let servicesContainsLateNight = false;
services.forEach((service) => {
const serviceHour = service["timeForSort"].split(":")[0];
if (serviceHour in SERVICE_HOURS_EARLY_MORNING) {
servicesContainsEarlyMorning = true;
} else if (serviceHour in SERVICE_HOURS_LATE_NIGHT) {
servicesContainsLateNight = true;
}
});
if (servicesContainsLateNight && servicesContainsEarlyMorning) {
services.forEach((service, index) => {
const serviceHour = service["timeForSort"].split(":")[0];
const serviceMinute = service["timeForSort"].split(":")[1];
if (serviceHour in SERVICE_HOURS_EARLY_MORNING) {
this[index]["timeForSort"] = `${parseInt(serviceHour)+24}:${serviceMinute}`;
}
});
}

data["services"] = services.sort().splice(0, NUM_SERVICES);
return Response.json(data);
}

const CANCEL_REASON_PREFIX = "This train has been cancelled because of "
const CRS_REGEX = /^[A-Z]{3}$/;
const SERVICE_HOURS_EARLY_MORNING = ["00", "01", "02", "03"];
const SERVICE_HOURS_LATE_NIGHT = ["20", "21", "22", "23"];

0 comments on commit 0605c8a

Please sign in to comment.