Compare commits
4 Commits
681d50e6b6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 45472e055f | |||
| bd2d0aeee6 | |||
| 26510d93c7 | |||
| d84f80a998 |
+183
-12
@@ -8,6 +8,92 @@
|
||||
<link rel="icon" type="image/x-icon" href="https://cdn.ballast-data.co.uk/Icon.svg">
|
||||
|
||||
<style>
|
||||
html {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
background-color: var(--A1);
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
min-height: 100%;
|
||||
width: 100%;"
|
||||
background-color: var(--A2);
|
||||
padding-left: 18px;
|
||||
padding-right: 18px;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--A0);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
background-color: var(--A0);
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
width: fit-content;
|
||||
min-width: 35em;
|
||||
color: var(--A0);
|
||||
background-color: var(--C2);
|
||||
border: 6px solid var(--C1);
|
||||
border-radius: 0 0 9px 9px;
|
||||
padding: 12px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: fit-content;
|
||||
background-color: white;
|
||||
border: 9px solid var(--B2);
|
||||
border-radius: 9px;
|
||||
padding: 12px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.branding {
|
||||
width: fit-content;
|
||||
border-bottom: 9px solid var(--C1);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.query {
|
||||
width: fit-content;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
width: fit-content;
|
||||
font-size: 0.8em;
|
||||
color: var(--A0);
|
||||
background-color: var(--D2);
|
||||
border: 6px solid var(--D1);
|
||||
border-radius: 9px 9px 0 0;
|
||||
padding: 12px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.level0 {
|
||||
text-align: right;
|
||||
}
|
||||
@@ -16,24 +102,59 @@
|
||||
.col2 {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: "Merriweather Sans";
|
||||
font-size: 1em;
|
||||
border: 3px solid var(--A1);
|
||||
}
|
||||
input:hover {
|
||||
background-color: var(--C2);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function querySt(attribute) {
|
||||
url = window.location.search.substring(1);
|
||||
substrings = url.split('&');
|
||||
for (i=0;i<substrings.length;i++) {
|
||||
ft = substrings[i].split('=');
|
||||
if (ft[0] == attribute) {
|
||||
return ft[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function prefill(attribute) {
|
||||
var value = querySt(attribute);
|
||||
value = decodeURIComponent(value);
|
||||
|
||||
if(value !== 'undefined'){
|
||||
document.getElementById(attribute).value = value;
|
||||
} else {
|
||||
document.getElementById(attribute).value = '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body style="padding: 0px; margin: 0px;">
|
||||
<body>
|
||||
|
||||
<div style="width: 100%; padding: 0px; margin: 0px; background-color: var(--A0);">
|
||||
<div class="wrapper">
|
||||
|
||||
<div style="margin-left: auto; margin-right: auto; width: fit-content; background-color: var(--C2); color: var(--A0); margin-top: 0px; padding: 12px; border-radius: 0 0 9px 9px; border: 6px solid var(--C1)">
|
||||
<div class="header">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://cdn.ballast-data.co.uk/Icon-NB.svg">
|
||||
<a href="/fares"><img src="https://cdn.ballast-data.co.uk/Icon-NB.svg"></a>
|
||||
</td>
|
||||
<td>
|
||||
Ballast-Data Product |
|
||||
Contact: Non-Doxxing Email Lorum Ipsum <br>
|
||||
Some Header Content
|
||||
Ballast-Data Product <br>
|
||||
<a href="/">Home</a> |
|
||||
<a href="/fares">Fares</a> |
|
||||
<a href="/stations">Stations</a> |
|
||||
<a href="/about">About Us</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -41,25 +162,75 @@
|
||||
|
||||
<br>
|
||||
|
||||
<div style="width: fit-content; margin-left: auto; margin-right: auto; border: 9px solid var(--B2); border-radius: 3px; padding: 12px; background-color: white; border-radius: 9px;">
|
||||
<div class="main">
|
||||
|
||||
<div style="width: fit-content; margin-left: auto; margin-right: auto; border-bottom: 9px solid var(--C1);">
|
||||
<div class="branding">
|
||||
<br>
|
||||
<img height=80px src="https://cdn.ballast-data.co.uk/Logo.svg">
|
||||
<a href="/fares">
|
||||
<img height=80px src="https://cdn.ballast-data.co.uk/Logo.svg">
|
||||
</a>
|
||||
<b style="font-size: 4em; line-height: 1; font-weight: 600;"> Fares </b> <br>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="query">
|
||||
<form method="get" action="/fares">
|
||||
<input
|
||||
name="origin"
|
||||
id="origin"
|
||||
required
|
||||
autocomplete="on"
|
||||
placeholder="Origin*"
|
||||
style="width: 10em;"
|
||||
/>
|
||||
<script>prefill('origin')</script>
|
||||
|
||||
<input
|
||||
name="destination"
|
||||
id="destination"
|
||||
required
|
||||
autocomplete="on"
|
||||
placeholder="Destination*"
|
||||
style="width: 10em;"
|
||||
/>
|
||||
<script>prefill('destination')</script>
|
||||
|
||||
<input
|
||||
name="toc"
|
||||
id="toc"
|
||||
autocomplete="on"
|
||||
placeholder="TOC"
|
||||
style="width: 5em;"
|
||||
/>
|
||||
<script>prefill('toc')</script>
|
||||
|
||||
<input type="submit" value="Go!" style="width: 5em;">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<!-- Content -->
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div style="margin-left: auto; margin-right: auto; width: fit-content; background-color: var(--D2); color: var(--A0); margin-top: 0px; padding: 12px; border-radius: 9px 9px 0 0; border: 6px solid var(--D1)">
|
||||
Open Rail Data Disclaimer Etc. | No Guarantees or Whatever | We do freelance and dont want to be poor.
|
||||
<div class="footer">
|
||||
<span>
|
||||
Powered by National Rail Enquiries. This site is not accredited by National Rail. <br>
|
||||
We use information from
|
||||
<a href="https://opendata.nationalrail.co.uk/" target="_blank">National Rail Open Data</a>
|
||||
and
|
||||
<a href="https://raildata.org.uk/" target="_blank">Rail Data Marketplace</a>. <br>
|
||||
This service is not to be used for safety critical purposes. <br>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
@@ -12,12 +12,20 @@ import json
|
||||
|
||||
|
||||
# Functions
|
||||
def fares_query(origin: str, destination: str) -> list[dict[str, dict[str, str]]]:
|
||||
def fares_query(
|
||||
origin: str,
|
||||
destination: str,
|
||||
toc: str | None = None,
|
||||
) -> list[dict[str, dict[str, str]]]:
|
||||
url = f"https://fares.ballast-data.co.uk/fares?origin={origin}&destination={destination}"
|
||||
url += f"&toc={toc.upper()}" if toc is not None else ""
|
||||
response = requests.get(
|
||||
url=f"https://fares.ballast-data.co.uk/fares?origin={origin}&destination={destination}",
|
||||
url=url,
|
||||
auth=(
|
||||
environ.get("BD_FARES_USER", ""),
|
||||
environ.get("BD_FARES_PASS", ""),
|
||||
),
|
||||
)
|
||||
if response.status_code != 200:
|
||||
return []
|
||||
return json.loads(response.content.decode()) # pyright: ignore[reportAny]
|
||||
|
||||
+31
-7
@@ -35,6 +35,8 @@ COLUMN_RENAMES = {
|
||||
"restriction_code": "Restriction",
|
||||
}
|
||||
|
||||
VALID_QUERY_TERMS = ["origin", "destination", "toc"]
|
||||
|
||||
DEFAULT_TEMPLATE_PATH = Path(__file__).parents[2] / "media/template.html"
|
||||
|
||||
logger: Logger = Logger(name=__name__, level=INFO)
|
||||
@@ -42,23 +44,42 @@ logger: Logger = Logger(name=__name__, level=INFO)
|
||||
|
||||
# Classes
|
||||
class FaresHandler(BaseHTTPRequestHandler):
|
||||
def parse_url_query(self) -> dict[str, str]:
|
||||
def parse_url_query(self) -> dict[str, str | None]:
|
||||
text = self.requestline.split(" ")[1].split("?")
|
||||
if len(text) == 1:
|
||||
return {}
|
||||
text = text[1]
|
||||
return {s.split("=")[0]: s.split("=")[-1] for s in text.split("&")}
|
||||
return {
|
||||
_s[0]: _s[-1] if _s[-1] != "" else None
|
||||
for s in text[1].split("&")
|
||||
if (_s := s.split("="))[0] in VALID_QUERY_TERMS
|
||||
}
|
||||
|
||||
def content_of_GET(self, template_path: Path = DEFAULT_TEMPLATE_PATH) -> str:
|
||||
"""
|
||||
Generates the content of a GET request.
|
||||
Responsible for receiving the requests' details and constructing the output.
|
||||
"""
|
||||
query_terms = self.parse_url_query()
|
||||
origin = query_terms.get("origin")
|
||||
destination = query_terms.get("destination")
|
||||
if origin is None or destination is None:
|
||||
text = "Search above including at least Origin and Destination."
|
||||
with open(template_path, "r") as rf:
|
||||
return rf.read().replace("<!-- Content -->", text)
|
||||
|
||||
assert origin is not None
|
||||
assert destination is not None
|
||||
data = fares_query(
|
||||
origin=(_d := self.parse_url_query()).get("origin", "NCL"),
|
||||
destination=_d.get("destination", "NCL"),
|
||||
origin=origin,
|
||||
destination=destination,
|
||||
toc=query_terms.get("toc"),
|
||||
)
|
||||
text = "<table>"
|
||||
if len(data) == 0:
|
||||
text = "No Fares Found."
|
||||
with open(template_path, "r") as rf:
|
||||
return rf.read().replace("<!-- Content -->", text)
|
||||
|
||||
text = '<table style="margin-left: auto; margin-right: auto;">'
|
||||
text += (
|
||||
'<tr style="font-size: 1.6em;"> <th> Flow </th> <th> Fares </th> </tr>\n'
|
||||
)
|
||||
@@ -101,9 +122,11 @@ class FaresHandler(BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
_ = self.wfile.write(content.encode())
|
||||
return None
|
||||
|
||||
except ConnectionError as ex:
|
||||
logger.warning(f"{type(ex)} | {ex}")
|
||||
self.send_response(503, "Upstream API refused to respond.")
|
||||
self.send_response(503, "Our upstream API refused to respond.")
|
||||
|
||||
except Exception as ex:
|
||||
logger.critical(f"{type(ex)} | {ex}")
|
||||
self.send_response(
|
||||
@@ -111,6 +134,7 @@ class FaresHandler(BaseHTTPRequestHandler):
|
||||
"Something abnormal occured, apologies. It has been logged with importance.",
|
||||
)
|
||||
raise ex
|
||||
|
||||
self.end_headers()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user