Merge pull request #276 from DanielGrams/issue/275

Move reverse geocoding to backend #275
This commit is contained in:
Daniel Grams 2021-08-20 15:53:01 +02:00 committed by GitHub
commit 9e170be116
15 changed files with 159 additions and 93 deletions

View File

@ -19,7 +19,7 @@ class FindEventDateForm(FlaskForm):
lazy_gettext("Category"), validators=[Optional()], coerce=int
)
coordinate = HiddenField(validators=[Optional()])
location = StringField(lazy_gettext("Location"), validators=[Optional()])
location = SelectField(lazy_gettext("Location"), validators=[Optional()])
distance = SelectField(
lazy_gettext("Distance"),
validators=[Optional()],

View File

@ -1,6 +1,6 @@
from flask_babelex import lazy_gettext
from flask_wtf import FlaskForm
from wtforms import HiddenField, SelectField, StringField, SubmitField
from wtforms import HiddenField, SelectField, SubmitField
from wtforms.validators import Optional
from project.forms.common import distance_choices, weekday_choices
@ -17,7 +17,7 @@ class PlaningForm(FlaskForm):
lazy_gettext("Category"), validators=[Optional()], coerce=int
)
coordinate = HiddenField(validators=[Optional()])
location = StringField(lazy_gettext("Location"), validators=[Optional()])
location = SelectField(lazy_gettext("Location"), validators=[Optional()])
distance = SelectField(
lazy_gettext("Distance"),
validators=[Optional()],

View File

@ -307,9 +307,7 @@ function handle_request_success(
$(error_id).hide();
}
function reset_place_form(prefix = "") {
$("#" + prefix + "name").val("");
$("#" + prefix + "url").val("");
function reset_location_form(prefix = "") {
$("#" + prefix + "location-street").val("");
$("#" + prefix + "location-postalCode").val("");
$("#" + prefix + "location-city").val("");
@ -318,11 +316,15 @@ function reset_place_form(prefix = "") {
$("#" + prefix + "location-longitude").val("");
}
function reset_place_form(prefix = "") {
$("#" + prefix + "name").val("");
$("#" + prefix + "url").val("");
reset_location_form(prefix);
}
function reset_organizer_form(prefix = "") {
$("#" + prefix + "name").val("");
$("#" + prefix + "location-street").val("");
$("#" + prefix + "location-postalCode").val("");
$("#" + prefix + "location-city").val("");
reset_location_form(prefix);
}
function fill_place_form_with_gmaps_place(
@ -425,7 +427,7 @@ $(function () {
$("#clear_location_btn").click(function () {
$("#coordinate").val("");
$("#location").val("");
$("#location").val("").trigger('change');
});
$(".btn-print").click(function () {

View File

@ -722,83 +722,111 @@
{% endmacro %}
{% macro render_google_place_autocomplete_header(location_only = False, prefix = '') %}
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key={{ "dev" | env_override('GOOGLE_MAPS_API_KEY') }}&libraries=places"></script>
{% macro render_google_place_autocomplete_header() %}
<script>
function initialize() {
var input = document.getElementById('location_search');
var autocomplete = new google.maps.places.Autocomplete(input);
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
$( function() {
var street_number = "";
var route = "";
var city = "";
$('#location_search').select2({
theme: 'bootstrap4',
ajax: {
url: "{{ url_for('js_autocomplete_place') }}",
dataType: 'json',
delay: 250,
cache: true,
data: function (params) {
return {
keyword: params.term
};
}
},
placeholder: $('#location_search').attr("placeholder"),
minimumInputLength: 1,
templateResult: select2TemplateResult
}).on('select2:close', function (e) {
var data = select2GetData(e);
for (var i = 0; i < place.address_components.length; i++) {
var component = place.address_components[i]
var addressType = component.types[0];
var val = component.long_name
if ("gmaps_id" in data) {
$(this).val(null).trigger('change');
var location_only = $(this).attr("data-location-only");
if (location_only) {
reset_location_form();
} else {
reset_place_form();
$('#place-name').val(data.main_text);
}
if (addressType == 'street_number') {
street_number = val;
} else if (addressType == 'route') {
route = val;
} else if (addressType == 'locality') {
city = val;
} else if (addressType == 'administrative_area_level_1') {
$('#{{ prefix }}location-state').val(val);
} else if (addressType == 'postal_code') {
$('#{{ prefix }}location-postalCode').val(val);
}
}
get_gmaps_place_details(data.gmaps_id, location_only);
}
});
{% if not location_only %}
$('#{{ prefix }}name').val(place.name);
if (place.website) {
$('#{{ prefix }}url').val(place.website);
}
{% endif %}
$('#{{ prefix }}location-street').val([route, street_number].join(' '));
$('#{{ prefix }}location-city').val(city);
$('#{{ prefix }}location-latitude').val(place.geometry.location.lat());
$('#{{ prefix }}location-longitude').val(place.geometry.location.lng());
$('#location_search').val('');
$('#location_search').trigger('place_changed');
});
function get_gmaps_place_details(place_id, location_only) {
$.ajax({
url: "{{ url_for('js_autocomplete_gmaps_place') }}",
type: "get",
dataType: "json",
data: "gmaps_id=" + place_id,
success: function (data) {
fill_place_form_with_gmaps_place(data, "", location_only);
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
});
</script>
{% endmacro %}
{% macro render_google_place_autocomplete_field() %}
{% macro render_google_place_autocomplete_field(location_only = False) %}
<div class="form-group row">
<div class="col-sm-12">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><span><i class="fab fa-google"></i></span></span>
</div>
<input id="location_search" type="search" class="form-control" placeholder="{{ _('Search location on Google') }}" runat="server" />
<select id="location_search" class="form-control" placeholder="{{ _('Search location on Google') }}" {% if location_only %}data-location-only="1"{% endif %}></select>
</div>
</div>
</div>
{% endmacro %}
{% macro render_google_filter_autocomplete_header() %}
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key={{ "dev" | env_override('GOOGLE_MAPS_API_KEY') }}&libraries=places"></script>
<script>
function initialize() {
var input = document.getElementById('location');
var autocomplete = new google.maps.places.Autocomplete(input);
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
$('#coordinate').val('' + place.geometry.location.lat() + ',' + place.geometry.location.lng());
});
$( function() {
$('#location').select2({
theme: 'bootstrap4',
ajax: {
url: "{{ url_for('js_autocomplete_place') }}",
dataType: 'json',
delay: 250,
cache: true,
data: function (params) {
return {
keyword: params.term
};
}
},
placeholder: $('#location').attr("placeholder"),
minimumInputLength: 1,
templateResult: select2TemplateResult
}).on('select2:close', function (e) {
var data = select2GetData(e);
if ("gmaps_id" in data) {
get_gmaps_place_details(data.gmaps_id);
}
});
function get_gmaps_place_details(place_id) {
$.ajax({
url: "{{ url_for('js_autocomplete_gmaps_place') }}",
type: "get",
dataType: "json",
data: "gmaps_id=" + place_id,
success: function (data) {
$('#coordinate').val('' + data.geometry.location.lat + ',' + data.geometry.location.lng);
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
});
</script>
{% endmacro %}

View File

@ -3,7 +3,7 @@
{% block header %}
{{ render_jquery_steps_header() }}
{{ render_google_place_autocomplete_header(True) }}
{{ render_google_place_autocomplete_header() }}
{{ render_cropper_logo_block() }}
{{ render_admin_unit_form_script() }}
{% endblock %}
@ -30,7 +30,7 @@
</div>
<div class="card-body">
{{ render_google_place_autocomplete_field() }}
{{ render_google_place_autocomplete_field(True) }}
{{ form.location.hidden_tag() }}
{{ render_field_with_errors(form.location.street) }}

View File

@ -4,7 +4,7 @@
{% block header %}
{{ render_jquery_steps_header() }}
{{ render_google_place_autocomplete_header(True) }}
{{ render_google_place_autocomplete_header() }}
{{ render_cropper_logo_block() }}
{{ render_admin_unit_form_script(admin_unit.id) }}
{% endblock %}
@ -31,7 +31,7 @@
</div>
<div class="card-body">
{{ render_google_place_autocomplete_field() }}
{{ render_google_place_autocomplete_field(True) }}
{{ form.location.hidden_tag() }}
{{ render_field_with_errors(form.location.street) }}

View File

@ -1,5 +1,5 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_cropper_header, render_end_container_handling, render_jquery_steps_header, render_google_place_autocomplete_field, render_google_place_autocomplete_header, render_cropper_header, render_cropper_code, render_crop_image_form_section, render_radio_buttons, render_field_with_errors, render_field %}
{% from "_macros.html" import render_cropper_header, render_end_container_handling, render_jquery_steps_header, render_cropper_header, render_cropper_code, render_crop_image_form_section, render_radio_buttons, render_field_with_errors, render_field %}
{%- block title -%}
{{ _('Create event') }}
{%- endblock -%}

View File

@ -1,9 +1,10 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_google_filter_autocomplete_header, render_event_dates_filter_form, render_pagination, render_place, render_events_sub_menu %}
{% from "_macros.html" import render_jquery_steps_header, render_google_filter_autocomplete_header, render_event_dates_filter_form, render_pagination, render_place, render_events_sub_menu %}
{%- block title -%}
{{ _('Event Dates') }}
{%- endblock -%}
{% block header %}
{{ render_jquery_steps_header() }}
{{ render_google_filter_autocomplete_header() }}
<script>

View File

@ -1,7 +1,8 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_cropper_block, render_crop_image_form_section, render_google_place_autocomplete_header, render_google_place_autocomplete_field, render_field_with_errors, render_field %}
{% from "_macros.html" import render_jquery_steps_header, render_cropper_block, render_crop_image_form_section, render_google_place_autocomplete_header, render_google_place_autocomplete_field, render_field_with_errors, render_field %}
{% block header %}
{{ render_jquery_steps_header() }}
{{ render_google_place_autocomplete_header() }}
{{ render_cropper_block() }}
{% endblock %}

View File

@ -1,7 +1,8 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_cropper_block, render_crop_image_form_section, render_google_place_autocomplete_header, render_google_place_autocomplete_field, render_field_with_errors, render_field %}
{% from "_macros.html" import render_jquery_steps_header, render_cropper_block, render_crop_image_form_section, render_google_place_autocomplete_header, render_google_place_autocomplete_field, render_field_with_errors, render_field %}
{% block header %}
{{ render_jquery_steps_header() }}
{{ render_google_place_autocomplete_header() }}
{{ render_cropper_block() }}
{% endblock %}

View File

@ -1,8 +1,9 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_cropper_logo_block, render_crop_image_form_section, render_google_place_autocomplete_header, render_google_place_autocomplete_field, render_field_with_errors, render_field %}
{% from "_macros.html" import render_jquery_steps_header, render_cropper_logo_block, render_crop_image_form_section, render_google_place_autocomplete_header, render_google_place_autocomplete_field, render_field_with_errors, render_field %}
{% block header %}
{{ render_google_place_autocomplete_header(True) }}
{{ render_jquery_steps_header() }}
{{ render_google_place_autocomplete_header() }}
{{ render_cropper_logo_block() }}
{% endblock %}
@ -27,7 +28,7 @@
</div>
<div class="card-body">
{{ render_google_place_autocomplete_field() }}
{{ render_google_place_autocomplete_field(True) }}
{{ form.location.hidden_tag() }}
{{ render_field_with_errors(form.location.street) }}

View File

@ -1,8 +1,9 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_cropper_logo_block, render_crop_image_form_section, render_google_place_autocomplete_header, render_google_place_autocomplete_field, render_field_with_errors, render_field %}
{% from "_macros.html" import render_jquery_steps_header, render_cropper_logo_block, render_crop_image_form_section, render_google_place_autocomplete_header, render_google_place_autocomplete_field, render_field_with_errors, render_field %}
{% block header %}
{{ render_google_place_autocomplete_header(True) }}
{{ render_jquery_steps_header() }}
{{ render_google_place_autocomplete_header() }}
{{ render_cropper_logo_block() }}
{% endblock %}
@ -27,7 +28,7 @@
</div>
<div class="card-body">
{{ render_google_place_autocomplete_field() }}
{{ render_google_place_autocomplete_field(True) }}
{{ form.location.hidden_tag() }}
{{ render_field_with_errors(form.location.street) }}

View File

@ -1,10 +1,11 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_field, render_field_with_errors, render_event_dates_date_field, render_event_dates_location_field, render_google_filter_autocomplete_header, render_pagination, render_place, render_events_sub_menu %}
{% from "_macros.html" import render_jquery_steps_header, render_field, render_field_with_errors, render_event_dates_date_field, render_event_dates_location_field, render_google_filter_autocomplete_header, render_pagination, render_place, render_events_sub_menu %}
{%- block title -%}
{{ _('Event Planing') }}
{%- endblock -%}
{% block content_container_attribs %}{% endblock %}
{% block header %}
{{ render_jquery_steps_header() }}
{{ render_google_filter_autocomplete_header() }}
{% set max_events = 50 %}
<script>

View File

@ -45,18 +45,33 @@ def js_check_register_email():
def js_autocomplete_place():
csrf.protect()
admin_unit_id = int(request.args["admin_unit_id"])
admin_unit_id = int(request.args.get("admin_unit_id", "0"))
keyword = request.args.get("keyword")
exclude_gmaps = request.args.get("exclude_gmaps")
places_result = list()
google_places_result = list()
places = get_event_places(admin_unit_id, keyword, 5)
places_result = [{"id": p.id, "text": get_place_str(p)} for p in places]
if admin_unit_id > 0:
places = get_event_places(admin_unit_id, keyword, 5)
places_result = [{"id": p.id, "text": get_place_str(p)} for p in places]
if not exclude_gmaps:
google_places = find_gmaps_places(keyword) if keyword else list()
google_places_result = [
{
"id": p["place_id"],
"gmaps_id": p["place_id"],
"text": p["description"],
"main_text": p["structured_formatting"]["main_text"],
}
for p in google_places
]
if exclude_gmaps:
results = places_result
elif admin_unit_id <= 0:
results = google_places_result
else:
google_places = find_gmaps_places(keyword) if keyword else list()
results = list()
if len(places) > 0:
@ -71,15 +86,7 @@ def js_autocomplete_place():
results.append(
{
"text": gettext("Places of Google Maps"),
"children": [
{
"id": p["place_id"],
"gmaps_id": p["place_id"],
"text": p["description"],
"main_text": p["structured_formatting"]["main_text"],
}
for p in google_places
],
"children": google_places_result,
}
)

View File

@ -103,6 +103,29 @@ def test_js_autocomplete_place(client, seeder: Seeder, utils: UtilActions):
assert response.json["results"][1]["children"][0]["text"] == "Beschreibung"
def test_js_autocomplete_place_gmaps_only(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_base()
seeder.upsert_default_event_place(admin_unit_id)
url = utils.get_url("event_dates")
response = utils.get(url)
utils.gmaps_places_autocomplete_query.return_value = [
{
"place_id": "123",
"description": "Beschreibung",
"structured_formatting": {"main_text": "Haupttext"},
}
]
with client:
url = utils.get_url("js_autocomplete_place", keyword="crew")
response = utils.get(url)
utils.assert_response_ok(response)
assert response.json["results"][0]["text"] == "Beschreibung"
def test_js_autocomplete_place_exclude_gmaps(
client, seeder: Seeder, utils: UtilActions
):