CE-1068 - Progress on scheduling reports, with variable inputs

This commit is contained in:
2024-04-30 10:28:22 -05:00
parent 0a35d02404
commit 8829408e54
5 changed files with 67 additions and 11 deletions

View File

@ -52,6 +52,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
import com.kingsrook.qqq.backend.core.processes.implementations.savedreports.RenderSavedReportMetaDataProducer;
import com.kingsrook.qqq.backend.core.processes.implementations.savedreports.RunScheduledReportMetaDataProducer;
/*******************************************************************************
@ -63,6 +64,8 @@ public class SavedReportsMetaDataProvider
public static final String SAVED_REPORT_JOIN_SCHEDULED_REPORT = "scheduledReportJoinSavedReport";
public static final String SCHEDULED_REPORT_VALUES_WIDGET = "scheduledReportValuesWidget";
public static final String RENDER_REPORT_PROCESS_VALUES_WIDGET = "renderReportProcessValuesWidget";
/*******************************************************************************
@ -85,6 +88,7 @@ public class SavedReportsMetaDataProvider
.filter(f -> RenderSavedReportMetaDataProducer.FIELD_NAME_STORAGE_TABLE_NAME.equals(f.getName()))
.findFirst()
.ifPresent(f -> f.setDefaultValue(REPORT_STORAGE_TABLE_NAME));
instance.addWidget(defineRenderReportProcessValuesWidget());
instance.addWidget(defineReportSetupWidget());
instance.addWidget(definePivotTableSetupWidget());
@ -98,6 +102,10 @@ public class SavedReportsMetaDataProvider
instance.addWidget(defineScheduledReportJoinSavedReportWidget(join));
QProcessMetaData scheduledReportSyncToScheduledJobProcess = new ScheduledReportSyncToScheduledJobProcess().produce(instance);
instance.addProcess(scheduledReportSyncToScheduledJobProcess);
instance.addWidget(defineScheduledReportValuesWidget());
QProcessMetaData runScheduledReportProcess = new RunScheduledReportMetaDataProducer().produce(instance);
instance.addProcess(runScheduledReportProcess);
if(instance.getPossibleValueSource(TimeZonePossibleValueSourceMetaDataProvider.NAME) == null)
{
@ -107,6 +115,36 @@ public class SavedReportsMetaDataProvider
/*******************************************************************************
**
*******************************************************************************/
private QWidgetMetaDataInterface defineScheduledReportValuesWidget()
{
return new QWidgetMetaData()
.withName(SCHEDULED_REPORT_VALUES_WIDGET)
.withType(WidgetType.DYNAMIC_FORM.getType())
.withIsCard(true)
.withLabel("Variable Values")
.withCodeReference(new QCodeReference(ReportValuesDynamicFormWidgetRenderer.class));
}
/*******************************************************************************
**
*******************************************************************************/
private QWidgetMetaDataInterface defineRenderReportProcessValuesWidget()
{
return new QWidgetMetaData()
.withName(RENDER_REPORT_PROCESS_VALUES_WIDGET)
.withType(WidgetType.DYNAMIC_FORM.getType())
.withIsCard(false)
.withDefaultValue("isEditable", true)
.withCodeReference(new QCodeReference(ReportValuesDynamicFormWidgetRenderer.class));
}
/*******************************************************************************
**
*******************************************************************************/
@ -277,7 +315,8 @@ public class SavedReportsMetaDataProvider
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "savedReportId")))
.withSection(new QFieldSection("settings", new QIcon().withName("settings"), Tier.T2, List.of("cronExpression", "cronTimeZoneId", "isActive", "format")))
.withSection(new QFieldSection("recipient", new QIcon().withName("email"), Tier.T2, List.of("toAddresses", "subject")))
.withSection(new QFieldSection("variableValues", new QIcon().withName("data_object"), Tier.T2, List.of("inputValues")))
.withSection(new QFieldSection("variableValues", new QIcon().withName("data_object"), Tier.T2).withWidgetName(SCHEDULED_REPORT_VALUES_WIDGET))
.withSection(new QFieldSection("hidden", new QIcon().withName("visibility_off"), Tier.T2, List.of("inputValues")).withIsHidden(true))
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
if(backendDetailEnricher != null)

