Author : Vikas Kumar
Director - Digital Content Management
03/Feb/2019

Vikas a Marketing Technologist heads the Customer Experience Platform practice at Espire. He leads all initiatives for competency building within the practice, especially on platforms such as Sitecore, Acquia (Drupal), SDL and others. Vikas plays a key role in the design and development of best practices that can help our clients implement customer experience initiatives.


Driving personalization with xConnect APIs and custom facet data | Part 1

Introduction

Personalization aims at delivering the right content to the right customer. The moment a user interacts with any of your systems, you are presented with a significant amount of data that can be utilized to deliver a moderately tailored and personalized experience to your visitor. The content can be significantly improved further once the user is identified and more is known about his preferences.

Sitecore xConnect provides the capability to capture data about a user from various sources and helps integrate everything into XDB for us to leverage.

The blog explains how custom data from external sources can be captured and used to personalize content created in Sitecore to improve the overall customer experience.

Follow our session on YouTube

Creating a Custom Facet and Model

1. In order to capture custom data about a contact, we first need to define a model that would hold the new information. We do so by creating a class with its properties resenting the data to be captured and inheriting the class Sitecore.XConnect.Facet. We also define a Default Facet Key which would later help us consult the facet using the API.


 [FacetKey(DefaultFacetKey)] 
    public class StudentInfo : Facet 
    { 
        public const string DefaultFacetKey = "StudentInfo"; 
        public string Courses { get; set; } 
        public string Country { get; set; } 
    } 

2. The next step is to create a custom collection model. A collection model defines what data is collected for a contact and stored in XDB. This model is used in extension with the default Core Collection Model, which defines the basic information such as First Name, Email etc., collected in XDB by default.


 public  class SitecoreStudentCustomModel 
    { 
        public static XdbModel Model { get; } = BuildModel(); 
        private static XdbModel BuildModel() 
        { 
            XdbModelBuilder modelBuilder = new XdbModelBuilder("SitecoreCustomModel", new XdbModelVersion(1, 0)); 
            modelBuilder.DefineFacet<contact,>(FacetKeys.StudentInfo); 
            modelBuilder.ReferenceModel(Sitecore.XConnect.Collection.Model.CollectionModel.Model); 
            return modelBuilder.BuildModel(); 
        } 
    } 

    public class FacetKeys 
    { 
        public const string StudentInfo = "StudentInfo"; 
    } 

3. The next step is to deploy the new model in the form of a JSON file to xConnect roles. In order to do so, create a console application and run the following code.


 class Program 
        { 
            static void Main(string[] args) 
            { 
                var model = SCM.SitecoreStudentCustomModel.Model; 
                var serializedModel = Sitecore.XConnect.Serialization.XdbModelWriter.Serialize(model); 
                File.WriteAllText("D:\\ImportCSV\\files\\" + model.FullName + ".json", serializedModel); 
            } 
        } 

4. Copy the created model JSON file to the following paths:


C:\\root\App_Data\Models 
C:\\root\App_data\Models 
C:\\root\App_Data\Models 


Importing Data into XDB

For demo purpose, we created a custom console application which would import contact information into XDB from a CSV file. A google form was created to capture the relevant information and the list of submissions were converted to a CSV file and used to create contacts.

1. Create method to load CSV file data in model


//load csv data to student list 
        public static List LoadCSV(string filePath) 
        { 
            try 
            { 
                var listStudent = (from row in File.ReadAllLines(filePath).Skip(1) 
                                   let studentColData = row.Split(',') 
                                   select new StudentData 
                                   { 
                                       StudentName = studentColData[1], 
                                       Courses = studentColData[3], 
                                       Country= studentColData[4], 
                                       Email= studentColData[2], 
                                       Uid= studentColData[0] 
                                   }).ToList(); 
                return listStudent; 
            } 
            catch (Exception ex) { System.IO.File.AppendAllText(ConfigHelper.getConfigValue("LogFilePath"), DateTime.Now.ToString() + ": " + ex.ToString() + "\n"); return null; } 
        } 


