diff --git a/flatisfy/database/whooshalchemy.py b/flatisfy/database/whooshalchemy.py index 223dd8a..af656bc 100644 --- a/flatisfy/database/whooshalchemy.py +++ b/flatisfy/database/whooshalchemy.py @@ -12,6 +12,7 @@ Based on Flask-whooshalchemy by Karl Gyllstrom (Flask is still supported, but no :copyright: (c) 2012 by Karl Gyllstrom :license: BSD (see LICENSE.txt) """ +# pylint: skip-file from __future__ import absolute_import, print_function, unicode_literals diff --git a/flatisfy/models/flat.py b/flatisfy/models/flat.py index aee1f31..df67f7a 100644 --- a/flatisfy/models/flat.py +++ b/flatisfy/models/flat.py @@ -76,6 +76,7 @@ class Flat(BASE): title = Column(String) urls = Column(MagicJSON) merged_ids = Column(MagicJSON) + notes = Column(Text) # Flatisfy data # TODO: Should be in another table with relationships diff --git a/flatisfy/web/app.py b/flatisfy/web/app.py index c79f6ff..2709dbd 100644 --- a/flatisfy/web/app.py +++ b/flatisfy/web/app.py @@ -78,6 +78,8 @@ def get_app(config): app.route("/api/v1/flat/:flat_id", "GET", api_routes.flat_v1) app.route("/api/v1/flat/:flat_id/status", "POST", 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/search", "POST", api_routes.search_v1) diff --git a/flatisfy/web/js_src/api/index.js b/flatisfy/web/js_src/api/index.js index 770a013..1c59ebb 100644 --- a/flatisfy/web/js_src/api/index.js +++ b/flatisfy/web/js_src/api/index.js @@ -68,7 +68,24 @@ export const updateFlatStatus = function (flatId, newStatus, callback) { ).then(callback).catch(function (ex) { console.error('Unable to update flat status: ' + ex) }) +} +export const updateFlatNotes = function (flatId, newNotes, callback) { + fetch( + '/api/v1/flat/' + encodeURIComponent(flatId) + '/notes', + { + credentials: 'same-origin', + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + notes: newNotes + }) + } + ).then(callback).catch(function (ex) { + console.error('Unable to update flat notes: ' + ex) + }) } export const getTimeToPlaces = function (callback) { @@ -82,7 +99,6 @@ export const getTimeToPlaces = function (callback) { }) } - export const doSearch = function (query, callback) { fetch( '/api/v1/search', @@ -101,5 +117,4 @@ export const doSearch = function (query, callback) { }).catch(function (ex) { console.error('Unable to perform search: ' + ex) }) - } diff --git a/flatisfy/web/js_src/store/actions.js b/flatisfy/web/js_src/store/actions.js index 66cdaaa..3d7909a 100644 --- a/flatisfy/web/js_src/store/actions.js +++ b/flatisfy/web/js_src/store/actions.js @@ -27,6 +27,12 @@ export default { commit(types.UPDATE_FLAT_STATUS, { flatId, newStatus }) }) }, + updateFlatNotes ({ commit }, { flatId, newNotes }) { + commit(types.IS_LOADING) + api.updateFlatNotes(flatId, newNotes, response => { + commit(types.UPDATE_FLAT_NOTES, { flatId, newNotes }) + }) + }, doSearch ({ commit }, { query }) { commit(types.IS_LOADING) api.doSearch(query, flats => { diff --git a/flatisfy/web/js_src/store/mutations-types.js b/flatisfy/web/js_src/store/mutations-types.js index b731821..f7a2cb6 100644 --- a/flatisfy/web/js_src/store/mutations-types.js +++ b/flatisfy/web/js_src/store/mutations-types.js @@ -1,5 +1,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 RECEIVE_TIME_TO_PLACES = 'RECEIVE_TIME_TO_PLACES' export const IS_LOADING = 'IS_LOADING' diff --git a/flatisfy/web/js_src/store/mutations.js b/flatisfy/web/js_src/store/mutations.js index 4d22563..9cda85e 100644 --- a/flatisfy/web/js_src/store/mutations.js +++ b/flatisfy/web/js_src/store/mutations.js @@ -32,6 +32,13 @@ export const mutations = { Vue.set(state.flats[index], 'status', newStatus) } }, + [types.UPDATE_FLAT_NOTES] (state, { flatId, newNotes }) { + state.loading = false + const index = state.flats.findIndex(flat => flat.id === flatId) + if (index > -1) { + Vue.set(state.flats[index], 'notes', newNotes) + } + }, [types.RECEIVE_TIME_TO_PLACES] (state, { timeToPlaces }) { state.loading = false state.timeToPlaces = timeToPlaces diff --git a/flatisfy/web/js_src/views/details.vue b/flatisfy/web/js_src/views/details.vue index ef114d6..a1a784e 100644 --- a/flatisfy/web/js_src/views/details.vue +++ b/flatisfy/web/js_src/views/details.vue @@ -101,6 +101,14 @@ +
+

Notes

+ +
+ +

+
+

{{ $t("flatsDetails.Contact") }}

@@ -242,6 +250,14 @@ export default { this.$store.dispatch('updateFlatStatus', { flatId: this.$route.params.id, newStatus: status }) }, + updateFlatNotes () { + const notes = this.$refs.notesTextarea.value + this.$store.dispatch( + 'updateFlatNotes', + { flatId: this.$route.params.id, newNotes: notes } + ) + }, + humanizeTimeTo (time) { const minutes = Math.floor(time.as('minutes')) return minutes + ' ' + this.$tc('common.mins', minutes) @@ -269,6 +285,10 @@ export default { grid-row: 1; } +.left-panel textarea { + width: 100%; +} + .right { text-align: right; } diff --git a/flatisfy/web/js_src/views/search.vue b/flatisfy/web/js_src/views/search.vue index 980d424..23f381b 100644 --- a/flatisfy/web/js_src/views/search.vue +++ b/flatisfy/web/js_src/views/search.vue @@ -47,24 +47,24 @@ export default { } return this.$store.getters.postalCodesFlatsBuckets( - flat => flat.status != "duplicate" && flat.status != "ignored" + flat => flat.status !== 'duplicate' && flat.status !== 'ignored' ) }, loading () { - return this.$store.getters.isLoading; + return this.$store.getters.isLoading } }, methods: { - onSearch(event) { + onSearch (event) { event.preventDefault() - let query = this.$refs.searchInput.value + const query = this.$refs.searchInput.value this.$router.replace({ name: 'search', query: { query: query }}) }, - doSearch() { - let query = this.$route.query.query + doSearch () { + const query = this.$route.query.query if (query) { this.$store.dispatch('doSearch', { query: query }) diff --git a/flatisfy/web/routes/api.py b/flatisfy/web/routes/api.py index 7def31b..6099188 100644 --- a/flatisfy/web/routes/api.py +++ b/flatisfy/web/routes/api.py @@ -147,6 +147,33 @@ def update_flat_status_v1(flat_id, db): } +def update_flat_notes_v1(flat_id, db): + """ + API v1 route to update flat notes: + + POST /api/v1/flat/:flat_id/notes + Data: { + "notes": "NEW_NOTES" + } + + :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.notes = json.load(bottle.request.body)["notes"] + except (ValueError, KeyError): + return bottle.HTTPError(400, "Invalid notes 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. @@ -165,7 +192,7 @@ def time_to_places_v1(config): } -def search_v1(config, db): +def search_v1(config): """ API v1 route to perform a fulltext search on flats.