View File

@ -41,7 +41,7 @@ import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJob;
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJobParameter;
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJobType;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
import com.kingsrook.qqq.backend.core.processes.implementations.savedreports.RenderSavedReportMetaDataProducer;
import com.kingsrook.qqq.backend.core.processes.implementations.savedreports.RunScheduledReportMetaDataProducer;
import com.kingsrook.qqq.backend.core.processes.implementations.tablesync.AbstractTableSyncTransformStep;
import com.kingsrook.qqq.backend.core.processes.implementations.tablesync.TableSyncProcess;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
@ -121,7 +121,7 @@ public class ScheduledReportSyncToScheduledJobProcess extends AbstractTableSyncT
scheduledJob.setForeignKeyValue(String.valueOf(scheduledReport.getId()));
scheduledJob.setJobParameters(List.of(
new ScheduledJobParameter().withKey("processName").withValue(getProcessNameScheduledJobParameter()),
new ScheduledJobParameter().withKey("scheduledReportId").withValue(ValueUtils.getValueAsString(scheduledReport.getId()))
new ScheduledJobParameter().withKey("recordId").withValue(ValueUtils.getValueAsString(scheduledReport.getId()))
));
}
else
@ -160,7 +160,7 @@ public class ScheduledReportSyncToScheduledJobProcess extends AbstractTableSyncT
*******************************************************************************/
private static String getProcessNameScheduledJobParameter()
{
return RenderSavedReportMetaDataProducer.NAME;
return RunScheduledReportMetaDataProducer.NAME;
}

View File

@ -100,6 +100,7 @@ public class RenderSavedReportExecuteStep implements BackendStep
String storageTableName = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_STORAGE_TABLE_NAME);
ReportFormat reportFormat = ReportFormat.fromString(runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_REPORT_FORMAT));
String sendToEmailAddress = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_EMAIL_ADDRESS);
String emailSubject = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_EMAIL_SUBJECT);
SavedReport savedReport = new SavedReport(runBackendStepInput.getRecords().get(0));
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, savedReport);
String storageReference = LocalDate.now() + "/" + LocalTime.now().toString().replaceAll(":", "").replaceFirst("\\..*", "") + "/" + UUID.randomUUID() + "/" + downloadFileBaseName + "." + reportFormat.getExtension();
@ -108,7 +109,7 @@ public class RenderSavedReportExecuteStep implements BackendStep
// if sending an email (or emails), validate the addresses before doing anything so user gets error and can fix //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
List<String> toEmailAddressList = new ArrayList<>();
if(sendToEmailAddress != null)
if(StringUtils.hasContent(sendToEmailAddress))
{
toEmailAddressList = validateEmailAddresses(sendToEmailAddress);
}
@ -146,6 +147,10 @@ public class RenderSavedReportExecuteStep implements BackendStep
.withReportFormat(reportFormat)
.withReportOutputStream(outputStream));
//////////////////////////
// todo variable-values //
//////////////////////////
Map<String, Serializable> values = runBackendStepInput.getValues();
reportInput.setInputValues(values);
@ -192,7 +197,7 @@ public class RenderSavedReportExecuteStep implements BackendStep
.withParty(new Party().withAddress(fromEmailAddress).withRole(EmailPartyRole.FROM))
.withParty(new Party().withAddress(replyToEmailAddress).withRole(EmailPartyRole.REPLY_TO))
)
.withSubject(downloadFileBaseName)
.withSubject(StringUtils.hasContent(emailSubject) ? emailSubject : downloadFileBaseName)
.withContent(new Content().withContentRole(EmailContentRole.TEXT).withBody("To download your report, open this URL in your browser: " + downloadURL))
.withContent(new Content().withContentRole(EmailContentRole.HTML).withBody("Link: <a target=\"_blank\" href=\"" + downloadURL + "\" download>" + downloadFileName + "</a>"))
);

