├── .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 | " PhraseId | \n",
70 | " SentenceId | \n",
71 | " Phrase | \n",
72 | " Sentiment | \n",
73 | "
\n",
74 | " \n",
75 | " \n",
76 | " \n",
77 | " 0 | \n",
78 | " 1 | \n",
79 | " 1 | \n",
80 | " A series of escapades demonstrating the adage ... | \n",
81 | " 1 | \n",
82 | "
\n",
83 | " \n",
84 | " 1 | \n",
85 | " 2 | \n",
86 | " 1 | \n",
87 | " A series of escapades demonstrating the adage ... | \n",
88 | " 2 | \n",
89 | "
\n",
90 | " \n",
91 | " 2 | \n",
92 | " 3 | \n",
93 | " 1 | \n",
94 | " A series | \n",
95 | " 2 | \n",
96 | "
\n",
97 | " \n",
98 | " 3 | \n",
99 | " 4 | \n",
100 | " 1 | \n",
101 | " A | \n",
102 | " 2 | \n",
103 | "
\n",
104 | " \n",
105 | " 4 | \n",
106 | " 5 | \n",
107 | " 1 | \n",
108 | " series | \n",
109 | " 2 | \n",
110 | "
\n",
111 | " \n",
112 | " 5 | \n",
113 | " 6 | \n",
114 | " 1 | \n",
115 | " of escapades demonstrating the adage that what... | \n",
116 | " 2 | \n",
117 | "
\n",
118 | " \n",
119 | " 6 | \n",
120 | " 7 | \n",
121 | " 1 | \n",
122 | " of | \n",
123 | " 2 | \n",
124 | "
\n",
125 | " \n",
126 | " 7 | \n",
127 | " 8 | \n",
128 | " 1 | \n",
129 | " escapades demonstrating the adage that what is... | \n",
130 | " 2 | \n",
131 | "
\n",
132 | " \n",
133 | " 8 | \n",
134 | " 9 | \n",
135 | " 1 | \n",
136 | " escapades | \n",
137 | " 2 | \n",
138 | "
\n",
139 | " \n",
140 | " 9 | \n",
141 | " 10 | \n",
142 | " 1 | \n",
143 | " demonstrating the adage that what is good for ... | \n",
144 | " 2 | \n",
145 | "
\n",
146 | " \n",
147 | " 10 | \n",
148 | " 11 | \n",
149 | " 1 | \n",
150 | " demonstrating the adage | \n",
151 | " 2 | \n",
152 | "
\n",
153 | " \n",
154 | " 11 | \n",
155 | " 12 | \n",
156 | " 1 | \n",
157 | " demonstrating | \n",
158 | " 2 | \n",
159 | "
\n",
160 | " \n",
161 | " 12 | \n",
162 | " 13 | \n",
163 | " 1 | \n",
164 | " the adage | \n",
165 | " 2 | \n",
166 | "
\n",
167 | " \n",
168 | " 13 | \n",
169 | " 14 | \n",
170 | " 1 | \n",
171 | " the | \n",
172 | " 2 | \n",
173 | "
\n",
174 | " \n",
175 | " 14 | \n",
176 | " 15 | \n",
177 | " 1 | \n",
178 | " adage | \n",
179 | " 2 | \n",
180 | "
\n",
181 | " \n",
182 | " 15 | \n",
183 | " 16 | \n",
184 | " 1 | \n",
185 | " that what is good for the goose | \n",
186 | " 2 | \n",
187 | "
\n",
188 | " \n",
189 | " 16 | \n",
190 | " 17 | \n",
191 | " 1 | \n",
192 | " that | \n",
193 | " 2 | \n",
194 | "
\n",
195 | " \n",
196 | " 17 | \n",
197 | " 18 | \n",
198 | " 1 | \n",
199 | " what is good for the goose | \n",
200 | " 2 | \n",
201 | "
\n",
202 | " \n",
203 | " 18 | \n",
204 | " 19 | \n",
205 | " 1 | \n",
206 | " what | \n",
207 | " 2 | \n",
208 | "
\n",
209 | " \n",
210 | " 19 | \n",
211 | " 20 | \n",
212 | " 1 | \n",
213 | " is good for the goose | \n",
214 | " 2 | \n",
215 | "
\n",
216 | " \n",
217 | "
\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 | " PhraseId | \n",
96 | " SentenceId | \n",
97 | " Phrase | \n",
98 | " Sentiment | \n",
99 | "
\n",
100 | " \n",
101 | " \n",
102 | " \n",
103 | " 0 | \n",
104 | " 1 | \n",
105 | " 1 | \n",
106 | " A series of escapades demonstrating the adage ... | \n",
107 | " 1 | \n",
108 | "
\n",
109 | " \n",
110 | " 1 | \n",
111 | " 2 | \n",
112 | " 1 | \n",
113 | " A series of escapades demonstrating the adage ... | \n",
114 | " 2 | \n",
115 | "
\n",
116 | " \n",
117 | " 2 | \n",
118 | " 3 | \n",
119 | " 1 | \n",
120 | " A series | \n",
121 | " 2 | \n",
122 | "
\n",
123 | " \n",
124 | " 3 | \n",
125 | " 4 | \n",
126 | " 1 | \n",
127 | " A | \n",
128 | " 2 | \n",
129 | "
\n",
130 | " \n",
131 | " 4 | \n",
132 | " 5 | \n",
133 | " 1 | \n",
134 | " series | \n",
135 | " 2 | \n",
136 | "
\n",
137 | " \n",
138 | " 5 | \n",
139 | " 6 | \n",
140 | " 1 | \n",
141 | " of escapades demonstrating the adage that what... | \n",
142 | " 2 | \n",
143 | "
\n",
144 | " \n",
145 | " 6 | \n",
146 | " 7 | \n",
147 | " 1 | \n",
148 | " of | \n",
149 | " 2 | \n",
150 | "
\n",
151 | " \n",
152 | " 7 | \n",
153 | " 8 | \n",
154 | " 1 | \n",
155 | " escapades demonstrating the adage that what is... | \n",
156 | " 2 | \n",
157 | "
\n",
158 | " \n",
159 | " 8 | \n",
160 | " 9 | \n",
161 | " 1 | \n",
162 | " escapades | \n",
163 | " 2 | \n",
164 | "
\n",
165 | " \n",
166 | " 9 | \n",
167 | " 10 | \n",
168 | " 1 | \n",
169 | " demonstrating the adage that what is good for ... | \n",
170 | " 2 | \n",
171 | "
\n",
172 | " \n",
173 | "
\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\nZS8iIhJ3LrgAtmyBW2+FXO7TEWkq+RLSuXNnJk2a9LvXR48eTZ8+fQIkEhGRqHMOnn/eXwdfpQrc\nfnuJfr1KPso2bNjAo48+urfgTzvtNLp168auXbvo168fjRs3DpxQRESiIisLrrsOXnwRypSBAQNK\nPIJKPgqmTZtG37592bJlyz6vT5gwgS5dugRKJSIiJSYzE/70J3j9dbjzTv84AJV8hDjn6NKlC99+\n+y3r1q3b+3rTpk259dZbOeecc6hRo0bAhCIiUiIyMvzh+cGD4d574f77I7YWfWGp5Itpy5YtbN++\nnTvuuGPvIfkTTjiBAQMG0L9//8DpRESkxK1bBx9/DA89BPfcEzSKSr4YXnrpJa6++up9Xps3bx5H\nH310oEQiIhJMerq/7r1BA1i4EGrWDJ1IJV9UGRkZfPzxxwA8/fTTlCpViosvvliH5EVEktGuXX4F\nu1at/Ip2MVDwoJIvEufc3sVrzjvvPG644YbAiUREJJidO6FvX3+Ivnfv0Gn2UTJX4yeQG264Ye/6\n8ACDBw8OmEZERILavh3OOAMmTIDXXoP9TuGGppIvhOnTp/PMM88AcPrpp7Njxw7Kli0bOJWIiATh\nHPTpA1Onwltv+Rn1MUaH6wtg9+7d1K1bl02bNgF+wt2fAl3zKCIiMcLMj9wHDPBL1sYglXw+1q1b\nR7169fY+Hzx4MJdccknARCIiEtTmzTBrFnTrBtk3D4tVKvk8ZGZm7lPw27dvp2LFigETiYhIUD//\nDF27wtKlsGJFzMyiz43OyefhnuxFDJo3b056eroKXkQkmW3YAB07wrffwjvvxHzBg0byuVqzZg2P\nP/44AF999RWlS+s/lYhI0lq3Djp3hlWr4H//g06dQicqEI3kD+Dzzz/fey/3Y445htTU1MCJREQk\nqLffhjVr4KOP4qbgQSX/O845TjzxRADOP/98vvrqq8CJREQkGOf8r7ffDl9/DSefHDZPIankc1i6\ndOne8+4VKlRg+PDhWKA7B4mISGBLl8Lxx8OSJf5yucaNQycqNJV8tunTp9O0aVN27doFwPfffx84\nkYiIBPPtt3DqqbBsGezYETpNkank8Yfo+/TpA/gZ9c65fS6dExGRJLJggZ9Fn5EBU6ZAmzahExWZ\nSh5o2LAhW7ZsAeChhx4KnEZERIJZvBhOOw1KlfLL1bZqFTpRsSR9yXfr1o3Vq1cDsGLFisBpREQk\nqEMPhS5dfMEfcUToNMWW1Bd/O+eYMGECAHPnzuWwww4LG0hERMKYNw8OPxxSU2Ho0NBpIiapR/Kb\nN28G4IwzzqBNHJ9zERGRYpg+3V8aN3Bg6CQRl9QlP2LECAC6dOkSOImIiAQxeTJ07w716sEjj4RO\nE3FJe7i+Z8+ejBs3DvDn5UVEJMl8/DGceaa//n3iRKhTJ3SiiEvKkfzMmTP3Fvzdd99NixYtAicS\nEZEStXs3XHstNG/uL5NLwIKHJB3Jn3vuuQCMGzeOHj16BE4jIiIlrmxZvw59jRpQvXroNFGTdCP5\nWbNmsXr1akqVKqWCFxFJNiNHwi23+DXpmzZN6IKHJCz5OXPmAPDmm28GTiIiIiXqv/+FCy+EmTMh\newnzRJdUJT916lSuvvpqADp27Bg2jIiIlJw33oDLLvPr0Y8bBxUqhE5UIpKm5Ddv3ry32Bs3bkz9\n+vXDBhIRkZLx8stwxRV+JbsPPoDKlUMnKjFJU/J7Sv3aa69l2bJlgdOIiEiJOfhg6NsXxoyB7NuJ\nJ4ukKPl77rmHnTt3AvDUU08FTiMiIiXi22/9r336wLvvQvnyYfMEkPAl/8UXX/BI9ipGX3zxBeWT\n8H9kEZGk8+ij/g5yn33mn5uFzRNIwpf8ddddB8Abb7zB8ccfHziNiIhElXPwwANw991+Jv1xx4VO\nFFRCL4YzZ84c5s2bB0C/fv0CpxERkahyDv76V78G/eWXwyuvQEpK6FRBJfRIfsGCBQA88cQTWJIe\nqhERSRrjx/uCHzAAXn016QseEnwkP3bsWADOPvvswElERCTquneH99/3E+00sAMSeCSflZXFqFGj\nAKhVq1bgNCIiEhVZWXD77bBggS/2M89UweeQsCP5PevSn3vuuaSmpgZOIyIiEZeZCVdd5VezO+gg\nP5te9pGwJf9t9vWRr776auAkIiIScRkZ0L8/DBkC998Pd94ZOlFMStiST0lJoV+/flSpUiV0FBER\niaT0dLjkEn9HuUcegbvuCp0oZiXkOfkPP/yQlStXkp6eHjqKiIhEWkYGbN4MTz6pgs9HQo7kL7jg\nAgC6desWOImIiETMrl3+p1o1f7mcLpHLV8KVfEZGBtu2baNt27ZcfvnloeOIiEgk7NjhbzKzdStM\nn66CL6CEK/nVq1cD0KZNm8BJREQkIrZtg969YepUeO01FXwhJFzJv/baawCccsopgZOIiEix/fYb\n9OwJX3wBgwfDxReHThRXEqrkMzIyeOihhwA45phjAqcREZFiu+oq+PJLGDYMzjsvdJq4k1Cz61et\nWgXAaaedxlFHHRU4jYiIFNtjj8F776ngiyihSn7KlCmA7jgnIhLXNm70179nZUHjxtCrV+hEcSuh\nDtc75wA46aSTAicREZEiWb8eOneG5cv9bPoWLUInimsJVfJ7lCtXLnQEEREprLVroVMnWLMGPvxQ\nBR8BCVnyIiISZ1at8gX/009+oRsdkY2IhCr5q666CoBSpRJqqoGISOL7/nt/PfyECXDccaHTJIyE\nKfkdO3bsfVy3bt2ASUREpMC2b4dKlfx5+GXL/GOJmIQZ8o4cORKAgQMHYmaB04iISL4WL4bmzWHo\nUP9cBR9xCVPy27ZtA+Avf/lL4CQiIpKvBQugY0d/R7nWrUOnSVgJU/Jjx44FoHr16oGTiIhInubN\n8wVfurRfj75ly9CJElbClPysWbMAqFmzZuAkIiKSq/Xr/Sz6ihV9wTdvHjpRQkuYki9TpoxG8SIi\nsa5OHbjvPpg2DZo0CZ0m4SVEyf/666+sX7+e3r17h44iIiIHMn06zJnjH99wAxx2WNA4ySIhSv6n\nn34CoF69eoGTiIjI70yaBN27w/XXQ/by41IyEqLk09LSAGihJRBFRGLL+PFwxhn+RjPvvAO6xLlE\nJUTJDx8+HICUlJTASUREZK8PPoA+feCII2DyZDj44NCJkk5ClPw333wDwOmnnx44iYiI7PXaa/4a\n+IkTQVc+BRH3y9o65xgzZgy1a9ematWqoeOIiEhmJqSkwJAhkJYG+rs5mLgfyb/99tuhI4iIyB6D\nB8Pxx8OWLVC+vAo+sLgv+V27dgEwffr0wElERJLca69Bv36Qmgply4ZOI0S55M2sh5l9Z2ZLzezO\nA7zfwMwmm9lcM/vGzHoW9jtc9uUYlXRjAxGRcF58Ea68Erp29RPu9HdyTIhayZtZCvAccDrQArjI\nzPa/xu2vwAjnXFvgQuD5wn7PkCFDAChdOu6nF4iIxKc334RrrvGXyo0e7ZeslZgQzZF8B2Cpc265\nc243MAw4c79tHFAl+3FVYF1hv2TatGkA1K5du+hJRUSk6E47DQYOhHff9efhJWZEs+TrAatzPF+T\n/VpO9wOXmtka4EOg0PeJrVWrFieeeGJRM4qISFGNGeNn0jdoAM8+q/PwMSj0xLuLgDecc/WBnsDb\nZva7TGY2wMxmmdmsjRs37vNeWloaLXWbQhGRkuOcv8nMmWf6Q/USs6JZ8muBQ3M8r5/9Wk5XAiMA\nnHNfAOWB362Y4Jx7yTnX3jnXvlatWntf/+CDD/jtt9/IysqKdHYRETkQ5+Cuu+DBB+GPf4T+/UMn\nkjxEs+S/ApqaWSMzK4ufWDdmv21WAZ0BzOxIfMlvpIDWrFkDwFVXXRWJvCIikhfn4JZb4PHH/US7\nl1/2i95IzIpayTvnMoCBwHhgMX4W/UIze9DM+mRvdgvwJzP7GhgKXO5cwW9RtHz5cgAaNmwY0ewi\nInIAS5bAf/7j7yb3/PNQKvQZX8lPVK87c859iJ9Ql/O1e3M8XgQUedbchAkTAEhNTS3qLkREJD/O\n+bvHNW8O8+ZBs2a6m1yciOt/hi1cuJBKlSppIRwRkWjJzIQrrvCH5sEXvQo+bsT1CjJVqlTh1FNP\nDR1DRCQxZWT4ZWqHDoUmTUKnkSKI65IvXbo0OWfbi4hIhOzeDRdfDO+8A489BnfcETqRFEFcl7yI\niERBVhacd55f7Oapp+Cmm0InkiKK25JPS0tjw4YNoWOIiCSeUqXghBOgWzf4859Dp5FiiNuS/+ab\nbwDdmEZEJGJ27IBly+Coo3R4PkHE7ez6QYMGAdCtW7fASUREEsC2bdCzJ3TsCL/8EjqNREjcDoMz\nMjIAOP300wMnERGJc7/95gt+xgx4+22oVi10IomQuC35X3/9lWbNmlGmTJnQUURE4teWLdCjB8yZ\nA8OHwznnhE4kERSXh+udc3z88cds27YtdBQRkfj2xBMwd66/VE4Fn3DiciS/dOlSAA499NB8thQR\nkTzdf7+/Zexxx4VOIlEQlyP59PR0AG7StZsiIoW3fr2/Dn7jRihbVgWfwOJyJL/nRnWm9ZNFRApn\n7Vro1Mn/+v33oFVDE1pcjuTvuusuQCUvIlIoP/wAp5wCP/4I48f7BW8kocXlSP7HH38EoG/fvoGT\niIjEieXL4bTT4NdfYcIEHaJPEnE5kp81axbVq1fX5XMiIgVVoQLUqweTJqngk0jcjeT3TLo78cQT\nAycREYkDK1dC/fpwyCHw2We6F3ySibuR/J6V7nr27Bk4iYhIjJs/Hzp0gJtv9s9V8Ekn7kp+jxo1\naoSOICISu+bO9efgy5aFgQNDp5FA4rbkRUQkFzNn+svkKlWCqVOhWbPQiSSQuDsnv3XrVkCXz4mI\nHNCuXXDWWXDQQTB5MjRsGDqRBBR3Jb/HCbq+U0Tk98qXh5EjoUEDP+FOklrcHq7X5XMiIjlMnAjP\nP+8fn3CCCl6AOC55ERHJ9tFH0KsXvPgipKWFTiMxRCUvIhLPxo71d5E78ki/0E25cqETSQxRyYuI\nxKt334Wzz4ajj/aH63VpsexHJS8iEq/WrPGL3UyY4GfTi+xHJS8iEm9+/tn/ev31MGUKVK0aNI7E\nLpW8iEg8efVVOPxw+Ppr/1xXGkkeVPIiIvHiP/+Bq67yl8hpFTspAJW8iEg8+Ne/4LrroHdveP99\nf+tYkXyo5EVEYt0HH8CNN8I558CoUbpMTgpMJS8iEut69IBnnoFhw/xd5UQKSCUvIhKLnPOH6H/8\nEUqXhr/8xf8qUggqeRGRWOMc3HGHP0T/0kuh00gc0z8LRURiiXNw001+FH/ttfC3v4VOJHFMI3kR\nkViRlQV//rMv+BtugOeeg1L6a1qKLu7+3/Nz9kpPutWsiCScrVth2jS4/XYYNAjMQieSOGfOudAZ\nCsXMHEC85RYRyVVmpv8pW9YXfeXKKnjZy8xmO+faF+WzcTeSB+jevXvoCCIikZGeDpdeChdd5A/X\np6aq4CVi4rLk69SpEzqCiEjx7d4NF17or38/7jidf5eIi8vZ9b169QodQUSkeNLS4LzzYOxYf/79\nxhtDJ5IEFJclLyIS9/r39wX//PP+UjmRKFDJi4iEcOON0L07XHFF6CSSwHQCSESkpGzdCkOG+MfH\nH6+Cl6hTyYuIlIRff/Uj9379YMmS0GkkSehwvYhItG3Z4gt+7lwYPhyaNQudSJKESl5EJJp+/hm6\ndoVFi+Ddd6F379CJJImo5EVEomnSJPjuOxg92t8XXqQEqeRFRKIhK8svbnP++XDSSVC3buhEkoQ0\n8U5EJNLWrIF27WDKFP9cBS+BaCQvIhJJK1dCp06waROUKxc6jSQ5lbyISKQsW+YL/rff4JNP4Nhj\nQyeSJBeXJd+4cePQEURE9rV2LZxyil+TftIkaNs2dCKR+Dwn36BBg9ARRET2VaeOv+HM5MkqeIkZ\ncTmSFxGJGfPnQ7VqcOih8PTTodOI7CMuR/IiIjFhzhzo2NHfUU4kBqnkRUSKYuZM6NwZUlPhlVdC\npxE5IJW8iEhhffYZdOkC1avD1KmgycASo1TyIiKF4RzcdRcccogv+IYNQycSyZUm3omIFIaZv9FM\nerovepEYppG8iEhBfPghnHsu7N4NNWuq4CUuqORFRPIzejT07QsrVsD27aHTiBRYXJZ8mTJlQkcQ\nkWQxapQfwbdtCxMnwkEHhU4kUmBxWfJVq1YNHUFEksGIEXDhhdChA0yY4Be9EYkjcVfy5cuXDx1B\nRJJFkybQuzeMHw9VqoROI1Jo5pwLnaFQKlSo4Hbu3Bk6hogksjlz/P3gRWKAmc12zrUvymfjbiQv\nIhJVzz0HxxwDw4aFTiJSbCp5EZE9Bg2CgQOhTx8466zQaUSKTSUvIgLw2GNw881wzjkwciSUKxc6\nkUixqeRFRL75Bu6+Gy66yB+mL1s2dCKRiNCytiIirVvDpElw8smQkhI6jUjEaCQvIsnJOT96Hz/e\nP+/YUQUvCUcjeRFJPs7BjTfCM8/Arl3QvXvoRCJRoZIXkeSSlQXXXQcvvgg33QRPPhk6kUjU6HC9\niCSPzEy46ipf8Hfe6QveLHQqkahRyYtI8jDz593vuw8eeUQFLwlPy9qKSOJLT4eNG6FuXX8+XuUu\ncSRml7U1sx5m9p2ZLTWzO3PZ5nwzW2RmC81sSDTziEgS2r0bLrgATjwRtm1TwUtSidrEOzNLAZ4D\nugJrgK/MbIxzblGObZoCdwEnOue2mFntaOURkSS0a5e/F/z//gf/+hdUrhw6kUiJiuZIvgOw1Dm3\n3Dm3GxgGnLnfNn8CnnPObQFwzv0UxTwikkx27IAzz/QF/8ILcP31oROJlLgClbyZlTWzJoXcdz1g\ndY7na7Jfy6kZ0MzMPjOzGWbWo5DfISJyYPfcAxMmwGuvwdVXh04jEkS+JW9mZwDzgQnZz9uY2XsR\n+v7SQFOgI3AR8LKZVTtAhgFmNsvMZmVmZkboq0Ukod13H4weDVdcETqJSDAFGck/CBwH/ALgnJsH\nFGRUvxY4NMfz+tmv5bQGGOOcS3fOrQCW4Et/H865l5xz7Z1z7VO07KSI5OaXX+DWW/25+GrVoHfv\n0IlEgipIyac7537Z77WCXHf3FdDUzBqZWVngQmDMftu8jx/FY2Y18Yfvlxdg3yIi+9q8Gbp08UvV\nfvVV6DQiMaEgJb/YzM4HSmUX9iBgRn4fcs5lAAOB8cBiYIRzbqGZPWhmfbI3Gw9sMrNFwGTgNufc\npiL9TkQkef38M3TqBPPnw7vv+rvJiUj+i+GYWSXgXqBb9kvjgQecc0FWpNFiOCKyjw0boHNnWLbM\nn4Pv1i3/z4jEkeIshlOQ6+S7O+fuAO7I8YVnA+8W5QtFRCJq0ya/yM3//udH8yKyV0FG8nOcc+32\ne222c+6YqCbLhUbyIgLAli1+cp2ZX9WubNnQiUSiIiojeTPrDvQA6pnZUzneqgJkFeXLREQiYuVK\nP2q/8kp/PbwKXuSA8jpc/xNvJ10qAAAgAElEQVSwANgFLMzx+lbggOvQi4hE3dKlvuC3bYPu3UOn\nEYlpBTlcX945t6uE8uRLh+tFkth33/mC373br2bXpk3oRCJRF+2Jd/XM7GGgBVB+z4vOuWZF+UIR\nkSLZscPPos/IgMmToVWr0IlEYl5BrpN/A3gdMOB0YAQwPIqZRER+r2JFGDQIpk5VwYsUUEEO1892\nzh1jZvOdc0dlvzarqIcOikuH60WSzOzZsG6dlqiVpBXtw/VpZlYKWGZm1+DXn08typeJiBTKjBnQ\nowfUqeN/LVMmdCKRuFKQkr8JqARcDzwMVAX+GM1QIiJMnw6nnw4HHwwff6yCFymCfEveOfdl9sOt\nwGUAZrb/feFFRCJnyhQ44ww49FCYOBHq6a8ckaLIc+KdmR1rZn2z7xCHmbU0s7eAL/P6nIhIsfzv\nf3DYYb7sVfAiRZbrxDszexQ4B/gaaAR8AFwHPA78xzm3o6RC5qSJdyIJLC0NypUD5+C336Bq1dCJ\nRIKL1sS7M4GjnXM7zaw6sBo4yjmn+72LSOS9/z7cfDN88gk0bqyCF4mAvA7X79pzO1nn3GZgiQpe\nRKJi5Eg47zyoXRuqVw+dRiRh5DWSb2xme24na0CjHM9xzp0d1WQikhz++1/o1w9OOMGfi69SJXQi\nkYSRV8mfs9/zf0cziIgkoQ8+gMsug44dYcwYqFw5dCKRhJJryTvnJpZkEBFJQqeeCrfcAg884Jet\nFZGIKsja9SIikTVqFGzfDqmp8MQTKniRKFHJi0jJevJJP8nuySdDJxFJeAUueTMrF80gIpIEHnkE\nbr3Vl/xdd4VOI5Lw8i15M+tgZvOB77OfH21mz0Y9mYgkDufg/vvhnnvgkktgyBCtRS9SAgoykn8G\n6AVsAnDOfQ2cFs1QIpJgfv4ZXngBLr8c3nwTShfk3lgiUlwF+ZNWyjn3g5nlfC0zSnlEJJHsWTa7\nVi2YNQvq1oVSmgokUlIK8qdttZl1AJyZpZjZjcCSKOcSkXiXlQXXXw+33ebLvn59FbxICSvIn7hr\ngZuBBsAG4Pjs10REDiwrC669Fv6tNbREQirI4foM59yFUU8iIokhMxOuugreeAPuvhseegj2Pd0n\nIiWkICP5r8zsQzPrb2apUU8kIvFtT8E/8IAKXiSwfEfyzrnDzewE4ELgATObBwxzzg2LejoRiT+9\nesERR8Add4ROIpL0zO2Z/VqQjf195Z8GLnHOpUQtVR4qVKjgdu7cGeKrRSQ3aWnw5Zdwyimhk4gk\nHDOb7ZxrX5TPFmQxnMpmdomZjQVmAhuBE4ryZSKSgHbtgrPPhi5dYMWK0GlEJIeCTLxbAIwF/uGc\n+zTKeUQknuzYAX37woQJ8OKL0KhR6EQikkNBSr6xcy4r6klEJL5s2wa9e8PUqfDaa3DFFaETich+\nci15M3vSOXcL8I6Z/e7EvXPu7KgmE5HYNngwTJsGb7/t16MXkZiT10h+ePavWs1CRH7v6qvhuOOg\nbdvQSUQkF7lOvHPOzcx+eKRzbmLOH+DIkoknIjFl82Y44wxYvNhf/66CF4lpBVkM548HeO3KSAcR\nkRi3cSOcdhpMnAg//BA6jYgUQF7n5C/AL4DTyMzezfFWKvBLtIOJSAxZvx46d4bly2HMGOjWLXQi\nESmAvM7Jz8TfQ74+8FyO17cCc6MZSkRiyPr10LEjrF4NH37oR/MiEhdyLXnn3ApgBfBJycURkZhT\npQo0awavvAInnRQ6jYgUQl6H66c65041sy1AzkvoDHDOuepRTyci4fzwAxx0kC/5MWNCpxGRIsjr\ncP2eY3I1SyKIiMSQ77+HTp387HkVvEjcyusSuj2r3B0KpDjnMoE/AFcDlUogm4iEsHgxnHqqX5P+\n738PnUZEiqEgl9C9DzgzOxx4HWgKDIlqKhEJY8ECP8kuKwumTIGjjw6dSESKoSAln+WcSwfOBp51\nzt0E1ItuLBEpcc7BZZdB6dJ+PfqWLUMnEpFiKsgNajLM7DzgMqBv9mtlohdJRIIwgxEj/K9NmoRO\nIyIRUNAV707D32p2uZk1AoZGN5aIlJgvvoA77vAj+aZNVfAiCcSc+90N5n6/kVlpYM+f/KXOuYyo\npspDhQoV3M6dO0N9vUhimTbNr0Vfpw58+SVU15WxIrHGzGY759oX5bP5Hq43s5OBt4G1+Gvk65jZ\nZc65z4ryhSISIyZN8veDb9DAr0evghdJOAU5Jz8I6OmcWwRgZkfiS79I/6oQkRgwfjz07esPzX/y\nCRx8cOhEIhIFBTknX3ZPwQM45xYDZaMXSUSiLj0dWreGyZNV8CIJLN9z8mb2BrALGJz90iVARedc\n/+hGOzCdkxcphrVroV72FbBZWVCqIP/OF5GQinNOviB/wq8BlgO3Z/8sx696JyLxZPhwOPxw+Ogj\n/1wFL5Lw8jwnb2ZHAYcD7znn/lEykUQk4gYPhv794cQT/Y+IJIVc/ylvZnfjl7S9BJhgZn8ssVQi\nEjmvvQb9+vnlaseNg9TU0IlEpITkek7ezBYCHZxz282sFvChc+7YEk13ADonL1IIs2dD+/bQrRu8\n/z5UqBA6kYgUUrTOyac557YDOOc25rOtiMSidu3grbdg9GgVvEgSymsk/wswac9T/NK2e57jnDs7\n6ukOQCN5kQL497/hlFP8ZXIiEteiteLdOfs9/3dRvkBEStjDD8Nf/wrXXAP/+U/oNCISUK4l75yb\nWJJBRKSYnIP774cHH4RLL4Vnnw2dSEQCK8iytiIS65yDu+6Cxx+HK66Al1+GlJTQqUQkME2mE0kE\nGRkwd64/RP/KKyp4EQEKMZI3s3LOubRohhGRQsrKgu3b/bXvY8ZA2bJgFjqViMSIfEfyZtbBzOYD\n32c/P9rMdLJPJLSsLLj6aujUCXbuhHLlVPAiso+CHK5/BugFbAJwzn2Nv5xORELJzIQ//tEfmu/e\nHcqXD51IRGJQQQ7Xl3LO/WD7jhAyo5RHRPKTkeGXqR061M+k/9vfQicSkRhVkJJfbWYdAGdmKcBf\ngCXRjSUiubr1Vl/wjz0Gd9wROo2IxLCClPy1+EP2DYANwCfZr4lICDfeCC1awIABoZOISIzLdVnb\nWKVlbSUp7dzpr30fOFD3gRdJMtFa1nbPzl8GfvcvAeechhEiJWHHDjjzTJg4Edq2hZNPDp1IROJE\nQQ7Xf5LjcXngLGB1dOKIyD62bYNeveDTT+GNN1TwIlIo+Za8c254zudm9jYwPWqJRMT77Tfo2RNm\nzIDBg+Gii0InEpE4U5S16xsBB0c6iIjsZ/58+PprGD4cztn/ppAiIvkryDn5Lfz/OflSwGbgzmiG\nEklq6elQpgyceCKsXAk1aoROJCJxKs9puuZXwDkaqJX9c5BzrrFzbkRJhBNJOj/9BMce68+/gwpe\nRIolz5J3/vq6D51zmdk/8XW9nUg8+fFH6NgRliyBQw8NnUZEEkBBLridZ2Zto55EJJmtXesLftUq\nGDcOOncOnUhEEkCu5+TNrLRzLgNoC3xlZsuA7YDhB/ntSiijSGLbuhVOOQU2boTx4/25eBGRCMhr\n4t1MoB3Qp6g7N7MewL+AFOAV59xjuWx3DjAKONY5N6uo3ycSl1JT4Zpr4NRToUOH0GlEJIHkVfIG\n4JxbVpQdZ9/M5jmgK7AGfzRgjHNu0X7bpQI3AF8W5XtE4tb33/tRfLt2cNttodOISALKq+RrmdnN\nub3pnHsqn313AJY655YDmNkw4Exg0X7b/R14HNDfcpI8Fi+GTp2galVYuBBSUkInEpEElNfEuxSg\nMpCay09+6rHv8rdrsl/by8zaAYc65/5XiMwi8W3+fH9oHuDdd1XwIhI1eY3kf3TOPRitLzazUsBT\nwOUF2HYAMACgTJky0YokEn1z50LXrlC+PEyaBM2ahU4kIgksr5G8FXPfa4GcF/vWz35tj1SgFTDF\nzFYCxwNjzOx3t9Nzzr3knGvvnGufolGPxLNBg6BSJZg6VQUvIlGX6/3kzay6c25zkXdsVhpYAnTG\nl/tXwMXOuYW5bD8FuDW/2fW6n7zEJefADNLS/KVy9euHTiQicaI495PPdSRfnILP/nwGMBAYDywG\nRjjnFprZg2ZW5MvyROLOtGn+FrGbNkG5cip4ESkxuY7kY5VG8hJXJk6E3r2hYUN/Dv6QQ0InEpE4\nE5WRvIgU00cfQa9ecPjhMGWKCl5ESpxKXiQaPv4YzjwTjjgCJk+Ggw8OnUhEkpBKXiQaWrSAvn39\nIfqaNUOnEZEkpZIXiaTPPoPMTD+5bvhwOOig0IlEJImp5EUi5a23/N3knnwydBIREUAlLxIZr74K\nl1/u7wn/5z+HTiMiAqjkRYrv+efhqquge3f44AO/op2ISAzQdfIixfHjj9CkCXTuDCNH+sVuREQi\nqDjXyed1gxoRyc8hh8D06dCyJZQtGzqNiMg+dLhepCgeegheeME/bttWBS8iMUklL1IYzsHf/uZ/\nZszwz0VEYpQO14sUlHNwxx3wxBNw5ZXw4ov+znIiIjFKI3mRgnAObr7ZF/y118JLL0FKSuhUIiJ5\nUsmLFIQZ1KsHN94Izz0HpfRHR0Riny6hE8lLZiYsWwbNmvnnzukQvYiUKN1qViQaMjPhiivg2GNh\n7Vr/mgpeROKISl7kQNLT4dJL4e234bbb/KF6EZE4o9n1IvvbvRsuvhjeeQcefxxuvz10IhGRIlHJ\ni+zv2Wd9wQ8a5CfaiYjEKZW8yP6uvx6OPBJ69gydRESkWHROXgRg+3a4+mr46ScoU0YFLyIJQSUv\nsnUrnH46vPIKfP556DQiIhGjw/WS3H791Rf8zJkwZAj07Rs6kYhIxKjkJXlt2QLdu8PcuTBiBJx9\nduhEIiIRpZKX5LV7t/95913o3Tt0GhGRiFPJS/LZtAmqVIGDD4bZs3WjGRFJWJp4J8nlxx/h5JNh\nwAD/XAUvIglMJS/JY80aOPVUWLUKLr88dBoRkajT4XpJDitXQqdO/lD9xx/DCSeETiQiEnUqeUl8\nWVl+Yt2WLfDJJ/6uciIiSUAlL4mvVCl44QWoWBHatg2dRkSkxOicvCSuRYvgxRf94xNPVMGLSNLR\nSF4S0zffQJcuULo0XHABVKsWOpGISInTSF4Sz5w5cNppULYsTJmigheRpKWSl8Ty5Zd+Fn1qKkyb\nBs2ahU4kIhKMSl4Sy7x5ULMmTJ0KjRuHTiMiEpQ550JnKJQKFSq4nTt3ho4hsWb7dqhUyT/escPP\npBcRSQBmNts5174on9VIXuLfJ59Ao0YwY4Z/roIXEQFU8hLvPvwQevWCOnV0eF5EZD8qeYlfo0dD\n377QsiVMngy1a4dOJCISU1TyEp9mzIBzz/UL3EycCDVqhE4kIhJzVPISn9q3h3vvhQkTdB28iEgu\nNLte4svIkX6J2rp1QycRESkRml0vyeHll/0StQ8+GDqJiEhcUMlLfHjuORgwAHr0gKefDp1GRCQu\nqOQl9g0aBAMHQp8+8N57UL586EQiInFB5+Qltu3aBR06+DXohwzxN50REUkixTknr1vNSuzKyPCj\n9smToWpVf9tYEREpMB2ul9jjHPz1r/46+PR0fw28Cl5EpNBU8hJbnIPbb4eHH/Yr2KWkhE4kIhK3\nVPISO5yDG2+Ef/4T/vxneOEFKKX/i4qIFJX+BpXYcccd8MwzcNNN8OyzKngRkWLSiU6JHeed5+8J\nf++9YBY6jYhI3NMldBJWRoa/XWyfPqGTiIjEJC1rK/EpPR0uuQTOPNPfVU5ERCJKh+sljN274cIL\n/Qp2TzwBxx8fOpGISMJRyUvJ27XLXwP/v//Bv/4F118fOpGISEJSyUvJmzwZxo3zl8hdfXXoNCIi\nCUslLyXHOT9r/vTTYdEiaN48dCIRkYSmiXdSMrZu9beJnTTJP1fBi4hEnUpeou+XX6BbN5g4EX7+\nOXQaEZGkocP1El2bN/uC/+YbGDkSzjordCIRkaShkpfo+fVX6NQJFi+Gd9+FXr1CJxIRSSo6XC/R\nk5oKf/gDjBmjghcRCUDL2krkrVvnl6tt0CB0EhGRuFecZW11uF4ia/Vqf4i+cmWYPVt3khMRCUgl\nL5GzYoUv+M2b4a23VPAiIoGp5CUyli71Bb9tm79Urn2RjiyJiEgEqeQlMm6+GXbu9IvdtGkTOo2I\niKCSl0h54w1Yvx5atAidREREsumkqRTd11/DZZdBWhpUr66CFxGJMRrJS9HMng1du0KlSrBhgy6X\nExGJQRrJS+HNmAGdO0PVqjBtmgpeRCRGqeSlcD77zI/ga9aEqVOhUaPQiUREJBcqeSmcypWhdWtf\n8BrBi4jENC1rKwWzdCk0aeIfOwdmYfOIiCSJ4ixrq5G85O/DD6FVK3jxRf9cBS8iEhdU8pK30aOh\nb19o2RLOPTd0GhERKYSolryZ9TCz78xsqZndeYD3bzazRWb2jZlNNLOG0cwjhTRypC/2du38UrU1\naoROJCIihRC1kjezFOA54HSgBXCRme2/WspcoL1zrjUwCvhHtPJIIa1aBZdcAscfDx9/DNWqhU4k\nIiKFFM2RfAdgqXNuuXNuNzAMODPnBs65yc65HdlPZwD1o5hHCqNBA3jvPRg3DqpUCZ1GRESKIJol\nXw9YneP5muzXcnMlMC6KeaQgXn7ZT7QDOOMMf8mciIjEpZiYeGdmlwLtgSdyeX+Amc0ys1mZmZkl\nGy6Z/PvfMGAAvPZa6CQiIhIB0Sz5tcChOZ7Xz35tH2bWBbgH6OOcSzvQjpxzLznn2jvn2qekpEQl\nbNJ76in4y1/8TPohQ0KnERGRCIhmyX8FNDWzRmZWFrgQGJNzAzNrC7yIL/ifophF8vLoo3DLLXDe\neTBiBJQtGzqRiIhEQNRK3jmXAQwExgOLgRHOuYVm9qCZ9cne7AmgMjDSzOaZ2ZhcdifR4hz88IOf\nST9kCJQpEzqRiIhEiJa1TVbOwaZN/kYzWVn+uU6FiIjEHC1rK4XjHNx6q1/k5qefoFQpFbyISAJS\nySebrCy4/no/0a5vX6hVK3QiERGJEpV8MsnKgmuv9ZfK3XIL/OtfutmMiEgCU8knkyeegJdegrvv\n9o9V8CIiCa106ABSgq65BqpXh6uuUsGLiCQBjeQTXXo6PPII7NgBVavCn/6kghcRSRIq+USWlgbn\nnw/33PP/69GLiEjS0OH6RLVrF5xzji/3Z5/194UXEZGkopJPRDt2+MvjPvkEXnzR33RGRESSjko+\nEa1bB/Pn+7vJXX556DQiIhKISj6R7NwJ5ctDkyawZAmkpoZOJCIiAWniXaL45Rc47TS4917/XAUv\nIpL0VPKJYPNm6NIF5syB9kW6h4GIiCQgHa6Pdxs3+oL/7jt4/33o2TN0IhERiREq+XiWkQFdu/rz\n72PH+sciIiLZVPLxrHRpvw59rVr+fLyIiEgO5pwLnaFQKlSo4Hbu3Bk6RlirVsGCBTo0LyKSBMxs\ntnOuSBOuNJKPNytWQKdOsH07LF8OlSuHTiQiIjFKJR9Pvv/+/wt+wgQVvIiI5EklHy8WL4bOnf1d\n5SZPhqOPDp1IRERinEo+XgwfDllZMGUKtGwZOo2IiMQBTbyLdVlZUKoUOAfr18Mhh4ROJCIiJag4\nE++04l0smzULWrf218GbqeBFRKRQVPKx6osv/Dn47duhbNnQaUREJA6p5GPRp59Ct25QuzZMmwaH\nHRY6kYiIxCGVfKyZORN69ID69WHqVDj00NCJREQkTqnkY02LFnDRRX4Wfd26odOIiEgc0+z6WDF1\nKrRrp/vAi4jIPjS7Pt69956/g9zdd4dOIiIiCUQlH9rw4XDeedC+PTz0UOg0IiKSQFTyIQ0eDBdf\nDCecAOPHQ9WqoROJiEgC0Tn5ULZvh+bN/c+YMVCpUuhEIiISg3Sr2XhUqZKfbFe3LlSoEDqNiIgk\nIB2uL2nPPAM33eTXoj/8cBW8iIhEjUq+JP3zn3DDDfDDD5CZGTqNiIgkOJV8SXn4YbjtNjj/fD+j\nvrTOlIiISHSp5EvC3/8Of/0rXHop/Pe/UKZM6EQiIpIEVPIloXVrGDAA3nhDI3gRESkxuoQuWpyD\nuXP9UrUiIiJFpGVtY01WFvzlL9ChA8ybFzqNiIgkKR07jrSsLLj6anjlFT/R7uijQycSEZEkpZF8\nJGVmwh//6Av+nnvg8cfBLHQqERFJUir5SHrnHXjzTXjwQX+zGRW8iIgEpMP1kXTeeVCrFpx2Wugk\nIiIiGskXW1qaP0S/cKEfuavgRUQkRqjki2PnTjjrLHj9dZgxI3QaERGRfehwfVHt2AFnngkTJ8JL\nL8GVV4ZOJCIisg+VfFFs2wa9esGnn/pRfP/+oROJiIj8jkq+KFJSoFw5GDwYLroodBoREZEDUskX\nxi+/+Ml1VavCRx/pEjkREYlpmnhXUJs2QadO/jy8cyp4ERGJeRrJF8RPP0GXLrBkCbz3ngpeRETi\ngko+Pz/+CJ07w8qV8MEHvuxFRETigEo+P/36wapVMG4cnHpq6DQiIiIFppLPzwsvwIYNcMIJoZOI\niIgUiibeHcjy5XDvvX6C3eGHq+BFRCQuqeT3t2QJnHIKPPecP0wvIiISp1TyOS1e7M+7794NkydD\nw4ahE4mIiBSZSn6P+fP/f2LdlCnQunXQOCIiIsWliXd7rF0Lqal+Fn2zZqHTiIiUiPT0dNasWcOu\nXbtCR0l65cuXp379+pQpUyZi+zTnXMR2VhIqVKjgdu7cGbkdbtkCBx3kH+/eDWXLRm7fIiIxbsWK\nFaSmplKjRg1MC30F45xj06ZNbN26lUaNGu3znpnNds61L8p+k/tw/eefQ+PG8M47/rkKXkSSzK5d\nu1TwMcDMqFGjRsSPqCRvyU+bBt26Qa1acNxxodOIiASjgo8N0fjfITlLfuJE6NEDGjSAqVOhfv3Q\niUREktr777+PmfHtt9/ufW3KlCn06tVrn+0uv/xyRo0aBfj5BHfeeSdNmzalXbt2/OEPf2DcuHHF\nzvLoo4/SpEkTmjdvzvjx4w+4zaRJk2jXrh2tWrWif//+ZGRkAPDrr7/Su3dvjj76aFq2bMnrr7++\n9zOrVq2iW7duHHnkkbRo0YKVK1cWO2t+kq/kly+HXr2gSRM/i/6QQ0InEhFJekOHDuWkk05i6NCh\nBf7M3/72N3788UcWLFjAnDlzeP/999m6dWuxcixatIhhw4axcOFCPvroI6677joyMzP32SYrK4v+\n/fszbNgwFixYQMOGDXnzzTcBeO6552jRogVff/01U6ZM4ZZbbmH37t0A9OvXj9tuu43Fixczc+ZM\nateuXaysBZF8Jd+4MTz1FEyaBCXwH1hERPK2bds2pk+fzquvvsqwYcMK9JkdO3bw8ssv8+yzz1Ku\nXDkADj74YM4///xiZRk9ejQXXngh5cqVo1GjRjRp0oSZM2fus82mTZsoW7YszbKvxOratSvvZM/t\nMjO2bt2Kc45t27ZRvXp1SpcuzaJFi8jIyKBr164AVK5cmYoVKxYra0EkzyV077/vF7dp2xauvTZ0\nGhGRmHPjjTcyb968iO6zTZs2PP3003luM3r0aHr06EGzZs2oUaMGs2fP5phjjsnzM0uXLqVBgwZU\nqVIl3ww33XQTkydP/t3rF154IXfeeec+r61du5bjjz9+7/P69euzdu3afbapWbMmGRkZzJo1i/bt\n2zNq1ChWr14NwMCBA+nTpw9169Zl69atDB8+nFKlSrFkyRKqVavG2WefzYoVK+jSpQuPPfYYKSkp\n+eYvjuQo+WHD4NJL4YwzYPTo0GlERCSHoUOHcsMNNwC+eIcOHcoxxxyT60S0wk5QGzRoULEz7v/9\nw4YN46abbiItLY1u3brtLevx48fTpk0bJk2axLJly+jatSsnn3wyGRkZfPrpp8ydO5cGDRpwwQUX\n8MYbb3DllVdGNNv+Er/k33oLrrgCTjoJBg8OnUZEJGblN+KOhs2bNzNp0iTmz5+PmZGZmYmZ8cQT\nT1CjRg22bNnyu+1r1qxJkyZNWLVqFb/99lu+o/nCjOTr1au3d1QOsGbNGurVq/e7z/7hD3/g008/\nBeDjjz9myZIlALz++uvceeedmBlNmjShUaNGfPvtt9SvX582bdrQuHFjAPr27cuMGTOiXvKJfU7+\n1Vfh8suhY0f48EO/op2IiMSMUaNGcdlll/HDDz+wcuVKVq9eTaNGjfj0009p2rQp69atY/HixQD8\n8MMPfP3117Rp04aKFSty5ZVXcsMNN+yd2LZx40ZGjhz5u+8YNGgQ8+bN+93P/gUP0KdPH4YNG0Za\nWhorVqzg+++/p0OHDr/b7qeffgIgLS2Nxx9/nGuuuQaABg0aMHHiRAA2bNjAd999R+PGjTn22GP5\n5Zdf2LhxI+Bn57do0SIC/wXzlrgl7xy8+y507w4ffACVKoVOJCIi+xk6dChnnXXWPq+dc845DB06\nlHLlyjF48GCuuOIK2rRpw7nnnssrr7xC1apVAXjooYeoVasWLVq0oFWrVvTq1atA5+jz0rJlS84/\n/3xatGhBjx49eO655/Yeiu/Zsyfr1q0D4IknnuDII4+kdevW9O7dm06dOgF+xv/nn3/OUUcdRefO\nnXn88cepWbMmKSkp/POf/6Rz584cddRROOf+r727D5ayLOM4/v2pwIFUShkbFRMc38A3UnJIZzLD\nHLVJJiXQQZSG8KWsUbOhRnxJ/cPXGkkNSRRxUEzLYowyxzDKAZVSECWVkLFTThoRk4qEh6s/7pta\nTgf2OYezu2ef8/vM7Mzu83qd6+zstc/93HvfTJkyZYdiLaKcw9pu3Aj9+sGGDbDTTum5mZn9n5Ur\nVzJs2LBGh2FZR/8PD2tb6aab4LjjYP166N/fBd7MzHqtchX5666DqVPTLHJ1+P2hmZlZT1aOIh8B\nV14JV10FEyemXvTdOFWfmZlZMypHkb/lFrj+epg8Ge69F2o8uICZWZk0W9+ssqrF/6Ecv5MfPx7e\neQeuvjp1tDMzs0JaWiUjyWcAAAiTSURBVFpYu3atp5ttsC3zybe0tHTrcZu3d/3mzTB3LkyY4MJu\nZtZFmzZtorW1tdvnMbfOa2lpYfDgwfRpd7t5R3rX1/RKXtIpwG3AzsDdEXFDu/X9gDnAMcBaYHxE\nrKl64LY2uOCCNNjNgAFw5pndHruZWW/Qp08fhg4d2ugwrEZqdgksaWfgDuBUYDhwtqT2w/tMBtZF\nxIHA94Abqx4X0jC1s2alznZnnNG9gZuZmZVELdu5jwVWRcTqiPg3MA8Y026bMcB9+fkjwGhVuSm0\n76ZNcP/96edy114LvodkZmbWoVoW+X2BP1e8bs3LOtwmIj4A1gN7bu+gu7e1wY03wrRp3RiqmZlZ\n+TRF73pJ5wPn55cbNXXqCqZObWRIZTcI+Hujg+gFnOfac45rzzmuvUO6umMti/xfgP0qXg/Oyzra\nplXSLsBAUge8rUTETGAmgKSlXe1laMU4x/XhPNeec1x7znHtSVra1X1r2Vz/HHCQpKGS+gJnAfPb\nbTMfOC8/Hwv8OprtN31mZmY9VM2u5CPiA0kXA4+TfkJ3T0S8JOlaYGlEzAdmAfdLWgX8g/RFwMzM\nzLpBTe/JR8QCYEG7ZVdVPH8f+GInDzuzG0Kz7XOO68N5rj3nuPac49rrco6bbsQ7MzMzK8bjwZqZ\nmZVUjy3ykk6R9IqkVZK+1cH6fpIeyuufkTSk/lE2twI5vkzSy5KWS3pS0v6NiLOZVctxxXZnSgpJ\n7qXcBUXyLGlcfj+/JOmBesfY7Ap8XnxM0kJJz+fPjNMaEWczk3SPpLckrdjGekmanv8HyyUdXfWg\nEdHjHqSOen8CDgD6AsuA4e22+QowIz8/C3io0XE306Ngjk8EBuTnFznH3Z/jvN1uwCJgCTCy0XE3\n26Pge/kg4HngI/n1Xo2Ou5keBXM8E7goPx8OrGl03M32AD4FHA2s2Mb604BfkEZ4HwU8U+2YPfVK\nviZD4tpWquY4IhZGxHv55RLSWAdWXJH3McB1pHkbPA1Y1xTJ8xTgjohYBxARb9U5xmZXJMcB7J6f\nDwT+Wsf4SiEiFpF+abYtY4A5kSwBPixp7+0ds6cW+ZoMiWtbKZLjSpNJ3yCtuKo5zs1t+0XEz+sZ\nWMkUeS8fDBws6WlJS/IMmVZckRxfA5wjqZX0q6qv1Se0XqWzn9vNMaytNZakc4CRwAmNjqVMJO0E\nfBeY1OBQeoNdSE32nya1SC2SdERE/LOhUZXL2cDsiLhV0idJY6AcHhGbGx1Yb9ZTr+Q7MyQu2xsS\n17apSI6RdBJwBXB6RGysU2xlUS3HuwGHA09JWkO6xzbfne86rch7uRWYHxGbIuJ14FVS0bdiiuR4\nMvAjgIhYDLSQxrW37lPoc7tSTy3yHhK39qrmWNLHgbtIBd73MDtvuzmOiPURMSgihkTEEFK/h9Mj\nosvjVPdSRT4vfkq6ikfSIFLz/ep6BtnkiuT4DWA0gKRhpCL/dl2jLL/5wLm5l/0oYH1EvLm9HXpk\nc314SNyaK5jjm4FdgYdzn8Y3IuL0hgXdZArm2HZQwTw/Dpws6WWgDfhmRLjlr6CCOf4G8ENJl5I6\n4U3yhVfnSHqQ9GV0UO7bcDXQByAiZpD6OpwGrALeA75U9Zj+H5iZmZVTT22uNzMzsx3kIm9mZlZS\nLvJmZmYl5SJvZmZWUi7yZmZmJeUib1ZnktokvVDxGLKdbYdsa0aqTp7zqTyD2LI8tOshXTjGhZLO\nzc8nSdqnYt3dkoZ3c5zPSRpRYJ9LJA3Y0XOblZGLvFn9bYiIERWPNXU674SIOIo0sdPNnd05ImZE\nxJz8chKwT8W6L0fEy90S5f/ivJNicV4CuMibdcBF3qwHyFfsv5X0h/w4roNtDpP0bL76Xy7poLz8\nnIrld0naucrpFgEH5n1H5/m/X8xzWffLy2/Ic68vl3RLXnaNpMsljSXNZTA3n7N/vgIfma/2/1uY\n8xX/7V2MczEVk29I+oGkpUrzwX8nL/s66cvGQkkL87KTJS3OeXxY0q5VzmNWWi7yZvXXv6Kp/tG8\n7C3gsxFxNDAemN7BfhcCt0XECFKRbc3Dh44Hjs/L24AJVc7/eeBFSS3AbGB8RBxBGgHzIkl7Al8A\nDouII4HrK3eOiEeApaQr7hERsaFi9Y/zvluMB+Z1Mc5TSMPRbnFFRIwEjgROkHRkREwnTWl6YkSc\nmIesnQaclHO5FLisynnMSqtHDmtrVnIbcqGr1Ae4Pd+DbiONrd7eYuAKSYOBn0TEa5JGA8cAz+Wh\nh/uTvjB0ZK6kDcAa0jSghwCvR8Sref19wFeB20lz28+S9BjwWNE/LCLelrQ6j6v9GnAo8HQ+bmfi\n7EsaUrkyT+MknU/63NobGA4sb7fvqLz86XyevqS8mfVKLvJmPcOlwN+Ao0gtbO+33yAiHpD0DPA5\nYIGkCwAB90XEtwucY0Ll5DeS9uhoozxO+bGkyUbGAhcDn+nE3zIPGAf8EXg0IkKp4haOE/g96X78\n94EzJA0FLgc+ERHrJM0mTYDSnoAnIuLsTsRrVlpurjfrGQYCb+a5tyeSJgHZiqQDgNW5ifpnpGbr\nJ4GxkvbK2+whaf+C53wFGCLpwPx6IvCbfA97YEQsIH35OKqDff9Fmiq3I48CY0jzi8/LyzoVZ57Y\n5EpglKRDgd2Bd4H1kj4KnLqNWJYAx2/5myR9SFJHrSJmvYKLvFnPcCdwnqRlpCbudzvYZhywQtIL\npHno5+Qe7dOAX0laDjxBasquKiLeJ81i9bCkF4HNwAxSwXwsH+93dHxPezYwY0vHu3bHXQesBPaP\niGfzsk7Hme/130qaMW4Z8DypdeAB0i2ALWYCv5S0MCLeJvX8fzCfZzEpn2a9kmehMzMzKylfyZuZ\nmZWUi7yZmVlJucibmZmVlIu8mZlZSbnIm5mZlZSLvJmZWUm5yJuZmZWUi7yZmVlJ/Qcm0s/0wrlp\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 |
--------------------------------------------------------------------------------