# frozen_string_literal: true # EffectiveCpdBulkAudit # # Mark your owner model with effective_cpd_bulk_audit to get all the includes module EffectiveCpdBulkAudit extend ActiveSupport::Concern module Base def effective_cpd_bulk_audit include ::EffectiveCpdBulkAudit end end module ClassMethods def effective_cpd_bulk_audit?; true; end end included do attr_accessor :current_user # App scoped belongs_to :cpd_audit_level, polymorphic: true log_changes if respond_to?(:log_changes) effective_resource do cpd_user_class_name :string audits :integer audit_reviewers_per_audit :integer notification_date :date email_form_skip :boolean auditees_count :integer audit_reviewers_count :integer timestamps end scope :deep, -> { includes(:cpd_audit_level) } scope :sorted, -> { order(:id) } before_validation(if: -> { current_user.present? }) do self.cpd_user_class_name ||= current_user.class.name end before_validation do self.notification_date ||= Time.zone.now end before_validation(if: -> { cpd_user_scope.present? }) do self.auditees_count ||= cpd_auditees.count() self.audit_reviewers_count ||= cpd_audit_reviewers.count() end validates :cpd_user_class_name, presence: true validates :audits, presence: true, numericality: { greater_than: 0, less_than_or_equal_to: 1000 } validates :audit_reviewers_per_audit, presence: true, numericality: { greater_than: 0, less_than_or_equal_to: 3 } validates :auditees_count, numericality: { greater_than: 0 } validates :audit_reviewers_count, numericality: { greater_than: 0 } validate(if: -> { cpd_user_scope.present? }) do self.errors.add(:cpd_user_class_name, "expecting an effective_cpd_user") unless cpd_user_scope.respond_to?(:effective_cpd_user?) end validate(if: -> { audits.present? && auditees_count.present? }) do self.errors.add(:audits, "can't be more than the number of auditees #{auditees_count}") if audits > auditees_count end validate(if: -> { audit_reviewers_per_audit.present? && audit_reviewers_count.present? }) do self.errors.add(:audit_reviewers_per_audit, "can't be more than the number of audit reviewers #{audit_reviewers_count}") if audit_reviewers_per_audit > audit_reviewers_count end after_commit(on: :create) { create_bulk_audit_job! } end def to_s model_name.human end def cpd_user_scope cpd_user_class_name.constantize.all if cpd_user_class_name.present? end def cpd_auditees cpd_user_scope.send(EffectiveCpd.auditee_user_scope) end def cpd_audit_reviewers cpd_user_scope.send(EffectiveCpd.audit_reviewer_user_scope) end def create_bulk_audit_job! raise('must be persisted') unless persisted? Effective::CpdBulkAuditJob.perform_later(id) end # Called by Effective::CpdBulkAuditJob def create_audits! save! @auditees = cpd_auditees.reorder('RANDOM()').limit(audits) @reviewers = cpd_audit_reviewers.reorder('RANDOM()').limit(audits).to_a @auditees.each do |auditee| reviewers = audit_reviewers_per_audit.times.map { next_audit_reviewer() } cpd_audit = build_cpd_audit(auditee, reviewers) cpd_audit.save! end true end def build_cpd_audit(auditee, reviewers) raise('expected auditee cpd user') unless auditee.class.respond_to?(:effective_cpd_user?) reviewers = Array(reviewers) raise('expected audit reviewers') unless reviewers.all? { |reviewer| reviewer.try(:cpd_audit_reviewer?) } cpd_audit = EffectiveCpd.CpdAudit.new( cpd_audit_level: cpd_audit_level, email_form_skip: email_form_skip?, user: auditee, notification_date: notification_date ) reviewers.each do |reviewer| cpd_audit.cpd_audit_reviews.build( cpd_audit_level: cpd_audit_level, email_form_skip: email_form_skip?, user: reviewer ) end cpd_audit end # Round robin def next_audit_reviewer raise('expected @audit_reviewers to be set') unless @reviewers @reviewers_index ||= -1 # Next Reviewer @reviewers_index += 1 @reviewers_index = 0 if @reviewers_index >= @reviewers.length @reviewers[@reviewers_index] end end