diff --git a/app/assets/images/mailer/gestionnaire_mailer/logo.png b/app/assets/images/mailer/gestionnaire_mailer/logo.png new file mode 100755 index 000000000..b8c63e7c3 Binary files /dev/null and b/app/assets/images/mailer/gestionnaire_mailer/logo.png differ diff --git a/app/mailers/gestionnaire_mailer.rb b/app/mailers/gestionnaire_mailer.rb index 8acbe7237..118537a8d 100644 --- a/app/mailers/gestionnaire_mailer.rb +++ b/app/mailers/gestionnaire_mailer.rb @@ -8,6 +8,11 @@ class GestionnaireMailer < ApplicationMailer send_mail email, email_admin, "Vous avez été assigné à un nouvel administrateur sur la plateforme TPS" end + def last_week_overview(gestionnaire, overview) + headers['X-mailjet-campaign'] = 'last_week_overview' + send_mail gestionnaire.email, overview, 'Résumé de la semaine' + end + private def vars_mailer email, args diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index b5e84bcdd..f4cb1578b 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -92,6 +92,26 @@ class Gestionnaire < ActiveRecord::Base notifications.pluck(:dossier_id).uniq.count end + def last_week_overview + start_date = DateTime.now.beginning_of_week + + active_procedure_overviews = procedures + .where(published: true) + .all + .map { |procedure| procedure.procedure_overview(start_date, dossiers_with_notifications_count_for_procedure(procedure)) } + .select(&:had_some_activities?) + + if active_procedure_overviews.count == 0 && notifications.count == 0 + nil + else + { + start_date: start_date, + procedure_overviews: active_procedure_overviews, + notifications: notifications + } + end + end + private def valid_couple_table_attr? table, column diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 2e910acd3..8c863697c 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -141,4 +141,7 @@ class Procedure < ActiveRecord::Base } end + def procedure_overview(start_date, notifications_count) + ProcedureOverview.new(self, start_date, notifications_count) + end end diff --git a/app/models/procedure_overview.rb b/app/models/procedure_overview.rb new file mode 100644 index 000000000..5bce9e5b5 --- /dev/null +++ b/app/models/procedure_overview.rb @@ -0,0 +1,82 @@ +class ProcedureOverview + include Rails.application.routes.url_helpers + attr_accessor :libelle, :notifications_count, :received_dossiers_count, :created_dossiers_count, :processed_dossiers_count, :date + + def initialize(procedure, start_date, notifications_count) + @libelle = procedure.libelle + @procedure_url = backoffice_dossiers_procedure_path(procedure) + @notifications_count = notifications_count + + @received_dossiers_count = procedure.dossiers.where(state: :received).count + @created_dossiers_count = procedure.dossiers + .where(created_at: start_date..DateTime.now) + .where.not(state: :draft) + .count + @processed_dossiers_count = procedure.dossiers.where(processed_at: start_date..DateTime.now).count + end + + def had_some_activities? + [received_dossiers_count, + created_dossiers_count, + processed_dossiers_count, + notifications_count].reduce(:+) > 0 + end + + def to_html + [libelle_description, + dossiers_en_instruction_description, + created_dossier_description, + processed_dossier_description, + notifications_description].compact.join('
') + end + + private + + def libelle_description + "#{libelle}" + end + + def dossiers_en_instruction_description + case received_dossiers_count + when 0 + nil + when 1 + "1 dossier est en cours d'instruction" + else + "#{received_dossiers_count} dossiers sont en cours d'instruction" + end + end + + def created_dossier_description + case created_dossiers_count + when 0 + nil + when 1 + '1 nouveau dossier a été déposé' + else + "#{created_dossiers_count} nouveaux dossiers ont été déposés" + end + end + + def processed_dossier_description + case processed_dossiers_count + when 0 + nil + when 1 + '1 dossier a été instruit' + else + "#{processed_dossiers_count} dossiers ont été instruits" + end + end + + def notifications_description + case notifications_count + when 0 + nil + when 1 + '1 notification en attente sur les dossiers que vous suivez' + else + "#{notifications_count} notifications en attente sur les dossiers que vous suivez" + end + end +end diff --git a/app/views/gestionnaire_mailer/last_week_overview.html.haml b/app/views/gestionnaire_mailer/last_week_overview.html.haml new file mode 100644 index 000000000..a82bb9497 --- /dev/null +++ b/app/views/gestionnaire_mailer/last_week_overview.html.haml @@ -0,0 +1,26 @@ +%table{ align: 'center', border: '0', cellpadding: '0', cellspacing: '0', height: '100%', style: 'background-color: #fafafa', width: '100%' } + %tbody + %tr + %td{ align: 'center', style: 'height: 100%; margin: 0; padding: 30px; width: 100%; border-top: 0', valign: 'top' } + %table{ border: '0', cellpadding: '0', cellspacing: '0', style: 'border-collapse: collapse; border: 0; max-width: 600px!important;', width: '100%' } + %tbody + %tr + %td{ style: 'background: #ffffff none no-repeat center/cover; background-color: #ffffff; background-image: none; background-repeat: no-repeat; background-position: center; background-size: cover; border-top: 0; padding-top: 0;', valign: 'top' } + %table{ border: '0', cellpadding: '0', cellspacing: '0', style: 'min-width: 100%; border-collapse: collapse', width: '100%' } + %tr + %td{ style: 'padding: 0 30px; mso-line-height-rule: exactly; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; ', valign: 'top' } + %img{ align: 'middle', alt: 'Logo TPS', src: image_url('mailer/gestionnaire_mailer/logo.png'), style: 'max-width: 125px; padding: 30px 0; display: inline !important; vertical-align: bottom; border: 0; height: auto; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic;' } + %tr + %td{ style: 'padding: 0 30px 30px; word-break: break-word; color: #333333; font-family: Helvetica; font-size: 16px; line-height: 150%; text-align: left; border-bottom: 2px solid #4393F3;', valign: 'top' } + Bonjour, voici votre résumé de l'activité de la semaine du #{l(@args[:start_date], format: '%d %B')} au #{l(DateTime.now, format: '%d %B')}. + %br + %br + + - @args[:procedure_overviews].each do |procedure_overview| + = procedure_overview.to_html.html_safe + %br + %br + Bonne journée, + %br + %br + L'équipe Téléprocédures Simplifiées diff --git a/app/workers/weekly_overview_worker.rb b/app/workers/weekly_overview_worker.rb new file mode 100644 index 000000000..8f15194a7 --- /dev/null +++ b/app/workers/weekly_overview_worker.rb @@ -0,0 +1,13 @@ +class WeeklyOverviewWorker + include Sidekiq::Worker + + def perform(*args) + # Feature flipped to avoid mails in staging due to unprocessed dossier + if Features.weekly_overview + Gestionnaire.all + .map { |gestionnaire| [gestionnaire, gestionnaire.last_week_overview] } + .reject { |_, overview| overview.nil? } + .each { |gestionnaire, overview| GestionnaireMailer.last_week_overview(gestionnaire, overview).deliver_now } + end + end +end diff --git a/config/environments/development.rb b/config/environments/development.rb index acb324db7..a03dda8e0 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -42,6 +42,7 @@ Rails.application.configure do # Action Mailer settings config.action_mailer.delivery_method = :smtp config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } + config.action_mailer.asset_host = 'http://localhost:3000' # Config for mailcatcher https://mailcatcher.me/ config.action_mailer.smtp_settings = { :address => "localhost", diff --git a/config/initializers/features.yml b/config/initializers/features.yml index e91fb5346..7776e7e30 100644 --- a/config/initializers/features.yml +++ b/config/initializers/features.yml @@ -1 +1,2 @@ remote_storage: false +weekly_overview: false diff --git a/config/schedule.yml b/config/schedule.yml index 79dbdefc9..9a2979d42 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -1,3 +1,6 @@ auto_archive_procedure: cron: "* * * * *" class: "AutoArchiveProcedureWorker" +weekly_overview_worker: + cron: "0 8 * * 0" + class: "WeeklyOverviewWorker" diff --git a/spec/mailers/previews/gestionnaire_mailer_preview.rb b/spec/mailers/previews/gestionnaire_mailer_preview.rb new file mode 100644 index 000000000..2e62b4f5c --- /dev/null +++ b/spec/mailers/previews/gestionnaire_mailer_preview.rb @@ -0,0 +1,6 @@ +class GestionnaireMailerPreview < ActionMailer::Preview + def last_week_overview + gestionnaire = Gestionnaire.first + GestionnaireMailer.last_week_overview(gestionnaire, gestionnaire.last_week_overview) + end +end diff --git a/spec/models/gestionnaire_spec.rb b/spec/models/gestionnaire_spec.rb index 8525b2e86..27cb3502e 100644 --- a/spec/models/gestionnaire_spec.rb +++ b/spec/models/gestionnaire_spec.rb @@ -347,4 +347,52 @@ describe Gestionnaire, type: :model do end end end + + describe 'last_week_overview' do + let!(:gestionnaire2) { create(:gestionnaire) } + subject { gestionnaire2.last_week_overview } + let(:friday) { DateTime.new(2017, 5, 12) } + let(:monday) { DateTime.now.beginning_of_week } + + before :each do + Timecop.freeze(friday) + end + + context 'when no procedure published was active last week' do + let!(:procedure) { create(:procedure, gestionnaires: [gestionnaire2], libelle: 'procedure', published: true) } + context 'when the gestionnaire has no notifications' do + it { is_expected.to eq(nil) } + end + + context 'when the gestionnaire has one notification' do + before :each do + expect(gestionnaire2).to receive(:notifications).twice.and_return([1]) + end + + it { is_expected.to eq({ start_date: monday, procedure_overviews: [], notifications: [1] }) } + end + end + + context 'when a procedure published was active' do + let!(:procedure) { create(:procedure, gestionnaires: [gestionnaire2], libelle: 'procedure', published: true) } + let(:procedure_overview) { double('procedure_overview', 'had_some_activities?'.to_sym => true) } + + before :each do + expect_any_instance_of(Procedure).to receive(:procedure_overview).and_return(procedure_overview) + end + + it { expect(gestionnaire.last_week_overview[:procedure_overviews]).to match([procedure_overview]) } + end + + context 'when a procedure not published was active with no notifications' do + let!(:procedure) { create(:procedure, gestionnaires: [gestionnaire2], libelle: 'procedure', published: false) } + let(:procedure_overview) { double('procedure_overview', 'had_some_activities?'.to_sym => true) } + + before :each do + allow_any_instance_of(Procedure).to receive(:procedure_overview).and_return(procedure_overview) + end + + it { is_expected.to eq(nil) } + end + end end diff --git a/spec/models/procedure_overview_spec.rb b/spec/models/procedure_overview_spec.rb new file mode 100644 index 000000000..c6670d730 --- /dev/null +++ b/spec/models/procedure_overview_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +describe ProcedureOverview, type: :model do + let(:procedure) { create(:procedure, libelle: 'libelle') } + let(:friday) { DateTime.new(2017, 5, 12) } # vendredi 12 mai 2017, de la semaine du 8 mai + let(:monday) { DateTime.new(2017, 5, 8) } + + before :each do + Timecop.freeze(friday) + end + + let(:procedure_overview) { ProcedureOverview.new(procedure, monday, 0) } + + describe 'received_dossiers_count' do + let!(:received_dossier) do + dossier = create(:dossier, procedure: procedure, state: :received, created_at: monday) + end + + it { expect(procedure_overview.received_dossiers_count).to eq(1) } + end + + describe 'created_dossiers_count' do + let!(:created_dossier_during_the_week) do + create(:dossier, procedure: procedure, created_at: monday, state: :received) + end + + let!(:created_dossier_during_the_week_but_in_draft) do + create(:dossier, procedure: procedure, created_at: monday, state: :draft) + end + + let!(:created_dossier_before_the_week) do + create(:dossier, procedure: procedure, created_at: (monday - 1.week), state: :received) + end + + it { expect(procedure_overview.created_dossiers_count).to eq(1) } + end + + describe 'processed_dossiers_count' do + let!(:processed_dossier_during_the_week) do + create(:dossier, procedure: procedure, created_at: monday, processed_at: monday) + end + + let!(:processed_dossier_before_the_week) do + create(:dossier, procedure: procedure, created_at: (monday - 1.week), processed_at: (monday - 1.week)) + end + + it { expect(procedure_overview.processed_dossiers_count).to eq(1) } + end + + describe 'to_html' do + subject { procedure_overview.to_html } + + context 'when the different count are equal to 0' do + it { is_expected.to match(/^libelle<\/strong><\/a>$/) } + end + + context 'when the different counts are equal to 1' do + before :each do + procedure_overview.notifications_count = 1 + procedure_overview.received_dossiers_count = 1 + procedure_overview.created_dossiers_count = 1 + procedure_overview.processed_dossiers_count = 1 + end + + it { is_expected.to match(/^libelle<\/strong><\/a>/) } + it { is_expected.to include("1 dossier est en cours d'instruction") } + it { is_expected.to include('1 nouveau dossier a été déposé') } + it { is_expected.to include('1 dossier a été instruit') } + it { is_expected.to include('1 notification en attente sur les dossiers que vous suivez') } + end + + context 'when the different counts are equal to 2' do + before :each do + procedure_overview.notifications_count = 2 + procedure_overview.received_dossiers_count = 3 + procedure_overview.created_dossiers_count = 4 + procedure_overview.processed_dossiers_count = 5 + end + + it { is_expected.to match(/^libelle<\/strong><\/a>/) } + it { is_expected.to include("3 dossiers sont en cours d'instruction") } + it { is_expected.to include('4 nouveaux dossiers ont été déposés') } + it { is_expected.to include('5 dossiers ont été instruits') } + it { is_expected.to include('2 notifications en attente sur les dossiers que vous suivez') } + end + end +end diff --git a/spec/workers/weekly_overview_worker_spec.rb b/spec/workers/weekly_overview_worker_spec.rb new file mode 100644 index 000000000..e2bff3e80 --- /dev/null +++ b/spec/workers/weekly_overview_worker_spec.rb @@ -0,0 +1,44 @@ +require 'rails_helper' + +RSpec.describe WeeklyOverviewWorker, type: :worker do + describe 'perform' do + let!(:gestionnaire) { create(:gestionnaire) } + let(:overview) { double('overview') } + let(:mailer_double) { double('mailer', deliver_now: true) } + + context 'if the feature is enabled' do + before { allow(Features).to receive(:weekly_overview).and_return(true) } + + context 'with one gestionnaire with one overview' do + before :each do + expect_any_instance_of(Gestionnaire).to receive(:last_week_overview).and_return(overview) + allow(GestionnaireMailer).to receive(:last_week_overview).and_return(mailer_double) + WeeklyOverviewWorker.new.perform + end + + it { expect(GestionnaireMailer).to have_received(:last_week_overview).with(gestionnaire, overview) } + it { expect(mailer_double).to have_received(:deliver_now) } + end + + context 'with one gestionnaire with no overviews' do + before :each do + expect_any_instance_of(Gestionnaire).to receive(:last_week_overview).and_return(nil) + allow(GestionnaireMailer).to receive(:last_week_overview) + WeeklyOverviewWorker.new.perform + end + + it { expect(GestionnaireMailer).not_to have_received(:last_week_overview) } + end + end + + context 'if the feature is disabled' do + before { allow(Features).to receive(:weekly_overview).and_return(false) } + before :each do + allow(Gestionnaire).to receive(:all) + WeeklyOverviewWorker.new.perform + end + + it { expect(Gestionnaire).not_to receive(:all) } + end + end +end