Compare commits
46 Commits
0.5.2-serv
...
0.6.2-serv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d852bd27a | ||
|
|
1e30b21d35 | ||
|
|
5008470ac2 | ||
|
|
15a7ed5a09 | ||
|
|
ad5d25f0a0 | ||
|
|
8cd94973ff | ||
|
|
8955b0cd9b | ||
|
|
6d88580fdf | ||
|
|
2ab7c78b35 | ||
|
|
c9ddc37051 | ||
|
|
c8ea635385 | ||
|
|
923774ffe4 | ||
|
|
cb921614c3 | ||
|
|
ccd5daa63e | ||
|
|
b4c1991c90 | ||
|
|
d5c896274c | ||
|
|
a0377150f3 | ||
|
|
79e69c6020 | ||
|
|
c04e8fa586 | ||
|
|
9c47bc45e7 | ||
|
|
a8dd211acf | ||
|
|
bfb8df54f4 | ||
|
|
5c23a43efa | ||
|
|
ac49dc1fda | ||
|
|
3af2a8f8f0 | ||
|
|
752d8317a2 | ||
|
|
8c5a4d32de | ||
|
|
0ba513d13d | ||
|
|
f29f0769d7 | ||
|
|
d625b26e57 | ||
|
|
62da0eff21 | ||
|
|
5f74d012a6 | ||
|
|
42b1d6f630 | ||
|
|
7de498cc95 | ||
|
|
1505e0e6ea | ||
|
|
2668a1dfdc | ||
|
|
03ce547b8e | ||
|
|
020f428ceb | ||
|
|
98a7762da6 | ||
|
|
0deb834531 | ||
|
|
67e1ddd6a4 | ||
|
|
1d5545b239 | ||
|
|
89ec3dcfa6 | ||
|
|
0117830ffd | ||
|
|
0ca1b84096 | ||
|
|
f92191a69f |
31
README.md
31
README.md
@@ -23,11 +23,11 @@ Steps to deploy a new Data Services API version :
|
||||
|
||||
### Local install instructions
|
||||
|
||||
- install data services extension
|
||||
- install data services geocoder extension
|
||||
|
||||
```
|
||||
git clone git@github.com:CartoDB/data-services.git
|
||||
data-services/geocoder/extension
|
||||
cd data-services/geocoder/extension
|
||||
sudo make install
|
||||
```
|
||||
|
||||
@@ -35,7 +35,7 @@ Steps to deploy a new Data Services API version :
|
||||
|
||||
```
|
||||
cd client && sudo make install
|
||||
cd server && sudo make install
|
||||
cd server/extension && sudo make install
|
||||
```
|
||||
|
||||
- install python library
|
||||
@@ -57,21 +57,28 @@ Steps to deploy a new Data Services API version :
|
||||
|
||||
|
||||
```
|
||||
# select CDB_Conf_SetConf('redis_metadata_config', '{"sentinel_host": "localhost", "sentinel_port": 26379, "sentinel_master_id": "mymaster", "timeout": 0.1, "redis_db": 5}');
|
||||
# select CDB_Conf_SetConf('redis_metrics_config', '{"sentinel_host": "localhost", "sentinel_port": 26379, "sentinel_master_id": "mymaster", "timeout": 0.1, "redis_db": 5}');
|
||||
|
||||
# select CDB_Conf_SetConf('heremaps_conf', '{"app_id": "APP_ID", "app_code": "APP_CODE"}');
|
||||
# select CDB_Conf_SetConf('user_config', '{"is_organization": false, "entity_name": "<YOUR_USERNAME>"}')
|
||||
# If sentinel is used:
|
||||
SELECT CDB_Conf_SetConf('redis_metadata_config', '{"sentinel_host": "localhost", "sentinel_port": 26379, "sentinel_master_id": "mymaster", "timeout": 0.1, "redis_db": 5}');
|
||||
SELECT CDB_Conf_SetConf('redis_metrics_config', '{"sentinel_host": "localhost", "sentinel_port": 26379, "sentinel_master_id": "", "timeout": 0.1, "redis_db": 5}');
|
||||
|
||||
# If sentinel is not used
|
||||
SELECT CDB_Conf_SetConf('redis_metadata_config', '{"redis_host": "localhost", "redis_port": 26379, "sentinel_master_id": "", "timeout": 0.1, "redis_db": 5}');
|
||||
SELECT CDB_Conf_SetConf('redis_metrics_config', '{"redis_host": "localhost", "redis_port": 6379, "sentinel_master_id": "", "timeout": 0.1, "redis_db": 5}');
|
||||
|
||||
SELECT CDB_Conf_SetConf('heremaps_conf', '{"app_id": "APP_ID", "app_code": "APP_CODE", "geocoder_cost_per_hit": "COST_PER_HIT"}');
|
||||
SELECT CDB_Conf_SetConf('user_config', '{"is_organization": false, "entity_name": "<YOUR_USERNAME>"}')
|
||||
SELECT CDB_Conf_SetConf('mapzen_conf', '{"routing_app_key": "ROUTING_API_KEY", "geocoder_app_key": "GEOCODER_API_KEY"}');
|
||||
SELECT CDB_Conf_SetConf('logger_con', '{"geocoder_log_path": "/tmp/geocodings.log"}')
|
||||
```
|
||||
|
||||
- configure plproxy to point to the same user database (you could do in a different one)
|
||||
- configure plproxy to point to the a database (you can use a specific database for the server or your same user)
|
||||
|
||||
```
|
||||
select CDB_Conf_SetConf('geocoder_server_config', '{ "connection_str": "host=localhost port=5432 dbname=cartodb_dev_user_accf0647-d942-4e37-b129-8287c117e687_db user=postgres"}');
|
||||
SELECT CDB_Conf_SetConf('geocoder_server_config', '{ "connection_str": "host=localhost port=5432 dbname=<SERVER_DB_NAME> user=postgres"}');
|
||||
```
|
||||
|
||||
- configure the search path in order to be able to execute the functions without use the schema:
|
||||
- configure the search path in order to be able to execute the functions without using the schema:
|
||||
|
||||
```
|
||||
alter role "<USER_ROLE>" set search_path='"$user", public, cartodb, cdb_dataservices_client';
|
||||
ALTER ROLE "<USER_ROLE>" SET search_path="$user", public, cartodb, cdb_dataservices_client;
|
||||
```
|
||||
|
||||
@@ -9,6 +9,7 @@ The isoline functions provide a way to generate isolines in terms of distance an
|
||||
## Documentation
|
||||
|
||||
* [Quickstart](quickstart.md)
|
||||
* [General concepts](general_concepts.md)
|
||||
* [Geocoding functions](geocoding_functions.md)
|
||||
* [Isoline functions](isoline_functions.md)
|
||||
* [General Concepts](general_concepts.md)
|
||||
* [Geocoding Functions](geocoding_functions.md)
|
||||
* [Isoline Functions](isoline_functions.md)
|
||||
* [Quota Information](quota_information.md)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# General concepts
|
||||
# General Concepts
|
||||
|
||||
The Data Services API offers geocoding and isoline services on top of the CartoDB SQL API by means of a set of functions. Each one of these functions is oriented to one kind of operation and returns the corresponding geometry (a `polygon` or a `point`), according to the input information.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Geocoding functions
|
||||
# Geocoding Functions
|
||||
|
||||
The following geocoding functions are available, grouped by categories.
|
||||
|
||||
@@ -36,7 +36,7 @@ UPDATE {tablename} SET the_geom = cdb_geocode_admin0_polygon({country_column})
|
||||
|
||||
## Level-1 Administrative regions geocoder
|
||||
|
||||
The following functions provide a geocoding service for administrative regions of level 1 (or NUTS-1) such as states for the United States, regions in France or autonomous communities in Spain.
|
||||
The following functions provide a geocoding service for administrative regions of level 1 (or NUTS-1) such as states for the United States, *départements* in France or autonomous communities in Spain.
|
||||
|
||||
### cdb_geocode_admin1_polygon(_admin1_name text_)
|
||||
|
||||
@@ -94,7 +94,7 @@ UPDATE {tablename} SET the_geom = cdb_geocode_admin1_polygon({province_column},
|
||||
|
||||
## City geocoder
|
||||
|
||||
The following functions provide a city geocoder service. It is recommended to use the more specific geocoding function -- the one that requires more parameters — in order for the result to be as accurate as possible when several cities share their name.
|
||||
The following functions provide a city geocoder service. It is recommended to use the more specific geocoding function -- the one that requires more parameters — in order for the result to be as accurate as possible when several cities share their name. If there are duplicate results for a city name, the city name with the highest population will be returned.
|
||||
|
||||
### cdb_geocode_namedplace_point(_city_name text_)
|
||||
|
||||
@@ -181,6 +181,8 @@ UPDATE {tablename} SET the_geom = cdb_geocode_namedplace_point({city_column}, {p
|
||||
|
||||
The following functions provide a postal code geocoding service that can be used to obtain points or polygon results. The postal code polygon geocoder covers the United States, France, Australia and Canada; a request for a different country will return an empty response.
|
||||
|
||||
**Note:** For the USA, US Census [Zip Code Tabulation Areas](https://www.census.gov/geo/reference/zctas.html) (ZCTA) are used to reference geocodes for USPS postal codes service areas. See the [FAQs](http://docs.cartodb.com/faqs/datasets-and-data/#why-does-cartodb-use-census-bureau-zctas-and-not-usps-zip-codes-for-postal-codes) about datasets and data for details.
|
||||
|
||||
### cdb_geocode_postalcode_polygon(_postal_code text, country_name text_)
|
||||
|
||||
#### Arguments
|
||||
@@ -208,8 +210,6 @@ SELECT cdb_geocode_postalcode_polygon('11211', 'USA')
|
||||
UPDATE {tablename} SET the_geom = cdb_geocode_postalcode_polygon({postal_code_column}, 'USA')
|
||||
```
|
||||
|
||||
**Note:** For the USA, US Census ZCTAs are considered.
|
||||
|
||||
### cdb_geocode_postalcode_point(_code text, country_name text_)
|
||||
|
||||
#### Arguments
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Isoline functions
|
||||
# Isoline Functions
|
||||
|
||||
The following functions provide an isolines generator service based on time or distance. This service uses the isolines service defined for the user (currently, only the Here isolines service is available).
|
||||
The following functions provide an isolines generator service based on time or distance. This service uses the isolines service defined for the user (currently, only the [HERE](https://developer.here.com/coverage-info) isolines service is available).
|
||||
|
||||
**This service is subject to quota limitations, and extra fees may apply**. Please view our [terms and conditions](https://cartodb.com/terms/) and check out the [Quota information section](http://docs.cartodb.com/cartodb-platform/dataservices-api/quota-information/) for details and recommendations related with quota usage.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Quota information
|
||||
# Quota Information
|
||||
|
||||
**This Data Services API provides functions which are subject to quota limitations, and extra fees may apply**. Please check our [terms and conditions](https://cartodb.com/terms/).
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ OLD_VERSIONS = $(wildcard old_versions/*.sql)
|
||||
# @see http://www.postgresql.org/docs/current/static/extend-pgxs.html
|
||||
DATA = $(NEW_EXTENSION_ARTIFACT) \
|
||||
$(OLD_VERSIONS) \
|
||||
cdb_dataservices_server--0.5.2--0.5.1.sql \
|
||||
cdb_dataservices_server--0.5.1--0.5.2.sql
|
||||
cdb_dataservices_server--0.6.2--0.6.1.sql \
|
||||
cdb_dataservices_server--0.6.1--0.6.2.sql
|
||||
|
||||
REGRESS = $(notdir $(basename $(wildcard test/sql/*test.sql)))
|
||||
TEST_DIR = test/
|
||||
|
||||
102
server/extension/cdb_dataservices_server--0.6.1--0.6.2.sql
Normal file
102
server/extension/cdb_dataservices_server--0.6.1--0.6.2.sql
Normal file
@@ -0,0 +1,102 @@
|
||||
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
|
||||
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.6.2'" to load this file. \quit
|
||||
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL)
|
||||
RETURNS Geometry AS $$
|
||||
from cartodb_services.mapzen import MapzenGeocoder
|
||||
from cartodb_services.mapzen.types import country_to_iso3
|
||||
from cartodb_services.metrics import QuotaService
|
||||
|
||||
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
|
||||
user_geocoder_config = GD["user_geocoder_config_{0}".format(username)]
|
||||
quota_service = QuotaService(user_geocoder_config, redis_conn)
|
||||
if not quota_service.check_user_quota():
|
||||
plpy.error('You have reach the limit of your quota')
|
||||
|
||||
try:
|
||||
geocoder = MapzenGeocoder(user_geocoder_config.mapzen_api_key)
|
||||
country_iso3 = None
|
||||
if country:
|
||||
country_iso3 = country_to_iso3(country)
|
||||
coordinates = geocoder.geocode(searchtext=searchtext, city=city,
|
||||
state_province=state_province,
|
||||
country=country_iso3)
|
||||
if coordinates:
|
||||
quota_service.increment_success_service_use()
|
||||
plan = plpy.prepare("SELECT ST_SetSRID(ST_MakePoint($1, $2), 4326); ", ["double precision", "double precision"])
|
||||
point = plpy.execute(plan, [coordinates[0], coordinates[1]], 1)[0]
|
||||
return point['st_setsrid']
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return None
|
||||
except BaseException as e:
|
||||
import sys, traceback
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
quota_service.increment_failed_service_use()
|
||||
error_msg = 'There was an error trying to geocode using mapzen geocoder: {0}'.format(e)
|
||||
plpy.notice(traceback.format_tb(traceback_))
|
||||
plpy.error(error_msg)
|
||||
finally:
|
||||
quota_service.increment_total_service_use()
|
||||
$$ LANGUAGE plpythonu;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_route_point_to_point(
|
||||
username TEXT,
|
||||
orgname TEXT,
|
||||
origin geometry(Point, 4326),
|
||||
destination geometry(Point, 4326),
|
||||
mode TEXT,
|
||||
options text[] DEFAULT ARRAY[]::text[],
|
||||
units text DEFAULT 'kilometers')
|
||||
RETURNS cdb_dataservices_server.simple_route AS $$
|
||||
import json
|
||||
from cartodb_services.mapzen import MapzenRouting, MapzenRoutingResponse
|
||||
from cartodb_services.mapzen.types import polyline_to_linestring
|
||||
from cartodb_services.metrics import QuotaService
|
||||
from cartodb_services.tools import Coordinate
|
||||
|
||||
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
|
||||
user_routing_config = GD["user_routing_config_{0}".format(username)]
|
||||
|
||||
quota_service = QuotaService(user_routing_config, redis_conn)
|
||||
if not quota_service.check_user_quota():
|
||||
plpy.error('You have reach the limit of your quota')
|
||||
|
||||
try:
|
||||
client = MapzenRouting(user_routing_config.mapzen_api_key)
|
||||
|
||||
if not origin or not destination:
|
||||
plpy.notice("Empty origin or destination")
|
||||
quota_service.increment_empty_service_use()
|
||||
return [None, None, None]
|
||||
|
||||
orig_lat = plpy.execute("SELECT ST_Y('%s') AS lat" % origin)[0]['lat']
|
||||
orig_lon = plpy.execute("SELECT ST_X('%s') AS lon" % origin)[0]['lon']
|
||||
origin_coordinates = Coordinate(orig_lon, orig_lat)
|
||||
dest_lat = plpy.execute("SELECT ST_Y('%s') AS lat" % destination)[0]['lat']
|
||||
dest_lon = plpy.execute("SELECT ST_X('%s') AS lon" % destination)[0]['lon']
|
||||
dest_coordinates = Coordinate(dest_lon, dest_lat)
|
||||
|
||||
resp = client.calculate_route_point_to_point(origin_coordinates, dest_coordinates, mode, options, units)
|
||||
|
||||
if resp and resp.shape:
|
||||
shape_linestring = polyline_to_linestring(resp.shape)
|
||||
if shape_linestring:
|
||||
quota_service.increment_success_service_use()
|
||||
return [shape_linestring, resp.length, resp.duration]
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return [None, None, None]
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return [None, None, None]
|
||||
except BaseException as e:
|
||||
import sys, traceback
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
quota_service.increment_failed_service_use()
|
||||
error_msg = 'There was an error trying to obtain route using mapzen provider: {0}'.format(e)
|
||||
plpy.notice(traceback.format_tb(traceback_))
|
||||
plpy.error(error_msg)
|
||||
finally:
|
||||
quota_service.increment_total_service_use()
|
||||
$$ LANGUAGE plpythonu SECURITY DEFINER;
|
||||
98
server/extension/cdb_dataservices_server--0.6.2--0.6.1.sql
Normal file
98
server/extension/cdb_dataservices_server--0.6.2--0.6.1.sql
Normal file
@@ -0,0 +1,98 @@
|
||||
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
|
||||
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.6.1'" to load this file. \quit
|
||||
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL)
|
||||
RETURNS Geometry AS $$
|
||||
from cartodb_services.mapzen import MapzenGeocoder
|
||||
from cartodb_services.mapzen.types import country_to_iso3
|
||||
from cartodb_services.metrics import QuotaService
|
||||
|
||||
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
|
||||
user_geocoder_config = GD["user_geocoder_config_{0}".format(username)]
|
||||
quota_service = QuotaService(user_geocoder_config, redis_conn)
|
||||
|
||||
try:
|
||||
geocoder = MapzenGeocoder(user_geocoder_config.mapzen_app_key)
|
||||
country_iso3 = None
|
||||
if country:
|
||||
country_iso3 = country_to_iso3(country)
|
||||
coordinates = geocoder.geocode(searchtext=searchtext, city=city,
|
||||
state_province=state_province,
|
||||
country=country_iso3)
|
||||
if coordinates:
|
||||
quota_service.increment_success_service_use()
|
||||
plan = plpy.prepare("SELECT ST_SetSRID(ST_MakePoint($1, $2), 4326); ", ["double precision", "double precision"])
|
||||
point = plpy.execute(plan, [coordinates[0], coordinates[1]], 1)[0]
|
||||
return point['st_setsrid']
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return None
|
||||
except BaseException as e:
|
||||
import sys, traceback
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
quota_service.increment_failed_service_use()
|
||||
error_msg = 'There was an error trying to geocode using mapzen geocoder: {0}'.format(e)
|
||||
plpy.notice(traceback.format_tb(traceback_))
|
||||
plpy.error(error_msg)
|
||||
finally:
|
||||
quota_service.increment_total_service_use()
|
||||
$$ LANGUAGE plpythonu;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_route_point_to_point(
|
||||
username TEXT,
|
||||
orgname TEXT,
|
||||
origin geometry(Point, 4326),
|
||||
destination geometry(Point, 4326),
|
||||
mode TEXT,
|
||||
options text[] DEFAULT ARRAY[]::text[],
|
||||
units text DEFAULT 'kilometers')
|
||||
RETURNS cdb_dataservices_server.simple_route AS $$
|
||||
import json
|
||||
from cartodb_services.mapzen import MapzenRouting, MapzenRoutingResponse
|
||||
from cartodb_services.mapzen.types import polyline_to_linestring
|
||||
from cartodb_services.metrics import QuotaService
|
||||
from cartodb_services.tools import Coordinate
|
||||
|
||||
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
|
||||
user_routing_config = GD["user_routing_config_{0}".format(username)]
|
||||
|
||||
quota_service = QuotaService(user_routing_config, redis_conn)
|
||||
|
||||
try:
|
||||
client = MapzenRouting(user_routing_config.mapzen_app_key)
|
||||
|
||||
if not origin or not destination:
|
||||
plpy.notice("Empty origin or destination")
|
||||
quota_service.increment_empty_service_use()
|
||||
return [None, None, None]
|
||||
|
||||
orig_lat = plpy.execute("SELECT ST_Y('%s') AS lat" % origin)[0]['lat']
|
||||
orig_lon = plpy.execute("SELECT ST_X('%s') AS lon" % origin)[0]['lon']
|
||||
origin_coordinates = Coordinate(orig_lon, orig_lat)
|
||||
dest_lat = plpy.execute("SELECT ST_Y('%s') AS lat" % destination)[0]['lat']
|
||||
dest_lon = plpy.execute("SELECT ST_X('%s') AS lon" % destination)[0]['lon']
|
||||
dest_coordinates = Coordinate(dest_lon, dest_lat)
|
||||
|
||||
resp = client.calculate_route_point_to_point(origin_coordinates, dest_coordinates, mode, options, units)
|
||||
|
||||
if resp and resp.shape:
|
||||
shape_linestring = polyline_to_linestring(resp.shape)
|
||||
if shape_linestring:
|
||||
quota_service.increment_success_service_use()
|
||||
return [shape_linestring, resp.length, resp.duration]
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return [None, None, None]
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return [None, None, None]
|
||||
except BaseException as e:
|
||||
import sys, traceback
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
quota_service.increment_failed_service_use()
|
||||
error_msg = 'There was an error trying to obtain route using mapzen provider: {0}'.format(e)
|
||||
plpy.notice(traceback.format_tb(traceback_))
|
||||
plpy.error(error_msg)
|
||||
finally:
|
||||
quota_service.increment_total_service_use()
|
||||
$$ LANGUAGE plpythonu SECURITY DEFINER;
|
||||
1024
server/extension/cdb_dataservices_server--0.6.2.sql
Normal file
1024
server/extension/cdb_dataservices_server--0.6.2.sql
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
comment = 'CartoDB dataservices server extension'
|
||||
default_version = '0.5.2'
|
||||
default_version = '0.6.2'
|
||||
requires = 'plpythonu, postgis, cdb_geocoder'
|
||||
superuser = true
|
||||
schema = cdb_dataservices_server
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
|
||||
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.6.0'" to load this file. \quit
|
||||
CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL)
|
||||
RETURNS Geometry AS $$
|
||||
plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username))
|
||||
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
|
||||
plpy.execute("SELECT cdb_dataservices_server._get_geocoder_config({0}, {1})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname)))
|
||||
user_geocoder_config = GD["user_geocoder_config_{0}".format(username)]
|
||||
|
||||
if user_geocoder_config.heremaps_geocoder:
|
||||
here_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_here_geocode_street_point($1, $2, $3, $4, $5, $6) as point; ", ["text", "text", "text", "text", "text", "text"])
|
||||
return plpy.execute(here_plan, [username, orgname, searchtext, city, state_province, country], 1)[0]['point']
|
||||
elif user_geocoder_config.google_geocoder:
|
||||
google_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_google_geocode_street_point($1, $2, $3, $4, $5, $6) as point; ", ["text", "text", "text", "text", "text", "text"])
|
||||
return plpy.execute(google_plan, [username, orgname, searchtext, city, state_province, country], 1)[0]['point']
|
||||
elif user_geocoder_config.mapzen_geocoder:
|
||||
mapzen_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapzen_geocode_street_point($1, $2, $3, $4, $5, $6) as point; ", ["text", "text", "text", "text", "text", "text"])
|
||||
return plpy.execute(mapzen_plan, [username, orgname, searchtext, city, state_province, country], 1)[0]['point']
|
||||
else:
|
||||
plpy.error('Requested geocoder is not available')
|
||||
|
||||
$$ LANGUAGE plpythonu;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL)
|
||||
RETURNS Geometry AS $$
|
||||
from cartodb_services.mapzen import MapzenGeocoder
|
||||
from cartodb_services.metrics import QuotaService
|
||||
|
||||
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
|
||||
user_geocoder_config = GD["user_geocoder_config_{0}".format(username)]
|
||||
quota_service = QuotaService(user_geocoder_config, redis_conn)
|
||||
|
||||
try:
|
||||
geocoder = MapzenGeocoder(user_geocoder_config.mapzen_app_key)
|
||||
coordinates = geocoder.geocode(searchtext, country=country)
|
||||
if coordinates:
|
||||
quota_service.increment_success_service_use()
|
||||
plan = plpy.prepare("SELECT ST_SetSRID(ST_MakePoint($1, $2), 4326) as point; ", ["double precision", "double precision"])
|
||||
point = plpy.execute(plan, [coordinates[0], coordinates[1]], 1)[0]
|
||||
return point['point']
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return None
|
||||
except BaseException as e:
|
||||
import sys, traceback
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
quota_service.increment_failed_service_use()
|
||||
error_msg = 'There was an error trying to geocode using mapzen geocoder: {0}'.format(e)
|
||||
plpy.notice(traceback.format_tb(traceback_))
|
||||
plpy.error(error_msg)
|
||||
finally:
|
||||
quota_service.increment_total_service_use()
|
||||
$$ LANGUAGE plpythonu;
|
||||
@@ -31,11 +31,6 @@ RETURNS cdb_dataservices_server.simple_route AS $$
|
||||
try:
|
||||
client = MapzenRouting(user_routing_config.mapzen_app_key)
|
||||
|
||||
if not origin or not destination:
|
||||
plpy.notice("Empty origin or destination")
|
||||
quota_service.increment_empty_service_use()
|
||||
return [None, None, None]
|
||||
|
||||
orig_lat = plpy.execute("SELECT ST_Y('%s') AS lat" % origin)[0]['lat']
|
||||
orig_lon = plpy.execute("SELECT ST_X('%s') AS lon" % origin)[0]['lon']
|
||||
origin_coordinates = Coordinate(orig_lon, orig_lat)
|
||||
@@ -47,21 +42,20 @@ RETURNS cdb_dataservices_server.simple_route AS $$
|
||||
|
||||
if resp and resp.shape:
|
||||
shape_linestring = polyline_to_linestring(resp.shape)
|
||||
if shape_linestring:
|
||||
quota_service.increment_success_service_use()
|
||||
return [shape_linestring, resp.length, resp.duration]
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return [None, None, None]
|
||||
quota_service.increment_success_service_use()
|
||||
return [shape_linestring, resp.length, resp.duration]
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return [None, None, None]
|
||||
except BaseException as e:
|
||||
import sys, traceback
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
quota_service.increment_failed_service_use()
|
||||
error_msg = 'There was an error trying to obtain route using mapzen provider: {0}'.format(e)
|
||||
plpy.notice(traceback.format_tb(traceback_))
|
||||
plpy.warning('Resp shape: {0}'.format(resp.shape))
|
||||
plpy.warning('Resp duration: {0}'.format(resp.duration))
|
||||
plpy.warning('Resp length: {0}'.format(resp.length))
|
||||
plpy.warning('Points: {0}, {1}'.format(origin, destination))
|
||||
plpy.warning(traceback.format_tb(traceback_))
|
||||
plpy.error(error_msg)
|
||||
finally:
|
||||
quota_service.increment_total_service_use()
|
||||
@@ -0,0 +1,22 @@
|
||||
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
|
||||
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.5.2'" to load this file. \quit
|
||||
DROP FUNCTION IF EXISTS cdb_dataservices_server._cdb_mapzen_geocode_street_point(TEXT, TEXT, TEXT, TEXT, TEXT, TEXT);
|
||||
|
||||
CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL)
|
||||
RETURNS Geometry AS $$
|
||||
plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username))
|
||||
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
|
||||
plpy.execute("SELECT cdb_dataservices_server._get_geocoder_config({0}, {1})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname)))
|
||||
user_geocoder_config = GD["user_geocoder_config_{0}".format(username)]
|
||||
|
||||
if user_geocoder_config.heremaps_geocoder:
|
||||
here_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_here_geocode_street_point($1, $2, $3, $4, $5, $6) as point; ", ["text", "text", "text", "text", "text", "text"])
|
||||
return plpy.execute(here_plan, [username, orgname, searchtext, city, state_province, country], 1)[0]['point']
|
||||
elif user_geocoder_config.google_geocoder:
|
||||
google_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_google_geocode_street_point($1, $2, $3, $4, $5, $6) as point; ", ["text", "text", "text", "text", "text", "text"])
|
||||
return plpy.execute(google_plan, [username, orgname, searchtext, city, state_province, country], 1)[0]['point']
|
||||
else:
|
||||
plpy.error('Requested geocoder is not available')
|
||||
|
||||
$$ LANGUAGE plpythonu;
|
||||
@@ -0,0 +1,39 @@
|
||||
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
|
||||
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.6.1'" to load this file. \quit
|
||||
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL)
|
||||
RETURNS Geometry AS $$
|
||||
from cartodb_services.mapzen import MapzenGeocoder
|
||||
from cartodb_services.mapzen.types import country_to_iso3
|
||||
from cartodb_services.metrics import QuotaService
|
||||
|
||||
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
|
||||
user_geocoder_config = GD["user_geocoder_config_{0}".format(username)]
|
||||
quota_service = QuotaService(user_geocoder_config, redis_conn)
|
||||
|
||||
try:
|
||||
geocoder = MapzenGeocoder(user_geocoder_config.mapzen_app_key)
|
||||
country_iso3 = None
|
||||
if country:
|
||||
country_iso3 = country_to_iso3(country)
|
||||
coordinates = geocoder.geocode(searchtext=searchtext, city=city,
|
||||
state_province=state_province,
|
||||
country=country_iso3)
|
||||
if coordinates:
|
||||
quota_service.increment_success_service_use()
|
||||
plan = plpy.prepare("SELECT ST_SetSRID(ST_MakePoint($1, $2), 4326); ", ["double precision", "double precision"])
|
||||
point = plpy.execute(plan, [coordinates[0], coordinates[1]], 1)[0]
|
||||
return point['st_setsrid']
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return None
|
||||
except BaseException as e:
|
||||
import sys, traceback
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
quota_service.increment_failed_service_use()
|
||||
error_msg = 'There was an error trying to geocode using mapzen geocoder: {0}'.format(e)
|
||||
plpy.notice(traceback.format_tb(traceback_))
|
||||
plpy.error(error_msg)
|
||||
finally:
|
||||
quota_service.increment_total_service_use()
|
||||
$$ LANGUAGE plpythonu;
|
||||
1014
server/extension/old_versions/cdb_dataservices_server--0.6.0.sql
Normal file
1014
server/extension/old_versions/cdb_dataservices_server--0.6.0.sql
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
|
||||
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION cdb_dataservices_server UPDATE TO '0.6.0'" to load this file. \quit
|
||||
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL)
|
||||
RETURNS Geometry AS $$
|
||||
from cartodb_services.mapzen import MapzenGeocoder
|
||||
from cartodb_services.metrics import QuotaService
|
||||
|
||||
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
|
||||
user_geocoder_config = GD["user_geocoder_config_{0}".format(username)]
|
||||
quota_service = QuotaService(user_geocoder_config, redis_conn)
|
||||
|
||||
try:
|
||||
geocoder = MapzenGeocoder(user_geocoder_config.mapzen_app_key)
|
||||
coordinates = geocoder.geocode(searchtext=searchtext, country=country)
|
||||
if coordinates:
|
||||
quota_service.increment_success_service_use()
|
||||
plan = plpy.prepare("SELECT ST_SetSRID(ST_MakePoint($1, $2), 4326); ", ["double precision", "double precision"])
|
||||
point = plpy.execute(plan, [coordinates[0], coordinates[1]], 1)[0]
|
||||
return point['st_setsrid']
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return None
|
||||
except BaseException as e:
|
||||
import sys, traceback
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
quota_service.increment_failed_service_use()
|
||||
error_msg = 'There was an error trying to geocode using mapzen geocoder: {0}'.format(e)
|
||||
plpy.notice(traceback.format_tb(traceback_))
|
||||
plpy.error(error_msg)
|
||||
finally:
|
||||
quota_service.increment_total_service_use()
|
||||
$$ LANGUAGE plpythonu;
|
||||
1021
server/extension/old_versions/cdb_dataservices_server--0.6.1.sql
Normal file
1021
server/extension/old_versions/cdb_dataservices_server--0.6.1.sql
Normal file
File diff suppressed because it is too large
Load Diff
@@ -24,9 +24,11 @@ RETURNS cdb_dataservices_server.simple_route AS $$
|
||||
user_routing_config = GD["user_routing_config_{0}".format(username)]
|
||||
|
||||
quota_service = QuotaService(user_routing_config, redis_conn)
|
||||
if not quota_service.check_user_quota():
|
||||
plpy.error('You have reach the limit of your quota')
|
||||
|
||||
try:
|
||||
client = MapzenRouting(user_routing_config.mapzen_app_key)
|
||||
client = MapzenRouting(user_routing_config.mapzen_api_key)
|
||||
|
||||
if not origin or not destination:
|
||||
plpy.notice("Empty origin or destination")
|
||||
|
||||
@@ -12,6 +12,9 @@ RETURNS Geometry AS $$
|
||||
elif user_geocoder_config.google_geocoder:
|
||||
google_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_google_geocode_street_point($1, $2, $3, $4, $5, $6) as point; ", ["text", "text", "text", "text", "text", "text"])
|
||||
return plpy.execute(google_plan, [username, orgname, searchtext, city, state_province, country], 1)[0]['point']
|
||||
elif user_geocoder_config.mapzen_geocoder:
|
||||
mapzen_plan = plpy.prepare("SELECT cdb_dataservices_server._cdb_mapzen_geocode_street_point($1, $2, $3, $4, $5, $6) as point; ", ["text", "text", "text", "text", "text", "text"])
|
||||
return plpy.execute(mapzen_plan, [username, orgname, searchtext, city, state_province, country], 1)[0]['point']
|
||||
else:
|
||||
plpy.error('Requested geocoder is not available')
|
||||
|
||||
@@ -82,3 +85,42 @@ RETURNS Geometry AS $$
|
||||
finally:
|
||||
quota_service.increment_total_service_use()
|
||||
$$ LANGUAGE plpythonu;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_geocode_street_point(username TEXT, orgname TEXT, searchtext TEXT, city TEXT DEFAULT NULL, state_province TEXT DEFAULT NULL, country TEXT DEFAULT NULL)
|
||||
RETURNS Geometry AS $$
|
||||
from cartodb_services.mapzen import MapzenGeocoder
|
||||
from cartodb_services.mapzen.types import country_to_iso3
|
||||
from cartodb_services.metrics import QuotaService
|
||||
|
||||
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
|
||||
user_geocoder_config = GD["user_geocoder_config_{0}".format(username)]
|
||||
quota_service = QuotaService(user_geocoder_config, redis_conn)
|
||||
if not quota_service.check_user_quota():
|
||||
plpy.error('You have reach the limit of your quota')
|
||||
|
||||
try:
|
||||
geocoder = MapzenGeocoder(user_geocoder_config.mapzen_api_key)
|
||||
country_iso3 = None
|
||||
if country:
|
||||
country_iso3 = country_to_iso3(country)
|
||||
coordinates = geocoder.geocode(searchtext=searchtext, city=city,
|
||||
state_province=state_province,
|
||||
country=country_iso3)
|
||||
if coordinates:
|
||||
quota_service.increment_success_service_use()
|
||||
plan = plpy.prepare("SELECT ST_SetSRID(ST_MakePoint($1, $2), 4326); ", ["double precision", "double precision"])
|
||||
point = plpy.execute(plan, [coordinates[0], coordinates[1]], 1)[0]
|
||||
return point['st_setsrid']
|
||||
else:
|
||||
quota_service.increment_empty_service_use()
|
||||
return None
|
||||
except BaseException as e:
|
||||
import sys, traceback
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
quota_service.increment_failed_service_use()
|
||||
error_msg = 'There was an error trying to geocode using mapzen geocoder: {0}'.format(e)
|
||||
plpy.notice(traceback.format_tb(traceback_))
|
||||
plpy.error(error_msg)
|
||||
finally:
|
||||
quota_service.increment_total_service_use()
|
||||
$$ LANGUAGE plpythonu;
|
||||
|
||||
@@ -19,13 +19,13 @@ SELECT cartodb.cdb_conf_setconf('redis_metadata_config', '{"redis_host": "localh
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT cartodb.cdb_conf_setconf('heremaps_conf', '{"app_id": "dummy_id", "app_code": "dummy_code", "geocoder_cost_per_hit": 1}');
|
||||
SELECT cartodb.cdb_conf_setconf('heremaps_conf', '{"geocoder": {"app_id": "dummy_id", "app_code": "dummy_code", "geocoder_cost_per_hit": 1}, "isolines": {"app_id": "dummy_id", "app_code": "dummy_code"}}');
|
||||
cdb_conf_setconf
|
||||
------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT cartodb.cdb_conf_setconf('mapzen_conf', '{"routing_app_key": "dummy_key"}');
|
||||
SELECT cartodb.cdb_conf_setconf('mapzen_conf', '{"routing": {"api_key": "routing_dummy_api_key", "monthly_quota": 1500000}, "geocoder": {"api_key": "geocoder_dummy_api_key", "monthly_quota": 1500000}}');
|
||||
cdb_conf_setconf
|
||||
------------------
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ CREATE EXTENSION cdb_dataservices_server;
|
||||
-- Mock the redis server connection to point to this very test db
|
||||
SELECT cartodb.cdb_conf_setconf('redis_metrics_config', '{"redis_host": "localhost", "redis_port": 6379, "timeout": 0.1, "redis_db": 5}');
|
||||
SELECT cartodb.cdb_conf_setconf('redis_metadata_config', '{"redis_host": "localhost", "redis_port": 6379, "timeout": 0.1, "redis_db": 5}');
|
||||
SELECT cartodb.cdb_conf_setconf('heremaps_conf', '{"app_id": "dummy_id", "app_code": "dummy_code", "geocoder_cost_per_hit": 1}');
|
||||
SELECT cartodb.cdb_conf_setconf('mapzen_conf', '{"routing_app_key": "dummy_key"}');
|
||||
SELECT cartodb.cdb_conf_setconf('heremaps_conf', '{"geocoder": {"app_id": "dummy_id", "app_code": "dummy_code", "geocoder_cost_per_hit": 1}, "isolines": {"app_id": "dummy_id", "app_code": "dummy_code"}}');
|
||||
SELECT cartodb.cdb_conf_setconf('mapzen_conf', '{"routing": {"api_key": "routing_dummy_api_key", "monthly_quota": 1500000}, "geocoder": {"api_key": "geocoder_dummy_api_key", "monthly_quota": 1500000}}');
|
||||
SELECT cartodb.cdb_conf_setconf('logger_conf', '{"geocoder_log_path": "/dev/null"}');
|
||||
|
||||
-- Mock the varnish invalidation function
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
from routing import MapzenRouting, MapzenRoutingResponse
|
||||
from geocoder import MapzenGeocoder
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import requests
|
||||
import json
|
||||
import re
|
||||
|
||||
from exceptions import WrongParams, MalformedResult
|
||||
from qps import qps_retry
|
||||
from cartodb_services.tools import Coordinate, PolyLine
|
||||
|
||||
|
||||
class MapzenGeocoder:
|
||||
'A Mapzen Geocoder wrapper for python'
|
||||
|
||||
BASE_URL = 'https://search.mapzen.com/v1/search'
|
||||
|
||||
def __init__(self, app_key, base_url=BASE_URL):
|
||||
self._app_key = app_key
|
||||
self._url = base_url
|
||||
|
||||
@qps_retry
|
||||
def geocode(self, searchtext, city=None, state_province=None, country=None):
|
||||
request_params = self._build_requests_parameters(searchtext, city, state_province, country)
|
||||
response = requests.get(self._url, params=request_params)
|
||||
if response.status_code == requests.codes.ok:
|
||||
return self.__parse_response(response.text)
|
||||
elif response.status_code == requests.codes.bad_request:
|
||||
return []
|
||||
else:
|
||||
response.raise_for_status()
|
||||
|
||||
def _build_requests_parameters(self, searchtext, city=None,
|
||||
state_province=None, country=None):
|
||||
request_params = {}
|
||||
search_string = self._build_search_text(searchtext, city, state_province)
|
||||
request_params['text'] = search_string
|
||||
request_params['layers'] = 'address'
|
||||
request_params['api_key'] = self._app_key
|
||||
if country:
|
||||
request_params['boundary.country'] = country
|
||||
return request_params
|
||||
|
||||
def _build_search_text(self, searchtext, city, state_province):
|
||||
search_string = searchtext
|
||||
if city:
|
||||
search_string = "{0}, {1}".format(search_string, city)
|
||||
if state_province:
|
||||
search_string = "{0}, {1}".format(search_string, state_province)
|
||||
|
||||
return search_string
|
||||
|
||||
def __parse_response(self, response):
|
||||
try:
|
||||
parsed_json_response = json.loads(response)
|
||||
feature = parsed_json_response['features'][0]
|
||||
return self._extract_lng_lat_from_result(feature)
|
||||
except IndexError:
|
||||
return []
|
||||
except KeyError:
|
||||
raise MalformedResult()
|
||||
|
||||
def _extract_lng_lat_from_result(self, result):
|
||||
location = result['geometry']['coordinates']
|
||||
longitude = location[0]
|
||||
latitude = location[1]
|
||||
return [longitude, latitude]
|
||||
@@ -1,4 +1,5 @@
|
||||
import time
|
||||
import random
|
||||
from datetime import datetime
|
||||
from exceptions import TimeoutException
|
||||
|
||||
|
||||
@@ -18,3 +18,17 @@ def polyline_to_linestring(polyline):
|
||||
geometry = None
|
||||
|
||||
return geometry
|
||||
|
||||
def country_to_iso3(country):
|
||||
""" Convert country to its iso3 code """
|
||||
try:
|
||||
country_plan = plpy.prepare("SELECT adm0_a3 as iso3 FROM admin0_synonyms WHERE lower(regexp_replace($1, " \
|
||||
"'[^a-zA-Z\u00C0-\u00ff]+', '', 'g'))::text = name_; ", ['text'])
|
||||
country_result = plpy.execute(country_plan, [country], 1)
|
||||
if country_result:
|
||||
return country_result[0]['iso3']
|
||||
else:
|
||||
return None
|
||||
except BaseException as e:
|
||||
plpy.warning("Can't get the iso3 code from {0}: {1}".format(country, e))
|
||||
return None
|
||||
|
||||
@@ -10,10 +10,15 @@ class ConfigException(Exception):
|
||||
class ServiceConfig(object):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, redis_connection, username, orgname=None):
|
||||
def __init__(self, redis_connection, db_conn, username, orgname=None):
|
||||
self._redis_connection = redis_connection
|
||||
self._username = username
|
||||
self._orgname = orgname
|
||||
self._db_config = ServicesDBConfig(db_conn)
|
||||
if redis_connection:
|
||||
self._redis_config = ServicesRedisConfig(redis_connection).build(username, orgname)
|
||||
else:
|
||||
self._redis_config = None
|
||||
|
||||
@abc.abstractproperty
|
||||
def service_type(self):
|
||||
@@ -30,69 +35,49 @@ class ServiceConfig(object):
|
||||
|
||||
class RoutingConfig(ServiceConfig):
|
||||
|
||||
ROUTING_CONFIG_KEYS = ['username', 'orgname', 'mapzen_app_key']
|
||||
MAPZEN_APP_KEY = 'mapzen_app_key'
|
||||
USERNAME_KEY = 'username'
|
||||
ORGNAME_KEY = 'orgname'
|
||||
PERIOD_END_DATE = 'period_end_date'
|
||||
|
||||
def __init__(self, redis_connection, db_conn, username, orgname=None):
|
||||
super(RoutingConfig, self).__init__(redis_connection, username,
|
||||
orgname)
|
||||
db_config = ServicesDBConfig(db_conn)
|
||||
self._mapzen_app_key = db_config.mapzen_routing_app_key
|
||||
super(RoutingConfig, self).__init__(redis_connection, db_conn,
|
||||
username, orgname)
|
||||
self._mapzen_api_key = self._db_config.mapzen_routing_api_key
|
||||
self._monthly_quota = self._db_config.mapzen_routing_monthly_quota
|
||||
self._period_end_date = date_parse(self._redis_config[self.PERIOD_END_DATE])
|
||||
|
||||
@property
|
||||
def service_type(self):
|
||||
return 'routing_mapzen'
|
||||
|
||||
@property
|
||||
def mapzen_app_key(self):
|
||||
return self._mapzen_app_key
|
||||
def mapzen_api_key(self):
|
||||
return self._mapzen_api_key
|
||||
|
||||
@property
|
||||
def monthly_quota(self):
|
||||
return self._monthly_quota
|
||||
|
||||
@property
|
||||
def period_end_date(self):
|
||||
return self._period_end_date
|
||||
|
||||
|
||||
class IsolinesRoutingConfig(ServiceConfig):
|
||||
|
||||
ROUTING_CONFIG_KEYS = ['here_isolines_quota', 'soft_here_isolines_limit',
|
||||
'period_end_date', 'username', 'orgname',
|
||||
'heremaps_app_id', 'heremaps_app_code',
|
||||
'heremaps_isolines_app_id', 'heremaps_isolines_app_code',
|
||||
'geocoder_type']
|
||||
NOKIA_APP_ID_KEY = 'heremaps_app_id'
|
||||
NOKIA_APP_CODE_KEY = 'heremaps_app_code'
|
||||
QUOTA_KEY = 'here_isolines_quota'
|
||||
SOFT_LIMIT_KEY = 'soft_here_isolines_limit'
|
||||
USERNAME_KEY = 'username'
|
||||
ORGNAME_KEY = 'orgname'
|
||||
PERIOD_END_DATE = 'period_end_date'
|
||||
GEOCODER_TYPE_KEY = 'geocoder_type'
|
||||
GOOGLE_GEOCODER = 'google'
|
||||
|
||||
def __init__(self, redis_connection, db_conn, username, orgname=None):
|
||||
super(IsolinesRoutingConfig, self).__init__(redis_connection, username,
|
||||
orgname)
|
||||
config = self.__get_user_config(username, orgname)
|
||||
db_config = ServicesDBConfig(db_conn)
|
||||
filtered_config = {key: config[key] for key in self.ROUTING_CONFIG_KEYS if key in config.keys()}
|
||||
self.__parse_config(filtered_config, db_config)
|
||||
|
||||
def __get_user_config(self, username, orgname):
|
||||
user_config = self._redis_connection.hgetall(
|
||||
"rails:users:{0}".format(username))
|
||||
if not user_config:
|
||||
raise ConfigException("""There is no user config available. Please check your configuration.'""")
|
||||
else:
|
||||
if orgname:
|
||||
self.__get_organization_config(orgname, user_config)
|
||||
|
||||
return user_config
|
||||
|
||||
def __get_organization_config(self, orgname, user_config):
|
||||
org_config = self._redis_connection.hgetall(
|
||||
"rails:orgs:{0}".format(orgname))
|
||||
if not org_config:
|
||||
raise ConfigException("""There is no organization config available. Please check your configuration.'""")
|
||||
else:
|
||||
user_config[self.QUOTA_KEY] = org_config[self.QUOTA_KEY]
|
||||
user_config[self.PERIOD_END_DATE] = org_config[self.PERIOD_END_DATE]
|
||||
super(IsolinesRoutingConfig, self).__init__(redis_connection, db_conn,
|
||||
username, orgname)
|
||||
filtered_config = {key: self._redis_config[key] for key in self.ROUTING_CONFIG_KEYS if key in self._redis_config.keys()}
|
||||
self.__parse_config(filtered_config, self._db_config)
|
||||
|
||||
def __parse_config(self, filtered_config, db_config):
|
||||
self._geocoder_type = filtered_config[self.GEOCODER_TYPE_KEY].lower()
|
||||
@@ -102,8 +87,8 @@ class IsolinesRoutingConfig(ServiceConfig):
|
||||
self._soft_isolines_limit = True
|
||||
else:
|
||||
self._soft_isolines_limit = False
|
||||
self._heremaps_app_id = db_config.heremaps_app_id
|
||||
self._heremaps_app_code = db_config.heremaps_app_code
|
||||
self._heremaps_app_id = db_config.heremaps_isolines_app_id
|
||||
self._heremaps_app_code = db_config.heremaps_isolines_app_code
|
||||
|
||||
@property
|
||||
def service_type(self):
|
||||
@@ -137,10 +122,10 @@ class IsolinesRoutingConfig(ServiceConfig):
|
||||
class InternalGeocoderConfig(ServiceConfig):
|
||||
|
||||
def __init__(self, redis_connection, db_conn, username, orgname=None):
|
||||
super(InternalGeocoderConfig, self).__init__(redis_connection,
|
||||
# For now, internal geocoder doesn't use the redis config
|
||||
super(InternalGeocoderConfig, self).__init__(None, db_conn,
|
||||
username, orgname)
|
||||
db_config = ServicesDBConfig(db_conn)
|
||||
self._log_path = db_config.geocoder_log_path
|
||||
self._log_path = self._db_config.geocoder_log_path
|
||||
|
||||
@property
|
||||
def service_type(self):
|
||||
@@ -168,15 +153,17 @@ class GeocoderConfig(ServiceConfig):
|
||||
GEOCODER_CONFIG_KEYS = ['google_maps_client_id', 'google_maps_api_key',
|
||||
'geocoding_quota', 'soft_geocoding_limit',
|
||||
'geocoder_type', 'period_end_date',
|
||||
'heremaps_app_id', 'heremaps_app_code', 'username',
|
||||
'orgname']
|
||||
NOKIA_GEOCODER_MANDATORY_KEYS = ['geocoding_quota', 'soft_geocoding_limit']
|
||||
'heremaps_geocoder_app_id', 'heremaps_geocoder_app_code',
|
||||
'mapzen_geocoder_api_key', 'username', 'orgname']
|
||||
NOKIA_GEOCODER_REDIS_MANDATORY_KEYS = ['geocoding_quota', 'soft_geocoding_limit']
|
||||
NOKIA_GEOCODER = 'heremaps'
|
||||
NOKIA_GEOCODER_APP_ID_KEY = 'heremaps_app_id'
|
||||
NOKIA_GEOCODER_APP_CODE_KEY = 'heremaps_app_code'
|
||||
NOKIA_GEOCODER_APP_ID_KEY = 'heremaps_geocoder_app_id'
|
||||
NOKIA_GEOCODER_APP_CODE_KEY = 'heremaps_geocoder_app_code'
|
||||
GOOGLE_GEOCODER = 'google'
|
||||
GOOGLE_GEOCODER_API_KEY = 'google_maps_api_key'
|
||||
GOOGLE_GEOCODER_CLIENT_ID = 'google_maps_client_id'
|
||||
MAPZEN_GEOCODER = 'mapzen'
|
||||
MAPZEN_GEOCODER_API_KEY = 'mapzen_geocoder_api_key'
|
||||
GEOCODER_TYPE = 'geocoder_type'
|
||||
QUOTA_KEY = 'geocoding_quota'
|
||||
SOFT_LIMIT_KEY = 'soft_geocoding_limit'
|
||||
@@ -185,12 +172,10 @@ class GeocoderConfig(ServiceConfig):
|
||||
PERIOD_END_DATE = 'period_end_date'
|
||||
|
||||
def __init__(self, redis_connection, db_conn, username, orgname=None):
|
||||
super(GeocoderConfig, self).__init__(redis_connection, username,
|
||||
orgname)
|
||||
db_config = ServicesDBConfig(db_conn)
|
||||
config = self.__get_user_config(username, orgname)
|
||||
filtered_config = {key: config[key] for key in self.GEOCODER_CONFIG_KEYS if key in config.keys()}
|
||||
self.__parse_config(filtered_config, db_config)
|
||||
super(GeocoderConfig, self).__init__(redis_connection, db_conn,
|
||||
username, orgname)
|
||||
filtered_config = {key: self._redis_config[key] for key in self.GEOCODER_CONFIG_KEYS if key in self._redis_config.keys()}
|
||||
self.__parse_config(filtered_config, self._db_config)
|
||||
self.__check_config(filtered_config)
|
||||
|
||||
def __get_user_config(self, username, orgname):
|
||||
@@ -217,11 +202,15 @@ class GeocoderConfig(ServiceConfig):
|
||||
|
||||
def __check_config(self, filtered_config):
|
||||
if filtered_config[self.GEOCODER_TYPE].lower() == self.NOKIA_GEOCODER:
|
||||
if not set(self.NOKIA_GEOCODER_MANDATORY_KEYS).issubset(set(filtered_config.keys())):
|
||||
if not set(self.NOKIA_GEOCODER_REDIS_MANDATORY_KEYS).issubset(set(filtered_config.keys())) or \
|
||||
not self.heremaps_app_id or not self.heremaps_app_code:
|
||||
raise ConfigException("""Some mandatory parameter/s for Nokia geocoder are missing. Check it please""")
|
||||
elif filtered_config[self.GEOCODER_TYPE].lower() == self.GOOGLE_GEOCODER:
|
||||
if self.GOOGLE_GEOCODER_API_KEY not in filtered_config.keys():
|
||||
raise ConfigException("""Google geocoder need the mandatory parameter 'google_maps_private_key'""")
|
||||
elif filtered_config[self.GEOCODER_TYPE].lower() == self.MAPZEN_GEOCODER:
|
||||
if not self.mapzen_api_key:
|
||||
raise ConfigException("""Mapzen config is not setted up""")
|
||||
|
||||
return True
|
||||
|
||||
@@ -235,18 +224,24 @@ class GeocoderConfig(ServiceConfig):
|
||||
else:
|
||||
self._soft_geocoding_limit = False
|
||||
if filtered_config[self.GEOCODER_TYPE].lower() == self.NOKIA_GEOCODER:
|
||||
self._heremaps_app_id = db_config.heremaps_app_id
|
||||
self._heremaps_app_code = db_config.heremaps_app_code
|
||||
self._heremaps_app_id = db_config.heremaps_geocoder_app_id
|
||||
self._heremaps_app_code = db_config.heremaps_geocoder_app_code
|
||||
self._cost_per_hit = db_config.heremaps_geocoder_cost_per_hit
|
||||
elif filtered_config[self.GEOCODER_TYPE].lower() == self.GOOGLE_GEOCODER:
|
||||
self._google_maps_api_key = filtered_config[self.GOOGLE_GEOCODER_API_KEY]
|
||||
self._google_maps_client_id = filtered_config[self.GOOGLE_GEOCODER_CLIENT_ID]
|
||||
self._cost_per_hit = 0
|
||||
elif filtered_config[self.GEOCODER_TYPE].lower() == self.MAPZEN_GEOCODER:
|
||||
self._mapzen_api_key = db_config.mapzen_geocoder_api_key
|
||||
self._geocoding_quota = db_config.mapzen_geocoder_monthly_quota
|
||||
self._cost_per_hit = 0
|
||||
|
||||
@property
|
||||
def service_type(self):
|
||||
if self._geocoder_type == self.GOOGLE_GEOCODER:
|
||||
return 'geocoder_google'
|
||||
elif self._geocoder_type == self.MAPZEN_GEOCODER:
|
||||
return 'geocoder_mapzen'
|
||||
else:
|
||||
return 'geocoder_here'
|
||||
|
||||
@@ -258,6 +253,10 @@ class GeocoderConfig(ServiceConfig):
|
||||
def google_geocoder(self):
|
||||
return self._geocoder_type == self.GOOGLE_GEOCODER
|
||||
|
||||
@property
|
||||
def mapzen_geocoder(self):
|
||||
return self._geocoder_type == self.MAPZEN_GEOCODER
|
||||
|
||||
@property
|
||||
def google_client_id(self):
|
||||
return self._google_maps_client_id
|
||||
@@ -268,10 +267,10 @@ class GeocoderConfig(ServiceConfig):
|
||||
|
||||
@property
|
||||
def geocoding_quota(self):
|
||||
if self.heremaps_geocoder:
|
||||
return self._geocoding_quota
|
||||
else:
|
||||
if self.google_geocoder:
|
||||
return None
|
||||
else:
|
||||
return self._geocoding_quota
|
||||
|
||||
@property
|
||||
def soft_geocoding_limit(self):
|
||||
@@ -289,6 +288,10 @@ class GeocoderConfig(ServiceConfig):
|
||||
def heremaps_app_code(self):
|
||||
return self._heremaps_app_code
|
||||
|
||||
@property
|
||||
def mapzen_api_key(self):
|
||||
return self._mapzen_api_key
|
||||
|
||||
@property
|
||||
def is_high_resolution(self):
|
||||
return True
|
||||
@@ -319,10 +322,12 @@ class ServicesDBConfig:
|
||||
raise ConfigException('Here maps configuration missing')
|
||||
else:
|
||||
heremaps_conf = json.loads(heremaps_conf_json)
|
||||
self._heremaps_app_id = heremaps_conf['app_id']
|
||||
self._heremaps_app_code = heremaps_conf['app_code']
|
||||
self._heremaps_geocoder_cost_per_hit = heremaps_conf[
|
||||
self._heremaps_geocoder_app_id = heremaps_conf['geocoder']['app_id']
|
||||
self._heremaps_geocoder_app_code = heremaps_conf['geocoder']['app_code']
|
||||
self._heremaps_geocoder_cost_per_hit = heremaps_conf['geocoder'][
|
||||
'geocoder_cost_per_hit']
|
||||
self._heremaps_isolines_app_id = heremaps_conf['isolines']['app_id']
|
||||
self._heremaps_isolines_app_code = heremaps_conf['isolines']['app_code']
|
||||
|
||||
def _get_mapzen_config(self):
|
||||
mapzen_conf_json = self._get_conf('mapzen_conf')
|
||||
@@ -330,7 +335,10 @@ class ServicesDBConfig:
|
||||
raise ConfigException('Mapzen configuration missing')
|
||||
else:
|
||||
mapzen_conf = json.loads(mapzen_conf_json)
|
||||
self._mapzen_routing_app_key = mapzen_conf['routing_app_key']
|
||||
self._mapzen_routing_api_key = mapzen_conf['routing']['api_key']
|
||||
self._mapzen_routing_quota = mapzen_conf['routing']['monthly_quota']
|
||||
self._mapzen_geocoder_api_key = mapzen_conf['geocoder']['api_key']
|
||||
self._mapzen_geocoder_quota = mapzen_conf['geocoder']['monthly_quota']
|
||||
|
||||
def _get_logger_config(self):
|
||||
logger_conf_json = self._get_conf('logger_conf')
|
||||
@@ -349,21 +357,79 @@ class ServicesDBConfig:
|
||||
raise ConfigException("Malformed config for {0}: {1}".format(key, e))
|
||||
|
||||
@property
|
||||
def heremaps_app_id(self):
|
||||
return self._heremaps_app_id
|
||||
def heremaps_isolines_app_id(self):
|
||||
return self._heremaps_isolines_app_id
|
||||
|
||||
@property
|
||||
def heremaps_app_code(self):
|
||||
return self._heremaps_app_code
|
||||
def heremaps_isolines_app_code(self):
|
||||
return self._heremaps_isolines_app_code
|
||||
|
||||
@property
|
||||
def heremaps_geocoder_app_id(self):
|
||||
return self._heremaps_geocoder_app_id
|
||||
|
||||
@property
|
||||
def heremaps_geocoder_app_code(self):
|
||||
return self._heremaps_geocoder_app_code
|
||||
|
||||
@property
|
||||
def heremaps_geocoder_cost_per_hit(self):
|
||||
return self._heremaps_geocoder_cost_per_hit
|
||||
|
||||
@property
|
||||
def mapzen_routing_app_key(self):
|
||||
return self._mapzen_routing_app_key
|
||||
def mapzen_routing_api_key(self):
|
||||
return self._mapzen_routing_api_key
|
||||
|
||||
@property
|
||||
def mapzen_routing_monthly_quota(self):
|
||||
return self._mapzen_routing_quota
|
||||
|
||||
@property
|
||||
def mapzen_geocoder_api_key(self):
|
||||
return self._mapzen_geocoder_api_key
|
||||
|
||||
@property
|
||||
def mapzen_geocoder_monthly_quota(self):
|
||||
return self._mapzen_geocoder_quota
|
||||
|
||||
@property
|
||||
def geocoder_log_path(self):
|
||||
return self._geocoder_log_path
|
||||
|
||||
|
||||
class ServicesRedisConfig:
|
||||
|
||||
GOOGLE_GEOCODER_API_KEY = 'google_maps_api_key'
|
||||
GOOGLE_GEOCODER_CLIENT_ID = 'google_maps_client_id'
|
||||
QUOTA_KEY = 'geocoding_quota'
|
||||
ISOLINES_QUOTA_KEY = 'here_isolines_quota'
|
||||
PERIOD_END_DATE = 'period_end_date'
|
||||
|
||||
def __init__(self, redis_conn):
|
||||
self._redis_connection = redis_conn
|
||||
|
||||
def build(self, username, password):
|
||||
return self.__get_user_config(username, password)
|
||||
|
||||
def __get_user_config(self, username, orgname):
|
||||
user_config = self._redis_connection.hgetall(
|
||||
"rails:users:{0}".format(username))
|
||||
if not user_config:
|
||||
raise ConfigException("""There is no user config available. Please check your configuration.'""")
|
||||
else:
|
||||
if orgname:
|
||||
self.__get_organization_config(orgname, user_config)
|
||||
|
||||
return user_config
|
||||
|
||||
def __get_organization_config(self, orgname, user_config):
|
||||
org_config = self._redis_connection.hgetall(
|
||||
"rails:orgs:{0}".format(orgname))
|
||||
if not org_config:
|
||||
raise ConfigException("""There is no organization config available. Please check your configuration.'""")
|
||||
else:
|
||||
user_config[self.QUOTA_KEY] = org_config[self.QUOTA_KEY]
|
||||
user_config[self.ISOLINES_QUOTA_KEY] = org_config[self.ISOLINES_QUOTA_KEY]
|
||||
user_config[self.PERIOD_END_DATE] = org_config[self.PERIOD_END_DATE]
|
||||
user_config[self.GOOGLE_GEOCODER_CLIENT_ID] = org_config[self.GOOGLE_GEOCODER_CLIENT_ID]
|
||||
user_config[self.GOOGLE_GEOCODER_API_KEY] = org_config[self.GOOGLE_GEOCODER_API_KEY]
|
||||
|
||||
@@ -70,6 +70,9 @@ class QuotaChecker:
|
||||
elif re.match('here_isolines',
|
||||
self._user_service_config.service_type) is not None:
|
||||
return self.__check_isolines_quota()
|
||||
elif re.match('routing_mapzen',
|
||||
self._user_service_config.service_type) is not None:
|
||||
return self.__check_routing_quota()
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -100,3 +103,14 @@ class QuotaChecker:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __check_routing_quota(self):
|
||||
user_quota = self._user_service_config.monthly_quota
|
||||
today = date.today()
|
||||
service_type = self._user_service_config.service_type
|
||||
current_used = self._user_service.used_quota(service_type, today)
|
||||
|
||||
if (user_quota > 0 and current_used <= user_quota):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -41,11 +41,12 @@ class UserMetricsService:
|
||||
return current_use
|
||||
|
||||
def __used_isolines_quota(self, service_type, date):
|
||||
""" Recover the used quota for the user in the current month """
|
||||
date_from, date_to = self.__current_billing_cycle()
|
||||
current_use = 0
|
||||
isolines_generated = self.get_metrics(service_type,
|
||||
'isolines_generated', date_from,
|
||||
date_to)
|
||||
'isolines_generated', date_from,
|
||||
date_to)
|
||||
empty_responses = self.get_metrics(service_type,
|
||||
'empty_responses', date_from,
|
||||
date_to)
|
||||
@@ -53,12 +54,27 @@ class UserMetricsService:
|
||||
|
||||
return current_use
|
||||
|
||||
def __used_routing_quota(self, service_type, date):
|
||||
""" Recover the used quota for the user in the current month """
|
||||
date_from, date_to = self.__current_billing_cycle()
|
||||
current_use = 0
|
||||
success_responses = self.get_metrics(service_type,
|
||||
'success_responses', date_from,
|
||||
date_to)
|
||||
empty_responses = self.get_metrics(service_type,
|
||||
'empty_responses', date_from,
|
||||
date_to)
|
||||
current_use += (success_responses + empty_responses)
|
||||
|
||||
def increment_service_use(self, service_type, metric, date=date.today(), amount=1):
|
||||
return current_use
|
||||
|
||||
def increment_service_use(self, service_type, metric, date=date.today(),
|
||||
amount=1):
|
||||
""" Increment the services uses in monthly and daily basis"""
|
||||
self.__increment_user_uses(service_type, metric, date, amount)
|
||||
if self._orgname:
|
||||
self.__increment_organization_uses(service_type, metric, date, amount)
|
||||
self.__increment_organization_uses(service_type, metric, date,
|
||||
amount)
|
||||
|
||||
def get_metrics(self, service, metric, date_from, date_to):
|
||||
aggregated_metric = 0
|
||||
@@ -83,11 +99,12 @@ class UserMetricsService:
|
||||
service_type, metric, date)
|
||||
self._redis_connection.zincrby(redis_prefix, date.day, amount)
|
||||
|
||||
def __parse_redis_prefix(self, prefix, entity_name, service_type, metric, date):
|
||||
def __parse_redis_prefix(self, prefix, entity_name, service_type, metric,
|
||||
date):
|
||||
yearmonth_key = date.strftime('%Y%m')
|
||||
redis_name = "{0}:{1}:{2}:{3}:{4}".format(prefix, entity_name,
|
||||
service_type, metric,
|
||||
yearmonth_key)
|
||||
service_type, metric,
|
||||
yearmonth_key)
|
||||
|
||||
return redis_name
|
||||
|
||||
|
||||
@@ -16,21 +16,21 @@ class RedisConnection:
|
||||
sentinel = Sentinel([(self._config.host,
|
||||
self._config.port)],
|
||||
socket_timeout=self._config.timeout)
|
||||
return sentinel.master_for(
|
||||
self._config.sentinel_id,
|
||||
socket_timeout=self._config.timeout,
|
||||
db=self._config.db
|
||||
)
|
||||
return sentinel.master_for(self._config.sentinel_id,
|
||||
socket_timeout=self._config.timeout,
|
||||
db=self._config.db,
|
||||
retry_on_timeout=True)
|
||||
else:
|
||||
conn = StrictRedis(host=self._config.host, port=self._config.port,
|
||||
db=self._config.db)
|
||||
db=self._config.db, retry_on_timeout=True,
|
||||
socket_timeout=self._config.timeout)
|
||||
return conn
|
||||
|
||||
|
||||
class RedisDBConfig:
|
||||
|
||||
DEFAULT_USER_DB = 5
|
||||
DEFAULT_TIMEOUT = 2 # seconds
|
||||
DEFAULT_TIMEOUT = 1.5 # seconds
|
||||
|
||||
def __init__(self, key, db_conn):
|
||||
self._db_conn = db_conn
|
||||
@@ -63,8 +63,9 @@ class RedisDBConfig:
|
||||
self._sentinel_id = None
|
||||
|
||||
def __str__(self):
|
||||
return "Host: {0}, Port: {1}, Sentinel id: {2}, DB: {3}".format(
|
||||
self.host, self.port, self.sentinel_id, self.db)
|
||||
return "Host: {0}, Port: {1}, Sentinel id: {2}, DB: {3}, " \
|
||||
"Timeout: {4}".format(self.host, self.port, self.sentinel_id,
|
||||
self.db, self.timeout)
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
|
||||
@@ -10,7 +10,7 @@ from setuptools import setup, find_packages
|
||||
setup(
|
||||
name='cartodb_services',
|
||||
|
||||
version='0.3.3',
|
||||
version='0.4.5',
|
||||
|
||||
description='CartoDB Services API Python Library',
|
||||
|
||||
|
||||
@@ -3,21 +3,23 @@ from mock import Mock
|
||||
|
||||
|
||||
def build_redis_user_config(redis_conn, username, quota=100, soft_limit=False,
|
||||
service="heremaps",
|
||||
service="heremaps", isolines_quota=0,
|
||||
end_date=datetime.today()):
|
||||
user_redis_name = "rails:users:{0}".format(username)
|
||||
redis_conn.hset(user_redis_name, 'soft_geocoding_limit', soft_limit)
|
||||
redis_conn.hset(user_redis_name, 'geocoding_quota', quota)
|
||||
redis_conn.hset(user_redis_name, 'here_isolines_quota', isolines_quota)
|
||||
redis_conn.hset(user_redis_name, 'geocoder_type', service)
|
||||
redis_conn.hset(user_redis_name, 'period_end_date', end_date)
|
||||
redis_conn.hset(user_redis_name, 'google_maps_client_id', '')
|
||||
redis_conn.hset(user_redis_name, 'google_maps_api_key', '')
|
||||
|
||||
|
||||
def build_redis_org_config(redis_conn, orgname, quota=100,
|
||||
def build_redis_org_config(redis_conn, orgname, quota=100, isolines_quota=0,
|
||||
end_date=datetime.today()):
|
||||
org_redis_name = "rails:orgs:{0}".format(orgname)
|
||||
redis_conn.hset(org_redis_name, 'geocoding_quota', quota)
|
||||
redis_conn.hset(org_redis_name, 'here_isolines_quota', isolines_quota)
|
||||
redis_conn.hset(org_redis_name, 'period_end_date', end_date)
|
||||
redis_conn.hset(org_redis_name, 'google_maps_client_id', '')
|
||||
redis_conn.hset(org_redis_name, 'google_maps_api_key', '')
|
||||
@@ -44,8 +46,8 @@ def build_plpy_mock(empty=False):
|
||||
|
||||
def _plpy_execute_side_effect(*args, **kwargs):
|
||||
if args[0] == "SELECT cartodb.CDB_Conf_GetConf('heremaps_conf') as conf":
|
||||
return [{'conf': '{"app_id": "app_id", "app_code": "code", "geocoder_cost_per_hit": 1}'}]
|
||||
return [{'conf': '{"geocoder": {"app_id": "app_id", "app_code": "code", "geocoder_cost_per_hit": 1}, "isolines": {"app_id": "app_id", "app_code": "code"}}'}]
|
||||
elif args[0] == "SELECT cartodb.CDB_Conf_GetConf('mapzen_conf') as conf":
|
||||
return [{'conf': '{"routing_app_key": "app_key"}'}]
|
||||
return [{'conf': '{"routing": {"api_key": "valhalla-Z61FWEs", "monthly_quota": 1500000}, "geocoder": {"api_key": "search-d744tp0", "monthly_quota": 1500000}}'}]
|
||||
elif args[0] == "SELECT cartodb.CDB_Conf_GetConf('logger_conf') as conf":
|
||||
return [{'conf': '{"geocoder_log_path": "/dev/null"}'}]
|
||||
|
||||
109
server/lib/python/cartodb_services/test/test_mapzengeocoder.py
Normal file
109
server/lib/python/cartodb_services/test/test_mapzengeocoder.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/local/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import unittest
|
||||
import requests_mock
|
||||
|
||||
from cartodb_services.mapzen import MapzenGeocoder
|
||||
from cartodb_services.mapzen.exceptions import MalformedResult
|
||||
|
||||
requests_mock.Mocker.TEST_PREFIX = 'test_'
|
||||
|
||||
|
||||
@requests_mock.Mocker()
|
||||
class GoogleGeocoderTestCase(unittest.TestCase):
|
||||
MAPZEN_GEOCODER_URL = 'https://search.mapzen.com/v1/search'
|
||||
|
||||
EMPTY_RESPONSE = """{
|
||||
"results" : [],
|
||||
"status" : "ZERO_RESULTS"
|
||||
}"""
|
||||
|
||||
GOOD_RESPONSE = """{
|
||||
"geocoding": {
|
||||
"version": "0.1",
|
||||
"attribution": "https://search.mapzen.com/v1/attribution",
|
||||
"query": {
|
||||
"text": "Calle siempreviva 3, Valladolid",
|
||||
"parsed_text": {
|
||||
"name": "Calle siempreviva 3",
|
||||
"regions": [
|
||||
"Calle siempreviva 3",
|
||||
"Valladolid"
|
||||
],
|
||||
"admin_parts": "Valladolid"
|
||||
},
|
||||
"types": {
|
||||
"from_layers": [
|
||||
"osmaddress",
|
||||
"openaddresses"
|
||||
]
|
||||
},
|
||||
"size": 10,
|
||||
"private": false,
|
||||
"type": [
|
||||
"osmaddress",
|
||||
"openaddresses"
|
||||
],
|
||||
"querySize": 20
|
||||
},
|
||||
"engine": {
|
||||
"name": "Pelias",
|
||||
"author": "Mapzen",
|
||||
"version": "1.0"
|
||||
},
|
||||
"timestamp": 1458661873749
|
||||
},
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"id": "df7428b955ae44a39dc40d52578f61e3",
|
||||
"gid": "oa:address:df7428b955ae44a39dc40d52578f61e3",
|
||||
"layer": "address",
|
||||
"source": "oa",
|
||||
"name": "5 Close Siempreviva",
|
||||
"housenumber": "5",
|
||||
"street": "Close Siempreviva",
|
||||
"country_a": "ESP",
|
||||
"country": "Spain",
|
||||
"region": "Valladolid",
|
||||
"localadmin": "Valladolid",
|
||||
"locality": "Valladolid",
|
||||
"confidence": 0.887,
|
||||
"label": "5 Close Siempreviva, Valladolid, Spain"
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
-4.730928,
|
||||
41.669034
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}"""
|
||||
|
||||
MALFORMED_RESPONSE = """{"manolo": "escobar"}"""
|
||||
|
||||
def setUp(self):
|
||||
self.geocoder = MapzenGeocoder('search-XXXXXXX')
|
||||
|
||||
def test_geocode_address_with_valid_params(self, req_mock):
|
||||
req_mock.register_uri('GET', self.MAPZEN_GEOCODER_URL,
|
||||
text=self.GOOD_RESPONSE)
|
||||
response = self.geocoder.geocode(
|
||||
searchtext='Calle Siempreviva 3, Valldolid',
|
||||
country='ESP')
|
||||
|
||||
self.assertEqual(response[0], -4.730928)
|
||||
self.assertEqual(response[1], 41.669034)
|
||||
|
||||
def test_geocode_with_malformed_result(self, req_mock):
|
||||
req_mock.register_uri('GET', self.MAPZEN_GEOCODER_URL,
|
||||
text=self.MALFORMED_RESPONSE)
|
||||
with self.assertRaises(MalformedResult):
|
||||
self.geocoder.geocode(
|
||||
searchtext='Calle Siempreviva 3, Valladolid',
|
||||
country='ESP')
|
||||
@@ -1,7 +1,7 @@
|
||||
import test_helper
|
||||
from mockredis import MockRedis
|
||||
from cartodb_services.metrics import QuotaService
|
||||
from cartodb_services.metrics import GeocoderConfig
|
||||
from cartodb_services.metrics import GeocoderConfig, RoutingConfig
|
||||
from unittest import TestCase
|
||||
from nose.tools import assert_raises
|
||||
from datetime import datetime, date
|
||||
@@ -18,79 +18,124 @@ class TestQuotaService(TestCase):
|
||||
self.redis_conn = MockRedis()
|
||||
|
||||
def test_should_return_true_if_user_quota_with_no_use(self):
|
||||
qs = self.__build_quota_service('test_user')
|
||||
qs = self.__build_geocoder_quota_service('test_user')
|
||||
assert qs.check_user_quota() is True
|
||||
|
||||
def test_should_return_true_if_org_quota_with_no_use(self):
|
||||
qs = self.__build_quota_service('test_user', orgname='test_org')
|
||||
qs = self.__build_geocoder_quota_service('test_user',
|
||||
orgname='test_org')
|
||||
assert qs.check_user_quota() is True
|
||||
|
||||
def test_should_return_true_if_user_quota_is_not_completely_used(self):
|
||||
qs = self.__build_quota_service('test_user')
|
||||
qs = self.__build_geocoder_quota_service('test_user')
|
||||
test_helper.increment_geocoder_uses(self.redis_conn, 'test_user')
|
||||
assert qs.check_user_quota() is True
|
||||
|
||||
def test_should_return_true_if_org_quota_is_not_completely_used(self):
|
||||
qs = self.__build_quota_service('test_user', orgname='test_org')
|
||||
qs = self.__build_geocoder_quota_service('test_user',
|
||||
orgname='test_org')
|
||||
test_helper.increment_geocoder_uses(self.redis_conn, 'test_user',
|
||||
orgname='test_org')
|
||||
assert qs.check_user_quota() is True
|
||||
|
||||
def test_should_return_false_if_user_quota_is_surpassed(self):
|
||||
qs = self.__build_quota_service('test_user')
|
||||
qs = self.__build_geocoder_quota_service('test_user')
|
||||
test_helper.increment_geocoder_uses(self.redis_conn, 'test_user',
|
||||
amount=300)
|
||||
assert qs.check_user_quota() is False
|
||||
|
||||
def test_should_return_false_if_org_quota_is_surpassed(self):
|
||||
qs = self.__build_quota_service('test_user', orgname='test_org')
|
||||
qs = self.__build_geocoder_quota_service('test_user',
|
||||
orgname='test_org')
|
||||
test_helper.increment_geocoder_uses(self.redis_conn, 'test_user',
|
||||
orgname='test_org', amount=400)
|
||||
assert qs.check_user_quota() is False
|
||||
|
||||
def test_should_return_true_if_user_quota_is_surpassed_but_soft_limit_is_enabled(self):
|
||||
qs = self.__build_quota_service('test_user', soft_limit=True)
|
||||
qs = self.__build_geocoder_quota_service('test_user', soft_limit=True)
|
||||
test_helper.increment_geocoder_uses(self.redis_conn, 'test_user',
|
||||
amount=300)
|
||||
assert qs.check_user_quota() is True
|
||||
|
||||
def test_should_return_true_if_org_quota_is_surpassed_but_soft_limit_is_enabled(self):
|
||||
qs = self.__build_quota_service('test_user', orgname='test_org',
|
||||
soft_limit=True)
|
||||
qs = self.__build_geocoder_quota_service('test_user',
|
||||
orgname='test_org',
|
||||
soft_limit=True)
|
||||
test_helper.increment_geocoder_uses(self.redis_conn, 'test_user',
|
||||
orgname='test_org', amount=400)
|
||||
assert qs.check_user_quota() is True
|
||||
|
||||
def test_should_check_user_increment_and_quota_check_correctly(self):
|
||||
qs = self.__build_quota_service('test_user', quota=2)
|
||||
qs = self.__build_geocoder_quota_service('test_user', quota=2)
|
||||
qs.increment_success_service_use()
|
||||
assert qs.check_user_quota() is True
|
||||
qs.increment_success_service_use(amount=2)
|
||||
assert qs.check_user_quota() is False
|
||||
month = date.today().strftime('%Y%m')
|
||||
|
||||
def test_should_check_org_increment_and_quota_check_correctly(self):
|
||||
qs = self.__build_quota_service('test_user', orgname='test_org',
|
||||
quota=2)
|
||||
qs = self.__build_geocoder_quota_service('test_user', quota=2,
|
||||
orgname='test_org')
|
||||
qs.increment_success_service_use()
|
||||
assert qs.check_user_quota() is True
|
||||
qs.increment_success_service_use(amount=2)
|
||||
assert qs.check_user_quota() is False
|
||||
month = date.today().strftime('%Y%m')
|
||||
|
||||
def __build_quota_service(self, username, quota=100, service='heremaps',
|
||||
orgname=None, soft_limit=False,
|
||||
end_date = datetime.today()):
|
||||
def test_should_check_user_mapzen_geocoder_quota_correctly(self):
|
||||
qs = self.__build_geocoder_quota_service('test_user', service='mapzen')
|
||||
qs.increment_success_service_use()
|
||||
assert qs.check_user_quota() is True
|
||||
qs.increment_success_service_use(amount=1500000)
|
||||
assert qs.check_user_quota() is False
|
||||
|
||||
def test_should_check_org_mapzen_geocoder_quota_correctly(self):
|
||||
qs = self.__build_geocoder_quota_service('test_user', orgname='testorg',
|
||||
service='mapzen')
|
||||
qs.increment_success_service_use()
|
||||
assert qs.check_user_quota() is True
|
||||
qs.increment_success_service_use(amount=1500000)
|
||||
assert qs.check_user_quota() is False
|
||||
|
||||
def test_should_check_user_routing_quota_correctly(self):
|
||||
qs = self.__build_routing_quota_service('test_user')
|
||||
qs.increment_success_service_use()
|
||||
assert qs.check_user_quota() is True
|
||||
qs.increment_success_service_use(amount=1500000)
|
||||
assert qs.check_user_quota() is False
|
||||
|
||||
def test_should_check_org_routing_quota_correctly(self):
|
||||
qs = self.__build_routing_quota_service('test_user', orgname='testorg')
|
||||
qs.increment_success_service_use()
|
||||
assert qs.check_user_quota() is True
|
||||
qs.increment_success_service_use(amount=1500000)
|
||||
assert qs.check_user_quota() is False
|
||||
|
||||
def __prepare_quota_service(self, username, quota, service, orgname,
|
||||
soft_limit, end_date):
|
||||
test_helper.build_redis_user_config(self.redis_conn, username,
|
||||
quota = quota, service = service,
|
||||
soft_limit = soft_limit,
|
||||
end_date = end_date)
|
||||
quota=quota, service=service,
|
||||
soft_limit=soft_limit,
|
||||
end_date=end_date)
|
||||
if orgname:
|
||||
test_helper.build_redis_org_config(self.redis_conn, orgname,
|
||||
quota=quota, end_date=end_date)
|
||||
plpy_mock = test_helper.build_plpy_mock()
|
||||
geocoder_config = GeocoderConfig(self.redis_conn, plpy_mock,
|
||||
username, orgname)
|
||||
return QuotaService(geocoder_config,
|
||||
redis_connection = self.redis_conn)
|
||||
self._plpy_mock = test_helper.build_plpy_mock()
|
||||
|
||||
def __build_geocoder_quota_service(self, username, quota=100,
|
||||
service='heremaps', orgname=None,
|
||||
soft_limit=False,
|
||||
end_date=datetime.today()):
|
||||
self.__prepare_quota_service(username, quota, service, orgname,
|
||||
soft_limit, end_date)
|
||||
geocoder_config = GeocoderConfig(self.redis_conn, self._plpy_mock,
|
||||
username, orgname)
|
||||
return QuotaService(geocoder_config, redis_connection=self.redis_conn)
|
||||
|
||||
def __build_routing_quota_service(self, username, quota=100,
|
||||
service='routing_mapzen', orgname=None,
|
||||
soft_limit=False,
|
||||
end_date=datetime.today()):
|
||||
self.__prepare_quota_service(username, quota, service, orgname,
|
||||
soft_limit, end_date)
|
||||
routing_config = RoutingConfig(self.redis_conn, self._plpy_mock,
|
||||
username, orgname)
|
||||
return QuotaService(routing_config, redis_connection=self.redis_conn)
|
||||
|
||||
@@ -87,5 +87,5 @@ class TestUserService(TestCase):
|
||||
quota=quota, end_date=end_date)
|
||||
plpy_mock = test_helper.build_plpy_mock()
|
||||
geocoder_config = GeocoderConfig(self.redis_conn, plpy_mock,
|
||||
username, orgname,)
|
||||
username, orgname)
|
||||
return UserMetricsService(geocoder_config, self.redis_conn)
|
||||
|
||||
Reference in New Issue
Block a user