# --------------------------------
# subroutine report_builder_export
# --------------------------------
# initiated from report_builder
include "templates.statistics.report_elements.util.table.get_sort_type";
include "templates.statistics.report_elements.util.set_filter_expression";
include "templates.statistics.report_elements.util.table.get_aggregation_method_is_average";
include "templates.statistics.report_elements.util.table.get_average_denominator_field";
include "templates.statistics.report_elements.util.table.get_average_denominator_column_number";
include "templates.shared.util.convert_to_valid_node_name";
include "templates.shared.util.check_webserver_mode";
include "templates.shared.util.get_log_analysis_info_directory";
subroutine(report_builder_export, (
# export_to_file is true if invoked from the user_agent (result is saved in a file)
# export to_file is false if invoked from command_line (result is displayed in output stream)
bool export_to_file = false;
if (internal.task_originator eq "user_agent") then (
export_to_file = true;
);
string profile_name = internal.profile_name;
string profile_path = "profiles." . profile_name;
string report_name = internal.report.report_name;
node report_node = profile_path . ".statistics.reports." . report_name;
string report_label = node_value(subnode_by_name(report_node, "label"));
debug_message("EXPORT report name: " . report_name . "\n");
string report_element = internal.export.report_element;
# if no report_element is defined then export the first report_element
if (report_element eq "") then (
report_element = node_name(subnode_by_number(report_node . ".report_elements", 0));
);
debug_message("EXPORT report element: " . report_element . "\n");
node report_element_node = report_node . ".report_elements." . report_element;
string report_element_type = node_value(subnode_by_name(report_element_node, "type"));
debug_message("report element type: " . report_element_type . "\n\n");
# get report element label
string report_element_label;
if (subnode_exists(report_element_node, "label")) then (
report_element_label = node_value(subnode_by_name(report_element_node, "label"));
)
else (
report_element_label = report_label;
);
# -----------------------------------------------------------------------------------------------------
# SET PROGRESS FOR REPORT ELEMENT STEP (volatile.report_element_number = 0, +1 for each report element)
# -----------------------------------------------------------------------------------------------------
# Required for C++ progress handling only!
volatile.report_element_number = 0;
if (export_to_file) then (
templates.shared.doctype;
'\n';
'
\n';
'\n';
'
\n';
); # if export_to_file
# ----------
# Set filter
# ----------
set_filter_expression(report_name, report_element);
# -----------------------------------------
# Query the database for the report-element
# -----------------------------------------
if (node_exists("volatile.query_result")) then (
delete_node("volatile.query_result");
);
# debug_message("report element node: " . report_element_node . "\n");
query_db_for_view(report_element_node, "volatile.query_result");
string query_header = ("volatile.query_result.header");
string query_data = ("volatile.query_result.data");
# debug_message("query header: \n" . node_as_string(query_header) . "\n\n");
# debug_message("query data: \n" . node_as_string(query_data) . "\n\n");
# -----------------------------------------------
# Sort the query as in the report element display
# -----------------------------------------------
string sort_field = "";
string sort_direction = "";
string sort_type;
string sort_method;
if (node_exists(report_element_node . ".sort_by") and (node_value(report_element_node . ".sort_by") ne "")) then (
sort_field = node_value(report_element_node . ".sort_by");
sort_direction = (if node_exists(report_element_node . ".sort_direction") then $(report_element_node . ".sort_direction") else "descending");
sort_type = get_sort_type(profile_name, sort_field);
sort_method = "field:" . sort_field . "," . sort_type . "," . sort_direction;
sort(query_data, sort_method);
);
# --------------
# number of rows
# --------------
int number_of_rows;
# Note, due the huge amount a log_detail report doesn't show all records in the query_data node
# The number of rows in log data depends of the current set ending_row, if set to 50, the 50 rows
# will be in the query data.
number_of_rows = num_subnodes(query_data);
# debug_message("EXPORT number of rows query_data: " . number_of_rows);
node row;
node column;
string main_field_node_name;
string column_label;
string column_type;
string column_field_name;
string column_field_type;
string column_display_format_type;
string column_value;
int last_column_number = num_subnodes(query_header) - 1;
int column_number;
bool aggregation_method_is_average;
string average_denominator_field;
int average_denominator_column_number;
bool contains_delimiter;
bool contains_double_quote;
string csv_contents = "";
# -------------
# csv_delimiter
# -------------
string csv_delimiter = ",";
if (node_exists(profile_path . ".statistics.miscellaneous.csv_delimiter")) then (
string dat = node_value(profile_path . ".statistics.miscellaneous.csv_delimiter");
if (dat ne "") then (
csv_delimiter = dat;
);
);
int csv_delimiter_int = char_to_ascii(csv_delimiter);
string csv_delimiter_in_hex = format(csv_delimiter_int, '%X');
csv_delimiter_in_hex = "%" . csv_delimiter_in_hex;
debug_message("#### CSV delimiter: " . csv_delimiter . "\n");
debug_message("#### CSV delimiter in hex: " . csv_delimiter_in_hex . "\n");
# -------------
# csv_file_name
# -------------
if (export_to_file) then (
string csv_filename;
int date_time_stamp_in_sec = now();
debug_message("#### date_time_stamp_in_sec: " . date_time_stamp_in_sec . "\n");
if (report_name eq report_element) then (
csv_filename = report_element . "_" . date_time_stamp_in_sec . ".csv";
)
else (
csv_filename = report_name . "_" . report_element . "_" . date_time_stamp_in_sec . ".csv";
);
# debug_message("#### csv_filename: " . csv_filename . "\n");
); # if export_to_file
# ----------
# CSV header
# ----------
column_number = 0;
foreach column query_header (
column_label = node_value(subnode_by_name(column, "header_label"));
column_label = capitalize(expand(column_label));
if (column_number != last_column_number) then (
csv_contents .= column_label . csv_delimiter;
)
else (
csv_contents .= column_label . "\n";
);
column_number++;
); # foreach column query_header
# --------
# CSV data
# --------
for (int row_number = 1; row_number <= number_of_rows; row_number++) (
# Get the query row node for this table row
row = subnode_by_number(query_data, row_number - 1);
# Loop through all table columns (query_header columns), and generate HTML columns
column_number = 0;
foreach column query_header (
column_type = node_value(subnode_by_name(column, "type"));
column_field_name = node_value(subnode_by_name(column, "field_name"));
column_field_type = node_value(subnode_by_name(column, "data_type"));
column_value = node_value(subnode_by_name(row, column_field_name));
# ------------------
# Handle Aggregation
# ------------------
if (column_type eq "number") then (
# ------------------
# Handle Aggregation
# ------------------
# If aggregation method (read property from database field!) is of type average then get the
# average_denominator_field and divide the columnvalue through the value of the average_denominator_field.
aggregation_method_is_average = get_aggregation_method_is_average(column_field_name, profile_name);
if (aggregation_method_is_average) then (
average_denominator_field = get_average_denominator_field(column_field_name, profile_name);
column_value = column_value / node_value(subnode_by_name(row, average_denominator_field));
);
);
# ------------------------
# Decode __HexEsc__ values
# ------------------------
if (contains(column_value, "__HexEsc__")) then (
column_value = replace_all(column_value, "__HexEsc__", "%");
);
# ----------------------------------
# Escape delimiter and double quotes
# ----------------------------------
# debug_message("#### #### export value before escape: " . column_value . "\n");
contains_delimiter = contains(column_value, csv_delimiter_in_hex);
contains_double_quote = contains(column_value, "%22");
if (contains_delimiter or contains_double_quote) then (
# enclose column_value with double quotes!
column_value = '"' . column_value . '"';
# debug_message("#### #### escaped export value: " . column_value . "\n");
);
# ----------------
# Write csv string
# ----------------
csv_contents .= column_value;
if (column_number != last_column_number) then (
csv_contents .= csv_delimiter;
)
else (
csv_contents .= "\n";
);
column_number++;
); # foreach column query_header
); # for rows
# -----------------
# Get CSV directory
# -----------------
if (export_to_file) then (
string divider = "/";
string user_node_name = "none";
bool webserver_mode = check_webserver_mode();
if (node_exists("volatile.authenticated_user_node_path")) then (
user_node_name = node_name(volatile.authenticated_user_node_path);
);
string csv_directory_path;
string csv_sub_directory_path = "csv_export" . divider . user_node_name . divider . profile_name . divider . csv_filename;
if (webserver_mode) then (
string log_analysis_info_directory = get_log_analysis_info_directory();
debug_message("#### log_analysis_info_directory: " . log_analysis_info_directory . "\n");
csv_directory_path = log_analysis_info_directory . "WebServerRoot" . divider . csv_sub_directory_path;
)
else (
# cgi-mode
csv_directory_path = node_value("preferences.server.temporary_directory_pathname");
if (!ends_with(csv_directory_path, divider)) then (
csv_directory_path .= divider;
);
csv_directory_path .= csv_sub_directory_path;
);
debug_message("#### csv_directory_path: " . csv_directory_path . "\n");
); # if export_to_file
# ----------------------------------
# Write file / Display output_stream
# ----------------------------------
if (export_to_file) then (
csv_contents = convert_escapes(csv_contents);
# If charset conversion is enabled, do the conversion now
if (node_exists("profiles." . profile_name . ".output.convert_export_charset") and node_value("profiles." . profile_name . ".output.convert_export_charset")) then (
debug_message("Converted CSV export from " . output.convert_export_from_charset . " to " . output.convert_export_to_charset . "\n");
csv_contents = convert_charset(csv_contents, output.convert_export_from_charset, output.convert_export_to_charset);
debug_message("Done converting CSV export\n");
);
write_file(csv_directory_path, csv_contents);
)
else (
# Save CSV data in a node, the value of the node becomes the output_stream in report_setup.cvf
string csv_data_node_name = node_value("internal.export.csv_data_node_name");
("IPC." . csv_data_node_name) = convert_escapes(csv_contents);
save_node("IPC." . csv_data_node_name);
);
# -----------------
# Set download link
# -----------------
if (export_to_file) then (
string download_url = "csv_export/" . user_node_name . "/" . profile_name . "/" . csv_filename;
if (webserver_mode) then (
# download_url = "/csv_export/" . csv_filename;
download_url = "/" . download_url;
)
else (
# cgi-mode
string temporary_directory_url = node_value("preferences.server.temporary_directory_url");
if (!ends_with(temporary_directory_url, "/")) then (
temporary_directory_url .= "/";
);
download_url = temporary_directory_url . download_url;
);
debug_message("#### download_url: " . download_url . "\n");
'
\n';
'
\n';
'\n';
'\n';
); # if eport_to_file
));