From bdf8a6b8d24c819d9561e12ded5bfb4b00531625 Mon Sep 17 00:00:00 2001 From: "Phyks (Lucas Verney)" Date: Fri, 16 Jun 2017 16:21:13 +0200 Subject: [PATCH] Handle multiple constraints in the config All the backend part has been rewritten to handle multiple constraints in the config (== multiple queries). Also did some linting. Still to be done: frontend part and doc. --- flatisfy/__main__.py | 26 +++++---- flatisfy/cmds.py | 56 ++++++++++++++++--- flatisfy/config.py | 83 +++++++++++++++------------- flatisfy/data.py | 9 ++- flatisfy/data_files/__init__.py | 4 +- flatisfy/fetch.py | 63 ++++++++++++--------- flatisfy/filters/__init__.py | 31 ++++++----- flatisfy/filters/metadata.py | 27 +++++---- flatisfy/models/flat.py | 1 + flatisfy/tools.py | 3 +- flatisfy/web/js_src/store/getters.js | 2 +- flatisfy/web/routes/api.py | 8 +-- 12 files changed, 195 insertions(+), 118 deletions(-) diff --git a/flatisfy/__main__.py b/flatisfy/__main__.py index 873c633..119fb3d 100644 --- a/flatisfy/__main__.py +++ b/flatisfy/__main__.py @@ -10,11 +10,13 @@ import sys logging.basicConfig() +# pylint: disable=locally-disabled,wrong-import-position import flatisfy.config from flatisfy import cmds from flatisfy import data from flatisfy import fetch from flatisfy import tools +# pylint: enable=locally-disabled,wrong-import-position LOGGER = logging.getLogger("flatisfy") @@ -166,31 +168,35 @@ def main(): # Fetch command if args.cmd == "fetch": # Fetch and filter flats list - flats_list = fetch.fetch_flats_list(config) - flats_list = cmds.filter_flats(config, flats_list=flats_list, - fetch_details=True)["new"] + fetched_flats = fetch.fetch_flats(config) + fetched_flats = cmds.filter_fetched_flats(config, + fetched_flats=fetched_flats, + fetch_details=True)["new"] # Sort by cost - flats_list = tools.sort_list_of_dicts_by(flats_list, "cost") + fetched_flats = tools.sort_list_of_dicts_by(fetched_flats, "cost") print( - tools.pretty_json(flats_list) + tools.pretty_json(sum(fetched_flats.values(), [])) ) return # Filter command elif args.cmd == "filter": # Load and filter flats list if args.input: - flats_list = fetch.load_flats_list_from_file(args.input) + fetched_flats = fetch.load_flats_from_file(args.input, config) - flats_list = cmds.filter_flats(config, flats_list=flats_list, - fetch_details=False)["new"] + fetched_flats = cmds.filter_fetched_flats( + config, + fetched_flats=fetched_flats, + fetch_details=False + )["new"] # Sort by cost - flats_list = tools.sort_list_of_dicts_by(flats_list, "cost") + fetched_flats = tools.sort_list_of_dicts_by(fetched_flats, "cost") # Output to stdout print( - tools.pretty_json(flats_list) + tools.pretty_json(sum(fetched_flats.values(), [])) ) else: cmds.import_and_filter(config, load_from_db=True) diff --git a/flatisfy/cmds.py b/flatisfy/cmds.py index f5cbd92..a0f5371 100644 --- a/flatisfy/cmds.py +++ b/flatisfy/cmds.py @@ -21,19 +21,28 @@ from flatisfy.web import app as web_app LOGGER = logging.getLogger(__name__) -def filter_flats(config, flats_list, fetch_details=True): +def filter_flats_list(config, constraint_name, flats_list, fetch_details=True): """ Filter the available flats list. Then, filter it according to criteria. :param config: A config dict. + :param constraint_name: The constraint name that the ``flats_list`` should + satisfy. :param fetch_details: Whether additional details should be fetched between the two passes. :param flats_list: The initial list of flat objects to filter. :return: A dict mapping flat status and list of flat objects. """ - # pylint: disable=locally-disabled,redefined-variable-type # Add the flatisfy metadata entry and prepare the flat objects - flats_list = metadata.init(flats_list) + flats_list = metadata.init(flats_list, constraint_name) + + # Get the associated constraint from config + try: + constraint = config["constraints"][constraint_name] + except KeyError: + LOGGER.warning("Missing constraint %s. Using default one.", + constraint_name) + constraint = config["constraints"]["default"] first_pass_result = collections.defaultdict(list) second_pass_result = collections.defaultdict(list) @@ -42,6 +51,7 @@ def filter_flats(config, flats_list, fetch_details=True): # unwanted postings as possible if config["passes"] > 0: first_pass_result = flatisfy.filters.first_pass(flats_list, + constraint, config) else: first_pass_result["new"] = flats_list @@ -56,7 +66,7 @@ def filter_flats(config, flats_list, fetch_details=True): # additional infos if config["passes"] > 1: second_pass_result = flatisfy.filters.second_pass( - first_pass_result["new"], config + first_pass_result["new"], constraint, config ) else: second_pass_result["new"] = first_pass_result["new"] @@ -84,6 +94,28 @@ def filter_flats(config, flats_list, fetch_details=True): } +def filter_fetched_flats(config, fetched_flats, fetch_details=True): + """ + Filter the available flats list. Then, filter it according to criteria. + + :param config: A config dict. + :param fetch_details: Whether additional details should be fetched between + the two passes. + :param fetched_flats: The initial dict mapping constraints to the list of + fetched flat objects to filter. + :return: A dict mapping constraints to a dict mapping flat status and list + of flat objects. + """ + for constraint_name, flats_list in fetched_flats.items(): + fetched_flats[constraint_name] = filter_flats_list( + config, + constraint_name, + flats_list, + fetch_details + ) + return fetched_flats + + def import_and_filter(config, load_from_db=False): """ Fetch the available flats list. Then, filter it according to criteria. @@ -96,18 +128,24 @@ def import_and_filter(config, load_from_db=False): """ # Fetch and filter flats list if load_from_db: - flats_list = fetch.load_flats_list_from_db(config) + fetched_flats = fetch.load_flats_from_db(config) else: - flats_list = fetch.fetch_flats_list(config) + fetched_flats = fetch.fetch_flats(config) # Do not fetch additional details if we loaded data from the db. - flats_list_by_status = filter_flats(config, flats_list=flats_list, - fetch_details=(not load_from_db)) + flats_by_status = filter_fetched_flats(config, fetched_flats=fetched_flats, + fetch_details=(not load_from_db)) # Create database connection get_session = database.init_db(config["database"], config["search_index"]) LOGGER.info("Merging fetched flats in database...") + # Flatten the flats_by_status dict + flatten_flats_by_status = collections.defaultdict(list) + for flats in flats_by_status.values(): + for status, flats_list in flats.items(): + flatten_flats_by_status[status].extend(flats_list) + with get_session() as session: - for status, flats_list in flats_list_by_status.items(): + for status, flats_list in flatten_flats_by_status.items(): # Build SQLAlchemy Flat model objects for every available flat flats_objects = { flat_dict["id"]: flat_model.Flat.from_dict(flat_dict) diff --git a/flatisfy/config.py b/flatisfy/config.py index 03d36f2..443d958 100644 --- a/flatisfy/config.py +++ b/flatisfy/config.py @@ -23,17 +23,19 @@ from flatisfy import tools DEFAULT_CONFIG = { # Constraints to match "constraints": { - "type": None, # RENT, SALE, SHARING - "house_types": [], # List of house types, must be in APART, HOUSE, - # PARKING, LAND, OTHER or UNKNOWN - "postal_codes": [], # List of postal codes - "area": (None, None), # (min, max) in m^2 - "cost": (None, None), # (min, max) in currency unit - "rooms": (None, None), # (min, max) - "bedrooms": (None, None), # (min, max) - "time_to": {} # Dict mapping names to {"gps": [lat, lng], - # "time": (min, max) } - # Time is in seconds + "default": { + "type": None, # RENT, SALE, SHARING + "house_types": [], # List of house types, must be in APART, HOUSE, + # PARKING, LAND, OTHER or UNKNOWN + "postal_codes": [], # List of postal codes + "area": (None, None), # (min, max) in m^2 + "cost": (None, None), # (min, max) in currency unit + "rooms": (None, None), # (min, max) + "bedrooms": (None, None), # (min, max) + "time_to": {} # Dict mapping names to {"gps": [lat, lng], + # "time": (min, max) } + # Time is in seconds + } }, # Navitia API key "navitia_api_key": None, @@ -94,41 +96,44 @@ def validate_config(config): # and use long lines whenever needed, in order to have the full assert # message in the log output. # pylint: disable=locally-disabled,line-too-long - assert "type" in config["constraints"] - assert isinstance(config["constraints"]["type"], str) - assert config["constraints"]["type"].upper() in ["RENT", - "SALE", "SHARING"] - assert "house_types" in config["constraints"] - assert config["constraints"]["house_types"] - for house_type in config["constraints"]["house_types"]: - assert house_type.upper() in ["APART", "HOUSE", "PARKING", "LAND", - "OTHER", "UNKNOWN"] + # Ensure default constraint is here + assert "default" in config["constraints"] + # Ensure constraints are ok + for constraint in config["constraints"].values(): + assert "type" in constraint + assert isinstance(constraint["type"], str) + assert constraint["type"].upper() in ["RENT", "SALE", "SHARING"] - assert "postal_codes" in config["constraints"] - assert config["constraints"]["postal_codes"] + assert "house_types" in constraint + assert constraint["house_types"] + for house_type in constraint["house_types"]: + assert house_type.upper() in ["APART", "HOUSE", "PARKING", "LAND", "OTHER", "UNKNOWN"] # noqa: E501 - assert "area" in config["constraints"] - _check_constraints_bounds(config["constraints"]["area"]) + assert "postal_codes" in constraint + assert constraint["postal_codes"] - assert "cost" in config["constraints"] - _check_constraints_bounds(config["constraints"]["cost"]) + assert "area" in constraint + _check_constraints_bounds(constraint["area"]) - assert "rooms" in config["constraints"] - _check_constraints_bounds(config["constraints"]["rooms"]) + assert "cost" in constraint + _check_constraints_bounds(constraint["cost"]) - assert "bedrooms" in config["constraints"] - _check_constraints_bounds(config["constraints"]["bedrooms"]) + assert "rooms" in constraint + _check_constraints_bounds(constraint["rooms"]) - assert "time_to" in config["constraints"] - assert isinstance(config["constraints"]["time_to"], dict) - for name, item in config["constraints"]["time_to"].items(): - assert isinstance(name, str) - assert "gps" in item - assert isinstance(item["gps"], list) - assert len(item["gps"]) == 2 - assert "time" in item - _check_constraints_bounds(item["time"]) + assert "bedrooms" in constraint + _check_constraints_bounds(constraint["bedrooms"]) + + assert "time_to" in constraint + assert isinstance(constraint["time_to"], dict) + for name, item in constraint["time_to"].items(): + assert isinstance(name, str) + assert "gps" in item + assert isinstance(item["gps"], list) + assert len(item["gps"]) == 2 + assert "time" in item + _check_constraints_bounds(item["time"]) assert config["passes"] in [0, 1, 2, 3] assert config["max_entries"] is None or (isinstance(config["max_entries"], int) and config["max_entries"] > 0) # noqa: E501 diff --git a/flatisfy/data.py b/flatisfy/data.py index b860b13..698bd63 100644 --- a/flatisfy/data.py +++ b/flatisfy/data.py @@ -24,6 +24,9 @@ except ImportError: from functools32 import lru_cache except ImportError: def lru_cache(maxsize=None): + """ + Identity implementation of ``lru_cache`` for fallback. + """ return lambda func: func LOGGER.warning( "`functools.lru_cache` is not available on your system. Consider " @@ -66,19 +69,21 @@ def preprocess_data(config, force=False): @lru_cache(maxsize=5) -def load_data(model, config): +def load_data(model, constraint, config): """ Load data of the specified model from the database. Only load data for the specific areas of the postal codes in config. :param model: SQLAlchemy model to load. + :param constraint: A constraint from configuration to limit the spatial + extension of the loaded data. :param config: A config dictionary. :returns: A list of loaded SQLAlchemy objects from the db """ get_session = database.init_db(config["database"], config["search_index"]) results = [] with get_session() as session: - for postal_code in config["constraints"]["postal_codes"]: + for postal_code in constraint["postal_codes"]: area = data_files.french_postal_codes_to_iso_3166(postal_code) results.extend( session.query(model) diff --git a/flatisfy/data_files/__init__.py b/flatisfy/data_files/__init__.py index 65ff2b6..ef4872a 100644 --- a/flatisfy/data_files/__init__.py +++ b/flatisfy/data_files/__init__.py @@ -38,9 +38,9 @@ def french_postal_codes_to_iso_3166(postal_code): "FR-IDF": ["75", "77", "78", "91", "92", "93", "94", "95"], "FR-NOR": ["14", "27", "50", "61", "76"], "FR-NAQ": ["16", "17", "19", "23", "24", "33", "40", "47", "64", "79", - "86", "87"], + "86", "87"], "FR-OCC": ["09", "11", "12", "30", "31", "32", "34", "46", "48", "65", - "66", "81", "82"], + "66", "81", "82"], "FR-PDL": ["44", "49", "53", "72", "85"], "FR-PAC": ["04", "05", "06", "13", "83", "84"] } diff --git a/flatisfy/fetch.py b/flatisfy/fetch.py index cbd9f8a..13a5012 100644 --- a/flatisfy/fetch.py +++ b/flatisfy/fetch.py @@ -4,6 +4,7 @@ This module contains all the code related to fetching and loading flats lists. """ from __future__ import absolute_import, print_function, unicode_literals +import collections import itertools import json import logging @@ -225,29 +226,32 @@ class WeboobProxy(object): return "{}" -def fetch_flats_list(config): +def fetch_flats(config): """ Fetch the available flats using the Flatboob / Weboob config. :param config: A config dict. - :return: A list of all available flats. + :return: A dict mapping constraint in config to all available matching + flats. """ - flats_list = [] + fetched_flats = {} - with WeboobProxy(config) as weboob_proxy: - LOGGER.info("Loading flats...") - queries = weboob_proxy.build_queries(config["constraints"]) - housing_posts = [] - for query in queries: - housing_posts.extend( - weboob_proxy.query(query, config["max_entries"]) - ) + for constraint_name, constraint in config["constraints"].items(): + LOGGER.info("Loading flats for constraint %s...", constraint_name) + with WeboobProxy(config) as weboob_proxy: + queries = weboob_proxy.build_queries(constraint) + housing_posts = [] + for query in queries: + housing_posts.extend( + weboob_proxy.query(query, config["max_entries"]) + ) LOGGER.info("Fetched %d flats.", len(housing_posts)) - flats_list = [json.loads(flat) for flat in housing_posts] - flats_list = [WeboobProxy.restore_decimal_fields(flat) - for flat in flats_list] - return flats_list + constraint_flats_list = [json.loads(flat) for flat in housing_posts] + constraint_flats_list = [WeboobProxy.restore_decimal_fields(flat) + for flat in constraint_flats_list] + fetched_flats[constraint_name] = constraint_flats_list + return fetched_flats def fetch_details(config, flat_id): @@ -269,12 +273,18 @@ def fetch_details(config, flat_id): return flat_details -def load_flats_list_from_file(json_file): +def load_flats_from_file(json_file, config): """ Load a dumped flats list from JSON file. :param json_file: The file to load housings list from. - :return: A list of all the flats in the dump file. + :return: A dict mapping constraint in config to all available matching + flats. + + .. note:: + As we do not know which constraint is met by a given flat, all the + flats are returned for any available constraint, and they will be + filtered out afterwards. """ flats_list = [] try: @@ -284,21 +294,24 @@ def load_flats_list_from_file(json_file): LOGGER.info("Found %d flats.", len(flats_list)) except (IOError, ValueError): LOGGER.error("File %s is not a valid dump file.", json_file) - return flats_list + return { + constraint_name: flats_list + for constraint_name in config["constraints"] + } -def load_flats_list_from_db(config): +def load_flats_from_db(config): """ Load flats from database. :param config: A config dict. - :return: A list of all the flats in the database. + :return: A dict mapping constraint in config to all available matching + flats. """ - flats_list = [] get_session = database.init_db(config["database"], config["search_index"]) + loaded_flats = collections.defaultdict(list) with get_session() as session: - # TODO: Better serialization - flats_list = [flat.json_api_repr() - for flat in session.query(flat_model.Flat).all()] - return flats_list + for flat in session.query(flat_model.Flat).all(): + loaded_flats[flat.flatisfy_constraint].append(flat.json_api_repr()) + return loaded_flats diff --git a/flatisfy/filters/__init__.py b/flatisfy/filters/__init__.py index 17f77ea..1b9ac44 100644 --- a/flatisfy/filters/__init__.py +++ b/flatisfy/filters/__init__.py @@ -16,7 +16,7 @@ from flatisfy.filters import metadata LOGGER = logging.getLogger(__name__) -def refine_with_housing_criteria(flats_list, config): +def refine_with_housing_criteria(flats_list, constraint, config): """ Filter a list of flats according to criteria. @@ -25,6 +25,7 @@ def refine_with_housing_criteria(flats_list, config): user criteria, and avoid exposing unwanted flats. :param flats_list: A list of flats dict to filter. + :param constraint: The constraint that the ``flats_list`` should satisfy. :param config: A config dict. :return: A tuple of flats to keep and flats to delete. """ @@ -37,7 +38,7 @@ def refine_with_housing_criteria(flats_list, config): postal_code = flat["flatisfy"].get("postal_code", None) if ( postal_code and - postal_code not in config["constraints"]["postal_codes"] + postal_code not in constraint["postal_codes"] ): LOGGER.info("Postal code for flat %s is out of range.", flat["id"]) is_ok[i] = is_ok[i] and False @@ -47,7 +48,7 @@ def refine_with_housing_criteria(flats_list, config): time = time["time"] is_within_interval = tools.is_within_interval( time, - *(config["constraints"]["time_to"][place_name]["time"]) + *(constraint["time_to"][place_name]["time"]) ) if not is_within_interval: LOGGER.info("Flat %s is too far from place %s: %ds.", @@ -56,7 +57,7 @@ def refine_with_housing_criteria(flats_list, config): # Check other fields for field in ["area", "cost", "rooms", "bedrooms"]: - interval = config["constraints"][field] + interval = constraint[field] is_within_interval = tools.is_within_interval( flat.get(field, None), *interval @@ -80,7 +81,7 @@ def refine_with_housing_criteria(flats_list, config): ) -def first_pass(flats_list, config): +def first_pass(flats_list, constraint, config): """ First filtering pass. @@ -89,6 +90,7 @@ def first_pass(flats_list, config): only request more data for the remaining housings. :param flats_list: A list of flats dict to filter. + :param constraint: The constraint that the ``flats_list`` should satisfy. :param config: A config dict. :return: A dict mapping flat status and list of flat objects. """ @@ -108,11 +110,12 @@ def first_pass(flats_list, config): ) # Guess the postal codes - flats_list = metadata.guess_postal_code(flats_list, config) + flats_list = metadata.guess_postal_code(flats_list, constraint, config) # Try to match with stations - flats_list = metadata.guess_stations(flats_list, config) + flats_list = metadata.guess_stations(flats_list, constraint, config) # Remove returned housing posts that do not match criteria - flats_list, ignored_list = refine_with_housing_criteria(flats_list, config) + flats_list, ignored_list = refine_with_housing_criteria(flats_list, + constraint, config) return { "new": flats_list, @@ -121,7 +124,7 @@ def first_pass(flats_list, config): } -def second_pass(flats_list, config): +def second_pass(flats_list, constraint, config): """ Second filtering pass. @@ -133,6 +136,7 @@ def second_pass(flats_list, config): possible from the fetched housings. :param flats_list: A list of flats dict to filter. + :param constraint: The constraint that the ``flats_list`` should satisfy. :param config: A config dict. :return: A dict mapping flat status and list of flat objects. """ @@ -141,16 +145,17 @@ def second_pass(flats_list, config): # left and we already tried to find postal code and nearby stations. # Confirm postal code - flats_list = metadata.guess_postal_code(flats_list, config) + flats_list = metadata.guess_postal_code(flats_list, constraint, config) # Better match with stations (confirm and check better) - flats_list = metadata.guess_stations(flats_list, config) + flats_list = metadata.guess_stations(flats_list, constraint, config) # Compute travel time to specified points - flats_list = metadata.compute_travel_times(flats_list, config) + flats_list = metadata.compute_travel_times(flats_list, constraint, config) # Remove returned housing posts that do not match criteria - flats_list, ignored_list = refine_with_housing_criteria(flats_list, config) + flats_list, ignored_list = refine_with_housing_criteria(flats_list, + constraint, config) return { "new": flats_list, diff --git a/flatisfy/filters/metadata.py b/flatisfy/filters/metadata.py index 0613d11..20c22cd 100644 --- a/flatisfy/filters/metadata.py +++ b/flatisfy/filters/metadata.py @@ -19,13 +19,14 @@ from flatisfy.models.public_transport import PublicTransport LOGGER = logging.getLogger(__name__) -def init(flats_list): +def init(flats_list, constraint): """ Create a flatisfy key containing a dict of metadata fetched by flatisfy for each flat in the list. Also perform some basic transform on flat objects to prepare for the metadata fetching. :param flats_list: A list of flats dict. + :param constraint: The constraint that the ``flats_list`` should satisfy. :return: The updated list """ for flat in flats_list: @@ -41,6 +42,8 @@ def init(flats_list): # Create merged_ids key if "merged_ids" not in flat: flat["merged_ids"] = [flat["id"]] + if "constraint" not in flat: + flat["constraint"] = constraint return flats_list @@ -119,11 +122,12 @@ def fuzzy_match(query, choices, limit=3, threshold=75): return matches -def guess_postal_code(flats_list, config, distance_threshold=20000): +def guess_postal_code(flats_list, constraint, config, distance_threshold=20000): """ Try to guess the postal code from the location of the flats. :param flats_list: A list of flats dict. + :param constraint: The constraint that the ``flats_list`` should satisfy. :param config: A config dict. :param distance_threshold: Maximum distance in meters between the constraint postal codes (from config) and the one found by this function, @@ -132,7 +136,7 @@ def guess_postal_code(flats_list, config, distance_threshold=20000): :return: An updated list of flats dict with guessed postal code. """ opendata = { - "postal_codes": data.load_data(PostalCode, config) + "postal_codes": data.load_data(PostalCode, constraint, config) } for flat in flats_list: @@ -200,10 +204,10 @@ def guess_postal_code(flats_list, config, distance_threshold=20000): next( (x.lat, x.lng) for x in opendata["postal_codes"] - if x.postal_code == constraint + if x.postal_code == constraint_postal_code ) ) - for constraint in config["constraints"]["postal_codes"] + for constraint_postal_code in constraint["postal_codes"] ) if distance > distance_threshold: @@ -229,21 +233,21 @@ def guess_postal_code(flats_list, config, distance_threshold=20000): return flats_list -def guess_stations(flats_list, config, distance_threshold=1500): +def guess_stations(flats_list, constraint, config, distance_threshold=1500): """ Try to match the station field with a list of available stations nearby. :param flats_list: A list of flats dict. + :param constraint: The constraint that the ``flats_list`` should satisfy. :param config: A config dict. :param distance_threshold: Maximum distance (in meters) between the center of the postal code and the station to consider it ok. :return: An updated list of flats dict with guessed nearby stations. """ - # TODO: opendata["stations"] opendata = { - "postal_codes": data.load_data(PostalCode, config), - "stations": data.load_data(PublicTransport, config) + "postal_codes": data.load_data(PostalCode, constraint, config), + "stations": data.load_data(PublicTransport, constraint, config) } for flat in flats_list: @@ -343,12 +347,13 @@ def guess_stations(flats_list, config, distance_threshold=1500): return flats_list -def compute_travel_times(flats_list, config): +def compute_travel_times(flats_list, constraint, config): """ Compute the travel time between each flat and the points listed in the constraints. :param flats_list: A list of flats dict. + :param constraint: The constraint that the ``flats_list`` should satisfy. :param config: A config dict. :return: An updated list of flats dict with computed travel times. @@ -371,7 +376,7 @@ def compute_travel_times(flats_list, config): # For each place, loop over the stations close to the flat, and find # the minimum travel time. - for place_name, place in config["constraints"]["time_to"].items(): + for place_name, place in constraint["time_to"].items(): time_to_place = None for station in flat["flatisfy"]["matched_stations"]: time_from_station = tools.get_travel_time_between( diff --git a/flatisfy/models/flat.py b/flatisfy/models/flat.py index 3111f2d..71de109 100644 --- a/flatisfy/models/flat.py +++ b/flatisfy/models/flat.py @@ -86,6 +86,7 @@ class Flat(BASE): flatisfy_stations = Column(MagicJSON) flatisfy_postal_code = Column(String) flatisfy_time_to = Column(MagicJSON) + flatisfy_constraint = Column(String) # Status status = Column(Enum(FlatStatus), default=FlatStatus.new) diff --git a/flatisfy/tools.py b/flatisfy/tools.py index 91e6a71..86045c3 100644 --- a/flatisfy/tools.py +++ b/flatisfy/tools.py @@ -298,5 +298,4 @@ def get_travel_time_between(latlng_from, latlng_to, config): "time": time, "sections": sections } - else: - return None + return None diff --git a/flatisfy/web/js_src/store/getters.js b/flatisfy/web/js_src/store/getters.js index 539948c..c5b1f1c 100644 --- a/flatisfy/web/js_src/store/getters.js +++ b/flatisfy/web/js_src/store/getters.js @@ -5,7 +5,7 @@ export default { flat: (state, getters) => id => state.flats.find(flat => flat.id === id), - isLoading: state => state.loading > 0, + isLoading: state => state.loading > 0, postalCodesFlatsBuckets: (state, getters) => filter => { const postalCodeBuckets = {} diff --git a/flatisfy/web/routes/api.py b/flatisfy/web/routes/api.py index 297e2a0..e851922 100644 --- a/flatisfy/web/routes/api.py +++ b/flatisfy/web/routes/api.py @@ -39,7 +39,7 @@ def flats_v1(config, db): :return: The available flats objects in a JSON ``data`` dict. """ - postal_codes = flatisfy.data.load_data(PostalCode, config) + postal_codes = flatisfy.data.load_data(PostalCode, config) # TODO flats = [ flat.json_api_repr() @@ -99,7 +99,7 @@ def flat_v1(flat_id, config, db): :return: The flat object in a JSON ``data`` dict. """ - postal_codes = flatisfy.data.load_data(PostalCode, config) + postal_codes = flatisfy.data.load_data(PostalCode, config) # TODO flat = db.query(flat_model.Flat).filter_by(id=flat_id).first() @@ -222,7 +222,7 @@ def time_to_places_v1(config): """ places = { k: v["gps"] - for k, v in config["constraints"]["time_to"].items() + for k, v in config["constraints"]["time_to"].items() # TODO: Constraints should be named and stored in db along flats } return { "data": places @@ -240,7 +240,7 @@ def search_v1(db, config): :return: The matching flat objects in a JSON ``data`` dict. """ - postal_codes = flatisfy.data.load_data(PostalCode, config) + postal_codes = flatisfy.data.load_data(PostalCode, config) # TODO try: query = json.load(bottle.request.body)["query"]