Thursday, January 28, 2016

Sitecore DMS tracker

using HA.ApplicationLogic.Session;
using HA.Web.Analytics.Tracking.Base;
using HA.Web.Analytics.Tracking.Models;
using HA.Web.ViewModels.MyAccount.UserProfile;
using Newtonsoft.Json;
using Sitecore.Analytics;
using Sitecore.Analytics.Automation;
using Sitecore.Analytics.Data;
using Sitecore.Analytics.Data.DataAccess;
using Sitecore.Analytics.Data.DataAccess.DataSets;
using Sitecore.Analytics.Web;
using Sitecore.Configuration;
using Sitecore.Diagnostics;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using HA.Web.Analytics.Tracking.Enums;
using Sitecore.Analytics.Data.Items;
using Sitecore.Analytics.Testing;
using Sitecore.Data.Items;

namespace HA.Web.Analytics.Tracking.Trackers
{
    /// <summary>
    /// A DMS specific implementation of the tracker base
    /// </summary>
    public sealed class SitecoreDmsTracker : AbstractTrackerBase
    {
        public const string _VisitorTestTagName = "MULTIVARIATE_TEST_NAME";
        public const string _VisitorTestCookieName = "VisitorMultivariateTestCookie";

        /// <summary>
        /// Determines if the current visitor has a specific pattern card
        /// </summary>
        /// <param name="profileName">The name of the profile grouping</param>
        /// <param name="patternLabel">The name of the pattern label to match</param>
        /// <param name="utcCutoffDateTime">An optional cuttoff date to exclude old data</param>
        /// <returns>true if the visitor has a pattern card</returns>
        public override bool VisitorHasPatternCard(string profileName, string patternLabel, DateTime? utcCutoffDateTime)
        {
            // Build a list of profiles that occured for the filtered list of visits
            var visitProfiles = new List<VisitorDataSet.ProfilesRow>();

            // Include profiles from the current visit (which may not be committed to DB yet)
            var currentVisitProfiles = Tracker.CurrentVisit.Profiles
                .Where(p => p.ProfileName == profileName)
                .Where(p => p.VisitId == Tracker.CurrentVisit.VisitId);
            visitProfiles.AddRange(currentVisitProfiles);

            // Check for matches in the current visit first, before reaching out to DMS
            if (visitProfiles.Any(row => row.PatternLabel.Equals(patternLabel, StringComparison.InvariantCultureIgnoreCase)))
            {
                return true;
            }

            // Load all visits and profile data for visitor
            string sessionKey = String.Format("HA.Web.Analytics.LoadedVisitorData-{0}-{1}", Tracker.Visitor.VisitorId, Tracker.CurrentVisit.VisitId);
            if (SessionManager.Instance.GetSession<bool?>(sessionKey) == null)
            {
                Tracker.Visitor.LoadAll(VisitLoadOptions.Visits | VisitLoadOptions.Profiles, VisitorOptions.None);
                SessionManager.Instance.SetSession(sessionKey, true);
            }

            // Get applicable profiles
            var profiles = Tracker.Visitor.DataSet.Profiles
                .Where(p => p.ProfileName == profileName)
                .Where(p => p.VisitorId == Tracker.Visitor.VisitorId)
                .ToList();

            // Get all visits
            var visits = Tracker.Visitor.DataSet.Visits.Where(v => v.VisitorId == Tracker.Visitor.VisitorId);

            // Filter out older visits if cutoff date was specified
            if (utcCutoffDateTime.HasValue)
            {
                visits = visits.Where(v => v.StartDateTime > utcCutoffDateTime.Value);
            }

            foreach (var v in visits.OrderBy(v => v.StartDateTime))
            {
                var visit = v;
                var matchingProfiles = profiles.Where(p => p.VisitId == visit.VisitId);
                visitProfiles.AddRange(matchingProfiles);
            }

            // Determine if there is a matching pattern card
            var match = visitProfiles.FirstOrDefault(row => row.PatternLabel.Equals(patternLabel, StringComparison.InvariantCultureIgnoreCase));
            return match != null;
        }

