You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by sh...@apache.org on 2022/02/03 17:07:33 UTC

[unomi] branch UNOMI-537-fix-control-groups created (now 3ce4cce)

This is an automated email from the ASF dual-hosted git repository.

shuber pushed a change to branch UNOMI-537-fix-control-groups
in repository https://gitbox.apache.org/repos/asf/unomi.git.


      at 3ce4cce  - Change percentage to 0-100 base - Fix problem with class cast exceptions

This branch includes the following new commits:

     new 3ce4cce  - Change percentage to 0-100 base - Fix problem with class cast exceptions

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[unomi] 01/01: - Change percentage to 0-100 base - Fix problem with class cast exceptions

Posted by sh...@apache.org.
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 <sh...@jahia.com>
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;
                         }
                     }