cartodb/app/controllers/carto/api/geocodings_controller.rb
2020-06-15 10:58:47 +08:00

106 lines
4.0 KiB
Ruby

module Carto
module Api
class GeocodingsController < ::Api::ApplicationController
GEOCODING_SQLAPI_CALLS_TIMEOUT = 45
ssl_required :index, :show, :available_geometries, :estimation_for
def index
# data_import_id is set for content guessing geocodes
# TODO: agree on a more flexible API with params
geocodings = Carto::Geocoding.where(
"user_id = ? AND (state NOT IN (?)) AND (data_import_id IS NULL)",
current_user.id, ['failed', 'finished', 'cancelled']
).all
render json: { geocodings: geocodings }, root: false
end
def show
geocoding = Carto::Geocoding.where(user_id: current_user.id, id: params[:id]).first
raise RecordNotFound unless geocoding
render json: geocoding.public_values
end
def available_geometries
case params[:kind]
when 'admin1'
return render(json: ['polygon'])
when 'namedplace'
return render(json: ['point'])
when 'postalcode'
return available_geometries_for_postalcode
else
return head(400)
end
end
def estimation_for
table = get_table(params[:table_name])
render_jsonp( { description: "table #{params[:table_name]} doesn't exist" }, 500) and return unless table
force_all_rows = (params[:force_all_rows].to_s == 'true')
total_rows = Carto::Geocoding.processable_rows(table, force_all_rows)
remaining_quota = uri_user.remaining_geocoding_quota
remaining_quota = (remaining_quota > 0 ? remaining_quota : 0)
used_credits = total_rows - remaining_quota
used_credits = (used_credits > 0 ? used_credits : 0)
render json: {
rows: total_rows,
estimation: (uri_user.geocoding_block_price.to_i * used_credits) / Carto::User::GEOCODING_BLOCK_SIZE.to_f
}
rescue => e
CartoDB.notify_exception(e, params: params)
render_jsonp( { description: e.message }, 500)
end
private
# TODO: this should be moved upwards in the controller hierarchy, and make it a replacement for current_user
def uri_user
@uri_user ||= Carto::User.where(id: current_user.id).first
end
def available_geometries_for_postalcode
return head(400) unless params[:free_text] || (params[:column_name] && params[:table_name])
input = params[:free_text].present? ? clean_free_text_input([params[:free_text]]) : select_distinct_from_table_and_column(params[:table_name], params[:column_name])
return head(400) if input.nil? && params[:table_name].present?
render(json: []) and return if input.nil? || input.empty?
list = input.map { |v| "'#{v.to_s.gsub("'", "''")}'" }.join(",")
internal_geocoder_api_config = CartoDB::GeocoderConfig.instance.get['internal']
.symbolize_keys
.merge(timeout: GEOCODING_SQLAPI_CALLS_TIMEOUT)
services = CartoDB::SQLApi.new(internal_geocoder_api_config)
.fetch("SELECT (admin0_available_services(Array[#{list}])).*")
geometries = []
points = services.select { |s| s['postal_code_points'] }.size
polygons = services.select { |s| s['postal_code_polygons'] }.size
geometries.append 'point' if points > 0 && points >= polygons
geometries.append 'polygon' if polygons > 0 && polygons >= points
render(json: geometries)
rescue => e
CartoDB.notify_exception(e, params: params)
end
def clean_free_text_input(input)
input.map{ |v| v.to_s.squish.downcase.gsub(/[^\p{Alnum}]/, '') }.reject(&:blank?)
end
def select_distinct_from_table_and_column(table_name, column_name)
table = get_table(table_name)
return nil if table.nil?
input = table.sequel.distinct.select_map(params[:column_name].to_sym)
end
def get_table(table_name)
Carto::Helpers::TableLocator.new.get_by_id_or_name(table_name, uri_user).try(&:service)
end
end
end
end