        /// <summary>
        /// Registers a "Page Event" (such as a Goal) on the current or previous page.
        /// </summary>
        /// <param name="pageEvent">The page event to register</param>
        /// <returns>true if the page event was successfully registered</returns>
        protected override void RegisterPageEvent(PageEvent pageEvent)
        {
            Assert.IsNotNull(pageEvent, "pageEvent must not be null");

            // Ensure analytics is enabled
            if (!IsAnalyticsEnabled())
            {
                Log.Warn("Cannot register page event when Analytics is disabled: " + JsonConvert.SerializeObject(pageEvent), this);
                return;
            }

            // Ensure the page event is valid before attempting to register it
            if (!ValidatePageEvent(pageEvent))
            {
                Log.Warn("Invalid page event. Cannot register. " + JsonConvert.SerializeObject(pageEvent), this);
                return;
            }

            // Resolve the page, whether current or previous
            // Get page by ID, or the most recent that's not null or empty
            List<VisitorDataSet.PagesRow> everyPage = Tracker.CurrentVisit.GetPages().ToList();
            VisitorDataSet.PagesRow page = GetPagesRow(everyPage, pageEvent.PageId);

            // If a valid page wasn't found, abort
            if (page == null)
            {
                var parts = new object[]
                {
                    "A valid page was not found for this visit. Cannot register page event.",
                    Tracker.CurrentVisit.VisitId,
                    JsonConvert.SerializeObject(pageEvent)
                };

                Log.Warn(String.Join(", ", parts), this);
                return;
            }

            // Finally, populate the PageEventData and register it with the page
            var pageEventData = new PageEventData(pageEvent.Name)
            {
                Text = pageEvent.Text,
                Data = pageEvent.Data,
                DataKey = pageEvent.DataKey
            };

            try
            {
                page.Register(pageEventData);
            }
            catch (Exception ex)
            {
                Log.Error("Could not register PageEvent via SitecoreDmsTracker: " + JsonConvert.SerializeObject(pageEvent), ex, this);
            }
        }

        /// <summary>
        /// Adds an acquaintance tag for the current visitor
        /// </summary>
        /// <param name="acquaintance">The guid to add</param>
        /// <returns>true if the tag was successfully added</returns>
        public override bool AddVisitorTag(Guid acquaintance)
        {
            // Ensure analytics is enabled
            if (!IsAnalyticsEnabled())
            {
                Log.Warn("Cannot submit visitor tag when Analytics is disabled: " + acquaintance, this);
                return false;
            }

            // Ensure the visitor tag is valid before attempting to register it
            if (acquaintance == Guid.Empty)
            {
                Log.Warn("Acquaintance guid cannot be null or empty. " + acquaintance, this);
                return false;
            }

            try
            {
                Tracker.Visitor.Tags.Add(acquaintance);
                return true;
            }
            catch (Exception ex)
            {
                var parts = new object[]
                {
                    "Error adding tag for visitor.",
                    Tracker.Visitor.VisitorId,
                    acquaintance
                };

                Log.Error(String.Join(", ", parts), ex, this);
                return false;
            }
        }

        /// <summary>
        /// Sets an existing tag (matched by name), or adds a new one if it doesn't exist
        /// </summary>
        /// <param name="visitorTag">The tag to insert / update</param>
        /// <returns>true if the operation was successful</returns>
        public override bool SetVisitorTag(VisitorTag visitorTag)
        {
            // Ensure analytics is enabled
            if (!IsAnalyticsEnabled())
            {
                Log.Warn("Cannot set visitor tag when Analytics is disabled: " + JsonConvert.SerializeObject(visitorTag), this);
                return false;
            }

            // Ensure the visitor tag is valid before attempting to register it
            if (String.IsNullOrWhiteSpace(visitorTag.TagName))
            {
                Log.Warn("Tag name cannot be null or empty. " + JsonConvert.SerializeObject(visitorTag), this);
                return false;
            }

            try
            {
                Tracker.Visitor.Tags.Set(visitorTag.TagName, visitorTag.TagValue);
                return true;
            }
            catch (Exception ex)
            {
                var parts = new object[]
                {
                    "Error setting tag for visitor.",
                    Tracker.Visitor.VisitorId,
                    JsonConvert.SerializeObject(visitorTag)
                };

                Log.Error(String.Join(", ", parts), ex, this);
                return false;
            }
        }

        /// <summary>
        /// Gets the visitor's GeoIP state/region
        /// </summary>
        /// <returns></returns>
        public override string GetVisitorState()
        {
            return IsAnalyticsEnabled() ? Tracker.CurrentVisit.Region : null;
        }

        /// <summary>
        /// Gets the visitor's GeoIP country
        /// </summary>
        /// <returns></returns>
        public override string GetVisitorCountry()
        {
            return IsAnalyticsEnabled() ? Tracker.CurrentVisit.Country : null;
        }

