Fixed issue #2292 - CTI Log: duration_talking_time is incorrect and after update initialized_at is a string (not timestamp)
This commit is contained in:
parent
fb24de11cc
commit
86c2df69ad
6 changed files with 174 additions and 34 deletions
|
@ -121,20 +121,6 @@ class App.CTI extends App.Controller
|
||||||
@renderCallerLog()
|
@renderCallerLog()
|
||||||
|
|
||||||
renderCallerLog: ->
|
renderCallerLog: ->
|
||||||
format = (time) ->
|
|
||||||
|
|
||||||
# Hours, minutes and seconds
|
|
||||||
hrs = ~~parseInt((time / 3600))
|
|
||||||
mins = ~~parseInt(((time % 3600) / 60))
|
|
||||||
secs = parseInt(time % 60)
|
|
||||||
|
|
||||||
# Output like "1:01" or "4:03:59" or "123:03:59"
|
|
||||||
mins = "0#{mins}" if mins < 10
|
|
||||||
secs = "0#{secs}" if secs < 10
|
|
||||||
if hrs > 0
|
|
||||||
return "#{hrs}:#{mins}:#{secs}"
|
|
||||||
"#{mins}:#{secs}"
|
|
||||||
|
|
||||||
for item in @list
|
for item in @list
|
||||||
item.status_class = ''
|
item.status_class = ''
|
||||||
item.disabled = true
|
item.disabled = true
|
||||||
|
@ -157,15 +143,12 @@ class App.CTI extends App.Controller
|
||||||
if item.comment
|
if item.comment
|
||||||
item.state_human += ", #{item.comment}"
|
item.state_human += ", #{item.comment}"
|
||||||
|
|
||||||
if item.start_at && item.end_at
|
|
||||||
item.duration = format((Date.parse(item.end_at) - Date.parse(item.start_at))/1000)
|
|
||||||
|
|
||||||
diff_in_min = ((Date.now() - Date.parse(item.created_at)) / 1000) / 60
|
diff_in_min = ((Date.now() - Date.parse(item.created_at)) / 1000) / 60
|
||||||
if diff_in_min > 1
|
if diff_in_min > 1
|
||||||
item.disabled = false
|
item.disabled = false
|
||||||
|
|
||||||
@removePopovers()
|
@removePopovers()
|
||||||
@callerLog.html( App.view('cti/caller_log')(list: @list))
|
@callerLog.html(App.view('cti/caller_log')(list: @list))
|
||||||
@renderPopovers()
|
@renderPopovers()
|
||||||
|
|
||||||
@updateNavMenu()
|
@updateNavMenu()
|
||||||
|
|
|
@ -43,6 +43,23 @@ class App extends Spine.Controller
|
||||||
decimal: (data, positions = 2) ->
|
decimal: (data, positions = 2) ->
|
||||||
App.Utils.decimal(data, positions)
|
App.Utils.decimal(data, positions)
|
||||||
|
|
||||||
|
# define time_duration / mm:ss / hh:mm:ss format helper
|
||||||
|
time_duration: (time) ->
|
||||||
|
return '' if !time
|
||||||
|
return '' if isNaN(parseInt(time))
|
||||||
|
|
||||||
|
# Hours, minutes and seconds
|
||||||
|
hrs = ~~parseInt((time / 3600))
|
||||||
|
mins = ~~parseInt(((time % 3600) / 60))
|
||||||
|
secs = parseInt(time % 60)
|
||||||
|
|
||||||
|
# Output like "1:01" or "4:03:59" or "123:03:59"
|
||||||
|
mins = "0#{mins}" if mins < 10
|
||||||
|
secs = "0#{secs}" if secs < 10
|
||||||
|
if hrs > 0
|
||||||
|
return "#{hrs}:#{mins}:#{secs}"
|
||||||
|
"#{mins}:#{secs}"
|
||||||
|
|
||||||
# define mask helper
|
# define mask helper
|
||||||
# mask an value like 'a***********yz'
|
# mask an value like 'a***********yz'
|
||||||
M: (item, start = 1, end = 2) ->
|
M: (item, start = 1, end = 2) ->
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
<th style="width: 40px;"></th>
|
<th style="width: 40px;"></th>
|
||||||
<th><%- @T('From') %></th>
|
<th><%- @T('From') %></th>
|
||||||
<th><%- @T('To') %></th>
|
<th><%- @T('To') %></th>
|
||||||
|
<!--<th style="width: 100px;"><%- @T('Queue') %></th>-->
|
||||||
<th style="width: 130px;"><%- @T('Status') %></th>
|
<th style="width: 130px;"><%- @T('Status') %></th>
|
||||||
<th style="width: 100px;"><%- @T('Duration') %></th>
|
<th style="width: 80px;"><%- @T('Waiting') %></th>
|
||||||
|
<th style="width: 80px;"><%- @T('Duration') %></th>
|
||||||
<th style="width: 170px;"><%- @T('Time') %></th>
|
<th style="width: 170px;"><%- @T('Time') %></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -83,12 +85,14 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</td>
|
</td>
|
||||||
|
<!--<td style="vertical-align: middle"><%= item.queue %></td>-->
|
||||||
<td style="vertical-align: middle">
|
<td style="vertical-align: middle">
|
||||||
<% if item.state_human: %>
|
<% if item.state_human: %>
|
||||||
<%- @Icon('status', "#{item.status_class} inline") %> <%- @T(item.state_human) %>
|
<%- @Icon('status', "#{item.status_class} inline") %> <%- @T(item.state_human) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</td>
|
</td>
|
||||||
<td style="vertical-align: middle"><%= item.duration %></td>
|
<td style="vertical-align: middle"><%= @time_duration(item.duration_waiting_time) %></td>
|
||||||
|
<td style="vertical-align: middle"><%= @time_duration(item.duration_talking_time) %></td>
|
||||||
<td style="vertical-align: middle"><%- @humanTime(item.created_at) %></td>
|
<td style="vertical-align: middle"><%- @humanTime(item.created_at) %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -68,7 +68,8 @@ example data, can be used for demo
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
created_at: Time.zone.now,
|
||||||
)
|
)
|
||||||
|
|
||||||
Cti::Log.create!(
|
Cti::Log.create!(
|
||||||
|
@ -91,7 +92,8 @@ example data, can be used for demo
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
created_at: Time.zone.now - 20.seconds,
|
||||||
)
|
)
|
||||||
|
|
||||||
Cti::Log.create!(
|
Cti::Log.create!(
|
||||||
|
@ -114,7 +116,11 @@ example data, can be used for demo
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
initialized_at: Time.zone.now - 20.seconds,
|
||||||
|
start_at: Time.zone.now - 30.seconds,
|
||||||
|
duration_waiting_time: 20,
|
||||||
|
created_at: Time.zone.now - 20.seconds,
|
||||||
)
|
)
|
||||||
|
|
||||||
Cti::Log.create!(
|
Cti::Log.create!(
|
||||||
|
@ -139,7 +145,13 @@ example data, can be used for demo
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
initialized_at: Time.zone.now - 80.seconds,
|
||||||
|
start_at: Time.zone.now - 45.seconds,
|
||||||
|
end_at: Time.zone.now,
|
||||||
|
duration_waiting_time: 35,
|
||||||
|
duration_talking_time: 45,
|
||||||
|
created_at: Time.zone.now - 80.seconds,
|
||||||
)
|
)
|
||||||
|
|
||||||
Cti::Log.create!(
|
Cti::Log.create!(
|
||||||
|
@ -164,7 +176,13 @@ example data, can be used for demo
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
initialized_at: Time.zone.now - 5.minutes,
|
||||||
|
start_at: Time.zone.now - 3.minutes,
|
||||||
|
end_at: Time.zone.now - 20.seconds,
|
||||||
|
duration_waiting_time: 120,
|
||||||
|
duration_talking_time: 160,
|
||||||
|
created_at: Time.zone.now - 5.minutes,
|
||||||
)
|
)
|
||||||
|
|
||||||
Cti::Log.create!(
|
Cti::Log.create!(
|
||||||
|
@ -189,7 +207,13 @@ example data, can be used for demo
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
initialized_at: Time.zone.now - 60.minutes,
|
||||||
|
start_at: Time.zone.now - 59.minutes,
|
||||||
|
end_at: Time.zone.now - 2.minutes,
|
||||||
|
duration_waiting_time: 60,
|
||||||
|
duration_talking_time: 3420,
|
||||||
|
created_at: Time.zone.now - 60.minutes,
|
||||||
)
|
)
|
||||||
|
|
||||||
Cti::Log.create!(
|
Cti::Log.create!(
|
||||||
|
@ -214,7 +238,13 @@ example data, can be used for demo
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
initialized_at: Time.zone.now - 240.minutes,
|
||||||
|
start_at: Time.zone.now - 235.minutes,
|
||||||
|
end_at: Time.zone.now - 222.minutes,
|
||||||
|
duration_waiting_time: 300,
|
||||||
|
duration_talking_time: 1080,
|
||||||
|
created_at: Time.zone.now - 240.minutes,
|
||||||
)
|
)
|
||||||
|
|
||||||
Cti::Log.create!(
|
Cti::Log.create!(
|
||||||
|
@ -226,7 +256,13 @@ example data, can be used for demo
|
||||||
state: 'hangup',
|
state: 'hangup',
|
||||||
start_at: Time.zone.now - 20.seconds,
|
start_at: Time.zone.now - 20.seconds,
|
||||||
end_at: Time.zone.now,
|
end_at: Time.zone.now,
|
||||||
preferences: {}
|
preferences: {},
|
||||||
|
initialized_at: Time.zone.now - 1440.minutes,
|
||||||
|
start_at: Time.zone.now - 1430.minutes,
|
||||||
|
end_at: Time.zone.now - 1429.minutes,
|
||||||
|
duration_waiting_time: 600,
|
||||||
|
duration_talking_time: 660,
|
||||||
|
created_at: Time.zone.now - 1440.minutes,
|
||||||
)
|
)
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
@ -307,7 +343,11 @@ Cti::Log.process(
|
||||||
preferences = nil
|
preferences = nil
|
||||||
done = true
|
done = true
|
||||||
if params['direction'] == 'in'
|
if params['direction'] == 'in'
|
||||||
to_comment = user
|
if user.present?
|
||||||
|
to_comment = user
|
||||||
|
elsif queue.present?
|
||||||
|
to_comment = queue
|
||||||
|
end
|
||||||
from_comment, preferences = CallerId.get_comment_preferences(params['from'], 'from')
|
from_comment, preferences = CallerId.get_comment_preferences(params['from'], 'from')
|
||||||
else
|
else
|
||||||
from_comment = user
|
from_comment = user
|
||||||
|
@ -367,7 +407,7 @@ Cti::Log.process(
|
||||||
log.state = 'hangup'
|
log.state = 'hangup'
|
||||||
log.end_at = Time.zone.now
|
log.end_at = Time.zone.now
|
||||||
if log.start_at
|
if log.start_at
|
||||||
log.duration_talking_time = log.start_at.to_i - log.end_at.to_i
|
log.duration_talking_time = log.end_at.to_i - log.start_at.to_i
|
||||||
elsif !log.duration_waiting_time && log.initialized_at
|
elsif !log.duration_waiting_time && log.initialized_at
|
||||||
log.duration_waiting_time = log.end_at.to_i - log.initialized_at.to_i
|
log.duration_waiting_time = log.end_at.to_i - log.initialized_at.to_i
|
||||||
end
|
end
|
||||||
|
|
39
db/migrate/20181017000001_cti_generic_api2.rb
Normal file
39
db/migrate/20181017000001_cti_generic_api2.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
class CtiGenericApi2 < ActiveRecord::Migration[5.1]
|
||||||
|
def up
|
||||||
|
|
||||||
|
# return if it's a new setup
|
||||||
|
return if !Setting.find_by(name: 'system_init_done')
|
||||||
|
return if !column_exists?(:cti_logs, :initialized_at)
|
||||||
|
return if !column_exists?(:cti_logs, :initialized_at_cleanup)
|
||||||
|
|
||||||
|
add_column :cti_logs, :initialized_at_cleanup, :timestamp, limit: 3, null: true
|
||||||
|
Cti::Log.connection.schema_cache.clear!
|
||||||
|
Cti::Log.reset_column_information
|
||||||
|
|
||||||
|
# clenaup table records
|
||||||
|
Cti::Log.order(created_at: :desc).limit(2000).each do |log|
|
||||||
|
if log.initialized_at
|
||||||
|
begin
|
||||||
|
initialized_at = Time.zone.parse(log.initialized_at)
|
||||||
|
log.update_column(:initialized_at_cleanup, initialized_at) # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
if initialized_at && log.start_at
|
||||||
|
log.update_column(:duration_waiting_time, log.start_at.to_i - initialized_at.to_i) # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
logger.error e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if log.end_at && log.start_at
|
||||||
|
log.update_column(:duration_talking_time, log.end_at.to_i - log.start_at.to_i) # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
remove_column(:cti_logs, :initialized_at)
|
||||||
|
Cti::Log.connection.schema_cache.clear!
|
||||||
|
Cti::Log.reset_column_information
|
||||||
|
|
||||||
|
rename_column :cti_logs, :initialized_at_cleanup, :initialized_at
|
||||||
|
Cti::Log.connection.schema_cache.clear!
|
||||||
|
Cti::Log.reset_column_information
|
||||||
|
end
|
||||||
|
end
|
|
@ -162,6 +162,8 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.duration_waiting_time).to be_nil
|
expect(log.duration_waiting_time).to be_nil
|
||||||
expect(log.duration_talking_time).to be_nil
|
expect(log.duration_talking_time).to be_nil
|
||||||
|
|
||||||
|
travel 2.seconds
|
||||||
|
|
||||||
# outbound - I - hangup by agent
|
# outbound - I - hangup by agent
|
||||||
params = 'event=hangup&direction=out&call_id=1234567890-1&cause=cancel'
|
params = 'event=hangup&direction=out&call_id=1234567890-1&cause=cancel'
|
||||||
post "/api/v1/cti/#{token}", params: params
|
post "/api/v1/cti/#{token}", params: params
|
||||||
|
@ -180,7 +182,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
expect(log.start_at).to be_nil
|
expect(log.start_at).to be_nil
|
||||||
expect(log.end_at).to be_truthy
|
expect(log.end_at).to be_truthy
|
||||||
expect(log.duration_waiting_time).to be_truthy
|
expect(log.duration_waiting_time).to eq(2)
|
||||||
expect(log.duration_talking_time).to be_nil
|
expect(log.duration_talking_time).to be_nil
|
||||||
|
|
||||||
# outbound - II - new call
|
# outbound - II - new call
|
||||||
|
@ -204,6 +206,8 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.duration_waiting_time).to be_nil
|
expect(log.duration_waiting_time).to be_nil
|
||||||
expect(log.duration_talking_time).to be_nil
|
expect(log.duration_talking_time).to be_nil
|
||||||
|
|
||||||
|
travel 2.seconds
|
||||||
|
|
||||||
# outbound - II - answer by customer
|
# outbound - II - answer by customer
|
||||||
params = 'event=answer&direction=out&call_id=1234567890-2&from=4930600000000&to=4912347114711'
|
params = 'event=answer&direction=out&call_id=1234567890-2&from=4930600000000&to=4912347114711'
|
||||||
post "/api/v1/cti/#{token}", params: params
|
post "/api/v1/cti/#{token}", params: params
|
||||||
|
@ -222,9 +226,11 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
expect(log.start_at).to be_truthy
|
expect(log.start_at).to be_truthy
|
||||||
expect(log.end_at).to be_nil
|
expect(log.end_at).to be_nil
|
||||||
expect(log.duration_waiting_time).to be_truthy
|
expect(log.duration_waiting_time).to eq(2)
|
||||||
expect(log.duration_talking_time).to be_nil
|
expect(log.duration_talking_time).to be_nil
|
||||||
|
|
||||||
|
travel 2.seconds
|
||||||
|
|
||||||
# outbound - II - hangup by customer
|
# outbound - II - hangup by customer
|
||||||
params = 'event=hangup&direction=out&call_id=1234567890-2&cause=normalClearing&from=4930600000000&to=4912347114711'
|
params = 'event=hangup&direction=out&call_id=1234567890-2&cause=normalClearing&from=4930600000000&to=4912347114711'
|
||||||
post "/api/v1/cti/#{token}", params: params
|
post "/api/v1/cti/#{token}", params: params
|
||||||
|
@ -243,8 +249,8 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
expect(log.start_at).to be_truthy
|
expect(log.start_at).to be_truthy
|
||||||
expect(log.end_at).to be_truthy
|
expect(log.end_at).to be_truthy
|
||||||
expect(log.duration_waiting_time).to be_truthy
|
expect(log.duration_waiting_time).to eq(2)
|
||||||
expect(log.duration_talking_time).to be_truthy
|
expect(log.duration_talking_time).to eq(2)
|
||||||
|
|
||||||
# inbound - I - new call
|
# inbound - I - new call
|
||||||
params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&call_id=1234567890-3&user%5B%5D=user+1'
|
params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&call_id=1234567890-3&user%5B%5D=user+1'
|
||||||
|
@ -488,5 +494,56 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(json_response['list'][5]['state']).to eq('hangup')
|
expect(json_response['list'][5]['state']).to eq('hangup')
|
||||||
expect(json_response['list'][6]['call_id']).to eq('1234567890-1')
|
expect(json_response['list'][6]['call_id']).to eq('1234567890-1')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'does queue param tests' do
|
||||||
|
token = Setting.get('cti_token')
|
||||||
|
|
||||||
|
# inbound - queue & user
|
||||||
|
params = 'event=newCall&direction=in&to=4930600000000&from=anonymous&call_id=1234567890-1&user%5B%5D=user+1,user+2&queue=some_queue_name'
|
||||||
|
post "/api/v1/cti/#{token}", params: params
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
log = Cti::Log.find_by(call_id: '1234567890-1')
|
||||||
|
expect(log).to be_truthy
|
||||||
|
expect(log.to).to eq('4930600000000')
|
||||||
|
expect(log.from).to eq('anonymous')
|
||||||
|
expect(log.direction).to eq('in')
|
||||||
|
expect(log.to_comment).to eq('user 1,user 2')
|
||||||
|
expect(log.from_comment).to be_nil
|
||||||
|
expect(log.preferences['to']).to be_falsey
|
||||||
|
expect(log.preferences['from']).to be_falsey
|
||||||
|
expect(log.comment).to be_nil
|
||||||
|
expect(log.queue).to eq('some_queue_name')
|
||||||
|
expect(log.state).to eq('newCall')
|
||||||
|
expect(log.done).to eq(false)
|
||||||
|
expect(log.initialized_at).to be_truthy
|
||||||
|
expect(log.start_at).to be_nil
|
||||||
|
expect(log.end_at).to be_nil
|
||||||
|
expect(log.duration_waiting_time).to be_nil
|
||||||
|
expect(log.duration_talking_time).to be_nil
|
||||||
|
|
||||||
|
# inbound - queue & no user
|
||||||
|
params = 'event=newCall&direction=in&to=4930600000000&from=anonymous&call_id=1234567890-2&user%5B%5D=&queue=some_queue_name'
|
||||||
|
post "/api/v1/cti/#{token}", params: params
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
log = Cti::Log.find_by(call_id: '1234567890-2')
|
||||||
|
expect(log).to be_truthy
|
||||||
|
expect(log.to).to eq('4930600000000')
|
||||||
|
expect(log.from).to eq('anonymous')
|
||||||
|
expect(log.direction).to eq('in')
|
||||||
|
expect(log.to_comment).to eq('some_queue_name')
|
||||||
|
expect(log.from_comment).to be_nil
|
||||||
|
expect(log.preferences['to']).to be_falsey
|
||||||
|
expect(log.preferences['from']).to be_falsey
|
||||||
|
expect(log.comment).to be_nil
|
||||||
|
expect(log.queue).to eq('some_queue_name')
|
||||||
|
expect(log.state).to eq('newCall')
|
||||||
|
expect(log.done).to eq(false)
|
||||||
|
expect(log.initialized_at).to be_truthy
|
||||||
|
expect(log.start_at).to be_nil
|
||||||
|
expect(log.end_at).to be_nil
|
||||||
|
expect(log.duration_waiting_time).to be_nil
|
||||||
|
expect(log.duration_talking_time).to be_nil
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue