helpscout: use replies_sent to compute contact rate
The `conversations_count` we previously used counts replies, but also all conversations that were tagged or re-tagged during the month – with made counting the actual work spent on user support brittle. Counting the replies is a better estimation of what we get. Unfortunately this also removes the filtering-by-tag feature. To mitigate this, the reports are now scoped to a specific mailbox. This allows to create extra mailboxes for conversations that should't be counted in the stats.
This commit is contained in:
parent
52226804d0
commit
503c393a87
6 changed files with 57 additions and 62 deletions
|
@ -13,7 +13,6 @@ class StatsController < ApplicationController
|
|||
@satisfaction_usagers = satisfaction_usagers
|
||||
|
||||
@contact_percentage = contact_percentage
|
||||
@contact_percentage_excluded_tags = Helpscout::UserConversationsAdapter::EXCLUDED_TAGS
|
||||
|
||||
@dossiers_states = dossiers_states
|
||||
|
||||
|
@ -177,7 +176,7 @@ class StatsController < ApplicationController
|
|||
.map do |monthly_report|
|
||||
start_date = monthly_report[:start_date].to_time.localtime
|
||||
end_date = monthly_report[:end_date].to_time.localtime
|
||||
replies_count = monthly_report[:conversations_count]
|
||||
replies_count = monthly_report[:replies_sent]
|
||||
|
||||
dossiers_count = Dossier.where(en_construction_at: start_date..end_date).count
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ class Helpscout::API
|
|||
body = {
|
||||
subject: subject,
|
||||
customer: customer(email),
|
||||
mailboxId: mailbox_id,
|
||||
mailboxId: user_support_mailbox_id,
|
||||
type: 'email',
|
||||
status: 'active',
|
||||
threads: [
|
||||
|
@ -44,7 +44,7 @@ class Helpscout::API
|
|||
|
||||
def add_phone_number(email, phone)
|
||||
query = URI.encode("(email:#{email})")
|
||||
response = call_api(:get, "#{CUSTOMERS}?mailbox=#{mailbox_id}&query=#{query}")
|
||||
response = call_api(:get, "#{CUSTOMERS}?mailbox=#{user_support_mailbox_id}&query=#{query}")
|
||||
if response.success?
|
||||
body = parse_response_body(response)
|
||||
if body[:page][:totalElements] > 0
|
||||
|
@ -57,17 +57,18 @@ class Helpscout::API
|
|||
end
|
||||
end
|
||||
|
||||
def conversations_report(year, month)
|
||||
Rails.logger.info("[HelpScout API] Retrieving conversations report for #{month}-#{year}…")
|
||||
def productivity_report(year, month)
|
||||
Rails.logger.info("[HelpScout API] Retrieving productivity report for #{month}-#{year}…")
|
||||
|
||||
params = {
|
||||
mailboxes: [user_support_mailbox_id].join(','),
|
||||
start: Time.utc(year, month).iso8601,
|
||||
end: Time.utc(year, month).next_month.iso8601
|
||||
}
|
||||
|
||||
response = call_api(:get, 'reports/conversations?' + params.to_query)
|
||||
response = call_api(:get, 'reports/productivity?' + params.to_query)
|
||||
if !response.success?
|
||||
raise StandardError, "Error while fetching conversation report: #{response.response_code} '#{response.body}'"
|
||||
raise StandardError, "Error while fetching productivity report: #{response.response_code} '#{response.body}'"
|
||||
end
|
||||
|
||||
parse_response_body(response)
|
||||
|
@ -107,7 +108,7 @@ class Helpscout::API
|
|||
end
|
||||
|
||||
def fetch_custom_fields
|
||||
call_api(:get, "#{MAILBOXES}/#{mailbox_id}/#{FIELDS}")
|
||||
call_api(:get, "#{MAILBOXES}/#{user_support_mailbox_id}/#{FIELDS}")
|
||||
end
|
||||
|
||||
def call_api(method, path, body = nil)
|
||||
|
@ -135,7 +136,7 @@ class Helpscout::API
|
|||
JSON.parse(response.body, symbolize_names: true)
|
||||
end
|
||||
|
||||
def mailbox_id
|
||||
def user_support_mailbox_id
|
||||
Rails.application.secrets.helpscout[:mailbox_id]
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# Fetch and compute monthly reports about the users conversations on Helpscout
|
||||
class Helpscout::UserConversationsAdapter
|
||||
EXCLUDED_TAGS = ['openlab', 'bizdev', 'admin', 'webinaire']
|
||||
|
||||
def initialize(from, to)
|
||||
@from = from
|
||||
@to = to
|
||||
|
@ -23,19 +21,12 @@ class Helpscout::UserConversationsAdapter
|
|||
private
|
||||
|
||||
def report(year, month)
|
||||
report = fetch_conversations_report(year, month)
|
||||
|
||||
total_conversations = report.dig(:current, :totalConversations)
|
||||
excluded_conversations = report
|
||||
.dig(:tags, :top)
|
||||
.select { |tag| tag[:name]&.in?(EXCLUDED_TAGS) }
|
||||
.map { |tag| tag[:count] }
|
||||
.sum
|
||||
report = fetch_productivity_report(year, month)
|
||||
|
||||
{
|
||||
start_date: report.dig(:current, :startDate).to_datetime,
|
||||
end_date: report.dig(:current, :endDate).to_datetime,
|
||||
conversations_count: total_conversations - excluded_conversations
|
||||
replies_sent: report.dig(:current, :repliesSent)
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -43,13 +34,13 @@ class Helpscout::UserConversationsAdapter
|
|||
@api_client ||= Helpscout::API.new
|
||||
end
|
||||
|
||||
def fetch_conversations_report(year, month)
|
||||
def fetch_productivity_report(year, month)
|
||||
if year == Date.today.year && month == Date.today.month
|
||||
raise ArgumentError, 'The report for the current month will change in the future, and cannot be cached.'
|
||||
end
|
||||
|
||||
Rails.cache.fetch("helpscout-conversation-report-#{year}-#{month}") do
|
||||
api_client.conversations_report(year, month)
|
||||
Rails.cache.fetch("helpscout-productivity-report-#{year}-#{month}") do
|
||||
api_client.productivity_report(year, month)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,16 +34,15 @@
|
|||
|
||||
.stat-card.stat-card-half.pull-left
|
||||
%span.stat-card-title
|
||||
Pourcentage de contact usager
|
||||
Pourcentage de contact utilisateur
|
||||
|
||||
.chart-container
|
||||
.chart
|
||||
= line_chart @contact_percentage
|
||||
|
||||
.stat-card-details
|
||||
%abbr{ title: "À l’exception des conversations taggées #{@contact_percentage_excluded_tags.join(', ')}" }
|
||||
Nombre de conversations actives dans HelpScout
|
||||
%span / nombre de dossiers déposés
|
||||
Nombre de réponses envoyées via HelpScout
|
||||
\/ nombre de dossiers déposés
|
||||
|
||||
.stat-card.stat-card-half.pull-left
|
||||
%span.stat-card-title
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -15,9 +15,7 @@ describe Helpscout::UserConversationsAdapter do
|
|||
|
||||
context 'when all required secrets are present' do
|
||||
before do
|
||||
Rails.application.secrets.helpscout[:mailbox_id] = '9999'
|
||||
Rails.application.secrets.helpscout[:client_id] = '1234'
|
||||
Rails.application.secrets.helpscout[:client_secret] = '5678'
|
||||
mock_helpscout_secrets
|
||||
end
|
||||
|
||||
it { expect(described_class.new(from, to).can_fetch_reports?).to be true }
|
||||
|
@ -25,7 +23,10 @@ describe Helpscout::UserConversationsAdapter do
|
|||
end
|
||||
|
||||
describe '#reports', vcr: { cassette_name: 'helpscout_conversations_reports' } do
|
||||
before { Rails.cache.clear }
|
||||
before do
|
||||
mock_helpscout_secrets
|
||||
Rails.cache.clear
|
||||
end
|
||||
|
||||
subject { described_class.new(from, to) }
|
||||
|
||||
|
@ -34,13 +35,19 @@ describe Helpscout::UserConversationsAdapter do
|
|||
end
|
||||
|
||||
it 'populates each report with data' do
|
||||
expect(subject.reports.first[:conversations_count]).to be > 0
|
||||
expect(subject.reports.first[:replies_sent]).to be > 0
|
||||
expect(subject.reports.first[:start_date]).to eq Time.utc(2017, 11)
|
||||
expect(subject.reports.first[:end_date]).to eq Time.utc(2017, 12)
|
||||
|
||||
expect(subject.reports.last[:conversations_count]).to be > 0
|
||||
expect(subject.reports.last[:replies_sent]).to be > 0
|
||||
expect(subject.reports.last[:start_date]).to eq Time.utc(2017, 12)
|
||||
expect(subject.reports.last[:end_date]).to eq Time.utc(2018, 01)
|
||||
end
|
||||
end
|
||||
|
||||
def mock_helpscout_secrets
|
||||
Rails.application.secrets.helpscout[:mailbox_id] = '9999'
|
||||
Rails.application.secrets.helpscout[:client_id] = '1234'
|
||||
Rails.application.secrets.helpscout[:client_secret] = '5678'
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue