<jupyter_start><jupyter_text>Introduction: Interacting with Slack from PythonIn 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)! First off, install the package. Documentation can be found [here](https://github.com/os/slacker).<jupyter_code>!pip install - q - U Slacker
# ALl outputs 
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = 'all'

# Slacker
from slacker import Slacker

# Data manipulation
import numpy as np
import pandas as pd

%load_ext autoreload
%autoreload 2



# Visualization
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('fivethirtyeight')

# Displaying images
from IPython.display import Image<jupyter_output><empty_output><jupyter_text>Get a Slack Legacy API TokenMost importantly, you need a [Slack workspace](https://slack.com/). I recommend making your own workspace where you are the admin just to get familiar with this package. Once you have your workspace, obtain a legacy token to enable interaction. Head to the following link to get a token.https://api.slack.com/custom-integrations/legacy-tokens![](images/legacy_tokens.PNG)Store the token in a safe location (not on GitHub)!This token will allow us to do anything one can do through the Slack API. The full documentation for the Slack API is [here](https://api.slack.com/methods)  Authenticate with SlackReplace the line below with the location of your slack api token.<jupyter_code>with open('C:/Users/willk/OneDrive/Desktop/slack_api_python.txt', 'r') as f:
    slack_api_token = f.read()<jupyter_output><empty_output><jupyter_text>Next we authenticate with slack by instantiating a `Slacker` object. We'll test this by checking in our connection is `successful`.<jupyter_code># Connect to Slack
slack = Slacker(slack_api_token)
if slack.api.test().successful:
    print(
        f"Successfully connected to {slack.team.info().body['team']['name']}.")
else:
    print('Try Again!')<jupyter_output>Successfully connected to Data Analysis.<jupyter_text>Interacting with SlackAnything (okay, nearly anything) you can do in Slack you can do through the Python API. First, let's just do some basic info retrieval.  Retrieving InformationEach time you make a request for information, you get back `response` object. The `body` object of the response holds the details of the request (if successful).<jupyter_code>r = slack.team.info()
r.body
from IPython.display import Image

Image(url='https://avatars.slack-edge.com/2018-11-30/492734348690_802c4805ab4a0d29383b_230.png')<jupyter_output><empty_output><jupyter_text>What Can We Do?This is our workspace's icon! (Okay that's not very exciting.) To see everything we can do with Slacker, let's take a look at the methods and attributes.<jupyter_code>from utils import get_data_and_model, get_options
import warnings
warnings.filterwarnings('ignore', category=FutureWarning)

command_dict = get_options(slack)
command_dict['functions']['users']
command_dict['functions']['channels']<jupyter_output>Using TensorFlow backend.<jupyter_text>We can look at all the channels by iterating through them.<jupyter_code>channels = slack.channels.list().body

# Iterate through channels
for channel in channels['channels']:
    print(
        f'Channel {channel["name"]} Purpose: {channel["purpose"]["value"]}\n')<jupyter_output>Channel general Purpose: This channel is for workspace-wide communication and announcements. All members are in this channel.

Channel python_content Purpose: 

Channel python_content_new Purpose: 

Channel python_content_new2 Purpose: Data Science and Python

Channel python_content2 Purpose: Working with Python and Slack

Channel python_content3 Purpose: Sharing great python related resources

Channel random Purpose: Deep philosophical discussions around the water cooler.

Channel slack_interaction Purpose: To interact with Slack from Python

Channel testing_exp Purpose: Experimental testing of interactions.

Channel training_report Purpose: Report progress while training machine learning models<jupyter_text>Here's the history of the `general` channel.<jupyter_code>slack.channels.history(channel='CCCT28F08').body<jupyter_output><empty_output><jupyter_text>We can also see all of the users along with their profile photo.<jupyter_code>users = slack.users.list().body

for user in users['members']:
    # Print some information
    print(
        f'\nUser: {user["name"]}, Real Name: {user["real_name"]}, Time Zone: {user["tz_label"]}.')
    print(f'Current Status: {user["profile"]["status_text"]}')
    # Get image data and show
    Image(user['profile']['image_192'])<jupyter_output>User: wjk68, Real Name: <NAME>, Time Zone: Eastern Standard Time.
Current Status:<jupyter_text>IDsWhen we interact with a `channel` or `user`, it's generally best to refer to it by the `id`. Therefore, it can be helpful to set up a mapping of channels and users to their ids.<jupyter_code>channel_dict = {}

for channel in channels['channels']:
    channel_dict[channel['name']] = channel['id']

user_dict = {}

for user in users['members']:
    user_dict[user['name']] = user['id']

user_dict<jupyter_output><empty_output><jupyter_text>Altering AttributesIt's pretty lonely in this Slack workspace! Now, we can alter any attributes of users of channels with the ids For example, we can set the purpose of the `python_content` channel as follows.<jupyter_code># Set the purpose of the channel
r = slack.channels.set_purpose(channel_dict['python_content2'],
                               purpose='Learning how to use Python for Slack interaction')

channels = slack.channels.list().body

# Iterate through channels
for channel in channels['channels']:
    if channel['name'] == 'python_content2':
        print(
            f'Channel {channel["name"]} Purpose: {channel["purpose"]["value"]}\n')<jupyter_output>Channel python_content2 Purpose: Learning how to use Python for Slack interaction<jupyter_text>Let's change a few of my attributes. First we can see what I'm up to.<jupyter_code>me = users['members'][0]
me['profile']['title']
me['profile']['display_name']
me['profile']['status_text']
me['profile']['status_emoji']<jupyter_output><empty_output><jupyter_text>Okay so not that exciting. Let's change a few things.<jupyter_code>for new_value, name in zip(['Data Scientist and Writer', 'Willk', 'data-sciencing', ':male-technologist:'],
                           ['title', 'display_name', 'status_text', 'status_emoji']):

    r = slack.users.profile.set(user=user_dict['wjk68'],
                                name=name, value=new_value)
    if not r:
        print(r.error)
