Class: Campaign::AssignDripCampaigns
- Inherits:
-
BaseService
- Object
- BaseService
- Campaign::AssignDripCampaigns
- Defined in:
- app/services/campaign/assign_drip_campaigns.rb
Defined Under Namespace
Classes: Result
Constant Summary collapse
- PRO_SM_DRIP_LAUNCH =
'PRO_SM_DRIP_LAUNCH'- PRO_EFH_DRIP_LAUNCH =
'PRO_EFH_DRIP_LAUNCH'- HOM_EFH_DRIP_LAUNCH =
'HOM_EFH_DRIP_LAUNCH'- HO_SM_DRIP_LAUNCH =
'HOM_SM_DRIP_LAUNCH'- PRO_SS_LAUNCH =
'PRO_SS_LAUNCH'- BG_DRIP_LAUNCH =
'BG_DRIP_LAUNCH'- PRO_CUSTOMER_FILTER_ID =
Pros w/out DB/ETL/HD/COSTCO
170- HOM_CUSTOMER_FILTER_ID =
Homeowner
40
Instance Attribute Summary collapse
-
#cf_bg ⇒ Object
readonly
Returns the value of attribute cf_bg.
-
#cf_ho ⇒ Object
readonly
Returns the value of attribute cf_ho.
-
#cf_pro ⇒ Object
readonly
Returns the value of attribute cf_pro.
Class Method Summary collapse
Instance Method Summary collapse
-
#create_buying_group_drips(_customer, valid_parties, _buying_group_id) ⇒ Object
Create buying group drip campaign activity, for now it's just any buying group.
-
#create_product_interest_drips(customer, valid_parties, product_line_ids) ⇒ Object
def create_service_drips(_customer, valid_parties, message) activities_created = [] drip_activity_type = ActivityType.active.where(task_type: PRO_SS_LAUNCH).first if drip_activity_type # If the parties previously had this activity open or closed, we skip valid_parties = valid_parties.where.not('exists(select 1 from activities where party_id = parties.id and activity_type_id = ?)', drip_activity_type.id) # Finally, if we have any parties left, they get this activity, since we manually checked for existence skip_if_exists can be false valid_parties.each do |p| activities_created << p.create_activity(drip_activity_type.task_type, skip_if_exists: false, notes: message) end else msg = "Missing drip activity #PRO_SS_LAUNCH" log_error msg ErrorReporting.error(msg) end activities_created end.
-
#evaluate_for_filter_and_drip(customer:, valid_parties:, customer_filter:, product_line_ids:, product_line_url:, task_type:) ⇒ Object
This is our main method where we evaluate a customer against a drip criteria.
-
#initialize(options = {}) ⇒ AssignDripCampaigns
constructor
A new instance of AssignDripCampaigns.
-
#process(params) ⇒ Object
params is expected to be an object responding to - party - product_line_ids_added - product_line_ids_removed - buying_group_id_added - buying_group_id_removed - latitude - longitude.
Methods inherited from BaseService
#log_debug, #log_error, #log_info, #log_warning, #logger, #options, #tagged_logger
Constructor Details
#initialize(options = {}) ⇒ AssignDripCampaigns
Returns a new instance of AssignDripCampaigns.
19 20 21 22 23 24 |
# File 'app/services/campaign/assign_drip_campaigns.rb', line 19 def initialize( = {}) # Initialize our filters @cf_pro = CustomerFilter.find(PRO_CUSTOMER_FILTER_ID) @cf_ho = CustomerFilter.find(HOM_CUSTOMER_FILTER_ID) super end |
Instance Attribute Details
#cf_bg ⇒ Object (readonly)
Returns the value of attribute cf_bg.
17 18 19 |
# File 'app/services/campaign/assign_drip_campaigns.rb', line 17 def cf_bg @cf_bg end |
#cf_ho ⇒ Object (readonly)
Returns the value of attribute cf_ho.
17 18 19 |
# File 'app/services/campaign/assign_drip_campaigns.rb', line 17 def cf_ho @cf_ho end |
#cf_pro ⇒ Object (readonly)
Returns the value of attribute cf_pro.
17 18 19 |
# File 'app/services/campaign/assign_drip_campaigns.rb', line 17 def cf_pro @cf_pro end |
Class Method Details
.compact_valid_parties(valid_parties, logger: nil) ⇒ Object
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'app/services/campaign/assign_drip_campaigns.rb', line 237 def self.compact_valid_parties(valid_parties, logger: nil) curated_parties = valid_parties # If the Customer and Contact share the same email, the Contact prevails, the Customer is eliminated if customer = curated_parties.detect { |p| p.is_a?(Customer) } if customer.email contact_parties = curated_parties.select { |p| p.is_a?(Contact) } contact_emails = contact_parties.filter_map(&:email).uniq # Does any of the contact have this customer email already? if customer.email.in?(contact_emails) # Remove the customer from valid parties logger.warn "Customer #{customer.reference_number} was removed from valid parties because a contact already contained the same email" curated_parties = curated_parties.where.not(id: customer.id) end end end curated_parties end |
Instance Method Details
#create_buying_group_drips(_customer, valid_parties, _buying_group_id) ⇒ Object
Create buying group drip campaign activity, for now it's just any buying group
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'app/services/campaign/assign_drip_campaigns.rb', line 104 def (_customer, valid_parties, ) activities_created = [] drip_activity_type = ActivityType.active.where(task_type: BG_DRIP_LAUNCH).first if drip_activity_type # If the parties previously had this activity open or closed, we skip valid_parties = valid_parties.where.not('exists(select 1 from activities where party_id = parties.id and activity_type_id = ?)', drip_activity_type.id) # Finally, if we have any parties left, they get this activity, since we manually checked for existence skip_if_exists can be false valid_parties.each do |p| append_drip_activity!(activities_created, p, drip_activity_type.task_type) end else # Report the activity type is missing msg = "Missing drip activity #{BG_DRIP_LAUNCH}" log_error msg ErrorReporting.error(msg) end activities_created end |
#create_product_interest_drips(customer, valid_parties, product_line_ids) ⇒ Object
def create_service_drips(_customer, valid_parties, message)
activities_created = []
drip_activity_type = ActivityType.active.where(task_type: PRO_SS_LAUNCH).first
if drip_activity_type
# If the parties previously had this activity open or closed, we skip
valid_parties = valid_parties.where.not('exists(select 1 from activities where party_id = parties.id and activity_type_id = ?)', drip_activity_type.id)
# Finally, if we have any parties left, they get this activity, since we manually checked for existence skip_if_exists can be false
valid_parties.each do |p|
activities_created << p.create_activity(drip_activity_type.task_type, skip_if_exists: false, notes: message)
end
else
msg = "Missing drip activity #PRO_SS_LAUNCH"
log_error msg
ErrorReporting.error(msg)
end
activities_created
end
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'app/services/campaign/assign_drip_campaigns.rb', line 142 def create_product_interest_drips(customer, valid_parties, product_line_ids) activities_created = [] # Use product_line_url (exact) for ltree-based hierarchy matching drips = [ { customer_filter: cf_pro, product_line_ids: product_line_ids, product_line_url: 'snow-melting', task_type: PRO_SM_DRIP_LAUNCH }, { customer_filter: cf_pro, product_line_ids: product_line_ids, product_line_url: 'floor-heating', task_type: PRO_EFH_DRIP_LAUNCH }, { customer_filter: cf_ho, product_line_ids: product_line_ids, product_line_url: 'floor-heating', task_type: HOM_EFH_DRIP_LAUNCH }, { customer_filter: cf_ho, product_line_ids: product_line_ids, product_line_url: 'snow-melting', task_type: HO_SM_DRIP_LAUNCH } ] # Loop through our drips drips.each do |drip_params| # Add customer and valid parties which are always the same to each drip merged_drip_params = { customer: customer, valid_parties: valid_parties }.merge(drip_params) # Now run the evaluation and assign method res = evaluate_for_filter_and_drip(**merged_drip_params) # Aggregate activities created, if it was an error this would be an empty array activities_created += res.activities_created end activities_created end |
#evaluate_for_filter_and_drip(customer:, valid_parties:, customer_filter:, product_line_ids:, product_line_url:, task_type:) ⇒ Object
This is our main method where we evaluate a customer against a drip criteria
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'app/services/campaign/assign_drip_campaigns.rb', line 188 def evaluate_for_filter_and_drip(customer:, valid_parties:, customer_filter:, product_line_ids:, product_line_url:, task_type:) return Result.new(error: 'Customer filter missing') unless customer_filter # Qualify our customer and parties which have an email unless customer_filter.applies_to_customer?(customer) msg = "Customer #{customer.reference_number} does not match Filter #{customer_filter.id}" log_warning msg return Result.new(error: msg) end activities_created = [] # Use ltree for precise hierarchy matching instead of URL LIKE root_pl = ProductLine.find_by(slug_ltree: LtreePaths.slug_ltree_from_legacy_hyphen_url(product_line_url)) || ProductLine.find_by(slug_ltree: product_line_url) product_lines_added = if root_pl ProductLine.where(id: product_line_ids).where(ProductLine[:ltree_path_ids].ltree_descendant(root_pl.ltree_path_ids)) else ProductLine.none end if product_lines_added.present? # Engage and assign the activity to our parties drip_activity_type = ActivityType.active.where(task_type: task_type).first if drip_activity_type # If the parties previously had this activity open or closed, we skip valid_parties = valid_parties.where.not('exists(select 1 from activities where party_id = parties.id and activity_type_id = ?)', drip_activity_type.id) # Finally, if we have any parties left, they get this activity, since we manually checked for existence skip_if_exists can be false valid_parties.each do |p| append_drip_activity!(activities_created, p, drip_activity_type.task_type) end else # Report the activity type is missing msg = "Missing drip activity #{task_type}" log_error msg ErrorReporting.error(msg) end else log_info "No qualifying product lines added matching #{product_line_url}. Product Line ids added are: #{product_line_ids.join(', ')}" end Result.new(activities_created: activities_created) end |
#process(params) ⇒ Object
params is expected to be an object responding to
- party
- product_line_ids_added
- product_line_ids_removed
- buying_group_id_added
- buying_group_id_removed
- latitude
- longitude
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'app/services/campaign/assign_drip_campaigns.rb', line 34 def process(params) party = params.party product_line_ids_added = params.product_line_ids_added = params. parties = [] case party when Customer customer = party parties = [customer, *customer.contacts.active] when Contact customer = party.customer parties = [party] end unless customer msg = "No customer present for party id #{party.id} for drip assignment" log_warning msg return Result.new(error: msg) end # We have to check that we have the right profile for this customer and that a snow melt interest was added activities_created = [] # Prequalify our parties, can they receive announcements for instance? valid_parties = Party.distinct.where(id: parties.map(&:id)).joins(:contact_points).merge(ContactPoint.emails).where.not('exists(select 1 from email_preferences ep where ep.email = contact_points.detail and disable_announcements = true)') if (excluded_parties = (parties.to_a - valid_parties.to_a)).present? log_info "Parties #{excluded_parties.map(&:id).join(', ')} were excluded from drip because they had no email or their email were unsubscribed from announcements emails" end # remove customer when we are have duplicate emails valid_parties = self.class.compact_valid_parties(valid_parties, logger: logger) if valid_parties.blank? msg = 'No valid parties are present for drip assignment' log_warning msg return Result.new(error: msg) end # At some point we might have unsubscribe if interests are removed activities_created += create_product_interest_drips(customer, valid_parties, product_line_ids_added) if product_line_ids_added.present? # At some point we might have unsubscribe if buying groups are removed activities_created += (customer, valid_parties, ) if .present? # if params.latitude && params.longitude # # What's our warehouse lat and long # store_address = Store.find(1).warehouse_address # service_center_latitude = store_address.lat # service_center_longitude = store_address.lng # # What's the distance (miles default) # d = Geocoder::Calculations.distance_between([service_center_latitude, service_center_longitude], [params.latitude, params.longitude]) # d = d.round(2) # # are we within service limits? # if d < SMART_SERVICES_MAX_DISTANCE # activities_created += create_service_drips(customer, valid_parties, "Coordinates within #{d} miles of service center") # end # end Result.new(activities_created: activities_created) rescue StandardError => e msg = "Exception #{e} raised in drip campaign assigner" log_error msg raise e unless Rails.env.production? ErrorReporting.error(e) Result.new(error: msg) # Render exception right away end |