From 701b72494db10715b7655ab5d06a104153d3df5e Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 11 May 2021 13:01:42 +0200 Subject: [PATCH 01/13] compute usual traitement time for specific month --- app/models/procedure.rb | 6 ++++- spec/models/procedure_spec.rb | 48 ++++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 0af87e5a9..abced3d34 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -566,10 +566,14 @@ class Procedure < ApplicationRecord end def usual_traitement_time + compute_usual_traitement_time_for_month(Time.zone.now) + end + + def compute_usual_traitement_time_for_month(month_date) times = Traitement.includes(:dossier) .where(dossier: self.dossiers) .where.not('dossiers.en_construction_at' => nil, :processed_at => nil) - .where(processed_at: 1.month.ago..Time.zone.now) + .where(processed_at: (month_date - 1.month)..month_date) .pluck('dossiers.en_construction_at', :processed_at) .map { |(en_construction_at, processed_at)| processed_at - en_construction_at } diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 1f7327fdb..8e4bebf22 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -983,10 +983,6 @@ describe Procedure do describe '#usual_traitement_time' do let(:procedure) { create(:procedure) } - def create_dossier(construction_date:, instruction_date:, processed_date:) - dossier = create(:dossier, :accepte, procedure: procedure, en_construction_at: construction_date, en_instruction_at: instruction_date, processed_at: processed_date) - end - before do Timecop.freeze(Time.utc(2019, 6, 1, 12, 0)) @@ -1034,6 +1030,36 @@ describe Procedure do end end + describe '.compute_usual_traitement_time_for_month' do + let(:procedure) { create(:procedure) } + + def create_dossiers(delays_by_month) + delays_by_month.each_with_index do |delays, index| + delays.each do |delay| + processed_date = (index.months + 1.week).ago + create_dossier(construction_date: processed_date - delay, instruction_date: processed_date - delay + 12.hours, processed_date: processed_date) + end + end + end + + before do + Timecop.freeze(Time.utc(2019, 6, 1, 12, 0)) + + create_dossiers(delays_by_month) + end + + after { Timecop.return } + + context 'when there are several processed dossiers' do + let(:delays_by_month) { [[1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days], [1.month, 2.months, 2.months, 2.months]] } + + it 'computes a time representative of the dossier instruction delay for a specific month' do + expect(procedure.compute_usual_traitement_time_for_month(Date.strptime("2019-05", "%Y-%m"))).to be_between(1.month, 2.months) + expect(procedure.compute_usual_traitement_time_for_month(Date.strptime("2019-06", "%Y-%m"))).to be_between(3.days, 4.days) + end + end + end + describe '.ensure_a_groupe_instructeur_exists' do let!(:procedure) { create(:procedure) } @@ -1082,12 +1108,16 @@ describe Procedure do it 'estimates average dossier weight' do expect(procedure.reload.average_dossier_weight).to eq 5 end + end - private + private - def create_dossier_with_pj_of_size(size, procedure) - dossier = create(:dossier, :accepte, procedure: procedure) - create(:champ_piece_justificative, size: size, dossier: dossier) - end + def create_dossier_with_pj_of_size(size, procedure) + dossier = create(:dossier, :accepte, procedure: procedure) + create(:champ_piece_justificative, size: size, dossier: dossier) + end + + def create_dossier(construction_date:, instruction_date:, processed_date:) + dossier = create(:dossier, :accepte, procedure: procedure, en_construction_at: construction_date, en_instruction_at: instruction_date, processed_at: processed_date) end end From 45ffae9eb3888e59ef83e38f4ce754d46dfb195d Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Fri, 4 Jun 2021 16:19:19 +0200 Subject: [PATCH 02/13] affiche evolution temps de traitement --- .../instructeurs/procedures_controller.rb | 1 + .../users/statistiques_controller.rb | 1 + .../concerns/procedure_stats_concern.rb | 6 ++++ app/models/procedure.rb | 31 +++++++++++++++++++ app/views/shared/procedures/_stats.html.haml | 8 +++++ 5 files changed, 47 insertions(+) diff --git a/app/controllers/instructeurs/procedures_controller.rb b/app/controllers/instructeurs/procedures_controller.rb index b96c83dc5..91b3c4f03 100644 --- a/app/controllers/instructeurs/procedures_controller.rb +++ b/app/controllers/instructeurs/procedures_controller.rb @@ -210,6 +210,7 @@ module Instructeurs @dossiers_funnel = @procedure.stats_dossiers_funnel @termines_states = @procedure.stats_termines_states @termines_by_week = @procedure.stats_termines_by_week + @usual_traitement_time_by_month = @procedure.usual_traitement_time_by_month_in_days end private diff --git a/app/controllers/users/statistiques_controller.rb b/app/controllers/users/statistiques_controller.rb index 8b1611027..5ccafc2bf 100644 --- a/app/controllers/users/statistiques_controller.rb +++ b/app/controllers/users/statistiques_controller.rb @@ -5,6 +5,7 @@ module Users return procedure_not_found if @procedure.blank? || @procedure.brouillon? @usual_traitement_time = @procedure.stats_usual_traitement_time + @usual_traitement_time_by_month = @procedure.stats_usual_traitement_time_by_month_in_days @dossiers_funnel = @procedure.stats_dossiers_funnel @termines_states = @procedure.stats_termines_states @termines_by_week = @procedure.stats_termines_by_week diff --git a/app/models/concerns/procedure_stats_concern.rb b/app/models/concerns/procedure_stats_concern.rb index 6c8414e8c..b930387ad 100644 --- a/app/models/concerns/procedure_stats_concern.rb +++ b/app/models/concerns/procedure_stats_concern.rb @@ -7,6 +7,12 @@ module ProcedureStatsConcern end end + def stats_usual_traitement_time_by_month_in_days + Rails.cache.fetch("#{cache_key_with_version}/stats_usual_traitement_time_by_month_in_days", expires_in: 12.hours) do + usual_traitement_time_by_month_in_days + end + end + def stats_dossiers_funnel Rails.cache.fetch("#{cache_key_with_version}/stats_dossiers_funnel", expires_in: 12.hours) do [ diff --git a/app/models/procedure.rb b/app/models/procedure.rb index abced3d34..1f1c0b159 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -582,6 +582,37 @@ class Procedure < ApplicationRecord end end + def usual_traitement_time_by_month + first_processed_at = Traitement.includes(:dossier) + .where(dossier: self.dossiers) + .where.not('dossiers.en_construction_at' => nil, :processed_at => nil) + .order(:processed_at) + .pick(:processed_at) + + return [] if first_processed_at.nil? + month_index = first_processed_at.at_end_of_month + month_range = [] + while month_index <= Time.zone.now.at_end_of_month + month_range << month_index + month_index += 1.month + end + + month_range.map do |month| + [I18n.l(month, format: "%B %Y"), compute_usual_traitement_time_for_month(month)] + end + end + + def usual_traitement_time_by_month_in_days + usual_traitement_time_by_month.map do |month, time_in_seconds| + if time_in_seconds.present? + time_in_days = (time_in_seconds / 60.0 / 60.0 / 24.0).ceil + else + time_in_days = nil + end + [month, time_in_days] + end + end + def populate_champ_stable_ids TypeDeChamp .joins(:revisions) diff --git a/app/views/shared/procedures/_stats.html.haml b/app/views/shared/procedures/_stats.html.haml index fb6925914..c049cb3f7 100644 --- a/app/views/shared/procedures/_stats.html.haml +++ b/app/views/shared/procedures/_stats.html.haml @@ -13,6 +13,14 @@ %span.big-number-card-detail 90% des demandes du mois dernier ont été traitées en moins de #{distance_of_time_in_words(@usual_traitement_time)}. + .stat-cards + .stat-card.stat-card-half.pull-left + %span.stat-card-title TEMPS DE TRAITEMENT + .chart-container + .chart + - colors = %w(#C3D9FF #0069CC #1C7EC9) # from _colors.scss + = column_chart @usual_traitement_time_by_month, ytitle: "Nb Jours" + .stat-cards .stat-card.stat-card-half.pull-left %span.stat-card-title AVANCÉE DES DOSSIERS From 98f0eb5ba956e1cf3344b3efa28d4cc39d8f4ee1 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 8 Jun 2021 14:47:36 +0200 Subject: [PATCH 03/13] =?UTF-8?q?ajoute=20legende=20=C3=A0=20la=20stat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/shared/procedures/_stats.html.haml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/app/views/shared/procedures/_stats.html.haml b/app/views/shared/procedures/_stats.html.haml index c049cb3f7..f0079e957 100644 --- a/app/views/shared/procedures/_stats.html.haml +++ b/app/views/shared/procedures/_stats.html.haml @@ -19,15 +19,15 @@ .chart-container .chart - colors = %w(#C3D9FF #0069CC #1C7EC9) # from _colors.scss - = column_chart @usual_traitement_time_by_month, ytitle: "Nb Jours" + = column_chart @usual_traitement_time_by_month, ytitle: "Nb Jours", legend: "bottom", label: "Temps de traitement entre le passage en instruction et la réponse (accepté, refusé, ou classé sans suite) pour 90% des dossiers" - .stat-cards .stat-card.stat-card-half.pull-left %span.stat-card-title AVANCÉE DES DOSSIERS .chart-container .chart = area_chart @dossiers_funnel + .stat-cards .stat-card.stat-card-half.pull-left %span.stat-card-title TAUX D’ACCEPTATION .chart-container @@ -35,9 +35,8 @@ - colors = %w(#C3D9FF #0069CC #1C7EC9) # from _colors.scss = pie_chart @termines_states, colors: colors - .stat-cards - .stat-card.stat-card-half.pull-left - %span.stat-card-title RÉPARTITION PAR SEMAINE - .chart-container - .chart - = line_chart @termines_by_week, colors: ["#387EC3", "#AE2C2B", "#FAD859"] + .stat-card.stat-card-half.pull-left + %span.stat-card-title RÉPARTITION PAR SEMAINE + .chart-container + .chart + = line_chart @termines_by_week, colors: ["#387EC3", "#AE2C2B", "#FAD859"] From a8840faadb0a281f53040f2cd77e0a88ad06a274 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 15 Jun 2021 11:33:17 +0200 Subject: [PATCH 04/13] move stats methods to appropriate concern class --- .../concerns/procedure_stats_concern.rb | 49 +++++++++++ app/models/procedure.rb | 48 ---------- .../concern/procedure_stats_concern_spec.rb | 87 +++++++++++++++++++ spec/models/procedure_spec.rb | 84 ------------------ 4 files changed, 136 insertions(+), 132 deletions(-) create mode 100644 spec/models/concern/procedure_stats_concern_spec.rb diff --git a/app/models/concerns/procedure_stats_concern.rb b/app/models/concerns/procedure_stats_concern.rb index b930387ad..0bde96361 100644 --- a/app/models/concerns/procedure_stats_concern.rb +++ b/app/models/concerns/procedure_stats_concern.rb @@ -51,4 +51,53 @@ module ProcedureStatsConcern end end end + + def usual_traitement_time + compute_usual_traitement_time_for_month(Time.zone.now) + end + + def compute_usual_traitement_time_for_month(month_date) + times = Traitement.includes(:dossier) + .where(dossier: self.dossiers) + .where.not('dossiers.en_construction_at' => nil, :processed_at => nil) + .where(processed_at: (month_date - 1.month)..month_date) + .pluck('dossiers.en_construction_at', :processed_at) + .map { |(en_construction_at, processed_at)| processed_at - en_construction_at } + + if times.present? + times.percentile(90).ceil + end + end + + def usual_traitement_time_by_month + first_processed_at = Traitement.includes(:dossier) + .where(dossier: self.dossiers) + .where.not('dossiers.en_construction_at' => nil, :processed_at => nil) + .order(:processed_at) + .pick(:processed_at) + + return [] if first_processed_at.nil? + month_index = first_processed_at.at_end_of_month + month_range = [] + while month_index <= Time.zone.now.at_end_of_month + month_range << month_index + month_index += 1.month + end + + month_range.map do |month| + [I18n.l(month, format: "%B %Y"), compute_usual_traitement_time_for_month(month)] + end + end + + def usual_traitement_time_by_month_in_days + usual_traitement_time_by_month.map do |month, time_in_seconds| + if time_in_seconds.present? + time_in_days = (time_in_seconds / 60.0 / 60.0 / 24.0).ceil + else + time_in_days = nil + end + [month, time_in_days] + end + end + end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 1f1c0b159..07ca5e6ff 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -565,54 +565,6 @@ class Procedure < ApplicationRecord end end - def usual_traitement_time - compute_usual_traitement_time_for_month(Time.zone.now) - end - - def compute_usual_traitement_time_for_month(month_date) - times = Traitement.includes(:dossier) - .where(dossier: self.dossiers) - .where.not('dossiers.en_construction_at' => nil, :processed_at => nil) - .where(processed_at: (month_date - 1.month)..month_date) - .pluck('dossiers.en_construction_at', :processed_at) - .map { |(en_construction_at, processed_at)| processed_at - en_construction_at } - - if times.present? - times.percentile(90).ceil - end - end - - def usual_traitement_time_by_month - first_processed_at = Traitement.includes(:dossier) - .where(dossier: self.dossiers) - .where.not('dossiers.en_construction_at' => nil, :processed_at => nil) - .order(:processed_at) - .pick(:processed_at) - - return [] if first_processed_at.nil? - month_index = first_processed_at.at_end_of_month - month_range = [] - while month_index <= Time.zone.now.at_end_of_month - month_range << month_index - month_index += 1.month - end - - month_range.map do |month| - [I18n.l(month, format: "%B %Y"), compute_usual_traitement_time_for_month(month)] - end - end - - def usual_traitement_time_by_month_in_days - usual_traitement_time_by_month.map do |month, time_in_seconds| - if time_in_seconds.present? - time_in_days = (time_in_seconds / 60.0 / 60.0 / 24.0).ceil - else - time_in_days = nil - end - [month, time_in_days] - end - end - def populate_champ_stable_ids TypeDeChamp .joins(:revisions) diff --git a/spec/models/concern/procedure_stats_concern_spec.rb b/spec/models/concern/procedure_stats_concern_spec.rb new file mode 100644 index 000000000..bf746aa27 --- /dev/null +++ b/spec/models/concern/procedure_stats_concern_spec.rb @@ -0,0 +1,87 @@ +describe ProcedureStatsConcern do + describe '#usual_traitement_time' do + let(:procedure) { create(:procedure) } + + before do + Timecop.freeze(Time.utc(2019, 6, 1, 12, 0)) + + delays.each do |delay| + create_dossier(construction_date: 1.week.ago - delay, instruction_date: 1.week.ago - delay + 12.hours, processed_date: 1.week.ago) + end + end + + after { Timecop.return } + + context 'when there are several processed dossiers' do + let(:delays) { [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days] } + + it 'returns a time representative of the dossier instruction delay' do + expect(procedure.usual_traitement_time).to be_between(3.days, 4.days) + end + end + + context 'when there are very old dossiers' do + let(:delays) { [2.days, 2.days] } + let!(:old_dossier) { create_dossier(construction_date: 3.months.ago, instruction_date: 2.months.ago, processed_date: 2.months.ago) } + + it 'ignores dossiers older than 1 month' do + expect(procedure.usual_traitement_time).to be_within(1.hour).of(2.days) + end + end + + context 'when there is a dossier with bad data' do + let(:delays) { [2.days, 2.days] } + let!(:bad_dossier) { create_dossier(construction_date: nil, instruction_date: nil, processed_date: 10.days.ago) } + + it 'ignores bad dossiers' do + expect(procedure.usual_traitement_time).to be_within(1.hour).of(2.days) + end + end + + context 'when there is only one processed dossier' do + let(:delays) { [1.day] } + it { expect(procedure.usual_traitement_time).to be_within(1.hour).of(1.day) } + end + + context 'where there is no processed dossier' do + let(:delays) { [] } + it { expect(procedure.usual_traitement_time).to be_nil } + end + end + + describe '.compute_usual_traitement_time_for_month' do + let(:procedure) { create(:procedure) } + + def create_dossiers(delays_by_month) + delays_by_month.each_with_index do |delays, index| + delays.each do |delay| + processed_date = (index.months + 1.week).ago + create_dossier(construction_date: processed_date - delay, instruction_date: processed_date - delay + 12.hours, processed_date: processed_date) + end + end + end + + before do + Timecop.freeze(Time.utc(2019, 6, 1, 12, 0)) + + create_dossiers(delays_by_month) + end + + after { Timecop.return } + + context 'when there are several processed dossiers' do + let(:delays_by_month) { [[1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days], [1.month, 2.months, 2.months, 2.months]] } + + it 'computes a time representative of the dossier instruction delay for a specific month' do + expect(procedure.compute_usual_traitement_time_for_month(Date.strptime("2019-05", "%Y-%m"))).to be_between(1.month, 2.months) + expect(procedure.compute_usual_traitement_time_for_month(Date.strptime("2019-06", "%Y-%m"))).to be_between(3.days, 4.days) + end + end + end + + private + + def create_dossier(construction_date:, instruction_date:, processed_date:) + dossier = create(:dossier, :accepte, procedure: procedure, en_construction_at: construction_date, en_instruction_at: instruction_date, processed_at: processed_date) + end +end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 8e4bebf22..9850686d4 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -980,86 +980,6 @@ describe Procedure do end end - describe '#usual_traitement_time' do - let(:procedure) { create(:procedure) } - - before do - Timecop.freeze(Time.utc(2019, 6, 1, 12, 0)) - - delays.each do |delay| - create_dossier(construction_date: 1.week.ago - delay, instruction_date: 1.week.ago - delay + 12.hours, processed_date: 1.week.ago) - end - end - - after { Timecop.return } - - context 'when there are several processed dossiers' do - let(:delays) { [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days] } - - it 'returns a time representative of the dossier instruction delay' do - expect(procedure.usual_traitement_time).to be_between(3.days, 4.days) - end - end - - context 'when there are very old dossiers' do - let(:delays) { [2.days, 2.days] } - let!(:old_dossier) { create_dossier(construction_date: 3.months.ago, instruction_date: 2.months.ago, processed_date: 2.months.ago) } - - it 'ignores dossiers older than 1 month' do - expect(procedure.usual_traitement_time).to be_within(1.hour).of(2.days) - end - end - - context 'when there is a dossier with bad data' do - let(:delays) { [2.days, 2.days] } - let!(:bad_dossier) { create_dossier(construction_date: nil, instruction_date: nil, processed_date: 10.days.ago) } - - it 'ignores bad dossiers' do - expect(procedure.usual_traitement_time).to be_within(1.hour).of(2.days) - end - end - - context 'when there is only one processed dossier' do - let(:delays) { [1.day] } - it { expect(procedure.usual_traitement_time).to be_within(1.hour).of(1.day) } - end - - context 'where there is no processed dossier' do - let(:delays) { [] } - it { expect(procedure.usual_traitement_time).to be_nil } - end - end - - describe '.compute_usual_traitement_time_for_month' do - let(:procedure) { create(:procedure) } - - def create_dossiers(delays_by_month) - delays_by_month.each_with_index do |delays, index| - delays.each do |delay| - processed_date = (index.months + 1.week).ago - create_dossier(construction_date: processed_date - delay, instruction_date: processed_date - delay + 12.hours, processed_date: processed_date) - end - end - end - - before do - Timecop.freeze(Time.utc(2019, 6, 1, 12, 0)) - - create_dossiers(delays_by_month) - end - - after { Timecop.return } - - context 'when there are several processed dossiers' do - let(:delays_by_month) { [[1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days], [1.month, 2.months, 2.months, 2.months]] } - - it 'computes a time representative of the dossier instruction delay for a specific month' do - expect(procedure.compute_usual_traitement_time_for_month(Date.strptime("2019-05", "%Y-%m"))).to be_between(1.month, 2.months) - expect(procedure.compute_usual_traitement_time_for_month(Date.strptime("2019-06", "%Y-%m"))).to be_between(3.days, 4.days) - end - end - end - describe '.ensure_a_groupe_instructeur_exists' do let!(:procedure) { create(:procedure) } @@ -1116,8 +1036,4 @@ describe Procedure do dossier = create(:dossier, :accepte, procedure: procedure) create(:champ_piece_justificative, size: size, dossier: dossier) end - - def create_dossier(construction_date:, instruction_date:, processed_date:) - dossier = create(:dossier, :accepte, procedure: procedure, en_construction_at: construction_date, en_instruction_at: instruction_date, processed_at: processed_date) - end end From 8cbbae89842be584444c90c4d49667eccb556c6a Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 15 Jun 2021 17:40:52 +0200 Subject: [PATCH 05/13] use cache for traitement time for instructeurs --- app/controllers/instructeurs/procedures_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/instructeurs/procedures_controller.rb b/app/controllers/instructeurs/procedures_controller.rb index 91b3c4f03..bb556675c 100644 --- a/app/controllers/instructeurs/procedures_controller.rb +++ b/app/controllers/instructeurs/procedures_controller.rb @@ -210,7 +210,7 @@ module Instructeurs @dossiers_funnel = @procedure.stats_dossiers_funnel @termines_states = @procedure.stats_termines_states @termines_by_week = @procedure.stats_termines_by_week - @usual_traitement_time_by_month = @procedure.usual_traitement_time_by_month_in_days + @usual_traitement_time_by_month = @procedure.stats_usual_traitement_time_by_month_in_days end private From 55efe367bf8e3745749114bb013f412c7b379c49 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 16 Jun 2021 13:20:06 +0200 Subject: [PATCH 06/13] percentile returns always an integer --- lib/core_ext/array.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core_ext/array.rb b/lib/core_ext/array.rb index 00aa9f59c..39d7ad777 100644 --- a/lib/core_ext/array.rb +++ b/lib/core_ext/array.rb @@ -17,7 +17,7 @@ class Array values = self.sort if values.empty? - return [] + return 0 elsif values.size == 1 return values.first elsif p == 100 From 21dd05cc569f11e279953dee7ed7c2f7a810186e Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 16 Jun 2021 10:51:56 +0200 Subject: [PATCH 07/13] less sql queries for usual_traitement_time_by_month --- .../concerns/procedure_stats_concern.rb | 56 ++++++++++--------- app/models/traitement.rb | 7 +++ 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/app/models/concerns/procedure_stats_concern.rb b/app/models/concerns/procedure_stats_concern.rb index 0bde96361..80265b3ba 100644 --- a/app/models/concerns/procedure_stats_concern.rb +++ b/app/models/concerns/procedure_stats_concern.rb @@ -52,6 +52,22 @@ module ProcedureStatsConcern end end + def traitement_times(date_range) + Traitement.for_traitement_time_stats(self) + .where(processed_at: date_range) + .pluck('dossiers.en_construction_at', :processed_at) + .map{|en_construction_at, processed_at| { en_construction_at: en_construction_at, processed_at: processed_at }} + end + + def usual_traitement_time_by_month_in_days + traitement_times(first_processed_at..last_considered_processed_at) + .group_by {|t| t[:processed_at].beginning_of_month } + .transform_values{|month| month.map{|h| h[:processed_at] - h[:en_construction_at]}} + .transform_values{|traitement_times_for_month| traitement_times_for_month.percentile(90).ceil } + .transform_values{|seconds| convert_seconds_in_days(seconds)} + .transform_keys{|month| pretty_month(month)} + end + def usual_traitement_time compute_usual_traitement_time_for_month(Time.zone.now) end @@ -69,35 +85,21 @@ module ProcedureStatsConcern end end - def usual_traitement_time_by_month - first_processed_at = Traitement.includes(:dossier) - .where(dossier: self.dossiers) - .where.not('dossiers.en_construction_at' => nil, :processed_at => nil) - .order(:processed_at) - .pick(:processed_at) - - return [] if first_processed_at.nil? - month_index = first_processed_at.at_end_of_month - month_range = [] - while month_index <= Time.zone.now.at_end_of_month - month_range << month_index - month_index += 1.month - end - - month_range.map do |month| - [I18n.l(month, format: "%B %Y"), compute_usual_traitement_time_for_month(month)] - end + private + def first_processed_at + Traitement.for_traitement_time_stats(self).pick(:processed_at) end - def usual_traitement_time_by_month_in_days - usual_traitement_time_by_month.map do |month, time_in_seconds| - if time_in_seconds.present? - time_in_days = (time_in_seconds / 60.0 / 60.0 / 24.0).ceil - else - time_in_days = nil - end - [month, time_in_days] - end + def last_considered_processed_at + (Time.zone.now - 1.month).end_of_month + end + + def convert_seconds_in_days(seconds) + (seconds / 60.0 / 60.0 / 24.0).ceil + end + + def pretty_month(month) + I18n.l(month, format: "%B %Y") end end diff --git a/app/models/traitement.rb b/app/models/traitement.rb index 8bb604945..52d67fd92 100644 --- a/app/models/traitement.rb +++ b/app/models/traitement.rb @@ -19,6 +19,13 @@ class Traitement < ApplicationRecord .where("traitements.processed_at + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: Dossier::INTERVAL_BEFORE_EXPIRATION }) end + scope :for_traitement_time_stats, ->(procedure) do + includes(:dossier) + .where(dossier: procedure.dossiers) + .where.not('dossiers.en_construction_at' => nil, :processed_at => nil) + .order(:processed_at) + end + def self.count_dossiers_termines_by_month(groupe_instructeurs) last_traitements_per_dossier = Traitement .select('max(traitements.processed_at) as processed_at') From f2deba733d8398cc65df4bd8777bbd972238d6fe Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 16 Jun 2021 13:23:03 +0200 Subject: [PATCH 08/13] use traitement_times to compute usual_traitement_time for recent dossiers --- .../concerns/procedure_stats_concern.rb | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/app/models/concerns/procedure_stats_concern.rb b/app/models/concerns/procedure_stats_concern.rb index 80265b3ba..3c3dcf33b 100644 --- a/app/models/concerns/procedure_stats_concern.rb +++ b/app/models/concerns/procedure_stats_concern.rb @@ -3,7 +3,7 @@ module ProcedureStatsConcern def stats_usual_traitement_time Rails.cache.fetch("#{cache_key_with_version}/stats_usual_traitement_time", expires_in: 12.hours) do - usual_traitement_time + usual_traitement_time_for_recent_dossiers(30) end end @@ -68,21 +68,12 @@ module ProcedureStatsConcern .transform_keys{|month| pretty_month(month)} end - def usual_traitement_time - compute_usual_traitement_time_for_month(Time.zone.now) - end - - def compute_usual_traitement_time_for_month(month_date) - times = Traitement.includes(:dossier) - .where(dossier: self.dossiers) - .where.not('dossiers.en_construction_at' => nil, :processed_at => nil) - .where(processed_at: (month_date - 1.month)..month_date) - .pluck('dossiers.en_construction_at', :processed_at) - .map { |(en_construction_at, processed_at)| processed_at - en_construction_at } - - if times.present? - times.percentile(90).ceil - end + def usual_traitement_time_for_recent_dossiers(nb_days) + now = Time.zone.now + traitement_times((now - nb_days.days)..now) + .map{|times| times[:processed_at] - times[:en_construction_at]} + .percentile(90) + .ceil end private From bdf2f47eb9fa9c3bc05df126bf648f22c3ae6663 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 16 Jun 2021 14:41:09 +0200 Subject: [PATCH 09/13] fix specs for procedure stats --- .../concern/procedure_stats_concern_spec.rb | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/spec/models/concern/procedure_stats_concern_spec.rb b/spec/models/concern/procedure_stats_concern_spec.rb index bf746aa27..0669598ee 100644 --- a/spec/models/concern/procedure_stats_concern_spec.rb +++ b/spec/models/concern/procedure_stats_concern_spec.rb @@ -1,5 +1,5 @@ describe ProcedureStatsConcern do - describe '#usual_traitement_time' do + describe '#usual_traitement_time_for_recent_dossiers' do let(:procedure) { create(:procedure) } before do @@ -16,7 +16,7 @@ describe ProcedureStatsConcern do let(:delays) { [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days] } it 'returns a time representative of the dossier instruction delay' do - expect(procedure.usual_traitement_time).to be_between(3.days, 4.days) + expect(procedure.usual_traitement_time_for_recent_dossiers(30)).to be_between(3.days, 4.days) end end @@ -25,7 +25,7 @@ describe ProcedureStatsConcern do let!(:old_dossier) { create_dossier(construction_date: 3.months.ago, instruction_date: 2.months.ago, processed_date: 2.months.ago) } it 'ignores dossiers older than 1 month' do - expect(procedure.usual_traitement_time).to be_within(1.hour).of(2.days) + expect(procedure.usual_traitement_time_for_recent_dossiers(30)).to be_within(1.hour).of(2.days) end end @@ -34,22 +34,22 @@ describe ProcedureStatsConcern do let!(:bad_dossier) { create_dossier(construction_date: nil, instruction_date: nil, processed_date: 10.days.ago) } it 'ignores bad dossiers' do - expect(procedure.usual_traitement_time).to be_within(1.hour).of(2.days) + expect(procedure.usual_traitement_time_for_recent_dossiers(30)).to be_within(1.hour).of(2.days) end end context 'when there is only one processed dossier' do let(:delays) { [1.day] } - it { expect(procedure.usual_traitement_time).to be_within(1.hour).of(1.day) } + it { expect(procedure.usual_traitement_time_for_recent_dossiers(30)).to be_within(1.hour).of(1.day) } end context 'where there is no processed dossier' do let(:delays) { [] } - it { expect(procedure.usual_traitement_time).to be_nil } + it { expect(procedure.usual_traitement_time_for_recent_dossiers(30)).to eq 0 } end end - describe '.compute_usual_traitement_time_for_month' do + describe '.usual_traitement_time_by_month_in_days' do let(:procedure) { create(:procedure) } def create_dossiers(delays_by_month) @@ -62,7 +62,7 @@ describe ProcedureStatsConcern do end before do - Timecop.freeze(Time.utc(2019, 6, 1, 12, 0)) + Timecop.freeze(Time.utc(2019, 6, 25, 12, 0)) create_dossiers(delays_by_month) end @@ -70,11 +70,16 @@ describe ProcedureStatsConcern do after { Timecop.return } context 'when there are several processed dossiers' do - let(:delays_by_month) { [[1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days], [1.month, 2.months, 2.months, 2.months]] } + let(:delays_by_month) { [ + [90.days, 90.days], + [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days], + [30.days, 60.days, 60.days, 60.days] + ] } - it 'computes a time representative of the dossier instruction delay for a specific month' do - expect(procedure.compute_usual_traitement_time_for_month(Date.strptime("2019-05", "%Y-%m"))).to be_between(1.month, 2.months) - expect(procedure.compute_usual_traitement_time_for_month(Date.strptime("2019-06", "%Y-%m"))).to be_between(3.days, 4.days) + it 'computes a time representative of the dossier instruction delay for each month except current month' do + expect(procedure.usual_traitement_time_by_month_in_days['avril 2019']).to eq 60 + expect(procedure.usual_traitement_time_by_month_in_days['mai 2019']).to eq 4 + expect(procedure.usual_traitement_time_by_month_in_days['juin 2019']).to eq nil end end end From a5ae5af56af6a64d06a3ced99e85f57181e575f7 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 16 Jun 2021 14:57:42 +0200 Subject: [PATCH 10/13] extract NB_DAYS_RECENT_DOSSIERS and PERCENTILE --- app/models/concerns/procedure_stats_concern.rb | 9 ++++++--- app/views/shared/procedures/_stats.html.haml | 2 +- app/views/users/dossiers/show/_estimated_delay.html.haml | 6 +++--- .../dossiers/show/_status_overview.html.haml_spec.rb | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/models/concerns/procedure_stats_concern.rb b/app/models/concerns/procedure_stats_concern.rb index 3c3dcf33b..868a7a7fe 100644 --- a/app/models/concerns/procedure_stats_concern.rb +++ b/app/models/concerns/procedure_stats_concern.rb @@ -1,9 +1,12 @@ module ProcedureStatsConcern extend ActiveSupport::Concern + NB_DAYS_RECENT_DOSSIERS = 30 + PERCENTILE = 90 + def stats_usual_traitement_time Rails.cache.fetch("#{cache_key_with_version}/stats_usual_traitement_time", expires_in: 12.hours) do - usual_traitement_time_for_recent_dossiers(30) + usual_traitement_time_for_recent_dossiers(NB_DAYS_RECENT_DOSSIERS) end end @@ -63,7 +66,7 @@ module ProcedureStatsConcern traitement_times(first_processed_at..last_considered_processed_at) .group_by {|t| t[:processed_at].beginning_of_month } .transform_values{|month| month.map{|h| h[:processed_at] - h[:en_construction_at]}} - .transform_values{|traitement_times_for_month| traitement_times_for_month.percentile(90).ceil } + .transform_values{|traitement_times_for_month| traitement_times_for_month.percentile(PERCENTILE).ceil } .transform_values{|seconds| convert_seconds_in_days(seconds)} .transform_keys{|month| pretty_month(month)} end @@ -72,7 +75,7 @@ module ProcedureStatsConcern now = Time.zone.now traitement_times((now - nb_days.days)..now) .map{|times| times[:processed_at] - times[:en_construction_at]} - .percentile(90) + .percentile(PERCENTILE) .ceil end diff --git a/app/views/shared/procedures/_stats.html.haml b/app/views/shared/procedures/_stats.html.haml index f0079e957..49df73a62 100644 --- a/app/views/shared/procedures/_stats.html.haml +++ b/app/views/shared/procedures/_stats.html.haml @@ -11,7 +11,7 @@ %span.big-number-card-number = distance_of_time_in_words(@usual_traitement_time) %span.big-number-card-detail - 90% des demandes du mois dernier ont été traitées en moins de #{distance_of_time_in_words(@usual_traitement_time)}. + #{ProcedureStatsConcern::PERCENTILE}% des demandes des #{ProcedureStatsConcern::NB_DAYS_RECENT_DOSSIERS} derniers jours ont été traitées en moins de #{distance_of_time_in_words(@usual_traitement_time)}. .stat-cards .stat-card.stat-card-half.pull-left diff --git a/app/views/users/dossiers/show/_estimated_delay.html.haml b/app/views/users/dossiers/show/_estimated_delay.html.haml index eabc51bb3..7b9e6acca 100644 --- a/app/views/users/dossiers/show/_estimated_delay.html.haml +++ b/app/views/users/dossiers/show/_estimated_delay.html.haml @@ -4,8 +4,8 @@ - show_time_means = procedure.id != procedure_id_for_which_we_hide_the_estimated_delay && procedure.path != procedure_path_for_which_we_hide_the_estimated_delay - cache(procedure.id, expires_in: 1.day) do - - if procedure.usual_traitement_time && show_time_means + - if procedure.usual_traitement_time_for_recent_dossiers(ProcedureStatsConcern::NB_DAYS_RECENT_DOSSIERS) && show_time_means %p - Habituellement, les dossiers de cette démarche sont traités dans un délai de #{distance_of_time_in_words(procedure.usual_traitement_time)}. + Habituellement, les dossiers de cette démarche sont traités dans un délai de #{distance_of_time_in_words(procedure.usual_traitement_time_for_recent_dossiers(ProcedureStatsConcern::NB_DAYS_RECENT_DOSSIERS))}. %p - Cette estimation est calculée automatiquement à partir des délais d’instruction constatés précédemment. Le délai réel peut être différent, en fonction du type de démarche (par exemple pour un appel à projet avec date de décision fixe). + Cette estimation est calculée automatiquement à partir des délais d’instruction constatés sur #{ProcedureStatsConcern::PERCENTILE}% des demandes qui ont été traitées lors des #{ProcedureStatsConcern::NB_DAYS_RECENT_DOSSIERS} derniers jours. Le délai réel peut être différent, en fonction du type de démarche (par exemple pour un appel à projet avec date de décision fixe). diff --git a/spec/views/users/dossiers/show/_status_overview.html.haml_spec.rb b/spec/views/users/dossiers/show/_status_overview.html.haml_spec.rb index 71362ea7d..5f95bd3b7 100644 --- a/spec/views/users/dossiers/show/_status_overview.html.haml_spec.rb +++ b/spec/views/users/dossiers/show/_status_overview.html.haml_spec.rb @@ -1,5 +1,5 @@ describe 'users/dossiers/show/_status_overview.html.haml', type: :view do - before { allow(dossier.procedure).to receive(:usual_traitement_time).and_return(1.day) } + before { allow(dossier.procedure).to receive(:usual_traitement_time_for_recent_dossiers).and_return(1.day) } subject! { render 'users/dossiers/show/status_overview.html.haml', dossier: dossier } From 887c5cb4a9acaf20e1d1e080783a86661b585467 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 16 Jun 2021 15:13:36 +0200 Subject: [PATCH 11/13] correct rubocop offenses --- app/models/concerns/procedure_stats_concern.rb | 15 ++++++++------- app/models/traitement.rb | 2 +- .../concern/procedure_stats_concern_spec.rb | 12 +++++++----- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/app/models/concerns/procedure_stats_concern.rb b/app/models/concerns/procedure_stats_concern.rb index 868a7a7fe..51a59281c 100644 --- a/app/models/concerns/procedure_stats_concern.rb +++ b/app/models/concerns/procedure_stats_concern.rb @@ -59,27 +59,28 @@ module ProcedureStatsConcern Traitement.for_traitement_time_stats(self) .where(processed_at: date_range) .pluck('dossiers.en_construction_at', :processed_at) - .map{|en_construction_at, processed_at| { en_construction_at: en_construction_at, processed_at: processed_at }} + .map { |en_construction_at, processed_at| { en_construction_at: en_construction_at, processed_at: processed_at } } end def usual_traitement_time_by_month_in_days traitement_times(first_processed_at..last_considered_processed_at) - .group_by {|t| t[:processed_at].beginning_of_month } - .transform_values{|month| month.map{|h| h[:processed_at] - h[:en_construction_at]}} - .transform_values{|traitement_times_for_month| traitement_times_for_month.percentile(PERCENTILE).ceil } - .transform_values{|seconds| convert_seconds_in_days(seconds)} - .transform_keys{|month| pretty_month(month)} + .group_by { |t| t[:processed_at].beginning_of_month } + .transform_values { |month| month.map { |h| h[:processed_at] - h[:en_construction_at] } } + .transform_values { |traitement_times_for_month| traitement_times_for_month.percentile(PERCENTILE).ceil } + .transform_values { |seconds| convert_seconds_in_days(seconds) } + .transform_keys { |month| pretty_month(month) } end def usual_traitement_time_for_recent_dossiers(nb_days) now = Time.zone.now traitement_times((now - nb_days.days)..now) - .map{|times| times[:processed_at] - times[:en_construction_at]} + .map { |times| times[:processed_at] - times[:en_construction_at] } .percentile(PERCENTILE) .ceil end private + def first_processed_at Traitement.for_traitement_time_stats(self).pick(:processed_at) end diff --git a/app/models/traitement.rb b/app/models/traitement.rb index 52d67fd92..83ca62867 100644 --- a/app/models/traitement.rb +++ b/app/models/traitement.rb @@ -19,7 +19,7 @@ class Traitement < ApplicationRecord .where("traitements.processed_at + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: Dossier::INTERVAL_BEFORE_EXPIRATION }) end - scope :for_traitement_time_stats, ->(procedure) do + scope :for_traitement_time_stats, -> (procedure) do includes(:dossier) .where(dossier: procedure.dossiers) .where.not('dossiers.en_construction_at' => nil, :processed_at => nil) diff --git a/spec/models/concern/procedure_stats_concern_spec.rb b/spec/models/concern/procedure_stats_concern_spec.rb index 0669598ee..7009bf203 100644 --- a/spec/models/concern/procedure_stats_concern_spec.rb +++ b/spec/models/concern/procedure_stats_concern_spec.rb @@ -70,11 +70,13 @@ describe ProcedureStatsConcern do after { Timecop.return } context 'when there are several processed dossiers' do - let(:delays_by_month) { [ - [90.days, 90.days], - [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days], - [30.days, 60.days, 60.days, 60.days] - ] } + let(:delays_by_month) { + [ + [90.days, 90.days], + [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days], + [30.days, 60.days, 60.days, 60.days] + ] +} it 'computes a time representative of the dossier instruction delay for each month except current month' do expect(procedure.usual_traitement_time_by_month_in_days['avril 2019']).to eq 60 From 896190b91e97e1da02ae2de2697890e11d99cff0 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Thu, 17 Jun 2021 10:30:27 +0200 Subject: [PATCH 12/13] return nil when no traitement time --- app/models/concerns/procedure_stats_concern.rb | 13 +++++++++---- spec/models/concern/procedure_stats_concern_spec.rb | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/models/concerns/procedure_stats_concern.rb b/app/models/concerns/procedure_stats_concern.rb index 51a59281c..44d072666 100644 --- a/app/models/concerns/procedure_stats_concern.rb +++ b/app/models/concerns/procedure_stats_concern.rb @@ -67,16 +67,21 @@ module ProcedureStatsConcern .group_by { |t| t[:processed_at].beginning_of_month } .transform_values { |month| month.map { |h| h[:processed_at] - h[:en_construction_at] } } .transform_values { |traitement_times_for_month| traitement_times_for_month.percentile(PERCENTILE).ceil } + .transform_values { |seconds| seconds == 0 ? nil : seconds } .transform_values { |seconds| convert_seconds_in_days(seconds) } .transform_keys { |month| pretty_month(month) } end def usual_traitement_time_for_recent_dossiers(nb_days) now = Time.zone.now - traitement_times((now - nb_days.days)..now) - .map { |times| times[:processed_at] - times[:en_construction_at] } - .percentile(PERCENTILE) - .ceil + traitement_time = + traitement_times((now - nb_days.days)..now) + .map { |times| times[:processed_at] - times[:en_construction_at] } + .percentile(PERCENTILE) + .ceil + + traitement_time = nil if traitement_time == 0 + traitement_time end private diff --git a/spec/models/concern/procedure_stats_concern_spec.rb b/spec/models/concern/procedure_stats_concern_spec.rb index 7009bf203..050989259 100644 --- a/spec/models/concern/procedure_stats_concern_spec.rb +++ b/spec/models/concern/procedure_stats_concern_spec.rb @@ -45,7 +45,7 @@ describe ProcedureStatsConcern do context 'where there is no processed dossier' do let(:delays) { [] } - it { expect(procedure.usual_traitement_time_for_recent_dossiers(30)).to eq 0 } + it { expect(procedure.usual_traitement_time_for_recent_dossiers(30)).to eq nil } end end From 4bda6600b1d1f2fb2ef747a7d98d073ed2a53c16 Mon Sep 17 00:00:00 2001 From: krichtof Date: Thu, 17 Jun 2021 14:33:37 +0200 Subject: [PATCH 13/13] explain what percentile constant means Co-authored-by: Pierre de La Morinerie --- app/models/concerns/procedure_stats_concern.rb | 10 ++++++---- app/views/shared/procedures/_stats.html.haml | 2 +- .../users/dossiers/show/_estimated_delay.html.haml | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/models/concerns/procedure_stats_concern.rb b/app/models/concerns/procedure_stats_concern.rb index 44d072666..cbe89c5a8 100644 --- a/app/models/concerns/procedure_stats_concern.rb +++ b/app/models/concerns/procedure_stats_concern.rb @@ -2,7 +2,10 @@ module ProcedureStatsConcern extend ActiveSupport::Concern NB_DAYS_RECENT_DOSSIERS = 30 - PERCENTILE = 90 + # Percentage of dossiers considered to compute the 'usual traitement time'. + # For instance, a value of '90' means that the usual traitement time will return + # the duration under which 90% of the given dossiers are closed. + USUAL_TRAITEMENT_TIME_PERCENTILE = 90 def stats_usual_traitement_time Rails.cache.fetch("#{cache_key_with_version}/stats_usual_traitement_time", expires_in: 12.hours) do @@ -66,7 +69,7 @@ module ProcedureStatsConcern traitement_times(first_processed_at..last_considered_processed_at) .group_by { |t| t[:processed_at].beginning_of_month } .transform_values { |month| month.map { |h| h[:processed_at] - h[:en_construction_at] } } - .transform_values { |traitement_times_for_month| traitement_times_for_month.percentile(PERCENTILE).ceil } + .transform_values { |traitement_times_for_month| traitement_times_for_month.percentile(USUAL_TRAITEMENT_TIME_PERCENTILE).ceil } .transform_values { |seconds| seconds == 0 ? nil : seconds } .transform_values { |seconds| convert_seconds_in_days(seconds) } .transform_keys { |month| pretty_month(month) } @@ -77,7 +80,7 @@ module ProcedureStatsConcern traitement_time = traitement_times((now - nb_days.days)..now) .map { |times| times[:processed_at] - times[:en_construction_at] } - .percentile(PERCENTILE) + .percentile(USUAL_TRAITEMENT_TIME_PERCENTILE) .ceil traitement_time = nil if traitement_time == 0 @@ -101,5 +104,4 @@ module ProcedureStatsConcern def pretty_month(month) I18n.l(month, format: "%B %Y") end - end diff --git a/app/views/shared/procedures/_stats.html.haml b/app/views/shared/procedures/_stats.html.haml index 49df73a62..2da3e10bc 100644 --- a/app/views/shared/procedures/_stats.html.haml +++ b/app/views/shared/procedures/_stats.html.haml @@ -11,7 +11,7 @@ %span.big-number-card-number = distance_of_time_in_words(@usual_traitement_time) %span.big-number-card-detail - #{ProcedureStatsConcern::PERCENTILE}% des demandes des #{ProcedureStatsConcern::NB_DAYS_RECENT_DOSSIERS} derniers jours ont été traitées en moins de #{distance_of_time_in_words(@usual_traitement_time)}. + #{ProcedureStatsConcern::USUAL_TRAITEMENT_TIME_PERCENTILE}% des demandes des #{ProcedureStatsConcern::NB_DAYS_RECENT_DOSSIERS} derniers jours ont été traitées en moins de #{distance_of_time_in_words(@usual_traitement_time)}. .stat-cards .stat-card.stat-card-half.pull-left diff --git a/app/views/users/dossiers/show/_estimated_delay.html.haml b/app/views/users/dossiers/show/_estimated_delay.html.haml index 7b9e6acca..e33eab09c 100644 --- a/app/views/users/dossiers/show/_estimated_delay.html.haml +++ b/app/views/users/dossiers/show/_estimated_delay.html.haml @@ -8,4 +8,4 @@ %p Habituellement, les dossiers de cette démarche sont traités dans un délai de #{distance_of_time_in_words(procedure.usual_traitement_time_for_recent_dossiers(ProcedureStatsConcern::NB_DAYS_RECENT_DOSSIERS))}. %p - Cette estimation est calculée automatiquement à partir des délais d’instruction constatés sur #{ProcedureStatsConcern::PERCENTILE}% des demandes qui ont été traitées lors des #{ProcedureStatsConcern::NB_DAYS_RECENT_DOSSIERS} derniers jours. Le délai réel peut être différent, en fonction du type de démarche (par exemple pour un appel à projet avec date de décision fixe). + Cette estimation est calculée automatiquement à partir des délais d’instruction constatés sur #{ProcedureStatsConcern::USUAL_TRAITEMENT_TIME_PERCENTILE}% des demandes qui ont été traitées lors des #{ProcedureStatsConcern::NB_DAYS_RECENT_DOSSIERS} derniers jours. Le délai réel peut être différent, en fonction du type de démarche (par exemple pour un appel à projet avec date de décision fixe).