CE-1068 - Initial checkin

This commit is contained in:
2024-04-30 10:28:36 -05:00
parent 8829408e54
commit 76028ddcaa
3 changed files with 356 additions and 0 deletions

View File

@ -0,0 +1,175 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. 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.savedreports;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
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.get.GetInput;
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.model.actions.widgets.RenderWidgetOutput;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.DynamicFormWidgetData;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.processes.implementations.savedreports.SavedReportToReportMetaDataAdapter;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
import org.json.JSONObject;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
/*******************************************************************************
** Note - exists under 2 names, for the RenderSavedReport process, and for the
** ScheduledReport table
*******************************************************************************/
public class ReportValuesDynamicFormWidgetRenderer extends AbstractWidgetRenderer
{
private static final QLogger LOG = QLogger.getLogger(ReportValuesDynamicFormWidgetRenderer.class);
/*******************************************************************************
**
*******************************************************************************/
@Override
public RenderWidgetOutput render(RenderWidgetInput input) throws QException
{
try
{
List<QFieldMetaData> fieldList = new ArrayList<>();
Map<String, String> defaultValues = new HashMap<>();
//////////////////////////////////////////////////////////////////////////////
// read params to ultimately find the query filter that has variables in it //
//////////////////////////////////////////////////////////////////////////////
SavedReport savedReport = null;
if(input.getQueryParams().containsKey("savedReportId"))
{
QRecord record = new GetAction().executeForRecord(new GetInput(SavedReport.TABLE_NAME).withPrimaryKey(ValueUtils.getValueAsInteger(input.getQueryParams().get("savedReportId"))));
savedReport = new SavedReport(record);
}
else if(input.getQueryParams().containsKey("id"))
{
QRecord scheduledReportRecord = new GetAction().executeForRecord(new GetInput(ScheduledReport.TABLE_NAME).withPrimaryKey(ValueUtils.getValueAsInteger(input.getQueryParams().get("id"))));
QRecord record = new GetAction().executeForRecord(new GetInput(SavedReport.TABLE_NAME).withPrimaryKey(ValueUtils.getValueAsInteger(scheduledReportRecord.getValueInteger("savedReportId"))));
savedReport = new SavedReport(record);
String inputValues = scheduledReportRecord.getValueString("inputValues");
if(StringUtils.hasContent(inputValues))
{
JSONObject jsonObject = JsonUtils.toJSONObject(inputValues);
for(String key : jsonObject.keySet())
{
defaultValues.put(key, jsonObject.optString(key));
}
}
}
else
{
//////////////////////////////////
// return quietly w/ nothing... //
//////////////////////////////////
DynamicFormWidgetData widgetData = new DynamicFormWidgetData();
return new RenderWidgetOutput(widgetData);
}
if(StringUtils.hasContent(savedReport.getQueryFilterJson()))
{
QQueryFilter queryFilter = SavedReportToReportMetaDataAdapter.getQQueryFilter(savedReport.getQueryFilterJson());
QTableMetaData table = QContext.getQInstance().getTable(savedReport.getTableName());
///////////////////////////////////////////////////////////////////////////////////////////////
// find variables in the query filter; convert them to a list of fields for the dynamic form //
///////////////////////////////////////////////////////////////////////////////////////////////
for(QFilterCriteria criteria : CollectionUtils.nonNullList(queryFilter.getCriteria()))
{
/////////////////////////////////
// todo - only variable fields //
/////////////////////////////////
////////////////////////////////
// todo - twice for "between" //
////////////////////////////////
//////////////////////////
// todo - join fields!! //
//////////////////////////
QFieldMetaData fieldMetaData = table.getField(criteria.getFieldName()).clone();
/////////////////////////////////
// make name & label for field //
/////////////////////////////////
String operatorHumanish = StringUtils.allCapsToMixedCase(criteria.getOperator().name()); // todo match frontend..?
String fieldName = criteria.getFieldName() + operatorHumanish.replaceAll("_", "");
String label = fieldMetaData.getLabel() + " " + operatorHumanish.replaceAll("_", " ");
fieldMetaData.setName(fieldName);
fieldMetaData.setLabel(label);
////////////////////////////////////////////////////////////
// in this use case, every field is required and editable //
////////////////////////////////////////////////////////////
fieldMetaData.setIsRequired(true);
fieldMetaData.setIsEditable(true);
if(defaultValues.containsKey(fieldName))
{
fieldMetaData.setDefaultValue(defaultValues.get(fieldName));
}
fieldList.add(fieldMetaData);
}
}
///////////////////////////////////
// make output object and return //
///////////////////////////////////
DynamicFormWidgetData widgetData = new DynamicFormWidgetData();
widgetData.setFieldList(fieldList);
widgetData.setMergedDynamicFormValuesIntoFieldName("inputValues");
if(CollectionUtils.nullSafeIsEmpty(fieldList))
{
widgetData.setNoFieldsMessage("This Report does not use any Variable Values");
}
return new RenderWidgetOutput(widgetData);
}
catch(Exception e)
{
LOG.warn("Error rendering scheduled report values dynamic form widget", e, logPair("queryParams", String.valueOf(input.getQueryParams())));
throw (new QException("Error rendering scheduled report values dynamic form widget", e));
}
}
}