View File

@ -38,6 +38,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMet
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.SavedReport;
import com.kingsrook.qqq.backend.core.model.savedreports.SavedReportsMetaDataProvider;
/*******************************************************************************
@ -53,6 +54,7 @@ public class RenderSavedReportMetaDataProducer implements MetaDataProducerInterf
public static final String FIELD_NAME_STORAGE_TABLE_NAME = "storageTableName";
public static final String FIELD_NAME_REPORT_FORMAT = "reportFormat";
public static final String FIELD_NAME_EMAIL_ADDRESS = "reportDestinationEmailAddress";
public static final String FIELD_NAME_EMAIL_SUBJECT = "emailSubject";
@ -67,6 +69,7 @@ public class RenderSavedReportMetaDataProducer implements MetaDataProducerInterf
.withLabel("Render Report")
.withTableName(SavedReport.TABLE_NAME)
.withIcon(new QIcon().withName("print"))
.addStep(new QBackendStepMetaData()
.withName("pre")
.withInputData(new QFunctionInputMetaData()
@ -76,18 +79,24 @@ public class RenderSavedReportMetaDataProducer implements MetaDataProducerInterf
.withField(new QFieldMetaData(FIELD_NAME_STORAGE_TABLE_NAME, QFieldType.STRING))
.withRecordListMetaData(new QRecordListMetaData().withTableName(SavedReport.TABLE_NAME)))
.withCode(new QCodeReference(RenderSavedReportPreStep.class)))
.addStep(new QFrontendStepMetaData()
.withName("input")
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM))
.withFormField(new QFieldMetaData(FIELD_NAME_REPORT_FORMAT, QFieldType.STRING)
.withPossibleValueSourceName(ReportFormatPossibleValueEnum.NAME)
.withIsRequired(true))
.withFormField(new QFieldMetaData(FIELD_NAME_EMAIL_ADDRESS, QFieldType.STRING).withLabel("Send To Email Address")))
.withFormField(new QFieldMetaData(FIELD_NAME_EMAIL_ADDRESS, QFieldType.STRING).withLabel("Send To Email Address"))
.withFormField(new QFieldMetaData(FIELD_NAME_EMAIL_SUBJECT, QFieldType.STRING).withLabel("Email Subject"))
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.WIDGET)
.withValue("widgetName", SavedReportsMetaDataProvider.RENDER_REPORT_PROCESS_VALUES_WIDGET)))
.addStep(new QBackendStepMetaData()
.withName("execute")
.withInputData(new QFunctionInputMetaData().withRecordListMetaData(new QRecordListMetaData()
.withTableName(SavedReport.TABLE_NAME)))
.withCode(new QCodeReference(RenderSavedReportExecuteStep.class)))
.addStep(new QFrontendStepMetaData()
.withName("output")
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.DOWNLOAD_FORM)));

View File

@ -63,17 +63,20 @@ public class RenderSavedReportPreStep implements BackendStep
List<QRecord> records = runBackendStepInput.getRecords();
if(!CollectionUtils.nullSafeHasContents(records))
{
throw (new QUserFacingException("No report was selected or found to be rendered."));
throw (new QUserFacingException("No report was selected or found."));
}
if(records.size() > 1)
{
throw (new QUserFacingException("You may only render 1 report at a time."));
throw (new QUserFacingException("You may only run 1 report at a time."));
}
///////////////////////////////////////////////////////////////////////////////////////
// put the savedReportId in values - this'll get passed into the widget, so it knows //
// what report we're working with, and thus what inputs to prompt for //
///////////////////////////////////////////////////////////////////////////////////////
SavedReport savedReport = new SavedReport(records.get(0));
// todo - check for inputs - set up the input screen...
runBackendStepOutput.addValue("savedReportId", savedReport.getId());
}
}