Merge pull request #116 from IndicoDataSolutions/development

Development
This commit is contained in:
Madison May
2015-08-14 14:33:12 -04:00
7 changed files with 115 additions and 29 deletions
+2
View File
@@ -34,3 +34,5 @@ v0.8.0 Fri Jul 10 -- Add Content Filtering API, Named Entities API, Facial Emoti
v0.8.1 Wed Jul 22 -- Add Sentiment HQ to predict_text API v0.8.1 Wed Jul 22 -- Add Sentiment HQ to predict_text API
v0.9.0 Tue Jul 28 -- Deprecate batch function calls in favor of type inference v0.9.0 Tue Jul 28 -- Deprecate batch function calls in favor of type inference
v0.9.1 Mon Aug 3 -- Add Facial Localization API, image resizing updates v0.9.1 Mon Aug 3 -- Add Facial Localization API, image resizing updates
v0.9.2 Fri Aug 14 -- Add intersections API, analyzeText, analyzeImage
+2 -2
View File
@@ -1,7 +1,7 @@
from functools import wraps, partial from functools import wraps, partial
import warnings import warnings
Version, version, __version__, VERSION = ('0.9.1',) * 4 Version, version, __version__, VERSION = ('0.9.2',) * 4
JSON_HEADERS = { JSON_HEADERS = {
'Content-type': 'application/json', 'Content-type': 'application/json',
@@ -22,7 +22,7 @@ from indicoio.images.features import facial_features
from indicoio.images.faciallocalization import facial_localization from indicoio.images.faciallocalization import facial_localization
from indicoio.images.features import image_features from indicoio.images.features import image_features
from indicoio.images.filtering import content_filtering from indicoio.images.filtering import content_filtering
from indicoio.utils.multi import predict_image, predict_text from indicoio.utils.multi import analyze_image, analyze_text, intersections
from indicoio.config import API_NAMES from indicoio.config import API_NAMES
+7 -1
View File
@@ -63,7 +63,13 @@ IMAGE_APIS = [
'content_filtering' 'content_filtering'
] ]
API_NAMES = IMAGE_APIS + TEXT_APIS + ["predict_text", "predict_image"] OTHER_APIS = [
"analyze_text",
"analyze_image",
"intersections"
]
API_NAMES = IMAGE_APIS + TEXT_APIS + OTHER_APIS
SETTINGS = Settings(files=[ SETTINGS = Settings(files=[
os.path.expanduser("~/.indicorc"), os.path.expanduser("~/.indicorc"),
-1
View File
@@ -19,7 +19,6 @@ def api_handler(arg, cloud, api, url_params=None, **kwargs):
cloud = cloud or config.cloud cloud = cloud or config.cloud
host = "%s.indico.domains" % cloud if cloud else config.PUBLIC_API_HOST host = "%s.indico.domains" % cloud if cloud else config.PUBLIC_API_HOST
url = create_url(host, api, url_params) url = create_url(host, api, url_params)
response = requests.post(url, data=json_data, headers=JSON_HEADERS) response = requests.post(url, data=json_data, headers=JSON_HEADERS)
if response.status_code == 503 and cloud != None: if response.status_code == 503 and cloud != None:
+54 -15
View File
@@ -11,19 +11,59 @@ AVAILABLE_APIS = {
'image': IMAGE_APIS 'image': IMAGE_APIS
} }
def invert_dictionary(d):
return {
element: key for key, values in d.iteritems()
for element in values
}
API_TYPES = invert_dictionary(AVAILABLE_APIS)
def intersections(data, apis = None, **kwargs):
"""
Helper to make multi requests of different types.
:param data: Data to be sent in API request
:param type: String type of API request
:rtype: Dictionary of api responses
"""
# Client side api name checking
# remove auto-inserted batch param
kwargs.pop('batch', None)
if not isinstance(apis, list) or len(apis) != 2:
raise IndicoError("Argument 'apis' must be of length 2")
if isinstance(data, list) and len(data) < 3:
raise IndicoError(
"At least 3 examples are required to use the intersections API"
)
api_types = map(API_TYPES.get, apis)
if api_types[0] != api_types[1]:
raise IndicoError(
"Both `apis` must accept the same kind of input to use the intersections API"
)
cloud = kwargs.get("cloud", None)
url_params = {
'batch': False,
'api_key': kwargs.pop('api_key', None),
'apis': apis
}
return api_handler(data, cloud=cloud, api="apis/intersections", url_params=url_params, **kwargs)
def multi(data, datatype, apis, batch=False, **kwargs): def multi(data, datatype, apis, batch=False, **kwargs):
""" """
Helper to make multi requests of different types. Helper to make multi requests of different types.
:param data: data to be sent in JSON. :param data: Data to be sent in API request
:param type: String type of API request :param datatype: String type of API request
:param apis: List of apis to use. :param apis: List of apis to use.
:param apis: List of apis available for use. :param batch: Is this a batch request?
:type data: str or image
:type type: str or unicode
:type apis: list of str
:type available: list of str
:rtype: Dictionary of api responses :rtype: Dictionary of api responses
""" """
# Client side api name checking - strictly only accept func name api # Client side api name checking - strictly only accept func name api
@@ -36,13 +76,12 @@ def multi(data, datatype, apis, batch=False, **kwargs):
) )
# Convert client api names to server names before sending request # Convert client api names to server names before sending request
apis = map(CLIENT_SERVER_MAP.get, apis)
cloud = kwargs.pop("cloud", None) cloud = kwargs.pop("cloud", None)
api_key = kwargs.pop('api_key', None) api_key = kwargs.pop('api_key', None)
result = api_handler( result = api_handler(
data, data,
cloud=cloud, cloud=cloud,
api='apis', api='apis/multiapi',
url_params={ url_params={
"apis":apis, "apis":apis,
"batch":batch, "batch":batch,
@@ -55,11 +94,11 @@ def multi(data, datatype, apis, batch=False, **kwargs):
def handle_response(result): def handle_response(result):
# Parse out the results to a dicionary of api: result # Parse out the results to a dicionary of api: result
return dict((SERVER_CLIENT_MAP[api], parsed_response(api, res)) return dict((api, parsed_response(api, res))
for api, res in result.iteritems()) for api, res in result.iteritems())
def predict_text(input_text, apis=TEXT_APIS, **kwargs): def analyze_text(input_text, apis=TEXT_APIS, **kwargs):
""" """
Given input text, returns the results of specified text apis. Possible apis Given input text, returns the results of specified text apis. Possible apis
include: [ 'text_tags', 'political', 'sentiment', 'language' ] include: [ 'text_tags', 'political', 'sentiment', 'language' ]
@@ -70,8 +109,8 @@ def predict_text(input_text, apis=TEXT_APIS, **kwargs):
>>> import indicoio >>> import indicoio
>>> text = 'Monday: Delightful with mostly sunny skies. Highs in the low 70s.' >>> text = 'Monday: Delightful with mostly sunny skies. Highs in the low 70s.'
>>> results = indicoio.text(data = text, apis = ["language", "sentiment"]) >>> results = indicoio.analyze_text(data = text, apis = ["language", "sentiment"])
>>> language_results = results["langauge"] >>> language_results = results["language"]
>>> sentiment_results = results["sentiment"] >>> sentiment_results = results["sentiment"]
:param text: The text to be analyzed. :param text: The text to be analyzed.
@@ -96,7 +135,7 @@ def predict_text(input_text, apis=TEXT_APIS, **kwargs):
) )
def predict_image(image, apis=IMAGE_APIS, **kwargs): def analyze_image(image, apis=IMAGE_APIS, **kwargs):
""" """
Given input image, returns the results of specified image apis. Possible apis Given input image, returns the results of specified image apis. Possible apis
include: ['fer', 'facial_features', 'image_features'] include: ['fer', 'facial_features', 'image_features']
@@ -108,7 +147,7 @@ def predict_image(image, apis=IMAGE_APIS, **kwargs):
>>> import indicoio >>> import indicoio
>>> import numpy as np >>> import numpy as np
>>> face = np.zeros((48,48)).tolist() >>> face = np.zeros((48,48)).tolist()
>>> results = indicoio.image(image = face, apis = ["fer", "facial_features"]) >>> results = indicoio.analyze_image(image = face, apis = ["fer", "facial_features"])
>>> fer = results["fer"] >>> fer = results["fer"]
>>> facial_features = results["facial_features"] >>> facial_features = results["facial_features"]
+1 -1
View File
@@ -9,7 +9,7 @@ except ImportError:
setup( setup(
name="IndicoIo", name="IndicoIo",
version="0.9.1", version="0.9.2",
packages=[ packages=[
"indicoio", "indicoio",
"indicoio.text", "indicoio.text",
+49 -9
View File
@@ -15,7 +15,7 @@ from indicoio import keywords, batch_keywords
from indicoio import sentiment_hq, batch_sentiment_hq from indicoio import sentiment_hq, batch_sentiment_hq
from indicoio import twitter_engagement, batch_twitter_engagement from indicoio import twitter_engagement, batch_twitter_engagement
from indicoio import named_entities, batch_named_entities from indicoio import named_entities, batch_named_entities
from indicoio import predict_image, predict_text, batch_predict_image, batch_predict_text from indicoio import intersections, analyze_image, analyze_text, batch_analyze_image, batch_analyze_text
from indicoio.utils.errors import IndicoError from indicoio.utils.errors import IndicoError
DIR = os.path.dirname(os.path.realpath(__file__)) DIR = os.path.dirname(os.path.realpath(__file__))
@@ -158,7 +158,7 @@ class BatchAPIRun(unittest.TestCase):
def test_batch_multi_api_image(self): def test_batch_multi_api_image(self):
test_data = [os.path.normpath(os.path.join(DIR, "data/48by48.png")), test_data = [os.path.normpath(os.path.join(DIR, "data/48by48.png")),
os.path.normpath(os.path.join(DIR, "data/48by48.png"))] os.path.normpath(os.path.join(DIR, "data/48by48.png"))]
response = predict_image(test_data, apis=config.IMAGE_APIS, api_key=self.api_key) response = analyze_image(test_data, apis=config.IMAGE_APIS, api_key=self.api_key)
self.assertTrue(isinstance(response, dict)) self.assertTrue(isinstance(response, dict))
self.assertTrue(set(response.keys()) == set(config.IMAGE_APIS)) self.assertTrue(set(response.keys()) == set(config.IMAGE_APIS))
@@ -166,32 +166,32 @@ class BatchAPIRun(unittest.TestCase):
def test_batch_multi_api_text(self): def test_batch_multi_api_text(self):
test_data = ['clearly an english sentence'] test_data = ['clearly an english sentence']
response = predict_text(test_data, apis=config.TEXT_APIS, api_key=self.api_key) response = analyze_text(test_data, apis=config.TEXT_APIS, api_key=self.api_key)
self.assertTrue(isinstance(response, dict)) self.assertTrue(isinstance(response, dict))
self.assertTrue(set(response.keys()) == set(config.TEXT_APIS)) self.assertTrue(set(response.keys()) == set(config.TEXT_APIS))
def test_default_multi_api_text(self): def test_default_multi_api_text(self):
test_data = ['clearly an english sentence'] test_data = ['clearly an english sentence']
response = predict_text(test_data, api_key=self.api_key) response = analyze_text(test_data, api_key=self.api_key)
self.assertTrue(isinstance(response, dict)) self.assertTrue(isinstance(response, dict))
self.assertTrue(set(response.keys()) == set(config.TEXT_APIS)) self.assertTrue(set(response.keys()) == set(config.TEXT_APIS))
def test_multi_api_bad_api(self): def test_multi_api_bad_api(self):
self.assertRaises(IndicoError, self.assertRaises(IndicoError,
predict_text, analyze_text,
"this shouldn't work", "this shouldn't work",
apis=["sentiment", "somethingbad"]) apis=["sentiment", "somethingbad"])
def test_multi_bad_mixed_api(self): def test_multi_bad_mixed_api(self):
self.assertRaises(IndicoError, self.assertRaises(IndicoError,
predict_text, analyze_text,
"this shouldn't work", "this shouldn't work",
apis=["fer", "sentiment", "facial_features"]) apis=["fer", "sentiment", "facial_features"])
def test_batch_multi_bad_mixed_api(self): def test_batch_multi_bad_mixed_api(self):
self.assertRaises(IndicoError, self.assertRaises(IndicoError,
predict_text, analyze_text,
["this shouldn't work"], ["this shouldn't work"],
apis=["fer", "sentiment", "facial_features"]) apis=["fer", "sentiment", "facial_features"])
@@ -470,18 +470,58 @@ class FullAPIRun(unittest.TestCase):
def test_multi_api_image(self): def test_multi_api_image(self):
test_data = os.path.normpath(os.path.join(DIR, "data/48by48.png")) test_data = os.path.normpath(os.path.join(DIR, "data/48by48.png"))
response = predict_image(test_data, apis=config.IMAGE_APIS, api_key=self.api_key) response = analyze_image(test_data, apis=config.IMAGE_APIS, api_key=self.api_key)
self.assertTrue(isinstance(response, dict)) self.assertTrue(isinstance(response, dict))
self.assertTrue(set(response.keys()) == set(config.IMAGE_APIS)) self.assertTrue(set(response.keys()) == set(config.IMAGE_APIS))
def test_multi_api_text(self): def test_multi_api_text(self):
test_data = 'clearly an english sentence' test_data = 'clearly an english sentence'
response = predict_text(test_data, apis=config.TEXT_APIS, api_key=self.api_key) response = analyze_text(test_data, apis=config.TEXT_APIS, api_key=self.api_key)
self.assertTrue(isinstance(response, dict)) self.assertTrue(isinstance(response, dict))
self.assertTrue(set(response.keys()) == set(config.TEXT_APIS)) self.assertTrue(set(response.keys()) == set(config.TEXT_APIS))
def test_intersections_not_enough_data(self):
test_data = ['test_Data']
self.assertRaises(
IndicoError,
intersections,
test_data,
apis=['text_tags', 'sentiment']
)
def test_intersections_wrong_number_of_apis(self):
test_data = ['test data']*3
self.assertRaises(
IndicoError,
intersections,
test_data,
apis=['text_tags', 'sentiment', 'language']
)
def test_intersections_bad_api_type(self):
test_data = ['test data']*3
self.assertRaises(
IndicoError,
intersections,
test_data,
apis=['text_tags', 'fer']
)
def test_intersections_valid_input(self):
test_data = ['test data']*3
apis = ['text_tags', 'sentiment']
results = intersections(test_data, apis=apis)
assert set(results.keys()) < set(apis)
def test_intersections_valid_raw_input(self):
test_data = {
'sentiment': [0.1, 0.2, 0.3],
'twitter_engagement': [0.1, 0.2, 0.3]
}
results = intersections(test_data, apis=['sentiment', 'twitter_engagement'])
assert set(results.keys()) < set(test_data.keys())
def test_language(self): def test_language(self):
language_set = set([ language_set = set([