mod_callcenter: Add ability to unload/reload/load a queue setting (You still need to reloadxml before).

Note that joining a queue will check for it in the config and load it on the fly... I've used the same system as in mod_voicemail.  Not sure if we should allow this, but just comment it out of the config before unload and it wont be available anymore
This commit is contained in:
Marc Olivier Chouinard 2010-08-13 19:15:24 -04:00
parent ba09b96d1e
commit 3eafca6029
1 changed files with 104 additions and 21 deletions

View File

@ -404,6 +404,37 @@ static void queue_rwunlock(cc_queue_t *queue)
}
}
static void destroy_queue(const char *queue_name, switch_bool_t block)
{
cc_queue_t *queue = NULL;
switch_mutex_lock(globals.mutex);
if ((queue = switch_core_hash_find(globals.queue_hash, queue_name))) {
switch_core_hash_delete(globals.queue_hash, queue_name);
}
switch_mutex_unlock(globals.mutex);
if (!queue) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[%s] Invalid queue\n", queue_name);
return;
}
if (block) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[%s] Waiting for write lock\n", queue->name);
switch_thread_rwlock_wrlock(queue->rwlock);
} else {
if (switch_thread_rwlock_trywrlock(queue->rwlock) != SWITCH_STATUS_SUCCESS) {
/* Lock failed, set the destroy flag so it'll be destroyed whenever its not in use anymore */
switch_set_flag(queue, PFLAG_DESTROY);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[%s] queue is in use, memory will be freed whenever its no longer in use\n",
queue->name);
return;
}
}
free_queue(queue);
}
switch_cache_db_handle_t *cc_get_db_handle(cc_queue_t *queue)
{
switch_cache_db_connection_options_t options = { {0} };
@ -437,7 +468,7 @@ cc_queue_t *queue_set_config(cc_queue_t *queue)
SWITCH _CONFIG_SET_ITEM(item, "key", type, flags,
pointer, default, options, help_syntax, help_description)
*/
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "strategy", SWITCH_CONFIG_STRING, 0, &queue->strategy, NULL, &queue->config_str_pool, NULL, NULL);
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "strategy", SWITCH_CONFIG_STRING, 0, &queue->strategy, "longest-idle-agent", &queue->config_str_pool, NULL, NULL);
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "moh-sound", SWITCH_CONFIG_STRING, 0, &queue->moh, NULL, &queue->config_str_pool, NULL, NULL);
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "record-template", SWITCH_CONFIG_STRING, 0, &queue->record_template, NULL, &queue->config_str_pool, NULL, NULL);
SWITCH_CONFIG_SET_ITEM(queue->config[i++], "time-base-score", SWITCH_CONFIG_STRING, 0, &queue->time_base_score, "queue", &queue->config_str_pool, NULL, NULL);
@ -1529,11 +1560,23 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName
cc_queue_t *queue = NULL;
char *sql = NULL;
char *sql_order_by = NULL;
char *queue_name = NULL;
char *queue_strategy = NULL;
char *queue_record_template = NULL;
agent_callback_t cbt;
if (!argv[0] || !(queue = get_queue(argv[0]))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Queue %s not found locally, skip this member\n", argv[0]);
goto end;
} else {
queue_name = strdup(queue->name);
queue_strategy = strdup(queue->strategy);
if (queue->record_template) {
queue_record_template = strdup(queue->record_template);
}
queue_rwunlock(queue);
}
memset(&cbt, 0, sizeof(cbt));
@ -1543,22 +1586,22 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName
cbt.caller_name = argv[3];
cbt.joined_epoch = argv[4];
cbt.queue = argv[0];
cbt.strategy = queue->strategy;
cbt.record_template = queue->record_template;
if (!strcasecmp(queue->strategy, "longest-idle-agent") || !strcasecmp(queue->strategy, "roundrobin") /* TODO TMP backward compatibility for taxi dispatch setup */) {
cbt.strategy = queue_strategy;
cbt.record_template = queue_record_template;
if (!strcasecmp(queue->strategy, "longest-idle-agent") || !strcasecmp(queue_strategy, "roundrobin") /* TODO TMP backward compatibility for taxi dispatch setup */) {
sql_order_by = switch_mprintf("level, agents.last_offered_call, position");
} else if (!strcasecmp(queue->strategy, "agent-with-least-talk-time")) {
} else if (!strcasecmp(queue_strategy, "agent-with-least-talk-time")) {
sql_order_by = switch_mprintf("level, agents.talk_time, position");
} else if (!strcasecmp(queue->strategy, "agent-with-fewest-calls")) {
} else if (!strcasecmp(queue_strategy, "agent-with-fewest-calls")) {
sql_order_by = switch_mprintf("level, agents.calls_answered, position");
} else if (!strcasecmp(queue->strategy, "ring-all")) {
} else if (!strcasecmp(queue_strategy, "ring-all")) {
/* If we set at Trying, who will put it back at Waiting ?? So we just dont change it state for the moment */
/*sql = switch_mprintf("UPDATE members SET state = '%q' WHERE state = '%q' AND uuid = '%q' AND system = 'single_box'", cc_member_state2str(CC_MEMBER_STATE_TRYING), cc_member_state2str(CC_MEMBER_STATE_WAITING), cbt.uuid);
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);*/
sql_order_by = switch_mprintf("level, position");
} else if(!strcasecmp(queue->strategy, "sequentially-by-agent-order")) {
} else if(!strcasecmp(queue_strategy, "sequentially-by-agent-order")) {
sql_order_by = switch_mprintf("level, position");
} else {
/* If the strategy doesn't exist, just fallback to the following */
@ -1571,7 +1614,7 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName
" AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')"
" AND agents.state = '%q' AND last_bridge_end < (%ld - wrap_up_time)"
" ORDER BY %q",
queue->name,
queue_name,
cc_tier_state2str(CC_TIER_STATE_READY), cc_tier_state2str(CC_TIER_STATE_NO_ANSWER),
cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE), cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND),
cc_agent_state2str(CC_AGENT_STATE_WAITING), (long) switch_epoch_time_now(NULL),
@ -1582,9 +1625,11 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName
switch_safe_free(sql);
switch_safe_free(sql_order_by);
queue_rwunlock(queue);
end:
switch_safe_free(queue_name);
switch_safe_free(queue_strategy);
switch_safe_free(queue_record_template);
return 0;
}
@ -1729,7 +1774,7 @@ SWITCH_STANDARD_APP(callcenter_function)
switch_threadattr_t *thd_attr = NULL;
switch_memory_pool_t *pool;
int cc_base_score_int = 0;
switch_channel_timetable_t *times = switch_channel_get_timetable(member_channel);
switch_channel_timetable_t *times = NULL;
const char *cc_base_score = switch_channel_get_variable(member_channel, "cc_base_score");
const char *cc_moh_override = switch_channel_get_variable(member_channel, "cc_moh_override");
const char *cur_moh = NULL;
@ -1754,7 +1799,11 @@ SWITCH_STANDARD_APP(callcenter_function)
goto end;
}
/* Make sure we answer the channel before getting the switch_channel_time_table_t answer time */
switch_channel_answer(member_channel);
/* Grab the start epoch of a channel */
times = switch_channel_get_timetable(member_channel);
switch_snprintf(start_epoch, sizeof(start_epoch), "%" SWITCH_TIME_T_FMT, times->answered);
/* Add manually imported score */
@ -1766,7 +1815,6 @@ SWITCH_STANDARD_APP(callcenter_function)
if (!switch_strlen_zero(start_epoch) && !strcasecmp("system", queue->time_base_score)) {
cc_base_score_int += (switch_epoch_time_now(NULL) - atoi(start_epoch));
}
switch_channel_answer(member_channel);
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(member_channel, event);
@ -1815,10 +1863,12 @@ SWITCH_STANDARD_APP(callcenter_function)
/* Playback MOH */
/* TODO Add DTMF callback support */
/* TODO add MOH infitite loop */
cur_moh = queue->moh;
if (cc_moh_override) {
cur_moh = cc_moh_override;
cur_moh = switch_core_session_strdup(session, cc_moh_override);
} else {
cur_moh = switch_core_session_strdup(session, queue->moh);
}
queue_rwunlock(queue);
if (cur_moh) {
switch_ivr_play_file(session, NULL, cur_moh, &args);
@ -1835,7 +1885,7 @@ SWITCH_STANDARD_APP(callcenter_function)
if (!switch_channel_up(member_channel)) { /* If channel is still up, it mean that the member didn't hangup, so we should leave the agent alone */
switch_core_session_hupall_matching_var("cc_member_uuid", uuid, SWITCH_CAUSE_ORIGINATOR_CANCEL);
sql = switch_mprintf("DELETE FROM members WHERE system = 'single_box' AND uuid = '%q'", uuid);
cc_execute_sql(queue, sql, NULL);
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
@ -1859,7 +1909,7 @@ SWITCH_STANDARD_APP(callcenter_function)
} else {
sql = switch_mprintf("UPDATE members SET state = '%q', bridge_epoch = '%ld' WHERE system = 'single_box' AND uuid = '%q'",
cc_member_state2str(CC_MEMBER_STATE_ANSWERED), (long) switch_epoch_time_now(NULL), uuid);
cc_execute_sql(queue, sql, NULL);
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
/* Send Event with queue count */
@ -1867,8 +1917,6 @@ SWITCH_STANDARD_APP(callcenter_function)
}
queue_rwunlock(queue);
end:
return;
@ -2166,7 +2214,42 @@ SWITCH_STANDARD_API(cc_config_api_function)
}
}
} else if (section && !strcasecmp(section, "queue")) {
if (action && !strcasecmp(action, "list")) {
if (action && !strcasecmp(action, "load")) {
if (argc-initial_argc < 1) {
stream->write_function(stream, "%s", "-ERR Invalid!\n");
goto done;
} else {
const char *queue_name = argv[0 + initial_argc];
cc_queue_t *queue = NULL;
if ((queue = get_queue(queue_name))) {
queue_rwunlock(queue);
}
stream->write_function(stream, "%s", "+OK\n");
}
} else if (action && !strcasecmp(action, "unload")) {
if (argc-initial_argc < 1) {
stream->write_function(stream, "%s", "-ERR Invalid!\n");
goto done;
} else {
const char *queue_name = argv[0 + initial_argc];
destroy_queue(queue_name, SWITCH_FALSE);
stream->write_function(stream, "%s", "+OK\n");
}
} else if (action && !strcasecmp(action, "reload")) {
if (argc-initial_argc < 1) {
stream->write_function(stream, "%s", "-ERR Invalid!\n");
goto done;
} else {
const char *queue_name = argv[0 + initial_argc];
cc_queue_t *queue = NULL;
destroy_queue(queue_name, SWITCH_FALSE);
if ((queue = get_queue(queue_name))) {
queue_rwunlock(queue);
}
stream->write_function(stream, "%s", "+OK\n");
}
} else if (action && !strcasecmp(action, "list")) {
if (argc-initial_argc < 1) {
stream->write_function(stream, "%s", "-ERR Invalid!\n");
goto done;