Snippets

Piotr Szrajber Smart M.App - Create PDF reports in the BI M.App

Created by Piotr Szrajber last modified
.box-my-content .widget-chart, .box-pdf-report .widget-chart {
    color: #000;
}

#pdfReport1 {
    width: 100%;
    border: none;
    height: calc(100% - 30px);
}

.pdf-tools {
    color: #000;
}
/**
 * Creating PDF reports in your BI Smart M.Apps using the PDFMake library http://pdfmake.org
 * The PDFMake library is licensed under MIT.
 *
 * In order to use this snippet you need to add references (EXT in the Studio Customization Step) to the following external scripts
 *   - "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.33/pdfmake.js",
 *   - "https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.33/vfs_fonts.js"
 *
 * This script assumes that you have a Custom Widget Container created in the UI with CSS class set to "box-pdf-report".
 * This will be the PDF preview box.
 * Modify the PDF_WINDOW_CSS_CLASS if your class is different. Take a look at the styling in the CSS part of this snippet!
 *
 *
 * 2017-11-10 Piotr Szrajber <piotr.szrajber@hexagongeospatial.com>
 */
const PDF_WINDOW_CSS_CLASS = ".box-pdf-report"
var $reportWindow;

init();

function init() {
    $reportWindow = $(PDF_WINDOW_CSS_CLASS);

    $reportWindow.hide();
    $reportWindow.find(".widget-chart").html(`
    <iframe id="pdfReport1" src=""></iframe>
    <div class="pdf-tools">
        <button class="download">Download</button>
        <button class="save">Save to My Content</button>
    </div>
    `);
    $reportWindowCloseButton = $reportWindow.find("a.hide-content");
    $reportWindowCloseButton.off("click");
    $reportWindowCloseButton.click(function() {
        $reportWindow.hide();
    });

    // Generate PDF report
    gsp.ui.toolbar.add({
        id: "new-toolbar-item",
        title: "PDF Report",
        style: "background-position: 0px 0px;" /* CHANGE_ICON */
    }, function(ret) {
        ret.div.onclick = generateReport;
    });
}

function generateReport() {
    gsp.bi.stage.findStage(null, function(stage) {
        // open the PDF in a new window
        var timestamp = new Date();
        var filename = `PDF Report ${timestamp.getHours()} ${timestamp.getMinutes()} ${timestamp.getSeconds()}.PDF`;
        var dd = getPdfContent(stage);
        var doc = pdfMake.createPdf(dd);
        var f = document.getElementById('pdfReport1');

        $reportWindow.find("button.download").click(function() {
            doc.download(filename);
        });
        var callback = function(url) {
            f.setAttribute('src', url);
        }
        doc.getDataUrl(callback, doc);
        $reportWindow.show();
    }, function() {
        console.warn("Could not find stage");
    });
}

// Prepare PDF content using PDFMake templates
// It's really easy to prepare complex PDFs with the PDFMake Playground http://pdfmake.org/playground.html
function getPdfContent(stage) {
    //var burntAreasByLandCover = prepareAggregation(stage, "Desc_Easy", "Land Cover");
    //var burntAreasByMunicipality = prepareAggregation(stage, "COMUNE", "COMUNE");
    //var burntAreasByFireId = prepareAggregation(stage, "Fire_ID", "Fire ID");

    return {
        content: [{
                text: 'Tables',
                style: 'header'
            },
            'Official documentation is in progress, this document is just a glimpse of what is possible with pdfmake and its layout engine.',
            {
                text: 'A simple table (no headers, no width specified, no spans, no styling)',
                style: 'subheader'
            },
            'The following table has nothing more than a body array',
            {
                style: 'tableExample',
                table: {
                    body: [
                        ['Column 1', 'Column 2', 'Column 3'],
                        ['One value goes here', 'Another one here', 'OK?']
                    ]
                }
            }
        ],
        styles: {
            header: {
                fontSize: 18,
                bold: true,
                margin: [0, 0, 0, 10]
            },
            subheader: {
                fontSize: 16,
                bold: true,
                margin: [0, 10, 0, 5]
            },
            tableExample: {
                margin: [0, 5, 0, 15]
            },
            tableHeader: {
                bold: true,
                fontSize: 13,
                color: 'black'
            }
        },
        defaultStyle: {
            // alignment: 'justify'
        }

    }
}

// sample aggregations. this is just an example as it really depends on the data model
function prepareAggregation(stage, aggregateBy, title) {
    //var allRowsInCurrentSelection = stage.facts().dimension(function(d){
    //    return d.Object_ID;
    //}).top(Infinity);

    var firesByDesc = stage.facts().dimension(function(d) {
        return [d[aggregateBy], d.Severity];
    }); // dimension that splits data according to description and severity

    var results = firesByDesc.group(function(a) {
        return a;
    }).reduce(function(p, v) {
        return p + v.Area_ha;
    }, function(p, v) {
        return p - v.Area_ha;
    }, function() {
        return 0;
    }).top(Infinity);

    var resultsAsMatrix = results.reduce(function(acc, v) {
        acc[v.key[0]] = acc[v.key[0]] || {};
        acc[v.key[0]][v.key[1]] = v.value;
        return acc;
    }, {});

    var tableBody = [
        [{
            text: title,
            style: 'tableHeader'
        }, {
            text: 'Low Severity',
            style: 'tableHeader'
        }, {
            text: 'Moderate Severity',
            style: 'tableHeader'
        }, {
            text: 'High Severity',
            style: 'tableHeader'
        }],
    ];

    for (var desc in resultsAsMatrix) {
        var s5 = resultsAsMatrix[desc][5] || 0.0;
        var s6 = resultsAsMatrix[desc][6] || 0.0;
        var s7 = resultsAsMatrix[desc][7] || 0.0;

        var tableRow = [{
            text: desc,
            style: 'tableHeader'
        }, s5.toFixed(2), s6.toFixed(2), s7.toFixed(2)];
        tableBody.push(tableRow);
    }

    return tableBody;
}

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.