From 9b34ee7fe700f6d7647a15af0e404b4e08fa6c06 Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Wed, 7 Dec 2022 15:31:48 -0600 Subject: [PATCH] SPRINT-17: updates to parent widget dropdown data, updated group bys to be objects allowing group by with custom formats --- .../widgets/ParentWidgetRenderer.java | 37 +++- .../tables/aggregate/AggregateInput.java | 30 ++-- .../tables/aggregate/AggregateResult.java | 16 +- .../actions/tables/aggregate/GroupBy.java | 152 +++++++++++++++++ .../aggregate/QFilterOrderByGroupBy.java | 123 ++++++++++++++ .../model/dashboard/widgets/ChartData.java | 129 +++++++++++++- .../dashboard/widgets/LineChartData.java | 39 ++++- .../dashboard/widgets/StatisticsData.java | 51 +++++- .../model/dashboard/widgets/WidgetType.java | 2 + .../dashboard/ParentWidgetMetaData.java | 159 +++++++++++++++++- .../processes/utils/GeneralProcessUtils.java | 17 ++ .../rdbms/actions/AbstractRDBMSAction.java | 42 ++++- .../rdbms/actions/RDBMSAggregateAction.java | 20 +-- .../actions/RDBMSAggregateActionTest.java | 16 +- 14 files changed, 766 insertions(+), 67 deletions(-) create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/GroupBy.java create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/QFilterOrderByGroupBy.java diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ParentWidgetRenderer.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ParentWidgetRenderer.java index db27b707..bca03c6c 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ParentWidgetRenderer.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ParentWidgetRenderer.java @@ -31,6 +31,9 @@ import java.util.Set; import com.kingsrook.qqq.backend.core.actions.ActionHelper; import com.kingsrook.qqq.backend.core.actions.values.SearchPossibleValueSourceAction; import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSourceInput; import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSourceOutput; import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput; @@ -40,6 +43,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.dashboard.ParentWidgetMetaD import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue; import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; +import com.kingsrook.qqq.backend.core.utils.StringUtils; /******************************************************************************* @@ -68,15 +72,42 @@ public class ParentWidgetRenderer extends AbstractWidgetRenderer List>> pvsData = new ArrayList<>(); List pvsLabels = new ArrayList<>(); List pvsNames = new ArrayList<>(); - for(String possibleValueSourceName : CollectionUtils.nonNullList(metaData.getPossibleValueNameList())) + for(ParentWidgetMetaData.DropdownData dropdownData : CollectionUtils.nonNullList(metaData.getDropdowns())) { - QPossibleValueSource possibleValueSource = input.getInstance().getPossibleValueSource(possibleValueSourceName); - pvsLabels.add(possibleValueSource.getLabel() != null ? possibleValueSource.getLabel() : possibleValueSourceName); + String possibleValueSourceName = dropdownData.getPossibleValueSourceName(); + QPossibleValueSource possibleValueSource = input.getInstance().getPossibleValueSource(possibleValueSourceName); + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // this looks complicated, but is just look for a label in the dropdown data and if found use it, // + // otherwise look for label in PVS and if found use that, otherwise just use the PVS name // + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + pvsLabels.add(dropdownData.getLabel() != null ? dropdownData.getLabel() : (possibleValueSource.getLabel() != null ? possibleValueSource.getLabel() : possibleValueSourceName)); pvsNames.add(possibleValueSourceName); SearchPossibleValueSourceInput pvsInput = new SearchPossibleValueSourceInput(input.getInstance()); pvsInput.setSession(input.getSession()); pvsInput.setPossibleValueSourceName(possibleValueSourceName); + + if(dropdownData.getForeignKeyFieldName() != null) + { + //////////////////////////////////////// + // look for an id in the query params // + //////////////////////////////////////// + Integer id = null; + if(input.getQueryParams() != null && input.getQueryParams().containsKey("id") && StringUtils.hasContent(input.getQueryParams().get("id"))) + { + id = Integer.parseInt(input.getQueryParams().get("id")); + } + if(id != null) + { + pvsInput.setDefaultQueryFilter(new QQueryFilter().withCriteria( + new QFilterCriteria( + dropdownData.getForeignKeyFieldName(), + QCriteriaOperator.EQUALS, + id))); + } + } + SearchPossibleValueSourceOutput output = new SearchPossibleValueSourceAction().execute(pvsInput); List> dropdownOptionList = new ArrayList<>(); diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/AggregateInput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/AggregateInput.java index 1ab93c43..6443cc4e 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/AggregateInput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/AggregateInput.java @@ -38,7 +38,7 @@ public class AggregateInput extends AbstractTableActionInput { private QQueryFilter filter; private List aggregates; - private List groupByFieldNames; + private List groupBys = new ArrayList<>(); private List queryJoins = null; @@ -148,50 +148,50 @@ public class AggregateInput extends AbstractTableActionInput /******************************************************************************* - ** Getter for groupByFieldNames + ** Getter for groupBys ** *******************************************************************************/ - public List getGroupByFieldNames() + public List getGroupBys() { - return groupByFieldNames; + return groupBys; } /******************************************************************************* - ** Setter for groupByFieldNames + ** Setter for groupBys ** *******************************************************************************/ - public void setGroupByFieldNames(List groupByFieldNames) + public void setGroupBys(List groupBys) { - this.groupByFieldNames = groupByFieldNames; + this.groupBys = groupBys; } /******************************************************************************* - ** Fluent setter for groupByFieldNames + ** Fluent setter for groupBys ** *******************************************************************************/ - public AggregateInput withGroupByFieldNames(List groupByFieldNames) + public AggregateInput withGroupBys(List groupBys) { - this.groupByFieldNames = groupByFieldNames; + this.groupBys = groupBys; return (this); } /******************************************************************************* - ** Fluent setter for groupByFieldNames + ** Fluent setter for groupBys ** *******************************************************************************/ - public AggregateInput withGroupByFieldName(String groupByFieldName) + public AggregateInput withGroupBy(GroupBy groupBy) { - if(this.groupByFieldNames == null) + if(this.groupBys == null) { - this.groupByFieldNames = new ArrayList<>(); + this.groupBys = new ArrayList<>(); } - this.groupByFieldNames.add(groupByFieldName); + this.groupBys.add(groupBy); return (this); } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/AggregateResult.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/AggregateResult.java index b7c3686c..7fe4f4e4 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/AggregateResult.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/AggregateResult.java @@ -33,7 +33,7 @@ import java.util.Map; public class AggregateResult { private Map aggregateValues = new LinkedHashMap<>(); - private Map groupByValues = new LinkedHashMap<>(); + private Map groupByValues = new LinkedHashMap<>(); @@ -101,7 +101,7 @@ public class AggregateResult ** Getter for groupByValues ** *******************************************************************************/ - public Map getGroupByValues() + public Map getGroupByValues() { return groupByValues; } @@ -112,7 +112,7 @@ public class AggregateResult ** Setter for groupByValues ** *******************************************************************************/ - public void setGroupByValues(Map groupByValues) + public void setGroupByValues(Map groupByValues) { this.groupByValues = groupByValues; } @@ -123,7 +123,7 @@ public class AggregateResult ** Fluent setter for groupByValues ** *******************************************************************************/ - public AggregateResult withGroupByValues(Map groupByValues) + public AggregateResult withGroupByValues(Map groupByValues) { this.groupByValues = groupByValues; return (this); @@ -135,13 +135,13 @@ public class AggregateResult ** Fluent setter for groupByValues ** *******************************************************************************/ - public AggregateResult withGroupByValue(String fieldName, Serializable value) + public AggregateResult withGroupByValue(GroupBy groupBy, Serializable value) { if(this.groupByValues == null) { this.groupByValues = new LinkedHashMap<>(); } - this.groupByValues.put(fieldName, value); + this.groupByValues.put(groupBy, value); return (this); } @@ -150,9 +150,9 @@ public class AggregateResult /******************************************************************************* ** *******************************************************************************/ - public Serializable getGroupByValue(String fieldName) + public Serializable getGroupByValue(GroupBy groupBy) { - return (this.groupByValues.get(fieldName)); + return (this.groupByValues.get(groupBy)); } } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/GroupBy.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/GroupBy.java new file mode 100644 index 00000000..a838e099 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/GroupBy.java @@ -0,0 +1,152 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. 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.model.actions.tables.aggregate; + + +import java.io.Serializable; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class GroupBy implements Serializable +{ + private QFieldType type; + private String fieldName; + private String formatString; + + + + /******************************************************************************* + ** + *******************************************************************************/ + public GroupBy(QFieldType type, String fieldName, String formatString) + { + this.type = type; + this.fieldName = fieldName; + this.formatString = formatString; + } + + + + /******************************************************************************* + ** Getter for type + ** + *******************************************************************************/ + public QFieldType getType() + { + return type; + } + + + + /******************************************************************************* + ** Setter for type + ** + *******************************************************************************/ + public void setType(QFieldType type) + { + this.type = type; + } + + + + /******************************************************************************* + ** Fluent setter for type + ** + *******************************************************************************/ + public GroupBy withType(QFieldType type) + { + this.type = type; + return (this); + } + + + + /******************************************************************************* + ** Getter for fieldName + ** + *******************************************************************************/ + public String getFieldName() + { + return fieldName; + } + + + + /******************************************************************************* + ** Setter for fieldName + ** + *******************************************************************************/ + public void setFieldName(String fieldName) + { + this.fieldName = fieldName; + } + + + + /******************************************************************************* + ** Fluent setter for fieldName + ** + *******************************************************************************/ + public GroupBy withFieldName(String fieldName) + { + this.fieldName = fieldName; + return (this); + } + + + + /******************************************************************************* + ** Getter for formatString + ** + *******************************************************************************/ + public String getFormatString() + { + return formatString; + } + + + + /******************************************************************************* + ** Setter for formatString + ** + *******************************************************************************/ + public void setFormatString(String formatString) + { + this.formatString = formatString; + } + + + + /******************************************************************************* + ** Fluent setter for formatString + ** + *******************************************************************************/ + public GroupBy withFormatString(String formatString) + { + this.formatString = formatString; + return (this); + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/QFilterOrderByGroupBy.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/QFilterOrderByGroupBy.java new file mode 100644 index 00000000..7d9a1042 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/aggregate/QFilterOrderByGroupBy.java @@ -0,0 +1,123 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. 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.model.actions.tables.aggregate; + + +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; + + +/******************************************************************************* + ** Bean representing an element of a query order-by clause - ordering by a + ** group by + ** + *******************************************************************************/ +public class QFilterOrderByGroupBy extends QFilterOrderBy implements Cloneable +{ + private GroupBy groupBy; + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public QFilterOrderByGroupBy clone() + { + return (QFilterOrderByGroupBy) super.clone(); + } + + + + /******************************************************************************* + ** Default no-arg constructor + *******************************************************************************/ + public QFilterOrderByGroupBy() + { + + } + + + + /******************************************************************************* + ** Constructor that sets groupBy, but leaves default for isAscending (true) + *******************************************************************************/ + public QFilterOrderByGroupBy(GroupBy groupBy) + { + this.groupBy = groupBy; + } + + + + /******************************************************************************* + ** Constructor that takes groupBy and isAscending. + *******************************************************************************/ + public QFilterOrderByGroupBy(GroupBy groupBy, boolean isAscending) + { + this.groupBy = groupBy; + setIsAscending(isAscending); + } + + + + /******************************************************************************* + ** Getter for groupBy + ** + *******************************************************************************/ + public GroupBy getGroupBy() + { + return groupBy; + } + + + + /******************************************************************************* + ** Setter for groupBy + ** + *******************************************************************************/ + public void setGroupBy(GroupBy groupBy) + { + this.groupBy = groupBy; + } + + + + /******************************************************************************* + ** Fluent setter for groupBy + ** + *******************************************************************************/ + public QFilterOrderByGroupBy withGroupBy(GroupBy groupBy) + { + this.groupBy = groupBy; + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public String toString() + { + return (groupBy + " " + (getIsAscending() ? "ASC" : "DESC")); + } +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChartData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChartData.java index 6847dc05..d2ffc89c 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChartData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChartData.java @@ -41,9 +41,21 @@ public class ChartData implements QWidget } */ - private String title; - private String description; - private Data chartData; + private String title; + private String description; + private List colors; + private Data chartData; + private boolean isCurrency = false; + private int height; + + + + /******************************************************************************* + ** + *******************************************************************************/ + public ChartData() + { + } @@ -176,6 +188,74 @@ public class ChartData implements QWidget + /******************************************************************************* + ** Getter for isCurrency + ** + *******************************************************************************/ + public boolean getIsCurrency() + { + return isCurrency; + } + + + + /******************************************************************************* + ** Setter for isCurrency + ** + *******************************************************************************/ + public void setIsCurrency(boolean isCurrency) + { + this.isCurrency = isCurrency; + } + + + + /******************************************************************************* + ** Fluent setter for isCurrency + ** + *******************************************************************************/ + public ChartData withIsCurrency(boolean isCurrency) + { + this.isCurrency = isCurrency; + return (this); + } + + + + /******************************************************************************* + ** Getter for height + ** + *******************************************************************************/ + public int getHeight() + { + return height; + } + + + + /******************************************************************************* + ** Setter for height + ** + *******************************************************************************/ + public void setHeight(int height) + { + this.height = height; + } + + + + /******************************************************************************* + ** Fluent setter for height + ** + *******************************************************************************/ + public ChartData withHeight(int height) + { + this.height = height; + return (this); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -224,9 +304,13 @@ public class ChartData implements QWidget ** Getter for datasets ** *******************************************************************************/ - public Dataset getDataset() + public List getDatasets() { - return dataset; + if(dataset != null) + { + return List.of(dataset); + } + return List.of(); } @@ -260,6 +344,7 @@ public class ChartData implements QWidget public static class Dataset { private String label; + private String color; private List data; @@ -286,6 +371,40 @@ public class ChartData implements QWidget + /******************************************************************************* + ** Getter for color + ** + *******************************************************************************/ + public String getColor() + { + return color; + } + + + + /******************************************************************************* + ** Setter for color + ** + *******************************************************************************/ + public void setColor(String color) + { + this.color = color; + } + + + + /******************************************************************************* + ** Fluent setter for color + ** + *******************************************************************************/ + public Dataset withColor(String color) + { + this.color = color; + return (this); + } + + + /******************************************************************************* ** Fluent setter for label ** diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/LineChartData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/LineChartData.java index e4e6a234..df040124 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/LineChartData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/LineChartData.java @@ -44,8 +44,9 @@ public class LineChartData implements QWidget }; */ - private String title; - private Data chartData; + private String title; + private Data chartData; + private boolean isYAxisCurrency = false; @@ -151,6 +152,40 @@ public class LineChartData implements QWidget + /******************************************************************************* + ** Getter for isYAxisCurrency + ** + *******************************************************************************/ + public boolean getIsYAxisCurrency() + { + return isYAxisCurrency; + } + + + + /******************************************************************************* + ** Setter for isYAxisCurrency + ** + *******************************************************************************/ + public void setIsYAxisCurrency(boolean isYAxisCurrency) + { + this.isYAxisCurrency = isYAxisCurrency; + } + + + + /******************************************************************************* + ** Fluent setter for isYAxisCurrency + ** + *******************************************************************************/ + public LineChartData withIsYAxisCurrency(boolean isYAxisCurrency) + { + this.isYAxisCurrency = isYAxisCurrency; + return (this); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/StatisticsData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/StatisticsData.java index 5e62e0d0..a2f0d2cf 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/StatisticsData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/StatisticsData.java @@ -38,17 +38,18 @@ public class StatisticsData implements QWidget } */ - private String title; - private int count; - private Number percentageAmount; - private String percentageLabel; + private String title; + private Number count; + private Number percentageAmount; + private String percentageLabel; + private boolean isCurrency = false; /******************************************************************************* ** *******************************************************************************/ - public StatisticsData(String title, int count, Number percentageAmount, String percentageLabel) + public StatisticsData(String title, Number count, Number percentageAmount, String percentageLabel) { this.title = title; this.count = count; @@ -107,7 +108,7 @@ public class StatisticsData implements QWidget ** Getter for count ** *******************************************************************************/ - public int getCount() + public Number getCount() { return count; } @@ -118,7 +119,7 @@ public class StatisticsData implements QWidget ** Setter for count ** *******************************************************************************/ - public void setCount(int count) + public void setCount(Number count) { this.count = count; } @@ -129,7 +130,7 @@ public class StatisticsData implements QWidget ** Fluent setter for count ** *******************************************************************************/ - public StatisticsData withCount(int count) + public StatisticsData withCount(Number count) { this.count = count; return (this); @@ -203,4 +204,38 @@ public class StatisticsData implements QWidget return (this); } + + + /******************************************************************************* + ** Getter for isCurrency + ** + *******************************************************************************/ + public boolean getIsCurrency() + { + return isCurrency; + } + + + + /******************************************************************************* + ** Setter for isCurrency + ** + *******************************************************************************/ + public void setIsCurrency(boolean isCurrency) + { + this.isCurrency = isCurrency; + } + + + + /******************************************************************************* + ** Fluent setter for isCurrency + ** + *******************************************************************************/ + public StatisticsData withIsCurrency(boolean isCurrency) + { + this.isCurrency = isCurrency; + return (this); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java index f39cb05d..22baab2d 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java @@ -27,9 +27,11 @@ package com.kingsrook.qqq.backend.core.model.dashboard.widgets; *******************************************************************************/ public enum WidgetType { + BAR_CHART("barChart"), CHART("chart"), CHILD_RECORD_LIST("childRecordList"), GENERIC("generic"), + HORIZONTAL_BAR_CHART("horizontalBarChart"), HTML("html"), LINE_CHART("lineChart"), LOCATION("location"), diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/ParentWidgetMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/ParentWidgetMetaData.java index afca25c4..471261d3 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/ParentWidgetMetaData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/ParentWidgetMetaData.java @@ -31,10 +31,11 @@ import java.util.List; *******************************************************************************/ public class ParentWidgetMetaData extends QWidgetMetaData implements QWidgetMetaDataInterface { - private String title; - private List possibleValueNameList; - private List childWidgetNameList; - private List childProcessNameList; + private String title; + private List possibleValueNameList; + private List childWidgetNameList; + private List childProcessNameList; + private List dropdowns; @@ -172,4 +173,154 @@ public class ParentWidgetMetaData extends QWidgetMetaData implements QWidgetMeta return (this); } + + + /******************************************************************************* + ** Getter for dropdowns + ** + *******************************************************************************/ + public List getDropdowns() + { + return dropdowns; + } + + + + /******************************************************************************* + ** Setter for dropdowns + ** + *******************************************************************************/ + public void setDropdowns(List dropdowns) + { + this.dropdowns = dropdowns; + } + + + + /******************************************************************************* + ** Fluent setter for dropdowns + ** + *******************************************************************************/ + public ParentWidgetMetaData withDropdowns(List dropdowns) + { + this.dropdowns = dropdowns; + return (this); + } + + + + /******************************************************************************* + ** inner class for specifying details about dropdown fields on a parent widget + ** + *******************************************************************************/ + public static class DropdownData + { + private String possibleValueSourceName; + private String foreignKeyFieldName; + private String label; + + + + /******************************************************************************* + ** Getter for possibleValueSourceName + ** + *******************************************************************************/ + public String getPossibleValueSourceName() + { + return possibleValueSourceName; + } + + + + /******************************************************************************* + ** Setter for possibleValueSourceName + ** + *******************************************************************************/ + public void setPossibleValueSourceName(String possibleValueSourceName) + { + this.possibleValueSourceName = possibleValueSourceName; + } + + + + /******************************************************************************* + ** Fluent setter for possibleValueSourceName + ** + *******************************************************************************/ + public DropdownData withPossibleValueSourceName(String possibleValueSourceName) + { + this.possibleValueSourceName = possibleValueSourceName; + return (this); + } + + + + /******************************************************************************* + ** Getter for foreignKeyFieldName + ** + *******************************************************************************/ + public String getForeignKeyFieldName() + { + return foreignKeyFieldName; + } + + + + /******************************************************************************* + ** Setter for foreignKeyFieldName + ** + *******************************************************************************/ + public void setForeignKeyFieldName(String foreignKeyFieldName) + { + this.foreignKeyFieldName = foreignKeyFieldName; + } + + + + /******************************************************************************* + ** Fluent setter for foreignKeyFieldName + ** + *******************************************************************************/ + public DropdownData withForeignKeyFieldName(String foreignKeyFieldName) + { + this.foreignKeyFieldName = foreignKeyFieldName; + return (this); + } + + + + /******************************************************************************* + ** Getter for label + ** + *******************************************************************************/ + public String getLabel() + { + return label; + } + + + + /******************************************************************************* + ** Setter for label + ** + *******************************************************************************/ + public void setLabel(String label) + { + this.label = label; + } + + + + /******************************************************************************* + ** Fluent setter for label + ** + *******************************************************************************/ + public DropdownData withLabel(String label) + { + this.label = label; + return (this); + } + + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/utils/GeneralProcessUtils.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/utils/GeneralProcessUtils.java index 9aa75a73..bf83def8 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/utils/GeneralProcessUtils.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/utils/GeneralProcessUtils.java @@ -197,6 +197,23 @@ public class GeneralProcessUtils + /******************************************************************************* + ** Query to get one entity by a unique key value. That field can be the primary + ** key, or any other field on the table. Note, if multiple rows do match the value, + ** only 1 (determined in an unspecified way) is returned. + *******************************************************************************/ + public static Optional getEntityByField(AbstractActionInput parentActionInput, String tableName, String fieldName, Serializable fieldValue, Class entityClass) throws QException + { + Optional optionalQRecord = getRecordByField(parentActionInput, tableName, fieldName, fieldValue); + if(optionalQRecord.isPresent()) + { + return (Optional.of(QRecordEntity.fromQRecord(entityClass, optionalQRecord.get()))); + } + return (Optional.empty()); + } + + + /******************************************************************************* ** Query to get one record by a unique key value. *******************************************************************************/ diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java index bbe66c2d..92787f41 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java @@ -37,7 +37,9 @@ import com.kingsrook.qqq.backend.core.actions.interfaces.QActionInterface; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.Aggregate; +import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate; +import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByGroupBy; import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; @@ -123,7 +125,7 @@ public abstract class AbstractRDBMSAction implements QActionInterface *******************************************************************************/ protected Serializable scrubValue(QFieldMetaData field, Serializable value, boolean isInsert) { - if("".equals(value)) + if("" .equals(value)) { QFieldType type = field.getType(); if(type.equals(QFieldType.INTEGER) || type.equals(QFieldType.DECIMAL) || type.equals(QFieldType.DATE) || type.equals(QFieldType.DATE_TIME) || type.equals(QFieldType.BOOLEAN)) @@ -513,9 +515,9 @@ public abstract class AbstractRDBMSAction implements QActionInterface /******************************************************************************* ** *******************************************************************************/ - protected Serializable getFieldValueFromResultSet(QFieldMetaData qFieldMetaData, ResultSet resultSet, int i) throws SQLException + protected Serializable getFieldValueFromResultSet(QFieldType type, ResultSet resultSet, int i) throws SQLException { - switch(qFieldMetaData.getType()) + switch(type) { case STRING: case TEXT: @@ -551,10 +553,19 @@ public abstract class AbstractRDBMSAction implements QActionInterface } default: { - throw new IllegalStateException("Unexpected field type: " + qFieldMetaData.getType()); + throw new IllegalStateException("Unexpected field type: " + type); } } + } + + + /******************************************************************************* + ** + *******************************************************************************/ + protected Serializable getFieldValueFromResultSet(QFieldMetaData qFieldMetaData, ResultSet resultSet, int i) throws SQLException + { + return (getFieldValueFromResultSet(qFieldMetaData.getType(), resultSet, i)); } @@ -575,6 +586,10 @@ public abstract class AbstractRDBMSAction implements QActionInterface String clause = (aggregate.getOperator() + "(" + escapeIdentifier(getColumnName(table.getField(aggregate.getFieldName()))) + ")"); clauses.add(clause + " " + ascOrDesc); } + else if(orderBy instanceof QFilterOrderByGroupBy orderByGroupBy) + { + clauses.add(getSingleGroupByClause(orderByGroupBy.getGroupBy(), joinsContext) + " " + ascOrDesc); + } else { JoinsContext.FieldAndTableNameOrAlias otherFieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(orderBy.getFieldName()); @@ -586,4 +601,23 @@ public abstract class AbstractRDBMSAction implements QActionInterface } return (String.join(", ", clauses)); } + + + + /******************************************************************************* + ** + *******************************************************************************/ + protected String getSingleGroupByClause(GroupBy groupBy, JoinsContext joinsContext) + { + JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupBy.getFieldName()); + String fullFieldName = escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(fieldAndTableNameOrAlias.field())); + if(groupBy.getFormatString() == null) + { + return (fullFieldName); + } + else + { + return (String.format(groupBy.getFormatString(), fullFieldName)); + } + } } diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSAggregateAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSAggregateAction.java index 912653e3..d35557b9 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSAggregateAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSAggregateAction.java @@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateIn import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOperator; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateResult; +import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy; import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; @@ -78,7 +79,7 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega sql += " WHERE " + makeWhereClause(aggregateInput.getInstance(), table, joinsContext, filter, params); } - if(CollectionUtils.nullSafeHasContents(aggregateInput.getGroupByFieldNames())) + if(CollectionUtils.nullSafeHasContents(aggregateInput.getGroupBys())) { sql += " GROUP BY " + makeGroupByClause(aggregateInput, joinsContext); } @@ -105,11 +106,10 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega results.add(result); int selectionIndex = 1; - for(String groupByFieldName : CollectionUtils.nonNullList(aggregateInput.getGroupByFieldNames())) + for(GroupBy groupBy : CollectionUtils.nonNullList(aggregateInput.getGroupBys())) { - JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupByFieldName); - Serializable value = getFieldValueFromResultSet(fieldAndTableNameOrAlias.field(), resultSet, selectionIndex++); - result.withGroupByValue(groupByFieldName, value); + Serializable value = getFieldValueFromResultSet(groupBy.getType(), resultSet, selectionIndex++); + result.withGroupByValue(groupBy, value); } for(Aggregate aggregate : aggregateInput.getAggregates()) @@ -148,10 +148,9 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega { List rs = new ArrayList<>(); - for(String groupByFieldName : CollectionUtils.nonNullList(aggregateInput.getGroupByFieldNames())) + for(GroupBy groupBy : CollectionUtils.nonNullList(aggregateInput.getGroupBys())) { - JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupByFieldName); - rs.add(escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(fieldAndTableNameOrAlias.field()))); + rs.add(getSingleGroupByClause(groupBy, joinsContext)); } for(Aggregate aggregate : aggregateInput.getAggregates()) @@ -170,10 +169,9 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega private String makeGroupByClause(AggregateInput aggregateInput, JoinsContext joinsContext) { List columns = new ArrayList<>(); - for(String groupByFieldName : aggregateInput.getGroupByFieldNames()) + for(GroupBy groupBy : CollectionUtils.nonNullList(aggregateInput.getGroupBys())) { - JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(groupByFieldName); - columns.add(escapeIdentifier(fieldAndTableNameOrAlias.tableNameOrAlias()) + "." + escapeIdentifier(getColumnName(fieldAndTableNameOrAlias.field()))); + columns.add(getSingleGroupByClause(groupBy, joinsContext)); } return (StringUtils.join(",", columns)); diff --git a/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSAggregateActionTest.java b/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSAggregateActionTest.java index 6af0fb55..a1261582 100644 --- a/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSAggregateActionTest.java +++ b/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSAggregateActionTest.java @@ -32,6 +32,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateIn import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOperator; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateResult; +import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy; import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.QFilterOrderByAggregate; import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator; @@ -40,6 +41,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin; import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.session.QSession; import com.kingsrook.qqq.backend.module.rdbms.TestUtils; import org.junit.jupiter.api.Assertions; @@ -145,7 +147,7 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest aggregateInput.withAggregate(countOfId); aggregateInput.withAggregate(sumOfDaysWorked); - aggregateInput.withGroupByFieldName("lastName"); + aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "lastName", null)); aggregateInput.setFilter(new QQueryFilter().withOrderBy(new QFilterOrderBy("lastName"))); AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput); @@ -182,8 +184,8 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest aggregateInput.withAggregate(countOfId); aggregateInput.withAggregate(sumOfDaysWorked); - aggregateInput.withGroupByFieldName("lastName"); - aggregateInput.withGroupByFieldName("firstName"); + aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "lastName", null)); + aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "firstName", null)); aggregateInput.setFilter(new QQueryFilter() .withOrderBy(new QFilterOrderBy("lastName")) @@ -238,7 +240,7 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest aggregateInput.withAggregate(countOfId); // note - don't query this value - just order by it!! aggregateInput.withAggregate(sumOfDaysWorked); - aggregateInput.withGroupByFieldName("lastName"); + aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "lastName", null)); aggregateInput.setFilter(new QQueryFilter().withOrderBy(new QFilterOrderByAggregate(sumOfDaysWorked, false))); @@ -290,7 +292,7 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest ///////////////////////////////////////////////////////////////////////////////////////// // but re-run w/ a group-by -- then, if no rows are found, there are 0 result objects. // ///////////////////////////////////////////////////////////////////////////////////////// - aggregateInput.withGroupByFieldName("lastName"); + aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, "lastName", null)); aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput); assertTrue(aggregateOutput.getResults().isEmpty()); } @@ -328,7 +330,7 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest aggregateInput.setSession(new QSession()); aggregateInput.setTableName(TestUtils.TABLE_NAME_ORDER); aggregateInput.withAggregate(sumOfQuantity); - aggregateInput.withGroupByFieldName(TestUtils.TABLE_NAME_ORDER_LINE + ".sku"); + aggregateInput.withGroupBy(new GroupBy(QFieldType.STRING, TestUtils.TABLE_NAME_ORDER_LINE + ".sku", null)); aggregateInput.withQueryJoin(new QueryJoin(TestUtils.TABLE_NAME_ORDER, TestUtils.TABLE_NAME_ORDER_LINE)); AggregateOutput aggregateOutput = new RDBMSAggregateAction().execute(aggregateInput); @@ -392,4 +394,4 @@ public class RDBMSAggregateActionTest extends RDBMSActionTest return aggregateInput; } -} \ No newline at end of file +}