Various bug fixes

Fix some bugs introduced in recent developments as well as some UI
issues (and i18n).

Closes issue #42.
This commit is contained in:
Lucas Verney 2017-05-04 20:52:10 +02:00
parent 49f5b6a714
commit a5aeadef86
No known key found for this signature in database
GPG Key ID: 75B45CF41F334690
9 changed files with 51 additions and 15 deletions

View File

@ -35,6 +35,7 @@ def filter_flats(config, flats_list, fetch_details=True):
first_pass_result = collections.defaultdict(list) first_pass_result = collections.defaultdict(list)
second_pass_result = collections.defaultdict(list) second_pass_result = collections.defaultdict(list)
third_pass_result = collections.defaultdict(list)
# Do a first pass with the available infos to try to remove as much # Do a first pass with the available infos to try to remove as much
# unwanted postings as possible # unwanted postings as possible
if config["passes"] > 0: if config["passes"] > 0:
@ -58,14 +59,25 @@ def filter_flats(config, flats_list, fetch_details=True):
else: else:
second_pass_result["new"] = first_pass_result["new"] second_pass_result["new"] = first_pass_result["new"]
# Do a third pass to deduplicate better
if config["passes"] > 2:
third_pass_result = flatisfy.filters.third_pass(
second_pass_result["new"], config
)
else:
third_pass_result["new"] = second_pass_result["new"]
return { return {
"new": second_pass_result["new"], "new": third_pass_result["new"],
"duplicate": ( "duplicate": (
first_pass_result["duplicate"] + first_pass_result["duplicate"] +
second_pass_result["duplicate"] second_pass_result["duplicate"] +
third_pass_result["duplicate"]
), ),
"ignored": ( "ignored": (
first_pass_result["ignored"] + second_pass_result["ignored"] first_pass_result["ignored"] +
second_pass_result["ignored"] +
third_pass_result["ignored"]
) )
} }

View File

