mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +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
|
||||
{
|
||||
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);
|
||||
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.QNoCodeWidgetMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.utils.BackendQueryFilterUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -58,22 +59,30 @@ public class NoCodeWidgetRenderer extends AbstractWidgetRenderer
|
||||
// build context by evaluating all values //
|
||||
////////////////////////////////////////////
|
||||
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())
|
||||
{
|
||||
LOG.trace("Computing: " + valueSource.getType() + " named " + valueSource.getName() + "...");
|
||||
Object value = valueSource.evaluate(context);
|
||||
LOG.trace("Computed: " + valueSource.getName() + " = " + value);
|
||||
context.put(valueSource.getName(), value);
|
||||
|
||||
context.put(valueSource.getName() + ".source", valueSource);
|
||||
try
|
||||
{
|
||||
LOG.trace("Computing: " + valueSource.getType() + " named " + valueSource.getName() + "...");
|
||||
Object value = valueSource.evaluate(context, input);
|
||||
LOG.trace("Computed: " + valueSource.getName() + " = " + value);
|
||||
context.put(valueSource.getName(), value);
|
||||
context.put(valueSource.getName() + ".source", valueSource);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error evaluating widget value source", e, logPair("widgetName", input.getWidgetMetaData().getName()), logPair("valueSourceName", valueSource.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// set default utils object in context too //
|
||||
/////////////////////////////////////////////
|
||||
context.put("utils", new NoCodeWidgetVelocityUtils(context));
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// build content by evaluating all outputs //
|
||||
/////////////////////////////////////////////
|
||||
|
@ -22,11 +22,18 @@
|
||||
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 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.logging.QLogger;
|
||||
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.utils.StringUtils;
|
||||
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 final Map<String, Object> context;
|
||||
private Map<String, Object> context;
|
||||
private RenderWidgetInput input;
|
||||
|
||||
|
||||
|
||||
@ -51,9 +55,10 @@ public class NoCodeWidgetVelocityUtils
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public NoCodeWidgetVelocityUtils(Map<String, Object> context)
|
||||
public NoCodeWidgetVelocityUtils(Map<String, Object> context, RenderWidgetInput input)
|
||||
{
|
||||
this.context = context;
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
|
||||
@ -61,7 +66,7 @@ public class NoCodeWidgetVelocityUtils
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public final String errorIcon()
|
||||
public String errorIcon()
|
||||
{
|
||||
return ("""
|
||||
<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 ("""
|
||||
<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");
|
||||
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));
|
||||
}
|
||||
catch(Exception e)
|
||||
@ -110,4 +255,14 @@ public class NoCodeWidgetVelocityUtils
|
||||
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 DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm a");
|
||||
private static DateTimeFormatter dateTimeWithZoneFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm a z");
|
||||
private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm a");
|
||||
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 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 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);
|
||||
}
|
||||
|
||||
}
|
@ -33,9 +33,13 @@ public class HtmlWrapper
|
||||
private String prefix;
|
||||
private String suffix;
|
||||
|
||||
public static final HtmlWrapper SUBHEADER = new HtmlWrapper("<h4>", "</h4>");
|
||||
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 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_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;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
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;
|
||||
|
||||
|
||||
@ -51,8 +54,18 @@ public class WidgetCalculation extends AbstractWidgetValueSource
|
||||
Integer sum = 0;
|
||||
for(String valueName : valueNames)
|
||||
{
|
||||
Integer addend = ValueUtils.getValueAsInteger(context.get(valueName));
|
||||
sum += addend;
|
||||
try
|
||||
{
|
||||
Integer addend = ValueUtils.getValueAsInteger(context.get(valueName));
|
||||
sum += addend;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
////////////////////////////////////////////////
|
||||
// assume value to be null or 0, don't add it //
|
||||
////////////////////////////////////////////////
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return (sum);
|
||||
}),
|
||||
@ -62,6 +75,30 @@ public class WidgetCalculation extends AbstractWidgetValueSource
|
||||
Instant now = Instant.now();
|
||||
Instant then = ValueUtils.getValueAsInstant(context.get(valueNames.get(0)));
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
@ -22,22 +22,22 @@
|
||||
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.CountAction;
|
||||
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.CountOutput;
|
||||
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
|
||||
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.setTableName(tableName);
|
||||
countInput.setFilter(filter);
|
||||
countInput.setFilter(getEffectiveFilter(input));
|
||||
|
||||
CountOutput countOutput = new CountAction().execute(countInput);
|
||||
return (countOutput.getCount());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void supplementContext(Map<String, Object> context)
|
||||
{
|
||||
context.put(getName() + ".filter", filter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public WidgetCount withTableName(String 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
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public WidgetCount withFilter(QQueryFilter filter)
|
||||
{
|
||||
this.filter = filter;
|
||||
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.QueryInput;
|
||||
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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class WidgetQueryField extends AbstractWidgetValueSource
|
||||
public class WidgetQueryField extends AbstractWidgetValueSourceWithFilter
|
||||
{
|
||||
private String tableName;
|
||||
private String selectFieldName;
|
||||
private QQueryFilter filter;
|
||||
private String selectFieldName;
|
||||
|
||||
|
||||
|
||||
@ -57,13 +56,11 @@ public class WidgetQueryField extends AbstractWidgetValueSource
|
||||
**
|
||||
*******************************************************************************/
|
||||
@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.setTableName(tableName);
|
||||
queryInput.setFilter(filter);
|
||||
queryInput.setFilter(getEffectiveFilter(input));
|
||||
queryInput.setLimit(1);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
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
|
||||
*******************************************************************************/
|
||||
@ -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
|
||||
*******************************************************************************/
|
||||
@ -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
|
||||
*******************************************************************************/
|
||||
|
@ -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()
|
||||
{
|
||||
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 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", QValueFormatter.formatDateTime(LocalDateTime.of(2023, Month.FEBRUARY, 1, 19, 15)));
|
||||
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