From 774309e846a2bead64fb3ea450d98eb2f14c60a3 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 27 Jul 2023 08:37:05 -0500 Subject: [PATCH] Add percents to ColumnStats --- .../columnstats/ColumnStatsStep.java | 44 ++++++++++++++++--- .../columnstats/ColumnStatsStepTest.java | 39 ++++++++++++++-- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/columnstats/ColumnStatsStep.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/columnstats/ColumnStatsStep.java index ae5a12ed..53816fae 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/columnstats/ColumnStatsStep.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/columnstats/ColumnStatsStep.java @@ -23,6 +23,8 @@ package com.kingsrook.qqq.backend.core.processes.implementations.columnstats; import java.io.Serializable; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -303,14 +305,18 @@ public class ColumnStatsStep implements BackendStep ///////////////////////////////////////////////////////////////////////////////// // just in case any of these don't fit in an integer, use decimal for them all // ///////////////////////////////////////////////////////////////////////////////// - Aggregate countNonNullAggregate = new Aggregate(fieldName, AggregateOperator.COUNT).withFieldType(QFieldType.DECIMAL); - Aggregate countDistinctAggregate = new Aggregate(fieldName, AggregateOperator.COUNT_DISTINCT).withFieldType(QFieldType.DECIMAL); - Aggregate sumAggregate = new Aggregate(fieldName, AggregateOperator.SUM).withFieldType(QFieldType.DECIMAL); - Aggregate avgAggregate = new Aggregate(fieldName, AggregateOperator.AVG).withFieldType(QFieldType.DECIMAL); - Aggregate minAggregate = new Aggregate(fieldName, AggregateOperator.MIN); - Aggregate maxAggregate = new Aggregate(fieldName, AggregateOperator.MAX); - AggregateInput statsAggregateInput = new AggregateInput(); + Aggregate countTotalRowsAggregate = new Aggregate(table.getPrimaryKeyField(), AggregateOperator.COUNT).withFieldType(QFieldType.DECIMAL); + Aggregate countNonNullAggregate = new Aggregate(fieldName, AggregateOperator.COUNT).withFieldType(QFieldType.DECIMAL); + Aggregate countDistinctAggregate = new Aggregate(fieldName, AggregateOperator.COUNT_DISTINCT).withFieldType(QFieldType.DECIMAL); + Aggregate sumAggregate = new Aggregate(fieldName, AggregateOperator.SUM).withFieldType(QFieldType.DECIMAL); + Aggregate avgAggregate = new Aggregate(fieldName, AggregateOperator.AVG).withFieldType(QFieldType.DECIMAL); + Aggregate minAggregate = new Aggregate(fieldName, AggregateOperator.MIN); + Aggregate maxAggregate = new Aggregate(fieldName, AggregateOperator.MAX); + + AggregateInput statsAggregateInput = new AggregateInput(); + statsAggregateInput.withAggregate(countTotalRowsAggregate); statsAggregateInput.withAggregate(countNonNullAggregate); + if(doCountDistinct) { statsAggregateInput.withAggregate(countDistinctAggregate); @@ -332,6 +338,7 @@ public class ColumnStatsStep implements BackendStep statsAggregateInput.withAggregate(maxAggregate); } + BigDecimal totalRows = null; if(CollectionUtils.nullSafeHasContents(statsAggregateInput.getAggregates())) { statsAggregateInput.setTableName(tableName); @@ -346,6 +353,8 @@ public class ColumnStatsStep implements BackendStep { AggregateResult statsAggregateResult = statsAggregateOutput.getResults().get(0); + totalRows = ValueUtils.getValueAsBigDecimal(statsAggregateResult.getAggregateValue(countTotalRowsAggregate)); + statsRecord.setValue(countNonNullField.getName(), statsAggregateResult.getAggregateValue(countNonNullAggregate)); if(doCountDistinct) { @@ -388,6 +397,27 @@ public class ColumnStatsStep implements BackendStep } } + ///////////////////// + // figure count%'s // + ///////////////////// + if(totalRows == null) + { + totalRows = new BigDecimal(valueCounts.stream().mapToInt(r -> r.getValueInteger("count")).sum()); + } + + if(totalRows != null && totalRows.compareTo(BigDecimal.ZERO) > 0) + { + BigDecimal oneHundred = new BigDecimal(100); + for(QRecord valueCount : valueCounts) + { + BigDecimal percent = new BigDecimal(Objects.requireNonNullElse(valueCount.getValueInteger("count"), 0)).divide(totalRows, 4, RoundingMode.HALF_UP).multiply(oneHundred).setScale(2, RoundingMode.HALF_UP); + valueCount.setValue("percent", percent); + } + + QFieldMetaData percentField = new QFieldMetaData("percent", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.PERCENT_POINT2).withLabel("Percent"); + QValueFormatter.setDisplayValuesInRecords(Map.of(fieldName, field, "percent", percentField), valueCounts); + } + QInstanceEnricher qInstanceEnricher = new QInstanceEnricher(null); fields.forEach(qInstanceEnricher::enrichField); diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/implementations/columnstats/ColumnStatsStepTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/implementations/columnstats/ColumnStatsStepTest.java index 0e7f9ed9..d2b6fa0b 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/implementations/columnstats/ColumnStatsStepTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/implementations/columnstats/ColumnStatsStepTest.java @@ -1,7 +1,29 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package com.kingsrook.qqq.backend.core.processes.implementations.columnstats; import java.io.Serializable; +import java.math.BigDecimal; import java.util.List; import java.util.Map; import com.kingsrook.qqq.backend.core.BaseTest; @@ -53,9 +75,20 @@ class ColumnStatsStepTest extends BaseTest @SuppressWarnings("unchecked") List valueCounts = (List) values.get("valueCounts"); - assertThat(valueCounts.get(0).getValues()).hasFieldOrPropertyWithValue("lastName", "Simpson").hasFieldOrPropertyWithValue("count", 3); - assertThat(valueCounts.get(1).getValues()).hasFieldOrPropertyWithValue("lastName", null).hasFieldOrPropertyWithValue("count", 2); // here's the assert for the "" and null record above. - assertThat(valueCounts.get(2).getValues()).hasFieldOrPropertyWithValue("lastName", "Flanders").hasFieldOrPropertyWithValue("count", 1); + assertThat(valueCounts.get(0).getValues()) + .hasFieldOrPropertyWithValue("lastName", "Simpson") + .hasFieldOrPropertyWithValue("count", 3) + .hasFieldOrPropertyWithValue("percent", new BigDecimal("50.00")); + + assertThat(valueCounts.get(1).getValues()) + .hasFieldOrPropertyWithValue("lastName", null) + .hasFieldOrPropertyWithValue("count", 2) // here's the assert for the "" and null record above. + .hasFieldOrPropertyWithValue("percent", new BigDecimal("33.33")); + + assertThat(valueCounts.get(2).getValues()) + .hasFieldOrPropertyWithValue("lastName", "Flanders") + .hasFieldOrPropertyWithValue("count", 1) + .hasFieldOrPropertyWithValue("percent", new BigDecimal("16.67")); } } \ No newline at end of file