diff --git a/app/controllers/notification_logs_controller.rb b/app/controllers/notification_logs_controller.rb index c378d2c0..7eb6cec7 100644 --- a/app/controllers/notification_logs_controller.rb +++ b/app/controllers/notification_logs_controller.rb @@ -7,7 +7,7 @@ class NotificationLogsController < ApplicationController active_scaffold :"notification_log" do |config| config.columns = [:notification_name, :to, :subject, :body, :created_at] - config.show.columns = [:notification_name, :to, :subject, :body, :created_at] + config.show.columns = [:notification_name, :to, :subject, :body, :attachments_file_names, :created_at] config.columns[:notification_name].label = I18n.t('activerecord.attributes.notification_log.notification_name') config.list.sorting = {:created_at => 'DESC'} config.actions.exclude :create, :delete, :update diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 9d4a74c4..edab0463 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -6,6 +6,8 @@ class NotificationsController < ApplicationController skip_authorization_check :only => [:notify] skip_before_action :authenticate_user!, :only => :notify before_action :permit_notification_params + helper PdfHelper + helper EnrollmentsPdfHelper def permit_notification_params params[:notification_params].permit! unless params[:notification_params].nil? @@ -21,10 +23,10 @@ def permit_notification_params :type => :member - form_columns = [:title, :frequency, :notification_offset, :query_offset, :query, :params, :individual, :to_template, :subject_template, :body_template] + form_columns = [:title, :frequency, :notification_offset, :query_offset, :query, :params, :individual, :has_grades_report_pdf_attachment, :to_template, :subject_template, :body_template] config.columns = form_columns config.update.columns = form_columns - config.create.columns = [:title, :frequency, :notification_offset, :query_offset, :query, :individual, :to_template, :subject_template, :body_template] + config.create.columns = [:title, :frequency, :notification_offset, :query_offset, :query, :individual, :has_grades_report_pdf_attachment, :to_template, :subject_template, :body_template] config.show.columns = form_columns + [:next_execution] config.list.columns = [:title, :frequency, :notification_offset, :query_offset, :next_execution] @@ -35,6 +37,7 @@ def permit_notification_params config.columns[:frequency].options = {:options => Notification::FREQUENCIES} config.columns[:frequency].description = I18n.t('active_scaffold.notification.frequency_description') config.columns[:individual].description = I18n.t('active_scaffold.notification.individual_description') + config.columns[:has_grades_report_pdf_attachment].description = I18n.t('active_scaffold.notification.has_grades_report_pdf_attachment_description') config.columns[:notification_offset].description = I18n.t('active_scaffold.notification.notification_offset_description') config.columns[:query_offset].description = I18n.t('active_scaffold.notification.query_offset_description') config.columns[:query].update_columns = [:query, :params] @@ -70,7 +73,26 @@ def execute_now notification_params = notification_params.to_unsafe_h if notification_params.is_a?(ActionController::Parameters) query_date = Date.strptime(params[:data_consulta], "%Y-%m-%d") unless params[:data_consulta].nil? query_date ||= notification.query_date.to_date - Notifier.send_emails(notification.execute(override_params: notification_params)) + + notification_execute = notification.execute(override_params: notification_params) + notification_execute[:notifications].each do |message| + + message_attachments = notification_execute[:notifications_attachments][message] + if message_attachments + if message_attachments[:grades_report_pdf] + enrollments_id = message_attachments[:grades_report_pdf][:file_contents] + + enrollment = Enrollment.find(enrollments_id) + class_enrollments = enrollment.class_enrollments.where(:situation => I18n.translate("activerecord.attributes.class_enrollment.situations.aproved")).joins(:course_class).order("course_classes.year", "course_classes.semester") + accomplished_phases = enrollment.accomplishments.order(:conclusion_date) + deferrals = enrollment.deferrals.order(:approval_date) + + message_attachments[:grades_report_pdf][:file_contents] = render_to_string(template: 'enrollments/grades_report_pdf', :assigns => {enrollment: enrollment, class_enrollments: class_enrollments, accomplished_phases: accomplished_phases, deferrals: deferrals}) + end + end + end + + Notifier.send_emails(notification_execute) self.successful = true flash[:info] = I18n.t('active_scaffold.notification.execute_now_success') diff --git a/app/models/notification.rb b/app/models/notification.rb index 48984022..8c289c2f 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -31,6 +31,8 @@ class Notification < ApplicationRecord validates_format_of :notification_offset, :with => /\A(\-?\d+[yMwdhms]?)+\z/, :message => :offset_invalid_value validates_format_of :query_offset, :with => /\A(\-?\d+[yMwdhms]?)+\z/, :message => :offset_invalid_value + validate :has_grades_report_pdf_attachment_requirements + validate do execution unless self.new_record? end @@ -51,6 +53,18 @@ class Notification < ApplicationRecord end after_create :update_next_execution! + def has_grades_report_pdf_attachment_requirements + if self.has_grades_report_pdf_attachment + if ! self.individual + errors.add(:has_grades_report_pdf_attachment, :individual_required) + end + + if ! self.query.execute(prepare_params_and_derivations({:data_consulta=>Time.zone.now}))[:columns].include?("enrollments_id") + errors.add(:has_grades_report_pdf_attachment, :query_with_enrollments_id_alias_column_required) + end + end + end + def query_params_ids (self.query.try(:params) || []).collect &:id end @@ -133,7 +147,7 @@ def set_notification_params_values(params) def execute(options={}) notifications = [] - + notifications_attachments = {} params = prepare_params_and_derivations(options[:override_params] || {}) set_notification_params_values(params) @@ -148,12 +162,25 @@ def execute(options={}) bindings.merge!(Hash[result[:columns].zip(raw_result)]) formatter = ERBFormatter.new(bindings) - notifications << { + + attachments = {} + + #add grades_report_pdf attachment if required + if self.has_grades_report_pdf_attachment + attachment_file_name = "#{I18n.t('pdf_content.enrollment.grades_report.title')} - #{Enrollment.find(bindings["enrollments_id"]).student.name}.pdf" + attachment_file_contents = bindings["enrollments_id"] + attachments[:grades_report_pdf] = {:file_name => attachment_file_name, :file_contents => attachment_file_contents} + end + + notification = { :notification_id => self.id, :to => formatter.format(self.to_template), :subject => formatter.format(self.subject_template), :body => formatter.format(self.body_template) } + + notifications << notification + notifications_attachments[notification] = attachments end else unless result[:rows].empty? @@ -169,7 +196,7 @@ def execute(options={}) end self.update_next_execution! unless options[:skip_update] end - {:notifications => notifications, :query => result[:query]} + {:notifications => notifications, :notifications_attachments => notifications_attachments, :query => result[:query]} end def set_params_for_exhibition(override_params) diff --git a/config/environments/production.rb b/config/environments/production.rb index c6ea28dd..d5cd1e7d 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -90,7 +90,7 @@ config.should_send_emails = true config.action_mailer.delivery_method = :sendmail config.action_mailer.default_url_options = { :host => 'sapos.ic.uff.br' } - config.action_mailer.sendmail_settings = { :arguments => '-i -f "SAPOS "', :location => '/usr/sbin/sendmail' } + config.action_mailer.sendmail_settings = { :arguments => '-i ', :location => '/usr/sbin/sendmail' } config.middleware.use ExceptionNotifier, :email_prefix => "[SAPOS: Error] ", diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index b92e52a6..207ba4bb 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -232,6 +232,7 @@ pt-BR: title: "Título" to_template: "Template do Destinatário" individual: "Individual" + has_grades_report_pdf_attachment: "Boletim anexado" no_name: "Sem nome" notification_log: body: "Corpo" @@ -241,6 +242,7 @@ pt-BR: notification: "Notificação" notification_name: "Notificação" notification_removed_or_sync: "Notificação Síncrona ou Assíncrona Removida" + attachments_file_names: "Anexos" phase: accomplishments: "Realização de Etapas" description: "Descrição" @@ -530,6 +532,8 @@ pt-BR: after_dismissal_date: "posterior a Data de Desligamento" notification: offset_invalid_value: "está com o valor inválido. Tente um número, seguido por y, M, W, d, h, m ou s" + individual_required: "requer que o email seja individual" + query_with_enrollments_id_alias_column_required: "requer que a Consulta possua com campo com alias enrollments_id" scholarship: attributes: end_date: @@ -848,6 +852,7 @@ pt-BR: execute_now: "Enviar agora" simulate: "Simular" individual_description: "Um e-mail para cada resultado" + has_grades_report_pdf_attachment_description: "Requer email individual e um campo na query, com alias enrollments_id" notification_offset_description: "Data de execução. Frequência Mensal com offset 3d executará todo dia 04 do mês" query_offset_description: "Data de consulta. Frequência Semestral com offset -1M terá variavel para dias 01/02 ou 01/07" execute_now_success: 'Notificação disparda com sucesso' diff --git a/db/migrate/20200924170230_add_has_grades_report_pdf_attachment_to_notification.rb b/db/migrate/20200924170230_add_has_grades_report_pdf_attachment_to_notification.rb new file mode 100644 index 00000000..1fe17a7b --- /dev/null +++ b/db/migrate/20200924170230_add_has_grades_report_pdf_attachment_to_notification.rb @@ -0,0 +1,12 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +class AddHasGradesReportPdfAttachmentToNotification < ActiveRecord::Migration[5.1] + def self.up + add_column :notifications, :has_grades_report_pdf_attachment, :boolean, default: false + end + + def self.down + remove_column :notifications, :has_grades_report_pdf_attachment + end +end diff --git a/db/migrate/20200924175430_add_attachments_file_names_to_notification_log.rb b/db/migrate/20200924175430_add_attachments_file_names_to_notification_log.rb new file mode 100644 index 00000000..a38d0ea8 --- /dev/null +++ b/db/migrate/20200924175430_add_attachments_file_names_to_notification_log.rb @@ -0,0 +1,12 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +class AddAttachmentsFileNamesToNotificationLog < ActiveRecord::Migration[5.1] + def self.up + add_column :notification_logs, :attachments_file_names, :string + end + + def self.down + remove_column :notification_logs, :attachments_file_names + end +end diff --git a/db/schema.rb b/db/schema.rb index d8bbe917..00245295 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2,15 +2,15 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). +# This file is the source Rails uses to define your schema when running `rails +# db:schema:load`. When creating a new database, `rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20200110161221) do +ActiveRecord::Schema.define(version: 2020_09_24_175430) do create_table "accomplishments", force: :cascade do |t| t.integer "enrollment_id" @@ -248,6 +248,7 @@ t.string "to", limit: 255 t.string "subject", limit: 255 t.text "body" + t.string "attachments_file_names" t.index ["notification_id"], name: "index_notification_logs_on_notification_id" end @@ -275,6 +276,7 @@ t.datetime "updated_at", null: false t.boolean "individual", default: true t.integer "query_id", null: false + t.boolean "has_grades_report_pdf_attachment", default: false end create_table "phase_completions", force: :cascade do |t| diff --git a/lib/notifier.rb b/lib/notifier.rb index a6fb94b0..685afe2c 100644 --- a/lib/notifier.rb +++ b/lib/notifier.rb @@ -45,10 +45,12 @@ def self.send_emails(notifications) end messages = notifications[:notifications] + messages_attachments = notifications[:notifications_attachments] || {} messages.each do |message| options = {} m = message.merge(options) + m_attachments = messages_attachments[message] unless CustomVariable.notification_footer.empty? m[:body] += "\n\n\n" + CustomVariable.notification_footer end @@ -58,24 +60,43 @@ def self.send_emails(notifications) m[:to] = CustomVariable.redirect_email end unless m[:to].nil? or m[:to].empty? - ActionMailer::Base.mail(m).deliver! + + actionmailer_base = ActionMailer::Base.new + + attachments_file_name_list = nil + if m_attachments + attachments_file_name_list = "" + m_attachments.keys.each do |attachment_key| + attachment = m_attachments[attachment_key] + actionmailer_base.attachments[attachment[:file_name]] = {mime_type: 'application/pdf', content: attachment[:file_contents]} + attachments_file_name_list += attachment[:file_name] + ", " + end + attachments_file_name_list = attachments_file_name_list[0..-3] + end + + actionmailer_base.mail(m).deliver! + NotificationLog.new( :notification_id => m[:notification_id], :to => m[:to], :subject => m[:subject], - :body => m[:body] + :body => m[:body], + :attachments_file_names => attachments_file_name_list ).save - Notifier.display_notification_info(m) + Notifier.display_notification_info(m, attachments_file_name_list) end end end - def self.display_notification_info(notification) + def self.display_notification_info(notification, attachments_file_name_list) Notifier.logger.info "\n#{Time.now.strftime('%Y/%m/%d %H:%M:%S')}" Notifier.logger.info "########## Notification ##########" Notifier.logger.info "Notifying #{notification[:to]}" Notifier.logger.info "Subject: #{notification[:subject]}" Notifier.logger.info "body: #{notification[:body]}" + if attachments_file_name_list + Notifier.logger.info "Attachments: #{attachments_file_name_list}" + end Notifier.logger.info "##################################" end end diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb index decfc027..51678401 100644 --- a/spec/models/notification_spec.rb +++ b/spec/models/notification_spec.rb @@ -121,6 +121,41 @@ end end end + + describe "has_grades_report_pdf_attachment" do + context "should be valid when" do + it "email is individual and query has enrollments_id column alias" do + notification.has_grades_report_pdf_attachment = true + notification.sql_query = "SELECT ENROLLMENTS.ID AS enrollments_id FROM ENROLLMENTS" + notification.individual = true + expect(notification).to have(0).errors_on :has_grades_report_pdf_attachment + end + end + + context "should have error when" do + it "email individual is not individual" do + notification.has_grades_report_pdf_attachment = true + notification.sql_query = "SELECT ENROLLMENTS.ID AS enrollments_id FROM ENROLLMENTS" + notification.individual = false + expect(notification).to have_error(:individual_required).on :has_grades_report_pdf_attachment + end + + it "query has not enrollments_id column alias" do + notification.has_grades_report_pdf_attachment = true + notification.sql_query = "SELECT ENROLLMENTS.ID FROM ENROLLMENTS" + notification.individual = true + expect(notification).to have_error(:query_with_enrollments_id_alias_column_required).on :has_grades_report_pdf_attachment + end + + it "email is not individual and query has not enrollments_id column alias" do + notification.has_grades_report_pdf_attachment = true + notification.sql_query = "SELECT ENROLLMENTS.ID FROM ENROLLMENTS" + notification.individual = false + expect(notification).to have(2).errors_on :has_grades_report_pdf_attachment + end + end + end + end describe "Methods" do