test
parent
c201ddce1b
commit
2e8ea7cbc1
|
@ -0,0 +1,6 @@
|
|||
from tpmcqr_service import create_app
|
||||
|
||||
iapp = create_app()
|
||||
|
||||
if __name__ == '__main__':
|
||||
iapp.run(debug=True, host= '0.0.0.0')
|
|
@ -0,0 +1,44 @@
|
|||
# coding: utf-8
|
||||
from flask import Flask
|
||||
from flask.logging import default_handler
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
from flask_mail import Mail
|
||||
#import logging
|
||||
import sys
|
||||
|
||||
from .config import Config
|
||||
|
||||
# Initialize extensions
|
||||
db = SQLAlchemy()
|
||||
mail = Mail()
|
||||
redis_client = FlaskRedis()
|
||||
|
||||
def create_app(config_class=Config):
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(config_class)
|
||||
|
||||
db.init_app(app)
|
||||
mail.init_app(app)
|
||||
redis.init_app(app)
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(config_class)
|
||||
|
||||
db.init_app(app)
|
||||
mail.init_app(app)
|
||||
redis_client.init_app(app)
|
||||
|
||||
from tpmcqr_service.api.parada import parada
|
||||
from tpmcqr_service.api.mapa import mapa
|
||||
from tpmcqr_service.content.paleta import paleta
|
||||
# from tpmcqr_service.errors.handlers import errors
|
||||
|
||||
app.register_blueprint(parada)
|
||||
app.register_blueprint(mapa)
|
||||
app.register_blueprint(paleta)
|
||||
# app.register_blueprint(errors)
|
||||
|
||||
return app
|
|
@ -0,0 +1,13 @@
|
|||
from flask import Blueprint, jsonify
|
||||
from tpmcqr_service import redis_client
|
||||
from tpmcqr_service.api.util import calcula_distancias_parada
|
||||
|
||||
paleta = Blueprint('paleta', __name__)
|
||||
|
||||
@paleta.route('/api/<string:parada>')
|
||||
def parada(parada=None):
|
||||
|
||||
current_file = redis_client.get('current_file')
|
||||
status, salida_parada = calcula_distancias_parada(current_file, parada)
|
||||
|
||||
return jsonify(salida_parada), status
|
|
@ -0,0 +1,106 @@
|
|||
|
||||
from tpmcqr_service import redis_client, db
|
||||
from tpmcqr_service.models.gtfs import QRDev, Shapes, find_shape_position
|
||||
import time
|
||||
|
||||
|
||||
def calcula_distancias_parada(redis_id, parada_id):
|
||||
|
||||
|
||||
|
||||
salida_parada = dict()
|
||||
|
||||
devdb = db.query(QRDev).filter(QRDev.id_dispositivo==parada_id).one_or_none()
|
||||
if not devdb:
|
||||
salida_parada['debug'] = 'Invalid Request'
|
||||
return 400, salida_parada
|
||||
|
||||
paradadb = obtiene_datos_parada(devdb.id_paradero)
|
||||
if not paradadb:
|
||||
salida_parada['debug'] = 'Parada {}'.format(devdb.id_paradero)
|
||||
return 200, salida_parada
|
||||
|
||||
|
||||
for key in ['stop_name', 'stop_lat', 'stop_lon']:
|
||||
salida_parada[key] = getattr(paradadb, key)
|
||||
|
||||
salida_parada['lineas'] = []
|
||||
|
||||
for id_linea in obtiene_lineas_parada(devdb.id_paradero):
|
||||
rt_linea = pickle.loads(redis_client.get(id_linea))
|
||||
|
||||
info_linea = dict()
|
||||
|
||||
for key in ['route_short_name', 'route_long_name', 'route_color', 'route_text_color', 'lur']:
|
||||
info_linea[key] = rt_linea[key]
|
||||
|
||||
info_linea['servicios'] = []
|
||||
info_linea['recientes'] = []
|
||||
|
||||
if 'id_shape' in rt_linea:
|
||||
parada_pos, parada_distance = find_shape_position(rt_linea['id_shape'], salida_parada['stop_lat'], salida_parada['stop_lon'])
|
||||
|
||||
expediciones_enruta = []
|
||||
expediciones_pasadas = []
|
||||
for item in rt_linea['servicios'].items():
|
||||
if item[0] < parada_distance:
|
||||
expediciones_enruta.append(item)
|
||||
else:
|
||||
expediciones_pasadas.append(item)
|
||||
|
||||
if len(expediciones_enruta) > 0:
|
||||
info_linea['servicios'].append( estima_llegada(parada_distance, expediciones_enruta.pop(-1)) )
|
||||
if len(expediciones_enruta) > 0:
|
||||
info_linea['servicios'].append( estima_llegada(parada_distance, expediciones_enruta.pop(-1)) )
|
||||
|
||||
if len(expediciones_pasadas) > 0:
|
||||
info_linea['recientes'].append( estima_llegada(parada_distance, expediciones_pasadas.pop(0)) )
|
||||
if len(expediciones_pasadas) > 0:
|
||||
info_linea['recientes'].append( estima_llegada(parada_distance, expediciones_pasadas.pop(0)) )
|
||||
|
||||
salida_parada['lineas'].append(info_linea)
|
||||
|
||||
return 200, salida_parada
|
||||
|
||||
|
||||
def estima_llegada(parada_distance, expedicion):
|
||||
ts = int(time.time())
|
||||
data_keys = ['trip_traveled', 'ppu', 'trip_lat', 'trip_lng', 'trip_pos', 'ts']
|
||||
trip_info = dict(zip(data_keys, expedicion)))
|
||||
trip_info['drift'] = int(trip_info['ts']) - ts
|
||||
trip_info['trip_distance'] = parada_distance - int(trip_info['trip_traveled'])
|
||||
estimator = int(trip_info['trip_distance'] / 5) # 18 Km/h promedio -> 5 m/s
|
||||
trip_info['debug_estimator'] = estimator
|
||||
|
||||
if estimator > 1570: # 26:10 minutos
|
||||
trip_info['trip_estimator'] = '25-30 minutos'
|
||||
elif estimator > 1260: # #21 minutos
|
||||
trip_info['trip_estimator'] = '20-25 minutos'
|
||||
elif estimator > 950: #15:50 minutos
|
||||
trip_info['trip_estimator'] = '15-20 minutos'
|
||||
elif estimator > 640: #10:40 minutos
|
||||
trip_info['trip_estimator'] = '10-15 minutos'
|
||||
elif estimator > 330: # 5:30
|
||||
trip_info['trip_estimator'] = '5-10 minutos'
|
||||
elif estimator > 200: # 3:20 sec
|
||||
trip_info['trip_estimator'] = '3 minutos'
|
||||
elif estimator > 70: #
|
||||
trip_info['trip_estimator'] = 'Llegando'
|
||||
elif estimator > 0: #
|
||||
trip_info['trip_estimator'] = 'En parada'
|
||||
else: #
|
||||
salida = ceil(estimator/60)
|
||||
trip_info['trip_estimator'] = 'Hace {} minutos'.format(salida)
|
||||
|
||||
|
||||
def obtiene_datos_parada(id_paradero):
|
||||
parada = db.query(Paradero).filter(Paradero.id_paradero==id_paradero).one_or_none()
|
||||
if parada is None:
|
||||
return None
|
||||
return parada
|
||||
|
||||
def obtiene_lineas_parada(id_paradero):
|
||||
lineas = []
|
||||
for linea in db.query(Trip).filter(Trip.id_paradero == id_paradero).distict(Trip.id_linea).all():
|
||||
lineas.append(linea.id_linea)
|
||||
return lineas
|
|
@ -0,0 +1,13 @@
|
|||
import os
|
||||
|
||||
|
||||
class Config:
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
|
||||
MAIL_SERVER = os.getenv('MAIL_SERVER')
|
||||
MAIL_PORT = os.getenv('MAIL_PORT')
|
||||
MAIL_USE_TLS = os.getenv('MAIL_TLS')
|
||||
APPLICATION_ROOT = os.getenv('API_BASE')
|
||||
MAIL_USERNAME = os.environ.get('EMAIL_USER')
|
||||
MAIL_PASSWORD = os.environ.get('EMAIL_PASS')
|
||||
REDIS_URL = os.getenv('REDIS_URL', 'redis://localhost:6379/1')
|
|
@ -0,0 +1,7 @@
|
|||
from flask import Blueprint, jsonify
|
||||
|
||||
paleta = Blueprint('paleta', __name__)
|
||||
|
||||
@paleta.route('/')
|
||||
def parada():
|
||||
return jsonify(message="Placeholder for JS page")
|
|
@ -0,0 +1,20 @@
|
|||
from flask import Blueprint, render_template
|
||||
|
||||
errors = Blueprint('errors', __name__)
|
||||
|
||||
|
||||
@errors.app_errorhandler(404)
|
||||
def error_404(error):
|
||||
return render_template('errors/404.html'), 404
|
||||
|
||||
|
||||
@errors.app_errorhandler(403)
|
||||
def error_403(error):
|
||||
return render_template('errors/403.html'), 403
|
||||
|
||||
|
||||
@errors.app_errorhandler(500)
|
||||
def error_500(error):
|
||||
return render_template('errors/500.html'), 500
|
||||
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
from tpmcqr_service import db
|
||||
from sqlalchemy.orm import aliased
|
||||
from sqlalchemy import any_
|
||||
from sqlalchemy import func
|
||||
from geoalchemy2 import Geometry
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
|
||||
class Lineas(db.Model):
|
||||
__tablename__ = 'linea'
|
||||
__table_args__ = { 'schema': 'public' }
|
||||
|
||||
id_linea = db.Column(db.String(150), primary_key=True )
|
||||
id_operador = db.Column(db.String(150))
|
||||
route_short_name = db.Column(db.String(150))
|
||||
route_long_name = db.Column(db.String(150))
|
||||
route_color = db.Column(db.String(150))
|
||||
route_text_color = db.Column(db.String(150))
|
||||
vigente = db.Column(db.Boolean)
|
||||
|
||||
class QRDev(db.Model):
|
||||
__tablename__ = 'device'
|
||||
__table_args__ = { 'schema': 'public' }
|
||||
|
||||
id_dispositivo = db.Column(db.String(100), primary_key=True )
|
||||
id_paradero = db.Column(db.String(50))
|
||||
|
||||
class Paradero(db.Model):
|
||||
__tablename__ = 'paredero'
|
||||
__table_args__ = { 'schema': 'public' }
|
||||
|
||||
id_paradero = db.Column(db.String(50), primary_key=True )
|
||||
stop_name = db.Column(db.String(50))
|
||||
stop_lat = db.Column(db.Float)
|
||||
stop_lon = db.Column(db.Float)
|
||||
|
||||
class Shapes(db.Model):
|
||||
__tablename__ = 'gtfs_shape'
|
||||
__table_args__ = { 'schema': 'public' }
|
||||
|
||||
id_gtfs_pk = db.Column(db.Integer, primary_key=True )
|
||||
id_shape = db.Column(db.String(150))
|
||||
shape_pt_lat = db.Column(db.Float)
|
||||
shape_pt_lon = db.Column(db.Float)
|
||||
shape_pt_sequence = db.Column(db.Integer)
|
||||
shape_dist_traveled = db.Column(db.Float)
|
||||
|
||||
class Trips(Base):
|
||||
__tablename__ = 'gtfs_trips'
|
||||
__table_args__ = { 'schema': 'public' }
|
||||
|
||||
id_trip = Column(String(150), primary_key=True)
|
||||
id_linea = Column(String(150))
|
||||
id_shape = Column(String(150))
|
||||
service_id = Column(String(50))
|
||||
|
||||
|
||||
def find_shape_position(shape_id, lat, lng):
|
||||
Shape1 = aliased(Shapes)
|
||||
Shape2 = aliased(Shapes)
|
||||
|
||||
point = func.ST_SetSRID(func.ST_MakePoint(lng, lat), 4326) # Create PostGIS point
|
||||
|
||||
segmento = db.query(
|
||||
Shape1.shape_pt_sequence.label("start_sequence"),
|
||||
# Shape2.shape_pt_sequence.label("end_sequence"),
|
||||
Shape1.shape_dist_traveled.label("traveled_start"),
|
||||
Shape2.shape_dist_traveled.label("traveled_end"),
|
||||
func.ST_Distance(
|
||||
func.ST_MakeLine(
|
||||
func.ST_SetSRID(func.ST_MakePoint(Shape1.shape_pt_lon, Shape1.shape_pt_lat), 4326),
|
||||
func.ST_SetSRID(func.ST_MakePoint(Shape2.shape_pt_lon, Shape2.shape_pt_lat), 4326)
|
||||
),
|
||||
point
|
||||
).label("distance"),
|
||||
func.ST_LineLocatePoint( # Compute fractional position on the segment
|
||||
func.ST_MakeLine(
|
||||
func.ST_SetSRID(func.ST_MakePoint(Shape1.shape_pt_lon, Shape1.shape_pt_lat), 4326),
|
||||
func.ST_SetSRID(func.ST_MakePoint(Shape2.shape_pt_lon, Shape2.shape_pt_lat), 4326)
|
||||
),
|
||||
point
|
||||
).label("fraction")
|
||||
).filter(
|
||||
Shape1.id_shape == shape_id,
|
||||
Shape2.id_shape == shape_id,
|
||||
Shape2.shape_pt_sequence == Shape1.shape_pt_sequence + 1 # Ensure correct segment order
|
||||
).order_by("distance").first()
|
||||
|
||||
avanzado = segmento.traveled_start + segmento.fraction * (segmento.traveled_end - segmento.traveled_start)
|
||||
|
||||
return ( segmento.start_sequence, avanzado )
|
|
@ -0,0 +1,31 @@
|
|||
bcrypt==3.1.4
|
||||
blinker==1.4
|
||||
certifi==2016.2.28
|
||||
gunicorn
|
||||
click==6.7
|
||||
Flask==1.0
|
||||
Flask-Bcrypt==0.7.1
|
||||
Flask-Login==0.4.1
|
||||
Flask-Mail==0.9.1
|
||||
Flask-SQLAlchemy==2.3.2
|
||||
Flask-WTF==0.14.2
|
||||
itsdangerous==0.24
|
||||
Jinja2==2.10
|
||||
MarkupSafe
|
||||
#Pillow==5.3.0
|
||||
pycparser==2.18
|
||||
six==1.11.0
|
||||
SQLAlchemy==1.2.7
|
||||
Werkzeug==0.14.1
|
||||
WTForms==2.1
|
||||
PyYAML==5.1.2
|
||||
ua-parser==0.8.0
|
||||
user-agents==2.0
|
||||
netaddr==0.7.19
|
||||
#psycopg2==2.8.4
|
||||
pwnedpasswords==2.0.0
|
||||
openpyxl==2.6.4
|
||||
xlrd==1.2.0
|
||||
unidecode==1.1.1
|
||||
uuid==1.30
|
||||
|
Loading…
Reference in New Issue