View File

@ -0,0 +1,105 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. 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.processes.implementations.savedreports;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallbackFactory;
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.savedreports.ScheduledReport;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
/*******************************************************************************
**
*******************************************************************************/
public class RunScheduledReportExecuteStep implements BackendStep
{
private static final QLogger LOG = QLogger.getLogger(RunScheduledReportExecuteStep.class);
/*******************************************************************************
**
*******************************************************************************/
@Override
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
{
Integer scheduledReportId = null;
try
{
List<QRecord> records = runBackendStepInput.getRecords();
if(!CollectionUtils.nullSafeHasContents(records))
{
throw (new QUserFacingException("No scheduled report was selected or found."));
}
ScheduledReport scheduledReport = new ScheduledReport(records.get(0));
scheduledReportId = scheduledReport.getId();
/////////////////////////////////////////////
// run the process that renders the report //
/////////////////////////////////////////////
RunProcessAction runProcessAction = new RunProcessAction();
RunProcessInput renderProcessInput = new RunProcessInput();
renderProcessInput.setProcessName(RenderSavedReportMetaDataProducer.NAME);
renderProcessInput.setCallback(QProcessCallbackFactory.forPrimaryKey("id", scheduledReport.getSavedReportId()));
renderProcessInput.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
renderProcessInput.setAsyncJobCallback(runBackendStepInput.getAsyncJobCallback());
renderProcessInput.addValue(RenderSavedReportMetaDataProducer.FIELD_NAME_REPORT_FORMAT, scheduledReport.getFormat());
renderProcessInput.addValue(RenderSavedReportMetaDataProducer.FIELD_NAME_EMAIL_ADDRESS, scheduledReport.getToAddresses());
renderProcessInput.addValue(RenderSavedReportMetaDataProducer.FIELD_NAME_EMAIL_SUBJECT, scheduledReport.getSubject());
if(StringUtils.hasContent(scheduledReport.getInputValues()))
{
//////////////////////////
// todo variable-values //
//////////////////////////
}
RunProcessOutput renderProcessOutput = runProcessAction.execute(renderProcessInput);
}
catch(QUserFacingException ufe)
{
LOG.info("Error running scheduled report", ufe, logPair("id", scheduledReportId));
throw (ufe);
}
catch(Exception e)
{
LOG.warn("Error running scheduled report", e, logPair("id", scheduledReportId));
throw (new QException("Error running scheduled report", e));
}
}
}

View File

@ -0,0 +1,76 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. 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.processes.implementations.savedreports;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.nocode.WidgetHtmlLine;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.processes.NoCodeWidgetFrontendComponentMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData;
import com.kingsrook.qqq.backend.core.model.savedreports.ScheduledReport;
/*******************************************************************************
** define process for rendering scheduled reports - that is - a thin layer on
** top of rendering a saved report.
*******************************************************************************/
public class RunScheduledReportMetaDataProducer implements MetaDataProducerInterface<QProcessMetaData>
{
public static final String NAME = "runScheduledReport";
/*******************************************************************************
**
*******************************************************************************/
@Override
public QProcessMetaData produce(QInstance qInstance) throws QException
{
QProcessMetaData process = new QProcessMetaData()
.withName(NAME)
.withLabel("Run Scheduled Report")
.withTableName(ScheduledReport.TABLE_NAME)
.withIcon(new QIcon().withName("print"))
.addStep(new QBackendStepMetaData()
.withName("execute")
.withInputData(new QFunctionInputMetaData().withRecordListMetaData(new QRecordListMetaData()
.withTableName(ScheduledReport.TABLE_NAME)))
.withCode(new QCodeReference(RunScheduledReportExecuteStep.class)))
.addStep(new QFrontendStepMetaData()
.withName("results")
.withComponent(new NoCodeWidgetFrontendComponentMetaData()
.withOutput(new WidgetHtmlLine().withVelocityTemplate("Success")))); // todo!!!
return (process);
}
}