# # 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; string table_field_type; string category; # # # 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); table_field_type = @column{"table_field_type"}; if (table_field_type eq "database_itemnum" or table_field_type eq "custom_itemnum") then ( 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); ); ) else ( # This is most likely a date_time timestamp field, however, # for any case we handle any numerical field. category = @column{"category"}; if (category ne "date_time") then ( column_value = table_get_cell_value(working_table, row_number, ssql_column_number); ) else ( # This must be a date_time timestamp, convert from epoc (int) to date_time column_value = epoc_to_date_time(table_get_cell_value(working_table, row_number, ssql_column_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; ));