users = slack.users.list().body
me = users['members'][0]
me['profile']['title']
me['profile']['display_name']
me['profile']['status_text']
me['profile']['status_emoji']<jupyter_output><empty_output><jupyter_text>The All-Important EmojiEmoji's are set using the `:name:` emoji code notation. The easiest way to figure out which one to use is go to Slack, bring up the emoji's hover over the one you want, and find the name.![](images/emoji_choose.PNG)<jupyter_code>import emoji


def print_emoji(emoji_name):
    print(emoji.emojize(emoji_name, use_aliases=True))


print_emoji(':runner:')
print_emoji(':computer:')<jupyter_output>💻<jupyter_text>Here's an image of all the available emoji at once. You can find all emoji codes at https://www.webpagefx.com/tools/emoji-cheat-sheet/.<jupyter_code>Image(url='https://a.slack-edge.com/c00d19/img/emoji_2017_12_06/sheet_google_64_indexed_256.png')<jupyter_output><empty_output><jupyter_text>SearchingYou can search through any messages or files in your team's workspace.<jupyter_code>def convert_ts(ts):
    return pd.to_datetime(ts, unit='s').date()
matches = slack.search.messages(query='Python').body['messages']['matches']
for match in matches:
    print(f"Time: {convert_ts(match['ts'])}, Text: {match['text']}")
print(emoji.emojize("Python is fun :+1:", use_aliases=True))
matches = slack.search.files(query='plot').body['files']['matches']
for match in matches:
    print(f"Time: {convert_ts(match['timestamp'])}, Title: {match['title']}")<jupyter_output>Time: 2018-12-01, Title: Iris Seaborn Plot
Time: 2018-11-30, Title: Iris Seaborn Plot
Time: 2018-11-30, Title: first plot
Time: 2018-11-30, Title: Iris Seaborn Plot
Time: 2018-11-30, Title: Iris Seaborn Plot
Time: 2018-11-30, Title: Iris Seaborn Plot
Time: 2018-08-23, Title: first plot
Time: 2018-08-23, Title: first plot<jupyter_text>Do Not DisturbYou can even find the do not disturb settings for everyone on your team.<jupyter_code>slack.dnd.team_info().body<jupyter_output><empty_output><jupyter_text>If you want to tell everyone to leave you alone, go ahead and do so!<jupyter_code>slack.dnd.set_snooze(num_minutes=120).body<jupyter_output><empty_output><jupyter_text>And then we can end that snooze becuase there's more to do.<jupyter_code>slack.dnd.end_snooze().body<jupyter_output><empty_output><jupyter_text>Creating a New ChannelLet's make a brand new channel.<jupyter_code>new_channel_name = 'python_content_update'

