module Mailings class AudienceQuery def initialize(project, filters = {}, mailing: nil) @project = project @filters = filters.is_a?(AudienceFilters) ? filters : AudienceFilters.new(filters) @mailing = mailing end def relation scope = Organization.for_project(@project) scope = apply_preset(scope) scope = apply_organization_filters(scope) scope = apply_opportunity_filters(scope) scope = apply_history_filters(scope) scope = apply_exclusions(scope) scope.distinct end def counts { total: relation.unscope(:order).distinct.count("organizations.id"), with_email: with_email_scope(relation).unscope(:order).distinct.count("organizations.id") } end def preview(limit = 15) relation.includes(:primary_contact).reorder(Arel.sql("organizations.list_position ASC NULLS LAST"), "organizations.name ASC").limit(limit) end private def apply_preset(scope) case @filters.preset when "to_send" scope.where(id: opportunity_scope.where(send_status: "to_send").select(:organization_id)) when "test_a" scope.where(id: opportunity_scope.where(ab_variant: "A").select(:organization_id)) when "test_b" scope.where(id: opportunity_scope.where(ab_variant: "B").select(:organization_id)) else scope end end def apply_organization_filters(scope) scope = scope.where(sport: @filters["sports"]) if @filters["sports"].present? scope = scope.where(team_gender: @filters["team_genders"]) if @filters["team_genders"].present? scope = scope.where(region: @filters["regions"]) if @filters["regions"].present? scope = scope.where(province: @filters["provinces"]) if @filters["provinces"].present? scope = scope.where(streaming_status: @filters["streaming_statuses"]) if @filters["streaming_statuses"].present? scope = scope.where(status: @filters["statuses"]) if @filters["statuses"].present? scope = scope.where("organizations.list_position >= ?", @filters["list_min"]) if @filters["list_min"].present? scope = scope.where("organizations.list_position <= ?", @filters["list_max"]) if @filters["list_max"].present? scope end def apply_opportunity_filters(scope) extra = opportunity_scope filtered = false if @filters["send_statuses"].present? extra = extra.where(send_status: @filters["send_statuses"]) filtered = true end if @filters["ab_variants"].present? extra = extra.where(ab_variant: @filters["ab_variants"]) filtered = true end if @filters["pipeline_stages"].present? extra = extra.where(pipeline_stage: @filters["pipeline_stages"]) filtered = true end case @filters["estimated_value"] when "present" extra = extra.where("opportunities.estimated_value IS NOT NULL AND opportunities.estimated_value > 0") filtered = true when "blank" extra = extra.where("opportunities.estimated_value IS NULL OR opportunities.estimated_value = 0") filtered = true end filtered ? scope.where(id: extra.select(:organization_id)) : scope end def apply_history_filters(scope) case @filters.history_kind when "never" scope.where.not(id: sent_org_ids).where.not(id: emailed_activity_org_ids) when "received" scope.where(id: sent_org_ids(mailing_id: @filters.history_mailing_id)) when "not_received" scope.where.not(id: sent_org_ids(mailing_id: @filters.history_mailing_id)) when "opened" scope.where(id: opened_org_ids(mailing_id: @filters.history_mailing_id)) when "not_opened" sent = sent_org_ids(mailing_id: @filters.history_mailing_id) opened = opened_org_ids(mailing_id: @filters.history_mailing_id) scope.where(id: sent).where.not(id: opened) when "recent" days = @filters.history_days.presence || 30 scope.where(id: sent_org_ids(since: days.days.ago)) else scope end end def apply_exclusions(scope) scope = scope.where.not(status: %w[active_customer inactive_customer]) if @filters["exclude_customers"] if @filters["exclude_received_mailing_ids"].present? scope = scope.where.not(id: sent_org_ids(mailing_id: project_mailing_ids(@filters["exclude_received_mailing_ids"]))) end if @filters["exclude_opened_mailing_ids"].present? scope = scope.where.not(id: opened_org_ids(mailing_id: project_mailing_ids(@filters["exclude_opened_mailing_ids"]))) end if @filters["exclude_mailed_within_days"].present? scope = scope.where.not(id: sent_org_ids(since: @filters["exclude_mailed_within_days"].days.ago)) end scope end def with_email_scope(scope) scope.left_joins(:contacts).where( "NULLIF(BTRIM(organizations.email), '') IS NOT NULL OR NULLIF(BTRIM(contacts.email), '') IS NOT NULL" ) end def opportunity_scope Opportunity.where(project_id: @project.id) end def emailed_activity_org_ids Activity.where(activity_type: "email_sent", organization_id: Organization.for_project(@project).select(:id)).select(:organization_id) end def sent_org_ids(mailing_id: nil, since: nil) recips = history_recipients.where(status: "sent") recips = recips.where(mailing_id: mailing_id) if mailing_id.present? recips = recips.where("mailing_recipients.sent_at >= ?", since) if since.present? recips.select(:organization_id) end def opened_org_ids(mailing_id: nil) recips = history_recipients.where.not(opened_at: nil) recips = recips.where(mailing_id: mailing_id) if mailing_id.present? recips.select(:organization_id) end def history_recipients recips = MailingRecipient.joins(:mailing).where(mailings: { project_id: @project.id }) recips = recips.where.not(mailing_id: @mailing.id) if @mailing&.persisted? recips end def project_mailing_ids(ids) Mailing.for_project(@project).where(id: ids).select(:id) end end end