DEPRECATION NOTICE Information provided in this article is deprecated and no longer valid, because of the deal-breaking issues with the Puppeteer. Please refer to "Automating report generation using Selenium Webdriver" for a working solution.
If you need to automate the generation of chart images or PDF, utilities with a self contained browser are your best options. There are few options out there as headless browsers.
We created this example based on Puppeteer as this is a new option supported by Google that uses latest Chrome.
What is Puppeteer
Puppetter is a npm package that contains Chrome on its distribution. When you run Puppeteer,
Chrome opens and you can run a given page in it.
Check more about Puppeteer on the links below:
Setting up
- Install NodeJS
- Create a folder
- Create a
package.json
file with the content shown below:{ "name": "amCharts 4", "description": "Automate chart export", "engines": { "node": ">=6.0.0" }, "dependencies": { "puppeteer": "^1.5.0" } }
- Open a terminal and run
npm i
on the same folder to install the dependencies
Generating a single chart report from external link
You can export charts that exist on a current page.
Add a file called external.js
for now. You can rename it as you wish. Add the code below
in it:
const puppeteer = require('puppeteer'); (async () => { let url = 'https://s.codepen.io/team/amcharts/fullpage/eKoVEP'; // Open the browser const browser = await puppeteer.launch({ // Run the browser headless or not headless: true }); // Open a tab const page = await browser.newPage(); // Set the referer. That is only needed if the server needs it for security reasons await page.setExtraHTTPHeaders({ 'referer': url }); await page.setRequestInterception(true); page.on('request', request => { request.continue(); }); // Defines where the file should be saved await page._client.send('Page.setDownloadBehavior', { behavior: 'allow', // Using a folder at the root to save the images downloadPath: '/export' }); // Define the URL where the chart is await page.goto(url); // Run scripts in the page await page.evaluate(() => { // Make the evaluate callback wait until the export is done return new Promise((resolve, reject) => { // Call the export on the chart chart.exporting.export('pdf').finally(() => { // Wait for the download to get done setTimeout(() => { resolve(); }, 2000); }); }); }); // Close the browser await browser.close(); })();
Now run node external.js
.
NOTE The exported files will be in the export folder under the root folder of your computer.
Generating a single chart report from the same package
If you prefer to create charts without requirement external pre-existing pages, you can have them built into this solution. We will setup a server using HapiJS by first adding the following dependencies in the package.json
file:
{ "name": "amCharts 4", "description": "Automate chart export", "engines": { "node": ">=6.0.0" }, "dependencies": { "puppeteer": "^1.5.0", "hapi": "17.5.1", "inert": "5.1.0" } }
Run npm i
to install the new dependencies.
Add server.js
file and the following code into it:
const Hapi = require('hapi'); const Inert = require('inert'); const exportChart = require('./internal'); const server = new Hapi.Server({ host: 'localhost', port: 3000 }); server.route({ method: 'GET', path: '/', handler: (request, h) => { return h.file('./public/index.html'); } }); server.route({ method: 'GET', path: '/{fileName*}', handler: (request, h) => { return h.file('./public/' + request.params.fileName); } }); const init = async () => { await server.register(Inert); await server.start(); console.log(`Server running at: ${server.info.uri}`); } server.events.on('start', () => { // Start the export feature exportChart(server); console.log('Server started and the export module is loaded'); }); server.events.on('stop', () => { console.log('Server stopped'); }); init();
Adding files and folders
At this point you should some of the files below. Please add the missing folders and files as shown and then the correspondent content:
/ ├── package.json ├── server.js ├── internal.js ├── external.js └── public/ ├── index.html ├── styles/ | └── app.css └── scripts/ └── chart.js
The index.html file
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>amCharts 4</title> <link rel="stylesheet" href="/styles/app.css"> </head> <body> <div id="chartdiv"></div> <script src="//www.amcharts.com/lib/4/core.js"></script> <script src="//www.amcharts.com/lib/4/charts.js"></script> <script src="//www.amcharts.com/lib/4/themes/animated.js"></script> <script src="/scripts/chart.js"></script> </body> </html>
The app.css file
@import url("https://fonts.googleapis.com/css?family=Archivo+Narrow"); body { font-family: "Archivo Narrow", Arial; } #chartdiv { width: 100%; height: 500px; }
The chart.js file
am4core.useTheme(am4themes_animated); var chart = am4core.create("chartdiv", am4charts.XYChart); chart.data = [{ category: "One", value1: 1, value2: 5, value3: 3, value4: 3 }, { category: "Two", value1: 2, value2: 5, value3: 3, value4: 4 }, { category: "Three", value1: 3, value2: 5, value3: 4, value4: 4 }, { category: "Four", value1: 4, value2: 5, value3: 6, value4: 4 }, { category: "Five", value1: 3, value2: 5, value3: 4, value4: 4 }, { category: "Six", value1: 8, value2: 7, value3: 10, value4: 4 }, { category: "Seven", value1: 10, value2: 8, value3: 6, value4: 4 }]; chart.legend = new am4charts.Legend(); chart.colors.step = 2; var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis()); categoryAxis.dataFields.category = "category"; categoryAxis.renderer.grid.template.location = 0; var valueAxis = chart.yAxes.push(new am4charts.ValueAxis()); valueAxis.min = 0; valueAxis.renderer.minWidth = 35; var series1 = chart.series.push(new am4charts.ColumnSeries()); series1.columns.template.width = am4core.percent(80); series1.columns.template.tooltipText = "{name}: {valueY.value}"; series1.name = "Series 1"; series1.dataFields.categoryX = "category"; series1.dataFields.valueY = "value1"; series1.stacked = true; var series2 = chart.series.push(new am4charts.ColumnSeries()); series2.columns.template.width = am4core.percent(80); series2.columns.template.tooltipText = "{name}: {valueY.value}"; series2.name = "Series 2"; series2.dataFields.categoryX = "category"; series2.dataFields.valueY = "value2"; series2.stacked = true; var series3 = chart.series.push(new am4charts.ColumnSeries()); series3.columns.template.width = am4core.percent(80); series3.columns.template.tooltipText = "{name}: {valueY.value}"; series3.name = "Series 3"; series3.dataFields.categoryX = "category"; series3.dataFields.valueY = "value3"; series3.stacked = true; var series4 = chart.series.push(new am4charts.ColumnSeries()); series4.columns.template.width = am4core.percent(80); series4.columns.template.tooltipText = "{name}: {valueY.value}"; series4.name = "Series 4"; series4.dataFields.categoryX = "category"; series4.dataFields.valueY = "value4"; series4.stacked = true; chart.scrollbarX = new am4core.Scrollbar(); chart.exporting.menu = new am4core.ExportMenu();
The internal.js file
const puppeteer = require('puppeteer'); module.exports = async function (server) { let url = 'http://localhost:3000'; // Open the browser const browser = await puppeteer.launch({ // Run the browser headless or not headless: true }); // Open a tab const page = await browser.newPage(); // Set the referer. That is only needed if the server needs it for security reasons await page.setExtraHTTPHeaders({ 'referer': url }); await page.setRequestInterception(true); page.on('request', request => { request.continue(); }); // Defines where the file should be saved await page._client.send('Page.setDownloadBehavior', { behavior: 'allow', // Using a folder at the root to save the images downloadPath: '/export' }); // Define the URL where the chart is await page.goto(url); // Run scripts in the page await page.evaluate(() => { // Make the evaluate callback wait until the export is done return new Promise((resolve, reject) => { // Call the export on the chart chart.exporting.export('pdf').finally(() => { // Wait for the download to get done setTimeout(() => { resolve(); }, 2000); }); }); }); // Wait for the browser to close to stop the server browser.on('disconnected', async () => { // Stop the server await server.stop({ timeout: 3000 }); }); // Close the browser await browser.close(); };
Now run node server.js
to start the server and to generate the chart.
Where to customize the export functionality
You can customize the code calling the export functionality inside the page.evaluate()
method as shown below:
// Run scripts in the page await page.evaluate(() => { // Make the evaluate callback wait until the export is done return new Promise((resolve, reject) => { // Call the export on the chart chart.exporting.export('pdf').finally(() => { // Wait for the download to get done setTimeout(() => { resolve(); }, 2000); }); }); });
Please check more about exporting here.
Download the example
Download the whole example here and run npm i
to load the dependencies.