Tony CHEMIT pushed to branch develop at ultreiaio / ird-observe Commits: b5febe07 by Tony Chemit at 2023-12-04T18:27:47+01:00 Stash - - - - - 13 changed files: - 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/report/HtmlExportModel.java - + client/datasource/actions/src/main/resources/fr/ird/observe/client/datasource/actions/report/HtmlExportModel.js - core/persistence/report/src/main/resources/observe-reports.properties - + src/site/markdown/report/embedded-column-renderers.md - src/site/markdown/report/syntax.md - toolkit/api-report/pom.xml - toolkit/api-report/src/main/java/fr/ird/observe/report/ColumnRendererConsumer.java - + toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfEquals18nReferentialValue.java - + toolkit/api-report/src/main/resources/fr/ird/observe/report/renderers/HighlightIfEqualsI18nReferentialValue.js - toolkit/api/src/main/java/fr/ird/observe/dto/ObserveUtil.java Changes: ===================================== client/datasource/actions/src/main/i18n/templates/reportHtmlExport_en_GB.ftl ===================================== @@ -199,112 +199,11 @@ } </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 searchValue1 = searchValue(); - let sortValue1 = sortValue(); - let resizableValue1 = resizableValue(); - // FIXME Need to update config.columns otherwise we will keep previous options (sort, resizable...) - let newConfig = { - language: config.language, - data: config.data, - columns: config.columns, - search: searchValue1, - resizable: resizableValue1, - sort: sortValue1, - pagination: paginationValue() - }; - gridContainerParent.innerHTML = '<div id="wrapper"></div>'; - // console.info(newConfig); - setTimeout(() => { - <#--noinspection JSUnresolvedReference--> - grid = new gridjs.Grid(newConfig).render(document.getElementById("wrapper")); - }, 50); - } - - function deserializeJson(json) { - let height = json.height; - let width = json.width; - let data = json.rows; - let result = new Array(height); - for (let row = 0; row < height; row++) { - let cells = data[row].split('||'); - let realRow = new Array(width); - result[row]=realRow; - for (let column = 0; column < width; column++) { - let rowElement = cells[column]; - realRow[column] = rowElement ==='$'?null:rowElement; - } - } - return result; - } + ${.data_model.script} <#list .data_model.columnRendererFunctions as key> -${key}</#list> - - function createColumns(json) { - let result = !!json["columnNames"] ? json["columnNames"] : []; - if (result.length === 0) { - return result; - } - let renderers = json["columnRendererDefinitions"]; - let i = 0; - let data = json.data; - <#list .data_model.columnRendererInitCode as value> - ${value}</#list> - return result; - } + ${key} + </#list> </script> </head> <body> @@ -364,62 +263,22 @@ ${key}</#list> </div> <script type="application/javascript"> - const json = ${.data_model.json}; - json.data.data = deserializeJson(json.data); - - 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: createColumns(json), - 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); - }); + new GridHandler( + document, + {}, + function (json) { + let result = !!json["columnNames"] ? json["columnNames"] : []; + if (result.length === 0) { + return result; + } + let renderers = json["columnRendererDefinitions"]; + let i = 0; + let data = json.data; + <#list .data_model.columnRendererInitCode as value> + ${value}</#list> + return result; + }, + ${.data_model.json}).init(); </script> </body> </html> ===================================== client/datasource/actions/src/main/i18n/templates/reportHtmlExport_es_ES.ftl ===================================== @@ -199,112 +199,11 @@ } </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 searchValue1 = searchValue(); - let sortValue1 = sortValue(); - let resizableValue1 = resizableValue(); - // FIXME Need to update config.columns otherwise we will keep previous options (sort, resizable...) - let newConfig = { - language: config.language, - data: config.data, - columns: config.columns, - search: searchValue1, - resizable: resizableValue1, - sort: sortValue1, - pagination: paginationValue() - }; - gridContainerParent.innerHTML = '<div id="wrapper"></div>'; - // console.info(newConfig); - setTimeout(() => { - <#--noinspection JSUnresolvedReference--> - grid = new gridjs.Grid(newConfig).render(document.getElementById("wrapper")); - }, 50); - } - - function deserializeJson(json) { - let height = json.height; - let width = json.width; - let data = json.rows; - let result = new Array(height); - for (let row = 0; row < height; row++) { - let cells = data[row].split('||'); - let realRow = new Array(width); - result[row]=realRow; - for (let column = 0; column < width; column++) { - let rowElement = cells[column]; - realRow[column] = rowElement ==='$'?null:rowElement; - } - } - return result; - } + ${.data_model.script} <#list .data_model.columnRendererFunctions as key> -${key}</#list> - - function createColumns(json) { - let result = !!json["columnNames"] ? json["columnNames"] : []; - if (result.length === 0) { - return result; - } - let renderers = json["columnRendererDefinitions"]; - let i = 0; - let data = json.data; - <#list .data_model.columnRendererInitCode as value> - ${value}</#list> - return result; - } + ${key} + </#list> </script> </head> <body> @@ -363,62 +262,22 @@ ${key}</#list> </div> <script type="application/javascript"> - const json = ${.data_model.json}; - json.data.data = deserializeJson(json.data); - - 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: createColumns(json), - 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); - }); + new GridHandler( + document, + {}, + function (json) { + let result = !!json["columnNames"] ? json["columnNames"] : []; + if (result.length === 0) { + return result; + } + let renderers = json["columnRendererDefinitions"]; + let i = 0; + let data = json.data; + <#list .data_model.columnRendererInitCode as value> + ${value}</#list> + return result; + }, + ${.data_model.json}).init(); </script> </body> </html> ===================================== client/datasource/actions/src/main/i18n/templates/reportHtmlExport_fr_FR.ftl ===================================== @@ -85,22 +85,22 @@ background-color: rgb(255, 165, 0); } -<#-- <#–noinspection CssUnusedSymbol–>--> -<#-- td:has(span.cellWarning):before {--> -<#-- padding-right: 8px;--> -<#-- content: "\26A0";--> -<#-- }--> + <#-- <#–noinspection CssUnusedSymbol–>--> + <#-- td:has(span.cellWarning):before {--> + <#-- padding-right: 8px;--> + <#-- content: "\26A0";--> + <#-- }--> <#--noinspection CssUnusedSymbol--> td.cellError { background-color: rgb(255, 0, 0); } -<#-- <#–noinspection CssUnusedSymbol–>--> -<#-- td:has(span.cellError):before {--> -<#-- padding-right: 8px;--> -<#-- content: "\26D4";--> -<#-- }--> + <#-- <#–noinspection CssUnusedSymbol–>--> + <#-- td:has(span.cellError):before {--> + <#-- padding-right: 8px;--> + <#-- content: "\26D4";--> + <#-- }--> .widget { position: relative; @@ -200,112 +200,11 @@ } </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 searchValue1 = searchValue(); - let sortValue1 = sortValue(); - let resizableValue1 = resizableValue(); - // FIXME Need to update config.columns otherwise we will keep previous options (sort, resizable...) - let newConfig = { - language: config.language, - data: config.data, - columns: config.columns, - search: searchValue1, - resizable: resizableValue1, - sort: sortValue1, - pagination: paginationValue() - }; - gridContainerParent.innerHTML = '<div id="wrapper"></div>'; - // console.info(newConfig); - setTimeout(() => { - <#--noinspection JSUnresolvedReference--> - grid = new gridjs.Grid(newConfig).render(document.getElementById("wrapper")); - }, 50); - } - - function deserializeJson(json) { - let height = json.height; - let width = json.width; - let data = json.rows; - let result = new Array(height); - for (let row = 0; row < height; row++) { - let cells = data[row].split('||'); - let realRow = new Array(width); - result[row]=realRow; - for (let column = 0; column < width; column++) { - let rowElement = cells[column]; - realRow[column] = rowElement ==='$'?null:rowElement; - } - } - return result; - } + ${.data_model.script} <#list .data_model.columnRendererFunctions as key> -${key}</#list> - - function createColumns(json) { - let result = !!json["columnNames"] ? json["columnNames"] : []; - if (result.length === 0) { - return result; - } - let renderers = json["columnRendererDefinitions"]; - let i = 0; - let data = json.data; - <#list .data_model.columnRendererInitCode as value> - ${value}</#list> - return result; - } + ${key} + </#list> </script> </head> <body> @@ -364,22 +263,9 @@ ${key}</#list> </div> <script type="application/javascript"> - const json = ${.data_model.json}; - json.data.data = deserializeJson(json.data); - - 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: { + new GridHandler( + document, + { 'search': { 'placeholder': '🔍 Recherche...' }, @@ -390,8 +276,8 @@ ${key}</#list> pagination: { previous: 'Précédent', next: 'Suivant', - navigate: (page, pages) => `Page ${r"${page}"} sur ${r"${pages}"}`, - page: (page) => `Page ${r"${page}"}`, + navigate: (page, pages) => `Page ${page} sur ${pages}`, + page: (page) => `Page ${page}`, showing: 'Affichage des lignes de', of: 'sur', to: 'à', @@ -399,27 +285,19 @@ ${key}</#list> }, loading: 'Chargement...' }, - columns: createColumns(json), - 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); - }); + function (json) { + let result = !!json["columnNames"] ? json["columnNames"] : []; + if (result.length === 0) { + return result; + } + let renderers = json["columnRendererDefinitions"]; + let i = 0; + let data = json.data; + <#list .data_model.columnRendererInitCode as value> + ${value}</#list> + return result; + }, + ${.data_model.json}).init(); </script> </body> </html> ===================================== client/datasource/actions/src/main/java/fr/ird/observe/client/datasource/actions/report/HtmlExportModel.java ===================================== @@ -24,14 +24,18 @@ package fr.ird.observe.client.datasource.actions.report; import com.google.gson.Gson; import fr.ird.observe.client.datasource.actions.config.SelectDataModel; +import fr.ird.observe.dto.ObserveUtil; import fr.ird.observe.report.ColumnRendererConsumers; import fr.ird.observe.report.Report; import fr.ird.observe.report.ReportColumnRenderersParameters; import fr.ird.observe.report.definition.ColumnRendererDefinition; import io.ultreia.java4all.application.template.spi.GenerateTemplate; +import io.ultreia.java4all.util.SingletonSupplier; import io.ultreia.java4all.util.matrix.DataMatrix; +import java.net.URL; import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -44,6 +48,7 @@ import java.util.Set; */ @GenerateTemplate(template = "reportHtmlExport.ftl") public class HtmlExportModel { + private static SingletonSupplier<String> SCRIPT_CONTENT; /** * Selected report. */ @@ -52,7 +57,6 @@ public class HtmlExportModel { * Selected data model. */ private final transient SelectDataModel selectDataModel; - private final List<String> columnNames; private final List<String> rowNames; private final DataMatrix data; @@ -92,6 +96,15 @@ public class HtmlExportModel { this.json = gson.toJson(this); } + public String getScript() { + if (SCRIPT_CONTENT == null) { + String resourceName = getClass().getSimpleName() + ".js"; + URL url = Objects.requireNonNull(getClass().getResource(resourceName), "Could not find resource: " + resourceName); + SCRIPT_CONTENT = ObserveUtil.loadResourceContentSupplier(url, content -> content.substring(content.indexOf("*/") + 2).trim()); + } + return SCRIPT_CONTENT.get(); + } + public String getJson() { return json; } ===================================== client/datasource/actions/src/main/resources/fr/ird/observe/client/datasource/actions/report/HtmlExportModel.js ===================================== @@ -0,0 +1,176 @@ +/*- + * #%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% + */ +class GridHandler { + language; + columns; + data; + searchOption; + resizableOption; + paginationOption; + paginationSizeOption; + sortOption; + gridContainerParent; + search; + sort; + resizable; + pagination; + + constructor(document, language, createColumns, json) { + this.gridContainerParent = document.getElementById("wrapperParent"); + this.searchOption = document.getElementById("search"); + this.resizableOption = document.getElementById("resizable"); + this.paginationOption = document.getElementById("pagination"); + this.paginationSizeOption = document.getElementById("paginationSize"); + this.sortOption = document.getElementById("sort"); + this.language = language; + json.data.data = this.deserializeJson(json.data); + this.columns = createColumns(json); + this.data = json.data.data; + this.search = this.searchValue(); + this.resizable = this.resizableValue(); + this.sort = this.sortValue(); + this.pagination = this.paginationValue(); + } + + updateGrid() { + this.gridContainerParent.innerHTML = '<div id="wrapper"></div>'; + setTimeout(() => { + // noinspection JSUnresolvedReference + new gridjs.Grid({ + language: this.language, + data: this.data, + columns: this.deepCopyColumns(this.columns), + search: this.search, + resizable: this.resizable, + sort: this.sort, + pagination: this.pagination, + }).render(document.getElementById("wrapper")); + }, 50); + } + + deepCopyColumns(columns) { + let result = []; + let index = 0; + for (const column of columns) { + if (column instanceof Object) { + let newColumn = {}; + newColumn['name'] = column['name']; + if (!!!column['formatter'] != null) { + newColumn['formatter'] = column['formatter']; + } + if (column['attributes'] != null) { + newColumn['attributes'] = column['attributes']; + } + result[index++] = newColumn; + } else { + result[index++] = column; + } + } + return result; + } + + deserializeJson(json) { + let height = json.height; + let width = json.width; + let data = json.rows; + let result = new Array(height); + for (let row = 0; row < height; row++) { + let cells = data[row].split('||'); + let realRow = new Array(width); + result[row] = realRow; + for (let column = 0; column < width; column++) { + let rowElement = cells[column]; + realRow[column] = rowElement === '$' ? null : rowElement; + } + } + return result; + } + + searchValue() { + return this.searchOption.checked; + } + + resizableValue() { + return this.resizableOption.checked; + } + + sortValue() { + return this.sortOption.checked; + } + + paginationValue() { + return this.paginationOption.checked ? {limit: this.paginationSizeOption.value} : false; + } + + toggleSearch(source) { + this.search = this.searchValue(); + this.updateGrid(); + } + + toggleResizable(source) { + this.resizable = this.resizableValue(); + this.updateGrid(); + } + + toggleSort(source) { + this.sort = this.sortValue(); + this.updateGrid(); + } + + togglePagination(source) { + let newValue = source.checked; + if (newValue) { + this.paginationSizeOption["disabled"] = null; + } else { + this.paginationSizeOption.disabled = true; + } + this.pagination = this.paginationValue(); + this.updateGrid(); + } + + changePaginationSize(source) { + this.pagination = this.paginationValue(); + this.updateGrid(); + } + + init() { + + this.updateGrid(); + let that = this; + this.searchOption.addEventListener("change", function () { + that.toggleSearch(this); + }); + this.resizableOption.addEventListener("change", function () { + that.toggleResizable(this); + }); + this.sortOption.addEventListener("change", function () { + that.toggleSort(this); + }); + this.paginationOption.addEventListener("change", function () { + that.togglePagination(this); + }); + + this.paginationSizeOption.addEventListener("change", function () { + that.changePaginationSize(this); + }); + } +} ===================================== core/persistence/report/src/main/resources/observe-reports.properties ===================================== @@ -888,8 +888,8 @@ Left Join t.localMarketSurveySamplingAcquisitionStatus localMarketSurveySampling Left Join t.advancedSamplingAcquisitionStatus advancedSamplingAcquisitionStatus \ Where t.id In :tripId \ Order By vessel.code,t.startDate,t.endDate -report.psLogbookTrip.columnRenderers.1.type=HighlightIfNotI18nReferentialValue -report.psLogbookTrip.columnRenderers.1.parameters=14,15,16,17,18,19,20,21|fr.ird.referential.ps.common.AcquisitionStatus#1464000000000#001 +report.psLogbookTrip.columnRenderers.1.type=HighlightIfEquals18nReferentialValue +report.psLogbookTrip.columnRenderers.1.parameters=14,15,16,17,18,19,20,21|fr.ird.referential.ps.common.AcquisitionStatus#1464000000000#999 report.psLogbookTrip.columnRenderers.2.type=HighlightIfAbsoluteDeltaIsPositive report.psLogbookTrip.columnRenderers.2.parameters=10|11|0.0001|0.5 report.psLogbookTrip.columnRenderers.3.type=HighlightIfAbsoluteDeltaIsPositive ===================================== src/site/markdown/report/embedded-column-renderers.md ===================================== @@ -0,0 +1,60 @@ +# Documentation des règles de rendu de colonnes disponibles + +Ce document décrit toutes les règles de rendu de colonnes disponibles sur les rapports. + +### HighlightIfAbsoluteDeltaIsPositive + +Ce rendu permet de vérifier que les valeurs de chaque cellule de deux colonnes sont identiques. + +Si deux valeurs ne sont pas égales, on colorise la cellule en orange (avertissement) pour un premier seuil et en rouge +(erreur) selon un second seuil. + +Le rendu nécessite quatre paramètres : + +* la première colonne +* la seconde colonne +* le seuil pour afficher des avertissements +* le seuil pour afficher des erreurs + +Exemple d'utilisation : + +```properties +report.xxx.columnRenderers.1.type=HighlightIfAbsoluteDeltaIsPositive +report.ccc.columnRenderers.1.parameters=10|11|0.0001|0.5 +``` + +### HighlightIfNumericalValueIsPositive + +Ce rendu permet de vérifier que les valeurs de chaque celleule d'une colonne n'est pas zéro. + +Si la valeur n'est pas zéro, on colorise la cellule en orange (avertissement) pour un premier seuil et en rouge +(erreur) selon un second seuil. + +Le rendu nécessite quatre paramètres : + +* la colonne +* le seuil pour afficher des avertissements +* le seuil pour afficher des erreurs + +Exemple d'utilisation + +```properties +report.xxx.columnRenderers.1.type=HighlightIfNumericalValueIsPositive +report.xxx.columnRenderers.1.parameters=19|0.0001|0.5 +``` + +### HighlightIfEquals18nReferentialValue + +Ce rendu permet d'afficher de coloriser en rouge toute cellule des colonnes sélectionnées dont la valeur (de type référentiel i18n) n'est pas celle spécifié (via son identifiant). + +Le rendu nécessite deux paramètres : + +* une liste de colonnes sépararées par des virgules +* l'identifiant du référentiel à mettre en valeur + +Exemple d'utilisation : + +```properties +report.xxx.columnRenderers.1.type=HighlightIfEquals18nReferentialValue +report.xxx.columnRenderers.1.parameters=14,15,16,17,18,19,20,21|fr.ird.referential.ps.common.AcquisitionStatus#1464000000000#999 +``` ===================================== src/site/markdown/report/syntax.md ===================================== @@ -319,6 +319,25 @@ report.xxx.operations.1.comment=Un commentaire optionnel pour documenter l'opér Les opérations disponibles et leur documentation sont décrites dans le document [suivant](./embedded-operations.html). +### Rendu de colonnes + +Depuis la version **9.3.0**, il est possible de définir des rendus de colonnes via le fichier de définition, +ce rendu sera valable dans le client swing ainsi que dans les rapports html. + +Un rendu est défini par deux lignes : + +1. Une pour définir le type de rendu +2. Une pour paramétrer ce rendu + +```properties +report.xxx.columnRenderers.1.type=HighlightIfAbsoluteDeltaIsPositive +report.ccc.columnRenderers.1.parameters=10|11|0.0001|0.5 +``` + +Il est possible d'ajouter plusieurs rendus sur un même rapport. + +Les rendus disponibles et leur documentation sont décrits dans le document [suivant](./embedded-column-renderers.html). + ## Pour aller plus loin Vous pouvez aussi consulter la [documentation des rapports embarqués par l'application](./embedded-reports.html). ===================================== toolkit/api-report/pom.xml ===================================== @@ -38,10 +38,6 @@ <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> - <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> - </dependency> <dependency> <groupId>io.ultreia.java4all</groupId> <artifactId>java-lang</artifactId> ===================================== toolkit/api-report/src/main/java/fr/ird/observe/report/ColumnRendererConsumer.java ===================================== @@ -22,7 +22,7 @@ package fr.ird.observe.report; * #L% */ -import com.google.common.io.Resources; +import fr.ird.observe.dto.ObserveUtil; import fr.ird.observe.report.renderers.HighlightIfAbsoluteDeltaIsPositive; import io.ultreia.java4all.util.SingletonSupplier; import org.jdesktop.swingx.JXTable; @@ -31,6 +31,8 @@ import org.jdesktop.swingx.decorator.HighlightPredicate; import java.awt.Color; import java.io.IOException; +import java.io.InputStream; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Objects; @@ -53,13 +55,18 @@ public interface ColumnRendererConsumer<P extends ColumnRendererParameters> { } static SingletonSupplier<String> htmlFunctions(Class<?> type) { - return SingletonSupplier.of(() -> { - try { - return Resources.toString(Objects.requireNonNull(HighlightIfAbsoluteDeltaIsPositive.class.getResource(type.getSimpleName() + ".js")), StandardCharsets.UTF_8); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); + String resourceName = type.getSimpleName() + ".js"; + URL url = Objects.requireNonNull(HighlightIfAbsoluteDeltaIsPositive.class.getResource(resourceName), "Could not find resource: " + resourceName); + return ObserveUtil.loadResourceContentSupplier(url, content -> content.substring(content.indexOf("*/") + 2)); + } + + static String loadResourceContent(URL url) { + try (InputStream in = url.openStream()) { + String content = new String(in.readAllBytes(), StandardCharsets.UTF_8); + return content.substring(content.indexOf("*/") + 2).trim(); + } catch (IOException e) { + throw new RuntimeException(e); + } } /** ===================================== toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfEquals18nReferentialValue.java ===================================== @@ -0,0 +1,139 @@ +package fr.ird.observe.report.renderers; + +/*- + * #%L + * ObServe Toolkit :: API :: Report + * %% + * 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.auto.service.AutoService; +import fr.ird.observe.report.ColumnRendererConsumer; +import fr.ird.observe.report.ColumnRendererParameters; +import fr.ird.observe.report.ReportRequestExecutor; +import io.ultreia.java4all.util.SingletonSupplier; +import org.jdesktop.swingx.JXTable; +import org.jdesktop.swingx.decorator.ColorHighlighter; + +import java.awt.Color; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; + +import static fr.ird.observe.report.renderers.HighlightIfEquals18nReferentialValue.Parameters; + +/** + * Created at 02/12/2023. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 9.3.0 + */ +@AutoService(ColumnRendererConsumer.class) +public class HighlightIfEquals18nReferentialValue implements ColumnRendererConsumer<Parameters> { + private final static SingletonSupplier<String> HTML_FUNCTION = ColumnRendererConsumer.htmlFunctions(HighlightIfNotI18nReferentialValue.class); + + @Override + public int parametersCount() { + return 2; + } + + @Override + public String parametersSyntax() { + return "column1,column2,...,columnN|id"; + } + + @Override + public Parameters parseParameters(String parameters) { + String[] split = pareParametersSyntax(parameters); + String[] split2 = split[0].trim().split("\\s*,\\s*"); + Set<Integer> columns = new TreeSet<>(); + for (String s : split2) { + columns.add(Integer.parseInt(s)); + } + String id = split[1]; + return new Parameters(columns, id); + } + + @Override + public Parameters createParameters(ReportRequestExecutor requestExecutor, String parameters) { + Parameters result = ColumnRendererConsumer.super.createParameters(requestExecutor, parameters); + String label = requestExecutor.getReferentialLabel(result.getId()); + return result.setLabel(label); + } + + @Override + public Object consumeHtml(Parameters parameters) { + return Map.of("name", name(), + "columns", parameters.getColumns(), + "label", parameters.getLabel()); + } + + @Override + public void consumeSwing(Parameters parameters, JXTable table) { + table.addHighlighter(new ColorHighlighter((renderer, adapter) -> { + Object value = adapter.getValue(); + int column = adapter.convertColumnIndexToModel(adapter.column); + return parameters.getColumns().contains(column) && Objects.equals(value, parameters.getLabel()); + }, Color.RED, Color.BLACK)); + } + + @Override + public String htmlFunctions() { + return HTML_FUNCTION.get(); + } + + public static final class Parameters implements ColumnRendererParameters { + private final Set<Integer> columns; + private final String id; + private final String label; + + public Parameters(Set<Integer> columns, String id, String label) { + this.columns = columns; + this.id = id; + this.label = label; + } + + public Parameters(Set<Integer> columns, String id) { + this.columns = columns; + this.id = id; + this.label = null; + } + + @Override + public String name() { + return HighlightIfEquals18nReferentialValue.class.getSimpleName(); + } + + public Set<Integer> getColumns() { + return columns; + } + + public String getId() { + return id; + } + + public String getLabel() { + return label; + } + + public Parameters setLabel(String label) { + return new Parameters(columns, id, label); + } + } +} ===================================== toolkit/api-report/src/main/resources/fr/ird/observe/report/renderers/HighlightIfEqualsI18nReferentialValue.js ===================================== @@ -0,0 +1,44 @@ +/*- + * #%L + * ObServe Toolkit :: API :: Report + * %% + * 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% + */ +function HighlightIfEqualsI18nReferentialValue(cell, label) { + if (!!!cell) { + return; + } + if (cell === label) { + return { + 'data-cell-content': cell, + 'class': 'gridjs-td cellError', + }; + } +} + +function initHighlightIfEqualsI18nReferentialValue(renderer, result, json) { + let columns = renderer["columns"]; + let label = renderer["label"]; + for (let j = 0; j < columns.length; j++) { + let column = columns[j]; + result [column] = { + name: result [column], + attributes: cell => HighlightIfEqualsI18nReferentialValue(cell, label) + }; + } +} ===================================== toolkit/api/src/main/java/fr/ird/observe/dto/ObserveUtil.java ===================================== @@ -31,15 +31,21 @@ import fr.ird.observe.spi.module.BusinessSubModule; import io.ultreia.java4all.config.ApplicationConfig; import io.ultreia.java4all.config.ConfigResource; import io.ultreia.java4all.lang.Strings; +import io.ultreia.java4all.util.SingletonSupplier; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Configurator; import org.hashids.Hashids; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; +import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.MalformedURLException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.text.Collator; @@ -75,38 +81,7 @@ public class ObserveUtil { public static final String PNG_EXTENSION = ".png"; public static final String JS_ENGINE_NAME = "rhino"; private static final Hashids ID_GENERATOR = new Hashids("ObServeHasSomeSalt", 8, "0123456789#abcdefghijklmnopqrestuvwxyz"); - - @SuppressWarnings("rawtypes") - private static class ClassComparator<C extends Class<?>> implements Comparator<C> { - - private final Map<Class, String> cache; - private final Function<Class, String> function; - - private final Collator collator; - - private ClassComparator(Function<Class, String> function, Locale locale) { - this.cache = new HashMap<>(); - this.function = function; - this.collator = Collator.getInstance(locale); - this.collator.setStrength(Collator.PRIMARY); - } - - @Override - public int compare(Class o1, Class o2) { - String s1 = getValue(o1); - String s2 = getValue(o2); - return this.collator.compare(s1, s2); - } - - String getValue(Class klass) { - return cache.computeIfAbsent(klass, k -> function.apply(klass)); - } - - public void sort(List<C> list) { - list.sort(this); - cache.clear(); - } - } + private static final Logger log = LogManager.getLogger(ObserveUtil.class); public static String newUUID(Date now) { return ID_GENERATOR.encode(now.getTime()); @@ -253,15 +228,62 @@ public class ObserveUtil { } /** - * * @param jsonString the json string in compact mode * @return the gson in a pretty mode */ - public static String toPrettyFormat(String jsonString) - { + public static String toPrettyFormat(String jsonString) { JsonObject json = JsonParser.parseString(jsonString).getAsJsonObject(); Gson gson = new GsonBuilder().setPrettyPrinting().create(); return gson.toJson(json); } + + public static SingletonSupplier<String> loadResourceContentSupplier(URL url, Function<String, String> contentTransformer) { + return SingletonSupplier.of(() -> { + String content = loadResourceContent(url); + return contentTransformer == null ? content : contentTransformer.apply(content); + }); + } + + public static String loadResourceContent(URL url) { + log.info("Loading resource content: {}", url); + try (InputStream in = url.openStream()) { + String content = new String(in.readAllBytes(), StandardCharsets.UTF_8); + return content.substring(content.indexOf("*/") + 1); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("rawtypes") + private static class ClassComparator<C extends Class<?>> implements Comparator<C> { + + private final Map<Class, String> cache; + private final Function<Class, String> function; + + private final Collator collator; + + private ClassComparator(Function<Class, String> function, Locale locale) { + this.cache = new HashMap<>(); + this.function = function; + this.collator = Collator.getInstance(locale); + this.collator.setStrength(Collator.PRIMARY); + } + + @Override + public int compare(Class o1, Class o2) { + String s1 = getValue(o1); + String s2 = getValue(o2); + return this.collator.compare(s1, s2); + } + + String getValue(Class klass) { + return cache.computeIfAbsent(klass, k -> function.apply(klass)); + } + + public void sort(List<C> list) { + list.sort(this); + cache.clear(); + } + } } View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/commit/b5febe07ec1ccb249e85d8b9f0... -- View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/commit/b5febe07ec1ccb249e85d8b9f0... You're receiving this email because of your account on gitlab.com.
participants (1)
-
Tony CHEMIT (@tchemit)