Merge pull request #5426 from betagouv/5288-optim-instructeur-dashboard

5288 optim instructeur dashboard
This commit is contained in:
Keirua 2020-07-30 10:42:48 +02:00 committed by GitHub
commit 808e2fb53f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 147 additions and 21 deletions

View file

@ -35,6 +35,7 @@ module CreateAvisConcern
persisted, failed = create_results.partition(&:persisted?)
if persisted.any?
dossier.update!(last_avis_updated_at: Time.zone.now)
sent_emails_addresses = []
persisted.each do |avis|
avis.dossier.demander_un_avis!(avis)

View file

@ -44,6 +44,7 @@ module Instructeurs
def update
if @avis.update(avis_params)
flash.notice = 'Votre réponse est enregistrée.'
@avis.dossier.update!(last_avis_updated_at: Time.zone.now)
redirect_to instruction_instructeur_avis_path(@avis.procedure, @avis)
else
flash.now.alert = @avis.errors.full_messages
@ -60,6 +61,7 @@ module Instructeurs
@commentaire = CommentaireService.build(current_instructeur, avis.dossier, commentaire_params)
if @commentaire.save
@commentaire.dossier.update!(last_commentaire_updated_at: Time.zone.now)
flash.notice = "Message envoyé"
redirect_to messagerie_instructeur_avis_path(avis.procedure, avis)
else

View file

@ -169,6 +169,7 @@ module Instructeurs
@commentaire = CommentaireService.build(current_instructeur, dossier, commentaire_params)
if @commentaire.save
@commentaire.dossier.update!(last_commentaire_updated_at: Time.zone.now)
current_instructeur.follow(dossier)
flash.notice = "Message envoyé"
redirect_to messagerie_instructeur_dossier_path(procedure, dossier)
@ -191,8 +192,12 @@ module Instructeurs
def update_annotations
dossier = current_instructeur.dossiers.includes(champs_private: :type_de_champ).find(params[:dossier_id])
dossier.update(champs_private_params)
dossier.modifier_annotations!(current_instructeur)
dossier.assign_attributes(champs_private_params)
if dossier.champs_private.any?(&:changed?)
dossier.last_champ_private_updated_at = Time.zone.now
end
dossier.save
dossier.log_modifier_annotations!(current_instructeur)
redirect_to annotations_privees_instructeur_dossier_path(procedure, dossier)
end

View file

@ -198,6 +198,7 @@ module Users
@commentaire = CommentaireService.build(current_user, dossier, commentaire_params)
if @commentaire.save
@commentaire.dossier.update!(last_commentaire_updated_at: Time.zone.now)
dossier.followers_instructeurs
.with_instant_email_message_notifications
.each do |instructeur|
@ -344,7 +345,11 @@ module Users
errors = []
if champs_params[:dossier]
if !@dossier.update(champs_params[:dossier])
@dossier.assign_attributes(champs_params[:dossier])
if @dossier.champs.any?(&:changed?)
@dossier.last_champ_updated_at = Time.zone.now
end
if !@dossier.save
errors += @dossier.errors.full_messages
elsif change_groupe_instructeur?
groupe_instructeur = @dossier.procedure.groupe_instructeurs.find(params[:dossier][:groupe_instructeur_id])

View file

@ -0,0 +1,27 @@
class TmpSetDossiersLastUpdatedAtJob < ApplicationJob
def perform(except)
dossiers = Dossier.where
.not(id: except)
.where(last_champ_updated_at: nil)
.includes(:champs, :avis, :commentaires)
.limit(100)
dossiers.find_each do |dossier|
last_commentaire_updated_at = dossier.commentaires.maximum(:updated_at)
last_avis_updated_at = dossier.avis.maximum(:updated_at)
last_champ_updated_at = dossier.champs.maximum(:updated_at)
last_champ_private_updated_at = dossier.champs_private.maximum(:updated_at)
dossier.update_columns(
last_commentaire_updated_at: last_commentaire_updated_at,
last_avis_updated_at: last_avis_updated_at,
last_champ_updated_at: last_champ_updated_at,
last_champ_private_updated_at: last_champ_private_updated_at
)
except << dossier.id
end
if dossiers.where.not(id: except).exists?
TmpSetDossiersLastUpdatedAtJob.perform_later(except)
end
end
end

