├── requirements.txt ├── install_corpora.sh ├── Dockerfile.upgrade ├── .gitignore ├── README.md ├── Dockerfile ├── LICENSE ├── setup_docker_host_ec2.sh ├── static └── index.html └── textblob-api-server.py /requirements.txt: -------------------------------------------------------------------------------- 1 | textblob==0.7.1 2 | textblob-aptagger==0.1.0 3 | flask==0.10.1 4 | numpy==1.7.1 5 | psutil==1.0.1 -------------------------------------------------------------------------------- /install_corpora.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export TEXTBLOB_VERSION=0.7.1 3 | cd /tmp && git clone https://github.com/sloria/TextBlob.git && cd /tmp/TextBlob && git checkout $TEXTBLOB_VERSION && python download_corpora.py && rm -rf /tmp/TextBlob && find $HOME/nltk_data -name '*.zip' | xargs rm 4 | -------------------------------------------------------------------------------- /Dockerfile.upgrade: -------------------------------------------------------------------------------- 1 | # textblob-api-server 2 | # 3 | # VERSION 0.1 4 | # DOCKER-VERSION 0.5.3 5 | 6 | FROM sguignot/textblob-api-server 7 | RUN cd /srv/textblob-api-server && git pull 8 | 9 | EXPOSE 5000 10 | CMD ["python", "/srv/textblob-api-server/textblob-api-server.py"] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | 37 | /tmp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | textblob-api-server 2 | =================== 3 | 4 | This is just [TextBlob](https://github.com/sloria/TextBlob) + [flask](https://github.com/mitsuhiko/flask) + ajax = *Live sentiment analysis while you're typing* 5 | 6 | ## Install and run 7 | 8 | ### From the [docker](http://docker.io) repository: 9 | ```bash 10 | docker run sguignot/textblob-api-server 11 | ``` 12 | 13 | ##### You can also build a docker image from the Dockerfile: 14 | ```bash 15 | docker build https://raw.github.com/sguignot/textblob-api-server/master/Dockerfile 16 | ``` 17 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # textblob-api-server 2 | # 3 | # VERSION 0.1 4 | # DOCKER-VERSION 0.5.3 5 | 6 | FROM ubuntu:precise 7 | MAINTAINER Sébastien Guignot "sebastien.guignot@gmail.com" 8 | 9 | RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list 10 | RUN apt-get update 11 | 12 | RUN apt-get install -y curl git python-pip python-dev gcc && apt-get clean 13 | RUN cd /srv && git clone https://github.com/sguignot/textblob-api-server.git 14 | RUN cd /srv/textblob-api-server && pip install -r requirements.txt 15 | RUN /srv/textblob-api-server/install_corpora.sh 16 | 17 | EXPOSE 5000 18 | CMD ["python", "/srv/textblob-api-server/textblob-api-server.py"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Sébastien Guignot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /setup_docker_host_ec2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # You should use an AMI with docker >= 0.6.1 installed 3 | # 4 | # User data to set in your EC2 instances: 5 | # #!/bin/sh 6 | # set -x -e 7 | # curl https://raw.github.com/sguignot/textblob-api-server/master/setup_docker_host_ec2.sh | sh 8 | 9 | set -x -e 10 | 11 | echo "Downloading the textblob-api-server docker image..." 12 | docker pull sguignot/textblob-api-server 13 | 14 | echo "Install nginx..." 15 | apt-get -qq update 16 | apt-get -qq install nginx 17 | 18 | echo "Creating /etc/init/textblob-api-server.conf..." 19 | cat >/etc/init/textblob-api-server.conf </etc/nginx/sites-available/default < 3 | 4 | 5 | 6 | TextBlob API Demo 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 |

Type or paste your text here

17 | 18 |
19 |
20 |
21 |

Live analysis here

22 | Sentiment /textblob/api/sentiment 23 | 24 | Noun Phrases /textblob/api/noun_phrases 25 | 26 | POS Tags /textblob/api/pos_tags 27 | 28 | POS Tags (Perceptron) /textblob/api/pos_tags 29 | 30 |
31 |
32 | 33 |
34 |

TextBlob | Demo GitHub repository | Demo delivered to you inside a docker container!

