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