2. Create method to load xConnect configuration for the API

This method would contain logic to load the new custom collection model when getting contact data from XDB


//set the Xconnect configuration 
        public static async Task GetXConnectConfiguration() 
        { 
            CertificateWebRequestHandlerModifierOptions options = 
            CertificateWebRequestHandlerModifierOptions.Parse(CWRHMO); 
            var certificateModifier = new CertificateWebRequestHandlerModifier(options); 
            List clientModifiers = new List(); 
            var timeoutClientModifier = new TimeoutHttpClientModifier(new TimeSpan(0, 0, 20)); 
            clientModifiers.Add(timeoutClientModifier); 
            var collectionClient = new CollectionWebApiClient(new Uri(ConfigHelper.getConfigValue("CollectionWebApiClient")), clientModifiers, new[] { certificateModifier }); 
            var searchClient = new SearchWebApiClient(new Uri(ConfigHelper.getConfigValue("SearchWebApiClient")), clientModifiers, new[] { certificateModifier }); 
            var configurationClient = new ConfigurationWebApiClient(new Uri(ConfigHelper.getConfigValue("ConfigurationWebApiClient")), clientModifiers, new[] { certificateModifier }); 
            XdbModel[] models = { CollectionModel.Model, SitecoreStudentCustomModel.Model }; 
            var cfg = new XConnectClientConfiguration( 
            new XdbRuntimeModel(models), collectionClient, searchClient, configurationClient); 

            try 
            { 
                await cfg.InitializeAsync(); 
                return cfg; 
            } 
            catch (XdbModelConflictException ce) 
            { 
                System.IO.File.AppendAllText(ConfigHelper.getConfigValue("LogFilePath"), DateTime.Now.ToString() + ": " + ce.ToString() + "\n"); 
                return null; 
            } 
        } 

3. Create method to Import data in XDB

This would create contacts based on the information loaded from CSV and defines what data is stored in each facet.