        /// <summary>
        /// Gets a valid page by ID or returns the most recent page
        /// </summary>
        /// <param name="pages">All pages from the current visit</param>
        /// <param name="pageId">Optionally, the page ID to target</param>
        /// <returns></returns>
        private static VisitorDataSet.PagesRow GetPagesRow(List<VisitorDataSet.PagesRow> pages, Guid? pageId)
        {
            // Defense.. get rid of junk pages
            pages = pages.Where(p => p != null && p.ItemId != Guid.Empty).ToList();

            // If a pageId was supplied, find a page with that ID
            if (pageId.HasValue)
            {
                VisitorDataSet.PagesRow page = pages.FirstOrDefault(p => p.PageId == pageId.Value);
                if (page != null)
                {
                    return page;
                }
            }

            // Otherwise, sort them and return the most recent valid page
            return pages
                .OrderByDescending(p => p.VisitPageIndex)
                .FirstOrDefault();
        }

        /// <summary>
        /// Cancel the current page
        /// </summary>
        protected override void CancelCurrentPage()
        {
            if (IsAnalyticsEnabled())
            {
                Tracker.CurrentPage.Cancel();
            }
        }

        /// <summary>
        /// Determines if the current page is null or empty
        /// </summary>
        /// <returns></returns>
        public override bool IsCurrentPageNullOrEmpty()
        {
            if (IsAnalyticsEnabled())
            {
                return Tracker.CurrentPage == null || Tracker.CurrentPage.ItemId == Guid.Empty;
            }

            return true;
        }

        /// <summary>
        /// Identifies the visitor
        /// </summary>
        /// <param name="profile"></param>
        public override void Identify(UserProfileCustomer profile)
        {
            if (!IsAnalyticsEnabled())
            {
                return;
            }

            if (profile == null || profile.AccountInfo == null)
            {
                return;
            }

            try
            {
                // Set authentication level (for reporting)
                Tracker.Visitor.AuthenticationLevel = AuthenticationLevel.PasswordValidated;

                // Uniquely identify visitor
                string externalUser = profile.AccountInfo.AccountNo.ToString(CultureInfo.InvariantCulture);

                // Search for existing visitor by unique ID
                Visitor visitor = VisitorManager.GetVisitorByExternalUser(externalUser);

                // If there's an existing visitor,
                // reset the current visitors persistant cookie to match it
                if (visitor != null && visitor.VisitorId != Guid.Empty)
                {
                    // If the exisiting visitor is different than the current,
                    // Assume the role of the existing visitor by refreshing cookies
                    if (visitor.VisitorId != Tracker.Visitor.VisitorId)
                    {
                        new VisitorKeyCookie().Create(visitor.VisitorId);
                        new VisitCookie().Invalidate();
                    }
                }
                else
                {
                    // Identify new visitor
                    Tracker.Visitor.ExternalUser = externalUser;
                }

                // Tag visitor with HM Miles & Segments
                if (profile.AccountInfo != null)
                {
                    SetVisitorTag(new VisitorTag("Profile.AccountInfo.YTDQualifyingSegments", profile.AccountInfo.YTDQualifyingSegments));
                    SetVisitorTag(new VisitorTag("Profile.AccountInfo.YTDQualifyingMiles", profile.AccountInfo.YTDQualifyingMiles));
                    SetVisitorTag(new VisitorTag("Profile.AccountInfo.HMMembershipType", profile.AccountInfo.HMMembershipType));
                }
            }
            catch (Exception ex)
            {
                Log.Error("Error identifying visitor", ex, this);
            }
        }

        /// <summary>
        /// Determines if analytics is enabled for the current site, visitor, etc.
        /// </summary>
        /// <returns>Returns true if Sitecore analytics is enabled</returns>
        public override bool IsAnalyticsEnabled()
        {
            try
            {
                return Settings.Analytics.Enabled
                    && Sitecore.Context.Site != null
                    && Sitecore.Context.Site.EnableAnalytics;
            }
            catch (Exception ex)
            {
                Log.Error("Could not determine if Sitecore Analytics is enabled", ex, this);
                return false;
            }
        }

        /// <summary>
        /// Prevents any consecutive A/B tests from running
        /// </summary>
        /// <returns></returns>
        public override void CancelTests()
        {
            try
            {
                Tracker.CurrentPage.SetTestSetIdNull();
                Tracker.CurrentPage.SetTestValuesNull();
            }
            catch (Exception ex)
            {
                Log.Error("Could not cancel A/B tests", ex, this);
            }
        }

