Custom Sitecore Intel Transformers To Modify Date Format In Experience Profile

Going through the recently published Sitecore StackExchange site, I came across a question regarding modifying the date format of visits in Experience Profile since it shows the dd.mm.yyyy format by default. In case you don't know what I am talking about, here is what it looks like:



It's not just that one view, the default date format seems to apply to other views as well:



My obvious thought here was that there has to be a config setting somewhere that controls this date format. Here is my process of elimination:
  • Eliminated Experience Profile configs since it did not contain any date related settings
  • Looked at showconfig.aspx and found some index fields that contained date formats but eliminated since the default format was yyyy/mm/dd
  • Eliminated the Reporting Database since the data formats were different
  • Looked at the service calls made by Experience Explorer to fetch view data and found this:
     "InteractionStartDateTime":"2016-10-20T15:34:20.449"
    "FormattedInteractionStartDateTime":"20.10.2016 11:34:20 (23hr:31min)"


I was starting to see light at the end of the rabbit hole.
Digging further I found that Sitecore.Cintel.Client.dll is responsible for providing the data for the views, which took me back to Sitecore.ExperienceProfile.Client.config, specifically the following section:

<experienceProfile>
      
      <!-- Provides a hook to modify view results before they are displayed by Experience Profile appliction. -->
      <resultTransformManager>

        <!-- If http request header "X-SC-CintelTransformerClientName" matches clientName element, then the result transformer provider is applied.
             Header value has to be:  X-SC-CintelTransformerClientName: speakClient -->
        <resultTransformProvider clientName="speakClient" type="Sitecore.Cintel.Endpoint.Transfomers.ResultTransformProvider" singleInstance="true">

          <!-- If http request header "X-SC-CintelTransfomerKey" matches a viewName element, then the result transformer is applied.
               For example, header value could read as:  X-SC-CintelTransfomerKey: visits -->
          <intelResultTransformers>
            <add viewName="visits" type="Sitecore.Cintel.Client.Transformers.Contact.VisitResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="goals" type="Sitecore.Cintel.Client.Transformers.Contact.GoalResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="campaigns" type="Sitecore.Cintel.Client.Transformers.Contact.CampaignResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="events" type="Sitecore.Cintel.Client.Transformers.Contact.EventResultTransformer, Sitecore.Cintel.Client"/>
             <add viewName="latest-statistics" type="Sitecore.Cintel.Client.Transformers.Contact.OverviewResultTransformer, Sitecore.Cintel.Client"/> 		
            <add viewName="latest-events" type="Sitecore.Cintel.Client.Transformers.Contact.LatestEventResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="recent-campaigns" type="Sitecore.Cintel.Client.Transformers.Contact.RecentCampaignsResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="best-pattern-matches" type="Sitecore.Cintel.Client.Transformers.Contact.BestPatternMatchesResultTransformer, Sitecore.Cintel.Client"/>

            <add viewName="visit-summary" type="Sitecore.Cintel.Client.Transformers.Contact.VisitSummaryResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="visit-pages" type="Sitecore.Cintel.Client.Transformers.Contact.VisitPagesResultTransformer, Sitecore.Cintel.Client"/>

            <add viewName="visit-internal-searches" type="Sitecore.Cintel.Client.Transformers.Contact.VisitInternalSearchesResultTransformer, Sitecore.Cintel.Client"/>

            <add viewName="profile-info" type="Sitecore.Cintel.Client.Transformers.Contact.ProfilingProfilesResultTransformer, Sitecore.Cintel.Client"/>

            <add viewName="external-keyword-summary" type="Sitecore.Cintel.Client.Transformers.Contact.KeywordResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="internal-keyword-summary" type="Sitecore.Cintel.Client.Transformers.Contact.InternalKeywordSummaryResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="paid-keyword-summary" type="Sitecore.Cintel.Client.Transformers.Contact.KeywordResultTransformer, Sitecore.Cintel.Client"/>

            <add viewName="external-keyword-detail" type="Sitecore.Cintel.Client.Transformers.Contact.KeywordDetailResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="internal-keyword-detail" type="Sitecore.Cintel.Client.Transformers.Contact.KeywordDetailResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="paid-keyword-detail" type="Sitecore.Cintel.Client.Transformers.Contact.KeywordDetailResultTransformer, Sitecore.Cintel.Client"/>

            <add viewName="latest-visitors" type="Sitecore.Cintel.Client.Transformers.Contact.LatestVisitorsResultTransformer, Sitecore.Cintel.Client"/>

            <add viewName="journey" type="Sitecore.Cintel.Client.Transformers.Contact.JourneyResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="journey-detail-outcome" type="Sitecore.Cintel.Client.Transformers.Contact.JourneyOutcomeDetailResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="journey-detail-online-interaction" type="Sitecore.Cintel.Client.Transformers.Contact.JourneyOnlineInteractionDetailResultTransformer, Sitecore.Cintel.Client"/> 
            
            <add viewName="channel-interaction-distribution" type="Sitecore.Cintel.Client.Transformers.Contact.ChannelInteractionDistributionResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="channel-goal-distribution" type="Sitecore.Cintel.Client.Transformers.Contact.ChannelGoalDistributionResultTransformer, Sitecore.Cintel.Client"/>
            <add viewName="channel-summary" type="Sitecore.Cintel.Client.Transformers.Contact.ChannelSummaryResultTransformer, Sitecore.Cintel.Client"/>
            
            <add viewName="outcome-detail" type="Sitecore.Cintel.Client.Transformers.Contact.OutcomeDetailTransformer, Sitecore.Cintel.Client"/>
          
          </intelResultTransformers>

           <!-- If http request header "X-SC-CintelTransfomerKey" matches key element, then the result transformer is applied.
                Header value has to be:  X-SC-CintelTransfomerKey: default -->
          <contactSearchResultTransformer>
            <add key="default" type="Sitecore.Cintel.Client.Transformers.Contact.ContactSearchResultTransformer, Sitecore.Cintel.Client" />
          </contactSearchResultTransformer>

          <!-- If http request header "X-SC-CintelTransfomerKey" matches key element, then the result transformer is applied.
                Header value has to be:  X-SC-CintelTransfomerKey: default -->
          <contactDetailsTransformer>
            <add key="default" type="Sitecore.Cintel.Client.Transformers.Contact.ContactDetailsTransformer, Sitecore.Cintel.Client" />
          </contactDetailsTransformer>
		  
        </resultTransformProvider>
      </resultTransformManager>
    </experienceProfile>