//Import csv data to XDB 
        private static async Task ExportToXDB(List students) 
        { 
            try 
            { 
                var offlineGoal = Guid.Parse(ConfigurationManager.AppSettings.Get("OfflineGoal"));  
                var channelId = Guid.Parse(ConfigurationManager.AppSettings.Get("ChannelId")); 
                var source = "Uid"; 
                var cfg = await GetXConnectConfiguration(); 
                using (var client = new XConnectClient(cfg)) 
                { 
                    foreach (var objStudent in students) 
                    { 
                        // to identifier the student by uid 
                        IdentifiedContactReference reference = new IdentifiedContactReference(source, objStudent.Uid); 
                        Contact existingContact = await client.GetAsync(reference, new ContactExpandOptions(new string[] { PersonalInformation.DefaultFacetKey, EmailAddressList.DefaultFacetKey, StudentInfo.DefaultFacetKey })); 
                        if (existingContact != null) 
                        { 
                            PersonalInformation existingContactPersonalFacet = existingContact.GetFacet(PersonalInformation.DefaultFacetKey); 
                            if (existingContactPersonalFacet != null) 
                            { 
                                existingContactPersonalFacet.FirstName = objStudent.StudentName; 
                                existingContactPersonalFacet.Nickname = objStudent.Country; 
                                existingContactPersonalFacet.JobTitle = objStudent.Courses; 
                                client.SetFacet(existingContact, PersonalInformation.DefaultFacetKey, existingContactPersonalFacet); 
                            } 
                            else 
                            { 
                               PersonalInformation personalInfoFacet = new PersonalInformation(); 
                               personalInfoFacet.FirstName = objStudent.StudentName; 
                               client.SetFacet(existingContact, PersonalInformation.DefaultFacetKey, personalInfoFacet); 
                            } 

                            EmailAddressList existingContactEmailFacet = existingContact.GetFacet(EmailAddressList.DefaultFacetKey); 
                            if (existingContactEmailFacet != null) 
                            { 
                                existingContactEmailFacet.PreferredEmail = new EmailAddress(objStudent.Email, true); 
                                existingContactEmailFacet.PreferredKey = "work"; 
                                client.SetFacet(existingContact, EmailAddressList.DefaultFacetKey, existingContactEmailFacet); 
                            } 

                            else 
                            { 
                                EmailAddressList emailAddressList = new EmailAddressList(new EmailAddress(objStudent.Email, true), "work"); 
                                client.SetFacet(existingContact, EmailAddressList.DefaultFacetKey, emailAddressList); 
                            } 

                            StudentInfo existingStudentInfoFacet = existingContact.GetFacet(StudentInfo.DefaultFacetKey); 
                            if (existingStudentInfoFacet != null) 
                            { 
                                existingStudentInfoFacet.Uid = objStudent.Uid; 
                                client.SetFacet(existingContact, StudentInfo.DefaultFacetKey, existingStudentInfoFacet); 
                            } 

                            else 

                            { 
                                StudentInfo stuudentInfo = new StudentInfo() { Country = objStudent.Country, Courses = objStudent.Courses }; 
                                client.SetFacet(existingContact, StudentInfo.DefaultFacetKey, stuudentInfo); 
                            } 

                            // Create a new interaction for that contact 

                            Interaction interaction = new Interaction(existingContact, InteractionInitiator.Brand, channelId, ""); 

                           // Add events - all interactions must have at least one event 

                            var xConnectEvent = new Goal(offlineGoal, DateTime.UtcNow); 
                            interaction.Events.Add(xConnectEvent); 

                            // Add the contact and interaction 

                           client.AddInteraction(interaction); 
                        } 

                       else 
                        { 

                            // Identifier for a 'known' contact 

                            var identifier = new ContactIdentifier[] 
                           { 
                                    new ContactIdentifier(source, objStudent.Uid, ContactIdentifierType.Known) 
                            }; 

 

                            // Create a new contact with the identifier 

                            Contact knownContact = new Contact(identifier); 
                            PersonalInformation personalInfoFacet = new PersonalInformation(); 
                            personalInfoFacet.FirstName = objStudent.StudentName; 
                            client.SetFacet(knownContact, PersonalInformation.DefaultFacetKey, personalInfoFacet); 
                            EmailAddressList emailAddressList = new EmailAddressList(new EmailAddress(objStudent.Email, true), "work"); 
                            client.SetFacet(knownContact, EmailAddressList.DefaultFacetKey, emailAddressList); 
                            StudentInfo studentInfo = new StudentInfo() { Country = objStudent.Country, Courses = objStudent.Courses}; 
                            client.SetFacet(knownContact, StudentInfo.DefaultFacetKey, studentInfo); 
                            client.AddContact(knownContact); 


                            // Create a new interaction for that contact 

                            Interaction interaction = new Interaction(knownContact, InteractionInitiator.Brand, channelId, ""); 


                            // Add events - all interactions must have at least one event 

                            var xConnectEvent = new Goal(offlineGoal, DateTime.UtcNow); 
                            interaction.Events.Add(xConnectEvent); 


                            // Add the contact and interaction 

                           client.AddInteraction(interaction); 
                        } 


                        // Submit contact and interaction - a total of two operations 

                        await client.SubmitAsync(); 
                        Console.WriteLine(objStudent.Email); 
                        Console.WriteLine(objStudent.StudentName + "UID:" + objStudent.Uid); 

                        // Get the last batch that was executed 

                        var operations = client.LastBatch; 
                        Console.WriteLine("RESULTS..."); 

                        // Loop through operations and check status 

                        foreach (var operation in operations) 
                        { 
                            Console.WriteLine(operation.OperationType + operation.Target.GetType().ToString() + " Operation: " + operation.Status); 
                        } 
                    } 
                } 
                return true; 
            } 
            catch (Exception ex) 
            { 
                System.IO.File.AppendAllText(ConfigHelper.getConfigValue("LogFilePath"), DateTime.Now.ToString() + ": " + ex.ToString() + "\n"); 
                return false; 
            } 
        } 