View file

@ -615,7 +615,7 @@ class Dossier < ApplicationRecord
end
end
def modifier_annotations!(instructeur)
def log_modifier_annotations!(instructeur)
champs_private.filter(&:value_previously_changed?).each do |champ|
log_dossier_operation(instructeur, :modifier_annotation, champ)
end

View file

@ -0,0 +1,8 @@
class AddDossiersLatestUpdatesToDossiers < ActiveRecord::Migration[6.0]
def change
add_column :dossiers, :last_champ_updated_at, :datetime
add_column :dossiers, :last_champ_private_updated_at, :datetime
add_column :dossiers, :last_avis_updated_at, :datetime
add_column :dossiers, :last_commentaire_updated_at, :datetime
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_07_15_143010) do
ActiveRecord::Schema.define(version: 2020_07_22_135121) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -260,6 +260,10 @@ ActiveRecord::Schema.define(version: 2020_07_15_143010) do
t.bigint "revision_id"
t.index "to_tsvector('french'::regconfig, (search_terms || private_search_terms))", name: "index_dossiers_on_search_terms_private_search_terms", using: :gin
t.index "to_tsvector('french'::regconfig, search_terms)", name: "index_dossiers_on_search_terms", using: :gin
t.datetime "last_champ_updated_at"
t.datetime "last_champ_private_updated_at"
t.datetime "last_avis_updated_at"
t.datetime "last_commentaire_updated_at"
t.index ["archived"], name: "index_dossiers_on_archived"
t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id"
t.index ["hidden_at"], name: "index_dossiers_on_hidden_at"

View file

@ -2,6 +2,7 @@ describe Instructeurs::AvisController, type: :controller do
context 'with a instructeur signed in' do
render_views
let(:now) { Time.zone.parse('01/02/2345') }
let(:claimant) { create(:instructeur) }
let(:instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure, :published, instructeurs: [claimant]) }
@ -79,21 +80,24 @@ describe Instructeurs::AvisController, type: :controller do
end
describe '#update' do
describe 'without attachment' do
context 'without attachment' do
before do
Timecop.freeze(now)
patch :update, params: { id: avis_without_answer.id, procedure_id: procedure.id, avis: { answer: 'answer' } }
avis_without_answer.reload
end
after { Timecop.return }
it 'should be ok' do
expect(response).to redirect_to(instruction_instructeur_avis_path(avis_without_answer.procedure, avis_without_answer))
expect(avis_without_answer.answer).to eq('answer')
expect(avis_without_answer.piece_justificative_file).to_not be_attached
expect(dossier.reload.last_avis_updated_at).to eq(now)
expect(flash.notice).to eq('Votre réponse est enregistrée.')
end
end
describe 'with attachment' do
context 'with attachment' do
include ActiveJob::TestHelper
let(:file) { fixture_file_upload('spec/fixtures/files/piece_justificative_0.pdf', 'application/pdf') }
@ -117,18 +121,23 @@ describe Instructeurs::AvisController, type: :controller do
describe '#create_commentaire' do
let(:file) { nil }
let(:scan_result) { true }
let(:now) { Time.zone.parse("14/07/1789") }
subject { post :create_commentaire, params: { id: avis_without_answer.id, procedure_id: procedure.id, commentaire: { body: 'commentaire body', piece_jointe: file } } }
before do
allow(ClamavService).to receive(:safe_file?).and_return(scan_result)
Timecop.freeze(now)
end
after { Timecop.return }
it do
subject
expect(response).to redirect_to(messagerie_instructeur_avis_path(avis_without_answer.procedure, avis_without_answer))
expect(dossier.commentaires.map(&:body)).to match(['commentaire body'])
expect(dossier.reload.last_commentaire_updated_at).to eq(now)
end
context "with a file" do
@ -152,10 +161,12 @@ describe Instructeurs::AvisController, type: :controller do
let(:invite_linked_dossiers) { nil }
before do
Timecop.freeze(now)
@introduction_file = fixture_file_upload('spec/fixtures/files/piece_justificative_0.pdf', 'application/pdf')
post :create_avis, params: { id: previous_avis.id, procedure_id: procedure.id, avis: { emails: emails, introduction: intro, confidentiel: asked_confidentiel, invite_linked_dossiers: invite_linked_dossiers, introduction_file: @introduction_file } }
created_avis.reload
end
after { Timecop.return }
context 'when an invalid email' do
let(:previous_avis_confidentiel) { false }
@ -165,6 +176,7 @@ describe Instructeurs::AvisController, type: :controller do
it { expect(response).to render_template :instruction }
it { expect(flash.alert).to eq(["toto.fr : Email n'est pas valide"]) }
it { expect(Avis.last).to eq(previous_avis) }
it { expect(dossier.last_avis_updated_at).to eq(nil) }
end
context 'ask review with attachment' do
@ -174,6 +186,7 @@ describe Instructeurs::AvisController, type: :controller do
it { expect(created_avis.introduction_file).to be_attached }
it { expect(created_avis.introduction_file.filename).to eq("piece_justificative_0.pdf") }
it { expect(created_avis.dossier.reload.last_avis_updated_at).to eq(now) }
it { expect(flash.notice).to eq("Une demande d'avis a été envoyée à toto@totomail.com") }
end

