Sitecore Live Tracking and component personalization with Goals and Profiles but without out Session end and out of the box Rules

We have requirement to load component with different content based on user behavior, At first we thought of using out of box sitecore component personalization rule, but we see there a problem with this approach

Problem:
  • Content author has to create different personalized datasource content for each and every scenario
  • we have multiple category and sub categories this will end up creating a lot of personalization content
Solution:
  • Try to programmatically get the live and past Goals and profiles triggered by the contact 
  • Based on these these dynamically suggest the content to component instead of rule.
  • Do not wait to get the goals and profile till session end
  • Combine the score/points of each category's with individual sub category scores
  • Once the scores of the subcategory obtained then  rank the score of each subcategory and display content belong to that sub category in the dedicated Category Slide
  • Create content for category and sub category item but not like creating multiple datasource component item like before with all the combinations.
Requirement:

We need to load a carousel with different content on each slide based on user behavior, We have many categories, and each category will have many subcategory.
Carousel slide should display default category content if no user behavior captured, if user behavior captured based on the triggered goals and profiles different sub category content to be displayed in the slides.

Code to get the Goals and Profiles from ongoing event and past events

   public class ProfileScoreAndPointsList

    {

        public ID ProfileID { get; set; }

        public string ProfileKeyName { get; set; }

        public double ProfileKeyScore { get; set; }

    } 


public List<ProfileScoreAndPointsList> GetCombinedGoals()
        {
            return GetCombinedTrackerGoals();
        }
//Combining history and live goals points
        public List<ProfileScoreAndPointsList> GetCombinedTrackerGoals()
        {
 
            List<ProfileScoreAndPointsList> profileScoreLists = new List<ProfileScoreAndPointsList>();
            List<ProfileScoreAndPointsList> historyGoals = GetTrackerContactTriggeredHistoryGoals();
            List<ProfileScoreAndPointsList> currentGoals = new List<ProfileScoreAndPointsList>();
            if (Tracker.Current?.Session?.Interaction != null)
            {
                currentGoals = GetCurrentTrackerTriggeredGoals(Tracker.Current.Session.Interaction);
            }
            IEnumerable<ProfileScoreAndPointsList> result = historyGoals.Concat(currentGoals);
            if (result != null)
            {
                profileScoreLists = result.GroupBy(l => l.ProfileKeyName)
                .Select(cl => new ProfileScoreAndPointsList
                {
                    ProfileID = cl.Select(x => x.ProfileID).FirstOrDefault(),
                    ProfileKeyName = cl.Select(x => x.ProfileKeyName).FirstOrDefault(),
                    ProfileKeyScore = cl.Sum(c => c.ProfileKeyScore)
                }).OrderByDescending(p => p.ProfileKeyScore).ToList();
 
            }
 
            return profileScoreLists;
        }

//Getting Live Tracker Goals
protected List<ProfileScoreAndPointsList> GetCurrentTrackerTriggeredGoals(IInteractionData interaction)
        {
            List<ProfileScoreAndPointsList> profileScoreLists = new List<ProfileScoreAndPointsList>();
            if (interaction != null)
            {
                IEnumerable<PageEventData> events = interaction.Pages.SelectMany<Page, PageEventData>((p, v) => p.PageEvents).Select(d => d);
                profileScoreLists = events.Where(p => p.IsGoal).GroupBy(l => l.PageEventDefinitionId)
                 .Select(goal => new ProfileScoreAndPointsList
                 {
                     ProfileID = ID.Parse(goal.Select(x => x.PageEventDefinitionId).FirstOrDefault()),
                     ProfileKeyName = goal.Select(x => x.Name).FirstOrDefault(),
                     ProfileKeyScore = goal.Sum(c => c.Value)
                 }).ToList();
            }
            return profileScoreLists;
        }
