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