sudo apt-get update
sudo apt-get -y install python3-pip
pip3 install flask
pip3 install mattermostdriver
pip3 install -U PyYAML
pip3 install numpy
pip3 install pandas
pip3 install matplotlib
pip3 install seaborn
pip3 install mockito
- This bot provides users with correct function and libraries from Seaborn through sample source code.
- The user can also test the correctness of the plot by visualizing their data input on the plot generated by the bot.
- The types of plots the bot can plot are:
- Line graph
- Histogram
- Scatterplot
- Box-plot
- Bar chart with facets
Below are refined use cases for PlotBot
Use Case 1: Give the user with code snippet for the required type of graph.
1 Preconditions
User must have mattermost account.
2 Main Flow
User provides the bot with the type of graph it requires [S1].
Bot responds with a code snippet of the Seaborn library of that particular graph type[S2].
Bot also returns a sample graph for user to visualize the data[S3].
3 Subflows
[S1] User calls bot by a "sample" command and the graph type @graph_type. e.g. Histogram
[S2] Bot returns a code snippet.
[S3] Bot also returns a sample graph for the @graph type
4 Alternative Flows
[E1] Requested graph type not available to plot.
Use Case2: Plot the graph for the user with their custom data.
1 Preconditions
User must have mattermost account.
User must give the data in the format required by the bot.
2 Main Flow
User requests bot to generate a graph of a type for their data [S1].
User uploads the data in that particular format with labels[S2].
User also provides the labels, features it wants in the graph in a particular format[S3]
Bot generates the graph and returns it to the user [S4].
3 Subflows
[S1] User gives a "plot" command with @graph_type, ex. Scatterplot.
[S2] User uploads data on MatterMost in a particular format, ex. csv
[S3] User provides with X, Y labels as asked by the Bot
[S4] Bot returns the generated graph.
4 Alternative Flows
[E1] Incorrect data format. Requested data format to be provided
[E2] Requested graph type not available.
Use Case3: Provide user with the ability to view all his plots.
1 Preconditions
User must have mattermost account.
2 Main Flow
User requests to provide all his plotted graphs using this bot[S1]
User can also request the bot to provide with specific graphs within a time frame[S2]
Bot returns the graphs according to the requirements
3 Subflows
[S1] User asks the bot with a "retrieve" command to give all the plotted graphs.
[S2] User can mention the name of the specific graph or the graphs between two Dates, ex. From DD/MM/YY to DD/MM/YY
[S3] Bot will return the files according to the requirements
4 Alternative Flows
[E1] No plots available.
[E2] Incorrect data format
AWS instance creation, instance1 mattermost server - Deployable We created AWS instance for mattermost server. To make our bot independent of the server location of mattermost, we created another instance for out bot on AWS. Our bot is deployable and only requires a bot account token called PLOT_BOT_TOKEN from user after they make an account on MatterMost
The bot is integrated in a Flask framework which gets activated on certain trigger-words.
Flask Framework This python based application was used to make HTTP requests. The outgoing webhooks created on Mattermost were integrated with the responder functions of our basic chatbot.
Trigger words corresponding to different tasks
Run Commands: @plotbot - Activates the PlotBot sample - Covers usecase 1 plot - Covers usecase 2 retrieve - Covers usecase3
Example Usecase
@plotbot Hey!
sample [boxplot/scatterplot/barplot]
plot [scatterplot/boxplot/barplot] "iris.csv(upload file on Matternost)"
retrieve from:05/06/2018 to:08/07/2019 OR can be a particular file name the use wants(say iris.csv)
To test and automate our BOT, we used Pupeteer Node library for testing responses on headless Chromium. In order to run the tests:
- Do
npm install
- Export your MATTERMOST_EMAIL and MATTERMOST_PWD
- Do npm test. We add a total of 8 Test Cases covering our use cases.
- Below we have shown code snippets for happy flows of our usecases and a screenshot of all testcases(happy and alternate flows) passing is provided.
it('It should greet back', async () => {
var list = [,]
await page.waitForSelector('#post_textbox');
await page.focus('#post_textbox')
await page.keyboard.type( "@plotbot hi" );
await page.keyboard.press('Enter');
let promise = new Promise((res, rej) => {
setTimeout(() => res("Waiting for the Response!"), 10000)
});
let waiting = await promise;
await page.waitForSelector('.post__body');
const output = await page.evaluate(() => Array.from(
document.getElementsByClassName('post__body'), e => e.innerText));
var result = output[output.length-1];
expect(result).to.match(/^Hi*/);
await browser.close();
});
it('Happy flow when users asks for sample scatter plot', async () => {
await page.waitForSelector('#post_textbox');
await page.focus('#post_textbox')
await page.keyboard.type( "sample scatterplot" );
await page.keyboard.press('Enter');
let promise = new Promise((res, rej) => {
setTimeout(() => res("Waiting for the Response!"), 5000)
});
let waiting = await promise;
await page.waitForSelector('.post__body');
const output = await page.evaluate(() => Array.from(
document.getElementsByClassName('post__body'), e => e.innerText));
var result = output[output.length-1];
expect(result).to.match(/scatterplot_code\.png\n.*\nscatterplot_graph\.png/);
await browser.close();
});
it('Happy flow when user asks to plot a graph by giving dataset', async () => {
await page.waitForSelector('#post_textbox');
await page.focus('#post_textbox')
await page.keyboard.type( "plot scatterplot" );
const example = await page.$('#fileUploadButton');
const attach = await page.evaluateHandle(el => el.nextElementSibling, example);
await attach.uploadFile('test/dataset.csv')
let promise1 = new Promise((res, rej) => {
setTimeout(() => res("Waiting for the Response!"), 5000)
});
let waiting1 = await promise1;
await page.keyboard.press('Enter');
let promise = new Promise((res, rej) => {
setTimeout(() => res("Waiting for the Response!"), 5000)
});
let waiting = await promise;
await page.waitForSelector('.post__body');
const output = await page.evaluate(() => Array.from(
document.getElementsByClassName('post__body'), e => e.innerText));
var result = output[output.length-1];
expect(result).to.match(/\.png/);
await browser.close();
});
it('Happy flow when user requests for his graphs plotted during a time period', async () => {
await page.waitForSelector('#post_textbox');
await page.focus('#post_textbox')
await page.keyboard.type( "retrieve from:2019-10-19 13:0:0.0 to:2019-10-19 14:0:0.0" );
await page.keyboard.press('Enter');
let promise = new Promise((res, rej) => {
setTimeout(() => res("Waiting for the Response!"), 5000)
});
let waiting = await promise;
await page.waitForSelector('.post__body');
const output = await page.evaluate(() => Array.from(
document.getElementsByClassName('post__body'), e => e.innerText));
var result = output[output.length-1];
expect(result).to.match(/\.png/);
await browser.close();
});
The following screenshot shows that all testcases for happy and alternate flows for each use case pass.
We implemented mock services for each use case and data to support service integration. The code for mock data is in, framework/mocking_agent For services' integration and end-to-end work-flows, we have mocked up responses to services' requests at following points.
Mock graphs are generated (from seaborn's built in dataset) and stored locally at begening to emulate DataBase of graphs.
Usecase | Services required | service API | Mocked up | mocking API |
---|---|---|---|---|
1 | Provide code snippet for a type of plot | service. sampler. retrieve_snippet | Returns basic code snippet from local database | mocking_agent. responseSnippet[plotType] |
1 | Provide graph snippet | service. sampler. retrieve_snippet | Returns basic snippet of graph from local database | mocking_agent. responseSnippet[plotType] |
2 | For given data (dataset), generate plot | service. plotter. load_dataset | Uses seaborn built-in data set to plot | mocking_agent. responseDataSet [plotType] |
2 | For given data (dataset), plot on particular variable | service. plotter. fetchAxisInfo | Uses axes info from seaborn built-in dataset to plot | mocking_agent. responseAxisInfo[ plotType] |
3 | Retrieve particular plots from database | service. retrieval. fetchplotfromDB | Fetches a graph at random from mock graphs | mocking_agent. responseRetrieve['plot'] |
3 | Retrieve graphs from a particular time interval | service. retrieval. fetchplotfromDBtimed | Fetches a set of graphs at random from mock graphs | mocking_agent. responseRetrieve['datetime'] |