//Getting the past Goals triggered by the contact
        public List<ProfileScoreAndPointsList> GetTrackerContactTriggeredHistoryGoals()
        {
            List<ProfileScoreAndPointsList> profileScoreLists = new List<ProfileScoreAndPointsList>();
            if (!Sitecore.Analytics.Tracker.IsActive)
            {
                Sitecore.Analytics.Tracker.StartTracking();
            }
            if (Tracker.Current != null && Tracker.Current.Contact != null && Tracker.Current.Contact.KeyBehaviorCache != null &&
                Tracker.Current.Contact.KeyBehaviorCache.Goals != null)
            {
                IReadOnlyCollection<Sitecore.Analytics.Tracking.KeyBehaviorCacheEntry> goals = Tracker.Current.Contact.KeyBehaviorCache.Goals;
                foreach (Sitecore.Analytics.Tracking.KeyBehaviorCacheEntry goal in goals)
                {
                    Sitecore.Data.Items.Item goalItem = Sitecore.Context.Database.GetItem(Sitecore.Data.ID.Parse(goal.Id));
                    if (goalItem != null)
                    {
                        string name = goalItem.Name;
                        ProfileScoreAndPointsList profileItem = profileScoreLists.FirstOrDefault(p => p.ProfileKeyName.Equals(name));
                        if (profileItem != null)
                        {
                            //Combining the engagement points
                            double engamentvalue = profileItem.ProfileKeyScore;
                            int index = profileScoreLists.IndexOf(profileItem);
                            ProfileScoreAndPointsList profileDetail = profileScoreLists.ElementAt(index);
                            profileDetail.ProfileID = goalItem.ID;
                            profileDetail.ProfileKeyName = name;
                            profileDetail.ProfileKeyScore = engamentvalue + System.Convert.ToDouble(goalItem["Points"]);
 
                        }
                        else
                        {
                            profileScoreLists.Add(new ProfileScoreAndPointsList()
                            {
                                ProfileID = goalItem.ID,
                                ProfileKeyName = name,
                                ProfileKeyScore = System.Convert.ToDouble(goalItem["Points"])
                            });
 
                        }
                    }
                }
 
            }
            return profileScoreLists;
        }


//Getting profiles triggered live and history 

public Dictionary<ID, List<ProfileScoreAndPointsList>> GetCombinedProfiles()
        {
 
            Dictionary<ID, List<ProfileScoreAndPointsList>> allcategory = new Dictionary<ID, List<ProfileScoreAndPointsList>>();
            List<ProfileScoreAndPointsList> profileScoreLists = new List<ProfileScoreAndPointsList>();
            Dictionary<ID, List<ProfileScoreAndPointsList>> trackerProfiles = GetTrackerInteractionProfiles();
            Dictionary<ID, List<ProfileScoreAndPointsList>> xconnectProfiles = GetXconnectProfiles();
            Dictionary<ID, List<ProfileScoreAndPointsList>> combinedData = new Dictionary<ID, List<ProfileScoreAndPointsList>>();
            combinedData = combinedData.Union(trackerProfiles).ToDictionary(i => i.Key, i => i.Value);
            foreach (KeyValuePair<ID, List<ProfileScoreAndPointsList>> kvp in xconnectProfiles)
            {
                if (!combinedData.TryGetValue(kvp.Key, out List<ProfileScoreAndPointsList> value))
                {
                    combinedData.Add(kvp.Key, kvp.Value);
                }
                else
                {
                    List<ProfileScoreAndPointsList> data = combinedData[kvp.Key];
                    data.AddRange(kvp.Value);
                    combinedData[kvp.Key] = data;
                }
            }
            foreach (KeyValuePair<ID, List<ProfileScoreAndPointsList>> item in combinedData)
            {
                profileScoreLists = new List<ProfileScoreAndPointsList>();
                IEnumerable<ProfileScoreAndPointsList> result = item.Value;
                if (result != null)
                {
                    profileScoreLists = result.GroupBy(l => l.ProfileID)
                    .Select(pl => new ProfileScoreAndPointsList
                    {
                        ProfileID = pl.Select(x => x.ProfileID).FirstOrDefault(),
                        ProfileKeyName = pl.Select(x => x.ProfileKeyName).FirstOrDefault(),
                        ProfileKeyScore = pl.Sum(c => c.ProfileKeyScore)
                    }).OrderByDescending(p => p.ProfileKeyScore).ToList();
                    allcategory.Add(item.Key, profileScoreLists);
                }
            }
 
            return allcategory;
        }