# Create new channel
r = slack.channels.create(new_channel_name)
new_id = slack.channels.get_channel_id(new_channel_name)

# Set channel purpose
r = slack.channels.set_purpose(new_id, 'Data Science and Python')
r = slack.channels.info(new_id)
r.body['channel']['latest']<jupyter_output><empty_output><jupyter_text>Additional OptionsNearly anything you want to do on Slack you can do through this API! Feel free to check out all of the options (which you can find in the `command_dict`). The [Slack API reference](https://api.slack.com/methods) is helpful for formulating commands.  Posting MessagesNow let's get to the heart of why we want to interact with Slack: posting new messages. It's relatively simple to post a message to any channel.<jupyter_code>r = slack.chat.post_message(channel='python_content',
                            text='Have a great day!',
                            username='Python Test',
                            icon_url='http://devarea.com/wp-content/uploads/2017/11/python-300x300.png')
r.successful<jupyter_output><empty_output><jupyter_text>![](images/posted_message.PNG) If we set the `username` then the messages are posted as bots. We still get to choose the icon though.<jupyter_code>slack.channels.history(
    channel=channel_dict['python_content']).body['messages'][0]['text']<jupyter_output><empty_output><jupyter_text>Links in MessagesLinks are automatically expanded.<jupyter_code>r = slack.chat.post_message(channel='python_content',
                            text="Here's an uplifting story: https://www.forbes.com/sites/stevedenning/2017/11/30/why-the-world-is-getting-better-why-hardly-anyone-knows-it/#181c93b27826",
                            unfurl_links=True,
                            username='Uplift',
                            icon_emoji=':point_up::skin-tone-5:')
r.successful<jupyter_output><empty_output><jupyter_text>![](images/unfurl.PNG)  Mentioning UsersYou can mention specific users with ``.<jupyter_code>r = slack.chat.post_message(channel='python_content',
                            text='<@UCEKVNHPH> Have you read any good books lately?',
                            link_names=True,
                            username='Query',
                            icon_emoji=':green_book:')
r.successful<jupyter_output><empty_output><jupyter_text>![](images/query.PNG) We can also post as ourselves.<jupyter_code>r = slack.chat.post_message(channel='python_content',
                            text='Anyone up for a trip to the store?',
                            as_user='willk')
r.successful<jupyter_output><empty_output><jupyter_text>![](images/my_posted_message.PNG)<jupyter_code>slack.channels.history(
    channel=channel_dict['python_content']).body['messages'][0]['text']<jupyter_output><empty_output><jupyter_text>Of course you can also notify everyone!<jupyter_code>r = slack.chat.post_message(channel='python_content',
                            text='<!everyone> *This is not a test!*',
                            username='Alert',
                            icon_emoji=':female-firefighter:')
