├── .gitignore ├── README.md ├── a-minimal-api ├── README.md └── api.py ├── notebooks ├── .ipynb_checkpoints │ └── test_todo-checkpoint.ipynb ├── sentiment_EDA.ipynb ├── sentiment_clf.ipynb ├── test_sent_clf.ipynb └── test_todo.ipynb ├── sentiment-clf ├── .gitignore ├── README.md ├── app.py ├── build_model.py ├── lib │ ├── __init__.py │ ├── data │ │ ├── sampleSubmission.csv │ │ ├── test.tsv │ │ └── train.tsv │ └── models │ │ ├── SentimentClassifier.pkl │ │ └── TFIDFVectorizer.pkl ├── model.py ├── requirements.txt └── util.py └── to-do-api ├── README.md └── api.py /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flask-rest-setup 2 | Notes on Flask REST API and tutorial 3 | 4 | [Flask RESTful Documentation](http://flask-restful.readthedocs.io/en/latest/) 5 | [Flask-CORS](https://flask-cors.readthedocs.io/en/latest/) 6 | ___ 7 | 8 | ## Setup 9 | 10 | - [x] `pip install flask-restful` 11 | -------------------------------------------------------------------------------- /a-minimal-api/README.md: -------------------------------------------------------------------------------- 1 | # A Minimal API 2 | This API will return a "Hello World" JSON object 3 | 4 | 5 | ## Usage 6 | 1. Run the API 7 | ```python 8 | python api.py 9 | ``` 10 | 11 | 2. Open a new terminal window and run the following: 12 | ```bash 13 | curl http://127.0.0.1:5000/ 14 | ``` 15 | 16 | 3. This will output the following JSON object: 17 | ``` 18 | {"hello": "world"} 19 | ``` 20 | 21 | ## Reference 22 | 1. [Flask RESTful Quickstart](http://flask-restful.readthedocs.io/en/latest/quickstart.html) 23 | -------------------------------------------------------------------------------- /a-minimal-api/api.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_restful import Resource, Api 3 | 4 | app = Flask(__name__) 5 | api = Api(app) 6 | 7 | 8 | class HelloWorld(Resource): 9 | def get(self): 10 | return {'hello': 'world'} 11 | 12 | 13 | api.add_resource(HelloWorld, '/') 14 | 15 | 16 | if __name__ == '__main__': 17 | app.run(debug=True) 18 | -------------------------------------------------------------------------------- /notebooks/.ipynb_checkpoints/test_todo-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [], 3 | "metadata": {}, 4 | "nbformat": 4, 5 | "nbformat_minor": 2 6 | } 7 | -------------------------------------------------------------------------------- /notebooks/sentiment_EDA.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "/Users/nguyen/projects/flask-rest-setup/sentiment-clf\n" 13 | ] 14 | } 15 | ], 16 | "source": [ 17 | "cd ../sentiment-clf/" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 18, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import pandas as pd\n", 27 | "import matplotlib.pyplot as plt\n", 28 | "import numpy as np\n", 29 | "%matplotlib inline\n", 30 | "plt.style.use('ggplot')" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 3, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "with open('lib/data/train.tsv') as f:\n", 40 | " data = pd.read_csv(f, sep='\\t')" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 11, 46 | "metadata": {}, 47 | "outputs": [ 48 | { 49 | "data": { 50 | "text/html": [ 51 | "
\n", 52 | "\n", 65 | "\n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 190 | " \n", 191 | " \n", 192 | " \n", 193 | " \n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | " \n", 211 | " \n", 212 | " \n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | " \n", 217 | "
PhraseIdSentenceIdPhraseSentiment
011A series of escapades demonstrating the adage ...1
121A series of escapades demonstrating the adage ...2
231A series2
341A2
451series2
561of escapades demonstrating the adage that what...2
671of2
781escapades demonstrating the adage that what is...2
891escapades2
9101demonstrating the adage that what is good for ...2
10111demonstrating the adage2
11121demonstrating2
12131the adage2
13141the2
14151adage2
15161that what is good for the goose2
16171that2
17181what is good for the goose2
18191what2
19201is good for the goose2
\n", 218 | "
" 219 | ], 220 | "text/plain": [ 221 | " PhraseId SentenceId Phrase \\\n", 222 | "0 1 1 A series of escapades demonstrating the adage ... \n", 223 | "1 2 1 A series of escapades demonstrating the adage ... \n", 224 | "2 3 1 A series \n", 225 | "3 4 1 A \n", 226 | "4 5 1 series \n", 227 | "5 6 1 of escapades demonstrating the adage that what... \n", 228 | "6 7 1 of \n", 229 | "7 8 1 escapades demonstrating the adage that what is... \n", 230 | "8 9 1 escapades \n", 231 | "9 10 1 demonstrating the adage that what is good for ... \n", 232 | "10 11 1 demonstrating the adage \n", 233 | "11 12 1 demonstrating \n", 234 | "12 13 1 the adage \n", 235 | "13 14 1 the \n", 236 | "14 15 1 adage \n", 237 | "15 16 1 that what is good for the goose \n", 238 | "16 17 1 that \n", 239 | "17 18 1 what is good for the goose \n", 240 | "18 19 1 what \n", 241 | "19 20 1 is good for the goose \n", 242 | "\n", 243 | " Sentiment \n", 244 | "0 1 \n", 245 | "1 2 \n", 246 | "2 2 \n", 247 | "3 2 \n", 248 | "4 2 \n", 249 | "5 2 \n", 250 | "6 2 \n", 251 | "7 2 \n", 252 | "8 2 \n", 253 | "9 2 \n", 254 | "10 2 \n", 255 | "11 2 \n", 256 | "12 2 \n", 257 | "13 2 \n", 258 | "14 2 \n", 259 | "15 2 \n", 260 | "16 2 \n", 261 | "17 2 \n", 262 | "18 2 \n", 263 | "19 2 " 264 | ] 265 | }, 266 | "execution_count": 11, 267 | "metadata": {}, 268 | "output_type": "execute_result" 269 | } 270 | ], 271 | "source": [ 272 | "data.head(20)" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": 21, 278 | "metadata": {}, 279 | "outputs": [ 280 | { 281 | "data": { 282 | "text/plain": [ 283 | "" 284 | ] 285 | }, 286 | "execution_count": 21, 287 | "metadata": {}, 288 | "output_type": "execute_result" 289 | }, 290 | { 291 | "data": { 292 | "image/png": "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\nSUVORK5CYII=\n", 293 | "text/plain": [ 294 | "
" 295 | ] 296 | }, 297 | "metadata": {}, 298 | "output_type": "display_data" 299 | } 300 | ], 301 | "source": [ 302 | "num_unique = len(data['Sentiment'].unique())\n", 303 | "data['Sentiment'].hist(figsize=(12,6), bins=np.arange(num_unique+1)-0.5, rwidth=0.5)" 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": null, 309 | "metadata": {}, 310 | "outputs": [], 311 | "source": [] 312 | } 313 | ], 314 | "metadata": { 315 | "kernelspec": { 316 | "display_name": "Python 3", 317 | "language": "python", 318 | "name": "python3" 319 | }, 320 | "language_info": { 321 | "codemirror_mode": { 322 | "name": "ipython", 323 | "version": 3 324 | }, 325 | "file_extension": ".py", 326 | "mimetype": "text/x-python", 327 | "name": "python", 328 | "nbconvert_exporter": "python", 329 | "pygments_lexer": "ipython3", 330 | "version": "3.6.3" 331 | } 332 | }, 333 | "nbformat": 4, 334 | "nbformat_minor": 2 335 | } 336 | -------------------------------------------------------------------------------- /notebooks/sentiment_clf.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "/Users/nguyen/projects/flask-rest-setup/sentiment-clf\n" 13 | ] 14 | } 15 | ], 16 | "source": [ 17 | "cd ../sentiment-clf/" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 2, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "from model import NLPModel\n", 27 | "import pandas as pd\n", 28 | "from sklearn.model_selection import train_test_split" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "## Create the model object\n", 36 | "The NLP model object uses a Naive Bayes classifier and a TFIDF vectorizer:\n", 37 | "```\n", 38 | "self.clf = MultinomialNB()\n", 39 | "self.vectorizer = TfidfVectorizer()\n", 40 | "```" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 3, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "model = NLPModel()" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Get the data" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 4, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "with open('lib/data/train.tsv') as f:\n", 66 | " data = pd.read_csv(f, sep='\\t')" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 14, 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "data": { 76 | "text/html": [ 77 | "
\n", 78 | "\n", 91 | "\n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | "
PhraseIdSentenceIdPhraseSentiment
011A series of escapades demonstrating the adage ...1
121A series of escapades demonstrating the adage ...2
231A series2
341A2
451series2
561of escapades demonstrating the adage that what...2
671of2
781escapades demonstrating the adage that what is...2
891escapades2
9101demonstrating the adage that what is good for ...2
\n", 174 | "
" 175 | ], 176 | "text/plain": [ 177 | " PhraseId SentenceId Phrase \\\n", 178 | "0 1 1 A series of escapades demonstrating the adage ... \n", 179 | "1 2 1 A series of escapades demonstrating the adage ... \n", 180 | "2 3 1 A series \n", 181 | "3 4 1 A \n", 182 | "4 5 1 series \n", 183 | "5 6 1 of escapades demonstrating the adage that what... \n", 184 | "6 7 1 of \n", 185 | "7 8 1 escapades demonstrating the adage that what is... \n", 186 | "8 9 1 escapades \n", 187 | "9 10 1 demonstrating the adage that what is good for ... \n", 188 | "\n", 189 | " Sentiment \n", 190 | "0 1 \n", 191 | "1 2 \n", 192 | "2 2 \n", 193 | "3 2 \n", 194 | "4 2 \n", 195 | "5 2 \n", 196 | "6 2 \n", 197 | "7 2 \n", 198 | "8 2 \n", 199 | "9 2 " 200 | ] 201 | }, 202 | "execution_count": 14, 203 | "metadata": {}, 204 | "output_type": "execute_result" 205 | } 206 | ], 207 | "source": [ 208 | "data.head(10)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "## Use only the 1 star and 5 star reviews\n", 216 | "For this example, we want to only predict positive or negative sentiment using the extreme cases." 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 5, 222 | "metadata": {}, 223 | "outputs": [], 224 | "source": [ 225 | "pos_neg = data[(data['Sentiment'] == 0) | (data['Sentiment'] == 4)]" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "## Relabel as 0 for negative and 1 for positive" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": 6, 238 | "metadata": {}, 239 | "outputs": [ 240 | { 241 | "name": "stderr", 242 | "output_type": "stream", 243 | "text": [ 244 | "/Users/nguyen/anaconda3/lib/python3.6/site-packages/ipykernel_launcher.py:2: SettingWithCopyWarning: \n", 245 | "A value is trying to be set on a copy of a slice from a DataFrame.\n", 246 | "Try using .loc[row_indexer,col_indexer] = value instead\n", 247 | "\n", 248 | "See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy\n", 249 | " \n" 250 | ] 251 | } 252 | ], 253 | "source": [ 254 | "pos_neg['Binary'] = pos_neg.apply(\n", 255 | " lambda x: 0 if x['Sentiment'] == 0 else 1, axis=1)" 256 | ] 257 | }, 258 | { 259 | "cell_type": "markdown", 260 | "metadata": {}, 261 | "source": [ 262 | "## Fit a vectorizer to the vocabulary in the dataset" 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": 7, 268 | "metadata": {}, 269 | "outputs": [], 270 | "source": [ 271 | "model.vectorizer_fit(pos_neg.loc[:, 'Phrase'])" 272 | ] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "metadata": {}, 277 | "source": [ 278 | "## Transform the text in the dataset to its vectorized representation" 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "execution_count": 8, 284 | "metadata": {}, 285 | "outputs": [], 286 | "source": [ 287 | "X = model.vectorizer_transform(pos_neg.loc[:, 'Phrase'])" 288 | ] 289 | }, 290 | { 291 | "cell_type": "markdown", 292 | "metadata": {}, 293 | "source": [ 294 | "## Separate the target from the data" 295 | ] 296 | }, 297 | { 298 | "cell_type": "code", 299 | "execution_count": 9, 300 | "metadata": {}, 301 | "outputs": [], 302 | "source": [ 303 | "y = pos_neg.loc[:, 'Binary']" 304 | ] 305 | }, 306 | { 307 | "cell_type": "markdown", 308 | "metadata": {}, 309 | "source": [ 310 | "## Cross Validation!" 311 | ] 312 | }, 313 | { 314 | "cell_type": "code", 315 | "execution_count": 10, 316 | "metadata": {}, 317 | "outputs": [], 318 | "source": [ 319 | "X_train, X_test, y_train, y_test = train_test_split(X, y)" 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "metadata": {}, 325 | "source": [ 326 | "## Train the model" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": 11, 332 | "metadata": {}, 333 | "outputs": [], 334 | "source": [ 335 | "model.train(X_train, y_train)" 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": 12, 341 | "metadata": {}, 342 | "outputs": [], 343 | "source": [ 344 | "from util import plot_roc" 345 | ] 346 | }, 347 | { 348 | "cell_type": "code", 349 | "execution_count": 13, 350 | "metadata": {}, 351 | "outputs": [ 352 | { 353 | "data": { 354 | "image/png": "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\nI4/EFi/+oai7CVnyY4CBZjYMOA741TmnG0YkkJ07d+59vGzZMtatW8f8+fMpX74869evZ9myZVSq\nVInZs2czd+7cAu0zNTW10DnS09PZtWsXTZo0oU6dOrRq1YpWrVpRu3btA27boUOHXPeVlZVF3bp1\nOeyww2jevDlmBVnNVUSkkGrWhLffhhNOgIMPLvJuolbyZjYU6AjUNLM1+OUsywA4514APsSv770U\nv/zmFdHKItG1YMECvvnmGxYsWMDnn3/OnDlz2Lp1a4E+W61atb2jvYcffpgLLrgg121r165dpJIX\nEYkbY8ZASgqccQb07Vvs3UWt5J1zF+XzvgP+HK3vl8javn0748aN4z//8Vc6litXjnHjxlG2bFl2\n7969z7YpKSk0atSIFi1acPLJJwP+nHTr1q2pXbs2e+ZUVKxYkUqVKpXsb0REJFaNGgUXXeRH7z17\nQgSOFMbFxDsJY8uWLTzzzDN88sknTJ8+fZ/32rVrR9u2bTEzGjduTPfu3TnppJM4/PDDNdFKRKSw\nhgyBfv3guONg7NiIFDyo5CWHDRs28N133/HTTz+xcOFC7r///n3eP+GEExg8eDCNGjUKE1BEJBG9\n+SZccQWccgp88AFUrhyxXavkk8TKlSu566672LFjB6VL+//ZnXOMHTuW+vXrY2asWLHid5875phj\nmDx5ss6Fi4hEy1dfQefOMHo0VKwY0V3H3V3odAndga1cuZJdu3YBsGnTJlasWMGKFSv4xz/+QenS\npfe5PKxVq1bA/18XXr58eVq3bk16ejp16tShT58+VKtWjebNm+ucuYhItGzdCqmpkJUF6emQyyWn\nZjbbOde+KF+hkXwcGjFiBA888ACLFi0q0PaVK1fmuuuuo2XLllx77bW67EtEJLRBg/zP559D/fq5\nFnxxqeRj3KpVq/j+++8B+Oyzz7jvvvv2vte+fXuysrLo2rUrmzdvpmPHjnsXk9mzWEqdOnWoWrVq\nkOwiInIAjz0Gd90F555brGvgC0IlH2PS0tIYMWIEr732GqmpqYwdO/aA240cOZJzzz23hNOJiEiR\nOQd//zvcdx9cfLGfcFc6ujWskg9o3Lhx9OzZc58lVvdfWrV169accsopnH/++QA0bNiQBg0alHhW\nEREpplde8QXfvz+8+qpf9CbKVPIlLC0tjfT0dPr168d7770HwOGHH07Hjh33buOc47bbbuPQQw/N\n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\nHwAAAABJRU5ErkJggg==\n", 355 | "text/plain": [ 356 | "
" 357 | ] 358 | }, 359 | "metadata": {}, 360 | "output_type": "display_data" 361 | } 362 | ], 363 | "source": [ 364 | "model.plot_roc(X_test, y_test, size_x=8, size_y=8)" 365 | ] 366 | }, 367 | { 368 | "cell_type": "code", 369 | "execution_count": null, 370 | "metadata": {}, 371 | "outputs": [], 372 | "source": [] 373 | } 374 | ], 375 | "metadata": { 376 | "kernelspec": { 377 | "display_name": "Python 3", 378 | "language": "python", 379 | "name": "python3" 380 | }, 381 | "language_info": { 382 | "codemirror_mode": { 383 | "name": "ipython", 384 | "version": 3 385 | }, 386 | "file_extension": ".py", 387 | "mimetype": "text/x-python", 388 | "name": "python", 389 | "nbconvert_exporter": "python", 390 | "pygments_lexer": "ipython3", 391 | "version": "3.6.3" 392 | } 393 | }, 394 | "nbformat": 4, 395 | "nbformat_minor": 2 396 | } 397 | -------------------------------------------------------------------------------- /notebooks/test_sent_clf.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import requests" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 4, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "url = 'http://127.0.0.1:5000/'" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 5, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "params ={'query': 'that movie was boring'}" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 6, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "response = requests.get(url, params)" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 7, 42 | "metadata": {}, 43 | "outputs": [ 44 | { 45 | "data": { 46 | "text/plain": [ 47 | "" 48 | ] 49 | }, 50 | "execution_count": 7, 51 | "metadata": {}, 52 | "output_type": "execute_result" 53 | } 54 | ], 55 | "source": [ 56 | "response" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 8, 62 | "metadata": {}, 63 | "outputs": [ 64 | { 65 | "data": { 66 | "text/plain": [ 67 | "{'confidence': 0.128, 'prediction': 'Negative'}" 68 | ] 69 | }, 70 | "execution_count": 8, 71 | "metadata": {}, 72 | "output_type": "execute_result" 73 | } 74 | ], 75 | "source": [ 76 | "response.json()" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 9, 82 | "metadata": {}, 83 | "outputs": [ 84 | { 85 | "data": { 86 | "text/plain": [ 87 | "{'Content-Type': 'application/json', 'Content-Length': '58', 'Server': 'Werkzeug/0.14.1 Python/3.6.3', 'Date': 'Tue, 21 Aug 2018 22:19:01 GMT'}" 88 | ] 89 | }, 90 | "execution_count": 9, 91 | "metadata": {}, 92 | "output_type": "execute_result" 93 | } 94 | ], 95 | "source": [ 96 | "response.headers" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 10, 102 | "metadata": {}, 103 | "outputs": [ 104 | { 105 | "data": { 106 | "text/plain": [ 107 | "'http://127.0.0.1:5000/?query=that+movie+was+boring'" 108 | ] 109 | }, 110 | "execution_count": 10, 111 | "metadata": {}, 112 | "output_type": "execute_result" 113 | } 114 | ], 115 | "source": [ 116 | "response.url" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [] 125 | } 126 | ], 127 | "metadata": { 128 | "kernelspec": { 129 | "display_name": "Python 3", 130 | "language": "python", 131 | "name": "python3" 132 | }, 133 | "language_info": { 134 | "codemirror_mode": { 135 | "name": "ipython", 136 | "version": 3 137 | }, 138 | "file_extension": ".py", 139 | "mimetype": "text/x-python", 140 | "name": "python", 141 | "nbconvert_exporter": "python", 142 | "pygments_lexer": "ipython3", 143 | "version": "3.6.3" 144 | } 145 | }, 146 | "nbformat": 4, 147 | "nbformat_minor": 2 148 | } 149 | -------------------------------------------------------------------------------- /notebooks/test_todo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Implementation of Python and Requests with Flask REST API Todo Tutorial" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import requests" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "## Typical implementation for requests" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 2, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "response = requests.get('http://localhost:5000/todos')" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 3, 38 | "metadata": {}, 39 | "outputs": [ 40 | { 41 | "data": { 42 | "text/plain": [ 43 | "" 44 | ] 45 | }, 46 | "execution_count": 3, 47 | "metadata": {}, 48 | "output_type": "execute_result" 49 | } 50 | ], 51 | "source": [ 52 | "response" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 4, 58 | "metadata": {}, 59 | "outputs": [ 60 | { 61 | "data": { 62 | "text/plain": [ 63 | "{'todo1': {'task': 'build an API'},\n", 64 | " 'todo2': {'task': '?????'},\n", 65 | " 'todo3': {'task': 'profit!'}}" 66 | ] 67 | }, 68 | "execution_count": 4, 69 | "metadata": {}, 70 | "output_type": "execute_result" 71 | } 72 | ], 73 | "source": [ 74 | "response.json()" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "## Update an existing task" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 5, 87 | "metadata": {}, 88 | "outputs": [ 89 | { 90 | "data": { 91 | "text/plain": [ 92 | "{'task': 'Remember the milk'}" 93 | ] 94 | }, 95 | "execution_count": 5, 96 | "metadata": {}, 97 | "output_type": "execute_result" 98 | } 99 | ], 100 | "source": [ 101 | "requests.put('http://localhost:5000/todos/todo1', data={'task': 'Remember the milk'}).json()" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "## Check todo list" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 6, 114 | "metadata": {}, 115 | "outputs": [ 116 | { 117 | "data": { 118 | "text/plain": [ 119 | "{'todo1': {'task': 'Remember the milk'},\n", 120 | " 'todo2': {'task': '?????'},\n", 121 | " 'todo3': {'task': 'profit!'}}" 122 | ] 123 | }, 124 | "execution_count": 6, 125 | "metadata": {}, 126 | "output_type": "execute_result" 127 | } 128 | ], 129 | "source": [ 130 | "requests.get('http://localhost:5000/todos').json()" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "## Check a specific task" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 7, 143 | "metadata": {}, 144 | "outputs": [ 145 | { 146 | "data": { 147 | "text/plain": [ 148 | "{'task': 'Remember the milk'}" 149 | ] 150 | }, 151 | "execution_count": 7, 152 | "metadata": {}, 153 | "output_type": "execute_result" 154 | } 155 | ], 156 | "source": [ 157 | "requests.get('http://localhost:5000/todos/todo1').json()" 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": {}, 163 | "source": [ 164 | "## Delete the task that was just created" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 20, 170 | "metadata": {}, 171 | "outputs": [ 172 | { 173 | "data": { 174 | "text/plain": [ 175 | "" 176 | ] 177 | }, 178 | "execution_count": 20, 179 | "metadata": {}, 180 | "output_type": "execute_result" 181 | } 182 | ], 183 | "source": [ 184 | "requests.delete('http://localhost:5000/todos/todo4')" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": {}, 190 | "source": [ 191 | "## Check todo list" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 21, 197 | "metadata": {}, 198 | "outputs": [ 199 | { 200 | "data": { 201 | "text/plain": [ 202 | "{'todo1': {'task': 'Remember the milk'},\n", 203 | " 'todo2': {'task': '?????'},\n", 204 | " 'todo3': {'task': 'profit!'}}" 205 | ] 206 | }, 207 | "execution_count": 21, 208 | "metadata": {}, 209 | "output_type": "execute_result" 210 | } 211 | ], 212 | "source": [ 213 | "requests.get('http://localhost:5000/todos').json()" 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "metadata": {}, 219 | "source": [ 220 | "## Add a new task with POST" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 22, 226 | "metadata": {}, 227 | "outputs": [ 228 | { 229 | "data": { 230 | "text/plain": [ 231 | "{'task': 'Buy xmas gift'}" 232 | ] 233 | }, 234 | "execution_count": 22, 235 | "metadata": {}, 236 | "output_type": "execute_result" 237 | } 238 | ], 239 | "source": [ 240 | "requests.post('http://localhost:5000/todos', data={'task': 'Buy xmas gift'}).json()" 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "metadata": {}, 246 | "source": [ 247 | "## Check todo list" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": 23, 253 | "metadata": {}, 254 | "outputs": [ 255 | { 256 | "data": { 257 | "text/plain": [ 258 | "{'todo1': {'task': 'Remember the milk'},\n", 259 | " 'todo2': {'task': '?????'},\n", 260 | " 'todo3': {'task': 'profit!'},\n", 261 | " 'todo4': {'task': 'Buy xmas gift'}}" 262 | ] 263 | }, 264 | "execution_count": 23, 265 | "metadata": {}, 266 | "output_type": "execute_result" 267 | } 268 | ], 269 | "source": [ 270 | "requests.get('http://localhost:5000/todos').json()" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": null, 276 | "metadata": {}, 277 | "outputs": [], 278 | "source": [] 279 | } 280 | ], 281 | "metadata": { 282 | "kernelspec": { 283 | "display_name": "Python 3", 284 | "language": "python", 285 | "name": "python3" 286 | }, 287 | "language_info": { 288 | "codemirror_mode": { 289 | "name": "ipython", 290 | "version": 3 291 | }, 292 | "file_extension": ".py", 293 | "mimetype": "text/x-python", 294 | "name": "python", 295 | "nbconvert_exporter": "python", 296 | "pygments_lexer": "ipython3", 297 | "version": "3.6.3" 298 | } 299 | }, 300 | "nbformat": 4, 301 | "nbformat_minor": 2 302 | } 303 | -------------------------------------------------------------------------------- /sentiment-clf/.gitignore: -------------------------------------------------------------------------------- 1 | .chalice/deployments/ 2 | .chalice/venv/ 3 | -------------------------------------------------------------------------------- /sentiment-clf/README.md: -------------------------------------------------------------------------------- 1 | # Sentiment Classifier Deployed as a REST API using Flask 2 | 3 | * [Flask Restful Documentation]() 4 | * [HTTPie Documentation](https://httpie.org/doc) 5 | * [Data Source: Kaggle](https://www.kaggle.com/c/sentiment-analysis-on-movie-reviews/data) 6 | ___ 7 | 8 | ## Procedure 9 | 1. Start a virtual environment and install requirements 10 | 3. Build sentiment classifier 11 | 4. Write `app.py` which is the API application that will be deployed 12 | 5. Update requirements.txt as you write the code 13 | 6. Test the API 14 | 15 | 16 | ## File Structure 17 | * app_name 18 | * app.py: Flask API application 19 | * model.py: class object for classifier 20 | * build_model.py: imports the class object from `model.py` and initiates a new model, trains the model, and pickle 21 | * util.py: helper functions for `model.py` 22 | * requirements.txt: list of packages that the app will import 23 | * lib 24 | * data: directory that contains the data files from Kaggle 25 | * models: directory that contains the pickled model files 26 | 27 | 28 | ## Testing the API 29 | 1. Run the Flask API locally for testing. Go to directory with `app.py`. 30 | 31 | ```bash 32 | python app.py 33 | ``` 34 | 35 | 36 | 2. In a new terminal window, use HTTPie to make a GET request at the URL of the API. 37 | 38 | ```bash 39 | http http://127.0.0.1:5000/ query=="That was pretty entertaining" 40 | ``` 41 | 42 | 43 | 3. Example of successful output. 44 | 45 | ```bash 46 | HTTP/1.0 200 OK 47 | Content-Length: 57 48 | Content-Type: application/json 49 | Date: Tue, 21 Aug 2018 19:04:04 GMT 50 | Server: Werkzeug/0.14.1 Python/3.6.3 51 | 52 | { 53 | "confidence": 0.78, 54 | "prediction": "Positive" 55 | } 56 | ``` 57 | 58 | 4. Deploying the Flask app on an EC2 instance. 59 | 60 | 61 | ## Appendix 62 | 63 | ### Virtual Environment 64 | 1. Create new virtual environment 65 | ```bash 66 | cd ~/.virtualenvs 67 | virtualenv name-of-env 68 | ``` 69 | 2. Activate virtual environment 70 | ``` 71 | source env/bin/activate 72 | ``` 73 | 3. Go to app.py directory where `requirements.txt` is also located 74 | 4. Install required packages from `requirements.txt` 75 | ```bash 76 | pip install -r requirements.txt 77 | ``` 78 | You will only have to install the `requirements.txt` when working with a new virtual environment. 79 | -------------------------------------------------------------------------------- /sentiment-clf/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_restful import reqparse, abort, Api, Resource 3 | import pickle 4 | import numpy as np 5 | from model import NLPModel 6 | 7 | app = Flask(__name__) 8 | api = Api(app) 9 | 10 | model = NLPModel() 11 | 12 | clf_path = 'lib/models/SentimentClassifier.pkl' 13 | with open(clf_path, 'rb') as f: 14 | model.clf = pickle.load(f) 15 | 16 | vec_path = 'lib/models/TFIDFVectorizer.pkl' 17 | with open(vec_path, 'rb') as f: 18 | model.vectorizer = pickle.load(f) 19 | 20 | # argument parsing 21 | parser = reqparse.RequestParser() 22 | parser.add_argument('query') 23 | 24 | 25 | class PredictSentiment(Resource): 26 | def get(self): 27 | # use parser and find the user's query 28 | args = parser.parse_args() 29 | user_query = args['query'] 30 | 31 | # vectorize the user's query and make a prediction 32 | uq_vectorized = model.vectorizer_transform(np.array([user_query])) 33 | prediction = model.predict(uq_vectorized) 34 | pred_proba = model.predict_proba(uq_vectorized) 35 | 36 | # Output either 'Negative' or 'Positive' along with the score 37 | if prediction == 0: 38 | pred_text = 'Negative' 39 | else: 40 | pred_text = 'Positive' 41 | 42 | # round the predict proba value and set to new variable 43 | confidence = round(pred_proba[0], 3) 44 | 45 | # create JSON object 46 | output = {'prediction': pred_text, 'confidence': confidence} 47 | 48 | return output 49 | 50 | 51 | # Setup the Api resource routing here 52 | # Route the URL to the resource 53 | api.add_resource(PredictSentiment, '/') 54 | 55 | 56 | if __name__ == '__main__': 57 | app.run(debug=True) 58 | -------------------------------------------------------------------------------- /sentiment-clf/build_model.py: -------------------------------------------------------------------------------- 1 | from model import NLPModel 2 | import pandas as pd 3 | # import numpy as np 4 | # import matplotlib.pyplot as plt 5 | from sklearn.model_selection import train_test_split 6 | 7 | 8 | def build_model(): 9 | model = NLPModel() 10 | 11 | # filename = os.path.join( 12 | # os.path.dirname(__file__), 'chalicelib', 'all/train.tsv') 13 | with open('../sentiment_data/train.tsv') as f: 14 | data = pd.read_csv(f, sep='\t') 15 | 16 | pos_neg = data[(data['Sentiment'] == 0) | (data['Sentiment'] == 4)] 17 | 18 | pos_neg['Binary'] = pos_neg.apply( 19 | lambda x: 0 if x['Sentiment'] == 0 else 1, axis=1) 20 | 21 | model.vectorizer_fit(pos_neg.loc[:, 'Phrase']) 22 | print('Vectorizer fit complete') 23 | 24 | X = model.vectorizer_transform(pos_neg.loc[:, 'Phrase']) 25 | print('Vectorizer transform complete') 26 | y = pos_neg.loc[:, 'Binary'] 27 | 28 | X_train, X_test, y_train, y_test = train_test_split(X, y) 29 | 30 | model.train(X_train, y_train) 31 | print('Model training complete') 32 | 33 | model.pickle_clf() 34 | model.pickle_vectorizer() 35 | 36 | model.plot_roc(X_test, y_test) 37 | 38 | 39 | if __name__ == "__main__": 40 | build_model() 41 | -------------------------------------------------------------------------------- /sentiment-clf/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnguyenngo/flask-rest-setup/e64defabdf34a229aba5ea8b3fb1b7ca8a16b432/sentiment-clf/lib/__init__.py -------------------------------------------------------------------------------- /sentiment-clf/lib/models/SentimentClassifier.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnguyenngo/flask-rest-setup/e64defabdf34a229aba5ea8b3fb1b7ca8a16b432/sentiment-clf/lib/models/SentimentClassifier.pkl -------------------------------------------------------------------------------- /sentiment-clf/lib/models/TFIDFVectorizer.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnguyenngo/flask-rest-setup/e64defabdf34a229aba5ea8b3fb1b7ca8a16b432/sentiment-clf/lib/models/TFIDFVectorizer.pkl -------------------------------------------------------------------------------- /sentiment-clf/model.py: -------------------------------------------------------------------------------- 1 | # ML imports 2 | from sklearn.naive_bayes import MultinomialNB 3 | # from sklearn.naive_bayes import BernoulliNB 4 | from sklearn.feature_extraction.text import TfidfVectorizer 5 | 6 | # from sklearn.ensemble import RandomForestClassifier 7 | import pickle 8 | 9 | from util import plot_roc 10 | # spacy_tok 11 | 12 | 13 | class NLPModel(object): 14 | 15 | def __init__(self): 16 | """Simple NLP 17 | Attributes: 18 | clf: sklearn classifier model 19 | vectorizor: TFIDF vectorizer or similar 20 | """ 21 | self.clf = MultinomialNB() 22 | # self.vectorizer = TfidfVectorizer(tokenizer=spacy_tok) 23 | self.vectorizer = TfidfVectorizer() 24 | 25 | def vectorizer_fit(self, X): 26 | """Fits a TFIDF vectorizer to the text 27 | """ 28 | self.vectorizer.fit(X) 29 | 30 | def vectorizer_transform(self, X): 31 | """Transform the text data to a sparse TFIDF matrix 32 | """ 33 | X_transformed = self.vectorizer.transform(X) 34 | return X_transformed 35 | 36 | def train(self, X, y): 37 | """Trains the classifier to associate the label with the sparse matrix 38 | """ 39 | # X_train, X_test, y_train, y_test = train_test_split(X, y) 40 | self.clf.fit(X, y) 41 | 42 | def predict_proba(self, X): 43 | """Returns probability for the binary class '1' in a numpy array 44 | """ 45 | y_proba = self.clf.predict_proba(X) 46 | return y_proba[:, 1] 47 | 48 | def predict(self, X): 49 | """Returns the predicted class in an array 50 | """ 51 | y_pred = self.clf.predict(X) 52 | return y_pred 53 | 54 | def pickle_vectorizer(self, path='chalicelib/models/TFIDFVectorizer.pkl'): 55 | """Saves the trained vectorizer for future use. 56 | """ 57 | with open(path, 'wb') as f: 58 | pickle.dump(self.vectorizer, f) 59 | print("Pickled vectorizer at {}".format(path)) 60 | 61 | def pickle_clf(self, path='chalicelib/models/SentimentClassifier.pkl'): 62 | """Saves the trained classifier for future use. 63 | """ 64 | with open(path, 'wb') as f: 65 | pickle.dump(self.clf, f) 66 | print("Pickled classifier at {}".format(path)) 67 | 68 | def plot_roc(self, X, y, size_x, size_y): 69 | """Plot the ROC curve for X_test and y_test. 70 | """ 71 | plot_roc(self.clf, X, y, size_x, size_y) 72 | -------------------------------------------------------------------------------- /sentiment-clf/requirements.txt: -------------------------------------------------------------------------------- 1 | chalice 2 | matplotlib 3 | sklearn 4 | numpy 5 | scipy 6 | pandas 7 | -------------------------------------------------------------------------------- /sentiment-clf/util.py: -------------------------------------------------------------------------------- 1 | # import spacys 2 | from sklearn.metrics import roc_curve, auc 3 | import matplotlib.pyplot as plt 4 | 5 | # nlp = spacy.load('en') 6 | 7 | 8 | # def spacy_tok(text, lemmatize=False): 9 | # doc = nlp(text) 10 | # if lemmatize: 11 | # tokens = [tok.lemma_ for tok in doc] 12 | # else: 13 | # tokens = [tok.text for tok in doc] 14 | # return tokens 15 | 16 | 17 | def plot_roc(model, x_columns, y_true, size_x=12, size_y=12): 18 | """Returns a ROC plot 19 | 20 | Forked from Matt Drury. 21 | """ 22 | 23 | y_pred = model.predict_proba(x_columns) 24 | 25 | fpr, tpr, threshold = roc_curve(y_true, y_pred[:, 1]) 26 | area_under_curve = auc(fpr, tpr) 27 | 28 | # method I: plt 29 | fig, ax = plt.subplots(figsize=(size_x, size_y)) 30 | model_name = str(type(model)).split('.')[-1].strip(">\'") 31 | plt.title(f'{model_name} ROC') 32 | ax.plot(fpr, tpr, 'k', label='AUC = %0.3f' % area_under_curve) 33 | 34 | ax.legend(loc='lower right') 35 | ax.plot([0, 1], [0, 1], 'r--') 36 | plt.xlim([0, 1]) 37 | plt.ylim([0, 1]) 38 | plt.ylabel('True Positive Rate') 39 | plt.xlabel('False Positive Rate') 40 | plt.show() 41 | -------------------------------------------------------------------------------- /to-do-api/README.md: -------------------------------------------------------------------------------- 1 | # To Do API 2 | 3 | [Flask RESTful Documentation](http://flask-restful.readthedocs.io/en/latest/quickstart.html#a-minimal-api) 4 | 5 | ___ 6 | 7 | ## Usage 8 | 9 | ### Creating Resources 10 | ```python 11 | # Todo 12 | # shows a single todo item and lets you updatae or delete a todo item 13 | class Todo(Resource): 14 | def get(self, todo_id): 15 | """Return the specified todo item given the todo_id""" 16 | abort_if_todo_doesnt_exist(todo_id) 17 | return TODOS[todo_id] 18 | 19 | def delete(self, todo_id): 20 | """Deletes an existing task""" 21 | abort_if_todo_doesnt_exist(todo_id) 22 | del TODOS[todo_id] 23 | # 204: SUCCESS; NO FURTHER CONTENT 24 | return '', 204 25 | 26 | def put(self, todo_id): 27 | """Updates existing task""" 28 | 29 | # parser 30 | abort_if_todo_doesnt_exist(todo_id) 31 | args = parser.parse_args() 32 | task = {'task': args['task']} 33 | TODOS[todo_id] = task 34 | # 201: CREATED 35 | return task, 201 36 | 37 | 38 | # TodoList 39 | # shows a list of all todos, and lets you POST to add new tasks 40 | class TodoList(Resource): 41 | def get(self): 42 | """Return the current TODO dictionary""" 43 | return TODOS 44 | 45 | def post(self): 46 | """Adds task to TODO""" 47 | args = parser.parse_args() 48 | todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1 49 | todo_id = 'todo%i' % todo_id 50 | TODOS[todo_id] = {'task': args['task']} 51 | # 201: CREATED 52 | return TODOS[todo_id], 201 53 | ``` 54 | 55 | ### Routing to your Resources 56 | Route the URL to the resource classes. 57 | ```python 58 | # Setup the Api resource routing here 59 | api.add_resource(TodoList, '/todos') 60 | # is passed as an argument to the methods of the Todo class 61 | api.add_resource(Todo, '/todos/') 62 | ``` 63 | 64 | ### Request Argument Parsing 65 | [Request Parsing Docs](https://flask-restful.readthedocs.io/en/0.3.5/reqparse.html) 66 | 67 | ##### Setup 68 | Create a parser object and add the argument name 'task': 69 | ```python 70 | # argument parsing 71 | parser = reqparse.RequestParser() 72 | parser.add_argument('task') 73 | ``` 74 | 75 | ##### Make Request (User) 76 | 77 | 78 | In terminal: 79 | ```bash 80 | curl http://localhost:5000/todos/todo3 -d "task=something different" -X PUT -v 81 | ``` 82 | 83 | With Python using Requests: 84 | ```python 85 | requests.put('http://localhost:5000/todos/todo1', data={'task': 'Remember the milk'}).json() 86 | ``` 87 | 88 | ##### Parsing Request (API) 89 | args is a dictionary of the data that is passed through requests or curl. 90 | ```python 91 | args = parser.parse_args() 92 | task = {'task': args['task']} 93 | # add task to the TODOS dictionary 94 | TODOS[todo_id] = task 95 | ``` 96 | 97 | 98 | ### Aborting a Request 99 | #### Function Definition 100 | ```python 101 | def abort_if_todo_doesnt_exist(todo_id): 102 | """Abort request if todo_id does not exist in TODOS""" 103 | if todo_id not in TODOS: 104 | abort(404, message="Todo {} doesn't exist".format(todo_id)) 105 | ``` 106 | 107 | #### Implement in a request method 108 | ```python 109 | def delete(self, todo_id): 110 | """Deletes an existing task""" 111 | abort_if_todo_doesnt_exist(todo_id) 112 | del TODOS[todo_id] 113 | # 204: SUCCESS; NO FURTHER CONTENT 114 | return '', 204 115 | ``` 116 | -------------------------------------------------------------------------------- /to-do-api/api.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_restful import reqparse, abort, Api, Resource 3 | 4 | app = Flask(__name__) 5 | api = Api(app) 6 | 7 | TODOS = { 8 | 'todo1': {'task': 'build an API'}, 9 | 'todo2': {'task': '?????'}, 10 | 'todo3': {'task': 'profit!'}, 11 | } 12 | 13 | 14 | def abort_if_todo_doesnt_exist(todo_id): 15 | """Abort request if todo_id does not exist in TODOS""" 16 | if todo_id not in TODOS: 17 | abort(404, message="Todo {} doesn't exist".format(todo_id)) 18 | 19 | 20 | # argument parsing 21 | parser = reqparse.RequestParser() 22 | parser.add_argument('task') 23 | 24 | 25 | # Todo 26 | # shows a single todo item and lets you updatae or delete a todo item 27 | class Todo(Resource): 28 | def get(self, todo_id): 29 | """Return the specified todo item given the todo_id 30 | 31 | Example: 32 | # In the terminal 33 | $ curl http://localhost:5000/todos/todo1 34 | 35 | OR 36 | 37 | # Python 38 | requests.get('http://localhost:5000/todos/todo1').json() 39 | """ 40 | abort_if_todo_doesnt_exist(todo_id) 41 | return TODOS[todo_id] 42 | 43 | def delete(self, todo_id): 44 | """Deletes an existing task 45 | 46 | Example: 47 | # In the terminal 48 | $ curl http://localhost:5000/todos/todo1 -X DELETE -v 49 | 50 | OR 51 | 52 | # Python 53 | requests.delete('http://localhost:5000/todos/todo4') 54 | """ 55 | abort_if_todo_doesnt_exist(todo_id) 56 | del TODOS[todo_id] 57 | # 204: SUCCESS; NO FURTHER CONTENT 58 | return '', 204 59 | 60 | def put(self, todo_id): 61 | """Updates existing task 62 | 63 | Example: 64 | # In the terminal 65 | $ curl http://localhost:5000/todos/todo1 -d "task=Remember the milk" -X PUT -v 66 | 67 | OR 68 | 69 | # Python 70 | requests.put('http://localhost:5000/todos/todo1', 71 | data={'task': 'Remember the milk'}).json() 72 | """ 73 | 74 | # parser 75 | abort_if_todo_doesnt_exist(todo_id) 76 | args = parser.parse_args() 77 | task = {'task': args['task']} 78 | TODOS[todo_id] = task 79 | # 201: CREATED 80 | return task, 201 81 | 82 | 83 | # TodoList 84 | # shows a list of all todos, and lets you POST to add new tasks 85 | class TodoList(Resource): 86 | def get(self): 87 | """Return the current TODO dictionary 88 | 89 | Example: 90 | # In the terminal 91 | $ curl http://localhost:5000/todos 92 | 93 | OR 94 | 95 | # Python 96 | requests.get('http://localhost:5000/todos').json() 97 | """ 98 | return TODOS 99 | 100 | def post(self): 101 | """Adds task to TODO 102 | 103 | Example: 104 | # In the terminal 105 | $ curl http://localhost:5000/todos -d "task=Remember the milk" -X POST -v 106 | 107 | OR 108 | 109 | # Python 110 | requests.post('http://localhost:5000/todos', 111 | data={'task': 'Remember the milk'}).json() 112 | """ 113 | args = parser.parse_args() 114 | todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1 115 | todo_id = 'todo%i' % todo_id 116 | TODOS[todo_id] = {'task': args['task']} 117 | # 201: CREATED 118 | return TODOS[todo_id], 201 119 | 120 | 121 | # Setup the Api resource routing here 122 | # Route the URL to the resource 123 | api.add_resource(TodoList, '/todos') 124 | api.add_resource(Todo, '/todos/') 125 | 126 | 127 | if __name__ == '__main__': 128 | app.run(debug=True) 129 | --------------------------------------------------------------------------------