//Getting Live Tracker Profiles
public Dictionary<ID, List<ProfileScoreAndPointsList>> GetTrackerInteractionProfiles()
        {
            Dictionary<ID, List<ProfileScoreAndPointsList>> allcategoryProfiles = new Dictionary<ID, List<ProfileScoreAndPointsList>>();
            if (Tracker.Current?.Interaction != null)
            {
                VisitData visitdata = Tracker.Current.Interaction.ToVisitData();
                Dictionary<string, ProfileData> data = visitdata.Profiles;
                if (data.Any())
                {
                    foreach (KeyValuePair<string, ProfileData> profileCollections in data)
                    {
                        List<ProfileScoreAndPointsList> profileScoreLists = new List<ProfileScoreAndPointsList>();
                        //Profiles example:(Sector)
                        ProfileData value = profileCollections.Value;
                        Dictionary<string, double> profileKeyValue = value.Values;
                        foreach (KeyValuePair<string, double> profile in profileKeyValue)
                        {
                            //Profile Keys example:(Aviation)
                            string query = $"{SitecoreSettings.ProfilePath}/descendant::*[@@key ='{profile.Key.ToLower()}']";
                            Item profileKeyItem = Sitecore.Context.Database.SelectSingleItem(query);
                            if (profileKeyItem != null)
                            {
                                string profilename = profile.Key;
                                double profilevalue = profile.Value;
                                if (!string.IsNullOrEmpty(profilename))
                                {
                                    //If already have the Profile key data then appened Score
                                    ProfileScoreAndPointsList profileItem = profileScoreLists.FirstOrDefault(p => p.ProfileKeyName.Equals(profile.Key));
                                    if (profileItem != null)
                                    {
                                        //Getting the profile max score for contact's profile from latest interaction
                                        int index = profileScoreLists.IndexOf(profileItem);
                                        ProfileScoreAndPointsList profileDetail = profileScoreLists.ElementAt(index);
                                        profileDetail.ProfileKeyName = profilename;
                                        profileDetail.ProfileKeyScore = profileItem.ProfileKeyScore + profilevalue;
                                        profileDetail.ProfileID = profileKeyItem.ID;
                                    }
                                    else
                                    {
                                        //First time add the Score for the profile key
                                        profileScoreLists.Add(new ProfileScoreAndPointsList()
                                        {
                                            //ProfileID = ID.Parse(profile.Key),
                                            ProfileKeyScore = profilevalue,
                                            ProfileKeyName = profilename,
                                            ProfileID = profileKeyItem.ID
                                        });
 
                                    }
                                }
                            }
                        }
                        string queryProfile = $"{ SitecoreSettings.ProfilePath}/descendant::*[@@key ='{profileCollections.Key.ToLower()}']";
                        Item profileRootItem = Sitecore.Context.Database.SelectSingleItem(queryProfile);
                        if (profileRootItem != null)
                        {
                            allcategoryProfiles.Add(profileRootItem.ID, profileScoreLists);
                        }
                    }
                }
            }
            return allcategoryProfiles;
        }

//Getting History Profiles of the contact

public Dictionary<ID, List<ProfileScoreAndPointsList>> GetXconnectProfiles()
        {
            Dictionary<ID, List<ProfileScoreAndPointsList>> allcategory = new Dictionary<ID, List<ProfileScoreAndPointsList>>();
            List<ProfileScoreAndPointsList> profileScoreLists = new List<ProfileScoreAndPointsList>();
            IXConnectFacets xConnectFacets = Sitecore.Analytics.Tracker.Current.Session.Contact.GetFacet<IXConnectFacets>("XConnectFacets");
            if (xConnectFacets == null)
            {
                return allcategory;
            }
            if (xConnectFacets.Facets == null)
            {
                return allcategory;
            }
            if (!xConnectFacets.Facets.ContainsKey(ContactBehaviorProfile.DefaultFacetKey))
            {
                return allcategory;
            }
 
            ContactBehaviorProfile cbp = xConnectFacets.Facets[ContactBehaviorProfile.DefaultFacetKey] as ContactBehaviorProfile;
            Dictionary<Guid, ProfileScore> matchedProfileIDs = cbp.Scores;
            ProfileScore FiteredProfiles = null;
            if (matchedProfileIDs != null)
            {
                foreach (KeyValuePair<Guid, ProfileScore> item in matchedProfileIDs)
                {
 
                    //Getting ProfileKeys based on Profile parameter
                    Item ProfileItem = Sitecore.Context.Database.GetItem(ID.Parse(item.Key));
                    if (ProfileItem != null)
                    {
                        profileScoreLists = new List<ProfileScoreAndPointsList>();
                        FiteredProfiles = item.Value;
                        foreach (KeyValuePair<Guid, double> profile in FiteredProfiles.Values)
                        {
                            string profilename = Sitecore.Context.Database.GetItem(ID.Parse(profile.Key))?.Name;
                            double profilevalue = profile.Value;
                            if (!string.IsNullOrEmpty(profilename))
                            {
                                //If already have the Profile key data then appened Score
                                ProfileScoreAndPointsList profileItem = profileScoreLists.FirstOrDefault(p => p.ProfileID.Equals(ID.Parse(profile.Key)));
                                if (profileItem != null)
                                {
                                    //Getting the profile max score for contact's profile from latest interaction
                                    int index = profileScoreLists.IndexOf(profileItem);
                                    ProfileScoreAndPointsList profileDetail = profileScoreLists.ElementAt(index);
                                    profileDetail.ProfileID = ID.Parse(profile.Key);
                                    profileDetail.ProfileKeyName = profilename;
                                    profileDetail.ProfileKeyScore = profileItem.ProfileKeyScore + profilevalue;
                                }
                                else
                                {
                                    //First time add the Score for the profile key
                                    profileScoreLists.Add(new ProfileScoreAndPointsList()
                                    {
                                        ProfileID = ID.Parse(profile.Key),
                                        ProfileKeyScore = profilevalue,
                                        ProfileKeyName = profilename
                                    });
 
                                }
                            }
                        }
                        allcategory.Add(ProfileItem.ID, profileScoreLists);
                    }
                }
            }
            return allcategory;
        }

