Notification: add methods to retrieve notifications

This commit is contained in:
Simon Lehericey 2017-10-05 16:10:00 +02:00 committed by Mathieu Magnin
parent bb147bcb1f
commit 155a5f7826
7 changed files with 235 additions and 0 deletions

View file

@ -12,6 +12,7 @@ class Avis < ApplicationRecord
scope :without_answer, -> { where(answer: nil) } scope :without_answer, -> { where(answer: nil) }
scope :for_dossier, ->(dossier_id) { where(dossier_id: dossier_id) } scope :for_dossier, ->(dossier_id) { where(dossier_id: dossier_id) }
scope :by_latest, -> { order(updated_at: :desc) } scope :by_latest, -> { order(updated_at: :desc) }
scope :updated_since?, -> (date) { where('avis.updated_at > ?', date) }
def email_to_display def email_to_display
gestionnaire.try(:email) || email gestionnaire.try(:email) || email

View file

@ -11,6 +11,8 @@ class Champ < ActiveRecord::Base
after_save :internal_notification, if: Proc.new { !dossier.nil? } after_save :internal_notification, if: Proc.new { !dossier.nil? }
scope :updated_since?, -> (date) { where('champs.updated_at > ?', date) }
def mandatory? def mandatory?
mandatory mandatory
end end

View file

@ -5,6 +5,7 @@ class Commentaire < ActiveRecord::Base
belongs_to :piece_justificative belongs_to :piece_justificative
default_scope { order(created_at: :asc) } default_scope { order(created_at: :asc) }
scope :updated_since?, -> (date) { where('commentaires.updated_at > ?', date) }
after_create :notify after_create :notify

View file

@ -62,6 +62,7 @@ class Dossier < ActiveRecord::Base
scope :en_cours, -> { not_archived.state_en_construction_ou_instruction } scope :en_cours, -> { not_archived.state_en_construction_ou_instruction }
scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) } scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) }
scope :with_unread_notifications, -> { where(notifications: { already_read: false }) } scope :with_unread_notifications, -> { where(notifications: { already_read: false }) }
scope :followed_by , -> (gestionnaire) { joins(:follows).where(follows: { gestionnaire: gestionnaire }) }
accepts_nested_attributes_for :individual accepts_nested_attributes_for :individual

View file

@ -131,6 +131,50 @@ class Gestionnaire < ActiveRecord::Base
assign_to.find_by(procedure_id: procedure_id).procedure_presentation_or_default assign_to.find_by(procedure_id: procedure_id).procedure_presentation_or_default
end end
def notifications_for_dossier(dossier)
follow = Follow
.includes(dossier: [:champs, :avis, :commentaires])
.find_by(gestionnaire: self, dossier: dossier)
if follow.present?
#retirer le seen_at.present? une fois la contrainte de presence en base (et les migrations ad hoc)
champs_publiques = follow.demande_seen_at.present? &&
follow.dossier.champs.updated_since?(follow.demande_seen_at).any?
pieces_justificatives = follow.demande_seen_at.present? &&
follow.dossier.pieces_justificatives.updated_since?(follow.demande_seen_at).any?
demande = champs_publiques || pieces_justificatives
annotations_privees = follow.annotations_privees_seen_at.present? &&
follow.dossier.champs_private.updated_since?(follow.annotations_privees_seen_at).any?
avis_notif = follow.avis_seen_at.present? &&
follow.dossier.avis.updated_since?(follow.avis_seen_at).any?
messagerie = follow.messagerie_seen_at.present? &&
dossier.commentaires
.where.not(email: 'contact@tps.apientreprise.fr')
.updated_since?(follow.messagerie_seen_at).any?
annotations_hash(demande, annotations_privees, avis_notif, messagerie)
else
annotations_hash(false, false, false, false)
end
end
def notifications_for_procedure(procedure)
dossiers = procedure.dossiers.en_cours.followed_by(self)
dossiers_id_with_notifications(dossiers)
end
def notifications_per_procedure
dossiers = Dossier.en_cours.followed_by(self)
Dossier.where(id: dossiers_id_with_notifications(dossiers)).group(:procedure_id).count
end
private private
def valid_couple_table_attr? table, column def valid_couple_table_attr? table, column
@ -153,4 +197,42 @@ class Gestionnaire < ActiveRecord::Base
couples.include?({table: table, column: column}) couples.include?({table: table, column: column})
end end
def annotations_hash(demande, annotations_privees, avis, messagerie)
{
demande: demande,
annotations_privees: annotations_privees,
avis: avis,
messagerie: messagerie
}
end
def dossiers_id_with_notifications(dossiers)
updated_demandes = dossiers
.joins(:champs)
.where('champs.updated_at > follows.demande_seen_at')
updated_pieces_justificatives = dossiers
.joins(:pieces_justificatives)
.where('pieces_justificatives.updated_at > follows.demande_seen_at')
updated_annotations = dossiers
.joins(:champs_private)
.where('champs.updated_at > follows.annotations_privees_seen_at')
updated_avis = dossiers
.joins(:avis)
.where('avis.updated_at > follows.avis_seen_at')
updated_messagerie = dossiers
.joins(:commentaires)
.where('commentaires.updated_at > follows.messagerie_seen_at')
.where.not(commentaires: { email: 'contact@tps.apientreprise.fr' })
[updated_demandes,
updated_pieces_justificatives,
updated_annotations,
updated_avis,
updated_messagerie].map { |query| query.distinct.ids }.flatten.uniq
end
end end

