-
Anton Sarukhanov authored4eefe945
app.py 4.78 KiB
import os
from flask import Flask, jsonify, make_response, render_template, request, abort
from flask.ext.bower import Bower
from jinja2.exceptions import TemplateNotFound
from sqlalchemy.orm import joinedload
from models import db, Agency
from datetime import datetime
app = Flask(__name__, instance_relative_config=True)
# Load environment-specific settings from config.py
env = os.environ.get('BUSMAP_ENV', 'prod')
app.config.from_object('config.{0}Config'.format(env.capitalize()))
# Load deployment-specific settings from instance/config.cfg
app.config.from_pyfile('config.py', silent=True)
# Database init
db.init_app(app)
Bower(app)
# Flask Web Routes
@app.route('/')
def map():
# TODO: serve different agency depending on cookie (or special domain)
agency_tag = app.config['AGENCIES'][0]
agency = db.session.query(Agency).filter(Agency.tag==agency_tag).one()
r = make_response(render_template('map.html', agency=agency, config=app.config))
r.headers.add('X-Frame-Options', 'DENY')
return r
@app.route('/e')
def map_embed():
# TODO: serve different agency depending on cookie (or special domain)
agency_tag = app.config['AGENCIES'][0]
agency = db.session.query(Agency).filter(Agency.tag==agency_tag).one()
return render_template('map.html', agency=agency, config=app.config, embed=True)
@app.route('/ajax')
def ajax():
""" Handle all async requests (from JS). """
query = request.args.get('query')
agency_tag = app.config['AGENCIES'][0]
agency = db.session.query(Agency).filter(Agency.tag==agency_tag).one()
def modal(name):
""" Serve contents of a modal popup """
if not name.isalnum():
return abort(404)
template_name = 'modal-' + name + '.html'
try:
return render_template(template_name, agency=agency, config=app.config)
except TemplateNotFound:
return abort(404)
def routes():
""" Serve the route configuration (routes, stops) """
from models import Route, RouteStop, Stop
routes = db.session.query(Route).join(Agency)\
.filter(Agency.tag==agency_tag).all()
stops = db.session.query(Stop).options(joinedload(Stop.routes))\
.filter(Stop.routes.any(Route.id.in_([r.id for r in routes]))).all()
return {
"routes": {r.tag: r.serialize() for r in routes},
"stops": {s.id: s.serialize() for s in stops}
}
def vehicles():
""" Serve the current vehicle locations and arrival predictions. """
from models import Route, VehicleLocation, Prediction
# TODO: OPTIMIZE
# Over 1sec/request just to get predictions? Fuck that noise.
# TODO: Somehow bundle these queries into the object model definitions? So messy :(
# maybe VehicleLocation.get_latest_for_agency(a)?
# 1. Select the latest vehicle locations for each vehicle. (The DB may have old ones too).
v_inner = db.session.query(VehicleLocation.vehicle,
db.func.max(VehicleLocation.time).label("time"))\
.group_by(VehicleLocation.vehicle).subquery()
vehicle_locations = db.session.query(VehicleLocation).join(v_inner, db.and_(
v_inner.c.vehicle == VehicleLocation.vehicle,
v_inner.c.time == VehicleLocation.time
)).filter(Agency.tag==agency_tag).all()
# 2. Select the predictions for each vehicle:stop pair which came from the most recent
# API call for that vehicle:stop pair. Old predictions may be stored but we don't want them.
now = datetime.now()
p_inner = db.session.query(Prediction.vehicle, Prediction.stop_id,
db.func.max(Prediction.api_call_id).label("api_call_id"))\
.group_by(Prediction.vehicle, Prediction.stop_id)\
.subquery()
predictions = db.session.query(Prediction).join(p_inner, db.and_(
p_inner.c.api_call_id == Prediction.api_call_id,
p_inner.c.vehicle == Prediction.vehicle,
p_inner.c.stop_id == Prediction.stop_id
)).filter(
Agency.tag==agency_tag,
Prediction.prediction >= now)\
.group_by(Prediction.id, Prediction.vehicle, Prediction.stop_id)\
.all()
return {
"locations": {v.vehicle: v.serialize() for v in vehicle_locations},
"predictions": {p.id: p.serialize() for p in predictions}
}
if query == "routes":
return jsonify(routes())
elif query == "vehicles":
return jsonify(vehicles())
elif query == "modal":
modal_name = request.args.get('modal_name')
return modal(modal_name)
if __name__ == '__main__':
# Run Flask
app.run(host='0.0.0.0')