diff --git a/static/plugin_dbui/src/grid/plugin/Export.js b/static/plugin_dbui/src/grid/plugin/Export.js
index 89e1f00e73f54a75486c7a8a3f338be5d5f549ad..992c5d2ac9fb124749360f0f8cd8cf4b91ab28af 100644
--- a/static/plugin_dbui/src/grid/plugin/Export.js
+++ b/static/plugin_dbui/src/grid/plugin/Export.js
@@ -1,6 +1,6 @@
 /**
- * The plugin export the content of the grid, as display on the screen,
- * into a file. Several formats are available: CSV, LaTex and PDF.
+ * The plugin export the content of the grid, as rendered on the screen,
+ * into a file. Several formats are available: CSV, LaTeX and PDF.
  *
  * A context menu gives access to the different possibilities.
  *
@@ -24,6 +24,16 @@ Ext.define('App.grid.plugin.Export', {
      */
     newLine: '\n',
 
+    /**
+     * @cfg {Array}
+     * The LaTeX preamble.
+     */
+    preamble: [
+        '\\documentclass[a4paper]{article}',
+        '\\usepackage[utf8]{inputenc}',
+        '\\usepackage[T1]{fontenc}'
+    ],
+
     /**
      * @cfg {String}
      * The CSV field separator.
@@ -60,17 +70,17 @@ Ext.define('App.grid.plugin.Export', {
             items : [{
                 text: this.textToCSV,
                 iconCls: 'xminetype-csv',
-                handler: this.toCSV,
+                handler: this.onCsvExport,
                 scope: this
             }, {
                 text: this.textToLaTeX,
                 iconCls: 'xminetype-tex',
-                handler: this.toLaTeX,
+                handler: this.onLatexExport,
                 scope: this
             }, {
                 text: this.textToPDF,
                 iconCls: 'xminetype-pdf',
-                handler: this.toPDF,
+                handler: this.onPdfExport,
                 scope: this
             }]
         });
@@ -84,9 +94,14 @@ Ext.define('App.grid.plugin.Export', {
 
     },
 
-    // private
-    // add delimiter to value for CSV export
-    addDelimiter: function (value) {
+    /**
+     * Surround the value with the CSV delimiter.
+     * Delimiter are only applied to non-numeric value.
+     *
+     * @param {String/Number}
+     *
+     */
+    delimit: function (value) {
 
         "use strict";
 
@@ -97,152 +112,315 @@ Ext.define('App.grid.plugin.Export', {
     },
 
     /**
-     * Show the context menu when right clicking in an empty grid.
+     * Download the data using URI embedded into an HTML link.
+     * The URI is build from the first three arguments.
+     * In addition, the data are HTML encoded to preserve special
+     * character like /, ?, ...
      *
-     * @param {App.grid.Panel} grid
-     * @param {Ext.EventObject} event
-     * @param {Object} eOpts
+     * @param {String}
+     * The mine type of the return file, e.g "text/csv".
+     *
+     * @param {String}
+     * The data encoding, e.g. "utf-8".
+     *
+     * @param {String[]}
+     * The content of the file, one string per line.
+     *
+     * @param {String}
+     * The file name with its extension.
      *
      */
-    onContainerContextMenu: function (grid, event, eOpts) {
+    download: function (minetype, encoding, data, filename) {
 
         "use strict";
 
-        event.stopEvent();
-        this.menu.showAt(event.getXY());
+        var uri = "data:" + minetype + ";charset=" + encoding +",",
+            link;
+
+        // special character like quote, / , ? are HTML encoded
+        uri += encodeURIComponent(data.join(this.newLine));
+
+        // download the file by simulating a click on a link
+        link = Ext.getBody().createChild({
+            tag: "a",
+            href: uri,
+            download: filename
+        });
+
+        link.dom.click();
+        link.destroy();
     },
 
     /**
-     * Inhibit the context menu when right clicking in the grid header.
+     * Get the content of the cells as rendered by the grid.
+     * The method handle the grouping cell as well.
      *
-     * @param {Ext.grid.header.Container} gridheader
-     * @param {Ext.grid.column.Column} column
-     * @param {Ext.EventObject} event
-     * @param {HTMLElement} html
-     * @param {Object} eOpts
+     * @param {Ext.dom.Element}
+     * The DOM element corresponding to the x-grid-row.
+     *
+     * @return {Object}
+     * @return {String} return.groupValue
+     * @return {String[]} return.values
      *
      */
-    onHeaderContextMenu: function (gridheader, colum, event, html, eOpts) {
+    getCells: function (row) {
 
         "use strict";
-        event.stopEvent();
+
+        var cell,
+            groupValue,
+            values = [];
+
+        // get the first cell of the HTML row
+        cell = row.down("td");
+
+        // the first cell can be a container when grouping is used
+        //
+        // a) extract the grouping value in the first children
+        // b) extract the cells of the first row in the second children
+        //    keep in mind that they can be null is the group is closed.
+        //
+        if (cell.dom.className === "x-group-hd-container") {
+            groupValue = cell.dom.childNodes[0].textContent;
+            cell = cell.down("td");
+        }
+
+        // scan the row
+        if (cell) {
+            do {
+                values.push(cell.dom.textContent);
+                cell = cell.next();
+            } while (cell);
+        }
+
+        return {groupValue: groupValue, values: values};
     },
 
     /**
-     * Show the context menu when right clicking in the non-empty grid.
+     * Get the label and the alignment of the visible columns.
      *
-     * @param {Ext.view.View} view
-     * @param {Ext.data.Model} record
-     * @param {HTMLElement} item
-     * @param {Ext.EventObject} event
-     * @param {Object} eOpts
+     * @return {Object}
+     * @return {String[]} return.alignments
+     * @return {String[]} return.labels
      *
      */
-    onItemContextMenu: function (view, record, item, index, event, eOpts) {
+    getColumnHeader: function () {
 
         "use strict";
 
-        event.stopEvent();
-        this.menu.showAt(event.getXY());
+        var alignments = [],
+            columns,
+            container,
+            labels = [],
+            i;
+
+        container = this.getCmp().getDockedItems('headercontainer');
+
+        if (container.length) {
+            columns = container[0].getVisibleGridColumns();
+
+            for (i = 0; i < columns.length; i += 1) {
+                labels.push(columns[i].text);
+
+                if (columns[i].align) {
+                    alignments.push(columns[i].align);
+                } else {
+                    alignments.push('left');
+                }
+            }
+        }
+
+        return {alignments: alignments, labels: labels};
     },
 
     /**
-     * Export the content of the grid as a CSV file.
-     * The method scan the HTML to find the rendered headers, rows
-     * and cells content.
+     * Build the file content under CSV format.
+     *
+     * @return {String[]} One string per line.
      *
-     * Hidden columns and rows are ignored.
      */
-    toCSV: function () {
+    getCSV: function () {
 
         "use strict";
 
-        var el = this.getCmp().getView().getEl(),
-            headerContainer = this.getCmp().getDockedItems('headercontainer'),
-            csv = [],
-            li = [],
-            cell,
-            columns,
+        var cells,
+            data = [],
+            header = this.getColumnHeader(),
             i,
-            link,
-            row,
-            s,
-            value;
-
-        // scan the grid header
-        if (headerContainer.length) {
-            columns = headerContainer[0].getVisibleGridColumns();
+            row;
 
-            for (i = 0; i < columns.length; i += 1) {
-                value = this.addDelimiter(columns[i].text);
-                li.push(value);
+        // the grid header
+        if (header.labels.length) {
+            for (i = 0; i < header.labels.length; i += 1) {
+                header.labels[i] = this.delimit(header.labels[i]);
             }
-
-            csv.push(li.join(this.separator));
+            data.push(header.labels.join(this.separator));
         }
 
-        // scan each row of the HTML table
-        row = el.down("tr");
+        // scan the row of the HTML table
+        row = this.getCmp().getView().getEl().down("tr");
         do {
-            // scan each cell of the HTML row
-            cell = row.down("td");
-
-            // NOTE:
-            // A standard cell is a x-grid-cell
-            // however it is a x-group-hd-container when the grouping is used
-            // a) extract the grouping value in the first children
-            // b) extract the cells of the first row in the second children
-            //    keep in in that they can be null is the group is closed.
-            if (cell.dom.className === "x-group-hd-container") {
-                value = this.addDelimiter(cell.dom.childNodes[0].textContent);
-                csv.push(value);
-                cell = cell.down("td");
+            // get the cells of the HTML row
+            cells = this.getCells(row);
+
+            // group cell
+            if (cells.groupValue) {
+                data.push(this.delimit(cells.groupValue));
+            }
+
+            // standard cells
+            if (cells.values.length) {
+                for (i = 0; i < cells.values.length; i += 1) {
+                    cells.values[i] = this.delimit(cells.values[i]);
+                }
+                data.push(cells.values.join(this.separator));
             }
 
-            // add CSV delimiter to the cell value and separator
-            if (cell) {
-                li = [];
-                do {
-                    value = this.addDelimiter(cell.dom.textContent);
-                    li.push(value);
-                    cell = cell.next();
-                } while (cell);
+            // move to the next HTML row
+            row = row.next();
+
+        } while (row);
+
+        return data;
+    },
+
+    /**
+     * Build the file content under CSV format.
+     *
+     * @return {String[]} One string per line.
+     *
+     */
+    getLaTeX: function () {
+
+        "use strict";
+
+        var cells,
+            cmd,
+            data = [],
+            header = this.getColumnHeader(),
+            nColumns = header.alignments.length,
+            i,
+            row;
+
+        //begin document
+        data = data.concat(this.preamble);
+        data.push('\\begin{document}');
+
+
+        //begin table
+        for (i = 0; i < nColumns; i += 1) {
+            header.alignments[i] = header.alignments[i][0];
+        }
+
+        cmd = '\\begin{tabular}{' + header.alignments.join('') + '}';
+        data.push(cmd);
+
+        // table content
+        row = this.getCmp().getView().getEl().down("tr");
+        do {
+            // get the cells of the HTML row
+            cells = this.getCells(row);
 
-                csv.push(li.join(this.separator));
+            // group cell
+            if (cells.groupValue) {
+                cmd = '\\multicolumn{' + nColumns + '}{l}{' + cells.groupValue + '} \\\\';
+                data.push(cmd);
             }
 
+            // standard cells
+            data.push(cells.values.join(' & ') + ' \\\\');
+
             // move to the next HTML row
             row = row.next();
+
         } while (row);
 
-        // special character like quote, / , ? are HTML encoded
-        s = encodeURIComponent(csv.join(this.newLine));
+        //end table and document
+        data.push('\\end{tabular}');
+        data.push('\\end{document}');
 
-        // download the file by simulating a click on a link
-        link = Ext.getBody().createChild({
-            tag: "a",
-            href: "data:text/csv;charset=utf-8," + s,
-            download: "my_data.csv"
-        });
-        link.dom.click();
-        link.destroy();
+        return data;
+    },
+
+    /**
+     * Show the context menu when right clicking in an empty grid.
+     *
+     * @param {App.grid.Panel} grid
+     * @param {Ext.EventObject} event
+     * @param {Object} eOpts
+     *
+     */
+    onContainerContextMenu: function (grid, event, eOpts) {
+
+        "use strict";
+
+        event.stopEvent();
+        this.menu.showAt(event.getXY());
+    },
+
+    /**
+     * Export the content of the grid as a CSV file.
+     *
+     */
+    onCsvExport: function () {
+
+        "use strict";
+
+        var data = this.getCSV();
+        this.download("text/csv", "utf-8", data, "my_data.csv");
+    },
+
+    /**
+     * Inhibit the context menu when right clicking in the grid header.
+     *
+     * @param {Ext.grid.header.Container} gridheader
+     * @param {Ext.grid.column.Column} column
+     * @param {Ext.EventObject} event
+     * @param {HTMLElement} html
+     * @param {Object} eOpts
+     *
+     */
+    onHeaderContextMenu: function (gridheader, colum, event, html, eOpts) {
+
+        "use strict";
+        event.stopEvent();
+    },
+
+    /**
+     * Show the context menu when right clicking in the non-empty grid.
+     *
+     * @param {Ext.view.View} view
+     * @param {Ext.data.Model} record
+     * @param {HTMLElement} item
+     * @param {Ext.EventObject} event
+     * @param {Object} eOpts
+     *
+     */
+    onItemContextMenu: function (view, record, item, index, event, eOpts) {
+
+        "use strict";
+
+        event.stopEvent();
+        this.menu.showAt(event.getXY());
     },
 
     /**
      * Export the content of the grid as a LaTex file.
      *
      */
-    toLaTeX: function () {
+    onLatexExport: function () {
 
-       "use strict";
+        "use strict";
 
+        var data = this.getLaTeX();
+        this.download("application/x-latex", "utf-8", data, "my_data.tex");
     },
 
     /**
      * Export the content of the grid as a PDF file.
      *
      */
-    toPDF: function () {
+    onPdfExport: function () {
 
        "use strict";
 
diff --git a/views/reports/report_4.html b/views/reports/report_4.html
index 95887ef091cb3f88cc102e182c472abb960625f3..d48af783926b4faa11a076f68cfcc1a1b518488b 100644
--- a/views/reports/report_4.html
+++ b/views/reports/report_4.html
@@ -35,9 +35,16 @@
             {text: "Controller", dataIndex: 'controller', flex: 0.8},
             {text: "Host", dataIndex: 'host', flex: 1},
             {text: "Collections", dataIndex: 'collections', flex: 1.2},
-            {text: "Ratio", dataIndex: 'ratio', flex: 0.5}
+            {
+                text: "Ratio",
+                dataIndex: 'ratio',
+                flex: 0.5,
+                align: 'right',
+                summaryType: 'sum'
+            }
         ],
         features: [{ftype:'grouping'}],
+        //features: [{ftype:'groupingsummary'}, {ftype:'summary'}],
         forceFit: true,
         //hideHeaders: true,
         padding: "10 40 20 60",