mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
2nd iteration on no-code dashboards. add conditional filters, timeframes, more utils, calcualtions
This commit is contained in:
@ -234,7 +234,8 @@ public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static String aHrefTableFilterNoOfRecords(RenderWidgetInput input, String tableName, QQueryFilter filter, Integer noOfRecords, String singularLabel, String pluralLabel) throws QException
|
public static String aHrefTableFilterNoOfRecords(RenderWidgetInput input, String tableName, QQueryFilter filter, Integer noOfRecords, String singularLabel, String pluralLabel) throws QException
|
||||||
{
|
{
|
||||||
String displayText = QValueFormatter.formatValue(DisplayFormat.COMMAS, noOfRecords) + " " + StringUtils.plural(noOfRecords, singularLabel, pluralLabel);
|
String plural = StringUtils.plural(noOfRecords, singularLabel, pluralLabel);
|
||||||
|
String displayText = QValueFormatter.formatValue(DisplayFormat.COMMAS, noOfRecords) + (StringUtils.hasContent(plural) ? (" " + plural) : "");
|
||||||
String tablePath = QContext.getQInstance().getTablePath(tableName);
|
String tablePath = QContext.getQInstance().getTablePath(tableName);
|
||||||
if(tablePath == null)
|
if(tablePath == null)
|
||||||
{
|
{
|
||||||
|
@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode.AbstractWi
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode.AbstractWidgetValueSource;
|
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode.AbstractWidgetValueSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode.QNoCodeWidgetMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode.QNoCodeWidgetMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.utils.BackendQueryFilterUtils;
|
import com.kingsrook.qqq.backend.core.modules.backend.implementations.utils.BackendQueryFilterUtils;
|
||||||
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -58,21 +59,29 @@ public class NoCodeWidgetRenderer extends AbstractWidgetRenderer
|
|||||||
// build context by evaluating all values //
|
// build context by evaluating all values //
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
Map<String, Object> context = new HashMap<>();
|
Map<String, Object> context = new HashMap<>();
|
||||||
|
context.put("utils", new NoCodeWidgetVelocityUtils(context, input));
|
||||||
|
context.put("input", input);
|
||||||
|
|
||||||
|
for(Map.Entry<String, String> entry : input.getQueryParams().entrySet())
|
||||||
|
{
|
||||||
|
context.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
for(AbstractWidgetValueSource valueSource : widgetMetaData.getValues())
|
for(AbstractWidgetValueSource valueSource : widgetMetaData.getValues())
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
LOG.trace("Computing: " + valueSource.getType() + " named " + valueSource.getName() + "...");
|
LOG.trace("Computing: " + valueSource.getType() + " named " + valueSource.getName() + "...");
|
||||||
Object value = valueSource.evaluate(context);
|
Object value = valueSource.evaluate(context, input);
|
||||||
LOG.trace("Computed: " + valueSource.getName() + " = " + value);
|
LOG.trace("Computed: " + valueSource.getName() + " = " + value);
|
||||||
context.put(valueSource.getName(), value);
|
context.put(valueSource.getName(), value);
|
||||||
|
|
||||||
context.put(valueSource.getName() + ".source", valueSource);
|
context.put(valueSource.getName() + ".source", valueSource);
|
||||||
}
|
}
|
||||||
|
catch(Exception e)
|
||||||
/////////////////////////////////////////////
|
{
|
||||||
// set default utils object in context too //
|
LOG.warn("Error evaluating widget value source", e, logPair("widgetName", input.getWidgetMetaData().getName()), logPair("valueSourceName", valueSource.getName()));
|
||||||
/////////////////////////////////////////////
|
}
|
||||||
context.put("utils", new NoCodeWidgetVelocityUtils(context));
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// build content by evaluating all outputs //
|
// build content by evaluating all outputs //
|
||||||
|
@ -22,11 +22,18 @@
|
|||||||
package com.kingsrook.qqq.backend.core.actions.dashboard.widgets;
|
package com.kingsrook.qqq.backend.core.actions.dashboard.widgets;
|
||||||
|
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.backend.core.actions.dashboard.AbstractHTMLWidgetRenderer;
|
import com.kingsrook.qqq.backend.core.actions.dashboard.AbstractHTMLWidgetRenderer;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode.WidgetCount;
|
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode.WidgetCount;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
@ -39,11 +46,8 @@ public class NoCodeWidgetVelocityUtils
|
|||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(NoCodeWidgetVelocityUtils.class);
|
private static final QLogger LOG = QLogger.getLogger(NoCodeWidgetVelocityUtils.class);
|
||||||
|
|
||||||
|
private Map<String, Object> context;
|
||||||
/*******************************************************************************
|
private RenderWidgetInput input;
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
private final Map<String, Object> context;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -51,9 +55,10 @@ public class NoCodeWidgetVelocityUtils
|
|||||||
** Constructor
|
** Constructor
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public NoCodeWidgetVelocityUtils(Map<String, Object> context)
|
public NoCodeWidgetVelocityUtils(Map<String, Object> context, RenderWidgetInput input)
|
||||||
{
|
{
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.input = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -61,7 +66,7 @@ public class NoCodeWidgetVelocityUtils
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public final String errorIcon()
|
public String errorIcon()
|
||||||
{
|
{
|
||||||
return ("""
|
return ("""
|
||||||
<span class="material-icons-round notranslate MuiIcon-root MuiIcon-fontSizeInherit" style="color: red; position: relative; top: 6px;" aria-hidden="true">error_outline</span>
|
<span class="material-icons-round notranslate MuiIcon-root MuiIcon-fontSizeInherit" style="color: red; position: relative; top: 6px;" aria-hidden="true">error_outline</span>
|
||||||
@ -73,7 +78,7 @@ public class NoCodeWidgetVelocityUtils
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public final String checkIcon()
|
public String checkIcon()
|
||||||
{
|
{
|
||||||
return ("""
|
return ("""
|
||||||
<span class="material-icons-round notranslate MuiIcon-root MuiIcon-fontSizeInherit" style="color: green; position: relative; top: 6px;" aria-hidden="true">check</span>
|
<span class="material-icons-round notranslate MuiIcon-root MuiIcon-fontSizeInherit" style="color: green; position: relative; top: 6px;" aria-hidden="true">check</span>
|
||||||
@ -82,6 +87,42 @@ public class NoCodeWidgetVelocityUtils
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String spanColorGreen()
|
||||||
|
{
|
||||||
|
return ("""
|
||||||
|
<span style="color: green;">
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String spanColorOrange()
|
||||||
|
{
|
||||||
|
return ("""
|
||||||
|
<span style="color: orange;">
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String spanColorRed()
|
||||||
|
{
|
||||||
|
return ("""
|
||||||
|
<span style="color: red;">
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -92,6 +133,110 @@ public class NoCodeWidgetVelocityUtils
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String formatDateTime(Instant i)
|
||||||
|
{
|
||||||
|
return QValueFormatter.formatDateTimeWithZone(i.atZone(ZoneId.of(QContext.getQInstance().getDefaultTimeZoneId())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String formatSecondsAsDuration(Integer seconds)
|
||||||
|
{
|
||||||
|
StringBuilder rs = new StringBuilder();
|
||||||
|
|
||||||
|
if(seconds == null)
|
||||||
|
{
|
||||||
|
return ("");
|
||||||
|
}
|
||||||
|
|
||||||
|
int secondsPerDay = 24 * 60 * 60;
|
||||||
|
if(seconds >= secondsPerDay)
|
||||||
|
{
|
||||||
|
int days = seconds / (secondsPerDay);
|
||||||
|
seconds = seconds % secondsPerDay;
|
||||||
|
rs.append(days).append(StringUtils.plural(days, " day", " days")).append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
int secondsPerHour = 60 * 60;
|
||||||
|
if(seconds >= secondsPerHour)
|
||||||
|
{
|
||||||
|
int hours = seconds / (secondsPerHour);
|
||||||
|
seconds = seconds % secondsPerHour;
|
||||||
|
rs.append(hours).append(StringUtils.plural(hours, " hour", " hours")).append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
int secondsPerMinute = 60;
|
||||||
|
if(seconds >= secondsPerMinute)
|
||||||
|
{
|
||||||
|
int minutes = seconds / (secondsPerMinute);
|
||||||
|
seconds = seconds % secondsPerMinute;
|
||||||
|
rs.append(minutes).append(StringUtils.plural(minutes, " minute", " minutes")).append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(seconds > 0 || rs.length() == 0)
|
||||||
|
{
|
||||||
|
rs.append(seconds).append(StringUtils.plural(seconds, " second", " seconds")).append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rs.length() > 0)
|
||||||
|
{
|
||||||
|
rs.deleteCharAt(rs.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (rs.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String formatSecondsAsRoundedDuration(Integer seconds)
|
||||||
|
{
|
||||||
|
StringBuilder rs = new StringBuilder();
|
||||||
|
|
||||||
|
if(seconds == null)
|
||||||
|
{
|
||||||
|
return ("");
|
||||||
|
}
|
||||||
|
|
||||||
|
int secondsPerDay = 24 * 60 * 60;
|
||||||
|
if(seconds >= secondsPerDay)
|
||||||
|
{
|
||||||
|
int days = seconds / (secondsPerDay);
|
||||||
|
return (days + StringUtils.plural(days, " day", " days"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int secondsPerHour = 60 * 60;
|
||||||
|
if(seconds >= secondsPerHour)
|
||||||
|
{
|
||||||
|
int hours = seconds / (secondsPerHour);
|
||||||
|
return (hours + StringUtils.plural(hours, " hour", " hours"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int secondsPerMinute = 60;
|
||||||
|
if(seconds >= secondsPerMinute)
|
||||||
|
{
|
||||||
|
int minutes = seconds / (secondsPerMinute);
|
||||||
|
return (minutes + StringUtils.plural(minutes, " minute", " minutes"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(seconds > 0 || rs.length() == 0)
|
||||||
|
{
|
||||||
|
return (seconds + StringUtils.plural(seconds, " second", " seconds"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -101,7 +246,7 @@ public class NoCodeWidgetVelocityUtils
|
|||||||
{
|
{
|
||||||
WidgetCount widgetCount = (WidgetCount) context.get(countVariableName + ".source");
|
WidgetCount widgetCount = (WidgetCount) context.get(countVariableName + ".source");
|
||||||
Integer count = ValueUtils.getValueAsInteger(context.get(countVariableName));
|
Integer count = ValueUtils.getValueAsInteger(context.get(countVariableName));
|
||||||
QQueryFilter filter = widgetCount.getFilter();
|
QQueryFilter filter = widgetCount.getEffectiveFilter(input);
|
||||||
return (AbstractHTMLWidgetRenderer.aHrefTableFilterNoOfRecords(null, widgetCount.getTableName(), filter, count, singular, plural));
|
return (AbstractHTMLWidgetRenderer.aHrefTableFilterNoOfRecords(null, widgetCount.getTableName(), filter, count, singular, plural));
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
@ -110,4 +255,14 @@ public class NoCodeWidgetVelocityUtils
|
|||||||
return ("");
|
return ("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String round(BigDecimal input, int digits)
|
||||||
|
{
|
||||||
|
return String.valueOf(input.setScale(digits, RoundingMode.HALF_UP));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,8 +47,8 @@ public class QValueFormatter
|
|||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(QValueFormatter.class);
|
private static final QLogger LOG = QLogger.getLogger(QValueFormatter.class);
|
||||||
|
|
||||||
private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm a");
|
private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm a");
|
||||||
private static DateTimeFormatter dateTimeWithZoneFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm a z");
|
private static DateTimeFormatter dateTimeWithZoneFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm a z");
|
||||||
private static DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
private static DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
private static DateTimeFormatter localTimeFormatter = DateTimeFormatter.ofPattern("h:mm a");
|
private static DateTimeFormatter localTimeFormatter = DateTimeFormatter.ofPattern("h:mm a");
|
||||||
|
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public abstract class AbstractConditionalFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public abstract boolean testCondition(RenderWidgetInput renderWidgetInput);
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public abstract QQueryFilter getFilter(RenderWidgetInput renderWidgetInput);
|
||||||
|
|
||||||
|
}
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -39,7 +40,7 @@ public abstract class AbstractWidgetValueSource
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public abstract Object evaluate(Map<String, Object> context) throws QException;
|
public abstract Object evaluate(Map<String, Object> context, RenderWidgetInput input) throws QException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
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.widgets.RenderWidgetInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public abstract class AbstractWidgetValueSourceWithFilter extends AbstractWidgetValueSource
|
||||||
|
{
|
||||||
|
protected String tableName;
|
||||||
|
protected QQueryFilter filter;
|
||||||
|
|
||||||
|
protected List<AbstractConditionalFilter> conditionalFilterList;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQueryFilter getEffectiveFilter(RenderWidgetInput input)
|
||||||
|
{
|
||||||
|
QQueryFilter effectiveFilter;
|
||||||
|
if(filter == null)
|
||||||
|
{
|
||||||
|
effectiveFilter = new QQueryFilter();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
effectiveFilter = filter.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(AbstractConditionalFilter conditionalFilter : CollectionUtils.nonNullList(conditionalFilterList))
|
||||||
|
{
|
||||||
|
if(conditionalFilter.testCondition(input))
|
||||||
|
{
|
||||||
|
QQueryFilter additionalFilter = conditionalFilter.getFilter(input);
|
||||||
|
for(QFilterCriteria criterion : additionalFilter.getCriteria())
|
||||||
|
{
|
||||||
|
effectiveFilter.addCriteria(criterion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (effectiveFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for tableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getTableName()
|
||||||
|
{
|
||||||
|
return (this.tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for tableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for tableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public AbstractWidgetValueSourceWithFilter withTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for filter
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQueryFilter getFilter()
|
||||||
|
{
|
||||||
|
return (this.filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for filter
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFilter(QQueryFilter filter)
|
||||||
|
{
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for filter
|
||||||
|
*******************************************************************************/
|
||||||
|
public AbstractWidgetValueSourceWithFilter withFilter(QQueryFilter filter)
|
||||||
|
{
|
||||||
|
this.filter = filter;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for conditionalFilterList
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<AbstractConditionalFilter> getConditionalFilterList()
|
||||||
|
{
|
||||||
|
return (this.conditionalFilterList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for conditionalFilterList
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setConditionalFilterList(List<AbstractConditionalFilter> conditionalFilterList)
|
||||||
|
{
|
||||||
|
this.conditionalFilterList = conditionalFilterList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for conditionalFilterList
|
||||||
|
*******************************************************************************/
|
||||||
|
public AbstractWidgetValueSourceWithFilter withConditionalFilterList(List<AbstractConditionalFilter> conditionalFilterList)
|
||||||
|
{
|
||||||
|
this.conditionalFilterList = conditionalFilterList;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -34,8 +34,12 @@ public class HtmlWrapper
|
|||||||
private String suffix;
|
private String suffix;
|
||||||
|
|
||||||
public static final HtmlWrapper SUBHEADER = new HtmlWrapper("<h4>", "</h4>");
|
public static final HtmlWrapper SUBHEADER = new HtmlWrapper("<h4>", "</h4>");
|
||||||
|
public static final HtmlWrapper BIG_CENTERED = new HtmlWrapper("<div style='font-size: 2rem; font-weight: 400; line-height: 1.625; text-align: center; padding-bottom: 8px;'>", "</div>");
|
||||||
public static final HtmlWrapper INDENT_1 = new HtmlWrapper("<div style='padding-left: 1rem;'>", "</div>");
|
public static final HtmlWrapper INDENT_1 = new HtmlWrapper("<div style='padding-left: 1rem;'>", "</div>");
|
||||||
public static final HtmlWrapper INDENT_2 = new HtmlWrapper("<div style='padding-left: 2rem;'>", "</div>");
|
public static final HtmlWrapper INDENT_2 = new HtmlWrapper("<div style='padding-left: 2rem;'>", "</div>");
|
||||||
|
public static final HtmlWrapper RULE_ABOVE = new HtmlWrapper("""
|
||||||
|
<hr style="opacity: 0.25; height: 0.0625rem; border-width: 0; margin-bottom: 1rem; background-image: linear-gradient(to right, rgba(52, 71, 103, 0), rgba(52, 71, 103, 0.4), rgba(52, 71, 103, 0));" />
|
||||||
|
""", "");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
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.widgets.RenderWidgetInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.collections.MutableList;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class IfInputVariableExistsFilter extends AbstractConditionalFilter
|
||||||
|
{
|
||||||
|
private String inputVariableName;
|
||||||
|
private QQueryFilter filter;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public IfInputVariableExistsFilter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public IfInputVariableExistsFilter(String inputVariableName, QQueryFilter filter)
|
||||||
|
{
|
||||||
|
this.inputVariableName = inputVariableName;
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public boolean testCondition(RenderWidgetInput renderWidgetInput)
|
||||||
|
{
|
||||||
|
return (renderWidgetInput.getQueryParams().get(inputVariableName) != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public QQueryFilter getFilter(RenderWidgetInput renderWidgetInput)
|
||||||
|
{
|
||||||
|
for(QFilterCriteria criterion : CollectionUtils.nonNullList(filter.getCriteria()))
|
||||||
|
{
|
||||||
|
if(criterion.getValues() != null)
|
||||||
|
{
|
||||||
|
criterion.setValues(new MutableList<>(criterion.getValues()));
|
||||||
|
for(int i = 0; i < criterion.getValues().size(); i++)
|
||||||
|
{
|
||||||
|
Serializable value = criterion.getValues().get(i);
|
||||||
|
if(value instanceof String valueString && valueString.equals("${input." + inputVariableName + "}"))
|
||||||
|
{
|
||||||
|
criterion.getValues().set(i, renderWidgetInput.getQueryParams().get(inputVariableName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (filter);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.AggregateAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.Aggregate;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateInput;
|
||||||
|
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.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class WidgetAggregate extends AbstractWidgetValueSourceWithFilter
|
||||||
|
{
|
||||||
|
private Aggregate aggregate;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public WidgetAggregate()
|
||||||
|
{
|
||||||
|
setType(getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public Object evaluate(Map<String, Object> context, RenderWidgetInput input) throws QException
|
||||||
|
{
|
||||||
|
AggregateInput aggregateInput = new AggregateInput();
|
||||||
|
aggregateInput.setTableName(tableName);
|
||||||
|
aggregateInput.setAggregates(List.of(aggregate));
|
||||||
|
aggregateInput.setFilter(getEffectiveFilter(input));
|
||||||
|
|
||||||
|
AggregateOutput aggregateOutput = new AggregateAction().execute(aggregateInput);
|
||||||
|
List<AggregateResult> results = aggregateOutput.getResults();
|
||||||
|
if(results.isEmpty())
|
||||||
|
{
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AggregateResult aggregateResult = results.get(0);
|
||||||
|
return (aggregateResult.getAggregateValue(aggregate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for name
|
||||||
|
*******************************************************************************/
|
||||||
|
public WidgetAggregate withName(String name)
|
||||||
|
{
|
||||||
|
setName(name);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for tableName
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public WidgetAggregate withTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for filter
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public WidgetAggregate withFilter(QQueryFilter filter)
|
||||||
|
{
|
||||||
|
this.filter = filter;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for conditionalFilterList
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public WidgetAggregate withConditionalFilterList(List<AbstractConditionalFilter> conditionalFilterList)
|
||||||
|
{
|
||||||
|
this.conditionalFilterList = conditionalFilterList;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter to add a single conditionalFilter
|
||||||
|
*******************************************************************************/
|
||||||
|
public WidgetAggregate withConditionalFilter(AbstractConditionalFilter conditionalFilter)
|
||||||
|
{
|
||||||
|
if(this.conditionalFilterList == null)
|
||||||
|
{
|
||||||
|
this.conditionalFilterList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.conditionalFilterList.add(conditionalFilter);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for aggregate
|
||||||
|
*******************************************************************************/
|
||||||
|
public Aggregate getAggregate()
|
||||||
|
{
|
||||||
|
return (this.aggregate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for aggregate
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAggregate(Aggregate aggregate)
|
||||||
|
{
|
||||||
|
this.aggregate = aggregate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for aggregate
|
||||||
|
*******************************************************************************/
|
||||||
|
public WidgetAggregate withAggregate(Aggregate aggregate)
|
||||||
|
{
|
||||||
|
this.aggregate = aggregate;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,12 +22,15 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode;
|
package com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode;
|
||||||
|
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.MathContext;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -50,10 +53,20 @@ public class WidgetCalculation extends AbstractWidgetValueSource
|
|||||||
{
|
{
|
||||||
Integer sum = 0;
|
Integer sum = 0;
|
||||||
for(String valueName : valueNames)
|
for(String valueName : valueNames)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Integer addend = ValueUtils.getValueAsInteger(context.get(valueName));
|
Integer addend = ValueUtils.getValueAsInteger(context.get(valueName));
|
||||||
sum += addend;
|
sum += addend;
|
||||||
}
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// assume value to be null or 0, don't add it //
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
return (sum);
|
return (sum);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -62,6 +75,30 @@ public class WidgetCalculation extends AbstractWidgetValueSource
|
|||||||
Instant now = Instant.now();
|
Instant now = Instant.now();
|
||||||
Instant then = ValueUtils.getValueAsInstant(context.get(valueNames.get(0)));
|
Instant then = ValueUtils.getValueAsInstant(context.get(valueNames.get(0)));
|
||||||
return (then.until(now, ChronoUnit.MINUTES));
|
return (then.until(now, ChronoUnit.MINUTES));
|
||||||
|
}),
|
||||||
|
|
||||||
|
AGE_SECONDS((List<String> valueNames, Map<String, Object> context) ->
|
||||||
|
{
|
||||||
|
Instant now = Instant.now();
|
||||||
|
Instant then = ValueUtils.getValueAsInstant(context.get(valueNames.get(0)));
|
||||||
|
return (then.until(now, ChronoUnit.SECONDS));
|
||||||
|
}),
|
||||||
|
|
||||||
|
PERCENT_CHANGE((List<String> valueNames, Map<String, Object> context) ->
|
||||||
|
{
|
||||||
|
BigDecimal current = ValueUtils.getValueAsBigDecimal(context.get(valueNames.get(0)));
|
||||||
|
BigDecimal previous = ValueUtils.getValueAsBigDecimal(context.get(valueNames.get(1)));
|
||||||
|
|
||||||
|
///////////////////////////////////////////////
|
||||||
|
// 100 * ( (current - previous) / previous ) //
|
||||||
|
///////////////////////////////////////////////
|
||||||
|
BigDecimal difference = current.subtract(previous);
|
||||||
|
if(BigDecimal.ZERO.equals(previous))
|
||||||
|
{
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
BigDecimal quotient = difference.divide(previous, MathContext.DECIMAL32);
|
||||||
|
return new BigDecimal("100").multiply(quotient);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -105,7 +142,7 @@ public class WidgetCalculation extends AbstractWidgetValueSource
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public Object evaluate(Map<String, Object> context) throws QException
|
public Object evaluate(Map<String, Object> context, RenderWidgetInput input) throws QException
|
||||||
{
|
{
|
||||||
return (operator.execute(values, context));
|
return (operator.execute(values, context));
|
||||||
}
|
}
|
||||||
|
@ -22,22 +22,22 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode;
|
package com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class WidgetCount extends AbstractWidgetValueSource
|
public class WidgetCount extends AbstractWidgetValueSourceWithFilter
|
||||||
{
|
{
|
||||||
private String tableName;
|
|
||||||
private QQueryFilter filter;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -55,29 +55,18 @@ public class WidgetCount extends AbstractWidgetValueSource
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public Object evaluate(Map<String, Object> context) throws QException
|
public Object evaluate(Map<String, Object> context, RenderWidgetInput input) throws QException
|
||||||
{
|
{
|
||||||
// todo - look for params in the filter (fields or values)
|
|
||||||
// make sure to update it in supplementContext below too!!
|
|
||||||
CountInput countInput = new CountInput();
|
CountInput countInput = new CountInput();
|
||||||
countInput.setTableName(tableName);
|
countInput.setTableName(tableName);
|
||||||
countInput.setFilter(filter);
|
countInput.setFilter(getEffectiveFilter(input));
|
||||||
|
|
||||||
CountOutput countOutput = new CountAction().execute(countInput);
|
CountOutput countOutput = new CountAction().execute(countInput);
|
||||||
return (countOutput.getCount());
|
return (countOutput.getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public void supplementContext(Map<String, Object> context)
|
|
||||||
{
|
|
||||||
context.put(getName() + ".filter", filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for name
|
** Fluent setter for name
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -89,29 +78,10 @@ public class WidgetCount extends AbstractWidgetValueSource
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for tableName
|
|
||||||
*******************************************************************************/
|
|
||||||
public String getTableName()
|
|
||||||
{
|
|
||||||
return (this.tableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for tableName
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setTableName(String tableName)
|
|
||||||
{
|
|
||||||
this.tableName = tableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for tableName
|
** Fluent setter for tableName
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
public WidgetCount withTableName(String tableName)
|
public WidgetCount withTableName(String tableName)
|
||||||
{
|
{
|
||||||
this.tableName = tableName;
|
this.tableName = tableName;
|
||||||
@ -120,33 +90,41 @@ public class WidgetCount extends AbstractWidgetValueSource
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for filter
|
|
||||||
*******************************************************************************/
|
|
||||||
public QQueryFilter getFilter()
|
|
||||||
{
|
|
||||||
return (this.filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for filter
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setFilter(QQueryFilter filter)
|
|
||||||
{
|
|
||||||
this.filter = filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for filter
|
** Fluent setter for filter
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
public WidgetCount withFilter(QQueryFilter filter)
|
public WidgetCount withFilter(QQueryFilter filter)
|
||||||
{
|
{
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for conditionalFilterList
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public WidgetCount withConditionalFilterList(List<AbstractConditionalFilter> conditionalFilterList)
|
||||||
|
{
|
||||||
|
this.conditionalFilterList = conditionalFilterList;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter to add a single conditionalFilter
|
||||||
|
*******************************************************************************/
|
||||||
|
public WidgetCount withConditionalFilter(AbstractConditionalFilter conditionalFilter)
|
||||||
|
{
|
||||||
|
if(this.conditionalFilterList == null)
|
||||||
|
{
|
||||||
|
this.conditionalFilterList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.conditionalFilterList.add(conditionalFilter);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,17 +28,16 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class WidgetQueryField extends AbstractWidgetValueSource
|
public class WidgetQueryField extends AbstractWidgetValueSourceWithFilter
|
||||||
{
|
{
|
||||||
private String tableName;
|
|
||||||
private String selectFieldName;
|
private String selectFieldName;
|
||||||
private QQueryFilter filter;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -57,13 +56,11 @@ public class WidgetQueryField extends AbstractWidgetValueSource
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public Object evaluate(Map<String, Object> context) throws QException
|
public Object evaluate(Map<String, Object> context, RenderWidgetInput input) throws QException
|
||||||
{
|
{
|
||||||
// todo - look for params in the filter (fields or values)
|
|
||||||
// make sure to update it in supplementContext below too!!
|
|
||||||
QueryInput queryInput = new QueryInput();
|
QueryInput queryInput = new QueryInput();
|
||||||
queryInput.setTableName(tableName);
|
queryInput.setTableName(tableName);
|
||||||
queryInput.setFilter(filter);
|
queryInput.setFilter(getEffectiveFilter(input));
|
||||||
queryInput.setLimit(1);
|
queryInput.setLimit(1);
|
||||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||||
if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords()))
|
if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords()))
|
||||||
@ -76,16 +73,6 @@ public class WidgetQueryField extends AbstractWidgetValueSource
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public void supplementContext(Map<String, Object> context)
|
|
||||||
{
|
|
||||||
context.put(getName() + ".filter", filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for name
|
** Fluent setter for name
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -97,26 +84,6 @@ public class WidgetQueryField extends AbstractWidgetValueSource
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for tableName
|
|
||||||
*******************************************************************************/
|
|
||||||
public String getTableName()
|
|
||||||
{
|
|
||||||
return (this.tableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for tableName
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setTableName(String tableName)
|
|
||||||
{
|
|
||||||
this.tableName = tableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for tableName
|
** Fluent setter for tableName
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -128,26 +95,6 @@ public class WidgetQueryField extends AbstractWidgetValueSource
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Getter for filter
|
|
||||||
*******************************************************************************/
|
|
||||||
public QQueryFilter getFilter()
|
|
||||||
{
|
|
||||||
return (this.filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Setter for filter
|
|
||||||
*******************************************************************************/
|
|
||||||
public void setFilter(QQueryFilter filter)
|
|
||||||
{
|
|
||||||
this.filter = filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for filter
|
** Fluent setter for filter
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.actions.dashboard.widgets;
|
||||||
|
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for NoCodeWidgetVelocityUtils
|
||||||
|
*******************************************************************************/
|
||||||
|
class NoCodeWidgetVelocityUtilsTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testFormatSecondsAsDuration()
|
||||||
|
{
|
||||||
|
int HOUR = 60 * 60;
|
||||||
|
int DAY = 24 * 60 * 60;
|
||||||
|
|
||||||
|
NoCodeWidgetVelocityUtils utils = new NoCodeWidgetVelocityUtils(null, null);
|
||||||
|
assertEquals("", utils.formatSecondsAsDuration(null));
|
||||||
|
assertEquals("0 seconds", utils.formatSecondsAsDuration(0));
|
||||||
|
assertEquals("1 second", utils.formatSecondsAsDuration(1));
|
||||||
|
assertEquals("59 seconds", utils.formatSecondsAsDuration(59));
|
||||||
|
|
||||||
|
assertEquals("1 minute", utils.formatSecondsAsDuration(60));
|
||||||
|
assertEquals("1 minute 1 second", utils.formatSecondsAsDuration(61));
|
||||||
|
assertEquals("2 minutes 1 second", utils.formatSecondsAsDuration(121));
|
||||||
|
assertEquals("2 minutes 2 seconds", utils.formatSecondsAsDuration(122));
|
||||||
|
assertEquals("3 minutes", utils.formatSecondsAsDuration(180));
|
||||||
|
|
||||||
|
assertEquals("1 hour", utils.formatSecondsAsDuration(HOUR));
|
||||||
|
assertEquals("1 hour 1 second", utils.formatSecondsAsDuration(HOUR + 1));
|
||||||
|
assertEquals("1 hour 1 minute", utils.formatSecondsAsDuration(HOUR + 60));
|
||||||
|
assertEquals("1 hour 1 minute 1 second", utils.formatSecondsAsDuration(HOUR + 60 + 1));
|
||||||
|
assertEquals("1 hour 2 minutes 1 second", utils.formatSecondsAsDuration(HOUR + 120 + 1));
|
||||||
|
assertEquals("2 hours 2 minutes 2 seconds", utils.formatSecondsAsDuration(2 * 60 * 60 + 120 + 2));
|
||||||
|
assertEquals("23 hours 59 minutes 59 seconds", utils.formatSecondsAsDuration(DAY - 1));
|
||||||
|
|
||||||
|
assertEquals("1 day", utils.formatSecondsAsDuration(DAY));
|
||||||
|
assertEquals("1 day 1 second", utils.formatSecondsAsDuration(DAY + 1));
|
||||||
|
assertEquals("1 day 1 minute", utils.formatSecondsAsDuration(DAY + 60));
|
||||||
|
assertEquals("1 day 1 hour 1 minute 1 second", utils.formatSecondsAsDuration(DAY + HOUR + 60 + 1));
|
||||||
|
assertEquals("2 days 2 hours 2 minutes 2 seconds", utils.formatSecondsAsDuration(2 * DAY + 2 * 60 * 60 + 120 + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testFormatSecondsAsRoundedDuration()
|
||||||
|
{
|
||||||
|
int HOUR = 60 * 60;
|
||||||
|
int DAY = 24 * 60 * 60;
|
||||||
|
|
||||||
|
NoCodeWidgetVelocityUtils utils = new NoCodeWidgetVelocityUtils(null, null);
|
||||||
|
assertEquals("", utils.formatSecondsAsRoundedDuration(null));
|
||||||
|
assertEquals("0 seconds", utils.formatSecondsAsRoundedDuration(0));
|
||||||
|
assertEquals("1 second", utils.formatSecondsAsRoundedDuration(1));
|
||||||
|
assertEquals("59 seconds", utils.formatSecondsAsRoundedDuration(59));
|
||||||
|
|
||||||
|
assertEquals("1 minute", utils.formatSecondsAsRoundedDuration(60));
|
||||||
|
assertEquals("1 minute", utils.formatSecondsAsRoundedDuration(61));
|
||||||
|
assertEquals("2 minutes", utils.formatSecondsAsRoundedDuration(121));
|
||||||
|
assertEquals("2 minutes", utils.formatSecondsAsRoundedDuration(122));
|
||||||
|
assertEquals("3 minutes", utils.formatSecondsAsRoundedDuration(180));
|
||||||
|
|
||||||
|
assertEquals("1 hour", utils.formatSecondsAsRoundedDuration(HOUR));
|
||||||
|
assertEquals("1 hour", utils.formatSecondsAsRoundedDuration(HOUR + 1));
|
||||||
|
assertEquals("1 hour", utils.formatSecondsAsRoundedDuration(HOUR + 60));
|
||||||
|
assertEquals("1 hour", utils.formatSecondsAsRoundedDuration(HOUR + 60 + 1));
|
||||||
|
assertEquals("1 hour", utils.formatSecondsAsRoundedDuration(HOUR + 120 + 1));
|
||||||
|
assertEquals("2 hours", utils.formatSecondsAsRoundedDuration(2 * 60 * 60 + 120 + 2));
|
||||||
|
assertEquals("23 hours", utils.formatSecondsAsRoundedDuration(DAY - 1));
|
||||||
|
|
||||||
|
assertEquals("1 day", utils.formatSecondsAsRoundedDuration(DAY));
|
||||||
|
assertEquals("1 day", utils.formatSecondsAsRoundedDuration(DAY + 1));
|
||||||
|
assertEquals("1 day", utils.formatSecondsAsRoundedDuration(DAY + 60));
|
||||||
|
assertEquals("1 day", utils.formatSecondsAsRoundedDuration(DAY + HOUR + 60 + 1));
|
||||||
|
assertEquals("2 days", utils.formatSecondsAsRoundedDuration(2 * DAY + 2 * 60 * 60 + 120 + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -196,8 +196,8 @@ class QValueFormatterTest extends BaseTest
|
|||||||
void testFormatDates()
|
void testFormatDates()
|
||||||
{
|
{
|
||||||
assertEquals("2023-02-01", QValueFormatter.formatDate(LocalDate.of(2023, Month.FEBRUARY, 1)));
|
assertEquals("2023-02-01", QValueFormatter.formatDate(LocalDate.of(2023, Month.FEBRUARY, 1)));
|
||||||
assertEquals("2023-02-01 7:15 PM", QValueFormatter.formatDateTime(LocalDateTime.of(2023, Month.FEBRUARY, 1, 19, 15)));
|
assertEquals("2023-02-01 07:15 PM", QValueFormatter.formatDateTime(LocalDateTime.of(2023, Month.FEBRUARY, 1, 19, 15)));
|
||||||
assertEquals("2023-02-01 7:15 PM CST", QValueFormatter.formatDateTimeWithZone(ZonedDateTime.of(LocalDateTime.of(2023, Month.FEBRUARY, 1, 19, 15), ZoneId.of("US/Central"))));
|
assertEquals("2023-02-01 07:15 PM CST", QValueFormatter.formatDateTimeWithZone(ZonedDateTime.of(LocalDateTime.of(2023, Month.FEBRUARY, 1, 19, 15), ZoneId.of("US/Central"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user