So here is the Aha moment!
Good news - found what needs to be modified!
Bad news - can't believe Sitecore hardcoded the date formats!
SOLUTION:
  1. Add config setting for date format
  2. Write a custom TimeConverter
  3. Write a custom ClientFactory that provides the custom TimeConverter
  4. Write a custom Transformer that use the custom ClientFactory
NOTE: To fix the timeline view I wrote a custom JourneyOnlineInteractionDetailResultTransformer. Some Transformers actually format the date within the Transform() method itself in which case you can skip steps 1 thru 3, for e.g. OverviewResultTransformer.
Here is the code:
Step 1: Config setting for date format
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <settings>
      <!--  Date format for visit date in Experience Profile
      -->
      <setting name="ExprienceProfile.DateFormat" value="MM.dd.yyyy HH:mm:ss" />
    </settings>
  </sitecore>
</configuration>
Step 2 & 3: Custom TimeConverter and ClientFactory
using Sitecore.Cintel.Client;
using Sitecore.Cintel.Client.Transformers;

namespace Sc.Cintel.Client
{
    public class CustomClientFactory : ClientFactory
    {
        public new static CustomClientFactory Instance { get; set; } = new CustomClientFactory();

        public new ResultSetExtender GetResultSetExtender()
        {
            return new ResultSetExtender(this.GetResultSetHelper(), this.GetTimeConverter(), this.GetTextConverter());
        }
        public new TimeConverter GetTimeConverter()
        {
            return new CustomTimeConverter(this.GetRepository(), this.GetContextUtil());
        }
    }
}
Step 4: Custom Transformer
using Sitecore.Cintel.Client;
using Sitecore.Cintel.Client.Transformers;

namespace Sc.Cintel.Client
{
    public class CustomClientFactory : ClientFactory
    {
        public new static CustomClientFactory Instance { get; set; } = new CustomClientFactory();

        public new ResultSetExtender GetResultSetExtender()
        {
            return new ResultSetExtender(this.GetResultSetHelper(), this.GetTimeConverter(), this.GetTextConverter());
        }
        public new TimeConverter GetTimeConverter()
        {
            return new CustomTimeConverter(this.GetRepository(), this.GetContextUtil());
        }
    }
}
Finally, don't forget to replace the default transformer with your custom one in the config file.

<add viewName="journey-detail-online-interaction" type="Sc.Cintel.Client.Transformers.Contact.CustomJourneyOnlineInteractionDetailTransformer, Sc.Cintel.Client"/>

As always, there is more than one solution to a problem. This was the one I could think of. So, please feel free to leave a comment or suggestion for better approaches.


Comments

Post a Comment

Popular posts from this blog

Is Rendered Item Valid XHtml Document Could not find schema information warnings during publish item Sitecore 7.2

Decision tree for selecting the best cloud hosting option (App service, VM, Containers, Serverless)

SOLVED: Sitecore Habitat Home Sync-Unicorn fails with the error: GetResponse" with "0" argument(s): "The remote server returned an error: (403) Forbidden.