Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
BenjaminEHowe committed Jul 17, 2024
1 parent 79a9eff commit a8c0a56
Show file tree
Hide file tree
Showing 3 changed files with 155 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
151 changes: 151 additions & 0 deletions functions/api/nreDepartures/v1/[[departures]].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { USER_AGENT } from "../../../../functions-src/util";

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);
});
}

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

// TODO: sorting

const CANCEL_REASON_PREFIX = "This train has been cancelled because of "
const CRS_REGEX = /^[A-Z]{3}$/;

/*
data = {}
data["location"] = r["locationName"]
if (CRS_FILTER != None):
data["filterLocation"] = r["filterLocationName"]
groups = re.match("^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).\d*(\+\d{2}:\d{2})$", r["generatedAt"]).groups()
data["generatedAt"] = f"{groups[0]}-{groups[1]}-{groups[2]}T{groups[3]}:{groups[4]}:{groups[5]}{groups[6]}"
services = []
if "trainServices" in r:
for service in r["trainServices"]:
thisService = {}
if (CRS_FILTER != None):
filterLocationCallingPointIter = filter(lambda x: (x["crs"] == CRS_FILTER), service["subsequentCallingPoints"][0]["callingPoint"]) # TODO: why is subsequantCallingPoints an array?
thisService["callsAtFilterLocation"] = not next(filterLocationCallingPointIter)["isCancelled"]
thisService["cancelled"] = service["isCancelled"]
if "cancelReason" in service:
if "This train has been cancelled because of " in service["cancelReason"]:
thisService["cancelReason"] = "Due to " + service["cancelReason"][41:]
else:
thisService["cancelReason"] = service["cancelReason"]
thisService["etd"] = service["etd"]
thisService["std"] = service["std"]
thisService["timeForSort"] = thisService["std"]
if ":" in thisService["etd"]:
thisService["timeForSort"] = thisService["etd"]
thisService["destination"] = service["destination"][0]["locationName"] # TODO: handle multiple destinations
thisService["length"] = str(service["length"])
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.append(thisService)
servicesContainsLateNight = False
for service in services:
serviceHour = service["timeForSort"].split(":")[0]
if serviceHour in ("20", "21", "22", "23"):
servicesContainsLateNight = True
break
servicesContainsEarlyMorning = False
for service in services:
serviceHour = service["timeForSort"].split(":")[0]
if serviceHour in ("00", "01", "02", "03"):
servicesContainsEarlyMorning = True
break
if (servicesContainsLateNight and servicesContainsEarlyMorning):
for service in services:
serviceHour = service["timeForSort"].split(":")[0]
serviceMinute = service["timeForSort"].split(":")[1]
if serviceHour in ("00", "01", "02", "03"):
service["timeForSort"] = f"{str(int(serviceHour)+24)}:{serviceMinute}"
data["services"] = sorted(services, key=lambda s: s["timeForSort"])[:NUM_SERVICES]
return data
*/

0 comments on commit a8c0a56

Please sign in to comment.