[Git][ultreiaio/ird-observe][feature/issue_2755] 2 commits: Ajout de la fonctionnalité d'export html dans l'interface graphique
Tony CHEMIT pushed to branch feature/issue_2755 at ultreiaio / ird-observe Commits: 38a62dbb by Tony Chemit at 2023-11-17T12:14:33+01:00 Ajout de la fonctionnalité d'export html dans l'interface graphique - - - - - 939468ef by Tony Chemit at 2023-11-17T12:15:13+01:00 Ajout des templates pour générer l'export html - - - - - 20 changed files: - client/configuration/src/main/java/fr/ird/observe/client/configuration/ClientConfig.java - client/core/src/main/resources/observe-ui.properties - client/datasource/actions/pom.xml - client/datasource/actions/src/main/i18n/getters/java.getter - + client/datasource/actions/src/main/i18n/templates/reportHtmlExport_en_GB.ftl - + client/datasource/actions/src/main/i18n/templates/reportHtmlExport_es_ES.ftl - + client/datasource/actions/src/main/i18n/templates/reportHtmlExport_fr_FR.ftl - client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/ObserveKeyStrokesActions.java - + client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/HtmlExportModel.java - client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/ReportModel.java - client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/ReportUI.jaxx - client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/ResultTableModel.java - client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/actions/ExportToClipboard.java - + client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/actions/ExportToHtml.java - + client/datasource/actions/src/main/resources/icons/action-export-clipboard.png - client/datasource/actions/src/main/resources/icons/action-export-csv.png - + client/datasource/actions/src/main/resources/icons/action-export-html.png - client/runner/src/main/i18n/translations/client-runner_en_GB.properties - client/runner/src/main/i18n/translations/client-runner_es_ES.properties - client/runner/src/main/i18n/translations/client-runner_fr_FR.properties Changes: ===================================== client/configuration/src/main/java/fr/ird/observe/client/configuration/ClientConfig.java ===================================== @@ -466,7 +466,7 @@ public class ClientConfig extends GeneratedClientConfig implements TripMapConfig } @Override - protected Supplier<Gson> getGsonSupplier() { + public Supplier<Gson> getGsonSupplier() { return gsonSupplier == null ? gsonSupplier = new DtoGsonSupplier(true) : gsonSupplier; } ===================================== client/core/src/main/resources/observe-ui.properties ===================================== @@ -239,6 +239,8 @@ icon.action.wizard-refresh=action-wizard-refresh-16.png icon.action.wizard-config=action-wizard-config-16.png icon.action.wizard-message=action-wizard-message-16.png icon.action.export-csv=action-export-csv.png +icon.action.export-html=action-export-html.png +icon.action.export-clipboard=action-export-clipboard.png # couleurs au format (r,g,b) icon.Table.removeIcon=action-delete.png color.Table.lastRowColor=255,255,0 ===================================== client/datasource/actions/pom.xml ===================================== @@ -112,6 +112,10 @@ <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + </dependency> <dependency> <groupId>io.ultreia.java4all</groupId> <artifactId>application-template</artifactId> ===================================== client/datasource/actions/src/main/i18n/getters/java.getter ===================================== @@ -84,6 +84,9 @@ observe.ui.datasource.editor.actions.report.exportToClipboard observe.ui.datasource.editor.actions.report.exportToCsv observe.ui.datasource.editor.actions.report.exportToCsv.done observe.ui.datasource.editor.actions.report.exportToCsv.tip +observe.ui.datasource.editor.actions.report.exportToHtml +observe.ui.datasource.editor.actions.report.exportToHtml.done +observe.ui.datasource.editor.actions.report.exportToHtml.tip observe.ui.datasource.editor.actions.report.no.report.found observe.ui.datasource.editor.actions.report.report.count.found observe.ui.datasource.editor.actions.report.title ===================================== client/datasource/actions/src/main/i18n/templates/reportHtmlExport_en_GB.ftl ===================================== @@ -0,0 +1,240 @@ +<#-- @ftlvariable name=".data_model" type="fr.ird.observe.client.datasource.actions.report.HtmlExportModel" --> +<!DOCTYPE html> +<!-- + #%L + ObServe Client :: DataSource :: Actions + %% + Copyright (C) 2008 - 2023 IRD, Ultreia.io + %% + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program. If not, see + <http://www.gnu.org/licenses/gpl-3.0.html>. + #L% + --> + +<html lang="en"> +<head> + <link href="https://unpkg.com/gridjs/dist/theme/mermaid.min.css" rel="stylesheet"/> + <title>Report ${.data_model.selectedReport.name}</title> + <script src="https://unpkg.com/gridjs/dist/gridjs.umd.js"></script> + <style> + .config-panel { + padding: 10px; + margin: 10px 0; + background: #fcfcfc; + border: 1px solid #e9e9e9; + display: inline-block; + } + + .config-panel label { + margin-right: 10px; + } + + td.gridjs-td { + background-color: transparent; + } + + tr:nth-child(even) { + background-color: #dddddd; + } + + tr:hover td { + background-color: rgba(0, 0, 0, 0.1); + } + + tr td:hover { + color: #fff; + } + + th:hover { + background-color: #999; + color: #fff; + } + </style> + <script type="application/javascript"> + + function searchValue() { + return searchOption.checked; + } + + function resizableValue() { + return resizableOption.checked; + } + + function sortValue() { + return sortOption.checked; + } + + function paginationValue() { + return paginationOption.checked ? {limit: paginationSizeOption.value} : false; + } + + function toggleSearch(config, source) { + let newValue = source.checked; + // console.info("Toggle search to: " + newValue); + updateGrid(config); + } + + function toggleResizable(config, source) { + let newValue = source.checked; + // console.info("Toggle resizable to: " + newValue); + updateGrid(config); + } + + function toggleSort(config, source) { + let newValue = source.checked; + // console.info("Toggle sort to: " + newValue); + updateGrid(config); + } + + function togglePagination(config, source) { + let newValue = source.checked; + // console.info("Toggle pagination to: " + newValue); + if (newValue) { + paginationSizeOption["disabled"] = null; + } else { + paginationSizeOption.disabled = true; + } + updateGrid(config); + } + + function changePaginationSize(config, source) { + let newValue = source.value; + // console.info("Change pagination size to: " + newValue); + updateGrid(config); + } + + function updateGrid(config) { + let newConfig = { + language: config.language, + data: config.data, + columns: config.columns, + search: searchValue(), + resizable: resizableValue(), + sort: sortValue(), + pagination: paginationValue() + }; + gridContainerParent.innerHTML = "<div id=\"wrapper\"></div>"; + // console.info(newConfig); + setTimeout(() => { + grid = new gridjs.Grid(newConfig).render(document.getElementById("wrapper")); + }, 50); + } + </script> +</head> +<body> +<h1>Report ${.data_model.selectedReport.name}</h1> +<h2>Configuration</h2> +<h3>Selected data:</h3> +<ul> + <li> + <#assign selectDataModel = .data_model.selectDataModel.selectionDataModel /> + <#assign selectDataCount = selectDataModel.getSelectedCount() /> + + <#if selectDataCount == 1> 1 trip <#else> ${selectDataCount} trips</#if>. + <ul> + <#list selectDataModel.getSelectedLabels() as key, value> + <li> + ${key} + <ul> + <#list value as trip> + <li>${trip}</li> + </#list> + </ul> + </li> + </#list> + </ul> + </li> +</ul> +<h3>Selected report:</h3> + +<#assign selectedReport = .data_model.selectedReport /> +<p><b>${selectedReport.name}</b></p> +<ul> + <#list selectedReport.getVariables() as variable> + <li> + Variable ${variable.name} : ${variable.selectedValue}. + </li> + </#list> +</ul> +<h2>Result</h2> + +<div class="config-panel"> + <label><input id="search" type="checkbox" checked/> Search</label>| + <label><input id="resizable" type="checkbox" checked/> Resizable columns</label>| + <label><input id="sort" type="checkbox" checked/> Sort</label>| + <label><input id="pagination" type="checkbox"/> Pagination</label>| + <label>Page size <input id="paginationSize" type="number" disabled value="20"/></label> +</div> +<div id="wrapperParent"> + <div id="wrapper"></div> +</div> +<script type="application/javascript"> + const json = ${.data_model.json}; + + const gridContainerParent = document.getElementById("wrapperParent"); + const searchOption = document.getElementById("search"); + const resizableOption = document.getElementById("resizable"); + const paginationOption = document.getElementById("pagination"); + const paginationSizeOption = document.getElementById("paginationSize"); + const sortOption = document.getElementById("sort"); + + let grid = new gridjs.Grid({ + search: searchValue(), + resizable: resizableValue(), + sort: sortValue(), + pagination: paginationValue(), + <#--language: {--> + <#-- 'search': {--> + <#-- 'placeholder': '🔍 Recherche...'--> + <#-- },--> + <#-- sort: {--> + <#-- sortAsc: 'Tri ascendant',--> + <#-- sortDesc: 'Tri descendant',--> + <#-- },--> + <#-- pagination: {--> + <#-- previous: 'Précédent',--> + <#-- next: 'Suivant',--> + <#-- navigate: (page, pages) => `Page ${r"${page}"} sur ${r"${pages}"}`,--> + <#-- page: (page) => `Page ${r"${page}"}`,--> + <#-- showing: 'Affichage des lignes de',--> + <#-- of: 'sur',--> + <#-- to: 'à',--> + <#-- results: 'lignes.',--> + <#-- },--> + <#-- loading: 'Chargement...'--> + <#--},--> + columns: !!json.columnNames ? json.columnNames : [], + data: json.data.data + }); + updateGrid(grid.config); + + searchOption.addEventListener("change", function () { + toggleSearch(grid.config, this); + }); + resizableOption.addEventListener("change", function () { + toggleResizable(grid.config, this); + }); + sortOption.addEventListener("change", function () { + toggleSort(grid.config, this); + }); + paginationOption.addEventListener("change", function () { + togglePagination(grid.config, this); + }); + + paginationSizeOption.addEventListener("change", function () { + changePaginationSize(grid.config, this); + }); +</script> +</body> +</html> ===================================== client/datasource/actions/src/main/i18n/templates/reportHtmlExport_es_ES.ftl ===================================== @@ -0,0 +1,240 @@ +<#-- @ftlvariable name=".data_model" type="fr.ird.observe.client.datasource.actions.report.HtmlExportModel" --> +<!DOCTYPE html> +<!-- + #%L + ObServe Client :: DataSource :: Actions + %% + Copyright (C) 2008 - 2023 IRD, Ultreia.io + %% + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program. If not, see + <http://www.gnu.org/licenses/gpl-3.0.html>. + #L% + --> +<#--TODO--> +<html lang="en"> +<head> + <link href="https://unpkg.com/gridjs/dist/theme/mermaid.min.css" rel="stylesheet"/> + <title>Report ${.data_model.selectedReport.name}</title> + <script src="https://unpkg.com/gridjs/dist/gridjs.umd.js"></script> + <style> + .config-panel { + padding: 10px; + margin: 10px 0; + background: #fcfcfc; + border: 1px solid #e9e9e9; + display: inline-block; + } + + .config-panel label { + margin-right: 10px; + } + + td.gridjs-td { + background-color: transparent; + } + + tr:nth-child(even) { + background-color: #dddddd; + } + + tr:hover td { + background-color: rgba(0, 0, 0, 0.1); + } + + tr td:hover { + color: #fff; + } + + th:hover { + background-color: #999; + color: #fff; + } + </style> + <script type="application/javascript"> + + function searchValue() { + return searchOption.checked; + } + + function resizableValue() { + return resizableOption.checked; + } + + function sortValue() { + return sortOption.checked; + } + + function paginationValue() { + return paginationOption.checked ? {limit: paginationSizeOption.value} : false; + } + + function toggleSearch(config, source) { + let newValue = source.checked; + // console.info("Toggle search to: " + newValue); + updateGrid(config); + } + + function toggleResizable(config, source) { + let newValue = source.checked; + // console.info("Toggle resizable to: " + newValue); + updateGrid(config); + } + + function toggleSort(config, source) { + let newValue = source.checked; + // console.info("Toggle sort to: " + newValue); + updateGrid(config); + } + + function togglePagination(config, source) { + let newValue = source.checked; + // console.info("Toggle pagination to: " + newValue); + if (newValue) { + paginationSizeOption["disabled"] = null; + } else { + paginationSizeOption.disabled = true; + } + updateGrid(config); + } + + function changePaginationSize(config, source) { + let newValue = source.value; + // console.info("Change pagination size to: " + newValue); + updateGrid(config); + } + + function updateGrid(config) { + let newConfig = { + language: config.language, + data: config.data, + columns: config.columns, + search: searchValue(), + resizable: resizableValue(), + sort: sortValue(), + pagination: paginationValue() + }; + gridContainerParent.innerHTML = "<div id=\"wrapper\"></div>"; + // console.info(newConfig); + setTimeout(() => { + grid = new gridjs.Grid(newConfig).render(document.getElementById("wrapper")); + }, 50); + } + </script> +</head> +<body> +<h1>Report ${.data_model.selectedReport.name}</h1> +<h2>Configuration</h2> +<h3>Selected data:</h3> +<ul> + <li> + <#assign selectDataModel = .data_model.selectDataModel.selectionDataModel /> + <#assign selectDataCount = selectDataModel.getSelectedCount() /> + + <#if selectDataCount == 1> 1 trip <#else> ${selectDataCount} trips</#if>. + <ul> + <#list selectDataModel.getSelectedLabels() as key, value> + <li> + ${key} + <ul> + <#list value as trip> + <li>${trip}</li> + </#list> + </ul> + </li> + </#list> + </ul> + </li> +</ul> +<h3>Selected report:</h3> + +<#assign selectedReport = .data_model.selectedReport /> +<p><b>${selectedReport.name}</b></p> +<ul> + <#list selectedReport.getVariables() as variable> + <li> + Variable ${variable.name} : ${variable.selectedValue}. + </li> + </#list> +</ul> +<h2>Result</h2> + +<div class="config-panel"> + <label><input id="search" type="checkbox" checked/> Search</label>| + <label><input id="resizable" type="checkbox" checked/> Resizable columns</label>| + <label><input id="sort" type="checkbox" checked/> Sort</label>| + <label><input id="pagination" type="checkbox"/> Pagination</label>| + <label>Page size <input id="paginationSize" type="number" disabled value="20"/></label> +</div> +<div id="wrapperParent"> + <div id="wrapper"></div> +</div> +<script type="application/javascript"> + const json = ${.data_model.json}; + + const gridContainerParent = document.getElementById("wrapperParent"); + const searchOption = document.getElementById("search"); + const resizableOption = document.getElementById("resizable"); + const paginationOption = document.getElementById("pagination"); + const paginationSizeOption = document.getElementById("paginationSize"); + const sortOption = document.getElementById("sort"); + + let grid = new gridjs.Grid({ + search: searchValue(), + resizable: resizableValue(), + sort: sortValue(), + pagination: paginationValue(), + <#--language: {--> + <#-- 'search': {--> + <#-- 'placeholder': '🔍 Recherche...'--> + <#-- },--> + <#-- sort: {--> + <#-- sortAsc: 'Tri ascendant',--> + <#-- sortDesc: 'Tri descendant',--> + <#-- },--> + <#-- pagination: {--> + <#-- previous: 'Précédent',--> + <#-- next: 'Suivant',--> + <#-- navigate: (page, pages) => `Page ${r"${page}"} sur ${r"${pages}"}`,--> + <#-- page: (page) => `Page ${r"${page}"}`,--> + <#-- showing: 'Affichage des lignes de',--> + <#-- of: 'sur',--> + <#-- to: 'à',--> + <#-- results: 'lignes.',--> + <#-- },--> + <#-- loading: 'Chargement...'--> + <#--},--> + columns: !!json.columnNames ? json.columnNames : [], + data: json.data.data + }); + updateGrid(grid.config); + + searchOption.addEventListener("change", function () { + toggleSearch(grid.config, this); + }); + resizableOption.addEventListener("change", function () { + toggleResizable(grid.config, this); + }); + sortOption.addEventListener("change", function () { + toggleSort(grid.config, this); + }); + paginationOption.addEventListener("change", function () { + togglePagination(grid.config, this); + }); + + paginationSizeOption.addEventListener("change", function () { + changePaginationSize(grid.config, this); + }); +</script> +</body> +</html> ===================================== client/datasource/actions/src/main/i18n/templates/reportHtmlExport_fr_FR.ftl ===================================== @@ -0,0 +1,240 @@ +<#-- @ftlvariable name=".data_model" type="fr.ird.observe.client.datasource.actions.report.HtmlExportModel" --> +<!DOCTYPE html> +<!-- + #%L + ObServe Client :: DataSource :: Actions + %% + Copyright (C) 2008 - 2023 IRD, Ultreia.io + %% + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program. If not, see + <http://www.gnu.org/licenses/gpl-3.0.html>. + #L% + --> + +<html lang="fr"> +<head> + <link href="https://unpkg.com/gridjs/dist/theme/mermaid.min.css" rel="stylesheet"/> + <title>Rapport ${.data_model.selectedReport.name}</title> + <script src="https://unpkg.com/gridjs/dist/gridjs.umd.js"></script> + <style> + .config-panel { + padding: 10px; + margin: 10px 0; + background: #fcfcfc; + border: 1px solid #e9e9e9; + display: inline-block; + } + + .config-panel label { + margin-right: 10px; + } + + td.gridjs-td { + background-color: transparent; + } + + tr:nth-child(even) { + background-color: #dddddd; + } + + tr:hover td { + background-color: rgba(0, 0, 0, 0.1); + } + + tr td:hover { + color: #fff; + } + + th:hover { + background-color: #999; + color: #fff; + } + </style> + <script type="application/javascript"> + + function searchValue() { + return searchOption.checked; + } + + function resizableValue() { + return resizableOption.checked; + } + + function sortValue() { + return sortOption.checked; + } + + function paginationValue() { + return paginationOption.checked ? {limit: paginationSizeOption.value} : false; + } + + function toggleSearch(config, source) { + let newValue = source.checked; + // console.info("Toggle search to: " + newValue); + updateGrid(config); + } + + function toggleResizable(config, source) { + let newValue = source.checked; + // console.info("Toggle resizable to: " + newValue); + updateGrid(config); + } + + function toggleSort(config, source) { + let newValue = source.checked; + // console.info("Toggle sort to: " + newValue); + updateGrid(config); + } + + function togglePagination(config, source) { + let newValue = source.checked; + // console.info("Toggle pagination to: " + newValue); + if (newValue) { + paginationSizeOption["disabled"] = null; + } else { + paginationSizeOption.disabled = true; + } + updateGrid(config); + } + + function changePaginationSize(config, source) { + let newValue = source.value; + // console.info("Change pagination size to: " + newValue); + updateGrid(config); + } + + function updateGrid(config) { + let newConfig = { + language: config.language, + data: config.data, + columns: config.columns, + search: searchValue(), + resizable: resizableValue(), + sort: sortValue(), + pagination: paginationValue() + }; + gridContainerParent.innerHTML = "<div id=\"wrapper\"></div>"; + // console.info(newConfig); + setTimeout(() => { + grid = new gridjs.Grid(newConfig).render(document.getElementById("wrapper")); + }, 50); + } + </script> +</head> +<body> +<h1>Rapport ${.data_model.selectedReport.name}</h1> +<h2>Configuration</h2> +<h3>Données sélectionnées :</h3> +<ul> + <li> + <#assign selectDataModel = .data_model.selectDataModel.selectionDataModel /> + <#assign selectDataCount = selectDataModel.getSelectedCount() /> + + <#if selectDataCount == 1> 1 marée <#else> ${selectDataCount} marées</#if>. + <ul> + <#list selectDataModel.getSelectedLabels() as key, value> + <li> + ${key} + <ul> + <#list value as trip> + <li>${trip}</li> + </#list> + </ul> + </li> + </#list> + </ul> + </li> +</ul> +<h3>Rapport sélectionné :</h3> + +<#assign selectedReport = .data_model.selectedReport /> +<p><b>${selectedReport.name}</b></p> +<ul> + <#list selectedReport.getVariables() as variable> + <li> + Variable ${variable.name} : ${variable.selectedValue}. + </li> + </#list> +</ul> +<h2>Résultat</h2> + +<div class="config-panel"> + <label><input id="search" type="checkbox" checked/> Recherche</label>| + <label><input id="resizable" type="checkbox" checked/> Colonnes redimensionnables</label>| + <label><input id="sort" type="checkbox" checked/> Tri des colonnes</label>| + <label><input id="pagination" type="checkbox"/> Pagination</label>| + <label>Nombre de lignes par page <input id="paginationSize" type="number" disabled value="20"/></label> +</div> +<div id="wrapperParent"> + <div id="wrapper"></div> +</div> +<script type="application/javascript"> + const json = ${.data_model.json}; + + const gridContainerParent = document.getElementById("wrapperParent"); + const searchOption = document.getElementById("search"); + const resizableOption = document.getElementById("resizable"); + const paginationOption = document.getElementById("pagination"); + const paginationSizeOption = document.getElementById("paginationSize"); + const sortOption = document.getElementById("sort"); + + let grid = new gridjs.Grid({ + search: searchValue(), + resizable: resizableValue(), + sort: sortValue(), + pagination: paginationValue(), + language: { + 'search': { + 'placeholder': '🔍 Recherche...' + }, + sort: { + sortAsc: 'Tri ascendant', + sortDesc: 'Tri descendant', + }, + pagination: { + previous: 'Précédent', + next: 'Suivant', + navigate: (page, pages) => `Page ${r"${page}"} sur ${r"${pages}"}`, + page: (page) => `Page ${r"${page}"}`, + showing: 'Affichage des lignes de', + of: 'sur', + to: 'à', + results: 'lignes.', + }, + loading: 'Chargement...' + }, + columns: !!json.columnNames ? json.columnNames : [], + data: json.data.data + }); + updateGrid(grid.config); + + searchOption.addEventListener("change", function () { + toggleSearch(grid.config, this); + }); + resizableOption.addEventListener("change", function () { + toggleResizable(grid.config, this); + }); + sortOption.addEventListener("change", function () { + toggleSort(grid.config, this); + }); + paginationOption.addEventListener("change", function () { + togglePagination(grid.config, this); + }); + + paginationSizeOption.addEventListener("change", function () { + changePaginationSize(grid.config, this); + }); +</script> +</body> +</html> ===================================== client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/ObserveKeyStrokesActions.java ===================================== @@ -42,6 +42,7 @@ public class ObserveKeyStrokesActions extends ObserveKeyStrokesEditorApi { public static final KeyStroke KEY_STROKE_EXPORT_POPUP = KeyStroke.getKeyStroke("pressed F10"); public static final KeyStroke KEY_STROKE_EXPORT_CLIPBOARD = KeyStroke.getKeyStroke("pressed F2"); public static final KeyStroke KEY_STROKE_EXPORT_CSV = KeyStroke.getKeyStroke("pressed F3"); + public static final KeyStroke KEY_STROKE_EXPORT_HTML = KeyStroke.getKeyStroke("pressed F4"); public static final KeyStroke KEY_STROKE_EXIT = KeyStroke.getKeyStroke("alt pressed F"); } ===================================== client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/HtmlExportModel.java ===================================== @@ -0,0 +1,108 @@ +package fr.ird.observe.client.datasource.actions.report; + +/*- + * #%L + * ObServe Client :: DataSource :: Actions + * %% + * Copyright (C) 2008 - 2023 IRD, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import com.google.gson.Gson; +import fr.ird.observe.client.datasource.actions.config.SelectDataModel; +import fr.ird.observe.report.Report; +import io.ultreia.java4all.application.template.spi.GenerateTemplate; +import io.ultreia.java4all.util.matrix.DataMatrix; + +import java.util.List; + +/** + * The model used to generate data file in html export. + * + * Created at 15/11/2023. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 9.3.0 + */ +@GenerateTemplate(template = "reportHtmlExport.ftl") +public class HtmlExportModel { + /** + * Selected report. + */ + private final transient Report selectedReport; + /** + * Selected data model. + */ + private final transient SelectDataModel selectDataModel; + + private final List<String> columnNames; + private final List<String> rowNames; + private final DataMatrix data; + private final boolean withColumnHeader; + private final boolean withRowHeader; + private final transient String json; + + public HtmlExportModel(Gson gson, + Report selectedReport, + SelectDataModel selectDataModel, + List<String> columnNames, + List<String> rowNames, + DataMatrix data, + boolean withColumnHeader, + boolean withRowHeader) { + this.selectedReport = selectedReport; + this.selectDataModel = selectDataModel; + this.columnNames = columnNames; + this.rowNames = rowNames; + this.data = data; + this.withColumnHeader = withColumnHeader; + this.withRowHeader = withRowHeader; + this.json = gson.toJson(this); + } + + public String getJson() { + return json; + } + + public Report getSelectedReport() { + return selectedReport; + } + + public SelectDataModel getSelectDataModel() { + return selectDataModel; + } + + public List<String> getColumnNames() { + return columnNames; + } + + public List<String> getRowNames() { + return rowNames; + } + + public DataMatrix getData() { + return data; + } + + public boolean isWithColumnHeader() { + return withColumnHeader; + } + + public boolean isWithRowHeader() { + return withRowHeader; + } +} ===================================== client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/ReportModel.java ===================================== @@ -60,6 +60,7 @@ import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -397,6 +398,10 @@ public class ReportModel extends AdminActionModel { return new File(getClientConfig().getExportDirectory(), String.format("export-%1$s--%2$tF--%2$tk-%2$tM-%2$tS.csv", Objects.requireNonNull(getSelectedReport()).getId().replaceAll("\\.", "_"), new Date())); } + public Path newExportHtmlDirectory() { + return getClientConfig().getExportDirectory().toPath().resolve( String.format("export-%1$s--%2$tF--%2$tk-%2$tM-%2$tS", Objects.requireNonNull(getSelectedReport()).getId().replaceAll("\\.", "_"), new Date())); + } + void onReportsChanged(ReportUI ui, List<ReportDefinition> newValue) { log.debug(String.format("New reports : %s", newValue)); newValue.sort(Comparator.comparing(ReportDefinition::getName)); @@ -596,6 +601,11 @@ public class ReportModel extends AdminActionModel { } } + public HtmlExportModel toJsonModel() { + + return new HtmlExportModel(getClientConfig().getGsonSupplier().get(), getSelectedReport(), selectDataModel, getResultModel().getColumnNames(), getResultModel().getRowNames(), getResultModel().getData(), getResultModel().isWithColumnHeader(), getResultModel().isWithRowHeader()); + } + private Map<String, FilterableComboBox<?>> initVariableComponents(ReportUI ui, Report report) { Map<String, FilterableComboBox<?>> result = new TreeMap<>(); JPanel variablesPanel = ui.getReportVariableSelectorPanel(); ===================================== client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/ReportUI.jaxx ===================================== @@ -80,6 +80,7 @@ public void destroy() { <JPopupMenu id='exportPopup'> <JMenuItem id='exportToClipboard' styleClass="enabledIfReportSelected"/> <JMenuItem id='exportToCsv' styleClass="enabledIfReportSelected"/> + <JMenuItem id='exportToHtml' styleClass="enabledIfReportSelected"/> </JPopupMenu> <JToolBar id='titleRightToolBar'> <JToggleButton id='toggleExport'/> ===================================== client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/ResultTableModel.java ===================================== @@ -236,6 +236,26 @@ public class ResultTableModel extends AbstractTableModel { return data == null ? null : data.getValue(columnIndex, rowIndex); } + public List<String> getColumnNames() { + return columnNames; + } + + public List<String> getRowNames() { + return rowNames; + } + + public DataMatrix getData() { + return data; + } + + public boolean isWithColumnHeader() { + return withColumnHeader; + } + + public boolean isWithRowHeader() { + return withRowHeader; + } + public String getClipboardContent(boolean copyRowHeaders, boolean copyColumnHeaders) { return getDataContent(copyRowHeaders, copyColumnHeaders, true, '\t'); } @@ -264,5 +284,4 @@ public class ResultTableModel extends AbstractTableModel { result += data.getClipboardContent(copyRowHeaders || !withRowHeader, true, escapeCell, separator); return result; } - } ===================================== client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/actions/ExportToClipboard.java ===================================== @@ -43,7 +43,7 @@ public class ExportToClipboard extends AdminTabUIActionSupport<ReportUI> { public ExportToClipboard() { super(t("observe.ui.datasource.editor.actions.report.exportToClipboard"), t("observe.ui.datasource.editor.actions.synchro.copy.tip"), - "report-copy", + "export-clipboard", ObserveKeyStrokesActions.KEY_STROKE_EXPORT_CLIPBOARD); setCheckMenuItemIsArmed(false); } ===================================== client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/actions/ExportToHtml.java ===================================== @@ -0,0 +1,95 @@ +package fr.ird.observe.client.datasource.actions.report.actions; + +/*- + * #%L + * ObServe Client :: DataSource :: Actions + * %% + * Copyright (C) 2008 - 2023 IRD, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import fr.ird.observe.client.datasource.actions.ObserveKeyStrokesActions; +import fr.ird.observe.client.datasource.actions.actions.AdminTabUIActionSupport; +import fr.ird.observe.client.datasource.actions.report.HtmlExportModel; +import fr.ird.observe.client.datasource.actions.report.HtmlExportModelTemplate; +import fr.ird.observe.client.datasource.actions.report.ReportModel; +import fr.ird.observe.client.datasource.actions.report.ReportUI; +import fr.ird.observe.client.util.UIHelper; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static io.ultreia.java4all.i18n.I18n.t; + +/** + * Created at 15/11/2023. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 9.3.0 + */ +public class ExportToHtml extends AdminTabUIActionSupport<ReportUI> { + + private static final Logger log = LogManager.getLogger(ExportToClipboard.class); + + public ExportToHtml() { + super(t("observe.ui.datasource.editor.actions.report.exportToHtml"), + t("observe.ui.datasource.editor.actions.report.exportToHtml.tip"), + "export-html", + ObserveKeyStrokesActions.KEY_STROKE_EXPORT_HTML); + setCheckMenuItemIsArmed(false); + } + + @Override + protected void doActionPerformed(ActionEvent e, ReportUI ui) { + try { + run(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + + private void run() throws IOException { + ReportModel stepModel = ui.getStepModel(); + + String reportId = stepModel.getSelectedReport().getId(); + // generate result directory + Path exportDirectory = stepModel.newExportHtmlDirectory(); + Files.createDirectories(exportDirectory); + + // generate data file + Path dataFile = exportDirectory.resolve("data.json"); + log.info("Generate data file for report ({}) at {}", reportId, dataFile); + HtmlExportModel dataModel = stepModel.toJsonModel(); + String dataContent = dataModel.getJson(); + Files.writeString(dataFile, dataContent); + + // generate html file + Path htmlFile = exportDirectory.resolve("report.html"); + log.info("Generate html file for report ({}) at {}", reportId, htmlFile); + String fileContent = HtmlExportModelTemplate.generate(dataModel); + Files.writeString(htmlFile, fileContent); + + // open html file + UIHelper.openLink(htmlFile.toUri()); + setUiStatus(t("observe.ui.datasource.editor.actions.report.exportToHtml.done", exportDirectory)); + } +} ===================================== client/datasource/actions/src/main/resources/icons/action-export-clipboard.png ===================================== Binary files /dev/null and b/client/datasource/actions/src/main/resources/icons/action-export-clipboard.png differ ===================================== client/datasource/actions/src/main/resources/icons/action-export-csv.png ===================================== Binary files a/client/datasource/actions/src/main/resources/icons/action-export-csv.png and b/client/datasource/actions/src/main/resources/icons/action-export-csv.png differ ===================================== client/datasource/actions/src/main/resources/icons/action-export-html.png ===================================== Binary files /dev/null and b/client/datasource/actions/src/main/resources/icons/action-export-html.png differ ===================================== client/runner/src/main/i18n/translations/client-runner_en_GB.properties ===================================== @@ -573,6 +573,9 @@ observe.ui.datasource.editor.actions.report.exportToClipboard=Clipboard observe.ui.datasource.editor.actions.report.exportToCsv=CSV observe.ui.datasource.editor.actions.report.exportToCsv.done=Csv export done at %s observe.ui.datasource.editor.actions.report.exportToCsv.tip=Export to csv +observe.ui.datasource.editor.actions.report.exportToHtml=Html +observe.ui.datasource.editor.actions.report.exportToHtml.done=Html export done at %s +observe.ui.datasource.editor.actions.report.exportToHtml.tip=Export to html observe.ui.datasource.editor.actions.report.no.report.found=No report found. observe.ui.datasource.editor.actions.report.report.count.found=Found %s reports. observe.ui.datasource.editor.actions.report.report.description=Description of selected report ===================================== client/runner/src/main/i18n/translations/client-runner_es_ES.properties ===================================== @@ -573,6 +573,9 @@ observe.ui.datasource.editor.actions.report.exportToClipboard=Clipboard observe.ui.datasource.editor.actions.report.exportToCsv=CSV observe.ui.datasource.editor.actions.report.exportToCsv.done=Csv export done at %s observe.ui.datasource.editor.actions.report.exportToCsv.tip=Export to csv +observe.ui.datasource.editor.actions.report.exportToHtml=Html +observe.ui.datasource.editor.actions.report.exportToHtml.done=Html export done at %s \#TODO +observe.ui.datasource.editor.actions.report.exportToHtml.tip=Export to html \#TODO observe.ui.datasource.editor.actions.report.no.report.found=No report found. \#TODO observe.ui.datasource.editor.actions.report.report.count.found=Found %s reports. \#TODO observe.ui.datasource.editor.actions.report.report.description=Descripción del informe seleccionado ===================================== client/runner/src/main/i18n/translations/client-runner_fr_FR.properties ===================================== @@ -573,6 +573,9 @@ observe.ui.datasource.editor.actions.report.exportToClipboard=Presse papier observe.ui.datasource.editor.actions.report.exportToCsv=Csv observe.ui.datasource.editor.actions.report.exportToCsv.done=Export csv sauvegardé dans le fichier %s observe.ui.datasource.editor.actions.report.exportToCsv.tip=Exporter au format Csv +observe.ui.datasource.editor.actions.report.exportToHtml=Html +observe.ui.datasource.editor.actions.report.exportToHtml.done=Export html sauvegardé dans le répertoire %s +observe.ui.datasource.editor.actions.report.exportToHtml.tip=Exporter au format Html observe.ui.datasource.editor.actions.report.no.report.found=Aucun rapport trouvé. observe.ui.datasource.editor.actions.report.report.count.found=%s rapports détectés. observe.ui.datasource.editor.actions.report.report.description=Description du rapport sélectionné View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/compare/455386042bf6ae7140dabc8a2... -- View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/compare/455386042bf6ae7140dabc8a2... You're receiving this email because of your account on gitlab.com.
participants (1)
-
Tony CHEMIT (@tchemit)