4. Call the methods created above for execution


 private static void Main(string[] args) 
        { 
            string filePath = ConfigurationManager.AppSettings.Get("csvPath"); 
            if (IsFileExists(filePath)) 
            { 
                ExportToXDB(LoadCSV(filePath)).ConfigureAwait(false).GetAwaiter().GetResult(); 
            } 
            else 
            { 
               Console.WriteLine("File doesn't exists"); 
            } 
          } 


After this step, you can verify the contacts created in the Experience Profile application in Sitecore. This concludes the steps required to capture custom data in XDB using XConnect APIs.

Follow part 2 of the blog to see how to use this custom data to personalize content using custom personalization rules.

MORE FROM OUR BLOGS

Top challenges in customer journey mapping and how to mitigate them in 2021

In the new normal, customer journey mapping has emerged as a pre-requisite for analyzing customer expectations and delivering personalized customer experiences across all touchpoints. Cross-device and multiple-channel access of services has made mapping customer journey a tedious task for businesses.

How espire aced digital experiences amid global pandemic top 10 highlights

2020 was defined not just by unprecedented events and crisis but also by the spirit of survival, agility and resilience. The COVID-19 pandemic disrupted lives, livelihoods and businesses on a global scale. At Espire, our commitment to combat the pandemic with a spirit of unity, agility and resilience has helped our customers, partners and employees with uninterrupted and excellent service delivery - without compromising on health & safety standards.

5 pillars of contextual communications to drive impressive cx and greater customer engagement

In recent times and especially during the pandemic, the phrase ‘content is king’ has caught on like wildfire in the world of marketing but in retrospect, content without context has no value for customers. In simple words, Contextual Communications means that the right message is delivered to the right audience at the right time, using the right channel.

How has the logistics industry reshaped in new normal and roadmap ahead

Logistics industry has been undergoing a rapid boom initiated by globalization and catapulted to new heights by new means of communication and rapid advancement in supply chain technologies. However, the new normal as we face today is shaping the logistics industry in newer ways.

Top 4 benefits of embracing digital transformation in insurance for 2021

With the increase in Insurtech players, tech savvy customers and competitive growth of digital services, digital transformation has become imperative for the insurance industry for both customer acquisition and retention. Eminent insurance providers are utilizing disruptive technology and predictive analysis to examine customer demands and deliver personalized solutions and great customer experience

5 top use cases of digital transformation in 2021

Digital transformation in today’s fast paced digital world with empowered customers has become imminent and can only be achieved through enterprise-wide transformation, design thinking and deployment of cutting edge technology. In this blog, we will present before you the top 5 use cases of digital transformation in 2021.

Maximize your personalization potential with sitecore content profiling

In an era of empowered customers and stiff competition, communicating and delivering products and services to customers in a way they prefer has become essential. Sitecore Profiling offers marketers with tremendous opportunity to know what their customers are looking for and what interests them for a real-time content personalization and customer service, by tagging Sitecore content with defined visitor profiles.

Sitecore list manager a walkthrough of segmented list from custom rules

Using the List Manager, you can create Segment Lists segmented with rules. We can use the rules that Sitecore provides out of the box, also we can create custom rules if required. In this blog, we will discuss about how to create custom rules for segmented list using custom facets.

A comprehensive guide to sitecore list manager going beyond exm

The Sitecore List Manager in most implementation is under-utilized. Sometime marketers are not familiar with real power of Sitecore List manager. The capabilities and potential utility of List Manager even transcends beyond EXM and acts as powerful tool for managing customer segments, when it configured correctly.

Sitecore content profiling simplifying essential steps for marketers

Sitecore profiling offers marketers with tremendous opportunity to know what their customers are looking for and what interests them for a real-time content personalization and customer service by tagging Sitecore content with defined visitor profiles.

Subscribe To Our Blog

By clicking on "SUBSCRIBE NOW" you acknowledge having read our Privacy Notice.