Use stars to note flats
Instead of a binary "followed" / "not followed" status, use 5 stars to allow users to give a note to a flat between 0 (not followed) and 5. Any note different from zero add a "followed" status. Closes issue #36.
This commit is contained in:
parent
69588a9601
commit
0e3d1576b2
@ -10,7 +10,9 @@ import logging
|
|||||||
import arrow
|
import arrow
|
||||||
import enum
|
import enum
|
||||||
|
|
||||||
from sqlalchemy import Column, DateTime, Enum, Float, String, Text
|
from sqlalchemy import (
|
||||||
|
Column, DateTime, Enum, Float, SmallInteger, String, Text
|
||||||
|
)
|
||||||
|
|
||||||
from flatisfy.database.base import BASE
|
from flatisfy.database.base import BASE
|
||||||
from flatisfy.database.types import MagicJSON
|
from flatisfy.database.types import MagicJSON
|
||||||
@ -77,6 +79,7 @@ class Flat(BASE):
|
|||||||
urls = Column(MagicJSON)
|
urls = Column(MagicJSON)
|
||||||
merged_ids = Column(MagicJSON)
|
merged_ids = Column(MagicJSON)
|
||||||
notes = Column(Text)
|
notes = Column(Text)
|
||||||
|
notation = Column(SmallInteger, default=0)
|
||||||
|
|
||||||
# Flatisfy data
|
# Flatisfy data
|
||||||
# TODO: Should be in another table with relationships
|
# TODO: Should be in another table with relationships
|
||||||
|
@ -80,6 +80,8 @@ def get_app(config):
|
|||||||
api_routes.update_flat_status_v1)
|
api_routes.update_flat_status_v1)
|
||||||
app.route("/api/v1/flat/:flat_id/notes", "POST",
|
app.route("/api/v1/flat/:flat_id/notes", "POST",
|
||||||
api_routes.update_flat_notes_v1)
|
api_routes.update_flat_notes_v1)
|
||||||
|
app.route("/api/v1/flat/:flat_id/notation", "POST",
|
||||||
|
api_routes.update_flat_notation_v1)
|
||||||
|
|
||||||
app.route("/api/v1/search", "POST", api_routes.search_v1)
|
app.route("/api/v1/search", "POST", api_routes.search_v1)
|
||||||
|
|
||||||
|
@ -88,6 +88,24 @@ export const updateFlatNotes = function (flatId, newNotes, callback) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateFlatNotation = function (flatId, newNotation, callback) {
|
||||||
|
fetch(
|
||||||
|
'/api/v1/flat/' + encodeURIComponent(flatId) + '/notation',
|
||||||
|
{
|
||||||
|
credentials: 'same-origin',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
notation: newNotation
|
||||||
|
})
|
||||||
|
}
|
||||||
|
).then(callback).catch(function (ex) {
|
||||||
|
console.error('Unable to update flat notation: ' + ex)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const getTimeToPlaces = function (callback) {
|
export const getTimeToPlaces = function (callback) {
|
||||||
fetch('/api/v1/time_to/places', { credentials: 'same-origin' })
|
fetch('/api/v1/time_to/places', { credentials: 'same-origin' })
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="flat in sortedFlats" :key="flat.id">
|
<tr v-for="flat in sortedFlats" :key="flat.id">
|
||||||
<td>
|
<td>
|
||||||
<template v-if="flat.status === 'followed'">
|
<template v-for="n in range(flat.notation)">
|
||||||
<i class="fa fa-star" aria-hidden="true" :title="capitalize($t('status.followed'))"></i>
|
<i class="fa fa-star" aria-hidden="true" :title="capitalize($t('status.followed'))"></i>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -82,7 +82,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import 'font-awesome-webpack'
|
import 'font-awesome-webpack'
|
||||||
|
|
||||||
import { capitalize } from '../tools'
|
import { capitalize, range } from '../tools'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
@ -123,7 +123,8 @@ export default {
|
|||||||
this.sortBy = field
|
this.sortBy = field
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
capitalize: capitalize
|
capitalize: capitalize,
|
||||||
|
range: range
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -27,6 +27,12 @@ export default {
|
|||||||
commit(types.UPDATE_FLAT_STATUS, { flatId, newStatus })
|
commit(types.UPDATE_FLAT_STATUS, { flatId, newStatus })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
updateFlatNotation ({ commit }, { flatId, newNotation }) {
|
||||||
|
commit(types.IS_LOADING)
|
||||||
|
api.updateFlatNotation(flatId, newNotation, response => {
|
||||||
|
commit(types.UPDATE_FLAT_NOTATION, { flatId, newNotation })
|
||||||
|
})
|
||||||
|
},
|
||||||
updateFlatNotes ({ commit }, { flatId, newNotes }) {
|
updateFlatNotes ({ commit }, { flatId, newNotes }) {
|
||||||
commit(types.IS_LOADING)
|
commit(types.IS_LOADING)
|
||||||
api.updateFlatNotes(flatId, newNotes, response => {
|
api.updateFlatNotes(flatId, newNotes, response => {
|
||||||
|
@ -2,5 +2,6 @@ export const REPLACE_FLATS = 'REPLACE_FLATS'
|
|||||||
export const MERGE_FLATS = 'MERGE_FLATS'
|
export const MERGE_FLATS = 'MERGE_FLATS'
|
||||||
export const UPDATE_FLAT_STATUS = 'UPDATE_FLAT_STATUS'
|
export const UPDATE_FLAT_STATUS = 'UPDATE_FLAT_STATUS'
|
||||||
export const UPDATE_FLAT_NOTES = 'UPDATE_FLAT_NOTES'
|
export const UPDATE_FLAT_NOTES = 'UPDATE_FLAT_NOTES'
|
||||||
|
export const UPDATE_FLAT_NOTATION = 'UPDATE_FLAT_NOTATION'
|
||||||
export const RECEIVE_TIME_TO_PLACES = 'RECEIVE_TIME_TO_PLACES'
|
export const RECEIVE_TIME_TO_PLACES = 'RECEIVE_TIME_TO_PLACES'
|
||||||
export const IS_LOADING = 'IS_LOADING'
|
export const IS_LOADING = 'IS_LOADING'
|
||||||
|
@ -39,6 +39,13 @@ export const mutations = {
|
|||||||
Vue.set(state.flats[index], 'notes', newNotes)
|
Vue.set(state.flats[index], 'notes', newNotes)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
[types.UPDATE_FLAT_NOTATION] (state, { flatId, newNotation }) {
|
||||||
|
state.loading = false
|
||||||
|
const index = state.flats.findIndex(flat => flat.id === flatId)
|
||||||
|
if (index > -1) {
|
||||||
|
Vue.set(state.flats[index], 'notation', newNotation)
|
||||||
|
}
|
||||||
|
},
|
||||||
[types.RECEIVE_TIME_TO_PLACES] (state, { timeToPlaces }) {
|
[types.RECEIVE_TIME_TO_PLACES] (state, { timeToPlaces }) {
|
||||||
state.loading = false
|
state.loading = false
|
||||||
state.timeToPlaces = timeToPlaces
|
state.timeToPlaces = timeToPlaces
|
||||||
|
@ -19,3 +19,7 @@ export function findFlatGPS (flat) {
|
|||||||
export function capitalize (string) {
|
export function capitalize (string) {
|
||||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function range (n) {
|
||||||
|
return [...Array(n).keys()]
|
||||||
|
}
|
||||||
|
@ -136,17 +136,15 @@
|
|||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<template v-if="flat.status !== 'user_deleted'">
|
<template v-if="flat.status !== 'user_deleted'">
|
||||||
<li>
|
<li ref="notationButton">
|
||||||
<template v-if="flat.status !== 'followed'">
|
<template v-for="n in range(notation)">
|
||||||
<button v-on:click="updateFlatStatus('followed')">
|
<button class="btnIcon" v-on:mouseover="handleNotationHover(n)" v-on:mouseout="handleNotationOut()" v-on:click="updateFlatNotation(n)">
|
||||||
<i class="fa fa-star" aria-hidden="true"></i>
|
<i class="fa fa-star" aria-hidden="true"></i>
|
||||||
{{ $t("common.Follow") }}
|
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-for="n in range(5 - notation)">
|
||||||
<button v-on:click="updateFlatStatus('new')">
|
<button class="btnIcon" v-on:mouseover="handleNotationHover(notation + n)" v-on:mouseout="handleNotationOut()" v-on:click="updateFlatNotation(notation + n)">
|
||||||
<i class="fa fa-star-o" aria-hidden="true"></i>
|
<i class="fa fa-star-o" aria-hidden="true"></i>
|
||||||
{{ $t("common.Unfollow") }}
|
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</li>
|
</li>
|
||||||
@ -181,7 +179,7 @@ import 'font-awesome-webpack'
|
|||||||
import FlatsMap from '../components/flatsmap.vue'
|
import FlatsMap from '../components/flatsmap.vue'
|
||||||
import Slider from '../components/slider.vue'
|
import Slider from '../components/slider.vue'
|
||||||
|
|
||||||
import { capitalize } from '../tools'
|
import { capitalize, range } from '../tools'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -202,6 +200,12 @@ export default {
|
|||||||
'$route': 'fetchData'
|
'$route': 'fetchData'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
'overloadNotation': null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
flatMarkers () {
|
flatMarkers () {
|
||||||
return this.$store.getters.flatsMarkers(this.$router, flat => flat.id === this.$route.params.id)
|
return this.$store.getters.flatsMarkers(this.$router, flat => flat.id === this.$route.params.id)
|
||||||
@ -212,6 +216,12 @@ export default {
|
|||||||
flat () {
|
flat () {
|
||||||
return this.$store.getters.flat(this.$route.params.id)
|
return this.$store.getters.flat(this.$route.params.id)
|
||||||
},
|
},
|
||||||
|
notation () {
|
||||||
|
if (this.overloadNotation) {
|
||||||
|
return this.overloadNotation
|
||||||
|
}
|
||||||
|
return this.flat.notation
|
||||||
|
},
|
||||||
journeys () {
|
journeys () {
|
||||||
if (Object.keys(this.flat.flatisfy_time_to).length > 0) {
|
if (Object.keys(this.flat.flatisfy_time_to).length > 0) {
|
||||||
const journeys = []
|
const journeys = []
|
||||||
@ -246,8 +256,16 @@ export default {
|
|||||||
this.$store.dispatch('getAllTimeToPlaces')
|
this.$store.dispatch('getAllTimeToPlaces')
|
||||||
},
|
},
|
||||||
|
|
||||||
updateFlatStatus (status) {
|
updateFlatNotation (notation) {
|
||||||
this.$store.dispatch('updateFlatStatus', { flatId: this.$route.params.id, newStatus: status })
|
notation = notation + 1
|
||||||
|
|
||||||
|
if (notation === this.flat.notation) {
|
||||||
|
this.$store.dispatch('updateFlatNotation', { flatId: this.$route.params.id, newNotation: 0 })
|
||||||
|
this.$store.dispatch('updateFlatStatus', { flatId: this.$route.params.id, newStatus: 'new' })
|
||||||
|
} else {
|
||||||
|
this.$store.dispatch('updateFlatNotation', { flatId: this.$route.params.id, newNotation: notation })
|
||||||
|
this.$store.dispatch('updateFlatStatus', { flatId: this.$route.params.id, newStatus: 'followed' })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateFlatNotes () {
|
updateFlatNotes () {
|
||||||
@ -263,7 +281,17 @@ export default {
|
|||||||
return minutes + ' ' + this.$tc('common.mins', minutes)
|
return minutes + ' ' + this.$tc('common.mins', minutes)
|
||||||
},
|
},
|
||||||
|
|
||||||
capitalize: capitalize
|
handleNotationHover (n) {
|
||||||
|
this.overloadNotation = n + 1
|
||||||
|
},
|
||||||
|
|
||||||
|
handleNotationOut () {
|
||||||
|
this.overloadNotation = null
|
||||||
|
},
|
||||||
|
|
||||||
|
capitalize: capitalize,
|
||||||
|
|
||||||
|
range: range
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -328,4 +356,10 @@ td {
|
|||||||
list-style-position: outside;
|
list-style-position: outside;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btnIcon {
|
||||||
|
border: none;
|
||||||
|
width: auto;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -174,6 +174,34 @@ def update_flat_notes_v1(flat_id, db):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def update_flat_notation_v1(flat_id, db):
|
||||||
|
"""
|
||||||
|
API v1 route to update flat notation:
|
||||||
|
|
||||||
|
POST /api/v1/flat/:flat_id/notation
|
||||||
|
Data: {
|
||||||
|
"notation": "NEW_NOTATION"
|
||||||
|
}
|
||||||
|
|
||||||
|
:return: The new flat object in a JSON ``data`` dict.
|
||||||
|
"""
|
||||||
|
flat = db.query(flat_model.Flat).filter_by(id=flat_id).first()
|
||||||
|
if not flat:
|
||||||
|
return bottle.HTTPError(404, "No flat with id {}.".format(flat_id))
|
||||||
|
|
||||||
|
try:
|
||||||
|
flat.notation = json.load(bottle.request.body)["notation"]
|
||||||
|
assert flat.notes >= 0 and flat.notes <= 5
|
||||||
|
except (AssertionError, ValueError, KeyError):
|
||||||
|
return bottle.HTTPError(400, "Invalid notation provided.")
|
||||||
|
|
||||||
|
json_flat = flat.json_api_repr()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"data": json_flat
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def time_to_places_v1(config):
|
def time_to_places_v1(config):
|
||||||
"""
|
"""
|
||||||
API v1 route to fetch the details of the places to compute time to.
|
API v1 route to fetch the details of the places to compute time to.
|
||||||
|
Loading…
Reference in New Issue
Block a user