additionalFields = new ArrayList<>();
+ for(QFieldMetaData field : tableStructure.getFields())
+ {
+ if(field.getIsRequired())
+ {
+ requiredFields.add(field);
+ }
+ else
+ {
+ additionalFields.add(field);
+ }
+ }
+
+ StringBuilder html;
+ String childTableLabels = "";
+
+ StringBuilder tallCSV = new StringBuilder();
+ StringBuilder wideCSV = new StringBuilder();
+ StringBuilder flatCSV = new StringBuilder();
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // potentially this could be a parameter - for now, hard-code false, but keep the code around that did this //
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ boolean listFieldsInHelpText = false;
+
+ if(!CollectionUtils.nullSafeHasContents(tableStructure.getAssociations()))
+ {
+ html = new StringBuilder("""
+ Upload either a CSV or Excel (.xlsx) file, with one row for each record you want to
+ insert in the ${tableLabel} table.
+
+ Your file can contain any number of columns. You will be prompted to map fields from
+ the ${tableLabel} table to columns from your file or default values for all records that
+ you are loading on the next screen. It is optional (though encouraged) whether you include
+ a header row in your file. For Excel files, only the first sheet in the workbook will be used.
+ """);
+
+ if(listFieldsInHelpText)
+ {
+ appendTableRequiredAndAdditionalFields(html, requiredFields, additionalFields);
+ html.append("""
+ Template: ${tableLabel}.csv""");
+ }
+ else
+ {
+ html.append("""
+ You can download a template file to see the full list of available fields:
+ ${tableLabel}.csv
+
+ """);
+ }
+ }
+ else
+ {
+ childTableLabels = StringUtils.joinWithCommasAndAnd(tableStructure.getAssociations().stream().map(a -> a.getLabel()).toList()) + " table" + StringUtils.plural(table.getAssociations());
+
+ html = new StringBuilder("""
+ Upload either a CSV or Excel (.xlsx) file. Your file can be in one of three layouts:
+ ${openUL}
+
Flat: Each row in the file will create one record in the ${tableLabel} table.
+ Wide: Each row in the file will create one record in the ${tableLabel} table,
+ and optionally one or more records in the ${childTableLabels}, by supplying additional columns
+ for each sub-record that you want to create.
+ Tall: Rows with matching values in the fields being used for the ${tableLabel}
+ table will be used to create one ${tableLabel} record. One or more records will also be built
+ in the ${childTableLabels} by providing unique values in each row for the sub-records.
+
+
+ Your file can contain any number of columns. You will be prompted to map fields from
+ the ${tableLabel} table to columns from your file or default values for all records that
+ you are loading on the next screen. It is optional (though encouraged) whether you include
+ a header row in your file. For Excel files, only the first sheet in the workbook will be used.
+ """);
+
+ if(listFieldsInHelpText)
+ {
+ appendTableRequiredAndAdditionalFields(html, requiredFields, additionalFields);
+ }
+
+ addCsvFields(tallCSV, requiredFields, additionalFields);
+ addCsvFields(wideCSV, requiredFields, additionalFields);
+
+ for(BulkLoadTableStructure association : tableStructure.getAssociations())
+ {
+ if(listFieldsInHelpText)
+ {
+ html.append("""
+ You can also add values for these ${childLabel} fields:
+ """.replace("${childLabel}", association.getLabel()));
+ appendFieldsAsUlToHtml(html, association.getFields());
+ }
+
+ addCsvFields(tallCSV, association.getFields(), Collections.emptyList(), association.getLabel() + ": ", "");
+ addCsvFields(wideCSV, association.getFields(), Collections.emptyList(), association.getLabel() + ": ", " - 1");
+ addCsvFields(wideCSV, association.getFields(), Collections.emptyList(), association.getLabel() + ": ", " - 2");
+ }
+
+ finishCSV(tallCSV);
+ finishCSV(wideCSV);
+
+ if(listFieldsInHelpText)
+ {
+ html.append("""
+ Templates: ${tableLabel} - Flat.csv
+ | ${tableLabel} - Tall.csv
+ | ${tableLabel} - Wide.csv
+ """);
+ }
+ else
+ {
+ html.append("""
+ You can download a template file to see the full list of available fields:
+ ${tableLabel} - Flat.csv
+ | ${tableLabel} - Tall.csv
+ | ${tableLabel} - Wide.csv
+
+ """);
+ }
+ }
+
+ html.insert(0, """
+
+ File Upload Instructions
+
+ """);
+ html.append(" ");
+
+ addCsvFields(flatCSV, requiredFields, additionalFields);
+ finishCSV(flatCSV);
+
+ String htmlString = html.toString()
+ .replace("${tableLabel}", table.getLabel())
+ .replace("${childTableLabels}", childTableLabels)
+ .replace("${flatCSV}", Base64.getEncoder().encodeToString(flatCSV.toString().getBytes(StandardCharsets.UTF_8)))
+ .replace("${tallCSV}", Base64.getEncoder().encodeToString(tallCSV.toString().getBytes(StandardCharsets.UTF_8)))
+ .replace("${wideCSV}", Base64.getEncoder().encodeToString(wideCSV.toString().getBytes(StandardCharsets.UTF_8)))
+ .replace("${openUL}", "");
+
+ runBackendStepOutput.addValue("upload.html", htmlString);
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ private static void finishCSV(StringBuilder flatCSV)
+ {
+ flatCSV.deleteCharAt(flatCSV.length() - 1);
+ flatCSV.append("\n");
+ flatCSV.append(flatCSV.toString().replaceAll("[^,]", ""));
+ flatCSV.append("\n");
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ private static void addCsvFields(StringBuilder csv, List requiredFields, List additionalFields)
+ {
+ addCsvFields(csv, requiredFields, additionalFields, "", "");
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ private static void addCsvFields(StringBuilder csv, List requiredFields, List additionalFields, String fieldLabelPrefix, String fieldLabelSuffix)
+ {
+ for(QFieldMetaData field : requiredFields)
+ {
+ csv.append(fieldLabelPrefix).append(field.getLabel()).append(fieldLabelSuffix).append(",");
+ }
+
+ for(QFieldMetaData field : additionalFields)
+ {
+ csv.append(fieldLabelPrefix).append(field.getLabel()).append(fieldLabelSuffix).append(",");
+ }
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ private static void appendTableRequiredAndAdditionalFields(StringBuilder html, List requiredFields, List additionalFields)
+ {
+ if(!requiredFields.isEmpty())
+ {
+ html.append("""
+ You will be required to supply values (either in a column in the file, or by
+ choosing a default value on the next screen) for the following ${tableLabel} fields:
+ """);
+ appendFieldsAsUlToHtml(html, requiredFields);
+ }
+
+ if(!additionalFields.isEmpty())
+ {
+ if(requiredFields.isEmpty())
+ {
+ html.append("""
+ You can supply values (either in a column in the file, or by choosing a
+ default value on the next screen) for the following ${tableLabel} fields:
+ """);
+ }
+ else
+ {
+ html.append("You can also add values for these fields:
");
+ }
+
+ appendFieldsAsUlToHtml(html, additionalFields);
+ }
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ private static void appendFieldsAsUlToHtml(StringBuilder html, List additionalFields)
+ {
+ html.append("${openUL}");
+ for(QFieldMetaData field : additionalFields)
+ {
+ html.append("- ").append(field.getLabel()).append("
");
+ }
+ html.append("
");
+ }
+
+}