r.successful<jupyter_output><empty_output><jupyter_text>![](images/Alert.PNG)  Complex MessagesMessages can be as complex as you want using attachments.<jupyter_code>r = slack.chat.post_message(channel='random',
                            username='Fancy Message',
                            icon_emoji=':earth_africa:',
                            attachments=[
                                {
                                    "fallback": "Required plain-text summary of the attachment.",
                                    "color": "#2eb886",
                                    "pretext": "Optional text that appears above the attachment block",
                                    "author_name": "Fancy Message",
                                    "title": "Slack API Documentation",
                                    "title_link": "https://api.slack.com/",
                                    "text": "Optional text that appears within the attachment",
                                    "fields": [
                                        {
                                            "title": "Priority",
                                            "value": "High",
                                            "short": False
                                        }
                                    ],
                                    "image_url": "http://my-website.com/path/to/image.jpg",
                                    "thumb_url": "http://example.com/path/to/thumb.png",
                                    "footer": "Slack API",
                                    "footer_icon": "https://platform.slack-edge.com/img/default_application_icon.png",
                                    "ts": 123456789
                                }
                            ]
                            )<jupyter_output><empty_output><jupyter_text>![](images/complex.PNG) We can even do crazy things like posting clickable games!<jupyter_code>r = slack.chat.post_message(channel='random', text='Play a game!',
                            username='Game Overseer',
                            icon_emoji=':black_joker:',
                            attachments=[
                                {
                                    "text": "Choose a game to play",
                                    "fallback": "You are unable to choose a game",
                                    "callback_id": "wopr_game",
                                    "color": "#3AA3E3",
                                    "attachment_type": "default",
                                    "actions": [
                                        {
                                            "name": "game",
                                            "text": "Chess",
                                            "type": "button",
                                            "value": "chess",
                                            "confirm": {
                                                "title": "Chess is a great game for the mind",
                                                "text": "Are you up to the challenge?",
                                                "ok_text": "Yes",
                                                "dismiss_text": "No"
                                            }
                                        },
                                        {
                                            "name": "game",
                                            "text": "Falken's Maze",
                                            "type": "button",
                                            "value": "maze",
                                            "confirm": {
                                                "title": "Mazes aren't that interesting!",
                                                "text": "How about the other options?",
                                                "ok_text": "Yes",
                                                "dismiss_text": "No"
                                            }
                                        },
                                        {
                                            "name": "game",
                                            "text": "Thermonuclear War",
                                            "style": "danger",
                                            "type": "button",
                                            "value": "war",
                                            "confirm": {
                                                "title": "Are you sure?",
                                                "text": "Wouldn't you prefer a good game of chess?",
                                                "ok_text": "Yes",
                                                "dismiss_text": "No"
                                            }
                                        }
                                    ]
                                }
                            ]
                            )<jupyter_output><empty_output><jupyter_text>![](images/game_button.PNG)  Uploading FilesOne of my favorite parts of Slack is uploading files. This is done using a local file stored on your machine.<jupyter_code>r = slack.files.upload(file_='images/volcano_crater.jpg',
                       channels=['random'], title='Volcano Crater',
                       initial_comment='This would make a great display background')
r.successful<jupyter_output><empty_output><jupyter_text>Here's the image that we posted.<jupyter_code>img_url = r.body['file']['thumb_480']
img_url
Image('images/volcano_crater.jpg')<jupyter_output><empty_output><jupyter_text>Posting PlotsOnce we know how to upload files, we can start posting results of our analyses.<jupyter_code>import seaborn as sns
import warnings
warnings.filterwarnings('ignore', category=FutureWarning)

# library & dataset
df = sns.load_dataset('iris')

# Basic 2D density plot
sns.set_style("white")

# Some features are characteristic of 2D: color palette and wether or not color the lowest range
sns.kdeplot(df.sepal_width, df.sepal_length,
            cmap="Blues", shade=True, shade_lowest=True, )
plt.title('Good Ole Iris Data Set')

# Save last figure
plt.savefig('iris_plot.png', dpi=500)
r = slack.files.upload(file_='iris_plot.png',
                       channels=['slack_interaction', 'python_content3'],
                       title='Iris Seaborn Plot',
                       initial_comment="I've seen this data set way too many times. Seaborn makes some nice plots though.")<jupyter_output><empty_output><jupyter_text>![](images/plot_posted.PNG)<jupyter_code>from string import ascii_letters

d = pd.DataFrame(data=np.random.normal(size=(100, 26)),
                 columns=list(ascii_letters[26:]))

# Compute the correlation matrix
corr = d.corr()

# Generate a mask for the upper triangle
mask = np.zeros_like(corr, dtype=np.bool)
mask[np.triu_indices_from(mask)] = True

# Set up the matplotlib figure
f, ax = plt.subplots(figsize=(11, 9))

# Generate a custom diverging colormap
cmap = sns.diverging_palette(220, 10, as_cmap=True)

# Draw the heatmap with the mask and correct aspect ratio
sns.heatmap(corr, mask=mask, cmap=cmap, vmax=.3, center=0,
            square=True, linewidths=.5, cbar_kws={"shrink": .5})

plt.savefig('heatmap_ex.png')
r = slack.files.upload(file_='heatmap_ex.png',
                       channels=['slack_interaction', 'python_content3'],
                       title='Correlation Heatmap',
                       initial_comment="The correlations are meaningless becuase they are random. Nice plot though.")<jupyter_output><empty_output><jupyter_text>Monitor TrainingLet's get serious and use slack for some real work. Here we'll monitor the progess of a convolutional neural network as it is training. First we'll create a new channel if it does not yet exist.<jupyter_code>channel_list = [c['name'] for c in slack.channels.list().body['channels']]

