stats: display contact rate
This commit is contained in:
parent
dcb452d7e6
commit
d614ea6bd5
7 changed files with 282 additions and 0 deletions
|
@ -63,6 +63,12 @@ $stat-card-half-horizontal-spacing: 4 * $default-space;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stat-card-details {
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
$segmented-control-margin-top: $default-space;
|
$segmented-control-margin-top: $default-space;
|
||||||
|
|
||||||
.segmented-control {
|
.segmented-control {
|
||||||
|
|
|
@ -13,6 +13,10 @@ class StatsController < ApplicationController
|
||||||
@dossiers_numbers = dossiers_numbers(dossiers)
|
@dossiers_numbers = dossiers_numbers(dossiers)
|
||||||
|
|
||||||
@satisfaction_usagers = satisfaction_usagers
|
@satisfaction_usagers = satisfaction_usagers
|
||||||
|
|
||||||
|
@contact_percentage = contact_percentage
|
||||||
|
@contact_percentage_excluded_tags = Helpscout::UserConversationsAdapter::EXCLUDED_TAGS
|
||||||
|
|
||||||
@dossiers_states = dossiers_states
|
@dossiers_states = dossiers_states
|
||||||
|
|
||||||
@procedures_cumulative = cumulative_hash(procedures, :published_at)
|
@procedures_cumulative = cumulative_hash(procedures, :published_at)
|
||||||
|
@ -160,6 +164,24 @@ class StatsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def contact_percentage
|
||||||
|
from = Date.new(2017, 10)
|
||||||
|
to = Date.today.prev_month
|
||||||
|
|
||||||
|
Helpscout::UserConversationsAdapter.new(from, to)
|
||||||
|
.reports
|
||||||
|
.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]
|
||||||
|
|
||||||
|
dossiers_count = Dossier.where(en_construction_at: start_date..end_date).count
|
||||||
|
|
||||||
|
monthly_contact_percentage = replies_count.fdiv(dossiers_count || 1) * 100
|
||||||
|
[I18n.l(start_date, format: '%b %y'), monthly_contact_percentage.round(1)]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def cloned_from_library_procedures_ratio
|
def cloned_from_library_procedures_ratio
|
||||||
[3.weeks.ago, 2.weeks.ago, 1.week.ago].map do |date|
|
[3.weeks.ago, 2.weeks.ago, 1.week.ago].map do |date|
|
||||||
min_date = date.beginning_of_week
|
min_date = date.beginning_of_week
|
||||||
|
|
|
@ -59,6 +59,22 @@ class Helpscout::API
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def conversations_report(year, month)
|
||||||
|
Rails.logger.info("[HelpScout API] Retrieving conversations report for #{month}-#{year}…")
|
||||||
|
|
||||||
|
params = {
|
||||||
|
start: Time.utc(year, month).iso8601,
|
||||||
|
end: Time.utc(year, month).next_month.iso8601
|
||||||
|
}
|
||||||
|
|
||||||
|
response = call_api(:get, 'reports/conversations?' + params.to_query)
|
||||||
|
if !response.success?
|
||||||
|
raise StandardError, "Error while fetching conversation report: #{response.status} '#{response.body}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
parse_response_body(response)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def attachments(file)
|
def attachments(file)
|
||||||
|
|
49
app/lib/helpscout/user_conversations_adapter.rb
Normal file
49
app/lib/helpscout/user_conversations_adapter.rb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# 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
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return an array of monthly reports
|
||||||
|
def reports
|
||||||
|
@reports ||= (@from..@to)
|
||||||
|
.group_by { |date| [date.year, date.month] }
|
||||||
|
.keys
|
||||||
|
.map { |key| { year: key[0], month: key[1] } }
|
||||||
|
.map { |interval| report(interval[:year], interval[:month]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
{
|
||||||
|
start_date: report.dig(:current, :startDate).to_datetime,
|
||||||
|
end_date: report.dig(:current, :endDate).to_datetime,
|
||||||
|
conversations_count: total_conversations - excluded_conversations
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_conversations_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
|
||||||
|
|
||||||
|
@helpscout_api ||= Helpscout::API.new
|
||||||
|
|
||||||
|
Rails.cache.fetch("helpscout-conversation-report-#{year}-#{month}") do
|
||||||
|
@helpscout_api.conversations_report(year, month)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -28,6 +28,19 @@
|
||||||
= line_chart @satisfaction_usagers,
|
= line_chart @satisfaction_usagers,
|
||||||
colors: ["#15AD70", "#F28900", "rgba(161, 0, 5, 0.9)"]
|
colors: ["#15AD70", "#F28900", "rgba(161, 0, 5, 0.9)"]
|
||||||
|
|
||||||
|
.stat-card.stat-card-half.pull-left
|
||||||
|
%span.stat-card-title
|
||||||
|
Pourcentage de contact usagers
|
||||||
|
|
||||||
|
.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
|
||||||
|
|
||||||
.stat-card.stat-card-half.pull-left
|
.stat-card.stat-card-half.pull-left
|
||||||
%span.stat-card-title
|
%span.stat-card-title
|
||||||
Répartition des dossiers
|
Répartition des dossiers
|
||||||
|
|
150
spec/fixtures/cassettes/helpscout_conversations_reports.yml
vendored
Normal file
150
spec/fixtures/cassettes/helpscout_conversations_reports.yml
vendored
Normal file
File diff suppressed because one or more lines are too long
26
spec/lib/helpscout/user_conversations_adapter_spec.rb
Normal file
26
spec/lib/helpscout/user_conversations_adapter_spec.rb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Helpscout::UserConversationsAdapter do
|
||||||
|
describe '#reports', vcr: { cassette_name: 'helpscout_conversations_reports' } do
|
||||||
|
let(:from) { Date.new(2017, 11) }
|
||||||
|
let(:to) { Date.new(2017, 12) }
|
||||||
|
|
||||||
|
before { Rails.cache.clear }
|
||||||
|
|
||||||
|
subject { described_class.new(from, to) }
|
||||||
|
|
||||||
|
it 'returns one report result per month' do
|
||||||
|
expect(subject.reports.count).to eq 2
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'populates each report with data' do
|
||||||
|
expect(subject.reports.first[:conversations_count]).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[:start_date]).to eq Time.utc(2017, 12)
|
||||||
|
expect(subject.reports.last[:end_date]).to eq Time.utc(2018, 01)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue