Class: CampaignDecisionService

Inherits:
Object
  • Object
show all
Defined in:
lib/wingify/services/campaign_decision_service.rb

Instance Method Summary collapse

Instance Method Details

#bucket_user_to_variation(context, account_id, campaign) ⇒ VariationModel

Buckets a user into a variation for a given campaign

Parameters:

  • user_id (String)

    The ID of the user

  • account_id (String)

    The ID of the account

  • campaign (CampaignModel)

    The campaign to bucket the user into

Returns:



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/wingify/services/campaign_decision_service.rb', line 79

def bucket_user_to_variation(context, , campaign)
  return nil if campaign.nil? || context.nil?

  user_id = context.get_id
  bucketing_seed = context.get_bucketing_seed
  bucketing_id = bucketing_seed || user_id

  multiplier = campaign.get_traffic ? 1 : nil
  percent_traffic = campaign.get_traffic
  salt = campaign.get_salt
  bucket_key = salt ? "#{salt}_#{}_#{bucketing_id}" : "#{campaign.get_id}_#{}_#{bucketing_id}"

  hash_value = DecisionMaker.new.generate_hash_value(bucket_key)
  bucket_value = DecisionMaker.new.generate_bucket_value(hash_value, Constants::MAX_TRAFFIC_VALUE, multiplier)

  LoggerService.log(LogLevelEnum::DEBUG, "USER_BUCKET_TO_VARIATION", {
    userId: bucketing_id != user_id ? "#{user_id} (Seed: #{bucketing_id})" : user_id,
    campaignKey: campaign.get_key,
    percentTraffic: percent_traffic,
    bucketValue: bucket_value,
    hashValue: hash_value
  })

  get_variation(campaign.get_variations, bucket_value)
end

#check_in_range(variation, bucket_value) ⇒ VariationModel

Checks if the bucket value is in the range of the variation

Parameters:

  • variation (VariationModel)

    The variation to check

  • bucket_value (Integer)

    The bucket value to check

Returns:

  • (VariationModel)

    The variation if the bucket value is in the range, nil otherwise



70
71
72
# File 'lib/wingify/services/campaign_decision_service.rb', line 70

def check_in_range(variation, bucket_value)
  variation if bucket_value >= variation.get_start_range_variation && bucket_value <= variation.get_end_range_variation
end

#get_pre_segmentation_decision(campaign, context) ⇒ Boolean

Pre-segmentation decision based on user context and campaign rules

Parameters:

Returns:

  • (Boolean)

    True if the user satisfies the campaign rules, false otherwise



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/wingify/services/campaign_decision_service.rb', line 109

def get_pre_segmentation_decision(campaign, context)
  campaign_type = campaign.get_type
  segments = if [CampaignTypeEnum::ROLLOUT, CampaignTypeEnum::PERSONALIZE].include?(campaign_type)
               campaign.get_variations.first.get_segments
             elsif campaign_type == CampaignTypeEnum::AB
               campaign.get_segments
             end

  if DataTypeUtil.is_object(segments) && segments.empty?
    LoggerService.log(LogLevelEnum::INFO, "SEGMENTATION_SKIP", {
      userId: context.get_id,
      campaignKey: campaign.get_type == CampaignTypeEnum::AB ? campaign.get_key : "#{campaign.get_name}_#{campaign.get_rule_key}"
    })
    return true
  else
    pre_segmentation_result = SegmentationManager.instance.validate_segmentation(segments, context.get_custom_variables)

    if !pre_segmentation_result
      LoggerService.log(LogLevelEnum::INFO, "SEGMENTATION_STATUS", {
        userId: context.get_id,
        campaignKey: campaign.get_type == CampaignTypeEnum::AB ? campaign.get_key : "#{campaign.get_name}_#{campaign.get_rule_key}",
        status: 'failed'
      })
      return false
    end

    LoggerService.log(LogLevelEnum::INFO, "SEGMENTATION_STATUS", {
      userId: context.get_id,
      campaignKey: campaign.get_type == CampaignTypeEnum::AB ? campaign.get_key : "#{campaign.get_name}_#{campaign.get_rule_key}",
      status: 'passed'
    })

    return true
  end
end

#get_variation(variations, bucket_value) ⇒ VariationModel

Returns the variation assigned to a user based on bucket value

Parameters:

  • variations (Array<VariationModel>)

    The variations to check

  • bucket_value (Integer)

    The bucket value to check

Returns:



62
63
64
# File 'lib/wingify/services/campaign_decision_service.rb', line 62

def get_variation(variations, bucket_value)
  variations.find { |variation| bucket_value >= variation.get_start_range_variation && bucket_value <= variation.get_end_range_variation }
end

#get_variation_alloted(context, account_id, campaign) ⇒ VariationModel

Determines the variation assigned to a user for a campaign

Parameters:

  • user_id (String)

    The ID of the user

  • account_id (String)

    The ID of the account

  • campaign (CampaignModel)

    The campaign to evaluate

Returns:



150
151
152
153
154
155
156
157
158
159
160
# File 'lib/wingify/services/campaign_decision_service.rb', line 150

def get_variation_alloted(context, , campaign)
  is_user_part = is_user_part_of_campaign(context, campaign)

  if [CampaignTypeEnum::ROLLOUT, CampaignTypeEnum::PERSONALIZE].include?(campaign.get_type)
    return campaign.get_variations.first if is_user_part
  else
    return bucket_user_to_variation(context, , campaign) if is_user_part
  end

  nil
end

#is_user_part_of_campaign(context, campaign) ⇒ Boolean

Determines if a user is part of a campaign based on bucketing logic

Parameters:

  • user_id (String)

    The ID of the user

  • campaign (CampaignModel)

    The campaign to check

Returns:

  • (Boolean)

    True if the user is part of the campaign, false otherwise



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/wingify/services/campaign_decision_service.rb', line 33

def is_user_part_of_campaign(context, campaign)
  return false if campaign.nil? || context.nil?

  user_id = context.get_id
  bucketing_seed = context.get_bucketing_seed
  bucketing_id = bucketing_seed || user_id

  is_rollout_or_personalize = [CampaignTypeEnum::ROLLOUT, CampaignTypeEnum::PERSONALIZE].include?(campaign.get_type)
  salt = is_rollout_or_personalize ? campaign.get_variations.first.get_salt : campaign.get_salt
  traffic_allocation = is_rollout_or_personalize ? campaign.get_variations.first.get_weight : campaign.get_traffic

  bucket_key = salt ? "#{salt}_#{bucketing_id}" : "#{campaign.get_id}_#{bucketing_id}"
  value_assigned_to_user = DecisionMaker.new.get_bucket_value_for_user(bucket_key)

  is_user_part = value_assigned_to_user != 0 && value_assigned_to_user <= traffic_allocation

  LoggerService.log(LogLevelEnum::INFO, "USER_PART_OF_CAMPAIGN", {
    userId: bucketing_id != user_id ? "#{user_id} (Seed: #{bucketing_id})" : user_id,
    notPart: is_user_part ? '' : 'not',
    campaignKey: campaign.get_type == CampaignTypeEnum::AB ? campaign.get_key : "#{campaign.get_name}_#{campaign.get_rule_key}"
  })

  is_user_part
end