Styling update, fix some Pylint errors

Also update the stations fetching code to handle the warn the user when
fetched stations differ from the one fetched at previous pass.
This commit is contained in:
Lucas Verney 2017-04-13 23:22:11 +02:00
parent d7012e3834
commit 4966fe2111
No known key found for this signature in database
GPG Key ID: 75B45CF41F334690
7 changed files with 52 additions and 13 deletions

View File

@ -99,7 +99,7 @@ evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / stateme
[BASIC] [BASIC]
# Good variable names which should always be accepted, separated by a comma # Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_,fh good-names=i,j,k,ex,Run,_,fh,db
# Bad variable names which should always be refused, separated by a comma # Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata bad-names=foo,bar,baz,toto,tutu,tata

View File

@ -27,7 +27,8 @@ class StringyJSON(types.TypeDecorator):
def process_bind_param(self, value, dialect): def process_bind_param(self, value, dialect):
""" """
TODO Process the bound param, serialize the object to JSON before saving
into database.
""" """
if value is not None: if value is not None:
value = json.dumps(value) value = json.dumps(value)
@ -35,7 +36,8 @@ class StringyJSON(types.TypeDecorator):
def process_result_value(self, value, dialect): def process_result_value(self, value, dialect):
""" """
TODO Process the value fetched from the database, deserialize the JSON
string before returning the object.
""" """
if value is not None: if value is not None:
value = json.loads(value) value = json.loads(value)

View File

@ -149,7 +149,7 @@ def guess_postal_code(flats_list, config, distance_threshold=20000):
"Found postal code in location field for flat %s: %s.", "Found postal code in location field for flat %s: %s.",
flat["id"], postal_code flat["id"], postal_code
) )
except AssertionError as e: except AssertionError:
postal_code = None postal_code = None
# If not found, try to find a city # If not found, try to find a city
@ -294,7 +294,23 @@ def guess_stations(flats_list, config, distance_threshold=1500):
flat["id"], flat["id"],
", ".join(x["name"] for x in good_matched_stations) ", ".join(x["name"] for x in good_matched_stations)
) )
# TODO: Handle update (second pass)
# If some stations were already filled in and the result is different,
# display some warning to the user
if (
"matched_stations" in flat["flatisfy"]["matched_stations"] and
(
# Do a set comparison, as ordering is not important
set(flat["flatisfy"]["matched_stations"]) !=
set(good_matched_stations)
)
):
LOGGER.warning(
"Replacing previously fetched stations for flat %s. Found "
"stations differ from the previously found ones.",
flat["id"]
)
flat["flatisfy"]["matched_stations"] = good_matched_stations flat["flatisfy"]["matched_stations"] = good_matched_stations
return flats_list return flats_list

View File

