Add a page listing flats by status
Also display the journeys on the details view.
This commit is contained in:
parent
5f2f4d0ccf
commit
0fb5f28184
@ -379,7 +379,7 @@ def compute_travel_times(flats_list, config):
|
||||
if time_to_place:
|
||||
LOGGER.info(
|
||||
"Travel time between %s and flat %s is %ds.",
|
||||
place_name, flat["id"], time_to_place
|
||||
place_name, flat["id"], time_to_place["time"]
|
||||
)
|
||||
flat["flatisfy"]["time_to"][place_name] = time_to_place
|
||||
return flats_list
|
||||
|
@ -235,8 +235,8 @@ def get_travel_time_between(latlng_from, latlng_to, config):
|
||||
:param latlng_from: A tuple of (latitude, longitude) for the starting
|
||||
point.
|
||||
:param latlng_to: A tuple of (latitude, longitude) for the destination.
|
||||
:return: The travel time in seconds. Returns ``None`` if it could not fetch
|
||||
it.
|
||||
:return: A dict of the travel time in seconds and sections of the journey
|
||||
with GeoJSON paths. Returns ``None`` if it could not fetch it.
|
||||
|
||||
.. note :: Uses the Navitia API. Requires a ``navitia_api_key`` field to be
|
||||
filled-in in the ``config``.
|
||||
@ -258,7 +258,28 @@ def get_travel_time_between(latlng_from, latlng_to, config):
|
||||
auth=(config["navitia_api_key"], "")
|
||||
)
|
||||
req.raise_for_status()
|
||||
time = req.json()["journeys"][0]["durations"]["total"]
|
||||
|
||||
journeys = req.json()["journeys"][0]
|
||||
time = journeys["durations"]["total"]
|
||||
sections = []
|
||||
for section in journeys["sections"]:
|
||||
if section["type"] == "public_transport":
|
||||
# Public transport
|
||||
sections.append({
|
||||
"geojson": section["geojson"],
|
||||
"color": (
|
||||
section["display_informations"].get("color", None)
|
||||
)
|
||||
})
|
||||
elif section["type"] == "street_network":
|
||||
# Walking
|
||||
sections.append({
|
||||
"geojson": section["geojson"],
|
||||
"color": None
|
||||
})
|
||||
else:
|
||||
# Skip anything else
|
||||
continue
|
||||
except (requests.exceptions.RequestException,
|
||||
ValueError, IndexError, KeyError) as exc:
|
||||
# Ignore any possible exception
|
||||
@ -272,4 +293,10 @@ def get_travel_time_between(latlng_from, latlng_to, config):
|
||||
"No API key available for travel time lookup. Please provide "
|
||||
"a Navitia API key. Skipping travel time lookup."
|
||||
)
|
||||
return time
|
||||
if time:
|
||||
return {
|
||||
"time": time,
|
||||
"sections": sections
|
||||
}
|
||||
else:
|
||||
return None
|
||||
|
@ -4,9 +4,8 @@
|
||||
<nav>
|
||||
<ul>
|
||||
<li><router-link :to="{name: 'home'}">{{ $t("menu.available_flats") }}</router-link></li>
|
||||
<li><router-link :to="{name: 'followed'}">{{ $t("menu.followed_flats") }}</router-link></li>
|
||||
<li><router-link :to="{name: 'ignored'}">{{ $t("menu.ignored_flats") }}</router-link></li>
|
||||
<li><router-link :to="{name: 'user_deleted'}">{{ $t("menu.user_deleted_flats") }}</router-link></li>
|
||||
<li><router-link :to="{name: 'status', params: {status: 'followed'}}">{{ $t("menu.followed_flats") }}</router-link></li>
|
||||
<li><router-link :to="{name: 'status', params: {status: 'new'}}">{{ $t("menu.by_status") }}</router-link></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<router-view></router-view>
|
||||
|
@ -12,6 +12,9 @@
|
||||
<v-tooltip :content="place_name"></v-tooltip>
|
||||
</v-marker>
|
||||
</template>
|
||||
<template v-for="journey in journeys">
|
||||
<v-geojson-layer :geojson="journey.geojson" :options="Object.assign({}, defaultGeoJSONOptions, journey.options)"></v-geojson-layer>
|
||||
</template>
|
||||
</v-map>
|
||||
</div>
|
||||
</template>
|
||||
@ -30,6 +33,13 @@ import Vue2Leaflet from 'vue2-leaflet'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
defaultGeoJSONOptions: {
|
||||
weight: 5,
|
||||
color: '#000',
|
||||
opacity: 1,
|
||||
fillColor: '#e4ce7f',
|
||||
fillOpacity: 1
|
||||
},
|
||||
center: null,
|
||||
zoom: {
|
||||
defaultZoom: 13,
|
||||
@ -59,7 +69,8 @@ export default {
|
||||
'v-tilelayer': Vue2Leaflet.TileLayer,
|
||||
'v-marker': Vue2Leaflet.Marker,
|
||||
'v-tooltip': Vue2Leaflet.Tooltip,
|
||||
'v-popup': Vue2Leaflet.Popup
|
||||
'v-popup': Vue2Leaflet.Popup,
|
||||
'v-geojson-layer': Vue2Leaflet.GeoJSON
|
||||
},
|
||||
|
||||
computed: {
|
||||
@ -77,7 +88,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
props: ['flats', 'places']
|
||||
props: ['flats', 'places', 'journeys']
|
||||
|
||||
// TODO: Add a switch to display a layer with isochrones
|
||||
}
|
||||
|
@ -21,8 +21,7 @@ export default {
|
||||
menu: {
|
||||
'available_flats': 'Available flats',
|
||||
'followed_flats': 'Followed flats',
|
||||
'ignored_flats': 'Ignored flats',
|
||||
'user_deleted_flats': 'User deleted flats'
|
||||
'by_status': 'Flats by status'
|
||||
},
|
||||
flatsDetails: {
|
||||
'Title': 'Title',
|
||||
@ -49,7 +48,8 @@ export default {
|
||||
'new': 'new',
|
||||
'followed': 'followed',
|
||||
'ignored': 'ignored',
|
||||
'user_deleted': 'user deleted'
|
||||
'user_deleted': 'user deleted',
|
||||
'duplicate': 'duplicate'
|
||||
},
|
||||
slider: {
|
||||
'Fullscreen_photo': 'Fullscreen photo'
|
||||
|
@ -11,9 +11,7 @@ export default new VueRouter({
|
||||
routes: [
|
||||
{ path: '/', component: Home, name: 'home' },
|
||||
{ path: '/new', redirect: '/' },
|
||||
{ path: '/followed', component: Status, name: 'followed' },
|
||||
{ path: '/ignored', component: Status, name: 'ignored' },
|
||||
{ path: '/user_deleted', component: Status, name: 'user_deleted' },
|
||||
{ path: '/status/:status', component: Status, name: 'status' },
|
||||
{ path: '/flat/:id', component: Details, name: 'details' }
|
||||
]
|
||||
})
|
||||
|
@ -84,7 +84,7 @@
|
||||
<template v-if="Object.keys(flat.flatisfy_time_to).length">
|
||||
<ul class="time_to_list">
|
||||
<li v-for="(time_to, place) in flat.flatisfy_time_to" :key="place">
|
||||
{{ place }}: {{ time_to }}
|
||||
{{ place }}: {{ time_to["time"] }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
@ -98,7 +98,7 @@
|
||||
<div>
|
||||
<h3>{{ $t("flatsDetails.Location") }}</h3>
|
||||
|
||||
<FlatsMap :flats="flatMarkers" :places="timeToPlaces"></FlatsMap>
|
||||
<FlatsMap :flats="flatMarkers" :places="timeToPlaces" :journeys="journeys"></FlatsMap>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-panel">
|
||||
@ -195,6 +195,24 @@ export default {
|
||||
flat () {
|
||||
return this.$store.getters.flat(this.$route.params.id)
|
||||
},
|
||||
journeys () {
|
||||
if (Object.keys(this.flat.flatisfy_time_to).length > 0) {
|
||||
const journeys = []
|
||||
for (const place in this.flat.flatisfy_time_to) {
|
||||
this.flat.flatisfy_time_to[place].sections.forEach(
|
||||
section => journeys.push({
|
||||
geojson: section.geojson,
|
||||
options: {
|
||||
color: section.color ? ('#' + section.color) : '#2196f3',
|
||||
dashArray: section.color ? 'none' : '2, 10'
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
return journeys
|
||||
}
|
||||
return []
|
||||
},
|
||||
displayedStations () {
|
||||
if (this.flat.flatisfy_stations.length > 0) {
|
||||
const stationsNames = this.flat.flatisfy_stations.map(station => station.name)
|
||||
|
@ -1,6 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2>{{ capitalize($t("status." + $route.name)) }}</h2>
|
||||
<h2 class="btn-group">
|
||||
<button type="button" class="dropdownToggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-on:click="toggleDropdown()" ref="dropdownToggle">
|
||||
<span class="dashedUnderline">{{ capitalize($t("status." + $route.params.status)) }}</span>
|
||||
<i class="fa fa-caret-down"/>
|
||||
</button>
|
||||
<ul class="dropdownMenu" :class="isDropdownVisible ? '' : 'hidden'">
|
||||
<li v-for="status in available_status" :key="status" :class="$route.params.status == status.toLowerCase() ? 'active' : ''">
|
||||
<router-link :to="{ name: 'status', params: {status: status.toLowerCase()}}" role="button">
|
||||
{{ capitalize($t("status." + status)) }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</h2>
|
||||
<template v-if="Object.keys(postalCodesFlatsBuckets).length">
|
||||
<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", 42) }}</h3>
|
||||
@ -19,6 +31,19 @@ import { capitalize } from '../tools'
|
||||
import FlatsTable from '../components/flatstable.vue'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
'isDropdownVisible': false,
|
||||
'available_status': [
|
||||
'new',
|
||||
'followed',
|
||||
'ignored',
|
||||
'duplicate',
|
||||
'user_deleted'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
FlatsTable
|
||||
},
|
||||
@ -28,14 +53,114 @@ export default {
|
||||
this.$store.dispatch('getAllFlats')
|
||||
},
|
||||
|
||||
mounted () {
|
||||
window.addEventListener('click', event => {
|
||||
if (event.target !== this.$refs.dropdownToggle) {
|
||||
this.isDropdownVisible = false
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
computed: {
|
||||
postalCodesFlatsBuckets () {
|
||||
return this.$store.getters.postalCodesFlatsBuckets(flat => flat.status === this.$route.name)
|
||||
return this.$store.getters.postalCodesFlatsBuckets(flat => flat.status === this.$route.params.status)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleDropdown () {
|
||||
this.isDropdownVisible = !this.isDropdownVisible
|
||||
},
|
||||
capitalize: capitalize
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.btn-group {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.btn-group > .btn {
|
||||
position: relative;
|
||||
flex: 0 1 auto;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.btn-group > .btn:hover {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.btn-group > .btn:focus,
|
||||
.btn-group > .btn:active,
|
||||
.btn-group > .btn.active {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.dropdownToggle {
|
||||
border: none;
|
||||
padding: 0;
|
||||
line-height: 1em;
|
||||
background-color: transparent;
|
||||
font-size: 21px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
font-family: "Helvetica", "Arial", sans-serif;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dropdownMenu {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
min-width: 160px;
|
||||
background-color: white;
|
||||
z-index: 1;
|
||||
padding-left: 0;
|
||||
border-radius: .25rem;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.dropdownMenu li {
|
||||
list-style-type: none;
|
||||
padding-left: 1em;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
|
||||
.dropdownMenu a {
|
||||
text-decoration: none;
|
||||
color: #555;
|
||||
font-size: 0.75em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.dashedUnderline {
|
||||
border-bottom: 1px dotted black;
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #0275d8;
|
||||
}
|
||||
|
||||
.dropdownMenu li.active a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.fa-caret-down {
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
margin-top: 1em;
|
||||
margin-left: 0.1em;
|
||||
}
|
||||
|
||||
.dashedUnderline,
|
||||
.fa-caret-down {
|
||||
/* Fix for alignment of caret and border-bottom */
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user