reporting_channel = 'training_report'

if reporting_channel not in channel_list:
    slack.channels.create('training_report')

cid = slack.channels.get_channel_id('training_report')
r = slack.channels.set_purpose(
    cid, 'Report progress while training machine learning models')
r = slack.channels.set_topic(cid, 'Progress Monitoring')
r = slack.channels.info(cid).body
r['channel']['latest']<jupyter_output><empty_output><jupyter_text>Model and DataNow we'll import some data and a model. This is just a basic convolutional neural network that will be trained to identify numbers (MNIST). The script is from https://github.com/keras-team/keras/blob/master/examples/mnist_cnn.py<jupyter_code>from utils import get_data_and_model

x_train, x_test, y_train, y_test, model = get_data_and_model()
model.summary()<jupyter_output>x_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 26, 26, 32)        320       
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 12, 12, 64)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 12, 12, 64)        0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 9216)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 128)               1179776   
___[...]<jupyter_text>Keras Custom CallbackWe'll write a quick custom Keras callback that reports the progress while training. Refer to [the documentation](https://keras.io/callbacks/) for specifics.<jupyter_code>from keras.callbacks import Callback
from datetime import datetime


def report_stats(text, channel):
    """Report training stats"""
    r = slack.chat.post_message(channel=channel, text=text,
                                username='Training Report',
                                icon_emoji=':clipboard:')

    if r.successful:
        return True
    else:
        return r.error
from timeit import default_timer as timer


class SlackUpdate(Callback):
    """Custom Keras callback that posts to Slack while training a neural network"""

    def __init__(self, channel):
        self.channel = channel

    def on_train_begin(self, logs={}):
        report_stats(text=f'Training started at {datetime.now()}',
                     channel=reporting_channel)

        self.start_time = timer()
        self.train_acc = []
        self.valid_acc = []
        self.train_loss = []
        self.valid_loss = []
        self.n_epochs = 0

    def on_epoch_end(self, batch, logs={}):

        self.train_acc.append(logs.get('acc'))
        self.valid_acc.append(logs.get('val_acc'))
        self.train_loss.append(logs.get('loss'))
        self.valid_loss.append(logs.get('val_loss'))
        self.n_epochs += 1

        message = f'Epoch: {self.n_epochs} Training Loss: {self.train_loss[-1]:.4f} Validation Loss: {self.valid_loss[-1]:.4f}'

        report_stats(message, channel=self.channel)

    def on_train_end(self, logs={}):

        best_epoch = np.argmin(self.valid_loss)
        valid_loss = self.valid_loss[best_epoch]
        train_loss = self.train_loss[best_epoch]
        train_acc = self.train_acc[best_epoch]
        valid_acc = self.valid_acc[best_epoch]

        message = f'Trained for {self.n_epochs} epochs. Best epoch was {best_epoch + 1}.'
        report_stats(message, channel=self.channel)
        message = f'Best validation loss = {valid_loss:.4f} Training Loss = {train_loss:.2f} Validation accuracy = {100*valid_acc:.2f}%'
        report_stats(message, channel=self.channel)<jupyter_output><empty_output><jupyter_text>Now let's instantiate this callback. We'll report to the new channel we created. Then we start training and can monitor the training in the channel.<jupyter_code>import tensorflow as tf
updater = SlackUpdate(channel=reporting_channel)


# Fit the model for 10 epochs
history = model.fit(x_train, y_train, epochs=12, batch_size=512,
                    callbacks=[updater], validation_split=0.4)<jupyter_output>Train on 36000 samples, validate on 24000 samples