        /// <summary>
        /// Tries to get and "out" a multivariate test definition item of a currently running test.
        /// </summary>
        /// <returns>Returns false if attempt fails</returns>
        public override bool TryGetTest(out TestDefinitionItem test)
        {
            try
            {
                TestCombination testCombination = Tracker.CurrentPage.GetTestCombination();

                if (testCombination == null)
                {
                    test = null;
                    return false;
                }
                  
                Item testItem = Sitecore.Context.Database.GetItem(testCombination.Testset.Id.ToString());
                test = new TestDefinitionItem(testItem);
                return true;
            }
            catch (Exception ex)
            {
                Log.Error("Could not find a multivariate test" , ex, this);
                test = null;
                return false;
            }
        }

        /// <summary>
        /// Gets test exposure strategy (custom field)
        /// </summary>
        /// <returns>Returns null if unable to read the strategy</returns>
        public override ExposureStrategy GetTestExposureStrategy()
        {
            try
            {
                TestCombination testCombination = Tracker.CurrentPage.GetTestCombination();
                Item testItem = Sitecore.Context.Database.GetItem(testCombination.Testset.Id.ToString());

                if (testItem == null)
                    return ExposureStrategy.Default;

                ExposureStrategy strategy = ExposureStrategy.Default;

                switch (testItem.Fields["Exposure Strategy"].Value.ToLower())
                {
                    case "once per contact":
                        strategy = ExposureStrategy.OncePerContact;
                        break;
                    case "once per session":
                        strategy = ExposureStrategy.OncePerSession;
                        break;
                }

                return strategy;
            }
            catch (Exception ex)
            {
                Log.Error("Could not find a multivariate test ", ex, this);
                return ExposureStrategy.Default;
            }
        }

        public override bool TryGetRunningVisitorTestFromCookie(out TestDefinitionItem test)
        {
            string testId = string.Empty;

            try
            {
                //cookie exists and contains value
                if (!string.IsNullOrEmpty(HttpContext.Current.Request.Cookies[_VisitorTestCookieName].Value))
                {
                    Item testItem = Sitecore.Context.Database.GetItem(HttpContext.Current.Request.Cookies[_VisitorTestCookieName].Value);

                    if (testItem != null)
                    {
                        test = new TestDefinitionItem(testItem);

                        if (test.IsRunning)
                        {
                            return true;
                        }
                    }

                }
            }
            catch (Exception ex)
            {
                Log.Error("Error reading a Visitor Multivariate Test cookie ", ex, this);
                test = null;
                return false;
            }

            test = null;
            return false;
        }

        /// <summary>
        /// Tries to get a test definition out by using Guid stored in a Visitor Tag
        /// </summary>
        /// <returns>Returns false if no Guid is writtein the Conect Tag, not valid test could be created with that guid or
        /// the test is valid but not running
        /// </returns>
        public override bool TryGetRunningVisitorTest(out TestDefinitionItem test)
        {
            string testId = string.Empty;

            try
            {
                if (!string.IsNullOrEmpty(Tracker.Visitor.Tags[_VisitorTestTagName]))
                {
                    //test for test not being in the web db
                    testId = Tracker.Visitor.Tags[_VisitorTestTagName];
                    Item testItem = Sitecore.Context.Database.GetItem(testId);

                    if (testItem != null)
                    {
                        test = new TestDefinitionItem(testItem);
                       
                        if (test.IsRunning)
                        {
                            return true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("Error reading a Visitor Tag " + _VisitorTestTagName, ex, this);
                test = null;
                return false;
            }

            test = null;
            return false;
        }

        /// <summary>
        /// Tries to get a test definition out by using Guid stored in Session
        /// </summary>
        /// <returns>Returns false if no Guid is written in Session, not valid test could be created with that guid or
        /// the test is valid but not running
        /// </returns>
        public override bool TryGetRunningSessionTest(out TestDefinitionItem test)
        {
            string testId = string.Empty;

            try
            {
                if (!string.IsNullOrEmpty(SessionManager.Instance.MultivariateTestPage))
                {
                    testId = SessionManager.Instance.MultivariateTestPage;
                    Item testItem = Sitecore.Context.Database.GetItem(testId);

                    if (testItem != null)
                    {
                        test = new TestDefinitionItem(testItem);

                        if (test.IsRunning)
                        {
                            return true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("Error reading multivariate test info from session ", ex, this);
                test = null;
                return false;
            }

            test = null;
            return false;
        }

        /// <summary>
        /// Gets a constant used as a key for storing a multivariate test Guid in Visitor Tag
        /// </summary>
        /// <returns>Returns an empty string if it fails</returns>
        public override string GetVisitorTagName()
        {
            return _VisitorTestTagName;
        }

        public override string GetVisitorMultivariateTestCookieName()
        {
            return _VisitorTestCookieName;
        }
    }

}

No comments:

Post a Comment