Merge pull request #65 from IndicoDataSolutions/development

Development
This commit is contained in:
Madison May
2015-06-11 18:22:44 -04:00
7 changed files with 102 additions and 20 deletions
+1
View File
@@ -24,3 +24,4 @@ v0.5.2, Tue March 7 -- Required API keys, configuration settings
v0.5.3, Wed Apr 15 -- Added scipy to requirements, edited Readme to not break pypi page
v0.6.0, Thu May 29 -- Remove numpy / scipy dependency in favor of Pillow
v0.7.0, Tue Jun 9 -- Added support for calling multiple APIs in a single function and accepting filenames as image API inputs
v0.7.1 Thu Jun 11 -- High quality sentiment API for private beta, fix for multi API support
+2 -2
View File
@@ -1,6 +1,6 @@
from functools import partial
Version, version, __version__, VERSION = ('0.7.0',) * 4
Version, version, __version__, VERSION = ('0.7.1',) * 4
JSON_HEADERS = {
'Content-type': 'application/json',
@@ -9,7 +9,7 @@ JSON_HEADERS = {
'version-number': VERSION
}
from indicoio.text.sentiment import political, posneg
from indicoio.text.sentiment import political, posneg, sentiment_hq
from indicoio.text.sentiment import posneg as sentiment
from indicoio.text.lang import language
from indicoio.text.tagging import text_tags
+2 -1
View File
@@ -49,7 +49,8 @@ TEXT_APIS = [
'text_tags',
'political',
'sentiment',
'language'
'language',
'sentiment_hq'
]
IMAGE_APIS = [
+23
View File
@@ -50,3 +50,26 @@ def posneg(text, cloud=None, batch=False, api_key=None, **kwargs):
"""
return api_handler(text, cloud=cloud, api="sentiment", url_params={"batch":batch, "api_key":api_key}, **kwargs)
def sentiment_hq(text, cloud=None, batch=False, api_key=None, **kwargs):
"""
Given input text, returns a scalar estimate of the sentiment of that text.
Values are roughly in the range 0 to 1 with 0.5 indicating neutral sentiment.
For reference, 0 suggests very negative sentiment and 1 suggests very positive sentiment.
Example usage:
.. code-block:: python
>>> from indicoio import sentimenthq
>>> text = 'Thanks everyone for the birthday wishes!! It was a crazy few days ><'
>>> sentiment = sentimenthq(text)
>>> sentiment
0.6210052967071533
:param text: The text to be analyzed.
:type text: str or unicode
:rtype: Float
"""
return api_handler(text, cloud=cloud, api="sentimenthq", url_params={"batch":batch, "api_key":api_key}, **kwargs)
+9 -2
View File
@@ -17,6 +17,7 @@ def api_handler(arg, cloud, api, url_params = {"batch":False, "api_key":None}, *
if cloud:
host = "%s.indico.domains" % cloud
else:
# default to indico public cloud
host = config.PUBLIC_API_HOST
@@ -24,8 +25,14 @@ def api_handler(arg, cloud, api, url_params = {"batch":False, "api_key":None}, *
url = config.url_protocol + "//%s/%s" % (host, api)
url = url + "/batch" if url_params.get("batch", False) else url
url += "?key=%s" % (url_params.get("api_key", None) or config.api_key)
if "apis" in url_params:
url += "&apis=%s" % ",".join(url_params["apis"])
apis = url_params.get("apis", [])
if apis:
url += "&apis=%s" % ",".join(apis)
# private beta
if host == config.PUBLIC_API_HOST:
if (api == 'sentimenthq') or ('sentimenthq' in apis):
raise IndicoError("The high quality sentiment API is currently in private beta.")
response = requests.post(url, data=json_data, headers=JSON_HEADERS)
if response.status_code == 503 and cloud != None:
+41 -15
View File
@@ -6,8 +6,12 @@ from indicoio.utils.errors import IndicoError
CLIENT_SERVER_MAP = dict((api, api.strip().replace("_", "").lower()) for api in API_NAMES)
SERVER_CLIENT_MAP = dict((v, k) for k, v in CLIENT_SERVER_MAP.iteritems())
AVAILABLE_APIS = {
'text': TEXT_APIS,
'image': IMAGE_APIS
}
def multi(data, type, apis, available, batch=False, **kwargs):
def multi(data, datatype, apis, batch=False, **kwargs):
"""
Helper to make multi requests of different types.
@@ -22,23 +26,39 @@ def multi(data, type, apis, available, batch=False, **kwargs):
:rtype: Dictionary of api responses
"""
# Client side api name checking - strictly only accept func name api
available = AVAILABLE_APIS.get(datatype)
invalid_apis = [api for api in apis if api not in available]
if invalid_apis:
raise IndicoError("%s are not valid %s APIs. Please reference the available APIs below:\n%s"
% (", ".join(invalid_apis), type, ", ".join(available))
)
raise IndicoError(
"%s are not valid %s APIs. Please reference the available APIs below:\n%s"
% (", ".join(invalid_apis), datatype, ", ".join(available))
)
# Convert client api names to server names before sending request
apis = map(CLIENT_SERVER_MAP.get, apis)
result = api_handler(data, url_params = {"apis":apis, "batch":batch}, **kwargs)
cloud = kwargs.pop("cloud", None)
api_key = kwargs.pop('api_key', None)
result = api_handler(
data,
cloud=cloud,
api='apis',
url_params={
"apis":apis,
"batch":batch,
"api_key":api_key
},
**kwargs
)
return handle_response(result)
def handle_response(result):
# Parse out the results to a dicionary of api: result
return dict((SERVER_CLIENT_MAP[api], parsed_response(api, res))
for api, res in result.iteritems())
def predict_text(input_text, apis=TEXT_APIS, cloud=None, batch=False, api_key=None, **kwargs):
def predict_text(input_text, apis=TEXT_APIS, **kwargs):
"""
Given input text, returns the results of specified text apis. Possible apis
include: [ 'text_tags', 'political', 'sentiment', 'language' ]
@@ -60,19 +80,22 @@ def predict_text(input_text, apis=TEXT_APIS, cloud=None, batch=False, api_key=No
:rtype: Dictionary of api responses
"""
cloud = kwargs.pop('cloud', None)
batch = kwargs.pop('batch', False)
api_key = kwargs.pop('api_key', None)
return multi(
api="apis",
data=input_text,
type="text",
available = TEXT_APIS,
datatype="text",
cloud=cloud,
batch=batch,
api_key=api_key,
apis=apis,
**kwargs)
**kwargs
)
def predict_image(image, apis=IMAGE_APIS, cloud=None, batch=False, api_key=None, **kwargs):
def predict_image(image, apis=IMAGE_APIS, **kwargs):
"""
Given input image, returns the results of specified image apis. Possible apis
include: ['fer', 'facial_features', 'image_features']
@@ -95,16 +118,19 @@ def predict_image(image, apis=IMAGE_APIS, cloud=None, batch=False, api_key=None,
:rtype: Dictionary of api responses
"""
cloud = kwargs.pop('cloud', None)
batch = kwargs.pop('batch', False)
api_key = kwargs.pop('api_key', None)
return multi(
api="apis",
data=image_preprocess(image, batch=batch),
type="image",
available=IMAGE_APIS,
datatype="image",
cloud=cloud,
batch=batch,
api_key=api_key,
apis=apis,
**kwargs)
**kwargs
)
def parsed_response(api, response):
result = response.get('results', False)
+24
View File
@@ -9,9 +9,13 @@ from indicoio import config
from indicoio import political, sentiment, fer, facial_features, language, image_features, text_tags
from indicoio import batch_political, batch_sentiment, batch_fer, batch_facial_features
from indicoio import batch_language, batch_image_features, batch_text_tags
from indicoio import sentiment_hq, batch_sentiment_hq
from indicoio import predict_image, predict_text, batch_predict_image, batch_predict_text
from indicoio.utils.errors import IndicoError
# TODO: remove once sentiment_hq is added to the public API
config.TEXT_APIS.remove("sentiment_hq")
DIR = os.path.dirname(os.path.realpath(__file__))
class BatchAPIRun(unittest.TestCase):
@@ -37,6 +41,13 @@ class BatchAPIRun(unittest.TestCase):
self.assertTrue(isinstance(response, list))
self.assertTrue(response[0] < 0.5)
# TODO: uncomment once the high quality sentiment API is publicly released
# def test_batch_sentiment_hq(self):
# test_data = ['Worst song ever', 'Best song ever']
# response = batch_sentiment_hq(test_data, api_key=self.api_key)
# self.assertTrue(isinstance(response, list))
# self.assertTrue(response[0] < 0.5)
def test_batch_political(self):
test_data = ["Guns don't kill people, people kill people."]
response = batch_political(test_data, api_key=self.api_key)
@@ -220,6 +231,19 @@ class FullAPIRun(unittest.TestCase):
self.assertTrue(isinstance(response, float))
self.assertTrue(response > 0.5)
# TODO: uncomment when the high quality sentiment API is publicly released
# def test_sentiment_hq(self):
# test_string = "Worst song ever."
# response = sentiment_hq(test_string)
# self.assertTrue(isinstance(response, float))
# self.assertTrue(response < 0.5)
# test_string = "Best song ever."
# response = sentiment_hq(test_string)
# self.assertTrue(isinstance(response, float))
# self.assertTrue(response > 0.5)
def test_good_fer(self):
fer_set = set(['Angry', 'Sad', 'Neutral', 'Surprise', 'Fear', 'Happy'])
test_face = generate_array((48,48))