I use this a lot in my CloverETL graphs to send output to end users.
Most often, I format a table and then send it as the content of an email message.
I have attached a simple example graph that will send me the output of the DataGenerator. It uses record functions to get the datatype and the names of the input fields, so it should work with any metadata.
<?xml version="1.0" encoding="UTF-8"?> <!\[CDATA\[//#CTL2// Generates output record.
function integer generate() {
$out.0.field1 = random();
$out.0.field4 = randomString(3,5);
$out.0.field3 = randomString(3,5);
$out.0.field2 = randomDate(2017-01-01,2017-12-31);
$out.0.field5 = randomLong(0,100);
return ALL;
}
// Called during component initialization.
// function boolean init() {}
// Called during each graph run before the transform is executed. May be used to allocate and initialize resources
// required by the generate. All resources allocated within this method should be released
// by the postExecute() method.
// function void preExecute() {}
// Called only if generate() throws an exception.
// function integer generateOnError(string errorMessage, string stackTrace) {
// }
// Called during each graph run after the entire transform was executed. Should be used to free any resources
// allocated within the preExecute() method.
// function void postExecute() {}
// Called to return a user-defined error message when an error occurs.
// function string getMessage() {}
]]>
<![CDATA[//#CTL2
integer columncounter = 0;
string outputString = “”;
// Called for the first data record in a new group. Starts the parsing of the new group.
// This will collect the column headings and the meta data description
function void initGroup(outputStream groupAccumulator) {
// The metadata description is stored in the record properties.
map[string, string] properties = getRecordProperties($in.0);
string listOfKeys = getKeys(properties );
// If there is no description, fill the value
outputString = “
” + nvl(properties[“description”],“metadata description” ) +“
”;// start the table
outputString = outputString + “
// Now get the column headings
for(columncounter = 0; columncounter < length($in.0); columncounter++)
{
outputString = outputString + “”;
}
outputString = outputString + " ";
} // end initGroup
// Called for each data record in the group (including the first one and the last one).
// Implicitly returns false => updateTransform() is not called. When returns true, calls updateTransform().
function boolean updateGroup(outputStream groupAccumulator) {
return true;
}
// Called for the last data records in all groups sequentially, but only after all incoming data records have been parsed.
// Implicitly returns true => transform() is called for the whole group.
function boolean finishGroup(outputStream groupAccumulator) {
return true;
}
// Called to transform data records that have been parsed so far into user-specified number of output data record(s).
// Counter (incremented by 1 starting from 0) stores the number of previous calls to this method for the current group update.
// Group accumulator can optionally be used.
// Function implicitly returns SKIP to skip sending any data records to output.
// Returning ALL causes each data record to be sent to all output port(s).
// Can also return a number of the output port to which individual data record should be sent.
function integer updateTransform(integer counter, outputStream groupAccumulator) {
outputString = outputString + “
”;for(columncounter = 0; columncounter < length($in.0); columncounter++)
{
if (isNull($in.0,columncounter))
{
outputString = outputString + “”;
}
else
{
outputString = outputString + “”;
}
} // end for next
outputString = outputString + “”;
return SKIP;
}
// Called to transform the whole group of incoming data record(s) into user-specified number of output data record(s).
// Counter (incremented by 1 starting from 0) stores the number of previous calls to this method for the current group update.
// Group accumulator can optionally be used.
// Function implicitly returns SKIP to skip sending any data records to output.
// Returning ALL causes each data record to be sent to all output port(s).
// Can also return a number of the output port to which individual data record should be sent.
function integer transform(integer counter, outputStream groupAccumulator) {
// only a single output data record will be generated
if (counter > 0)
{
return SKIP;
}
outputString = outputString + “
”+nvl(getFieldLabel($in.0,columncounter),getFieldName($in.0,columncounter))+“ | ||
---|---|---|
”; } else if (getFieldType($in.0, columncounter) == “date”) //format the date { outputString = outputString + “ | ”+ date2str(getDateValue($in.0, columncounter),“MM/dd/yyyy”) + “ | ”+getValueAsString($in.0,columncounter)+“ |
$out.0.0 = outputString;
return ALL;
}
// Called during component initialization.
// function void init() {}
// Called during each graph run before the transform is executed. May be used to allocate and initialize resources
// required by the transform. All resources allocated within this method should be released
// by the postExecute() method.
// function void preExecute() {}
// Called only if initGroup(DataRecord) throws an exception.
//function void initGroupOnError(string errorMessage, string stackTrace, recordName1 groupAccumulator) {
//}
// Called only if updateGroup(DataRecord) throws an exception.
//function boolean updateGroupOnError(string errorMessage, string stackTrace, recordName1 groupAccumulator) {
//}
// Called only if finishGroup(DataRecord) throws an exception.
//function boolean finishGroupOnError(string errorMessage, string stackTrace, recordName1 groupAccumulator) {
//}
// Called only if updateTransform(integer, DataRecord) throws an exception.
//function integer updateTransformOnError(string errorMessage, string stackTrace, integer counter, recordName1 groupAccumulator) {
//}
// Called only if transform(integer, DataRecord) throws an exception.
//function integer transformOnError(string errorMessage, string stackTrace, integer counter, recordName1 groupAccumulator) {
//}
// Called during each graph run after the entire transform was executed. Should be used to free any resources
// allocated within the preExecute() method.
// function void postExecute() {}
// Called to return a user-defined error message when an error occurs.
// function string getMessage() {}
]]>
<![CDATA[Use the metadata description for column headings]]>
<![CDATA[Add HTML formatting and any styles]]>