Skip to content

Commit 7cc139c

Browse files
committed
Working on article for Simpson's Paradox
1 parent 931539e commit 7cc139c

File tree

12 files changed

+1386
-484
lines changed

12 files changed

+1386
-484
lines changed
Lines changed: 339 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,343 @@
11
{
2-
"cells": [],
3-
"metadata": {},
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# Introduction: Interacting with Slack from Python\n",
8+
"\n",
9+
"In this notebook, we will use `Slacker` to interact with Slack. This allows us to send messages and upload files, which can be useful when training models, or monitoring scripts (or just for fun of course)!"
10+
]
11+
},
12+
{
13+
"cell_type": "code",
14+
"execution_count": 1,
15+
"metadata": {},
16+
"outputs": [
17+
{
18+
"name": "stderr",
19+
"output_type": "stream",
20+
"text": [
21+
"distributed 1.20.2 has requirement tornado>=4.5.1, but you'll have tornado 4.3 which is incompatible.\n",
22+
"You are using pip version 10.0.1, however version 18.0 is available.\n",
23+
"You should consider upgrading via the 'python -m pip install --upgrade pip' command.\n"
24+
]
25+
}
26+
],
27+
"source": [
28+
"!pip install -q -U Slacker"
29+
]
30+
},
31+
{
32+
"cell_type": "code",
33+
"execution_count": 2,
34+
"metadata": {},
35+
"outputs": [],
36+
"source": [
37+
"import numpy as np\n",
38+
"import pandas as pd\n",
39+
"\n",
40+
"# Slacker \n",
41+
"from slacker import Slacker\n",
42+
"\n",
43+
"from sklearn.model_selection import train_test_split, StratifiedKFold\n",
44+
"import lightgbm as lgb\n",
45+
"\n",
46+
"# Plotting\n",
47+
"import matplotlib.pyplot as plt\n",
48+
"import seaborn as sns\n",
49+
"%matplotlib inline\n",
50+
"plt.style.use('fivethirtyeight')"
51+
]
52+
},
53+
{
54+
"cell_type": "markdown",
55+
"metadata": {},
56+
"source": [
57+
"# Get a Slack Legacy API Token\n",
58+
"\n",
59+
"https://api.slack.com/custom-integrations/legacy-tokens\n",
60+
"\n",
61+
"Store in a safe location (not on GitHub)!"
62+
]
63+
},
64+
{
65+
"cell_type": "markdown",
66+
"metadata": {},
67+
"source": [
68+
"Read in slack api token. Replace the line below with the location of your slack api token."
69+
]
70+
},
71+
{
72+
"cell_type": "code",
73+
"execution_count": 3,
74+
"metadata": {},
75+
"outputs": [],
76+
"source": [
77+
"with open('C:/Users/willk/OneDrive/Desktop/slack_api_python.txt', 'r') as f:\n",
78+
" slack_api_token = f.read()"
79+
]
80+
},
81+
{
82+
"cell_type": "markdown",
83+
"metadata": {},
84+
"source": [
85+
"Authenticate with slack and print out channels to make sure everything worked."
86+
]
87+
},
88+
{
89+
"cell_type": "code",
90+
"execution_count": 27,
91+
"metadata": {},
92+
"outputs": [
93+
{
94+
"name": "stdout",
95+
"output_type": "stream",
96+
"text": [
97+
"Channel general Purpose: This channel is for workspace-wide communication and announcements. All members are in this channel. ID: CCCT28F08\n",
98+
"\n",
99+
"Channel python_content Purpose: ID: CCEG5VCEM\n",
100+
"\n",
101+
"Channel random Purpose: A place for non-work-related flimflam, faffing, hodge-podge or jibber-jabber you'd prefer to keep out of more focused work-related channels. ID: CCDP3ES85\n",
102+
"\n",
103+
"Channel slack_interaction Purpose: To interact with Slack from Python ID: CCDC464LU\n",
104+
"\n",
105+
"Channel testing_exp Purpose: Experimental testing of interactions. ID: CCD27690R\n",
106+
"\n"
107+
]
108+
}
109+
],
110+
"source": [
111+
"slack = Slacker(slack_api_token)\n",
112+
"channels = slack.channels.list()\n",
113+
"\n",
114+
"for channel in channels.body['channels']:\n",
115+
" print(f'Channel {channel[\"name\"]} Purpose: {channel[\"purpose\"][\"value\"]} ID: {channel[\"id\"]}\\n')"
116+
]
117+
},
118+
{
119+
"cell_type": "code",
120+
"execution_count": 28,
121+
"metadata": {},
122+
"outputs": [
123+
{
124+
"data": {
125+
"text/plain": [
126+
"'CCEG5VCEM'"
127+
]
128+
},
129+
"execution_count": 28,
130+
"metadata": {},
131+
"output_type": "execute_result"
132+
}
133+
],
134+
"source": []
135+
},
136+
{
137+
"cell_type": "code",
138+
"execution_count": 30,
139+
"metadata": {},
140+
"outputs": [],
141+
"source": [
142+
"slack.channels.create('python_content2')\n",
143+
"new_id = slack.channels.get_channel_id('python_content2')\n",
144+
"r = slack.channels.history(new_id)"
145+
]
146+
},
147+
{
148+
"cell_type": "code",
149+
"execution_count": 31,
150+
"metadata": {},
151+
"outputs": [
152+
{
153+
"data": {
154+
"text/plain": [
155+
"{'has_more': False,\n",
156+
" 'messages': [{'subtype': 'channel_join',\n",
157+
" 'text': '<@UCEKVNHPH> has joined the channel',\n",
158+
" 'ts': '1535161714.000200',\n",
159+
" 'type': 'message',\n",
160+
" 'user': 'UCEKVNHPH'}],\n",
161+
" 'ok': True}"
162+
]
163+
},
164+
"execution_count": 31,
165+
"metadata": {},
166+
"output_type": "execute_result"
167+
}
168+
],
169+
"source": [
170+
"r.body"
171+
]
172+
},
173+
{
174+
"cell_type": "code",
175+
"execution_count": 9,
176+
"metadata": {},
177+
"outputs": [
178+
{
179+
"ename": "Error",
180+
"evalue": "channel_not_found",
181+
"output_type": "error",
182+
"traceback": [
183+
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
184+
"\u001b[1;31mError\u001b[0m Traceback (most recent call last)",
185+
"\u001b[1;32m<ipython-input-9-056f05d6c662>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mslack\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mset_purpose\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'#testing_exp'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'Deep philosophical discussions around the water cooler.'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 2\u001b[0m \u001b[0mslack\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcreate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'python_content'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mslack\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mset_purpose\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'python_content'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'Sharing great python related resources'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[0mchannels\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mslack\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mchannels\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlist\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
186+
"\u001b[1;32m~\\Anaconda3\\lib\\site-packages\\slacker\\__init__.py\u001b[0m in \u001b[0;36mset_purpose\u001b[1;34m(self, channel, purpose)\u001b[0m\n\u001b[0;32m 334\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mset_purpose\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mchannel\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mpurpose\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 335\u001b[0m return self.post('channels.setPurpose',\n\u001b[1;32m--> 336\u001b[1;33m data={'channel': channel, 'purpose': purpose})\n\u001b[0m\u001b[0;32m 337\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 338\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mset_topic\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mchannel\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtopic\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
187+
"\u001b[1;32m~\\Anaconda3\\lib\\site-packages\\slacker\\__init__.py\u001b[0m in \u001b[0;36mpost\u001b[1;34m(self, api, **kwargs)\u001b[0m\n\u001b[0;32m 121\u001b[0m return self._request(\n\u001b[0;32m 122\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_session_post\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msession\u001b[0m \u001b[1;32melse\u001b[0m \u001b[0mrequests\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpost\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 123\u001b[1;33m \u001b[0mapi\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 124\u001b[0m )\n\u001b[0;32m 125\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
188+
"\u001b[1;32m~\\Anaconda3\\lib\\site-packages\\slacker\\__init__.py\u001b[0m in \u001b[0;36m_request\u001b[1;34m(self, method, api, **kwargs)\u001b[0m\n\u001b[0;32m 97\u001b[0m \u001b[0mresponse\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mResponse\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mresponse\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtext\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 98\u001b[0m \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mresponse\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msuccessful\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 99\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mresponse\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0merror\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 100\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 101\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mresponse\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
189+
"\u001b[1;31mError\u001b[0m: channel_not_found"
190+
]
191+
}
192+
],
193+
"source": [
194+
"slack.channels.set_purpose('#testing_exp', 'Deep philosophical discussions around the water cooler.')\n",
195+
"slack.channels.create('python_content')\n",
196+
"slack.channels.set_purpose('python_content', 'Sharing great python related resources')\n",
197+
"\n",
198+
"channels = slack.channels.list()\n",
199+
"\n",
200+
"for channel in channels.body['channels']:\n",
201+
" print(f'Channel {channel[\"name\"]} Purpose: {channel[\"purpose\"][\"value\"]}\\n')"
202+
]
203+
},
204+
{
205+
"cell_type": "code",
206+
"execution_count": null,
207+
"metadata": {},
208+
"outputs": [],
209+
"source": [
210+
"from PIL import Image\n",
211+
"import requests\n",
212+
"url = user['profile']['image_192']\n",
213+
"im = Image.open(requests.get(url, stream=True).raw)\n",
214+
"plt.imshow(im)"
215+
]
216+
},
217+
{
218+
"cell_type": "code",
219+
"execution_count": null,
220+
"metadata": {},
221+
"outputs": [],
222+
"source": [
223+
"PIL.Image.from"
224+
]
225+
},
226+
{
227+
"cell_type": "code",
228+
"execution_count": null,
229+
"metadata": {},
230+
"outputs": [],
231+
"source": [
232+
"from IPython.display import Image\n",
233+
"from PIL import Image\n",
234+
"import PIL\n",
235+
"users = slack.users.list()\n",
236+
"\n",
237+
"for user in users.body['members']:\n",
238+
" # Print some information\n",
239+
" print(f'\\nUser: {user[\"name\"]}, Real Name: {user[\"real_name\"]}, Time Zone: {user[\"tz_label\"]}.')\n",
240+
" print(f'Current Status: {user[\"profile\"][\"status_text\"]}')\n",
241+
" # Get image data and show\n",
242+
" plt.figure(facecolor = 'w')\n",
243+
" plt.imshow(Image.open(requests.get(user['profile']['image_192'], stream=True).raw))\n",
244+
" plt.axis('off');\n",
245+
" plt.grid('off');\n",
246+
" plt.show();"
247+
]
248+
},
249+
{
250+
"cell_type": "code",
251+
"execution_count": null,
252+
"metadata": {},
253+
"outputs": [],
254+
"source": [
255+
"plt.plot(np.random.randn(100), np.random.randn(100), 'ro')\n",
256+
"plt.savefig('first_plot.png')"
257+
]
258+
},
259+
{
260+
"cell_type": "code",
261+
"execution_count": null,
262+
"metadata": {},
263+
"outputs": [],
264+
"source": [
265+
"slack.files.upload('first_plot.png', channels = ['testing_exp', 'slack_interaction']);"
266+
]
267+
},
268+
{
269+
"cell_type": "code",
270+
"execution_count": null,
271+
"metadata": {},
272+
"outputs": [],
273+
"source": [
274+
"Image(user['profile']['image_192'])"
275+
]
276+
},
277+
{
278+
"cell_type": "code",
279+
"execution_count": null,
280+
"metadata": {},
281+
"outputs": [],
282+
"source": [
283+
"\n",
284+
"Image('https://secure.gravatar.com/avatar/f85b7564fd35d5c86054b95090052d94.jpg?s=192&d=https%3A%2F%2Fa.slack-edge.com%2F7fa9%2Fimg%2Favatars%2Fava_0025-192.png')"
285+
]
286+
},
287+
{
288+
"cell_type": "code",
289+
"execution_count": null,
290+
"metadata": {},
291+
"outputs": [],
292+
"source": [
293+
"users.body['members']"
294+
]
295+
},
296+
{
297+
"cell_type": "code",
298+
"execution_count": null,
299+
"metadata": {},
300+
"outputs": [],
301+
"source": [
302+
"x = slack.channels.list()"
303+
]
304+
},
305+
{
306+
"cell_type": "code",
307+
"execution_count": null,
308+
"metadata": {},
309+
"outputs": [],
310+
"source": [
311+
"x.body"
312+
]
313+
},
314+
{
315+
"cell_type": "code",
316+
"execution_count": null,
317+
"metadata": {},
318+
"outputs": [],
319+
"source": []
320+
}
321+
],
322+
"metadata": {
323+
"kernelspec": {
324+
"display_name": "Python 3",
325+
"language": "python",
326+
"name": "python3"
327+
},
328+
"language_info": {
329+
"codemirror_mode": {
330+
"name": "ipython",
331+
"version": 3
332+
},
333+
"file_extension": ".py",
334+
"mimetype": "text/x-python",
335+
"name": "python",
336+
"nbconvert_exporter": "python",
337+
"pygments_lexer": "ipython3",
338+
"version": "3.6.4"
339+
}
340+
},
4341
"nbformat": 4,
5342
"nbformat_minor": 2
6343
}

0 commit comments

Comments
 (0)