View file

@ -15,6 +15,8 @@ class PieceJustificative < ActiveRecord::Base
after_save :internal_notification, if: Proc.new { !dossier.nil? } after_save :internal_notification, if: Proc.new { !dossier.nil? }
scope :updated_since?, -> (date) { where('pieces_justificatives.updated_at > ?', date) }
def empty? def empty?
content.blank? content.blank?
end end

View file

@ -318,6 +318,8 @@ describe Gestionnaire, type: :model do
Timecop.freeze(friday) Timecop.freeze(friday)
end end
after { Timecop.return }
context 'when no procedure published was active last week' do context 'when no procedure published was active last week' do
let!(:procedure) { create(:procedure, gestionnaires: [gestionnaire2], libelle: 'procedure', published_at: Time.now) } let!(:procedure) { create(:procedure, gestionnaires: [gestionnaire2], libelle: 'procedure', published_at: Time.now) }
context 'when the gestionnaire has no notifications' do context 'when the gestionnaire has no notifications' do
@ -404,4 +406,148 @@ describe Gestionnaire, type: :model do
it { expect(gestionnaire.procedure_presentation_for_procedure_id(procedure.id)).to eq(pp)} it { expect(gestionnaire.procedure_presentation_for_procedure_id(procedure.id)).to eq(pp)}
it { expect(gestionnaire.procedure_presentation_for_procedure_id(procedure_2.id).persisted?).to be_falsey} it { expect(gestionnaire.procedure_presentation_for_procedure_id(procedure_2.id).persisted?).to be_falsey}
end end
describe '#notifications_for_dossier' do
let!(:dossier) { create(:dossier, :followed, state: 'initiated') }
let(:gestionnaire) { dossier.follows.first.gestionnaire }
subject { gestionnaire.notifications_for_dossier(dossier) }
context 'when the gestionnaire has just followed the dossier' do
it { is_expected.to match({ demande: false, annotations_privees: false, avis: false, messagerie: false }) }
end
context 'when there is a modification on public champs' do
before { dossier.champs.first.update_attribute('value', 'toto') }
it { is_expected.to match({ demande: true, annotations_privees: false, avis: false, messagerie: false }) }
end
context 'when there is a modification on a piece jusitificative' do
before { dossier.pieces_justificatives << create(:piece_justificative, :contrat) }
it { is_expected.to match({ demande: true, annotations_privees: false, avis: false, messagerie: false }) }
end
context 'when there is a modification on private champs' do
before { dossier.champs_private.first.update_attribute('value', 'toto') }
it { is_expected.to match({ demande: false, annotations_privees: true, avis: false, messagerie: false }) }
end
context 'when there is a modification on avis' do
before { create(:avis, dossier: dossier) }
it { is_expected.to match({ demande: false, annotations_privees: false, avis: true, messagerie: false }) }
end
context 'messagerie' do
context 'when there is a new commentaire' do
before { create(:commentaire, dossier: dossier, email: 'a@b.com') }
it { is_expected.to match({ demande: false, annotations_privees: false, avis: false, messagerie: true }) }
end
context 'when there is a new commentaire issued by tps' do
before { create(:commentaire, dossier: dossier, email: 'contact@tps.apientreprise.fr') }
it { is_expected.to match({ demande: false, annotations_privees: false, avis: false, messagerie: false }) }
end
end
end
describe '#notification_for_procedure' do
let!(:dossier) { create(:dossier, :followed, state: 'initiated') }
let(:gestionnaire) { dossier.follows.first.gestionnaire }
let(:procedure) { dossier.procedure }
let!(:gestionnaire_2) { create(:gestionnaire, procedures: [procedure]) }
let!(:dossier_on_procedure_2) { create(:dossier, :followed, state: 'initiated') }
let!(:gestionnaire_on_procedure_2) { dossier_on_procedure_2.follows.first.gestionnaire }
before do
gestionnaire_2.followed_dossiers << dossier
end
subject { gestionnaire.notifications_for_procedure(procedure) }
context 'when the gestionnaire has just followed the dossier' do
it { is_expected.to match([]) }
end
context 'when there is a modification on public champs' do
before { dossier.champs.first.update_attribute('value', 'toto') }
it { is_expected.to match([dossier.id]) }
it { expect(gestionnaire_2.notifications_for_procedure(procedure)).to match([dossier.id]) }
it { expect(gestionnaire_on_procedure_2.notifications_for_procedure(procedure)).to match([]) }
context 'and there is a modification on private champs' do
before { dossier.champs_private.first.update_attribute('value', 'toto') }
it { is_expected.to match([dossier.id]) }
end
context 'when gestionnaire update it s public champs last seen' do
let(:follow) { gestionnaire.follows.find_by(dossier: dossier) }
before { follow.update_attribute('demande_seen_at', DateTime.now) }
it { is_expected.to match([]) }
it { expect(gestionnaire_2.notifications_for_procedure(procedure)).to match([dossier.id]) }
end
end
context 'when there is a modification on a piece justificative' do
before { dossier.pieces_justificatives << create(:piece_justificative, :contrat) }
it { is_expected.to match([dossier.id]) }
end
context 'when there is a modification on public champs on a followed dossier from another procedure' do
before { dossier_on_procedure_2.champs.first.update_attribute('value', 'toto') }
it { is_expected.to match([]) }
end
context 'when there is a modification on private champs' do
before { dossier.champs_private.first.update_attribute('value', 'toto') }
it { is_expected.to match([dossier.id]) }
end
context 'when there is a modification on avis' do
before { create(:avis, dossier: dossier) }
it { is_expected.to match([dossier.id]) }
end
context 'the messagerie' do
context 'when there is a new commentaire' do
before { create(:commentaire, dossier: dossier, email: 'a@b.com') }
it { is_expected.to match([dossier.id]) }
end
context 'when there is a new commentaire issued by tps' do
before { create(:commentaire, dossier: dossier, email: 'contact@tps.apientreprise.fr') }
it { is_expected.to match([]) }
end
end
end
describe '#notifications_per_procedure' do
let!(:dossier) { create(:dossier, :followed, state: 'initiated') }
let(:gestionnaire) { dossier.follows.first.gestionnaire }
let(:procedure) { dossier.procedure }
subject { gestionnaire.notifications_per_procedure }
context 'when there is a modification on public champs' do
before { dossier.champs.first.update_attribute('value', 'toto') }
it { is_expected.to match({ procedure.id => 1 }) }
end
end
end end