//Combining profiles and goals based on profile category with subcategory
public Dictionary<ID, List<ProfileScoreAndPointsList>> GetCombinedProfileAndGoalsOfContact(List<ProfileScoreAndPointsList> goals)
        {
            Dictionary<ID, List<ProfileScoreAndPointsList>> profiles = GetCombinedProfiles();
            Dictionary<ID, List<ProfileScoreAndPointsList>> allcategory = new Dictionary<ID, List<ProfileScoreAndPointsList>>();
            List<ProfileScoreAndPointsList> profileScoreLists = new List<ProfileScoreAndPointsList>();
            Dictionary<ID, List<ProfileScoreAndPointsList>> allgoals = new Dictionary<ID, List<ProfileScoreAndPointsList>>();
            //If no profiles available for the contact then check goals available for the contact
            //and add the goal to list for personalization (goal name is the combination of Profilename_Action)
            //goal name example : Aviavation_CTA  (Aviation is profile , CTA is action)
 
            foreach (ProfileScoreAndPointsList item in goals)
            {
                //extracting profile from the goal name
                string[] splitGoal = item.ProfileKeyName.Split('_');
                if (splitGoal != null && splitGoal.Any())
                {
                    string profilename = splitGoal[0];
                    string query = $"{SitecoreSettings.ProfilePath}/descendant::*[@@key ='{profilename.ToLower()}']";
                    Item profileKeyItem = Sitecore.Context.Database.SelectSingleItem(query);
                    if (profileKeyItem != null)
                    {
                        ProfileScoreAndPointsList data = new ProfileScoreAndPointsList()
                        {
                            ProfileID = profileKeyItem.ID,
                            ProfileKeyName = profileKeyItem.Name,
                            ProfileKeyScore = item.ProfileKeyScore
                        };
                        if (!allgoals.ContainsKey(profileKeyItem.Parent.ID))
                        {
                            allgoals.Add(profileKeyItem.Parent.ID, new List<ProfileScoreAndPointsList>() { data });
                        }
                        else
                        {
                            List<ProfileScoreAndPointsList> existingdata = allgoals[profileKeyItem.Parent.ID];
                            existingdata.Add(data);
                            allgoals[profileKeyItem.Parent.ID] = existingdata.OrderByDescending(s => s.ProfileKeyScore).ToList();
                        }
                    }
                }
            }
            if (!profiles.Any())
            {
                return allgoals;
            }
            else
            {
                Dictionary<ID, List<ProfileScoreAndPointsList>> combinedData = new Dictionary<ID, List<ProfileScoreAndPointsList>>();
                combinedData = combinedData.Union(allgoals).ToDictionary(i => i.Key, i => i.Value);
                foreach (KeyValuePair<ID, List<ProfileScoreAndPointsList>> kvp in profiles)
                {
                    if (!combinedData.TryGetValue(kvp.Key, out List<ProfileScoreAndPointsList> value))
                    {
                        combinedData.Add(kvp.Key, kvp.Value);
                    }
                    else
                    {
                        List<ProfileScoreAndPointsList> data = combinedData[kvp.Key];
                        data.AddRange(kvp.Value);
                        combinedData[kvp.Key] = data;
                    }
                }
                foreach (KeyValuePair<ID, List<ProfileScoreAndPointsList>> profileData in combinedData)
                {
                    profileScoreLists = new List<ProfileScoreAndPointsList>();
                    //if Both goals and profile available
                    IEnumerable<ProfileScoreAndPointsList> result = profileData.Value;
                    if (result != null)
                    {
                        profileScoreLists = result.GroupBy(l => l.ProfileID)
                         .Select(cl => new ProfileScoreAndPointsList
                         {
                             ProfileID = cl.Select(x => x.ProfileID).FirstOrDefault(),
                             ProfileKeyName = cl.Select(x => x.ProfileKeyName).FirstOrDefault(),
                             ProfileKeyScore = cl.Sum(c => c.ProfileKeyScore)
                         }).ToList();
                        allcategory.Add(profileData.Key, profileScoreLists.OrderByDescending(s => s.ProfileKeyScore).ToList());
                    }
                }
                return allcategory;
            }
        }

Once we captured scores based on sub categoried of each category then load the content relavent to it.

Comments

Popular posts from this blog

Custom Item Url and resolving the item in Sitecore - Buckets

Fixing Sitecore Buckets folder path - Items created after 12 AM server time zone

Sitecore Search - API Crawler with Edge Pagination