@ -19,6 +19,9 @@ import unidecode
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
# Constants
NAVITIA_ENDPOINT = "https://api.navitia.io/v1/coverage/fr-idf/journeys"
def pretty_json(data): def pretty_json(data):
""" """
@ -204,7 +207,6 @@ def get_travel_time_between(latlng_from, latlng_to, config):
.. note :: Uses the Navitia API. Requires a ``navitia_api_key`` field to be .. note :: Uses the Navitia API. Requires a ``navitia_api_key`` field to be
filled-in in the ``config``. filled-in in the ``config``.
""" """
NAVITIA_ENDPOINT = "https://api.navitia.io/v1/coverage/fr-idf/journeys"
time = None time = None
# Check that Navitia API key is available # Check that Navitia API key is available
@ -224,12 +226,12 @@ def get_travel_time_between(latlng_from, latlng_to, config):
req.raise_for_status() req.raise_for_status()
time = req.json()["journeys"][0]["durations"]["total"] time = req.json()["journeys"][0]["durations"]["total"]
except (requests.exceptions.RequestException, except (requests.exceptions.RequestException,
ValueError, IndexError, KeyError) as e: ValueError, IndexError, KeyError) as exc:
# Ignore any possible exception # Ignore any possible exception
LOGGER.warning( LOGGER.warning(
"An exception occurred during travel time lookup on " "An exception occurred during travel time lookup on "
"Navitia: %s.", "Navitia: %s.",
str(e) str(exc)
) )
else: else:
LOGGER.warning( LOGGER.warning(

View File

@ -42,7 +42,7 @@ def get_app(config):
# API v1 routes # API v1 routes
app.route("/api/v1/", "GET", api_routes.index_v1) app.route("/api/v1/", "GET", api_routes.index_v1)
app.route("/api/v1/flats", "GET", api_routes.flats_v1) app.route("/api/v1/flats", "GET", api_routes.flats_v1)
app.route("/api/v1/flat/:id", "GET", api_routes.flat_v1) app.route("/api/v1/flat/:flat_id", "GET", api_routes.flat_v1)
# Index # Index
app.route("/", "GET", lambda: _serve_static_file("index.html")) app.route("/", "GET", lambda: _serve_static_file("index.html"))

View File

@ -2,6 +2,10 @@
""" """
This module contains a Bottle plugin to pass the database argument to any route This module contains a Bottle plugin to pass the database argument to any route
which needs it. which needs it.
This module is heavily based on code from
[Bottle-SQLAlchemy](https://github.com/iurisilvio/bottle-sqlalchemy) which is
licensed under MIT license.
""" """
from __future__ import ( from __future__ import (
absolute_import, division, print_function, unicode_literals absolute_import, division, print_function, unicode_literals
@ -14,6 +18,10 @@ import bottle
class DatabasePlugin(object): class DatabasePlugin(object):
"""
A Bottle plugin to automatically pass an SQLAlchemy database session object
to the routes specifying they need it.
"""
name = 'database' name = 'database'
api = 2 api = 2
KEYWORD = "db" KEYWORD = "db"
@ -26,7 +34,7 @@ class DatabasePlugin(object):
""" """
self.get_session = get_session self.get_session = get_session
def setup(self, app): def setup(self, app): # pylint: disable-no-self-use
""" """
Make sure that other installed plugins don't affect the same Make sure that other installed plugins don't affect the same
keyword argument and check if metadata is available. keyword argument and check if metadata is available.
@ -40,6 +48,15 @@ class DatabasePlugin(object):
) )
def apply(self, callback, route): def apply(self, callback, route):
"""
Method called on route invocation. Should apply some transformations to
the route prior to returing it.
We check the presence of ``self.KEYWORD`` in the route signature and
replace the route callback by a partial invocation where we replaced
this argument by a valid SQLAlchemy session.
"""
# Check whether the route needs a valid db session or not.
try: try:
callback_args = inspect.signature(route.callback).parameters callback_args = inspect.signature(route.callback).parameters
except AttributeError: except AttributeError:
@ -47,8 +64,10 @@ class DatabasePlugin(object):
callback_args = inspect.getargspec(route.callback).args callback_args = inspect.getargspec(route.callback).args
if self.KEYWORD not in callback_args: if self.KEYWORD not in callback_args:
# If no need for a db session, call the route callback
return callback return callback
else: else:
# Otherwise, we get a db session and pass it to the callback
with self.get_session() as session: with self.get_session() as session:
kwargs = {} kwargs = {}
kwargs[self.KEYWORD] = session kwargs[self.KEYWORD] = session

View File

@ -35,13 +35,13 @@ def flats_v1(db):
} }
def flat_v1(id, db): def flat_v1(flat_id, db):
""" """
API v1 flat route: API v1 flat route:
GET /api/v1/flat/:id GET /api/v1/flat/:flat_id
""" """
flat = db.query(flat_model.Flat).filter_by(id=id).first() flat = db.query(flat_model.Flat).filter_by(id=flat_id).first()
return { return {
"data": flat.json_api_repr() "data": flat.json_api_repr()
} }