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.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier; 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.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 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())) .filter(f -> RenderSavedReportMetaDataProducer.FIELD_NAME_STORAGE_TABLE_NAME.equals(f.getName()))
.findFirst() .findFirst()
.ifPresent(f -> f.setDefaultValue(REPORT_STORAGE_TABLE_NAME)); .ifPresent(f -> f.setDefaultValue(REPORT_STORAGE_TABLE_NAME));
instance.addWidget(defineRenderReportProcessValuesWidget());
instance.addWidget(defineReportSetupWidget()); instance.addWidget(defineReportSetupWidget());
instance.addWidget(definePivotTableSetupWidget()); instance.addWidget(definePivotTableSetupWidget());
@ -98,6 +102,10 @@ public class SavedReportsMetaDataProvider
instance.addWidget(defineScheduledReportJoinSavedReportWidget(join)); instance.addWidget(defineScheduledReportJoinSavedReportWidget(join));
QProcessMetaData scheduledReportSyncToScheduledJobProcess = new ScheduledReportSyncToScheduledJobProcess().produce(instance); QProcessMetaData scheduledReportSyncToScheduledJobProcess = new ScheduledReportSyncToScheduledJobProcess().produce(instance);
instance.addProcess(scheduledReportSyncToScheduledJobProcess); instance.addProcess(scheduledReportSyncToScheduledJobProcess);
instance.addWidget(defineScheduledReportValuesWidget());
QProcessMetaData runScheduledReportProcess = new RunScheduledReportMetaDataProducer().produce(instance);
instance.addProcess(runScheduledReportProcess);
if(instance.getPossibleValueSource(TimeZonePossibleValueSourceMetaDataProvider.NAME) == null) 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("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("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("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"))); .withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
if(backendDetailEnricher != null) 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.ScheduledJobParameter;
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJobType; 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.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.AbstractTableSyncTransformStep;
import com.kingsrook.qqq.backend.core.processes.implementations.tablesync.TableSyncProcess; import com.kingsrook.qqq.backend.core.processes.implementations.tablesync.TableSyncProcess;
import com.kingsrook.qqq.backend.core.utils.ValueUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils;
@ -121,7 +121,7 @@ public class ScheduledReportSyncToScheduledJobProcess extends AbstractTableSyncT
scheduledJob.setForeignKeyValue(String.valueOf(scheduledReport.getId())); scheduledJob.setForeignKeyValue(String.valueOf(scheduledReport.getId()));
scheduledJob.setJobParameters(List.of( scheduledJob.setJobParameters(List.of(
new ScheduledJobParameter().withKey("processName").withValue(getProcessNameScheduledJobParameter()), 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 else
@ -160,7 +160,7 @@ public class ScheduledReportSyncToScheduledJobProcess extends AbstractTableSyncT
*******************************************************************************/ *******************************************************************************/
private static String getProcessNameScheduledJobParameter() 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); String storageTableName = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_STORAGE_TABLE_NAME);
ReportFormat reportFormat = ReportFormat.fromString(runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_REPORT_FORMAT)); ReportFormat reportFormat = ReportFormat.fromString(runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_REPORT_FORMAT));
String sendToEmailAddress = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_EMAIL_ADDRESS); 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)); SavedReport savedReport = new SavedReport(runBackendStepInput.getRecords().get(0));
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, savedReport); String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, savedReport);
String storageReference = LocalDate.now() + "/" + LocalTime.now().toString().replaceAll(":", "").replaceFirst("\\..*", "") + "/" + UUID.randomUUID() + "/" + downloadFileBaseName + "." + reportFormat.getExtension(); 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 // // if sending an email (or emails), validate the addresses before doing anything so user gets error and can fix //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
List<String> toEmailAddressList = new ArrayList<>(); List<String> toEmailAddressList = new ArrayList<>();
if(sendToEmailAddress != null) if(StringUtils.hasContent(sendToEmailAddress))
{ {
toEmailAddressList = validateEmailAddresses(sendToEmailAddress); toEmailAddressList = validateEmailAddresses(sendToEmailAddress);
} }
@ -146,6 +147,10 @@ public class RenderSavedReportExecuteStep implements BackendStep
.withReportFormat(reportFormat) .withReportFormat(reportFormat)
.withReportOutputStream(outputStream)); .withReportOutputStream(outputStream));
//////////////////////////
// todo variable-values //
//////////////////////////
Map<String, Serializable> values = runBackendStepInput.getValues(); Map<String, Serializable> values = runBackendStepInput.getValues();
reportInput.setInputValues(values); reportInput.setInputValues(values);
@ -192,7 +197,7 @@ public class RenderSavedReportExecuteStep implements BackendStep
.withParty(new Party().withAddress(fromEmailAddress).withRole(EmailPartyRole.FROM)) .withParty(new Party().withAddress(fromEmailAddress).withRole(EmailPartyRole.FROM))
.withParty(new Party().withAddress(replyToEmailAddress).withRole(EmailPartyRole.REPLY_TO)) .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.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>")) .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.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData; 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.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_STORAGE_TABLE_NAME = "storageTableName";
public static final String FIELD_NAME_REPORT_FORMAT = "reportFormat"; 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_ADDRESS = "reportDestinationEmailAddress";
public static final String FIELD_NAME_EMAIL_SUBJECT = "emailSubject";
@ -67,6 +69,7 @@ public class RenderSavedReportMetaDataProducer implements MetaDataProducerInterf
.withLabel("Render Report") .withLabel("Render Report")
.withTableName(SavedReport.TABLE_NAME) .withTableName(SavedReport.TABLE_NAME)
.withIcon(new QIcon().withName("print")) .withIcon(new QIcon().withName("print"))
.addStep(new QBackendStepMetaData() .addStep(new QBackendStepMetaData()
.withName("pre") .withName("pre")
.withInputData(new QFunctionInputMetaData() .withInputData(new QFunctionInputMetaData()
@ -76,18 +79,24 @@ public class RenderSavedReportMetaDataProducer implements MetaDataProducerInterf
.withField(new QFieldMetaData(FIELD_NAME_STORAGE_TABLE_NAME, QFieldType.STRING)) .withField(new QFieldMetaData(FIELD_NAME_STORAGE_TABLE_NAME, QFieldType.STRING))
.withRecordListMetaData(new QRecordListMetaData().withTableName(SavedReport.TABLE_NAME))) .withRecordListMetaData(new QRecordListMetaData().withTableName(SavedReport.TABLE_NAME)))
.withCode(new QCodeReference(RenderSavedReportPreStep.class))) .withCode(new QCodeReference(RenderSavedReportPreStep.class)))
.addStep(new QFrontendStepMetaData() .addStep(new QFrontendStepMetaData()
.withName("input") .withName("input")
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM)) .withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM))
.withFormField(new QFieldMetaData(FIELD_NAME_REPORT_FORMAT, QFieldType.STRING) .withFormField(new QFieldMetaData(FIELD_NAME_REPORT_FORMAT, QFieldType.STRING)
.withPossibleValueSourceName(ReportFormatPossibleValueEnum.NAME) .withPossibleValueSourceName(ReportFormatPossibleValueEnum.NAME)
.withIsRequired(true)) .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() .addStep(new QBackendStepMetaData()
.withName("execute") .withName("execute")
.withInputData(new QFunctionInputMetaData().withRecordListMetaData(new QRecordListMetaData() .withInputData(new QFunctionInputMetaData().withRecordListMetaData(new QRecordListMetaData()
.withTableName(SavedReport.TABLE_NAME))) .withTableName(SavedReport.TABLE_NAME)))
.withCode(new QCodeReference(RenderSavedReportExecuteStep.class))) .withCode(new QCodeReference(RenderSavedReportExecuteStep.class)))
.addStep(new QFrontendStepMetaData() .addStep(new QFrontendStepMetaData()
.withName("output") .withName("output")
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.DOWNLOAD_FORM))); .withComponent(new QFrontendComponentMetaData().withType(QComponentType.DOWNLOAD_FORM)));

View File

@ -63,17 +63,20 @@ public class RenderSavedReportPreStep implements BackendStep
List<QRecord> records = runBackendStepInput.getRecords(); List<QRecord> records = runBackendStepInput.getRecords();
if(!CollectionUtils.nullSafeHasContents(records)) 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) 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)); SavedReport savedReport = new SavedReport(records.get(0));
runBackendStepOutput.addValue("savedReportId", savedReport.getId());
// todo - check for inputs - set up the input screen...
} }
} }