Wednesday, June 20, 2018

FIFA World Cup part 4 of 4 - XConnect

Part 4 in a series of 4
  1. Introduction
  2. Sitecore Forms
  3. Marketing Automation
  4. XConnect
Everyone's signed up, matches has started and scoring is underway. Now all we need to do is visualize who predicted what - and more importantly, a leaderboard.

To do that I need to fetch the data from XConnect. Again, I was happy to find it well documented by Sitecore. Even better, the API seems easy to use if you're familiar with LINQ. Unfortunately almost too easy for this post. I had imagined myself having to setup some indexing and doing some clever work to get my data out as I wanted it. But between the ease of use of the API, and the simplicity of my datamodel for what I need here, it's basically down to a copy/paste of a few lines from the documentation.



So, this is what I'd want my leaderboard to look like:

  • An ordered list of "progress bar"-charts for each player, illustrating the number of rights, wrongs and unplayed (green/red/grey). When hovered you see the correct guesses of the player so far.
  • A list of pie charts for each match illustrating the distribution of predictions of said match. If played in green/red colors, if unplayed in greyscale. When each slice is hovered you see which players had that prediction.

Sounds pretty neat, right? Well at least I thought so! Unfortunately I'm a backender by heart, and I couldn't justify taking the time I would need to make this. I'll spare your screens from having to show what it actually looks like, and just leave you dreaming of charts and hover effects.

What I will do however, is show you the controller I made to supply the data necesary for the dream. The models can be inferred from the usage, no need to paste in those too.

using Sitecore.Analytics.Model.Entities;
using Sitecore.Data.Fields;
using Sitecore.Diagnostics;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;
using Sitecore.XConnect.Client.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using WorldCup.Models.WorldCup;
using WorldCup.XConnect.Facets;

namespace WorldCup.Controllers
{
    public class WorldCupController : Controller
    {
        public ActionResult ShowLeaderboard()
        {
            var leaderboard = InitializeLeaderboard();

            //Go through all xconnect clients and score them
            using (XConnectClient client = SitecoreXConnectClientConfiguration.GetClient())
            {
                IAsyncQueryable<Contact> queryable = client.Contacts
                    .Where(c => c.Identifiers.Any(id => id.Source == "WorldCupForm"))
                    .WithExpandOptions(new ContactExpandOptions(WorldCupBets.DefaultFacetKey));

                var enumerator = queryable.GetBatchEnumeratorSync(10);

                while (enumerator.MoveNext())
                {
                    foreach (var contact in enumerator.Current)
                    {
                        InitializeContestant(contact, leaderboard);
                    }
                }
            }

            //order the leaderboard and the matches
            leaderboard.Contestants = leaderboard.Contestants.OrderByDescending(contestant => contestant.CorrectBets.Count).ToList();
            leaderboard.Matches = leaderboard.Matches.OrderBy(match => match.StartDate).ToList();

            //Behold!
            return View("~/Views/WorldCup/Leaderboard.cshtml", leaderboard);
        }

        private Leaderboard InitializeLeaderboard()
        {
            var leaderboard = new Leaderboard();
            var matches = Sitecore.Context.Database.SelectItems("/sitecore/content/Home/matches//*[@@templatekey='match']");
            foreach (var match in matches)
            {
                leaderboard.Matches.Add(
                    new Match
                    {
                        Name = match.Name,
                        Result = match["Result"],
                        StartDate = new DateField(match.Fields["Start Date"]).DateTime
                    });
            }
            return leaderboard;
        }
        
        private void InitializeContestant(Contact contact, Leaderboard leaderboard)
        {
            var bets = contact.GetFacet<WorldCupBets>(WorldCupBets.DefaultFacetKey)?.Matches;
            if (bets == null)
                return;

            var contestant = new Contestant();
            contestant.Identifier = contact.Identifiers.First(id => id.Source == "WorldCupForm").Identifier;
            leaderboard.Contestants.Add(contestant);

            foreach (var bet in bets)
            {
                var match = leaderboard.Matches.FirstOrDefault(m => m.Name == bet.Key);
                if (match == null)
                    continue;

                if (match.Result == bet.Value)
                    contestant.CorrectBets.Add(match.Name);

                if (bet.Value == "1")
                    match.Bet1.Add(contestant.Identifier);
                else if (bet.Value == "2")
                    match.Bet2.Add(contestant.Identifier);
                else
                    match.BetX.Add(contestant.Identifier);
            }
        }
    }
}

And there you have it folks. This was my first adventure into the excitements of the new parts of Sitecore 9. To sum it all up, I'm very positive about the new/improved modules. The documentation and ease of use that goes along seems to have improved by leaps and bounds.
These 4 posts haven't supplied a lot in terms of new and exciting ways to do stuff, but maybe it will inspire someone on how to utilize some of the new functionality - or more importantly - to start playing around with it yourselves, rather than just reading about it!