Kenneth Bruen
3 years ago
16 changed files with 230 additions and 39 deletions
@ -1,33 +0,0 @@ |
|||||||
from flask import Flask, json, request, jsonify |
|
||||||
|
|
||||||
from cache import CachedData |
|
||||||
|
|
||||||
app = Flask(__name__) |
|
||||||
|
|
||||||
@app.route('/') |
|
||||||
def root(): |
|
||||||
return 'Test' |
|
||||||
|
|
||||||
train_data_cache = {} |
|
||||||
|
|
||||||
@app.route('/train/<int:train_no>') |
|
||||||
def get_train_info(train_no: int): |
|
||||||
def get_data(): |
|
||||||
print(f'Cache miss for {train_no}') |
|
||||||
from scraper.scraper import scrape |
|
||||||
use_yesterday = False |
|
||||||
return scrape(train_no, use_yesterday=use_yesterday) |
|
||||||
if train_no not in train_data_cache: |
|
||||||
train_data_cache[train_no] = CachedData(get_data, validity=1000 * 30) |
|
||||||
data, fetch_time = train_data_cache[train_no]() |
|
||||||
resp = jsonify(data) |
|
||||||
resp.headers['X-Last-Fetched'] = fetch_time.isoformat() |
|
||||||
return resp |
|
||||||
|
|
||||||
@app.route('/trains') |
|
||||||
def get_trains(): |
|
||||||
return jsonify(list(train_data_cache.keys())) |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
print('Starting debug server on port 5001') |
|
||||||
app.run(port=5000) |
|
@ -0,0 +1,85 @@ |
|||||||
|
# Globals |
||||||
|
stations = [] |
||||||
|
trains = [] |
||||||
|
|
||||||
|
# Examples |
||||||
|
example_station = { |
||||||
|
'name': 'Gară', |
||||||
|
'stoppedAtBy': [123, 456] |
||||||
|
} |
||||||
|
|
||||||
|
example_train = { |
||||||
|
'rank': 'IR', |
||||||
|
'numberString': '74', |
||||||
|
'number': 74, |
||||||
|
'company': 'CFR Călători' |
||||||
|
} |
||||||
|
|
||||||
|
# Init |
||||||
|
|
||||||
|
import json |
||||||
|
import os |
||||||
|
from os import path, stat |
||||||
|
from .utils import take_while |
||||||
|
|
||||||
|
DB_DIR = os.environ.get('DB_DIR', '') or './db' |
||||||
|
if not path.exists(DB_DIR): |
||||||
|
os.mkdir(DB_DIR) |
||||||
|
|
||||||
|
STATIONS_FILE = path.join(DB_DIR, 'stations.json') |
||||||
|
|
||||||
|
if path.exists(STATIONS_FILE): |
||||||
|
with open(STATIONS_FILE) as f: |
||||||
|
stations = json.load(f) |
||||||
|
|
||||||
|
TRAINS_FILE = path.join(DB_DIR, 'trains.json') |
||||||
|
|
||||||
|
if path.exists(TRAINS_FILE): |
||||||
|
with open(TRAINS_FILE) as f: |
||||||
|
trains = json.load(f) |
||||||
|
|
||||||
|
def found_train(rank: str, number: str, company: str) -> int: |
||||||
|
number_int = int(''.join(take_while(lambda s: str(s).isnumeric(), number))) |
||||||
|
try: |
||||||
|
next(filter(lambda tr: tr['number'] == number_int, trains)) |
||||||
|
except StopIteration: |
||||||
|
trains.append({ |
||||||
|
'number': number_int, |
||||||
|
'numberString': number, |
||||||
|
'company': company, |
||||||
|
'rank': rank, |
||||||
|
}) |
||||||
|
with open(TRAINS_FILE, 'w') as f: |
||||||
|
json.dump(trains, f) |
||||||
|
return number_int |
||||||
|
|
||||||
|
def found_station(name: str): |
||||||
|
try: |
||||||
|
next(filter(lambda s: s['name'] == name, stations)) |
||||||
|
except StopIteration: |
||||||
|
stations.append({ |
||||||
|
'name': name, |
||||||
|
'stoppedAtBy': [], |
||||||
|
}) |
||||||
|
stations.sort(key=lambda s: len(s['stoppedAtBy']), reverse=True) |
||||||
|
with open(STATIONS_FILE, 'w') as f: |
||||||
|
json.dump(stations, f) |
||||||
|
|
||||||
|
def found_train_at_station(station_name: str, train_number: int): |
||||||
|
found_station(station_name) |
||||||
|
for i in range(len(stations)): |
||||||
|
if stations[i]['name'] == station_name: |
||||||
|
if train_number not in stations[i]['stoppedAtBy']: |
||||||
|
stations[i]['stoppedAtBy'].append(train_number) |
||||||
|
stations.sort(key=lambda s: len(s['stoppedAtBy']), reverse=True) |
||||||
|
with open(STATIONS_FILE, 'w') as f: |
||||||
|
json.dump(stations, f) |
||||||
|
break |
||||||
|
|
||||||
|
def on_train_data(train_data: dict): |
||||||
|
train_no = found_train(train_data['rank'], train_data['number'], train_data['operator']) |
||||||
|
for station in train_data['stations']: |
||||||
|
found_train_at_station(station['name'], train_no) |
||||||
|
|
||||||
|
def on_train_lookup_failure(train_no: int): |
||||||
|
pass |
@ -0,0 +1,53 @@ |
|||||||
|
print(f'Server {__name__=}') |
||||||
|
|
||||||
|
import datetime |
||||||
|
from flask import Flask, json, request, jsonify |
||||||
|
|
||||||
|
from .cache import CachedData |
||||||
|
|
||||||
|
app = Flask(__name__) |
||||||
|
|
||||||
|
from .v2 import v2 |
||||||
|
app.register_blueprint(v2.bp) |
||||||
|
|
||||||
|
@app.route('/') |
||||||
|
def root(): |
||||||
|
return 'Test' |
||||||
|
|
||||||
|
train_data_cache = {} |
||||||
|
|
||||||
|
@app.route('/train/<int:train_no>') |
||||||
|
def get_train_info(train_no: int): |
||||||
|
def get_data(): |
||||||
|
from .scraper.scraper import scrape |
||||||
|
use_yesterday = False |
||||||
|
result = scrape(train_no, use_yesterday=use_yesterday) |
||||||
|
|
||||||
|
from . import db |
||||||
|
db.on_train_data(result) |
||||||
|
|
||||||
|
# Convert to v1 |
||||||
|
# datetime ISO string to hh:mm |
||||||
|
for i in range(len(result['stations'])): |
||||||
|
if result['stations'][i]['arrival']: |
||||||
|
date = datetime.datetime.fromisoformat(result['stations'][i]['arrival']['scheduleTime']) |
||||||
|
result['stations'][i]['arrival']['scheduleTime'] = f'{date.hour}:{date.minute:02}' |
||||||
|
if result['stations'][i]['departure']: |
||||||
|
date = datetime.datetime.fromisoformat(result['stations'][i]['departure']['scheduleTime']) |
||||||
|
result['stations'][i]['departure']['scheduleTime'] = f'{date.hour}:{date.minute:02}' |
||||||
|
|
||||||
|
return result |
||||||
|
if train_no not in train_data_cache: |
||||||
|
train_data_cache[train_no] = CachedData(get_data, validity=1000 * 30) |
||||||
|
data, fetch_time = train_data_cache[train_no]() |
||||||
|
resp = jsonify(data) |
||||||
|
resp.headers['X-Last-Fetched'] = fetch_time.isoformat() |
||||||
|
return resp |
||||||
|
|
||||||
|
@app.route('/trains') |
||||||
|
def get_trains(): |
||||||
|
return jsonify(list(train_data_cache.keys())) |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
print('Starting debug server on port 5001') |
||||||
|
app.run(port=5000) |
@ -0,0 +1,18 @@ |
|||||||
|
def take_while(predicate, input): |
||||||
|
for element in input: |
||||||
|
if not predicate(element): |
||||||
|
break |
||||||
|
yield element |
||||||
|
|
||||||
|
_NO_DEFAULT = object() |
||||||
|
|
||||||
|
def check_yes_no(input: str, default=_NO_DEFAULT, considered_yes=None) -> bool: |
||||||
|
input = str(input).strip().lower() |
||||||
|
if not input: |
||||||
|
if default == _NO_DEFAULT: |
||||||
|
raise Exception('Empty input with no default') |
||||||
|
return default |
||||||
|
if not considered_yes: |
||||||
|
considered_yes = ['y', 'yes', 't', 'true', '1'] |
||||||
|
return input in considered_yes |
||||||
|
|
@ -0,0 +1,32 @@ |
|||||||
|
from flask import Blueprint, jsonify, request |
||||||
|
|
||||||
|
from .. import db |
||||||
|
from ..cache import CachedData |
||||||
|
from ..utils import check_yes_no |
||||||
|
|
||||||
|
bp = Blueprint('v2', __name__, url_prefix='/v2') |
||||||
|
|
||||||
|
@bp.get('/trains') |
||||||
|
def get_known_trains(): |
||||||
|
return jsonify(db.trains) |
||||||
|
|
||||||
|
@bp.get('/stations') |
||||||
|
def get_known_stations(): |
||||||
|
return jsonify(db.stations) |
||||||
|
|
||||||
|
train_data_cache = {} |
||||||
|
|
||||||
|
@bp.route('/train/<int:train_no>') |
||||||
|
def get_train_info(train_no: int): |
||||||
|
use_yesterday = check_yes_no(request.args.get('use_yesterday', ''), default=False) |
||||||
|
def get_data(): |
||||||
|
from ..scraper.scraper import scrape |
||||||
|
result = scrape(train_no, use_yesterday=use_yesterday) |
||||||
|
db.on_train_data(result) |
||||||
|
return result |
||||||
|
if train_no not in train_data_cache: |
||||||
|
train_data_cache[(train_no, use_yesterday)] = CachedData(get_data, validity=1000 * 30) |
||||||
|
data, fetch_time = train_data_cache[(train_no, use_yesterday)]() |
||||||
|
resp = jsonify(data) |
||||||
|
resp.headers['X-Last-Fetched'] = fetch_time.isoformat() |
||||||
|
return resp |
Loading…
Reference in new issue