diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f08471e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +language: python +python: + - 2.7 + +# Setup anaconda +before_install: + - wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh + - chmod +x miniconda.sh + - ./miniconda.sh -b + - export PATH=/home/travis/miniconda/bin:$PATH + - conda update --yes conda + # The next couple lines fix a crash with multiprocessing on Travis and are not specific to using Miniconda + - sudo rm -rf /dev/shm + - sudo ln -s /run/shm /dev/shm + +# Install packages +install: + - conda install --yes python=$TRAVIS_PYTHON_VERSION atlas numpy scipy matplotlib nose dateutil pandas statsmodels requests requests six scikit-image + - python setup.py install + +# Run test +script: + - nosetests -w ./tests/remote + \ No newline at end of file diff --git a/AUTHORS b/AUTHORS index 65ce50e..2aed9bb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,4 @@ -Slater Victoroff -Alec Radford -Aidan McLaughlin +Slater Victoroff +Alec Radford +Aidan McLaughlin +Madison May diff --git a/CHANGES.txt b/CHANGES.txt index 79f07c8..3a6f273 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,4 +9,5 @@ v0.4.0, Fri Aug 1 -- Changed api import to lowercase, added language example v0.4.1, Fri Aug 1 -- Updated __version__ variable to be accurate v0.4.2, Wed Aug 6 -- Updated README to accurately reflect political analysis results v0.4.3, Thu Sep 11 -- Added image features api and sphinx compliant documentation -v0.4.4, Thu Sep 25 -- Added dependencies installation to setup.py \ No newline at end of file +v0.4.4, Thu Sep 25 -- Added dependencies installation to setup.py +v0.4.5, Thu Sep 25 -- Added interface to local indico server diff --git a/README b/README index 12869f1..95cec92 100644 --- a/README +++ b/README @@ -55,7 +55,12 @@ Examples >>> sorted(language_dict.keys(), key=lambda x: language_dict[x], reverse=True) [u'Latin', u'Dutch', u'Greek', u'Portuguese', u'Spanish', u'Chinese', u'Lithuanian', u'Korean', u'Finnish', u'Persian (Farsi)', u'Hungarian', u'Danish', u'Norwegian', u'Japanese', u'Tagalog', u'Turkish', u'Swedish', u'Vietnamese', u'Russian', u'Esperanto', u'French', u'Arabic', u'English', u'German', u'Czech', u'Polish', u'Slovak', u'Thai', u'Bulgarian', u'Hebrew', u'Romanian', u'Italian', u'Indonesian'] +``` +If you have a local indico server running, simply import from `indicoio.local`. + +``` +>>> from indicoio.local import political, sentiment, fer, facial_features, language ``` Installation diff --git a/README.md b/README.md index 9b063c2..272b0bd 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Examples Local -------- -When using a local version of the api you must remember to import '.local', otherwise it functions the exact same as the remote api documented above. +When using a local version of the api you must remember to import '.local', otherwise it functions the exact same as the remote api documented above. Local API versions are not yet available at this point in time. ``` >>> from indicoio.local import sentiment @@ -71,11 +71,10 @@ When using a local version of the api you must remember to import '.local', othe ``` - Installation ------------ ``` pip install indicoio ``` -Announcement: Indico has partnered with Experfy, a data science consulting marketplace based in the Harvard Innovation Lab. Through Experfy, we are helping our data science community members find lucrative projects and advance their skills. Please signup for Experfy at https://www.experfy.com/ to get started. +Announcement: indico has partnered with Experfy, a data science consulting marketplace based in the Harvard Innovation Lab. Through Experfy, we are helping our data science community members find lucrative projects and advance their skills. Please signup for Experfy at https://www.experfy.com/ to get started. diff --git a/indicoio/__init__.py b/indicoio/__init__.py index 0646c43..d92b8ba 100644 --- a/indicoio/__init__.py +++ b/indicoio/__init__.py @@ -1,16 +1,16 @@ from functools import partial -from utils import config +import indicoio.config as config JSON_HEADERS = {'Content-type': 'application/json', 'Accept': 'text/plain'} Version, version, __version__, VERSION = ('0.4.5',) * 4 -from text.sentiment import political, posneg -from text.sentiment import posneg as sentiment -from text.lang import language -from images.fer import fer -from images.features import facial_features -from images.features import image_features +from indicoio.text.sentiment import political, posneg +from indicoio.text.sentiment import posneg as sentiment +from indicoio.text.lang import language +from indicoio.images.fer import fer +from indicoio.images.features import facial_features +from indicoio.images.features import image_features political = partial(political, config.api_root) posneg = partial(posneg, config.api_root) diff --git a/indicoio/utils/config.py b/indicoio/config.py similarity index 53% rename from indicoio/utils/config.py rename to indicoio/config.py index f6b622e..150d0f9 100644 --- a/indicoio/utils/config.py +++ b/indicoio/config.py @@ -1,2 +1,2 @@ local_api_root = "http://localhost:9438/" -api_root = "http://api.indico.io/" \ No newline at end of file +api_root = "http://apiv1.indico.io/" \ No newline at end of file diff --git a/indicoio/images/features.py b/indicoio/images/features.py index cd56087..f7c0f2a 100644 --- a/indicoio/images/features.py +++ b/indicoio/images/features.py @@ -3,8 +3,7 @@ import json import requests import numpy as np -from indicoio import JSON_HEADERS -from indicoio.utils import image_preprocess +from indicoio.utils import image_preprocess, api_handler def facial_features(api_root, image): """ @@ -28,14 +27,7 @@ def facial_features(api_root, image): :type image: list of lists :rtype: List containing feature responses """ - - data_dict = json.dumps({"face": image}) - response = requests.post(api_root + "facialfeatures", data=data_dict, headers=JSON_HEADERS) - response_dict = response.json() - if 'response' not in response_dict: - raise ValueError(response_dict.values()[0]) - else: - return response_dict['response'] + return api_handler(image, api_root + "facialfeatures") def image_features(api_root, image): """ @@ -68,10 +60,4 @@ def image_features(api_root, image): :rtype: List containing features """ image = image_preprocess(image) - data_dict = json.dumps({"image": image}) - response = requests.post(api_root + "imagefeatures", data=data_dict, headers=JSON_HEADERS) - response_dict = response.json() - if 'Features' not in response_dict: - raise ValueError(response_dict.values()[0]) - else: - return response_dict['Features'] \ No newline at end of file + return api_handler(image, api_root + "imagefeatures") diff --git a/indicoio/images/fer.py b/indicoio/images/fer.py index 27f8162..1e600aa 100644 --- a/indicoio/images/fer.py +++ b/indicoio/images/fer.py @@ -2,7 +2,7 @@ import json import requests import numpy as np -from indicoio import JSON_HEADERS +from indicoio.utils import api_handler def fer(api_root, image): """ @@ -28,10 +28,4 @@ def fer(api_root, image): :rtype: Dictionary containing emotion probability pairs """ - data_dict = json.dumps({"face": image}) - response = requests.post(api_root + "fer", data=data_dict, headers=JSON_HEADERS) - response_dict = response.json() - if len(response_dict) < 2: - raise ValueError(response_dict.values()[0]) - else: - return response_dict + return api_handler(image, api_root + "fer") diff --git a/indicoio/local/__init__.py b/indicoio/local/__init__.py index 2d50e7f..9d44439 100644 --- a/indicoio/local/__init__.py +++ b/indicoio/local/__init__.py @@ -1,5 +1,5 @@ from functools import partial -from indicoio.utils import config +import indicoio.config as config JSON_HEADERS = {'Content-type': 'application/json', 'Accept': 'text/plain'} diff --git a/indicoio/text/lang.py b/indicoio/text/lang.py index 665522d..169b415 100644 --- a/indicoio/text/lang.py +++ b/indicoio/text/lang.py @@ -1,7 +1,7 @@ import requests import json -from indicoio import JSON_HEADERS +from indicoio.utils import api_handler def language(api_root, text): """ @@ -26,10 +26,4 @@ def language(api_root, text): :rtype: Dictionary of language probability pairs """ - data_dict = json.dumps({'text': text}) - response = requests.post(api_root + "language", data=data_dict, headers=JSON_HEADERS) - response_dict = response.json() - if len(response_dict) < 2: - raise ValueError(response_dict.values()[0]) - else: - return response_dict \ No newline at end of file + return api_handler(text, api_root + "language") diff --git a/indicoio/text/sentiment.py b/indicoio/text/sentiment.py index b570f5f..e60cd42 100644 --- a/indicoio/text/sentiment.py +++ b/indicoio/text/sentiment.py @@ -2,7 +2,7 @@ import requests import json from indicoio import JSON_HEADERS -from indicoio.utils import normalize +from indicoio.utils import api_handler def political(api_root, text): """ @@ -30,13 +30,7 @@ def political(api_root, text): :rtype: Dictionary of party probability pairs """ - data_dict = json.dumps({'text': text}) - response = requests.post(api_root + "political", data=data_dict, headers=JSON_HEADERS) - response_dict = response.json() - if len(response_dict) < 2: - raise ValueError(response_dict.values()[0]) - else: - return response_dict + return api_handler(text, api_root + "political") def posneg(api_root, text): """ @@ -59,10 +53,4 @@ def posneg(api_root, text): :rtype: Float """ - data_dict = json.dumps({'text': text}) - response = requests.post(api_root + "sentiment", data=data_dict, headers=JSON_HEADERS) - response_dict = response.json() - if 'Sentiment' not in response_dict: - raise ValueError(response_dict.values()[0]) - else: - return response_dict['Sentiment'] + return api_handler(text, api_root + "sentiment") diff --git a/indicoio/utils/__init__.py b/indicoio/utils/__init__.py index 18ae6db..f01ac36 100644 --- a/indicoio/utils/__init__.py +++ b/indicoio/utils/__init__.py @@ -1,7 +1,18 @@ -import inspect +import inspect, json, requests import numpy as np from skimage.transform import resize +from indicoio import JSON_HEADERS + +def api_handler(arg, url): + data_dict = json.dumps({'data': arg}) + response = requests.post(url, data=data_dict, headers=JSON_HEADERS).json() + results = response.get('results', False) + if not results: + error = response.get('error') + raise ValueError(error) + return results + class TypeCheck(object): """ Decorator that performs a typecheck on the input to a function diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bc40c5f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +numpy>=1.8.0 +six>=1.3.0 +scikit-image>=0.10.1 +requests>=2.2.1 diff --git a/setup.py b/setup.py index 10b4e53..331195d 100644 --- a/setup.py +++ b/setup.py @@ -8,12 +8,13 @@ except ImportError: setup( name="IndicoIo", - version='0.4.4', + version='0.4.6', packages=[ "indicoio", "indicoio.text", "indicoio.images", "indicoio.utils", + "indicoio.local", "tests", ], description=""" @@ -33,6 +34,6 @@ setup( "requests >= 1.2.3", "six >= 1.3.0", "numpy >= 1.8.1", - "scikit-image >= 0.10.1" + "scikit-image >= 0.10.1", ], ) diff --git a/tests/local/__init__.py b/tests/local/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_local.py b/tests/local/test_local.py similarity index 76% rename from tests/test_local.py rename to tests/local/test_local.py index 241d7da..f60ac59 100644 --- a/tests/test_local.py +++ b/tests/local/test_local.py @@ -2,7 +2,7 @@ import unittest import numpy as np -from indicoio.local import political, sentiment, fer, facial_features, language +from indicoio.local import political, sentiment, fer, facial_features, language, image_features class FullAPIRun(unittest.TestCase): @@ -16,15 +16,15 @@ class FullAPIRun(unittest.TestCase): self.assertEqual(political_set, set(response.keys())) def test_posneg(self): - posneg_set = set(['Sentiment']) test_string = "Worst song ever." response = sentiment(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 = np.linspace(0,50,48*48).reshape(48,48).tolist() + test_face = np.random.rand(48,48).tolist() response = fer(test_face) self.assertTrue(isinstance(response, dict)) @@ -32,18 +32,32 @@ class FullAPIRun(unittest.TestCase): def test_bad_fer(self): fer_set = set(['Angry', 'Sad', 'Neutral', 'Surprise', 'Fear', 'Happy']) - test_face = np.linspace(0,50,56*56).reshape(56,56).tolist() + test_face = np.random.rand(56,56).tolist() response = fer(test_face) self.assertTrue(isinstance(response, dict)) self.assertEqual(fer_set, set(response.keys())) def test_good_facial_features(self): - test_face = np.linspace(0,50,48*48).reshape(48,48).tolist() + test_face = np.random.rand(48,48).tolist() response = facial_features(test_face) self.assertTrue(isinstance(response, list)) self.assertEqual(len(response), 48) + + def test_good_image_features_greyscale(self): + test_image = np.random.rand(64, 64).tolist() + response = image_features(test_image) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 2048) + + def test_good_image_features_rgb(self): + test_image = np.random.rand(64, 64, 3).tolist() + response = image_features(test_image) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 2048) def test_language(self): language_set = set([ diff --git a/tests/remote/__init__.py b/tests/remote/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_remote.py b/tests/remote/test_remote.py similarity index 76% rename from tests/test_remote.py rename to tests/remote/test_remote.py index d5169cb..962fa3d 100644 --- a/tests/test_remote.py +++ b/tests/remote/test_remote.py @@ -2,7 +2,7 @@ import unittest import numpy as np -from indicoio import political, sentiment, fer, facial_features, language +from indicoio import political, sentiment, fer, facial_features, language, image_features class FullAPIRun(unittest.TestCase): @@ -16,15 +16,15 @@ class FullAPIRun(unittest.TestCase): self.assertEqual(political_set, set(response.keys())) def test_posneg(self): - posneg_set = set(['Sentiment']) test_string = "Worst song ever." response = sentiment(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 = np.linspace(0,50,48*48).reshape(48,48).tolist() + test_face = np.random.rand(48,48).tolist() response = fer(test_face) self.assertTrue(isinstance(response, dict)) @@ -32,18 +32,32 @@ class FullAPIRun(unittest.TestCase): def test_bad_fer(self): fer_set = set(['Angry', 'Sad', 'Neutral', 'Surprise', 'Fear', 'Happy']) - test_face = np.linspace(0,50,56*56).reshape(56,56).tolist() + test_face = np.random.rand(56,56).tolist() response = fer(test_face) self.assertTrue(isinstance(response, dict)) self.assertEqual(fer_set, set(response.keys())) def test_good_facial_features(self): - test_face = np.linspace(0,50,48*48).reshape(48,48).tolist() + test_face = np.random.rand(48,48).tolist() response = facial_features(test_face) self.assertTrue(isinstance(response, list)) self.assertEqual(len(response), 48) + + def test_good_image_features_greyscale(self): + test_image = np.random.rand(64, 64).tolist() + response = image_features(test_image) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 2048) + + def test_good_image_features_rgb(self): + test_image = np.random.rand(64, 64, 3).tolist() + response = image_features(test_image) + + self.assertTrue(isinstance(response, list)) + self.assertEqual(len(response), 2048) def test_language(self): language_set = set([