// Author: Robert Rusecki // Company: Kolmisoft // Year: 2020 // About: Script generates table with rates data #define SCRIPT_VERSION "1.1.8" #define SCRIPT_NAME "m2_rates_notification_data" #define error_checker(n) _error_checker(n, __func__) #include "m2_functions.c" #define DEBUG 0 #define UPDATE_BATCH_SIZE 50 #define UPDATE_BUFFER_SIZE UPDATE_BATCH_SIZE * 1000 #if DEBUG == 1 #define m2_dbg_1(M, ...) {char m2_log_buffer[10000] = ""; sprintf(m2_log_buffer, M, ##__VA_ARGS__); _m2_log(m2_log_buffer, 0); m2_log_buffer[0] = 0;} #else #define m2_dbg_1(M, ...) {} #endif typedef struct rate_struct { char id[21]; int active; struct rate_struct *next; char prefix[64]; char r_id[21]; double rate; double connection_fee; char effective_from[64]; int increment_s; int min_time; int blocked; int dst_grp_id; int dst_id; char dst_grp_name[64]; char dst_name[64]; double raw_rate; double percent_rate; int is_new; } rate_t; typedef struct rates_diff_struct { char id[21]; int number; double raw_rate; double percent_rate; int rate_assigned; struct main_node_struct *child; } rate_diff_t; typedef struct main_node_struct { rate_diff_t *sub_node[10]; } main_node_t; // FUNCTIONS DECLARATION int create_new_table(); int insert_data_into_table(); int insert_batch(); int get_rnj_data(); int get_rates(); int update_rnj_status(int state, char *status); rate_t *initialize_rate(int id); int _error_checker(int return_code, char const * caller_name); int flush_table(); int get_diff_rates(); int add_rate(main_node_t *main_n, char *rate_id, double raw_rate, double percent_rate); void free_rates(); rate_diff_t *trie_add_subnode(); int trie_null_subnodes(main_node_t *main_n); rate_diff_t *find_rate_diff(main_node_t *main_node, char *rate); int is_allowed_number(int n); main_node_t *initialize_main_node(); // GLOBAL VARIABLES DECLARATION int tariff_id = 0; int rnj_id = 0; int rnj_type = 0; int changes_present = 0; rate_t *rates_head; main_node_t *root; // MAIN FUNCTION int main(int argc, char *argv[]) { // starting sript m2_init("Starting rates effective from cache script\n"); if (argc == 2) { rnj_id = atoi(argv[1]); } else { m2_log("Bad arguments \n"); return error_checker(-1); } rates_head = initialize_rate(0); root = initialize_main_node(); get_rnj_data(); get_diff_rates(); get_rates(); create_new_table(); flush_table(); insert_data_into_table(); if (changes_present) { update_rnj_status(0," "); } else { update_rnj_status(2," "); } // Free data free_rates(); m2_log("Script completed\n"); return 0; } /* Insert rates into table */ int insert_data_into_table() { char insert_sql[UPDATE_BUFFER_SIZE] = ""; rate_t *current_rate = rates_head; int n = 0; int total_count = 0; m2_log("Inserting Data\n"); while (current_rate->next != NULL && current_rate->active == 1) { char insert[1000] = ""; sprintf(insert, "('%s','%s','%s','%s','%lf','%lf','%d','%d','%d','%d','%s','%d','%s','%d','%lf','%lf')", current_rate->id, current_rate->r_id, current_rate->prefix, current_rate->effective_from, current_rate->raw_rate, current_rate->percent_rate, current_rate->increment_s, current_rate->min_time, current_rate->blocked, current_rate->dst_grp_id, current_rate->dst_grp_name, current_rate->dst_id, current_rate->dst_name, current_rate->is_new, current_rate->rate, current_rate->connection_fee); m2_dbg_1("Insert: %s\n", insert); if (n == 0) { memset(insert_sql, 0, sizeof(insert_sql)); strcpy(insert_sql, insert); } else { strcat(insert_sql, ","); strcat(insert_sql, insert); } n++; total_count++; if (n == UPDATE_BATCH_SIZE) { m2_dbg_1("Inserting data count: %d\n", n); insert_batch(insert_sql); m2_dbg_1("DATA inserted\n"); m2_dbg_1("Total inserted data count: %d\n", total_count); n = 0; } current_rate = current_rate->next; } if (n > 0) { m2_dbg_1("Inserting data number: %d\n", n); insert_batch(insert_sql); m2_dbg_1("DATA inserted\n"); } if (total_count) { changes_present = 1; } return error_checker(0); } /* Insert batch of rates */ int insert_batch(char *sql) { char query[UPDATE_BUFFER_SIZE] = ""; sprintf(query, "INSERT INTO rate_notification_data_%d (rate_id, ratedetail_id, prefix, effective_from, " "rate_difference_raw, rate_difference_percentage, increment, minimal_time, is_blocked, " "destinationgroup_id, destinationgroup_name, destination_id, destination_name, is_new, rate, connection_fee) VALUES %s", rnj_id, sql); if (m2_mysql_query(query)) { return error_checker(-1); } return error_checker(0); } /* Get rate notification job data */ int get_rnj_data() { MYSQL_RES *result; MYSQL_ROW row; char query[1024] = ""; m2_log("Getting rate notification job data\n"); sprintf(query, "SELECT tariff_id, rate_notification_type FROM rate_notification_jobs WHERE id = %d", rnj_id); if (m2_mysql_query(query)) { return error_checker(-1); } result = mysql_store_result(&mysql); if (result) { row = mysql_fetch_row(result); if (row == NULL) { error_checker(-4); } if (row[0]) { tariff_id = atoi(row[0]); } else { error_checker(-2); } if (row[1]) { rnj_type = atoi(row[1]); } else { error_checker(-3); } mysql_free_result(result); } m2_log("Tariff id: %d, Rate notification type: %d, Rate notification job id: %d \n", tariff_id, rnj_type, rnj_id); return error_checker(0); } /* Update rato notifcation job status */ int update_rnj_status(int state, char *status) { char query[1024] = ""; char state_str[32] = ""; if (state == 1) { strcpy(state_str, "generation_failed"); } else if (state == 0) { strcpy(state_str, "data_generated"); } else if (state == 2) { strcpy(state_str, "no_changes_present"); } m2_log("Changing rate notification job status: %s\n", state_str); sprintf(query, "UPDATE rate_notification_jobs SET status = '%s', status_reason = '%s' WHERE id = %d", state_str, status, rnj_id); if (m2_mysql_query(query)) { return error_checker(-1); } return error_checker(0); } /* Create new rate notification data table */ int create_new_table() { char query[2028] = ""; m2_log("Creating new table rate_notification_data_%d \n", rnj_id); sprintf(query, "CREATE TABLE IF NOT EXISTS `rate_notification_data_%d` " "( `id` bigint(20) NOT NULL AUTO_INCREMENT, " "`rate_id` bigint(20) NOT NULL, " "`ratedetail_id` bigint(20) NOT NULL, " "`prefix` varchar(60) NOT NULL, " "`effective_from` datetime NOT NULL, " "`rate` decimal(30,15) NOT NULL, " "`rate_difference_raw` decimal(30,15) NOT NULL, " "`rate_difference_percentage` decimal(30,15) NOT NULL, " "`increment` int(11) NOT NULL, " "`connection_fee` decimal(30,15) NOT NULL, " "`minimal_time` int(11) NOT NULL, " "`is_blocked` tinyint(4) NOT NULL, " "`is_deleted` tinyint(4) NOT NULL, " "`is_new` tinyint(4) NOT NULL, " "`destinationgroup_id` int(11) NOT NULL, " "`destinationgroup_name` varchar(255) NOT NULL, " "`destination_id` bigint(20) NOT NULL, " "`destination_name` varchar(255) NOT NULL, " "`client_agreement` varchar(255) NOT NULL, " "PRIMARY KEY (`id`))" "ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;", rnj_id); if (m2_mysql_query(query)) { return error_checker(-1); } return error_checker(0); } /* Get rates differences */ int get_diff_rates() { MYSQL_RES *result; MYSQL_ROW row; char query[2028] = ""; char select_current_active_rate[32] = ""; char current_prefix[32] = ""; double previous_rate = 0; m2_log("Getting diff rates \n"); if (rnj_type) { strcpy(select_current_active_rate, " OR rates.id = crates.id"); } sprintf(query, "SELECT rates.id, rates.prefix, rt.rate, crt.rate " "FROM rates " "LEFT JOIN rates AS crates ON (crates.prefix = rates.prefix AND crates.currently_effective = 1 AND crates.tariff_id = rates.tariff_id) " "LEFT JOIN ratedetails AS rt ON (rates.id = rt.rate_id) " "LEFT JOIN destinations ON (rates.prefix = destinations.prefix) " "LEFT JOIN destinationgroups ON (destinationgroups.id = destinations.destinationgroup_id) " "LEFT JOIN ratedetails AS crt ON (crates.id = crt.rate_id) " "WHERE rates.tariff_id = %d AND rates.prefix != 'did_default' AND (rates.effective_from > crates.effective_from OR crates.prefix IS NULL " "OR (crates.effective_from IS NULL AND rates.effective_from IS NOT NULL) %s) " "GROUP BY rates.id " "ORDER BY rates.prefix, rates.effective_from", tariff_id, select_current_active_rate); if (m2_mysql_query(query)) { return error_checker(-1); } m2_dbg_1(query); result = mysql_store_result(&mysql); if (result) { while ((row = mysql_fetch_row(result))) { char prefix[32]; double rate; double crate; char id[21]; double raw_rate; double percent_rate; if (row[0]) strcpy(id, row[0]); else continue; if (row[1]) strcpy(prefix, row[1]); else continue; if (row[2]) rate = atof(row[2]); else rate = 0; if (row[3]) crate = atof(row[3]); else crate = -1; if (strcmp(current_prefix, prefix) != 0) { memset(current_prefix, 0, strlen(current_prefix)); strcpy(current_prefix, prefix); previous_rate = 0; } if (previous_rate == 0) { if (crate == -1) { raw_rate = rate; percent_rate = 0; } else { raw_rate = rate - crate; percent_rate = (rate - crate) / crate * 100; } } else { raw_rate = rate - previous_rate; percent_rate = (rate - previous_rate) / previous_rate * 100; } add_rate(root, id, raw_rate, percent_rate); previous_rate = rate; m2_dbg_1("Rate id: %s, rate %lf \n", id, rate); } mysql_free_result(result); } return error_checker(0); } /* Get future rates */ int get_rates() { MYSQL_RES *result; MYSQL_ROW row; char query[2028] = ""; rate_t *current_rate = rates_head; int first_rate = 1; char select_current_active_rate[32] = ""; m2_log("Getting rates \n"); if (rnj_type) { strcpy(select_current_active_rate, " OR rates.id = crates.id"); } sprintf(query, "SELECT rates.id, rt.id, rates.prefix, rt.rate, rates.effective_from, rt.increment_s, " "rt.min_time, rt.blocked, destinationgroups.id, destinationgroups.name, " "destinations.id, destinations.name, crt.rate, rt.connection_fee FROM rates " "LEFT JOIN rates AS crates ON (crates.prefix = rates.prefix AND crates.currently_effective = 1 AND crates.tariff_id = rates.tariff_id) " "LEFT JOIN ratedetails AS rt ON (rates.id = rt.rate_id) " "LEFT JOIN destinations ON (rates.destination_id = destinations.id) " "LEFT JOIN destinationgroups ON (destinationgroups.id = destinations.destinationgroup_id) " "LEFT JOIN ratedetails AS crt ON (crates.id = crt.rate_id) " "WHERE rates.tariff_id = %d AND rates.prefix != 'did_default' AND (rates.effective_from > crates.effective_from OR crates.prefix IS NULL " "OR (crates.effective_from IS NULL AND rates.effective_from IS NOT NULL) %s) " "GROUP BY rates.id " "ORDER BY IF(destinationgroups.name IS NULL, destinations.name, destinationgroups.name), destinations.name, rates.prefix, rates.effective_from", tariff_id, select_current_active_rate); if (m2_mysql_query(query)) { return error_checker(-1); } m2_dbg_1(query); result = mysql_store_result(&mysql); if (result) { while ((row = mysql_fetch_row(result))) { if (!row[0] || !row[1]) continue; if (first_rate == 0) { current_rate = current_rate->next; } first_rate = 0; if (row[0]) strcpy(current_rate->id, row[0]); else continue; if (row[1]) strcpy(current_rate->r_id, row[1]); else continue; if (row[2]) strcpy(current_rate->prefix, row[2]); else strcpy(current_rate->prefix, ""); if (row[3]) current_rate->rate = atof(row[3]); else current_rate->rate = 0; if (row[4]) strcpy(current_rate->effective_from, row[4]); else strcpy(current_rate->effective_from, ""); if (row[5]) current_rate->increment_s = atoi(row[5]); else current_rate->increment_s = 0; if (row[6]) current_rate->min_time = atoi(row[6]); else current_rate->min_time = 0; if (row[7]) current_rate->blocked = atoi(row[7]); else current_rate->blocked = 0; if (row[8]) current_rate->dst_grp_id = atoi(row[8]); else current_rate->dst_grp_id = 0; if (row[9]) strcpy(current_rate->dst_grp_name, row[9]); else strcpy(current_rate->dst_grp_name, ""); if (row[10]) current_rate->dst_id = atoi(row[10]); else current_rate->dst_id = 0; if (row[11]) strcpy(current_rate->dst_name, row[11]); else strcpy(current_rate->dst_name, ""); if (row[13]) current_rate->connection_fee = atof(row[13]); else current_rate->connection_fee = 0; m2_escape_string(current_rate->dst_grp_name, '\''); m2_escape_string(current_rate->dst_name, '\''); rate_diff_t *rate_diff = find_rate_diff(root, row[0]); if (row[12]) current_rate->is_new = 0; else current_rate->is_new = 1; if (rate_diff == NULL) { current_rate->raw_rate = 0; current_rate->percent_rate = 0; } else { current_rate->active = 1; current_rate->raw_rate = rate_diff->raw_rate; current_rate->percent_rate = rate_diff->percent_rate; } m2_dbg_1("Rate id: %s, prefix %s, effective_from: %s, raw_rate: %lf, \n", current_rate->id, current_rate->prefix, current_rate->effective_from, current_rate->raw_rate); current_rate->next = initialize_rate(0); } mysql_free_result(result); } return error_checker(0); } /* Remove all data from tables */ int flush_table() { char query[256] = ""; m2_log("Flushing table rate_notification_data_%d \n", rnj_id); sprintf(query, "DELETE FROM rate_notification_data_%d", rnj_id); if (m2_mysql_query(query)) { return error_checker(-1); } return error_checker(0); } /* Helper methods */ int _error_checker(int return_code, char const * caller_name) { if (return_code < 0) { char error_msg[256] = ""; sprintf(error_msg, "Error in: %s; return_code: %d\n", caller_name, return_code); m2_log(error_msg); update_rnj_status(1, error_msg); exit(return_code); } else { return return_code; } } rate_t *initialize_rate(int id) { rate_t *rate = (rate_t *) malloc(sizeof(rate_t)); strcpy(rate->id, "0"); rate->next = NULL; rate->active = 0; return rate; } void free_rates() { rate_t* tmp; while (rates_head != NULL) { tmp = rates_head; rates_head = rates_head->next; if (tmp) { free(tmp); tmp = NULL; } } } /* Trie struct for rates ids */ int add_rate(main_node_t *main_n, char *rate_id, double raw_rate, double percent_rate) { int len = strlen(rate_id); int i = 0; main_node_t *current_node = main_n; rate_diff_t *current_sub_node; m2_dbg_1("Adding rate_id: %s, raw_rate: %lf, percent_rate: %lf\n", rate_id, raw_rate, percent_rate); if (main_n == NULL && rate_id == NULL) { m2_log("Bad parametres\n"); return error_checker(1); } for (i = 0; i < len; i++) { int number = rate_id[i] - '0'; current_sub_node = current_node->sub_node[number]; if (current_sub_node == NULL) { current_node->sub_node[number] = trie_add_subnode(); current_sub_node = current_node->sub_node[number]; } if (current_sub_node == NULL) { m2_log("Something went wrong\n"); return error_checker(2); } current_sub_node->number = number; current_node = current_sub_node->child; } m2_dbg_1("New rate added\n"); current_sub_node->raw_rate = raw_rate; current_sub_node->percent_rate = percent_rate; current_sub_node->rate_assigned = 1; return error_checker(0); } rate_diff_t *trie_add_subnode() { rate_diff_t *sub_node = (rate_diff_t *) malloc(sizeof(rate_diff_t)); if(sub_node == NULL) { m2_log("No more memory!\n"); exit(-1); } memset(sub_node, 0, sizeof(rate_diff_t)); sub_node->raw_rate = 0; sub_node->percent_rate = 0; sub_node->rate_assigned = 0; sub_node->child = initialize_main_node(); return sub_node; } int trie_null_subnodes(main_node_t *main_n) { int i; for (i = 0; i < 10; i++) { main_n->sub_node[i] = NULL; } return error_checker(0); } rate_diff_t *find_rate_diff(main_node_t *main_node, char *rate) { rate_diff_t *return_rate = NULL; int i = 0; int number; int len = strlen(rate); for (i = 0; i < len; i++) { number = (int)(rate[i] - '0'); if (is_allowed_number(number) == 0) break; return_rate = main_node->sub_node[number]; if (main_node->sub_node[number]->child == NULL) break; main_node = main_node->sub_node[number]->child; } return return_rate; } int is_allowed_number(int n) { int ok = 0; int i; for (i = 0; i < 10; i++) { if (n == i) ok = 1; } return ok; } main_node_t *initialize_main_node() { main_node_t *main_node = (main_node_t*) malloc(sizeof(main_node_t)); memset(main_node, 0, sizeof(main_node_t)); trie_null_subnodes(main_node); return main_node; }