Skip to content

Commit

Permalink
feat: personalize support in MEG
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhi591 authored and rohitesh-wingify committed Mar 5, 2025
1 parent a89672f commit 9c0aef8
Show file tree
Hide file tree
Showing 17 changed files with 311 additions and 117 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ jobs:


steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Use java ${{ matrix.java_version }}
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java_version }}
distribution: 'adopt'
Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

[1.3.0] - 2024-11-22

### Added
- Added support for Personalise rules within `Mutually Exclusive Groups`.

[1.2.0] - 2024-06-17

### Fixed
Expand Down Expand Up @@ -69,7 +74,7 @@ Send event properties as third param in trackEvent() call`

@Override
public void set(Map<String, Object> data) throws Exception {
String key = data.get("featureKey") + "_" + data.get("user");
String key = data.get("featureKey") + "_" + data.get("userId");

// Create a map to store the data
Map<String, Object> value = new HashMap<>();
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ limitations under the License. -->

<groupId>com.vwo.sdk</groupId>
<artifactId>vwo-fme-java-sdk</artifactId>
<version>1.2.1</version>
<version>1.3.0</version>
<packaging>jar</packaging>

<name>VWO FME Java SDK</name>
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/vwo/api/GetFlagAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ public static GetFlag getFlag(String featureKey, Settings settings, VWOContext c
if (shouldCheckForExperimentsRules) {
List<Campaign> experimentRulesToEvaluate = new ArrayList<>();
List<Campaign> experimentRules = getAllExperimentRules(feature);
Map<Integer, Integer> megGroupWinnerCampaigns = new HashMap<>();
Map<Integer, String> megGroupWinnerCampaigns = new HashMap<>();

for (Campaign rule : experimentRules) {
// Evaluate the rule here
Expand Down Expand Up @@ -225,7 +225,7 @@ public static GetFlag getFlag(String featureKey, Settings settings, VWOContext c
if (getFlag.isEnabled()){
Map<String, Object> storageMap = new HashMap<>();
storageMap.put("featureKey", feature.getKey());
storageMap.put("user", context.getId());
storageMap.put("userId", context.getId());
storageMap.putAll(passedRulesInformation);
new StorageDecorator().setDataInStorage(storageMap, storageService);
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/vwo/constants/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ public class Constants {
public static final String HTTPS_PROTOCOL = "https";

public static final int RANDOM_ALGO = 1;
public static final String VWO_META_MEG_KEY = "_vwo_meta_meg_";
}
2 changes: 1 addition & 1 deletion src/main/java/com/vwo/decorators/StorageDecorator.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public Map<String, Object> getFeatureFromStorage(String featureKey, VWOContext c
@Override
public Variation setDataInStorage(Map<String, Object> data, StorageService storageService) {
String featureKey = (String) data.get("featureKey");
String userId = data.get("user").toString();
String userId = data.get("userId").toString();

if (featureKey == null || featureKey.isEmpty()) {
LoggerService.log(LogLevelEnum.ERROR, "STORING_DATA_ERROR", new HashMap<String, String>(){
Expand Down
20 changes: 11 additions & 9 deletions src/main/java/com/vwo/models/Groups.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

Expand All @@ -26,15 +28,15 @@ public class Groups {
@JsonProperty("name")
private String name;
@JsonProperty("campaigns")
private List<Integer> campaigns;
private List<String> campaigns;

// this is where algo, priority, weight go
@JsonProperty("et")
private Integer et;
@JsonProperty("p")
private List<Integer> p;
private List<String> p = new ArrayList<>();
@JsonProperty("wt")
private Map<String, Integer> wt;
private Map<String, Double> wt = new HashMap<>();

@JsonProperty("name")
public String getName() {
Expand All @@ -47,12 +49,12 @@ public void setName(String name) {
}

@JsonProperty("campaigns")
public List<Integer> getCampaigns() {
public List<String> getCampaigns() {
return campaigns;
}

@JsonProperty("campaigns")
public void setCampaigns(List<Integer> campaigns) {
public void setCampaigns(List<String> campaigns) {
this.campaigns = campaigns;
}

Expand All @@ -71,22 +73,22 @@ public Integer getEt() {
}

@JsonProperty("p")
public void setP(List<Integer> p) {
public void setP(List<String> p) {
this.p = p;
}

@JsonProperty("p")
public List<Integer> getP() {
public List<String> getP() {
return p;
}

@JsonProperty("wt")
public void setWt(Map<String, Integer> wt) {
public void setWt(Map<String, Double> wt) {
this.wt = wt;
}

@JsonProperty("wt")
public Map<String, Integer> getWt() {
public Map<String, Double> getWt() {
return wt;
}
}
17 changes: 17 additions & 0 deletions src/main/java/com/vwo/models/Variation.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ public class Variation {
@JsonProperty("name")
private String name;

@JsonProperty("ruleKey")
private String ruleKey;

@JsonProperty("type")
private String type;

@JsonProperty("weight")
private double weight;

Expand All @@ -59,6 +65,14 @@ public void setId(Integer id) {
this.id = id;
}

public void setRuleKey(String ruleKey) {
this.ruleKey = ruleKey;
}

public String getRuleKey() {
return ruleKey;
}

public String getKey() {
return key;
}
Expand Down Expand Up @@ -123,4 +137,7 @@ public void setSegments(Map<String, Object> segments) {
this.segments = segments;
}

public String getType() {return type;}

public void setType(String type) {this.type = type;}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public Boolean evaluateCustomVariableDSL(JsonNode dslOperandValue, Map<String, O

// Handle 'inlist' operand
if (operandValue.contains("inlist")) {
Pattern listIdPattern = Pattern.compile("inlist\\((\\w+:\\d+)\\)");
Pattern listIdPattern = Pattern.compile("inlist\\(([^)]+)\\)");
Matcher matcher = listIdPattern.matcher(operandValue);
if (!matcher.find()) {
LoggerService.log(LogLevelEnum.ERROR, "Invalid 'inList' operand format");
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/vwo/services/CampaignDecisionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ public boolean isUserPartOfCampaign(String userId, Campaign campaign) {

LoggerService.log(LogLevelEnum.INFO, "USER_PART_OF_CAMPAIGN", new HashMap<String, String>() {{
put("userId", userId);
put("campaignKey", campaign.getRuleKey());
put("notPart", isUserPart? "" : "not");
put("campaignKey", campaign.getType().equals(CampaignTypeEnum.AB.getValue()) ? campaign.getKey() : campaign.getName() + "_" + campaign.getRuleKey());
}});
return isUserPart;
}
Expand Down Expand Up @@ -137,14 +137,14 @@ public boolean getPreSegmentationDecision(Campaign campaign, VWOContext context)
if (segments.isEmpty()) {
LoggerService.log(LogLevelEnum.INFO, "SEGMENTATION_SKIP", new HashMap<String, String>() {{
put("userId", context.getId());
put("campaignKey", campaign.getRuleKey());
put("campaignKey",campaign.getType().equals(CampaignTypeEnum.AB.getValue()) ? campaign.getKey() : campaign.getName() + "_" + campaign.getRuleKey());
}});
return true;
} else {
boolean preSegmentationResult = SegmentationManager.getInstance().validateSegmentation(segments, (Map<String, Object>) context.getCustomVariables());
LoggerService.log(LogLevelEnum.INFO, "SEGMENTATION_STATUS", new HashMap<String, String>() {{
put("userId", context.getId());
put("campaignKey", campaign.getRuleKey());
put("campaignKey",campaign.getType().equals(CampaignTypeEnum.AB.getValue()) ? campaign.getKey() : campaign.getName() + "_" + campaign.getRuleKey());
put("status", preSegmentationResult ? "passed" : "failed");
}});
return preSegmentationResult;
Expand Down
59 changes: 43 additions & 16 deletions src/main/java/com/vwo/utils/CampaignUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,17 @@ public static void setCampaignAllocation(List<Variation> campaigns) {
* @param campaignId The ID of the campaign to check.
* @return An object containing the group ID and name if the campaign is part of a group, otherwise an empty object.
*/
public static Map<String, String> getGroupDetailsIfCampaignPartOfIt(Settings settings, int campaignId) {
// Check if the campaign is associated with a group and return the group details
public static Map<String, String> getGroupDetailsIfCampaignPartOfIt(Settings settings, int campaignId, int variationId) {
// If variationId is null, that means that campaign is testing campaign
// If variationId is not null, that means that campaign is personalization campaign and we need to append variationId to campaignId using _
// then check if the current campaign is part of any group
Map<String, String> groupDetails = new HashMap<>();
if (settings.getCampaignGroups() != null && settings.getCampaignGroups().containsKey(String.valueOf(campaignId))) {
int groupId = settings.getCampaignGroups().get(String.valueOf(campaignId));
String campaignToCheck = String.valueOf(campaignId);
if (variationId != -1) {
campaignToCheck = campaignToCheck + "_" + variationId;
}
if (settings.getCampaignGroups() != null && settings.getCampaignGroups().containsKey(campaignToCheck)) {
int groupId = settings.getCampaignGroups().get(campaignToCheck);
String groupName = settings.getGroups().get(String.valueOf(groupId)).getName();
groupDetails.put("groupId", String.valueOf(groupId));
groupDetails.put("groupName", groupName);
Expand All @@ -178,22 +184,25 @@ public static Map<String, String> getGroupDetailsIfCampaignPartOfIt(Settings set
* @return An array of groups associated with the feature.
*/
public static List<Map<String, String>> findGroupsFeaturePartOf(Settings settings, String featureKey) {
List<Integer> campaignIds = new ArrayList<>();
// Loop over all rules inside the feature where the feature key matches and collect all campaign IDs
// Initialize an array to store all rules for the given feature to fetch campaignId and variationId later
List<Rule> ruleArrayList = new ArrayList<>();
for (Feature feature : settings.getFeatures()) {
if (feature.getKey().equals(featureKey)) {
feature.getRules().forEach(rule -> {
if (!campaignIds.contains(rule.getCampaignId())) {
campaignIds.add(rule.getCampaignId());
// Add rule to the array if it's not already present
if (!ruleArrayList.contains(rule)) {
ruleArrayList.add(rule);
}
});
}
}

// Loop over all campaigns and find the group for each campaign
// Initialize an array to store all groups associated with the feature
List<Map<String, String>> groups = new ArrayList<>();
for (int campaignId : campaignIds) {
Map<String, String> group = getGroupDetailsIfCampaignPartOfIt(settings, campaignId);
// Iterate over each rule to find the group details
for (Rule rule : ruleArrayList) {
Map<String, String> group = getGroupDetailsIfCampaignPartOfIt(settings, rule.getCampaignId(), rule.getType().equals(CampaignTypeEnum.PERSONALIZE.getValue()) ? rule.getVariationId() : -1);
// Add group to the array if it's not already present
if (!group.isEmpty() && groups.stream().noneMatch(g -> g.get("groupId").equals(group.get("groupId")))) {
groups.add(group);
}
Expand All @@ -207,7 +216,7 @@ public static List<Map<String, String>> findGroupsFeaturePartOf(Settings setting
* @param groupId The ID of the group.
* @return An array of campaigns associated with the specified group ID.
*/
public static List<Integer> getCampaignsByGroupId(Settings settings, int groupId) {
public static List<String> getCampaignsByGroupId(Settings settings, int groupId) {
// find the group
Groups group = settings.getGroups().get(String.valueOf(groupId));
return group.getCampaigns();
Expand All @@ -216,16 +225,34 @@ public static List<Integer> getCampaignsByGroupId(Settings settings, int groupId
/**
* Retrieves feature keys from a list of campaign IDs.
* @param settings The settings model containing all features.
* @param campaignIds An array of campaign IDs.
* @param campaignIdWithVariation An array of campaign IDs.
* @return An array of feature keys associated with the provided campaign IDs.
*/
public static List<String> getFeatureKeysFromCampaignIds(Settings settings, List<Integer> campaignIds) {
public static List<String> getFeatureKeysFromCampaignIds(Settings settings, List<String> campaignIdWithVariation) {
List<String> featureKeys = new ArrayList<>();
for (int campaignId : campaignIds) {
for (String campaign : campaignIdWithVariation) {
// split key with _ to separate campaignId and variationId
String[] campaignIdVariationId = campaign.split("_");
int campaignId = Integer.parseInt(campaignIdVariationId[0]);
Integer variationId = (campaignIdVariationId.length > 1) ? Integer.parseInt(campaignIdVariationId[1]) : null;
// Iterate over each feature to find the feature key
for (Feature feature : settings.getFeatures()) {
// Break if feature key is already added
if (featureKeys.contains(feature.getKey())) {
continue;
}
feature.getRules().forEach(rule -> {
if (rule.getCampaignId() == campaignId) {
featureKeys.add(feature.getKey());
// Check if variationId is provided and matches the rule's variationId
if (variationId != null) {
// Add feature key if variationId matches
if (Objects.equals(rule.getVariationId(), variationId)) {
featureKeys.add(feature.getKey());
}
} else {
// Add feature key if no variationId is provided
featureKeys.add(feature.getKey());
}
}
});
}
Expand Down
Loading

0 comments on commit 9c0aef8

Please sign in to comment.