This is an automated email from the ASF dual-hosted git repository. shuber pushed a commit to branch UNOMI-537-fix-control-groups in repository https://gitbox.apache.org/repos/asf/unomi.git
commit 3ce4cce48ebbe7a00041c57f6aed237874821d75 Author: Serge Huber <[email protected]> AuthorDate: Thu Feb 3 18:07:21 2022 +0100 - Change percentage to 0-100 base - Fix problem with class cast exceptions --- .../resources/personalization-controlgroup.json | 2 +- .../src/main/asciidoc/samples/twitter-sample.adoc | 4 ++-- .../impl/personalization/ControlGroup.java | 23 ++++++++++++++++++++ .../PersonalizationServiceImpl.java | 25 ++++++++++++++++------ 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/itests/src/test/resources/personalization-controlgroup.json b/itests/src/test/resources/personalization-controlgroup.json index 8a931d7..8b8c74f 100644 --- a/itests/src/test/resources/personalization-controlgroup.json +++ b/itests/src/test/resources/personalization-controlgroup.json @@ -22,7 +22,7 @@ "strategyOptions": { "threshold": -1, "controlGroup" : { - "percentage" : 1.0, + "percentage" : 100.0, "displayName" : "First perso", "path" : "/home/perso1.html", "storeInSession" : ###storeInSession### diff --git a/manual/src/main/asciidoc/samples/twitter-sample.adoc b/manual/src/main/asciidoc/samples/twitter-sample.adoc index 7d7eeba..861708e 100644 --- a/manual/src/main/asciidoc/samples/twitter-sample.adoc +++ b/manual/src/main/asciidoc/samples/twitter-sample.adoc @@ -298,7 +298,7 @@ curl --location --request POST 'http://localhost:8181/context.json' \ "strategyOptions": { "fallback": "var2", "controlGroup" : { - "percentage" : 0.1, + "percentage" : 10.0, "displayName" : "Gender test control group", "path" : "/gender-test", "storeInSession" : true @@ -339,7 +339,7 @@ curl --location --request POST 'http://localhost:8181/context.json' \ In the above example, we basically setup two variants : `var1` and `var2` and setup the `var2` to be the fallback variant in case no variant is matched. We could of course specify more than a variant. The `strategy` indicates to the -personalization service how to calculate the "winning" variant. In this case the strategy `matching-first` will return variants that match the current profile. We also use the `controlGroups` option to specify that we want to have a control group for this personalization. The `0.1` percentage value represents 10% (0 to 1) of traffic that will be assigned randomly to the control group. The control group will be stored in the profile and the session of the visitors if they were assigned to [...] +personalization service how to calculate the "winning" variant. In this case the strategy `matching-first` will return variants that match the current profile. We also use the `controlGroups` option to specify that we want to have a control group for this personalization. The `10.0` percentage value represents 10% (0.0 to 100.0) of traffic that will be assigned randomly to the control group. The control group will be stored in the profile and the session of the visitors if they were assi [...] Currently the following strategies are available: diff --git a/services/src/main/java/org/apache/unomi/services/impl/personalization/ControlGroup.java b/services/src/main/java/org/apache/unomi/services/impl/personalization/ControlGroup.java index d6435d0..01cbd9c 100644 --- a/services/src/main/java/org/apache/unomi/services/impl/personalization/ControlGroup.java +++ b/services/src/main/java/org/apache/unomi/services/impl/personalization/ControlGroup.java @@ -16,12 +16,21 @@ */ package org.apache.unomi.services.impl.personalization; +import org.apache.unomi.persistence.spi.CustomObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.ParseException; import java.util.Date; +import java.util.Map; /** * Represents a personalization control group, stored in a profile and/or a session */ public class ControlGroup { + + private static final Logger logger = LoggerFactory.getLogger(ControlGroup.class.getName()); + String id; String displayName; String path; @@ -34,6 +43,20 @@ public class ControlGroup { this.timeStamp = timeStamp; } + public static ControlGroup fromMap(Map<String,Object> map) { + String id = (String) map.get("id"); + String displayName = (String) map.get("displayName"); + String path = (String) map.get("path"); + String dateStr = (String) map.get("timeStamp"); + Date date = null; + try { + date = CustomObjectMapper.getObjectMapper().getDateFormat().parse(dateStr); + } catch (ParseException e) { + logger.error("Error parsing control group date", e); + } + return new ControlGroup(id, displayName, path, date); + } + public String getId() { return id; } diff --git a/services/src/main/java/org/apache/unomi/services/impl/personalization/PersonalizationServiceImpl.java b/services/src/main/java/org/apache/unomi/services/impl/personalization/PersonalizationServiceImpl.java index eeef772..b5b24cd 100644 --- a/services/src/main/java/org/apache/unomi/services/impl/personalization/PersonalizationServiceImpl.java +++ b/services/src/main/java/org/apache/unomi/services/impl/personalization/PersonalizationServiceImpl.java @@ -34,6 +34,7 @@ import java.util.stream.Collectors; public class PersonalizationServiceImpl implements PersonalizationService { + public static final String CONTROL_GROUPS_PROPERTY_NAME = "unomiControlGroups"; private BundleContext bundleContext; private ProfileService profileService; @@ -103,9 +104,13 @@ public class PersonalizationServiceImpl implements PersonalizationService { List<ControlGroup> controlGroups = null; if (storeInSession) { - controlGroups = (List<ControlGroup>) session.getProperty("unomiControlGroups"); + if (session.getProperty(CONTROL_GROUPS_PROPERTY_NAME) != null) { + controlGroups = ((List<Map<String, Object>>) session.getProperty(CONTROL_GROUPS_PROPERTY_NAME)).stream().map(ControlGroup::fromMap).collect(Collectors.toList()); + } } else { - controlGroups = (List<ControlGroup>) profile.getProperty("unomiControlGroups"); + if (profile.getProperty(CONTROL_GROUPS_PROPERTY_NAME) != null) { + controlGroups = ((List<Map<String, Object>>) profile.getProperty(CONTROL_GROUPS_PROPERTY_NAME)).stream().map(ControlGroup::fromMap).collect(Collectors.toList()); + } } if (controlGroups == null) { controlGroups = new ArrayList<>(); @@ -115,8 +120,16 @@ public class PersonalizationServiceImpl implements PersonalizationService { // we already have an entry for this personalization so this means the profile is in the control group profileInControlGroup = true; } else { - double randomDouble = controlGroupRandom.nextDouble(); - Double controlGroupPercentage = (Double) controlGroupMap.get("percentage"); + double randomDouble = controlGroupRandom.nextDouble() * 100.0; + Object percentageObject = controlGroupMap.get("percentage"); + Double controlGroupPercentage = null; + if (percentageObject != null) { + if (percentageObject instanceof Double) { + controlGroupPercentage = (Double) percentageObject; + } else if (percentageObject instanceof Integer) { + controlGroupPercentage = ((Integer) percentageObject).doubleValue(); + } + } if (randomDouble <= controlGroupPercentage) { // Profile is elected to be in control group @@ -127,10 +140,10 @@ public class PersonalizationServiceImpl implements PersonalizationService { new Date()); controlGroups.add(controlGroup); if (storeInSession) { - session.setProperty("unomiControlGroups", controlGroups); + session.setProperty(CONTROL_GROUPS_PROPERTY_NAME, controlGroups); changeType = EventService.SESSION_UPDATED; } else { - profile.setProperty("unomiControlGroups", controlGroups); + profile.setProperty(CONTROL_GROUPS_PROPERTY_NAME, controlGroups); changeType = EventService.PROFILE_UPDATED; } }