35 |
36 |
37 | 38 | 39 | 40 | 41 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /textblob-api-server.py: -------------------------------------------------------------------------------- 1 | from text.blob import TextBlob 2 | from text.blob import Blobber 3 | from text.sentiments import NaiveBayesAnalyzer 4 | from text.np_extractors import ConllExtractor 5 | from text.taggers import NLTKTagger 6 | from textblob_aptagger import PerceptronTagger 7 | from flask import Flask, jsonify, abort, request, make_response, url_for, redirect 8 | import os, psutil 9 | 10 | DEV_ENV = bool(os.environ.get('DEV_ENV', False)) 11 | 12 | class TextBlobFactory: 13 | def __init__(self): 14 | # create custom components 15 | self.naive_bayes_analyzer = NaiveBayesAnalyzer() 16 | self.conll_extractor = ConllExtractor() 17 | self.nltk_tagger = NLTKTagger() 18 | self.perceptron_tagger = PerceptronTagger() 19 | if DEV_ENV: 20 | return 21 | # train all components (default and custom) 22 | text = 'TextBlob blobs great!' 23 | default_blob = TextBlob(text) 24 | default_blob.sentiment 25 | default_blob.noun_phrases 26 | default_blob.pos_tags 27 | custom_blob = TextBlob(text, analyzer=self.naive_bayes_analyzer, np_extractor=self.conll_extractor, pos_tagger=self.nltk_tagger) 28 | custom_blob.sentiment 29 | custom_blob.noun_phrases 30 | custom_blob.pos_tags 31 | custom2_blob = TextBlob(text, pos_tagger=self.perceptron_tagger) 32 | custom2_blob.pos_tags 33 | 34 | def create_blob(self, request_json): 35 | options = {} 36 | if request_json.get('analyzer') == 'NaiveBayesAnalyzer': 37 | options['analyzer'] = self.naive_bayes_analyzer 38 | if request_json.get('np_extractor') == 'ConllExtractor': 39 | options['np_extractor'] = self.conll_extractor 40 | if request_json.get('pos_tagger') == 'NLTKTagger': 41 | options['pos_tagger'] = self.nltk_tagger 42 | elif request_json.get('pos_tagger') == 'PerceptronTagger': 43 | options['pos_tagger'] = self.perceptron_tagger 44 | return TextBlob(request_json['text'], **options) 45 | 46 | # human size 47 | def hs(bytes): 48 | for x in ['bytes','KB','MB','GB']: 49 | if bytes < 1024.0: 50 | return "%3.1f%s" % (bytes, x) 51 | bytes /= 1024.0 52 | return "%3.1f%s" % (bytes, 'TB') 53 | 54 | 55 | tb_factory = TextBlobFactory() 56 | app = Flask(__name__, static_url_path = "") 57 | 58 | @app.errorhandler(400) 59 | def not_found(error): 60 | return make_response(jsonify( { 'error': 'Bad request' } ), 400) 61 | 62 | @app.errorhandler(404) 63 | def not_found(error): 64 | return make_response(jsonify( { 'error': 'Not found' } ), 404) 65 | 66 | @app.route('/') 67 | def index(): 68 | return redirect(url_for('static', filename='index.html')) 69 | 70 | @app.route('/textblob/api/sentiment', methods = ['POST']) 71 | def get_sentiment(): 72 | if not request.json or not 'text' in request.json: 73 | abort(400) 74 | blob = tb_factory.create_blob(request.json) 75 | if isinstance(blob.analyzer, NaiveBayesAnalyzer): 76 | classification, pos_probability, neg_probability = blob.sentiment 77 | return jsonify( { 'classification': classification, 'pos_probability': pos_probability, 'neg_probability': neg_probability } ), 200 78 | polarity, subjectivity = blob.sentiment 79 | return jsonify( { 'polarity': polarity, 'subjectivity': subjectivity } ), 200 80 | 81 | @app.route('/textblob/api/pos_tags', methods = ['POST']) 82 | def get_pos_tags(): 83 | if not request.json or not 'text' in request.json: 84 | abort(400) 85 | blob = tb_factory.create_blob(request.json) 86 | pos_tags = blob.pos_tags 87 | return jsonify( { 'pos_tags': pos_tags } ), 200 88 | 89 | @app.route('/textblob/api/noun_phrases', methods = ['POST']) 90 | def get_noun_phrases(): 91 | if not request.json or not 'text' in request.json: 92 | abort(400) 93 | blob = tb_factory.create_blob(request.json) 94 | noun_phrases = blob.noun_phrases 95 | return jsonify( { 'noun_phrases': noun_phrases } ), 200 96 | 97 | @app.route('/monitor/meminfo', methods = ['GET']) 98 | def get_meminfo(): 99 | process = psutil.Process(os.getpid()) 100 | appmem = process.get_memory_info() 101 | vmem = psutil.virtual_memory() 102 | swap = psutil.swap_memory() 103 | meminfo = { 104 | 'app': { 105 | 'percent': "%.1f%%" % process.get_memory_percent(), 106 | 'rss': hs(appmem[0]), 107 | 'vms': hs(appmem[1]) 108 | }, 109 | 'vmem': { 110 | 'total': hs(vmem[0]), 111 | 'available': hs(vmem[1]), 112 | 'percent': "{percent}%".format(percent=vmem[2]), 113 | 'used': hs(vmem[3]), 114 | 'free': hs(vmem[4]), 115 | 'active': hs(vmem[5]), 116 | 'inactive': hs(vmem[6]), 117 | 'buffers': hs(vmem[7]), 118 | 'cached': hs(vmem[8]) 119 | }, 120 | 'swap': { 121 | 'total': hs(swap[0]), 122 | 'used': hs(swap[1]), 123 | 'free': hs(swap[2]), 124 | 'percent': "{percent}%".format(percent=swap[3]), 125 | 'sin': hs(swap[4]), 126 | 'sout': hs(swap[5]) 127 | } 128 | } 129 | return jsonify( { 'meminfo': meminfo } ), 200 130 | 131 | 132 | if __name__ == '__main__': 133 | app.run(host='0.0.0.0', debug=DEV_ENV) 134 | --------------------------------------------------------------------------------