@ -38,7 +38,7 @@ DEFAULT_CONFIG = {
# Navitia API key # Navitia API key
"navitia_api_key": None, "navitia_api_key": None,
# Number of filtering passes to run # Number of filtering passes to run
"passes": 2, "passes": 3,
# Maximum number of entries to fetch # Maximum number of entries to fetch
"max_entries": None, "max_entries": None,
# Directory in wich data will be put. ``None`` is XDG default location. # Directory in wich data will be put. ``None`` is XDG default location.
@ -129,7 +129,7 @@ def validate_config(config):
assert "time" in item assert "time" in item
_check_constraints_bounds(item["time"]) _check_constraints_bounds(item["time"])
assert config["passes"] in [0, 1, 2] 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 assert config["max_entries"] is None or (isinstance(config["max_entries"], int) and config["max_entries"] > 0) # noqa: E501
assert config["data_directory"] is None or isinstance(config["data_directory"], str) # noqa: E501 assert config["data_directory"] is None or isinstance(config["data_directory"], str) # noqa: E501

View File

@ -44,13 +44,14 @@ def refine_with_housing_criteria(flats_list, config):
# Check time_to # Check time_to
for place_name, time in flat["flatisfy"].get("time_to", {}).items(): for place_name, time in flat["flatisfy"].get("time_to", {}).items():
time = time["time"]
is_within_interval = tools.is_within_interval( is_within_interval = tools.is_within_interval(
time, time,
*(config["constraints"]["time_to"][place_name]["time"]) *(config["constraints"]["time_to"][place_name]["time"])
) )
if not is_within_interval: if not is_within_interval:
LOGGER.info("Flat %s is too far from place %s.", LOGGER.info("Flat %s is too far from place %s: %ds.",
flat["id"], place_name) flat["id"], place_name, time)
is_ok[i] = is_ok[i] and is_within_interval is_ok[i] = is_ok[i] and is_within_interval
# Check other fields # Check other fields
@ -148,14 +149,32 @@ def second_pass(flats_list, config):
# Compute travel time to specified points # Compute travel time to specified points
flats_list = metadata.compute_travel_times(flats_list, config) flats_list = metadata.compute_travel_times(flats_list, config)
# Deduplicate the list using every available data
flats_list, duplicate_flats = duplicates.deep_detect(flats_list)
# Remove returned housing posts that do not match criteria # 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, config)
return { return {
"new": flats_list, "new": flats_list,
"ignored": ignored_list, "ignored": ignored_list,
"duplicate": []
}
def third_pass(flats_list, config):
"""
Third filtering pass.
This pass is expected to perform deep duplicate detection on available
flats.
:param flats_list: A list of flats dict to filter.
:param config: A config dict.
:return: A dict mapping flat status and list of flat objects.
"""
# Deduplicate the list using every available data
flats_list, duplicate_flats = duplicates.deep_detect(flats_list)
return {
"new": flats_list,
"ignored": [],
"duplicate": duplicate_flats "duplicate": duplicate_flats
} }

View File

@ -184,6 +184,7 @@ def deep_detect(flats_list):
the flats objects that should be removed and considered as duplicates (they the flats objects that should be removed and considered as duplicates (they
were already merged). were already merged).
""" """
LOGGER.info("Running deep duplicates detection.")
matching_flats = collections.defaultdict(list) matching_flats = collections.defaultdict(list)
for i, flat1 in enumerate(flats_list): for i, flat1 in enumerate(flats_list):
matching_flats[flat1["id"]].append(flat1["id"]) matching_flats[flat1["id"]].append(flat1["id"])

View File

@ -268,6 +268,10 @@ export default {
} }
}, },
updateFlatStatus (status) {
this.$store.dispatch('updateFlatStatus', { flatId: this.$route.params.id, newStatus: status })
},
updateFlatNotes () { updateFlatNotes () {
const notes = this.$refs.notesTextarea.value const notes = this.$refs.notesTextarea.value
this.$store.dispatch( this.$store.dispatch(

View File

@ -6,7 +6,7 @@
<h2>{{ $t("home.new_available_flats") }}</h2> <h2>{{ $t("home.new_available_flats") }}</h2>
<template v-if="Object.keys(postalCodesFlatsBuckets).length > 0"> <template v-if="Object.keys(postalCodesFlatsBuckets).length > 0">
<template v-for="(postal_code_data, postal_code) in postalCodesFlatsBuckets"> <template v-for="(postal_code_data, postal_code) in postalCodesFlatsBuckets">
<h3>{{ postal_code_data.name }} ({{ postal_code }}) - {{ postal_code_data.flats.length }} {{ $tc("common.flats", Object.keys(postalCodesFlatsBuckets).length) }}</h3> <h3>{{ postal_code_data.name }} ({{ postal_code }}) - {{ postal_code_data.flats.length }} {{ $tc("common.flats", postal_code_data.flats.length) }}</h3>
<FlatsTable :flats="postal_code_data.flats"></FlatsTable> <FlatsTable :flats="postal_code_data.flats"></FlatsTable>
</template> </template>
</template> </template>

View File

@ -14,7 +14,7 @@
</template> </template>
<template v-else-if="Object.keys(postalCodesFlatsBuckets).length > 0"> <template v-else-if="Object.keys(postalCodesFlatsBuckets).length > 0">
<template v-for="(postal_code_data, postal_code) in postalCodesFlatsBuckets"> <template v-for="(postal_code_data, postal_code) in postalCodesFlatsBuckets">
<h3>{{ postal_code_data.name }} ({{ postal_code }}) - {{ postal_code_data.flats.length }} {{ $tc("common.flats", Object.keys(postalCodesFlatsBuckets).length) }}</h3> <h3>{{ postal_code_data.name }} ({{ postal_code }}) - {{ postal_code_data.flats.length }} {{ $tc("common.flats", postal_code_data.flats.length) }}</h3>
<FlatsTable :flats="postal_code_data.flats"></FlatsTable> <FlatsTable :flats="postal_code_data.flats"></FlatsTable>
</template> </template>
</template> </template>
@ -47,7 +47,7 @@ export default {
} }
return this.$store.getters.postalCodesFlatsBuckets( return this.$store.getters.postalCodesFlatsBuckets(
flat => flat.status !== 'duplicate' && flat.status !== 'ignored' flat => flat.status !== 'duplicate' && flat.status !== 'ignored' && flat.status !== "user_deleted"
) )
}, },
loading () { loading () {

View File

@ -15,7 +15,7 @@
</h2> </h2>
<template v-if="Object.keys(postalCodesFlatsBuckets).length"> <template v-if="Object.keys(postalCodesFlatsBuckets).length">
<template v-for="(postal_code_data, postal_code) in postalCodesFlatsBuckets"> <template v-for="(postal_code_data, postal_code) in postalCodesFlatsBuckets">
<h3>{{ postal_code_data.name }} ({{ postal_code }}) - {{ postal_code_data.flats.length }} {{ $tc("common.flats", Object.keys(postalCodesFlatsBuckets).length) }}</h3> <h3>{{ postal_code_data.name }} ({{ postal_code }}) - {{ postal_code_data.flats.length }} {{ $tc("common.flats", postal_code_data.flats.length) }}</h3>
<FlatsTable :flats="postal_code_data.flats"></FlatsTable> <FlatsTable :flats="postal_code_data.flats"></FlatsTable>
</template> </template>
</template> </template>

View File

@ -191,7 +191,7 @@ def update_flat_notation_v1(flat_id, db):
try: try:
flat.notation = json.load(bottle.request.body)["notation"] flat.notation = json.load(bottle.request.body)["notation"]
assert flat.notes >= 0 and flat.notes <= 5 assert flat.notation >= 0 and flat.notation <= 5
except (AssertionError, ValueError, KeyError): except (AssertionError, ValueError, KeyError):
return bottle.HTTPError(400, "Invalid notation provided.") return bottle.HTTPError(400, "Invalid notation provided.")