Epoch 1/12
36000/36000 [==============================] - 18s 491us/step - loss: 0.7556 - acc: 0.7602 - val_loss: 0.2250 - val_acc: 0.9309
Epoch 2/12
36000/36000 [==============================] - 9s 263us/step - loss: 0.2029 - acc: 0.9413 - val_loss: 0.1062 - val_acc: 0.9685
Epoch 3/12
36000/36000 [==============================] - 10s 267us/step - loss: 0.1291 - acc: 0.9616 - val_loss: 0.1180 - val_acc: 0.9632
Epoch 4/12
36000/36000 [==============================] - 10s 272us/step - loss: 0.1004 - acc: 0.9697 - val_loss: 0.0744 - val_acc: 0.9775
Epoch 5/12
36000/36000 [==============================] - 10s 279us/step - loss: 0.0870 - acc: 0.9736 - val_loss: 0.0671 - val_acc: 0.9799
Epoch 6/12
36000/36000 [==============================] - 10s 269us/step - loss: 0.0728 - acc: 0.9782 - val_loss: 0.0659 - val_acc: 0.9797
Epoch 7/12
36000/36000 [==============================] - 10s 274us/step - loss: 0.0655 - acc: 0.9804 - val_loss: 0.0[...]<jupyter_text>We have a thorough report of what happened during training. ![](images/training_report.PNG)  Plot ResultsNext we can create a plot of the training curves and send them to the reporting channel. This could also be included in the custom callback `on_train_end` method. First we'll plot the figure and save it.<jupyter_code>from utils import plot_history

plot_history(history.history)
plt.savefig('training_curves.png')<jupyter_output><empty_output><jupyter_text>Next we upload the file along with some helpful comments.<jupyter_code># Find minimum loss
min_loss = min(history.history['val_loss'])
best_epoch = np.argmin(history.history['val_loss']) + 1

# Upload file
comment = f"Best loss of {min_loss:.4f} at epoch {best_epoch}."
r = slack.files.upload(file_='training_curves.png', title="Training Curves", channels=[reporting_channel],
                       initial_comment=comment)
r = slack.channels.info(cid).body
r['channel']['latest']['text']<jupyter_output><empty_output><jupyter_text>![](images/training_plot.PNG)  Upload PredictionsWe'll were at it, we might as well upload some predictions on test images.<jupyter_code>from matplotlib import MatplotlibDeprecationWarning
warnings.filterwarnings('ignore', category=MatplotlibDeprecationWarning)


def plot_image(image, ax=None):
    if ax is not None:
        ax.imshow(image.reshape((28, 28)), cmap='Greys')
    else:
        plt.imshow(image.reshape((28, 28)), cmap='Greys')
plot_image(x_test[10])
y_test[1]
def plot_predictions(n=4):
    """Plot test image and predictions"""

    # Get random images to plot
    to_plot = np.random.choice(list(range(x_test.shape[0])),
                               size=n, replace=False)
    correct = []
    # Make predictions and plot each image
    for i in to_plot:
        image = x_test[i]
        probs = model.predict_proba(image.reshape((1, 28, 28, 1)))[0]
        pred = pd.DataFrame({'prob': probs})
        fig, axs = plt.subplots(1, 2, figsize=(16, 6))
        plot_image(image, axs[0])

        pred['prob'].plot.bar(ax=axs[1])
        axs[1].set_xlabel('Class')
        axs[1].set_ylabel('Probability')
        axs[1].set_title('Predictions')
        plt.savefig(f'images/test-{i}-predictions.png')
        plt.show()

        correct.append(np.argmax(probs) == np.argmax(y_test[i]))

    return to_plot, correct
to_plot, c = plot_predictions()
def post_predictions(channel, n=4):
    """Post Keras preditions to Slack"""

    # Make predictions
    plot_indexes, correct = plot_predictions(n=n)

    # Iterate through images and correct indicators
    for i, r in zip(plot_indexes, correct):
        filename = f'images/test-{i}-predictions.png'
        # Upload the files
        r = slack.files.upload(file_=filename,
                               title="Predictions",
                               channels=[channel],
                               initial_comment='Correct' if r else 'Incorrect')
post_predictions(reporting_channel)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11466472201965483, 'fraction_numerical': 0.021634198006736813, 'mean_word_length': 2.6811964719417105, 'pattern_counts': {'":': 51, '<': 151, '<?xml version=': 0, '>': 151, 'https://': 13, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 97, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '39', 'dir': 'jupyter-structured-clean-dedup', 'id': '379401', 'license': 'MIT', 'n_tokens_mistral': 8695, 'n_tokens_neox': 8019, 'n_words': 2135, 'path': 'slack_interaction/Interacting with Slack.ipynb', 'repo_name': 'ventureBorbot/Data-Analysis', 'size': '28804'}