Merge pull request #5692 from betagouv/faster-instructeur-procedure-show2

Faster instructeur procedure show2
This commit is contained in:
LeSim 2020-10-28 15:08:42 +01:00 committed by GitHub
commit 52c2ea1905
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 188 additions and 17 deletions

View file

@ -98,6 +98,12 @@ module Instructeurs
@archived_dossiers
end
@has_en_cours_notifications = current_instructeur.notifications_for_procedure(@procedure, :en_cours).exists?
@has_termine_notifications = current_instructeur.notifications_for_procedure(@procedure, :termine).exists?
@not_archived_notifications_dossier_ids = current_instructeur.notifications_for_procedure(@procedure, :not_archived).pluck(:id)
@counters = @procedure.dossiers_count_for_instructeur(current_instructeur)
sorted_ids = procedure_presentation.sorted_ids(@dossiers, current_instructeur)
if @current_filters.count > 0

View file

@ -630,6 +630,46 @@ class Procedure < ApplicationRecord
draft_revision.deep_clone(include: [:revision_types_de_champ, :revision_types_de_champ_private])
end
def dossiers_count_for_instructeur(instructeur)
query = <<-EOF
SELECT
COUNT(*) FILTER ( WHERE "dossiers"."state" in ('en_construction', 'en_instruction') and (("follows"."id" IS NULL) or ("follows"."instructeur_id" = :instructeur_id and "follows"."unfollowed_at" < :now)) and not "dossiers"."archived") AS a_suivre,
COUNT(*) FILTER ( WHERE "dossiers"."state" in ('en_construction', 'en_instruction') and "follows"."instructeur_id" = :instructeur_id and not "dossiers"."archived" and "follows"."unfollowed_at" IS NULL) AS suivis,
COUNT(*) FILTER ( WHERE "dossiers"."state" in ('accepte', 'refuse', 'sans_suite') and not "dossiers"."archived" ) AS termines,
COUNT(*) FILTER ( WHERE "dossiers"."state" != 'brouillon' and not "dossiers"."archived" ) AS total,
COUNT(*) FILTER ( WHERE "dossiers"."archived" ) AS archived
FROM
"dossiers"
INNER JOIN
"groupe_instructeurs"
ON "dossiers"."groupe_instructeur_id" = "groupe_instructeurs"."id"
INNER JOIN
"assign_tos"
ON "groupe_instructeurs"."id" = "assign_tos"."groupe_instructeur_id"
INNER JOIN
"procedures"
ON "groupe_instructeurs"."procedure_id" = "procedures"."id"
LEFT OUTER JOIN
"follows"
ON "follows"."dossier_id" = "dossiers"."id"
WHERE
"dossiers"."hidden_at" IS NULL
AND "assign_tos"."instructeur_id" = :instructeur_id
AND "procedures"."id" = :procedure_id
GROUP BY
groupe_instructeurs.procedure_id, procedures.libelle
EOF
sanitized_query = ActiveRecord::Base.sanitize_sql([
query,
instructeur_id: instructeur.id,
procedure_id: self.id,
now: Time.zone.now
])
Procedure.connection.select_all(sanitized_query).first || { "a_suivre" => 0, "suivis" => 0, "termines" => 0, "total" => 0, "archived" => 0 }
end
private
def before_publish

View file

@ -82,7 +82,6 @@ class ProcedurePresentation < ApplicationRecord
end
def displayed_field_values(dossier)
assert_matching_procedure(dossier)
displayed_fields.map { |field| get_value(dossier, field['table'], field['column']) }
end
@ -256,12 +255,6 @@ class ProcedurePresentation < ApplicationRecord
end
end
def assert_matching_procedure(dossier)
if dossier.procedure != procedure
raise "Procedure mismatch (expected #{procedure.id}, got #{dossier.procedure.id})"
end
end
def get_value(dossier, table, column)
case table
when 'self'

View file

@ -24,29 +24,29 @@
= tab_item('à suivre',
instructeur_procedure_path(@procedure, statut: 'a-suivre'),
active: @statut == 'a-suivre',
badge: number_with_html_delimiter(@a_suivre_dossiers.count))
badge: number_with_html_delimiter(@counters['a_suivre']))
= tab_item(t('pluralize.followed', count: @followed_dossiers.count),
= tab_item(t('pluralize.followed', count: @counters['suivis']),
instructeur_procedure_path(@procedure, statut: 'suivis'),
active: @statut == 'suivis',
badge: number_with_html_delimiter(@followed_dossiers.count),
notification: current_instructeur.notifications_for_procedure(@procedure, :en_cours).exists?)
badge: number_with_html_delimiter(@counters['suivis']),
notification: @has_en_cours_notifications)
= tab_item(t('pluralize.processed', count: @termines_dossiers.count),
= tab_item(t('pluralize.processed', count: @counters['termines']),
instructeur_procedure_path(@procedure, statut: 'traites'),
active: @statut == 'traites',
badge: number_with_html_delimiter(@termines_dossiers.count),
notification: current_instructeur.notifications_for_procedure(@procedure, :termine).exists?)
badge: number_with_html_delimiter(@counters['termines']),
notification: @has_termine_notifications)
= tab_item('tous les dossiers',
instructeur_procedure_path(@procedure, statut: 'tous'),
active: @statut == 'tous',
badge: number_with_html_delimiter(@all_state_dossiers.count))
badge: number_with_html_delimiter(@counters['total']))
= tab_item(t('pluralize.archived', count: @archived_dossiers.count),
instructeur_procedure_path(@procedure, statut: 'archives'),
active: @statut == 'archives',
badge: number_with_html_delimiter(@archived_dossiers.count))
badge: number_with_html_delimiter(@counters['archived']))
.procedure-actions
= render partial: "download_dossiers",
@ -135,7 +135,7 @@
%td.folder-col
= link_to(instructeur_dossier_path(@procedure, dossier), class: 'cell-link') do
%span.icon.folder
- if current_instructeur.notifications_for_procedure(@procedure, :not_archived).include?(dossier)
- if @not_archived_notifications_dossier_ids.include?(dossier.id)
%span.notifications{ 'aria-label': 'notifications' }
%td.number-col

