-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathslack_channel_report.py
280 lines (257 loc) · 10.8 KB
/
slack_channel_report.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
#! /usr/bin/env python
import copy
import json
import random
import time
import sys
from slack_sdk import WebClient
import random_name
import config
import channel
import channel_members_log
import user
import utils
import enricher
import slack_token
import slack_formatter
class SlackChannelReport(object):
def __init__(self, fake=False):
self.sf = slack_formatter.SlackFormatter()
random.seed(time.time())
self.fake = fake
self.fake_channel = channel.Channel(fake=True)
self.channel = channel.Channel(fake=fake)
self.rn = random_name.RandomName()
self.user = user.User(fake=fake)
self.client = WebClient(token=slack_token.token)
self.enricher = enricher.Enricher(fake=fake)
self.cml = channel_members_log.ChannelMembersLog()
def membercount(self, cid, start, end):
sc = self.cml.earliest_count(cid, start)
ec = self.cml.latest_count(cid, end)
text = self.sf.simple_comparison(ec, sc)
diff = ec - sc
text = "{} ended this period with {} members".format(self.sf.show_cid(cid), text)
if diff:
text += ", a change of {} members".format(diff)
return self.sf.text_block(text)
def messages(self, cid, ur, pur):
text = ""
m = "{} messages and {} words were posted to the channel"
msg_comp = self.sf.comparison(ur, pur, ['channels', cid, 0])
word_comp = self.sf.comparison(ur, pur, ['channels', cid, 1])
m = m.format(msg_comp, word_comp)
text += m
cur_user_count = len(ur['channel_user'].get(cid, []))
prev_user_count = len(pur['channel_user'].get(cid, []))
user_comp = self.sf.simple_comparison(cur_user_count, prev_user_count)
text += " from {} unique users".format(user_comp)
curchannelvol = ur['channels'][cid][1]
curtotal = ur['statistics']["words"]
prevchannelvol = pur['channels'][cid][1]
prevtotal = pur['statistics']["words"]
curper = float("{:.1f}".format(curchannelvol * 100.0 / curtotal))
prevper = float("{:.1f}".format(prevchannelvol * 100.0 / prevtotal))
m = "In all, this channel represented {} of total traffic"
m = m.format(self.sf.simple_comparison(curper, prevper, True, True, True))
text += "\n" + m
b = self.sf.text_block(text)
return b
def users(self, cid, ur, pur):
blocks = []
cur_users = ur['channel_user'].get(cid)
prev_users = pur['channel_user'].get(cid)
total = sum([x[1] for x in cur_users.values()])
cur_user_names = list(cur_users.keys())
cur_user_names.sort(key=lambda x: cur_users[x][1])
cur_user_names.reverse()
fields = ["*User*", "*Activity"]
ctr = 1
for user in cur_user_names:
fields.append("{} {}".format(ctr, self.sf.show_uid(user)))
m = "{} m".format(self.sf.comparison(ur, pur, ['channel_user', cid, user, 0]))
m += " {} w".format(self.sf.comparison(ur, pur, ['channel_user', cid, user, 1]))
fields.append(m)
ctr += 1
for fset in self.sf.make_fields(fields):
block = {'type': 'section', 'fields': fset}
blocks.append(block)
return blocks
def make_header(self, ur, pur, cid):
blocks = []
header = "Channel Activity Report for *{}* Between {} and {}"
header = header.format(self.sf.show_cid(cid), ur['start_date'], ur['end_date'])
blocks.append(self.sf.text_block(header))
blocks.append(self.sf.divider())
return blocks
def make_report(self, ur, pur, cid):
blocks = []
blocks += self.make_header(ur, pur, cid)
if cid not in ur['channel_stats']:
text = "There was no activity in this channel for this time period"
blocks.append(self.sf.text_block(text))
return blocks
if cid not in pur['channel_stats']:
text = "*Note:* No data exists for the previous (penultimate) week"
blocks.append(self.sf.text_block(text))
blocks.append(self.membercount(cid, ur['start_date'], ur['end_date']))
blocks.append(self.messages(cid, ur, pur))
blocks.append(self.sf.divider())
blocks += self.users(cid, ur, pur)
blocks.append(self.sf.divider())
blocks.append(self.sf.text_block(
"People sent {} reactions to the channel".format(
self.sf.comparison(ur, pur, ['enriched_channel', cid, 'reaction_count']))))
blocks += self.popular_reactions(ur, cid)
blocks += self.reacted_messages(ur, cid)
blocks += self.replied_messages(ur, cid)
blocks += self.posting_hours(ur, cid)
blocks += self.posting_days(ur, cid)
return blocks
def posting_days(self, ur, cid):
"""
Report on activity per day of the week
"""
d = ur['enriched_channel'][cid]['posting_days']
return self.sf.posting_days(d)
def posting_hours(self, ur, cid):
"""
Report on activity per hour of the workday
"""
# Why might we not have posting hours? One possibility is
# that posting hours are just for weekdays, so if the only Activity
# was during the weekend, we won't see posting hours stats
d = ur['enriched_channel'][cid].get("posting_hours")
if not d:
text = "*Note*: No weekday posting hours statistics are available, "
text += "possibly because all activity during this time period was "
text += "during the weekend"
block = self.sf.text_block(text)
return [block]
return self.sf.posting_hours(d)
def reacted_messages(self, ur, cid):
return self.sf.messager(
ur['enriched_channel'][cid]['most_reacted'],
'reactions',
show_user=True,
show_channel=False)
def replied_messages(self, ur, cid):
return self.sf.messager(
ur['enriched_channel'][cid]['most_replied'],
'replies',
show_user=True,
show_channel=False)
def topten(self, ur, pur, uid, label, header):
blocks = []
blocks.append(self.sf.text_block("*{}*".format(header)))
fields = ["*Person*", "*Count*"]
d = ur['enriched_user'][uid].get(label, {})
if not d:
return []
pd = pur['enriched_user'][uid].get(label, {})
t = "*{}* times between you and *{}* unique people"
total = sum(d.values())
count = len(list(d.keys()))
ptotal = sum(pd.values())
pcount = len(list(pd.keys()))
cur = {'total': total, 'count': count}
prev = {'total': ptotal, 'count': pcount}
total_comp = self.sf.comparison(cur, prev, ['total'])
count_comp = self.sf.comparison(cur, prev, ['count'])
t = t.format(total_comp, count_comp)
blocks.append(self.sf.text_block(t))
uids = list(d.keys())[0:10]
for uid in uids:
# fields.append(ur['user_info'][uid]['label'])
fields.append(self.sf.show_uid(uid))
fields.append(str(d[uid]))
for fset in self.sf.make_fields(fields):
block = {'type': 'section', 'fields': fset}
blocks.append(block)
return blocks
def popular_reactions(self, ur, cid):
popularity = ur['enriched_channel'][cid]['reactions']
words = ur['channels'][cid][1]
return self.sf.reactions(popularity, count=words)
def make_channels(self, ur, pur):
fields = []
ctr = 1
if not ur['enriched_channels']:
return fields
fields.append("*Channel*")
fields.append("*Rank, Messages, Words*")
for channel_name in ur['enriched_channels']:
channel = ur['enriched_channels'][channel_name]
cname = channel['name']
if self.fake:
cname = self.sf.get_fake_channel(channel_name)
f1 = "{} *{}*".format(ctr, cname)
f2 = "*{}* rank, {} m, {} w"
messages = self.sf.comparison(ur, pur, ['enriched_channels', channel_name, 'messages'])
words = self.sf.comparison(ur, pur, ['enriched_channels', channel_name, 'words'])
f2 = f2.format(channel['rank'], messages, words)
fields.append(f1)
fields.append(f2)
ctr += 1
blocks = []
for fset in self.sf.make_fields(fields):
block = {'type': 'section', 'fields': fset}
blocks.append(block)
return blocks
def send_report(self, cid, ur, previous, send=True, override_cid=None,
summary=False):
"""
Send report for channel `cid`
ur is current period report; previous is the previous report
will not send if `send` is False
override_cid will send the report to the provided cid instead of to the channel
if summary, will send summary of report to config.channel_stats channel
"""
if override_cid == cid:
m = "You may not specify an override_cid that is the same as the "
m += "report cid"
raise RuntimeError(m)
enricher.Enricher(fake=self.fake).enrich(ur)
enricher.Enricher(fake=self.fake).enrich(previous)
utils.save_json(ur, "ur.json")
utils.save_json(previous, "previous.json")
blocks = self.make_report(ur, previous, cid)
if not send:
print("Saving report to slack.json")
f = open("slack.json", "w")
f.write(json.dumps(blocks, indent=4))
f.close()
return
# If set to true, this message will be sent as the user who owns the token we use
as_user = False
if override_cid:
cid = override_cid
urls = []
for blockset in utils.chunks(blocks, 49):
if send:
print("Sending report to {}".format(cid))
try:
response = self.client.chat_postMessage(
text="Weekly Channel Activity Report",
channel=cid,
blocks=blockset,
parse='full',
as_user=as_user,
unfurl_links=True,
link_names=True)
# print("Response: {}".format(response))
urls.append(utils.make_url(response['channel'], response['ts']))
except Exception:
print(Exception)
print(json.dumps(blockset, indent=4))
sys.exit(0)
if summary and urls:
cid = self.channel.get(config.channel_stats)['slack_cid']
self.client.chat_postMessage(
channel=cid,
parse='full',
as_user=as_user,
unfurl_links=True,
link_names=True,
text=urls[0]
)