Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate NRE departures script to JavaScript #56

Merged
merged 1 commit into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
121 changes: 121 additions & 0 deletions functions/api/nreDepartures/v1/[[departures]].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
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((nreService) => {
const service = {};

// verify that the service still calls at the filter station (if applicable)
if (crsFilter) {
const allCallingPoints = []
nreService["subsequentCallingPoints"].forEach((callingPoints => {
console.log(callingPoints["callingPoint"]);
allCallingPoints.push(...callingPoints["callingPoint"]);
}))
const callingPoint = allCallingPoints.filter(cp => cp["crs"] === crsFilter)[0];
if (callingPoint["isCancelled"]) {
return;
}
}

// cancellations
service["cancelled"] = nreService["isCancelled"];
if ("cancelReason" in nreService) {
if (nreService["cancelReason"].includes(CANCEL_REASON_PREFIX)) {
service["cancelReason"] = `Due to ${nreService["cancelReason"].substring(CANCEL_REASON_PREFIX.length)}`;
} else {
service["cancelReason"] = nreService["cancelReason"];
}
}

// timings
service["etd"] = nreService["etd"];
service["std"] = nreService["std"];
service["timeForSort"] = service["std"];
if (service["etd"].includes(":")) {
service["timeForSort"] = service["etd"];
}

// other bits
service["destination"] = nreService["destination"][0]["locationName"]; // TODO: handle multiple destinations
service["length"] = nreService["length"].toString();
if (service["length"] === "0") {
service["length"] = "?";
}
service["operator"] = nreService["operator"];
service["operatorCode"] = nreService["operatorCode"];
service["platform"] = "?";
if ("platform" in nreService) {
service["platform"] = nreService["platform"];
}

services.push(service);
});
}

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"];
Loading