View file

@ -1023,4 +1023,136 @@ describe Procedure do
it { is_expected.to be false }
end
end
describe "#dossiers_count_for_instructeur" do
let(:instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure, instructeurs: [instructeur]) }
let(:gi_2) { procedure.groupe_instructeurs.create(label: '2') }
let(:gi_3) { procedure.groupe_instructeurs.create(label: '3') }
subject do
procedure.dossiers_count_for_instructeur(instructeur)
end
context "when logged in, and belonging to gi_1, gi_2" do
before do
instructeur.groupe_instructeurs << gi_2
end
context "without any dossier" do
it { expect(subject['a_suivre']).to eq(0) }
it { expect(subject['suivis']).to eq(0) }
it { expect(subject['termines']).to eq(0) }
it { expect(subject['total']).to eq(0) }
it { expect(subject['archived']).to eq(0) }
end
context 'with a new brouillon dossier' do
let!(:brouillon_dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:brouillon)) }
it { expect(subject['a_suivre']).to eq(0) }
it { expect(subject['suivis']).to eq(0) }
it { expect(subject['termines']).to eq(0) }
it { expect(subject['total']).to eq(0) }
it { expect(subject['archived']).to eq(0) }
end
context 'with a new dossier without follower' do
let!(:new_unfollow_dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) }
it { expect(subject['a_suivre']).to eq(1) }
it { expect(subject['suivis']).to eq(0) }
it { expect(subject['termines']).to eq(0) }
it { expect(subject['total']).to eq(1) }
it { expect(subject['archived']).to eq(0) }
context 'and dossiers without follower on each of the others groups' do
let!(:new_unfollow_dossier_on_gi_2) { create(:dossier, groupe_instructeur: gi_2, state: Dossier.states.fetch(:en_instruction)) }
let!(:new_unfollow_dossier_on_gi_3) { create(:dossier, groupe_instructeur: gi_3, state: Dossier.states.fetch(:en_instruction)) }
before { subject }
it { expect(subject['a_suivre']).to eq(2) }
it { expect(subject['total']).to eq(2) }
end
end
context 'with a new dossier with a follower' do
let!(:new_followed_dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) }
before do
instructeur.followed_dossiers << new_followed_dossier
end
it { expect(subject['a_suivre']).to eq(0) }
it { expect(subject['suivis']).to eq(1) }
it { expect(subject['termines']).to eq(0) }
it { expect(subject['total']).to eq(1) }
it { expect(subject['archived']).to eq(0) }
context 'and dossier with a follower on each of the others groups' do
let!(:new_follow_dossier_on_gi_2) { create(:dossier, groupe_instructeur: gi_2, state: Dossier.states.fetch(:en_instruction)) }
let!(:new_follow_dossier_on_gi_3) { create(:dossier, groupe_instructeur: gi_3, state: Dossier.states.fetch(:en_instruction)) }
before do
instructeur.followed_dossiers << new_follow_dossier_on_gi_2 << new_follow_dossier_on_gi_3
end
# followed dossiers on another groupe should not be displayed
it { expect(subject['suivis']).to eq(2) }
it { expect(subject['total']).to eq(2) }
end
context 'and dossier with a follower is unfollowed' do
before do
instructeur.unfollow(new_followed_dossier)
end
it { expect(subject['a_suivre']).to eq(1) }
it { expect(subject['suivis']).to eq(0) }
it { expect(subject['total']).to eq(1) }
end
end
context 'with a termine dossier' do
let!(:termine_dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:accepte)) }
it { expect(subject['a_suivre']).to eq(0) }
it { expect(subject['suivis']).to eq(0) }
it { expect(subject['termines']).to eq(1) }
it { expect(subject['total']).to eq(1) }
it { expect(subject['archived']).to eq(0) }
context 'and terminer dossiers on each of the others groups' do
let!(:termine_dossier_on_gi_2) { create(:dossier, groupe_instructeur: gi_2, state: Dossier.states.fetch(:accepte)) }
let!(:termine_dossier_on_gi_3) { create(:dossier, groupe_instructeur: gi_3, state: Dossier.states.fetch(:accepte)) }
before { subject }
it { expect(subject['a_suivre']).to eq(0) }
it { expect(subject['suivis']).to eq(0) }
it { expect(subject['termines']).to eq(2) }
it { expect(subject['total']).to eq(2) }
it { expect(subject['archived']).to eq(0) }
end
end
context 'with an archived dossier' do
let!(:archived_dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction), archived: true) }
it { expect(subject['a_suivre']).to eq(0) }
it { expect(subject['suivis']).to eq(0) }
it { expect(subject['termines']).to eq(0) }
it { expect(subject['total']).to eq(0) }
it { expect(subject['archived']).to eq(1) }
context 'and terminer dossiers on each of the others groups' do
let!(:archived_dossier_on_gi_2) { create(:dossier, groupe_instructeur: gi_2, state: Dossier.states.fetch(:en_instruction), archived: true) }
let!(:archived_dossier_on_gi_3) { create(:dossier, groupe_instructeur: gi_3, state: Dossier.states.fetch(:en_instruction), archived: true) }
it { expect(subject['archived']).to eq(2) }
end
end
end
end
end