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:
Martin Edenhofer 2018-10-18 11:42:37 +02:00
parent fb24de11cc
commit 86c2df69ad
6 changed files with 174 additions and 34 deletions

View file

@ -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()

View file

@ -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) ->

View file

@ -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 %>

View file

@ -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'
if user.present?
to_comment = user 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

View 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

View file

@ -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