app_queue: Add feature to set wrapuptime on the queue member
authorRodrigo Ramírez Norambuena <a@rodrigoramirez.com>
Thu, 7 Dec 2017 02:35:40 +0000 (23:35 -0300)
committerRichard Mudgett <rmudgett@digium.com>
Mon, 18 Dec 2017 23:29:21 +0000 (17:29 -0600)
This patch adds the ability to set the wrapuptime on the queue member
config.

When the option is set the wrapuptime on the queue member is used instead
of the queue's wrapuptime.

ASTERISK-27483 #close

Change-Id: I11c85809537f974eb44dc5bbf82bcedd8a458902

CHANGES
apps/app_queue.c
configs/samples/queues.conf.sample
contrib/ast-db-manage/config/versions/e2f04d309071_add_wrapuptime_to_queue_members.py [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index bd1ca67..bd11c6c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -37,6 +37,12 @@ ARI
  * The ContactInfo event's contact_status field is now set to "NonQualified"
    when a contact exists but has not been qualified.
 
+app_queue
+------------------
+ * Added the ability to set the wrapuptime in the configuration of member.
+   When set the wrapuptime on the member is used instead of the wrapuptime
+   defined for the queue itself.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------
 ------------------------------------------------------------------------------
index a140847..af8b3b8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999 - 2016, Digium, Inc.
+ * Copyright (C) 1999 - 2017, Digium, Inc.
  *
  * Mark Spencer <markster@digium.com>
  *
@@ -1586,6 +1586,7 @@ struct member {
        char reason_paused[80];              /*!< Reason of paused if member is paused */
        int queuepos;                        /*!< In what order (pertains to certain strategies) should this member be called? */
        int callcompletedinsl;               /*!< Whether the current call was completed within service level */
+       int wrapuptime;                      /*!< Wrapup Time */
        time_t starttime;                    /*!< The time at which the member answered the current caller. */
        time_t lastcall;                     /*!< When last successful call was hungup */
        time_t lastpause;                    /*!< When started the last pause */
@@ -1826,6 +1827,21 @@ static int queue_cmp_cb(void *obj, void *arg, int flags)
        return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
 }
 
+/*!
+ * \brief Return wrapuptime
+ *
+ * This function checks if wrapuptime in member is set and return this value.
+ * Otherwise return value the wrapuptime in the queue configuration
+ * \return integer value
+ */
+static int get_wrapuptime(struct call_queue *q, struct member *member)
+{
+       if (member->wrapuptime) {
+               return member->wrapuptime;
+       }
+       return q->wrapuptime;
+}
+
 /*! \internal
  * \brief ao2_callback, Decreases queuepos of all followers with a queuepos greater than arg.
  * \param obj the member being acted on
@@ -2309,8 +2325,12 @@ static int get_member_status(struct call_queue *q, int max_penalty, int min_pena
                        if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
                                ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
                                break;
-                       } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
-                               ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime);
+                       } else if ((conditions & QUEUE_EMPTY_WRAPUP)
+                               && member->lastcall
+                               && get_wrapuptime(q, member)
+                               && (time(NULL) - get_wrapuptime(q, member) < member->lastcall)) {
+                               ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
+                                       member->membername, (int) (time(NULL) - member->lastcall), get_wrapuptime(q, member));
                                break;
                        } else {
                                ao2_ref(member, -1);
@@ -2436,6 +2456,7 @@ static void update_status(struct call_queue *q, struct member *m, const int stat
 static int is_member_available(struct call_queue *q, struct member *mem)
 {
        int available = 0;
+       int wrapuptime;
 
        switch (mem->status) {
                case AST_DEVICE_INVALID:
@@ -2459,7 +2480,8 @@ static int is_member_available(struct call_queue *q, struct member *mem)
        }
 
        /* Let wrapuptimes override device state availability */
-       if (mem->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < mem->lastcall)) {
+       wrapuptime = get_wrapuptime(q, mem);
+       if (mem->lastcall && wrapuptime && (time(NULL) - wrapuptime < mem->lastcall)) {
                available = 0;
        }
        return available;
@@ -3354,6 +3376,7 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
        int penalty = 0;
        int paused  = 0;
        int found = 0;
+       int wrapuptime = 0;
        int ringinuse = q->ringinuse;
 
        const char *config_val;
@@ -3363,6 +3386,7 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
        const char *state_interface = S_OR(ast_variable_retrieve(member_config, category, "state_interface"), interface);
        const char *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
        const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
+       const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
 
        if (ast_strlen_zero(rt_uniqueid)) {
                ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
@@ -3385,6 +3409,13 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
                }
        }
 
