#
# raw_graph_utilities.cfv
#
#
# get_raw_graph_query_header()
# get_raw_graph_legend_query_header()
# get_y_ticks_number_and_interval()
# set_raw_graph_y_data()
# get_raw_graph_x_data_info()
# set_raw_graph_legend()
#
#
# RAW CHRONO GRAPH UTILITIES
#
# get_raw_graph_chrono_main_field_info()
# get_raw_graph_number_of_variables_in_chrono_range()
# get_raw_graph_chrono_start_or_end_value()
# get_raw_graph_chrono_graph_query_header()
# get_raw_graph_chrono_next_time_unit()
#
# get_raw_graph_chrono_x_label()
# check_raw_graph_chrono_x_date_time_interval()
# get_chrono_variable_interval()
# get_raw_graph_chrono_x_data_info()
#
#
#
#
include "templates.util.date_time.date_time";
#
#
#
# get_raw_graph_query_header()
#
#
#
subroutine(get_raw_graph_query_header(
node base_query_header,
string report_field_name,
bool show_legend), (
debug_message("\n\n#### get_raw_graph_query_header() START \n\n");
# Creates a new query header which only contains the relevant columns for the given graph field
# and show_legend paramter;
delete_node("v.raw_graph_query_header");
v.raw_graph_query_header = "";
node column;
bool is_aggregating_field;
bool is_relevant_column;
string ssql_column_number;
foreach column base_query_header (
is_aggregating_field = @column{"is_aggregating_field"};
if (is_aggregating_field) then (
is_relevant_column = if (@column{"report_field_name"} eq report_field_name) then (true) else (false);
)
else (
is_relevant_column = if (show_legend) then (true) else (false);
);
if (is_relevant_column) then (
ssql_column_number = node_name(column);
clone_node(column, "v.raw_graph_query_header." . ssql_column_number);
);
);
debug_message("\n" . node_as_string("v.raw_graph_query_header") . "\n");
"v.raw_graph_query_header";
debug_message("\n\n#### get_raw_graph_query_header() END \n\n");
));
#
#
#
# get_raw_graph_legend_query_header()
#
#
#
subroutine(get_raw_graph_legend_query_header(
node base_query_header), (
debug_message("\n\n#### get_raw_graph_legend_query_header() START \n\n");
delete_node("v.raw_graph_legend_query_header");
v.raw_graph_legend_query_header = "";
# Create a new query header which only contains the non-aggregating fields of the table,
# this are the fields which are shown in the table
node column;
bool is_aggregating_field;
bool is_relevant_column;
string ssql_column_number;
foreach column base_query_header (
is_aggregating_field = @column{"is_aggregating_field"};
if (!is_aggregating_field) then (
ssql_column_number = node_name(column);
clone_node(column, "v.raw_graph_legend_query_header." . ssql_column_number);
);
);
debug_message("\n" . node_as_string("v.raw_graph_legend_query_header") . "\n");
"v.raw_graph_legend_query_header";
debug_message("\n\n#### get_raw_graph_legend_query_header() END \n\n");
));
subroutine(get_y_ticks_number_and_interval(
int original_y_height,
int approximate_tick_interval,
float max_variable_value,
int y_variable_interval), (
debug_message("\n\n#### get_y_ticks_number_and_interval() START \n");
# This subroutines computes the ticks on the y-axis by trying
# to come as close as possible to the image height so that multiple graphs
# have about the same height, which is important when displaying graphs
# side by side.
int number_of_ticks;
int y_height;
# Compute several y-axis and choose the one which fits best
node y_axis_candidates = new_node();
int number_of_candidates;
# Start with a lower tick interval
int y_tick_interval = approximate_tick_interval - 2;
if (y_tick_interval < 8) then (
# This is the minimum tick distance
y_tick_interval = 8;
);
int y_range;
int y_padding;
while (y_tick_interval <= (approximate_tick_interval + 2)) (
# Reset values
number_of_ticks = 0;
y_height = 0;
y_range = 0;
# Create a y-axis with as much ticks as possible
# for the given image height
while ((original_y_height - y_height) >= y_tick_interval) (
number_of_ticks = number_of_ticks + 1;
y_height = y_height + y_tick_interval;
y_range = y_range + y_variable_interval;
);
# Check if above iteration created a valid y-axis
if (y_range >= max_variable_value) then (
# This is a valid y-axis, add it to the y_axis_candidates
number_of_candidates = num_subnodes(y_axis_candidates);
@y_axis_candidates{number_of_candidates}{"y_tick_interval"} = y_tick_interval;
@y_axis_candidates{number_of_candidates}{"number_of_ticks"} = number_of_ticks;
@y_axis_candidates{number_of_candidates}{"y_height"} = y_height;
@y_axis_candidates{number_of_candidates}{"y_range"} = y_range;
@y_axis_candidates{number_of_candidates}{"y_padding"} = original_y_height - y_height;
# Start next iteration with an increased y_tick_interval
y_tick_interval = y_tick_interval + 1;
)
else (
last;
);
);
debug_message("#### y_axis_candidates:\n" . node_as_string(y_axis_candidates) . "\n");
#
# Choose the best candidate
#
# Get the items with the lowest y_padding
node item;
int lowest_y_padding = original_y_height;
foreach item y_axis_candidates (
if (@item{"y_padding"} < lowest_y_padding) then (
lowest_y_padding = @item{"y_padding"};
);
);
# We may further refine this, there could be multiple items with the same padding!
# If there are multiple items with the same lowest_y_padding then we choose the one
# which is most near to the initial approximate_tick_interval
int last_delta_from_approximate_tick_interval = 10;
int delta_from_approximate_tick_interval = 10;
foreach item y_axis_candidates (
if (@item{"y_padding"} == lowest_y_padding) then (
if (@item{"y_tick_interval"} >= approximate_tick_interval) then (
delta_from_approximate_tick_interval = @item{"y_tick_interval"} - approximate_tick_interval;
)
else (
delta_from_approximate_tick_interval = approximate_tick_interval - @item{"y_tick_interval"};
);
if (delta_from_approximate_tick_interval < last_delta_from_approximate_tick_interval) then (
last_delta_from_approximate_tick_interval = delta_from_approximate_tick_interval;
# Use this as final item
y_tick_interval = @item{"y_tick_interval"};
number_of_ticks = @item{"number_of_ticks"};
y_height = @item{"y_height"};
y_range = @item{"y_range"};
y_padding = @item{"y_padding"};
);
);
);
# Set return values
node n = new_node();
@n{"y_tick_interval"} = y_tick_interval;
@n{"number_of_ticks"} = number_of_ticks;
@n{"y_height"} = y_height;
@n{"y_range"} = y_range;
@n{"y_padding"} = y_padding;
# Return
n;
));
#
#
#
# subroutine set_raw_graph_y_data()
#
#
#
subroutine(set_raw_graph_y_data(
node y_data,
# int y_height,
int original_y_height,
float max_variable_value,
float _100_divided_by_total,
string display_format_type), (
debug_message("\n\n#### set_raw_graph_y_data() START \n");
# original_y_height is the height defined in the profile. The actual
# y_height will slightly differe, though it will be as close as possible
# to the original_y_height. The difference between original_y_height and
# y_height is defined as padding in the raw report element. The actual image height
# should consider y_height and padding so that all graph images have the same height.
# debug_message("\n");
debug_message("#### original_y_height: " . original_y_height . "\n");
debug_message("#### max_variable_value: " . max_variable_value . "\n");
# debug_message("#### _100_divided_by_total: " . _100_divided_by_total . "\n");
# debug_message("#### _100_divided_by_total * 1000000 = " . (_100_divided_by_total * 1000000) . "\n");
# debug_message("#### _100_divided_by_total * max_variable_value = " . (_100_divided_by_total * max_variable_value) . "\n");
# debug_message("\n");
#
#
# set approximate_tick_interval (in pixel)
#
#
int approximate_tick_interval;
if (original_y_height >= 69) then (
approximate_tick_interval = 19;
)
else if (original_y_height >= 46) then (
approximate_tick_interval = 17;
)
else if (original_y_height >= 27) then (
approximate_tick_interval = 15;
)
else if (original_y_height >= 23) then (
approximate_tick_interval = 13;
)
else (
approximate_tick_interval = 11;
);
debug_message("#### approximate_tick_interval: " . approximate_tick_interval . "\n");
#
#
# set approximate number of ticks
#
#
int approximate_number_of_ticks = original_y_height / approximate_tick_interval;
debug_message("#### approximate_number_of_ticks: " . approximate_number_of_ticks . "\n");
#
#
# Compute y variable interval
#
#
delete_node("volatile.temp.base_interval");
# I.e. 500, 1000, 1500, etc.
# Note, we need a different base interval for bytes!
if (display_format_type eq "bandwidth" and max_variable_value > 1000) then (
# base interval for bandwidth only if bandwidth max value greater 1000 bytes
bool search_y_variable_interval = true;
float k = 1024;
float y_variable_interval;
float j;
while (search_y_variable_interval) (
j = max_variable_value / k;
if (j <= approximate_number_of_ticks) then (
y_variable_interval = k;
search_y_variable_interval = false;
last;
);
k = k * 2;
);
)
else (
volatile.temp.base_interval.0 = 1;
volatile.temp.base_interval.1 = 2;
volatile.temp.base_interval.2 = 3;
volatile.temp.base_interval.3 = 5;
bool search_y_variable_interval = true;
int k = 1;
int y_variable_interval;
float j;
node item;
while (search_y_variable_interval) (
foreach item "volatile.temp.base_interval" (
j = max_variable_value / (node_value(item) * k);
if (j <= approximate_number_of_ticks) then (
y_variable_interval = node_value(item) * k;
search_y_variable_interval = false;
last;
);
);
k = k * 10;
);
);
debug_message("#### y_variable_interval: " . y_variable_interval . "\n");
#
#
# Compute final number of y ticks
#
#
node y_ticks_info = get_y_ticks_number_and_interval(
original_y_height,
approximate_tick_interval,
max_variable_value,
y_variable_interval);
debug_message("#### y_ticks_info (final data):\n" . node_as_string(y_ticks_info) . "\n");
int y_tick_interval = @y_ticks_info{"y_tick_interval"};
int number_of_ticks = @y_ticks_info{"number_of_ticks"};
int y_height = @y_ticks_info{"y_height"};
int y_range = @y_ticks_info{"y_range"};
int y_padding = @y_ticks_info{"y_padding"};
# float y_scale_max = 0.0;
# int number_of_final_ticks = 0;
#
# while (y_scale_max <= max_variable_value) (
#
# number_of_final_ticks++;
#
# y_scale_max = y_scale_max + y_variable_interval;
#
# # debug_message("\ny_scale_max: " . node_name(y_data) . " " . y_scale_max . "\n");
#
# );
#
#
# Compute final y tick interval
#
#
# Note, interval must be an even number!
# float y_tick_interval_draft = (y_height + 1) / number_of_final_ticks;
#
# int y_tick_interval;
#
# if (y_tick_interval_draft >= 17.4) then (
#
# y_tick_interval = 19;
# )
# else if (y_tick_interval_draft >= 15.4) then (
#
# y_tick_interval = 17;
# )
# else if (y_tick_interval_draft >= 13.4) then (
#
# y_tick_interval = 15;
# )
# else if (y_tick_interval_draft >= 11.4) then (
#
# y_tick_interval = 13;
# )
# else (
#
# y_tick_interval = 11;
# );
#
#
# Write values to y_data
#
#
set_subnode_value(y_data, "padding", y_padding); # padding is the difference between image height and y-axis height
set_subnode_value(y_data, "height", y_height);
set_subnode_value(y_data, "variable_interval", y_variable_interval);
set_subnode_value(y_data, "max_tick_value", y_range);
set_subnode_value(y_data, "tick_interval", y_tick_interval);
set_subnode_value(y_data, "number_of_ticks", number_of_ticks);
int count = 0;
int y_tick_value;
while (count < number_of_ticks) (
y_tick_value = y_variable_interval * (count + 1);
y_data . ".ticks." . count . ".label" = y_tick_value;
y_data . ".ticks." . count . ".percentage" = y_tick_value * _100_divided_by_total;
count++;
);
));
#
#
#
# subroutine get_raw_graph_x_data_info() (non-chrono graphs only!)
#
#
#
subroutine(get_raw_graph_x_data_info(
int x_length,
int number_of_variables,
bool is_remainder_variable), (
debug_message("\n\n#### get_raw_graph_x_data_info() START \n\n");
delete_node("v.temp_x_data_info");
v.temp_x_data_info = "";
node x_data_info = "v.temp_x_data_info";
set_subnode_value(x_data_info, "length", "");
set_subnode_value(x_data_info, "is_tick_on_center", true);
set_subnode_value(x_data_info, "is_label_on_tick_center", true);
#
#
# If is_remainder_variable increase the number_of_variables
#
#
if (is_remainder_variable) then (
number_of_variables += 1;
);
#
#
# Compute variable interval
#
#
# Note, the allowed variable interval for none chrological bar graphs
# is 6px - 32px, which results in a bar width of min 5px and max 31px.
# The variable interval must be an even number!
int variable_interval;
int draft_variable_interval = x_length / number_of_variables;
if (draft_variable_interval >= 32) then (
variable_interval = 32;
)
else if (draft_variable_interval >= 4) then (
variable_interval = draft_variable_interval;
if ((variable_interval % 2) != 0) then (
# subtruct 1 to get an even number
variable_interval--;
);
)
else (
# the variables don't fit on the available x_length, in this case
# set the variable interval to 6px and expand the x_length
variable_interval = 4;
x_length = 4 * number_of_variables;
);
# debug_message("#### variable_interval: " . variable_interval . "\n");
#
#
# Set final x_length and variable_interval
#
#
set_subnode_value(x_data_info, "length", x_length);
set_subnode_value(x_data_info, "variable_interval", variable_interval);
#
#
# Compute x-ticks
#
#
# The x_ticks specifies where ticks and labels have to be displayed,
# the x_ticks node also contains the labels to be displayed
#
# Get the x-tick interval
#
int x_tick_interval;
if (variable_interval >= 18) then (
x_tick_interval = 1;
)
else if (variable_interval >= 10) then (
x_tick_interval = 3;
)
else if (variable_interval >= 6) then (
x_tick_interval = 5;
)
else (
x_tick_interval = 10;
);
# debug_message("#### x_tick_interval: " . x_tick_interval . "\n");
set_subnode_value(x_data_info, "tick_interval", x_tick_interval);
#
#
# Create the x-ticks node
#
#
set_subnode_value(x_data_info, "ticks", "");
node x_ticks = x_data_info{"ticks"};
bool show_tick;
int x_tick_node_number;
for (int variable_count = 1; variable_count <= number_of_variables; variable_count++) (
show_tick = false;
if (variable_count > 1) then (
if (x_tick_interval == 3) then (
if ((variable_count % 2) != 0) then (
show_tick = true;
);
)
else (
if ((variable_count % x_tick_interval) == 0) then (
show_tick = true;
);
);
)
else (
show_tick = true;
);
# Note, ".show_label" has no special use here, but it has for chronolical graphs,
# and so the node is required within x_ticks
x_tick_node_number = variable_count - 1;
x_ticks . "." . x_tick_node_number . ".show_tick" = show_tick;
x_ticks . "." . x_tick_node_number . ".show_label" = show_tick;
#
# Add a x_ticks position node, it is actually set when the image
# is created.
#
x_ticks . "." . x_tick_node_number . ".position" = 0;
if (show_tick) then (
x_ticks . "." . x_tick_node_number . ".label" = variable_count;
);
);
#
#
# Return x_data_info
#
#
# debug_message("#### x_data_info:\n" . node_as_string(x_data_info) . "\n");
x_data_info;
));
#
#
#
# set_raw_graph_legend()
#
#
#
subroutine(set_raw_graph_legend(
node raw_variables,
node variables_in_percent,
float _100_divided_by_total,
bool is_remainder_variable,
node legend_query_header,
node raw_legend,
table working_table,
bool show_percent_in_legend,
int number_of_legend_rows,
bool is_xref_table), (
debug_message("\n#### set_raw_graph_legend START \n");
debug_message("\n" . node_as_string(legend_query_header) . "\n");
#
#
# The legend_query_header contains one or more non-aggregating fields,
# all given columns are relevant for the legend.
#
#
node column;
int column_count;
string column_value;
int item_number;
string database_field_number;
int ssql_column_number;
#
#
# Create the legend header
#
#
column_count = 0;
foreach column legend_query_header (
raw_legend . ".header." . column_count . ".report_field_name" = @column{"report_field_name"};
column_count++;
);
#
#
# Create the legend rows
#
#
set_subnode_value(raw_legend, "rows", "");
node raw_rows = raw_legend{"rows"};
for (int row_number = 0; row_number < number_of_legend_rows; row_number++) (
column_count = 0;
foreach column legend_query_header (
ssql_column_number = node_name(column);
if (is_xref_table) then (
column_value = table_get_cell_string_value(working_table, row_number, ssql_column_number);
)
else (
# This is a query from an xref table which does not contain string values.
# Get first the item number, then the string for the item number.
database_field_number = @column{"database_field_number"};
item_number = table_get_cell_value(working_table, row_number, ssql_column_number);
column_value = database_itemnum_to_item(database_field_number, item_number);
);
raw_rows . "." . row_number . "." . column_count = column_value;
column_count++;
);
if (show_percent_in_legend) then (
@variables_in_percent{row_number} = @raw_variables{row_number} * _100_divided_by_total;
);
);
if (show_percent_in_legend and is_remainder_variable) then (
# Set remainder in percent
int last_row_number = num_subnodes(raw_variables) - 1;
@variables_in_percent{last_row_number} = @raw_variables{last_row_number} * _100_divided_by_total;
);
));
#
#
#
#
#
#
# RAW CHRONO GRAPH UTILITIES
#
#
#
#
#
#
#
#
#
# get_raw_graph_chrono_main_field_info()
#
#
#
subroutine(get_raw_graph_chrono_main_field_info(
node base_query_header), (
# Returns the main field node of a chronological base_query_header
delete_node("v.temp_chrono_main_field_info");
v.temp_chrono_main_field_info = "";
node column;
int ssql_column_number;
foreach column base_query_header (
if (!(@column{"is_aggregating_field"})) then (
ssql_column_number = node_name(column);
clone_node(column, "v.temp_chrono_main_field_info");
set_subnode_value("v.temp_chrono_main_field_info", "ssql_column_number", ssql_column_number);
);
);
"v.temp_chrono_main_field_info";
));
#
#
#
# get_raw_graph_number_of_variables_in_chrono_range()
#
#
#
subroutine(get_raw_graph_number_of_variables_in_chrono_range(
string start_date,
string end_date,
string chrono_type), (
# This subroutine returns the number of variables between a start and end date
# for the specific chrono_type.
# Note, start_date and end_date is actually the start_time and end_time of date_filter_info in version 8,
# though not for day_of_week and hour_of_days!
subroutine(get_raw_graph_number_of_years(string start_date, string end_date), (
int start_year = substr(start_date, 7, 4);
int end_year = substr(end_date, 7, 4);
int the_year = start_year;
int number_of_years = 0;
while (the_year <= end_year) (
number_of_years++;
the_year++;
);
number_of_years;
));
int start_date_in_epoc;
int end_date_in_epoc;
int start;
int end;
int number_of_variables;
if (chrono_type eq "year") then (
number_of_variables = get_raw_graph_number_of_years(start_date, end_date);
)
else if (chrono_type eq "month") then (
int number_of_years = get_raw_graph_number_of_years(start_date, end_date);
int start_month = get_month_as_number(start_date);
int end_month = get_month_as_number(end_date);
if (number_of_years == 1) then (
number_of_variables = end_month - start_month + 1;
)
else if (number_of_years == 2) then (
number_of_variables = (13 - start_month) + end_month;
)
else (
number_of_variables = ((13 - start_month) + end_month) + (12 * (number_of_years - 2));
);
)
else if (chrono_type eq "day") then (
start_date_in_epoc = date_time_to_epoc(start_date);
end_date_in_epoc = date_time_to_epoc(end_date);
number_of_variables = ((end_date_in_epoc - start_date_in_epoc) / (24*60*60)) + 1;
)
else if (chrono_type eq "day_of_week") then (
# KHP 10/Apr/2012 - disabled day_of_week because this is not reliable since
# it is possible that there are day_of_week sequences which are not in chronological
# order.
# day_of_week are integers between 1 and 7.
# Note, start depends on number of first weekday, it could
# be any number between 1 and 7
start = start_date;
end = end_date;
int weekday_number = start;
number_of_variables = 0;
for (int i = 0; i < 7; i++) ( # maximum 7 iterations
# Increase number_of_variables
number_of_variables++;
if (weekday_number == end) then (
# Exit the loop
last;
)
else if (weekday_number == 7) then (
# Reset weekday_number to 0
weekday_number = 0;
)
else (
# Increase the weekday number
weekday_number++;
);
);
)
else if (chrono_type eq "hour_of_day") then (
# hour_of_day are integers
start = start_date;
end = end_date;
number_of_variables = (end - start) + 1;
)
else (
start_date_in_epoc = date_time_to_epoc(start_date);
end_date_in_epoc = date_time_to_epoc(end_date);
if (chrono_type eq "hour") then (
number_of_variables = ((end_date_in_epoc - start_date_in_epoc) / (60*60)) + 1;
)
else if (chrono_type eq "minute") then (
number_of_variables = ((end_date_in_epoc - start_date_in_epoc) / 60) + 1;
)
else if (chrono_type eq "second") then (
number_of_variables = (end_date_in_epoc - start_date_in_epoc) + 1;
);
);
number_of_variables;
));
#
#
#
# get_raw_graph_chrono_start_or_end_value()
#
#
#
subroutine(get_raw_graph_chrono_start_or_end_value(
string date_time_string,
string chrono_type), (
# This converts a date_time string to chrono_start or chrono_end value which matches
# the date format in the ssql table.
# I.e. if chrono type is day then "18/Feb/2007 00:00:00" becomes "18/Feb/2007 __:__:__"
string chrono_string;
if (chrono_type eq "year") then (
chrono_string = "__/___/" . substr(date_time_string, 7, 4) . " __:__:__";
)
else if (chrono_type eq "month") then (
chrono_string = "__/" . substr(date_time_string, 3, 8) . " __:__:__";
)
else if (chrono_type eq "day") then (
chrono_string = substr(date_time_string, 0, 11) . " __:__:__";
)
else if (chrono_type eq "hour") then (
chrono_string = substr(date_time_string, 0, 14) . ":__:__";
)
else if (chrono_type eq "minute") then (
chrono_string = substr(date_time_string, 0, 17) . ":__";
)
else (
# if second
chrono_string = date_time_string;
);
chrono_string;
));
#
#
#
# get_raw_graph_chrono_graph_query_header()
#
#
#
subroutine(get_raw_graph_chrono_graph_query_header(
node base_query_header), (
#
# Returns a query header which contains only the aggregating fields for which
# we create a graph
#
delete_node("v.temp_chrono_graph_query_header");
v.temp_chrono_graph_query_header = "";
node column;
int ssql_column_number;
foreach column base_query_header (
if (@column{"is_aggregating_field"} and @column{"create_graph"}) then (
ssql_column_number = node_name(column);
clone_node(column, "v.temp_chrono_graph_query_header." . ssql_column_number);
);
);
"v.temp_chrono_graph_query_header";
));
#
#
#
# get_raw_graph_chrono_next_time_unit()
#
#
#
subroutine(get_raw_graph_chrono_next_time_unit(
string calendar_date,
string chrono_type), (
# calendar_date is a date in Salang format
int number_unit;
string next_time_unit;
if (chrono_type eq "year") then (
int year = get_year(calendar_date);
year = year + 1;
next_time_unit = "__/___/" . year . " __:__:__";
)
else if (chrono_type eq "month") then (
int year = get_year(calendar_date);
int month_number = get_month_as_number(calendar_date);
if (month_number != 12) then (
month_number = month_number + 1;
)
else (
month_number = 1;
year = year + 1;
);
string month_string = get_month_as_string(month_number);
next_time_unit = "__/" . month_string . "/" . year . " __:__:__";
)
else if (chrono_type eq "day_of_week") then (
number_unit = calendar_date;
if (number_unit < 7) then (
next_time_unit = number_unit + 1;
)
else (
# number_unit is 7, start over with 1 because day of week exists from 1-7
# This is required because every day of week (1-7) could
# be the first day of week.
next_time_unit = 1;
);
)
else if (chrono_type eq "hour_of_day") then (
number_unit = calendar_date;
next_time_unit = number_unit + 1;
)
else (
string calendar_date_in_epoc = date_time_to_epoc(calendar_date);
int date_time_increment = 0;
if (chrono_type eq "day") then (
date_time_increment = 60 * 60 * 24;
)
else if (chrono_type eq "hour") then (
date_time_increment = 60 * 60;
)
else if (chrono_type eq "minute") then (
date_time_increment = 60;
)
else if (chrono_type eq "second") then (
date_time_increment = 1;
);
next_time_unit = epoc_to_date_time(calendar_date_in_epoc + date_time_increment);
if (chrono_type eq "day") then (
next_time_unit = substr(next_time_unit, 0, 11) . " __:__:__";
)
else if (chrono_type eq "hour") then (
next_time_unit = substr(next_time_unit, 0, 15) . "__:__";
)
else if (chrono_type eq "minute") then (
next_time_unit = substr(next_time_unit, 0, 18) . "__";
);
);
next_time_unit;
));
#
#
#
# get_raw_graph_chrono_x_label
#
#
#
subroutine(get_raw_graph_chrono_x_label(
string draft_label,
string x_label_format,
string main_field_category), (
# Returns the final chronological x_label
# Note, the labels are saved in the raw_graph and language independent, so we must use lang variables!
debug_message("\n#### get_raw_graph_chrono_x_label() - draft_label: " . draft_label . "\n");
debug_message("#### get_raw_graph_chrono_x_label() - x_label_format: " . x_label_format . "\n");
debug_message("#### get_raw_graph_chrono_x_label() - main_field_category: " . main_field_category . "\n");
string label;
if (main_field_category eq "date_time") then (
# ---------
# date_time
# ---------
# -------------------------------
# get individual date/time values
# -------------------------------
# Note, the draft label is in Salang date_time format
if (node_exists("volatile.temp_split")) then (
delete_node("volatile.temp_split");
);
string year = substr(draft_label, 7, 4);
string month_as_string = substr(draft_label, 3, 3);
debug_message("#### month_as_string: " . month_as_string . "\n");
string month_label = if (month_as_string ne "___") then ("$lang_stats.months_short." . month_as_string) else ("___");
int month_as_number = if (month_as_string ne "___") then (get_month_as_number(draft_label)) else (0);
string day = substr(draft_label, 0, 2);
string hh = substr(draft_label, 12, 2);
string mm = substr(draft_label, 15, 2);
string ss = substr(draft_label, 18, 2);
# -----------
# Get weekday
# -----------
string the_weekday;
if (contains(x_label_format, "wd")) then (
int the_weekday_number = get_weekday(year, month_as_number, day);
the_weekday = "$lang_stats.weekdays_twoletter." . the_weekday_number;
);
# -------------------
# Compose final label
# -------------------
if (x_label_format eq "yyyy") then (
label = year;
)
else if (x_label_format eq "mmm_yy") then (
label = month_label . " " . substr(year, 2, 2);
)
else if (x_label_format eq "mmm_yyyy") then (
label = month_label . " " . year;
)
else if (x_label_format eq "dd_mmm") then (
label = day . " " . month_label;
)
else if (x_label_format eq "dd_mmm_yyyy") then (
label = day . " " . month_label . " " . year;
)
else if (x_label_format eq "dd_mmm_yy") then (
label = day . " " . month_label . " " . substr(year, 2, 2);
)
else if (x_label_format eq "wd_dd_mmm_yyyy") then (
label = the_weekday . " " . day . " " . month_label . " " . year;
)
else if (x_label_format eq "hh") then (
label = hh . ":";
)
else if (x_label_format eq "dd_mmm_hh") then (
label = day . " " . month_label . " " . hh . ":";
)
else if (x_label_format eq "hh_dd_mmm") then (
label = hh . ":
(" . day . " " . month_label . ")";
)
else if (x_label_format eq "mm") then (
label = ":" . mm;
)
else if (x_label_format eq "hh_mm_dd_mmm") then (
label = hh . ":" . mm . "
(" . day . " " . month_label . ")";
)
else if (x_label_format eq "ss") then (
label = ":" . ss;
)
else if (x_label_format eq "hh_mm_ss_dd_mmm") then (
label = ":" . ss . "
(" . day . " " . month_label . " " . hh . ":" . mm . ")";
);
)
else if (main_field_category eq "day_of_week") then (
# label = node_value(subnode_by_name("lang_stats.weekdays_twoletter", draft_label));
# KHP 20/Sep/2011 - a wrong draft_label may cause problems, check if the label actually exists
if (?("lang_stats.weekdays_twoletter." . draft_label)) then (
label = "$lang_stats.weekdays_twoletter." . draft_label;
)
else (
label = "unknown";
);
)
else if (main_field_category eq "hour_of_day") then (
# label = node_value(subnode_by_name("lang_stats.hours_on_graph", draft_label));
# KHP 20/Sep/2011 - a wrong draft_label may cause problems, check if the label actually exists
if (?("lang_stats.hours_on_graph." . draft_label)) then (
label = "$lang_stats.hours_on_graph." . draft_label;
)
else (
label = "unknown";
);
);
# debug_message("#### get_raw_graph_chrono_x_label() - label: " . label . "\n\n");
# KHP 10/Jun/2010, don't expand the label because labels must be language independant in the raw report element!
# label = expand(label);
label;
));
#
#
#
# check_raw_graph_chrono_x_date_time_interval()
#
#
#
subroutine(check_raw_graph_chrono_x_date_time_interval(
string draft_label,
string x_date_time_interval), (
# debug_message("\n\n#### check_raw_graph_chrono_x_date_time_interval() START \n");
# debug_message("#### draft_label: " . draft_label . "\n");
# debug_message("#### x_date_time_interval: " . x_date_time_interval . "\n");
bool x_date_time_interval_match = false;
if (x_date_time_interval eq "first_day") then (
string day_as_string = substr(draft_label, 0, 2);
if (day_as_string eq "01" or day_as_string eq "1") then (
x_date_time_interval_match = true;
);
)
else if (x_date_time_interval eq "first_month") then (
string month_as_string = substr(draft_label, 3, 3);
int month_as_number = if (month_as_string ne "___") then (get_month_as_number(draft_label)) else (0);
debug_message("#### month_as_string: " . month_as_string . "\n");
debug_message("#### month_as_number: " . month_as_number . "\n");
if (month_as_number == 1) then (
x_date_time_interval_match = true;
);
);
# debug_message("#### x_date_time_interval_match: " . x_date_time_interval_match . "\n");
x_date_time_interval_match;
));
#
#
#
# get_chrono_variable_interval()
#
#
#
subroutine(get_chrono_variable_interval(
int x_length,
int number_of_variables_in_graph), (
debug_message("\n\n#### get_chrono_x_variable_interval() \n");
# Returns the chrono variable_interval and the given or a slightly modified x_length.
# Possible variable intervals are 1px up to 42px
node n = new_node();
int variable_interval;
int max_variable_interval = 42;
int draft_variable_interval = x_length / number_of_variables_in_graph;
debug_message("#### x draft_variable_interval: " . draft_variable_interval . "\n");
if (draft_variable_interval >= max_variable_interval) then (
variable_interval = max_variable_interval;
)
else if (draft_variable_interval == 1) then (
variable_interval = 1;
)
else (
variable_interval = draft_variable_interval;
# If variable_interval is not an even number then get one
# KHP 09/Mar/2011 - Disabled because this may shorten the
# graph coverage too much!
#
# if ((variable_interval % 2) != 0) then (
# subtruct 1 to get an even number
# variable_interval--;
# );
);
# Return
variable_interval;
));
#
#
#
# subroutine get_raw_graph_chrono_x_data_info() (chrono graphs only!)
#
#
#
subroutine(get_raw_graph_chrono_x_data_info(
string chrono_query_data,
bool is_special_chrono_graph,
string main_field_report_field_name,
string main_field_category,
string chrono_type,
int x_length,
int number_of_variables_in_graph,
int number_of_variables_in_chrono_range,
node every_1st_day_on_graph_lookup), (
debug_message("\n\n#### get_raw_graph_chrono_x_data_info() \n");
delete_node("v.temp_x_data_info");
v.temp_x_data_info = "";
node x_data_info = "v.temp_x_data_info";
debug_message("\n\n#### chrono_query_data 1:\n" . node_as_string(chrono_query_data) . "\n");
#
#
#
# Create a temp_x_ticks node with draft labels
#
#
#
delete_node("v.temp_x_ticks");
v.temp_x_ticks = "";
node temp_x_ticks = "v.temp_x_ticks";
node query_item;
# int query_item_count = 1; # Note, we use 1 because we use the old r7-1 code. We write the x_ticks later to final output and start with 0!
int query_item_count = 0;
string draft_label;
foreach query_item chrono_query_data (
if (!is_special_chrono_graph) then (
draft_label = node_name(query_item);
)
else (
draft_label = @query_item{main_field_report_field_name};
);
"v.temp_x_ticks." . query_item_count . ".draft_label" = draft_label;
query_item_count++;
);
#
#
#
# Compute variable interval
#
#
#
debug_message("#### x_length: " . x_length . "\n");
int variable_interval = get_chrono_variable_interval(x_length, number_of_variables_in_graph);
debug_message("#### final x variable_interval: " . variable_interval . "\n");
#
#
# Build x_ticks
#
#
# The x_ticks specifies where ticks and labels have to be displayed,
# the x_ticks node also contains the labels to be displayed
# -----------------------
# Get the x-tick interval
# -----------------------
bool x_number_interval;
bool x_date_time_interval;
# Note, a x_tick_interval of "0" means that we have a date_time_interval, i.e. "first_day"
int x_tick_interval = 0;
string x_date_time_interval;
int x_label_interval;
string x_label_format;
bool x_tick_on_center = true; # override value below if different!
bool x_label_on_tick_center = true; # override value below if different!
# string start_hour; # used to check the first label, on each repeating start hour we display the full date
# string start_minute;
# string start_second;
bool use_specific_date_time_interval = false;
if (chrono_type eq "year") then (
x_label_format = "yyyy";
if (variable_interval >= 32) then (x_tick_interval = 1; x_label_interval = 1;)
else if (variable_interval >= 20) then (x_tick_interval = 1; x_label_interval = 2;)
else if (variable_interval >= 15) then (x_tick_interval = 1; x_label_interval = 3;)
else if (variable_interval >= 10) then (x_tick_interval = 1; x_label_interval = 4;)
else if (variable_interval > 5) then (x_tick_interval = 1; x_label_interval = 5;)
else (x_tick_interval = 4; x_label_interval = 12;);
)
else if (chrono_type eq "month") then (
if (variable_interval >= 34) then (x_tick_interval = 1; x_label_interval = 1; x_label_format = "mmm_yyyy";)
else if (variable_interval >= 30) then (x_tick_interval = 1; x_label_interval = 1; x_label_format = "mmm_yy";)
else if (variable_interval >= 18) then (x_tick_interval = 3; x_label_interval = 3; x_label_format = "mmm_yyyy";)
else if (variable_interval >= 12) then (x_tick_interval = 4; x_label_interval = 4; x_label_format = "mmm_yyyy";)
else if (variable_interval >= 10) then (x_tick_interval = 6; x_label_interval = 6; x_label_format = "mmm_yyyy";)
else if (variable_interval >= 5) then (x_tick_interval = 12; x_label_interval = 12; x_label_format = "mmm_yyyy";)
else if (variable_interval >= 3) then (x_tick_interval = 12; x_label_interval = 24; x_label_format = "mmm_yyyy";)
else if (variable_interval == 2) then (x_tick_interval = 12; x_label_interval = 36; x_label_format = "mmm_yyyy";)
else (x_tick_interval = 60; x_label_interval = 60; x_label_format = "mmm_yyyy";);
# else (x_date_time_interval = "first_month"; x_label_format = "yyyy"; x_tick_on_center = false; x_label_on_tick_center = false;);
)
else if (chrono_type eq "day") then (
if (variable_interval == 42) then (x_tick_interval = 1; x_label_interval = 1; x_label_format = "wd_dd_mmm_yyyy";)
else if (variable_interval >= 24) then (x_tick_interval = 3; x_label_interval = 3; x_label_format = "wd_dd_mmm_yyyy";)
else if (variable_interval >= 10) then (x_tick_interval = 7; x_label_interval = 7; x_label_format = "wd_dd_mmm_yyyy";)
else if (variable_interval >= 6) then (x_tick_interval = 7; x_label_interval = 14; x_label_format = "wd_dd_mmm_yyyy";)
else (
use_specific_date_time_interval = true;
);
)
else if (chrono_type eq "hour") then (
x_label_format = "hh";
if (variable_interval >= 20) then (x_tick_interval = 1; x_label_interval = 1;)
else if (variable_interval >= 12) then (x_tick_interval = 1; x_label_interval = 3;)
else if (variable_interval >= 10) then (x_tick_interval = 1; x_label_interval = 4;)
else if (variable_interval >= 4) then (x_tick_interval = 4; x_label_interval = 8;)
else (
# We may be at 1px variable interval!
x_tick_interval = 24;
x_label_interval = 24;
);
)
else if (chrono_type eq "minute") then (
x_label_format = "mm";
if (variable_interval >= 20) then (x_tick_interval = 1; x_label_interval = 1;)
else if (variable_interval >= 6) then (x_tick_interval = 1; x_label_interval = 5;)
else if (variable_interval >= 3) then (x_tick_interval = 10; x_label_interval = 10;)
else (x_tick_interval = 30; x_label_interval = 60;);
)
else if (chrono_type eq "second") then (
x_label_format = "ss";
if (variable_interval >= 20) then (x_tick_interval = 1; x_label_interval = 1;)
else if (variable_interval >= 6) then (x_tick_interval = 1; x_label_interval = 5;)
else if (variable_interval >= 3) then (x_tick_interval = 10; x_label_interval = 10;)
else (x_tick_interval = 30; x_label_interval = 60;);
)
else if (chrono_type eq "day_of_week") then (
x_label_format = "";
if (variable_interval >= 22) then (x_tick_interval = 1; x_label_interval = 1; )
else if (variable_interval >= 10) then (x_tick_interval = 2; x_label_interval = 2;)
else if (variable_interval >= 7) then (x_tick_interval = 3; x_label_interval = 3;)
else if (variable_interval >= 5) then (x_tick_interval = 4; x_label_interval = 4;)
else (x_tick_interval = 7; x_label_interval = 7;);
)
else if (chrono_type eq "hour_of_day") then (
x_label_format = "";
x_tick_on_center = false;
if (variable_interval == 42) then (x_tick_interval = 1; x_label_interval = 1;)
else if (variable_interval >= 22) then (x_tick_interval = 2; x_label_interval = 2;)
else if (variable_interval >= 14) then (x_tick_interval = 4; x_label_interval = 4;)
else if (variable_interval >= 8) then (x_tick_interval = 6; x_label_interval = 6;)
else (x_tick_interval = 12; x_label_interval = 12;);
);
debug_message("\n\n#### use_specific_date_time_interval: " . use_specific_date_time_interval . "\n");
debug_message("#### chrono_type: " . chrono_type . "\n");
debug_message("#### variable_interval: " . variable_interval . "\n");
debug_message("#### x_tick_interval: " . x_tick_interval . "\n");
debug_message("#### x_label_interval: " . x_label_interval . "\n");
debug_message("#### x_date_time_interval: " . x_date_time_interval . "\n\n");
#
#
#
# Set x-ticks
#
#
#
node variable;
bool show_tick;
bool show_label;
string label;
if (!use_specific_date_time_interval) then (
if (x_tick_interval > 0) then (
# ------------------------------
# x_ticks with a number interval
# ------------------------------
int variable_count = 1;
int next_x_tick_count = 0;
int next_x_label_count = 0;
# Follwoing variables are used for hours, minutes and seconds.
# I.e.; if the day changes in hours display then we display the
# full date!
string last_day;
string this_day;
string last_hour;
string this_hour;
string last_minute;
string this_minute;
foreach variable temp_x_ticks (
debug_message("\n\n#### variable_count: " . variable_count . "\n");
show_tick = false;
show_label = false;
label = "";
if ((variable_count > 1) and (x_tick_interval > 1)) then (
if (variable_count == next_x_tick_count) then (
show_tick = true;
next_x_tick_count = variable_count + x_tick_interval;
);
)
else (
show_tick = true;
next_x_tick_count = variable_count + x_tick_interval;
);
if ((variable_count > 1) and (x_label_interval > 1)) then (
if (variable_count == next_x_label_count) then (
show_label = true;
draft_label = node_value(subnode_by_name(variable, "draft_label"));
label = get_raw_graph_chrono_x_label(draft_label, x_label_format, main_field_category);
next_x_label_count = variable_count + x_label_interval;
);
)
else (
show_label = true;
draft_label = node_value(subnode_by_name(variable, "draft_label"));
label = get_raw_graph_chrono_x_label(draft_label, x_label_format, main_field_category);
next_x_label_count = variable_count + x_label_interval;
);
#
# Modify label if chrono_type is hour and if the day changes.
# In this case we include the entire date in the label!
#
if (show_label and chrono_type eq "hour") then (
this_day = substr(draft_label, 0, 11);
if (this_day ne last_day) then (
label = get_raw_graph_chrono_x_label(draft_label, "hh_dd_mmm", main_field_category);
last_day = this_day;
);
);
if (show_label and chrono_type eq "minute") then (
this_hour = substr(draft_label, 0, 14);
if (this_hour ne last_hour) then (
label = get_raw_graph_chrono_x_label(draft_label, "hh_mm_dd_mmm", main_field_category);
last_hour = this_hour;
);
);
if (show_label and chrono_type eq "second") then (
this_minute = substr(draft_label, 0, 17);
if (this_minute ne last_minute) then (
label = get_raw_graph_chrono_x_label(draft_label, "hh_mm_ss_dd_mmm", main_field_category);
last_minute = this_minute;
);
);
variable . ".show_tick" = show_tick;
variable . ".show_label" = show_label;
variable . ".label" = label;
variable_count++;
);
) # if x_tick_interval > 0
else (
# ---------------------------------
# x_ticks with a date_time interval
# ---------------------------------
debug_message("#### x_ticks with a date_time interval #### \n");
# i.e. tick on every first day of month, or on every first month of year, etc.
# ticks are displayed on spaces (on left bar and right bar), labels are displayed between ticks.
foreach variable temp_x_ticks (
label = "";
show_tick = check_raw_graph_chrono_x_date_time_interval(node_value(subnode_by_name(variable, "draft_label")), x_date_time_interval);
if (show_tick) then (
label = get_raw_graph_chrono_x_label(node_value(subnode_by_name(variable, "draft_label")), x_label_format, main_field_category);
);
debug_message("#### show_tick: " . show_tick . "\n");
debug_message("#### label: " . label . "\n");
variable . ".show_tick" = show_tick;
variable . ".show_label" = show_tick;
variable . ".label" = label;
);
);
)
else (
#
# use_specific_date_time_interval
#
# This is the case if we have sampled data or a small variable interval where we want to show
# the tick and label on the bar center but the ticks actually follow a time interval, i.e. every
# first day of a month. Though due the sampling of the data it is possible that no "1st" day draft
# label exists. Hence we have to find all variables which approximately indicate the required date
# or time interval, i.e. find the variables which indicate the 1st day of each month.
# NOTE, a node with every first day of the month exists, it is "every_1st_day_on_graph_lookup"
if (chrono_type eq "day") then (
# We need the approximate x-segment-length which presents 31 days,
# depending on this length we defne the x-tick and x-label positions.
int max_occpied_x_length = variable_interval * number_of_variables_in_graph;
float number_of_months = number_of_variables_in_chrono_range / 30.5;
float x_length_per_month = max_occpied_x_length / number_of_months;
#
# get the x_label_interval
#
int x_label_interval;
if (x_length_per_month > 30) then (
x_label_interval = 1;
)
else if (x_length_per_month > 14) then (
x_label_interval = 2;
)
else if (x_length_per_month > 7) then (
x_label_interval = 6;
)
else if (x_length_per_month > 2) then (
x_label_interval = 12;
)
else (
x_label_interval = 24;
);
debug_message("\n\n@@@@ max_occpied_x_length: " . max_occpied_x_length . "\n");
debug_message("@@@@ number_of_months: " . number_of_months . "\n");
debug_message("@@@@ x_length_per_month: " . x_length_per_month . "\n");
debug_message("@@@@ x_label_interval: " . x_label_interval . "\n");
debug_message("\n\n every_1st_day_on_graph_lookup 1:\n" . node_as_string(every_1st_day_on_graph_lookup) . "\n");
debug_message("\n\n#### temp_x_ticks 1:\n" . node_as_string(temp_x_ticks) . "\n");
# We show a tick on each new month start. If the start_date is not the 1st day of the month then
# we don't show a tick.
int x_label_count = x_label_interval - 1; # x_label_interval - 1 ensures that we set a label on the first tick!
string variable_node_name;
string draft_label;
string label;
string this_label_year;
string last_label_year;
string date_format;
foreach variable temp_x_ticks (
variable_node_name = node_name(variable);
show_tick = false;
show_label = false;
draft_label = "";
label = "";
if (?(every_1st_day_on_graph_lookup . "." . variable_node_name . ".draft_label") and
(@(every_1st_day_on_graph_lookup . "." . variable_node_name . ".draft_label") ne "")) then (
draft_label = @(every_1st_day_on_graph_lookup . "." . variable_node_name . ".draft_label");
# KHP 10/Mar/2011 - show tick only with label, otherwise
# we get too many ticks on date ranges which spawn several years
# show_tick = true;
x_label_count = x_label_count + 1;
if (x_label_count == x_label_interval) then (
show_tick = true;
show_label = true;
x_label_count = 0;
this_label_year = substr(draft_label, 7, 4);
if (this_label_year eq last_label_year) then (
date_format = "dd_mmm";
)
else (
date_format = "dd_mmm_yyyy";
);
last_label_year = this_label_year;
label = get_raw_graph_chrono_x_label(draft_label, date_format, main_field_category);
);
);
variable . ".show_tick" = show_tick;
variable . ".show_label" = show_label;
variable . ".label" = label;
# debug_message("\n\n\n#### variable_node_name: " . variable_node_name . "\n");
# debug_message("#### x_label_count: " . x_label_count . "\n");
# debug_message("#### draft_label: " . draft_label . "\n");
# debug_message("#### label: " . label . "\n");
# debug_message("#### show_label: " . show_label . "\n");
# debug_message("#### show_label: " . show_label . "\n\n\n");
);
);
);
debug_message("\n\n#### temp_x_ticks 2:\n" . node_as_string(temp_x_ticks) . "\n");
#
#
#
#
# Write all x data to x_data_info
#
#
#
#
set_subnode_value(x_data_info, "length", x_length);
set_subnode_value(x_data_info, "is_tick_on_center", x_tick_on_center);
set_subnode_value(x_data_info, "is_label_on_tick_center", x_label_on_tick_center);
set_subnode_value(x_data_info, "variable_interval", variable_interval);
set_subnode_value(x_data_info, "tick_interval", x_tick_interval);
int x_data_info_ticks_count = 0;
node temp_x_tick_item;
foreach temp_x_tick_item temp_x_ticks (
x_data_info . ".ticks." . x_data_info_ticks_count . ".show_tick" = @temp_x_tick_item{"show_tick"};
x_data_info . ".ticks." . x_data_info_ticks_count . ".show_label" = @temp_x_tick_item{"show_label"};
x_data_info . ".ticks." . x_data_info_ticks_count . ".label" = @temp_x_tick_item{"label"};
x_data_info_ticks_count++;
);
#
#
# Return x_data_info
#
#
debug_message("\n#### x_data_info:\n" . node_as_string(x_data_info) . "\n");
x_data_info;
));