View file

@ -390,6 +390,7 @@ describe Instructeurs::DossiersController, type: :controller do
let(:body) { "avant\napres" }
let(:file) { fixture_file_upload('spec/fixtures/files/piece_justificative_0.pdf', 'application/pdf') }
let(:scan_result) { true }
let(:now) { Timecop.freeze("09/11/1989") }
subject {
post :create_commentaire, params: {
@ -404,14 +405,18 @@ describe Instructeurs::DossiersController, type: :controller do
before do
allow(ClamavService).to receive(:safe_file?).and_return(scan_result)
Timecop.freeze(now)
end
after { Timecop.return }
it "creates a commentaire" do
expect { subject }.to change(Commentaire, :count).by(1)
expect(instructeur.followed_dossiers).to include(dossier)
expect(response).to redirect_to(messagerie_instructeur_dossier_path(dossier.procedure, dossier))
expect(flash.notice).to be_present
expect(dossier.reload.last_commentaire_updated_at).to eq(now)
end
context "when the commentaire created with virus file" do
@ -459,6 +464,7 @@ describe Instructeurs::DossiersController, type: :controller do
it { expect(response).to render_template :avis }
it { expect(flash.alert).to eq(["emaila.com : Email n'est pas valide"]) }
it { expect { subject }.not_to change(Avis, :count) }
it { expect(dossier.last_avis_updated_at).to eq(nil) }
end
context 'with multiple emails' do
@ -578,9 +584,26 @@ describe Instructeurs::DossiersController, type: :controller do
create(:dossier, :en_construction, procedure: procedure, champs_private: [champ_multiple_drop_down_list, champ_linked_drop_down_list, champ_datetime, champ_repetition])
end
let(:now) { Time.zone.parse('01/01/2100') }
before do
patch :update_annotations, params: {
procedure_id: procedure.id,
Timecop.freeze(now)
patch :update_annotations, params: params
champ_multiple_drop_down_list.reload
champ_linked_drop_down_list.reload
champ_datetime.reload
champ_repetition.reload
end
after do
Timecop.return
end
context "with new values for champs_private" do
let(:params) do
{
procedure_id: procedure.id,
dossier_id: dossier.id,
dossier: {
champs_private_attributes: {
@ -610,20 +633,36 @@ describe Instructeurs::DossiersController, type: :controller do
}
}
}
}
champ_multiple_drop_down_list.reload
champ_linked_drop_down_list.reload
champ_datetime.reload
champ_repetition.reload
}
end
it { expect(champ_multiple_drop_down_list.value).to eq('["un", "deux"]') }
it { expect(champ_linked_drop_down_list.primary_value).to eq('primary') }
it { expect(champ_linked_drop_down_list.secondary_value).to eq('secondary') }
it { expect(champ_datetime.value).to eq('21/12/2019 13:17') }
it { expect(champ_repetition.champs.first.value).to eq('text') }
it { expect(dossier.reload.last_champ_private_updated_at).to eq(now) }
it { expect(response).to redirect_to(annotations_privees_instructeur_dossier_path(dossier.procedure, dossier)) }
end
it { expect(champ_multiple_drop_down_list.value).to eq('["un", "deux"]') }
it { expect(champ_linked_drop_down_list.primary_value).to eq('primary') }
it { expect(champ_linked_drop_down_list.secondary_value).to eq('secondary') }
it { expect(champ_datetime.value).to eq('21/12/2019 13:17') }
it { expect(champ_repetition.champs.first.value).to eq('text') }
it { expect(response).to redirect_to(annotations_privees_instructeur_dossier_path(dossier.procedure, dossier)) }
context "without new values for champs_private" do
let(:params) do
{
procedure_id: procedure.id,
dossier_id: dossier.id,
dossier: {
champs_private_attributes: {},
champs_attributes: {
'0': {
id: champ_multiple_drop_down_list.id,
value: ['', 'un', 'deux']
}
}
}
}
end
it { expect(dossier.reload.last_champ_private_updated_at).to eq(nil) }
it { expect(response).to redirect_to(annotations_privees_instructeur_dossier_path(dossier.procedure, dossier)) }
end
end
describe "#telecharger_pjs" do

View file

@ -378,6 +378,22 @@ describe Users::DossiersController, type: :controller do
expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_construction))
end
context 'without new values for champs' do
let(:submit_payload) do
{
id: dossier.id,
dossier: {
champs_attributes: {}
}
}
end
it "doesn't set last_champ_updated_at" do
subject
expect(dossier.reload.last_champ_updated_at).to eq(nil)
end
end
context 'with instructeurs ok to be notified instantly' do
let!(:instructeur_with_instant_email_dossier) { create(:instructeur) }
let!(:instructeur_without_instant_email_dossier) { create(:instructeur) }
@ -576,6 +592,7 @@ describe Users::DossiersController, type: :controller do
it 'updates the champs' do
subject
expect(first_champ.reload.value).to eq('beautiful value')
expect(first_champ.dossier.reload.last_champ_updated_at).to eq(now)
expect(piece_justificative_champ.reload.piece_justificative_file).to be_attached
end
@ -802,6 +819,7 @@ describe Users::DossiersController, type: :controller do
let(:body) { "avant\napres" }
let(:file) { fixture_file_upload('spec/fixtures/files/piece_justificative_0.pdf', 'application/pdf') }
let(:scan_result) { true }
let(:now) { Time.zone.parse("18/09/1981") }
subject {
post :create_commentaire, params: {
@ -814,6 +832,7 @@ describe Users::DossiersController, type: :controller do
}
before do
Timecop.freeze(now)
sign_in(user)
allow(ClamavService).to receive(:safe_file?).and_return(scan_result)
allow(DossierMailer).to receive(:notify_new_commentaire_to_instructeur).and_return(double(deliver_later: nil))
@ -823,6 +842,8 @@ describe Users::DossiersController, type: :controller do
create(:assign_to, instructeur: instructeur_without_instant_message, procedure: procedure, instant_email_message_notifications_enabled: false, groupe_instructeur: procedure.defaut_groupe_instructeur)
end
after { Timecop.return }
it "creates a commentaire" do
expect { subject }.to change(Commentaire, :count).by(1)
@ -830,6 +851,7 @@ describe Users::DossiersController, type: :controller do
expect(DossierMailer).to have_received(:notify_new_commentaire_to_instructeur).with(dossier, instructeur_with_instant_message.email)
expect(DossierMailer).not_to have_received(:notify_new_commentaire_to_instructeur).with(dossier, instructeur_without_instant_message.email)
expect(flash.notice).to be_present
expect(dossier.reload.last_commentaire_updated_at).to eq(now)
end
end