Merge branch 'dev'

This commit is contained in:
gregoirenovel 2018-12-18 14:19:43 +01:00
commit 42171f762e
17 changed files with 338 additions and 25 deletions

View file

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

View file

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

View file

@ -10,6 +10,7 @@ class ProcedureDashboard < Administrate::BaseDashboard
ATTRIBUTE_TYPES = { ATTRIBUTE_TYPES = {
types_de_piece_justificative: TypesDePieceJustificativeCollectionField, types_de_piece_justificative: TypesDePieceJustificativeCollectionField,
types_de_champ: TypesDeChampCollectionField, types_de_champ: TypesDeChampCollectionField,
types_de_champ_private: TypesDeChampCollectionField,
path: ProcedureLinkField, path: ProcedureLinkField,
dossiers: Field::HasMany, dossiers: Field::HasMany,
gestionnaires: Field::HasMany, gestionnaires: Field::HasMany,
@ -68,6 +69,7 @@ class ProcedureDashboard < Administrate::BaseDashboard
:hidden_at, :hidden_at,
:archived_at, :archived_at,
:types_de_champ, :types_de_champ,
:types_de_champ_private,
:types_de_piece_justificative, :types_de_piece_justificative,
:for_individual, :for_individual,
:individual_with_siret, :individual_with_siret,

View file

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

View 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

View file

@ -2,7 +2,6 @@ class TypesDeChampService
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
TOGGLES = { TOGGLES = {
TypeDeChamp.type_champs.fetch(:piece_justificative) => :champ_pj?,
TypeDeChamp.type_champs.fetch(:siret) => :champ_siret?, TypeDeChamp.type_champs.fetch(:siret) => :champ_siret?,
TypeDeChamp.type_champs.fetch(:integer_number) => :champ_integer_number? TypeDeChamp.type_champs.fetch(:integer_number) => :champ_integer_number?
} }

View file

@ -16,7 +16,7 @@
- if champ.private? - if champ.private?
= link_to 'supprimer', gestionnaire_champ_purge_champ_piece_justificative_path(procedure_id: champ.dossier.procedure_id, dossier_id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete = link_to 'supprimer', gestionnaire_champ_purge_champ_piece_justificative_path(procedure_id: champ.dossier.procedure_id, dossier_id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete
- else - else
= link_to 'supprimer', purge_champ_piece_justificative_dossier_path(champ_id: champ.id), remote: true, method: :delete = link_to 'supprimer', champ_purge_champ_piece_justificative_path(id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete
%br %br
Modifier : Modifier :
= form.file_field :piece_justificative_file, = form.file_field :piece_justificative_file,

View file

@ -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: "À lexception 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

View file

@ -7,8 +7,6 @@ Flipflop.configure do
strategy :default strategy :default
group :champs do group :champs do
feature :champ_pj,
title: "Champ pièce justificative"
feature :champ_siret, feature :champ_siret,
title: "Champ SIRET" title: "Champ SIRET"
feature :champ_integer_number, feature :champ_integer_number,
@ -28,4 +26,12 @@ Flipflop.configure do
feature :pre_maintenance_mode feature :pre_maintenance_mode
feature :maintenance_mode feature :maintenance_mode
end end
if Rails.env.test?
# It would be nicer to configure this in administrateur_spec.rb in #feature_enabled?,
# but that results in a FrozenError: can't modify frozen Hash
feature :test_a
feature :test_b
end
end end

View file

@ -284,7 +284,10 @@ Rails.application.routes.draw do
post 'commentaire' => 'dossiers#create_commentaire' post 'commentaire' => 'dossiers#create_commentaire'
post 'ask_deletion' post 'ask_deletion'
get 'attestation' get 'attestation'
delete 'purge_champ_piece_justificative'
resources :champs, only: [] do
delete 'purge_champ_piece_justificative' => 'dossiers#purge_champ_piece_justificative'
end
end end
collection do collection do

View file

@ -4,7 +4,6 @@ namespace :'2018_05_21_cerfa_to_pj' do
dossiers.group_by(&:procedure).each do |procedure, dossiers| dossiers.group_by(&:procedure).each do |procedure, dossiers|
if !procedure.types_de_champ.find_by(libelle: 'CERFA') if !procedure.types_de_champ.find_by(libelle: 'CERFA')
procedure.administrateur.enable_feature(:champ_pj)
type_de_champ = procedure.types_de_champ.create( type_de_champ = procedure.types_de_champ.create(
type_champ: 'piece_justificative', type_champ: 'piece_justificative',
libelle: 'CERFA' libelle: 'CERFA'

View file

@ -0,0 +1,20 @@
namespace :after_party do
desc 'Deployment task: remove_champ_pj_feature'
task remove_champ_pj_feature: :environment do
rake_puts "Running deploy task 'remove_champ_pj_feature'"
Administrateur.find_by_sql(
<<~SQL
SELECT administrateurs.*
FROM administrateurs, lateral jsonb_each(features)
WHERE key = 'champ_pj'
GROUP BY id
SQL
).each do |admin|
admin.features.delete('champ_pj')
admin.save
end
AfterParty::TaskRecord.create version: '20181210185634'
end
end

View file

@ -1,6 +1,6 @@
require 'spec_helper' require 'spec_helper'
feature 'Signin up:' do feature 'Signing up:' do
let(:user_email) { generate :user_email } let(:user_email) { generate :user_email }
let(:user_password) { 'testpassword' } let(:user_password) { 'testpassword' }
@ -17,6 +17,22 @@ feature 'Signin up:' do
expect(page).to have_current_path dossiers_path expect(page).to have_current_path dossiers_path
end end
scenario 'a new user cant sign-up with too short password' do
visit root_path
click_on 'Connexion'
click_on 'Créer un compte'
expect(page).to have_current_path new_user_registration_path
sign_up_with user_email, '1234567'
expect(page).to have_current_path user_registration_path
expect(page).to have_content 'Le mot de passe est trop court'
# Then with a good password
sign_up_with user_email, user_password
expect(page).to have_current_path new_user_confirmation_path user: { email: user_email }
expect(page).to have_content "nous avons besoin de vérifier votre adresse #{user_email}"
end
context 'when visiting a procedure' do context 'when visiting a procedure' do
let(:procedure) { create :simple_procedure } let(:procedure) { create :simple_procedure }

File diff suppressed because one or more lines are too long

View 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

View file

@ -65,11 +65,11 @@ describe Administrateur, type: :model do
let(:administrateur) { create(:administrateur) } let(:administrateur) { create(:administrateur) }
before do before do
administrateur.enable_feature(:champ_pj) administrateur.enable_feature(:test_a)
end end
it { expect(administrateur.feature_enabled?(:champ_siret)).to be_falsey } it { expect(administrateur.feature_enabled?(:test_b)).to be_falsey }
it { expect(administrateur.feature_enabled?(:champ_pj)).to be_truthy } it { expect(administrateur.feature_enabled?(:test_a)).to be_truthy }
end end
describe "#password_complexity" do describe "#password_complexity" do

View file

@ -107,20 +107,6 @@ describe TypesDeChampService do
subject { service.options } subject { service.options }
context "when the champ_pj is enabled" do
before do
Flipflop::FeatureSet.current.test!.switch!(:champ_pj, true)
end
it { is_expected.to include(pj_option) } it { is_expected.to include(pj_option) }
end end
context "when the champ_pj is disabled" do
before do
Flipflop::FeatureSet.current.test!.switch!(:champ_pj, false)
end
it { is_expected.not_to include(pj_option) }
end
end
end end