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 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.types import MagicJSON
|
||||
@ -77,6 +79,7 @@ class Flat(BASE):
|
||||
urls = Column(MagicJSON)
|
||||
merged_ids = Column(MagicJSON)
|
||||
notes = Column(Text)
|
||||
notation = Column(SmallInteger, default=0)
|
||||
|
||||
# Flatisfy data
|
||||
# TODO: Should be in another table with relationships
|
||||
|
@ -80,6 +80,8 @@ def get_app(config):
|
||||
api_routes.update_flat_status_v1)
|
||||
app.route("/api/v1/flat/:flat_id/notes", "POST",
|
||||
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)
|
||||
|
||||
|
@ -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) {
|
||||
fetch('/api/v1/time_to/places', { credentials: 'same-origin' })
|
||||
.then(function (response) {
|
||||
|
@ -36,7 +36,7 @@
|
||||
<tbody>
|
||||
<tr v-for="flat in sortedFlats" :key="flat.id">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
@ -82,7 +82,7 @@
|
||||
<script>
|
||||
import 'font-awesome-webpack'
|
||||
|
||||
import { capitalize } from '../tools'
|
||||
import { capitalize, range } from '../tools'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
@ -123,7 +123,8 @@ export default {
|
||||
this.sortBy = field
|
||||
}
|
||||
},
|
||||
capitalize: capitalize
|
||||
capitalize: capitalize,
|
||||
range: range
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -27,6 +27,12 @@ export default {
|
||||
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 }) {
|
||||
commit(types.IS_LOADING)
|
||||
api.updateFlatNotes(flatId, newNotes, response => {
|
||||
|
@ -2,5 +2,6 @@ export const REPLACE_FLATS = 'REPLACE_FLATS'
|
||||
export const MERGE_FLATS = 'MERGE_FLATS'
|
||||
export const UPDATE_FLAT_STATUS = 'UPDATE_FLAT_STATUS'
|
||||
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 IS_LOADING = 'IS_LOADING'
|
||||
|
@ -39,6 +39,13 @@ export const mutations = {
|
||||
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 }) {
|
||||
state.loading = false
|
||||
state.timeToPlaces = timeToPlaces
|
||||
|
@ -19,3 +19,7 @@ export function findFlatGPS (flat) {
|
||||
export function capitalize (string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||
}
|
||||
|
||||
export function range (n) {
|
||||
return [...Array(n).keys()]
|
||||
}
|
||||
|
@ -136,17 +136,15 @@
|
||||
<nav>
|
||||
<ul>
|
||||
<template v-if="flat.status !== 'user_deleted'">
|
||||
<li>
|
||||
<template v-if="flat.status !== 'followed'">
|
||||
<button v-on:click="updateFlatStatus('followed')">
|
||||
<li ref="notationButton">
|
||||
<template v-for="n in range(notation)">
|
||||
<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>
|
||||
{{ $t("common.Follow") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button v-on:click="updateFlatStatus('new')">
|
||||
<template v-for="n in range(5 - notation)">
|
||||
<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>
|
||||
{{ $t("common.Unfollow") }}
|
||||
</button>
|
||||
</template>
|
||||
</li>
|
||||
@ -181,7 +179,7 @@ import 'font-awesome-webpack'
|
||||
import FlatsMap from '../components/flatsmap.vue'
|
||||
import Slider from '../components/slider.vue'
|
||||
|
||||
import { capitalize } from '../tools'
|
||||
import { capitalize, range } from '../tools'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -202,6 +200,12 @@ export default {
|
||||
'$route': 'fetchData'
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
'overloadNotation': null
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
flatMarkers () {
|
||||
return this.$store.getters.flatsMarkers(this.$router, flat => flat.id === this.$route.params.id)
|
||||
@ -212,6 +216,12 @@ export default {
|
||||
flat () {
|
||||
return this.$store.getters.flat(this.$route.params.id)
|
||||
},
|
||||
notation () {
|
||||
if (this.overloadNotation) {
|
||||
return this.overloadNotation
|
||||
}
|
||||
return this.flat.notation
|
||||
},
|
||||
journeys () {
|
||||
if (Object.keys(this.flat.flatisfy_time_to).length > 0) {
|
||||
const journeys = []
|
||||
@ -246,8 +256,16 @@ export default {
|
||||
this.$store.dispatch('getAllTimeToPlaces')
|
||||
},
|
||||
|
||||
updateFlatStatus (status) {
|
||||
this.$store.dispatch('updateFlatStatus', { flatId: this.$route.params.id, newStatus: status })
|
||||
updateFlatNotation (notation) {
|
||||
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 () {
|
||||
@ -263,7 +281,17 @@ export default {
|
||||
return minutes + ' ' + this.$tc('common.mins', minutes)
|
||||
},
|
||||
|
||||
capitalize: capitalize
|
||||
handleNotationHover (n) {
|
||||
this.overloadNotation = n + 1
|
||||
},
|
||||
|
||||
handleNotationOut () {
|
||||
this.overloadNotation = null
|
||||
},
|
||||
|
||||
capitalize: capitalize,
|
||||
|
||||
range: range
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -328,4 +356,10 @@ td {
|
||||
list-style-position: outside;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.btnIcon {
|
||||
border: none;
|
||||
width: auto;
|
||||
background-color: transparent;
|
||||
}
|
||||
</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):
|
||||
"""
|
||||
API v1 route to fetch the details of the places to compute time to.
|
||||
|
Loading…
Reference in New Issue
Block a user