+       if (wrapuptime_str) {
+               wrapuptime = atoi(wrapuptime_str);
+               if (wrapuptime < 0) {
+                       wrapuptime = 0;
+               }
+       }
+
        if ((config_val = ast_variable_retrieve(member_config, category, realtime_ringinuse_field))) {
                if (ast_true(config_val)) {
                        ringinuse = 1;
@@ -3411,6 +3442,7 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
                        }
                        m->penalty = penalty;
                        m->ringinuse = ringinuse;
+                       m->wrapuptime = wrapuptime;
                        found = 1;
                        ao2_ref(m, -1);
                        break;
@@ -4343,6 +4375,8 @@ static int member_status_available(int status)
  */
 static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
 {
+       int wrapuptime;
+
        if (call->member->paused) {
                ast_debug(1, "%s paused, can't receive call\n", call->interface);
                return 0;
@@ -4353,8 +4387,12 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
                return 0;
        }
 
-       if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime))
-               || (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) {
+       if (call->lastqueue) {
+               wrapuptime = get_wrapuptime(call->lastqueue, call->member);
+       } else {
+               wrapuptime = get_wrapuptime(qe->parent, call->member);
+       }
+       if (wrapuptime && time(NULL) - call->lastcall < wrapuptime) {
                ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
                        (call->lastqueue ? call->lastqueue->name : qe->parent->name),
                        call->interface);
@@ -8554,7 +8592,7 @@ static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, ch
                        while ((m = ao2_iterator_next(&mem_iter))) {
                                /* Count the agents who are logged in, not paused and not wrapping up */
                                if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
-                                               !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
+                                               !(m->lastcall && get_wrapuptime(q, m) && ((now - get_wrapuptime(q, m)) < m->lastcall))) {
                                        count++;
                                }
                                ao2_ref(m, -1);
@@ -9098,12 +9136,14 @@ static void reload_single_member(const char *memberdata, struct call_queue *q)
        struct member tmpmem;
        int penalty;
        int ringinuse;
+       int wrapuptime;
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(interface);
                AST_APP_ARG(penalty);
                AST_APP_ARG(membername);
                AST_APP_ARG(state_interface);
                AST_APP_ARG(ringinuse);
+               AST_APP_ARG(wrapuptime);
        );
 
        if (ast_strlen_zero(memberdata)) {
@@ -9158,11 +9198,23 @@ static void reload_single_member(const char *memberdata, struct call_queue *q)
                ringinuse = q->ringinuse;
        }
 
+       if (!ast_strlen_zero(args.wrapuptime)) {
+               tmp = args.wrapuptime;
+               ast_strip(tmp);
+               wrapuptime = atoi(tmp);
+               if (wrapuptime < 0) {
+                       wrapuptime = 0;
+               }
+       } else {
+               wrapuptime = 0;
+       }
+
        /* Find the old position in the list */
        ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
        cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);
 
        if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface, ringinuse))) {
+               newm->wrapuptime = wrapuptime;
                if (cur) {
                        /* Round Robin Queue Position must be copied if this is replacing an existing member */
                        ao2_lock(q->members);
index 3e7cbd8..226a3a9 100644 (file)
@@ -551,7 +551,7 @@ monitor-type = MixMonitor
 ; must also preload pbx_config.so and chan_local.so (or pbx_ael.so, pbx_lua.so,
 ; or pbx_realtime.so, depending on how your dialplan is configured).
 ;
-; syntax: member => interface,[,penalty][,membername][,state_interface][,ringinuse]
+; syntax: member => interface,[,penalty][,membername][,state_interface][,ringinuse][,wrapuptime]
 ;
 ;member => DAHDI/1
 ;member => DAHDI/2,10
diff --git a/contrib/ast-db-manage/config/versions/e2f04d309071_add_wrapuptime_to_queue_members.py b/contrib/ast-db-manage/config/versions/e2f04d309071_add_wrapuptime_to_queue_members.py
new file mode 100644 (file)
index 0000000..fdb1416
--- /dev/null
@@ -0,0 +1,22 @@
+"""add wrapuptime to queue_members
+
+Revision ID: e2f04d309071
+Revises: 041c0d3d1857
+Create Date: 2017-12-07 08:32:45.360857
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'e2f04d309071'
+down_revision = '041c0d3d1857'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('queue_members', sa.Column('wrapuptime', sa.Integer))
+
+
+def downgrade():
+    op.drop_column('queue_members', 'wrapuptime')