/// <summary>
        /// Further configure this info object based on the options specified by the caller, then enqueue it to be bulk inserted.
        /// </summary>
        /// <param name="queue">The <see cref="ConcurrentQueue{InterationInfo}"/> into which this info object should be enqueued.</param>
        public void Initialize(ConcurrentQueue <InteractionTransactionInfo> queue)
        {
            RockPage    rockPage = null;
            HttpRequest request  = null;

            if (this.GetValuesFromHttpRequest)
            {
                try
                {
                    rockPage = HttpContext.Current.Handler as RockPage;
                }
                catch
                {
                    rockPage = null;
                }

                try
                {
                    if (rockPage != null)
                    {
                        request = rockPage.Request;
                    }
                    else if (HttpContext.Current != null)
                    {
                        request = HttpContext.Current.Request;
                    }
                }
                catch
                {
                    // Intentionally ignore exception (.Request will throw an exception instead of simply returning null if it isn't available).
                }
            }

            // Fall back to values from the HTTP request if specified by the caller AND the values weren't explicitly set on the info object:
            // (The rockPage and request variables will only be defined if this.GetValuesFromHttpRequest was true.)

            this.InteractionData = this.InteractionData ?? request?.Url.ToString();
            this.UserAgent       = this.UserAgent ?? request?.UserAgent;

            try
            {
                this.IPAddress = this.IPAddress ?? RockPage.GetClientIpAddress();
            }
            catch
            {
                this.IPAddress = string.Empty;
            }

            this.BrowserSessionId = this.BrowserSessionId ?? rockPage?.Session["RockSessionId"]?.ToString().AsGuidOrNull();

            this.PersonAliasId = this.PersonAliasId ?? rockPage?.CurrentPersonAliasId;

            // Make sure we don't exceed this field's character limit.
            this.InteractionOperation = EnforceLengthLimitation(this.InteractionOperation, 25);

            if (this.InteractionSummary.IsNullOrWhiteSpace())
            {
                // If InteractionSummary was not specified, use the Page title.
                var title = string.Empty;
                if (rockPage != null)
                {
                    if (rockPage.BrowserTitle.IsNotNullOrWhiteSpace())
                    {
                        title = rockPage.BrowserTitle;
                    }
                    else
                    {
                        title = rockPage.PageTitle;
                    }
                }

                // Remove the site name from the title.
                if (title?.Contains("|") == true)
                {
                    title = title.Substring(0, title.LastIndexOf('|')).Trim();
                }

                this.InteractionSummary = title;
            }

            // Make sure we don't exceed these fields' character limit.
            this.InteractionSummary               = EnforceLengthLimitation(this.InteractionSummary, 500);
            this.InteractionChannelCustom1        = EnforceLengthLimitation(this.InteractionChannelCustom1, 500);
            this.InteractionChannelCustom2        = EnforceLengthLimitation(this.InteractionChannelCustom2, 2000);
            this.InteractionChannelCustomIndexed1 = EnforceLengthLimitation(this.InteractionChannelCustomIndexed1, 500);
            this.InteractionSource   = EnforceLengthLimitation(this.InteractionSource, 25);
            this.InteractionMedium   = EnforceLengthLimitation(this.InteractionMedium, 25);
            this.InteractionCampaign = EnforceLengthLimitation(this.InteractionCampaign, 50);
            this.InteractionContent  = EnforceLengthLimitation(this.InteractionContent, 50);
            this.InteractionTerm     = EnforceLengthLimitation(this.InteractionTerm, 50);

            // Get existing (or create new) interaction channel and interaction component for this interaction.
            if (this.InteractionChannelId == default)
            {
                this.InteractionChannelId = InteractionChannelCache.GetChannelIdByTypeIdAndEntityId(this.ChannelTypeMediumValueId, this.ChannelEntityId, this.ChannelName, this.ComponentEntityTypeId, this.InteractionEntityTypeId);
            }

            this.InteractionComponentId = InteractionComponentCache.GetComponentIdByChannelIdAndEntityId(this.InteractionChannelId, this.ComponentEntityId, this.ComponentName);

            queue.Enqueue(this);
        }
Esempio n. 2
0
        public MediaElementInteraction PostWatchInteraction(MediaElementInteraction mediaInteraction)
        {
            var rockContext        = Service.Context as RockContext;
            var personService      = new PersonService(rockContext);
            var personAliasService = new PersonAliasService(rockContext);
            var interactionService = new InteractionService(rockContext);
            int?personAliasId;

            if (!IsWatchMapValid(mediaInteraction.WatchMap))
            {
                var errorResponse = Request.CreateErrorResponse(HttpStatusCode.BadRequest, $"The WatchMap contains invalid characters.");
                throw new HttpResponseException(errorResponse);
            }

            // Get the person alias to associate with the interaction in
            // order of provided Person.Guid, then PersonAlias.Guid, then
            // the logged in Person.
            if (mediaInteraction.PersonGuid.HasValue)
            {
                personAliasId = personAliasService.GetPrimaryAliasId(mediaInteraction.PersonGuid.Value);
            }
            else if (mediaInteraction.PersonAliasGuid.HasValue)
            {
                personAliasId = personAliasService.GetId(mediaInteraction.PersonAliasGuid.Value);
            }
            else
            {
                personAliasId = GetPersonAliasId(rockContext);
            }

            var mediaElement = new MediaElementService(rockContext).GetNoTracking(mediaInteraction.MediaElementGuid);

            // Ensure we have our required MediaElement.
            if (mediaElement == null)
            {
                var errorResponse = Request.CreateErrorResponse(HttpStatusCode.BadRequest, $"The MediaElement could not be found.");
                throw new HttpResponseException(errorResponse);
            }

            // Get (or create) the component.
            var         interactionChannelId   = InteractionChannelCache.Get(SystemGuid.InteractionChannel.MEDIA_EVENTS).Id;
            var         interactionComponentId = InteractionComponentCache.GetComponentIdByChannelIdAndEntityId(interactionChannelId, mediaElement.Id, mediaElement.Name);
            Interaction interaction            = null;

            if (mediaInteraction.InteractionGuid.HasValue)
            {
                // Look for an existing Interaction by it's Guid and the
                // component it is supposed to show up under. But also
                // check that the RelatedEntityTypeId/RelatedEntityId values
                // are either null or match what was passed by the user.
                // Finally also make sure the Interaction Person Alias is
                // either the one for this user OR if there is a Person
                // record attached to that alias that the alias Id we have is
                // also attached to that Person record OR the interaction is
                // not tied to a person.
                interaction = interactionService.Queryable()
                              .Where(a => a.Guid == mediaInteraction.InteractionGuid.Value && a.InteractionComponentId == interactionComponentId)
                              .Where(a => !a.RelatedEntityTypeId.HasValue || !mediaInteraction.RelatedEntityTypeId.HasValue || a.RelatedEntityTypeId == mediaInteraction.RelatedEntityTypeId)
                              .Where(a => !a.RelatedEntityId.HasValue || !mediaInteraction.RelatedEntityId.HasValue || a.RelatedEntityId == mediaInteraction.RelatedEntityId)
                              .Where(a => !a.PersonAliasId.HasValue || a.PersonAliasId == personAliasId || a.PersonAlias.Person.Aliases.Any(b => b.Id == personAliasId))
                              .SingleOrDefault();
            }

            var watchedPercentage = CalculateWatchedPercentage(mediaInteraction.WatchMap);

            if (interaction != null)
            {
                // Update the interaction data with the new watch map.
                var data = interaction.InteractionData.FromJsonOrNull <MediaWatchedInteractionData>() ?? new MediaWatchedInteractionData();
                data.WatchMap          = mediaInteraction.WatchMap;
                data.WatchedPercentage = watchedPercentage;

                interaction.InteractionData        = data.ToJson();
                interaction.InteractionLength      = watchedPercentage;
                interaction.InteractionEndDateTime = RockDateTime.Now;
                interaction.PersonAliasId          = interaction.PersonAliasId ?? personAliasId;

                if (mediaInteraction.RelatedEntityTypeId.HasValue)
                {
                    interaction.RelatedEntityTypeId = mediaInteraction.RelatedEntityTypeId;
                }

                if (mediaInteraction.RelatedEntityId.HasValue)
                {
                    interaction.RelatedEntityId = mediaInteraction.RelatedEntityId;
                }
            }
            else
            {
                // Generate the interaction data from the watch map.
                var data = new MediaWatchedInteractionData
                {
                    WatchMap          = mediaInteraction.WatchMap,
                    WatchedPercentage = watchedPercentage
                };

                interaction = interactionService.CreateInteraction(interactionComponentId,
                                                                   null, "Watch", string.Empty, data.ToJson(), personAliasId, RockDateTime.Now,
                                                                   null, null, null, null, null, null);

                interaction.InteractionLength      = watchedPercentage;
                interaction.InteractionEndDateTime = RockDateTime.Now;
                interaction.RelatedEntityTypeId    = mediaInteraction.RelatedEntityTypeId;
                interaction.RelatedEntityId        = mediaInteraction.RelatedEntityId;

                interactionService.Add(interaction);
            }

            rockContext.SaveChanges();

            mediaInteraction.InteractionGuid = interaction.Guid;

            return(mediaInteraction);
        }
Esempio n. 3
0
        public MediaElementInteraction GetWatchInteraction([FromUri] Guid?mediaElementGuid = null, [FromUri] Guid?personGuid = null, Guid?personAliasGuid = null)
        {
            var rockContext        = Service.Context as RockContext;
            var personService      = new PersonService(rockContext);
            var personAliasService = new PersonAliasService(rockContext);
            var interactionService = new InteractionService(rockContext);
            int?personAliasId;

            // Get the person alias to associate with the interaction in
            // order of provided Person.Guid, then PersonAlias.Guid, then
            // the logged in Person.
            if (personGuid.HasValue)
            {
                personAliasId = personAliasService.GetPrimaryAliasId(personGuid.Value);
            }
            else if (personAliasGuid.HasValue)
            {
                personAliasId = personAliasService.GetId(personAliasGuid.Value);
            }
            else
            {
                personAliasId = GetPersonAliasId(rockContext);
            }

            // Verify we have a person alias, otherwise bail out.
            if (!personAliasId.HasValue)
            {
                var errorResponse = Request.CreateErrorResponse(HttpStatusCode.BadRequest, $"The personAliasId could not be determined.");
                throw new HttpResponseException(errorResponse);
            }

            MediaElement mediaElement = null;

            // In the future we might make MediaElementGuid optional so
            // perform the check this way rather than making it required
            // in the parameter list.
            if (mediaElementGuid.HasValue)
            {
                mediaElement = new MediaElementService(rockContext).GetNoTracking(mediaElementGuid.Value);
            }

            // Ensure we have our required MediaElement.
            if (mediaElement == null)
            {
                var errorResponse = Request.CreateErrorResponse(HttpStatusCode.BadRequest, $"The MediaElement could not be found.");
                throw new HttpResponseException(errorResponse);
            }

            // Get (or create) the component.
            var interactionChannelId   = InteractionChannelCache.Get(SystemGuid.InteractionChannel.MEDIA_EVENTS).Id;
            var interactionComponentId = InteractionComponentCache.GetComponentIdByChannelIdAndEntityId(interactionChannelId, mediaElement.Id, mediaElement.Name);

            Interaction interaction = interactionService.Queryable()
                                      .AsNoTracking()
                                      .Include(a => a.PersonAlias)
                                      .Where(a => a.InteractionComponentId == interactionComponentId)
                                      .Where(a => a.PersonAliasId == personAliasId || a.PersonAlias.Person.Aliases.Any(b => b.Id == personAliasId))
                                      .OrderByDescending(a => a.InteractionEndDateTime)
                                      .ThenByDescending(a => a.InteractionDateTime)
                                      .FirstOrDefault();

            if (interaction == null)
            {
                var errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, $"The Interaction could not be found.");
                throw new HttpResponseException(errorResponse);
            }

            var data = interaction.InteractionData.FromJsonOrNull <MediaWatchedInteractionData>();

            return(new MediaElementInteraction
            {
                InteractionGuid = interaction.Guid,
                MediaElementGuid = mediaElement.Guid,
                PersonGuid = interaction.PersonAlias.Person?.Guid,
                PersonAliasGuid = interaction.PersonAlias.Guid,
                RelatedEntityTypeId = interaction.RelatedEntityTypeId,
                RelatedEntityId = interaction.RelatedEntityId,
                WatchMap = data?.WatchMap ?? string.Empty
            });
        }
Esempio n. 4
0
        public IHttpActionResult PostInteractions([FromBody] List <MobileInteractionSession> sessions, Guid?personalDeviceGuid = null)
        {
            var person    = GetPerson();
            var ipAddress = System.Web.HttpContext.Current?.Request?.UserHostAddress;

            using (var rockContext = new Data.RockContext())
            {
                var interactionChannelService   = new InteractionChannelService(rockContext);
                var interactionComponentService = new InteractionComponentService(rockContext);
                var interactionSessionService   = new InteractionSessionService(rockContext);
                var interactionService          = new InteractionService(rockContext);
                var userLoginService            = new UserLoginService(rockContext);
                var channelMediumTypeValue      = DefinedValueCache.Get(SystemGuid.DefinedValue.INTERACTIONCHANNELTYPE_WEBSITE);
                var pageEntityTypeId            = EntityTypeCache.Get(typeof(Model.Page)).Id;

                //
                // Check to see if we have a site and the API key is valid.
                //
                if (MobileHelper.GetCurrentApplicationSite() == null)
                {
                    return(StatusCode(System.Net.HttpStatusCode.Forbidden));
                }

                //
                // Get the personal device identifier if they provided it's unique identifier.
                //
                int?personalDeviceId = null;
                if (personalDeviceGuid.HasValue)
                {
                    personalDeviceId = new PersonalDeviceService(rockContext).GetId(personalDeviceGuid.Value);
                }

                //
                // Create a quick way to cache data since we have to loop twice.
                //
                var interactionComponentLookup = new Dictionary <string, int>();

                //
                // Helper method to get a cache key for looking up the component Id.
                //
                string GetComponentCacheKey(MobileInteraction mi)
                {
                    return($"{mi.AppId}:{mi.PageGuid}:{mi.ChannelGuid}:{mi.ChannelId}:{mi.ComponentId}:{mi.ComponentName}");
                }

                //
                // Interactions Components will now try to load from cache which
                // causes problems if we are inside a transaction. So first loop through
                // everything and make sure all our components and channels exist.
                //
                var prePassInteractions = sessions.SelectMany(a => a.Interactions)
                                          .DistinctBy(a => GetComponentCacheKey(a));

                //
                // It's safe to do this pre-pass outside the transaction since we are just creating
                // the channels and components (if necessary), which is going to have to be done at
                // at some point no matter what.
                //
                foreach (var mobileInteraction in prePassInteractions)
                {
                    //
                    // Lookup the interaction channel, and create it if it doesn't exist
                    //
                    if (mobileInteraction.AppId.HasValue && mobileInteraction.PageGuid.HasValue)
                    {
                        var site = SiteCache.Get(mobileInteraction.AppId.Value);
                        var page = PageCache.Get(mobileInteraction.PageGuid.Value);

                        if (site == null || page == null)
                        {
                            continue;
                        }

                        //
                        // Try to find an existing interaction channel.
                        //
                        var interactionChannelId = InteractionChannelCache.GetChannelIdByTypeIdAndEntityId(channelMediumTypeValue.Id, site.Id, site.Name, pageEntityTypeId, null);

                        //
                        // Get an existing or create a new component.
                        //
                        var interactionComponentId = InteractionComponentCache.GetComponentIdByChannelIdAndEntityId(interactionChannelId, page.Id, page.InternalName);

                        interactionComponentLookup.AddOrReplace(GetComponentCacheKey(mobileInteraction), interactionComponentId);
                    }
                    else if (mobileInteraction.ChannelId.HasValue || mobileInteraction.ChannelGuid.HasValue)
                    {
                        int?interactionChannelId = null;

                        if (mobileInteraction.ChannelId.HasValue)
                        {
                            interactionChannelId = mobileInteraction.ChannelId.Value;
                        }
                        else if (mobileInteraction.ChannelGuid.HasValue)
                        {
                            interactionChannelId = InteractionChannelCache.Get(mobileInteraction.ChannelGuid.Value)?.Id;
                        }

                        if (interactionChannelId.HasValue)
                        {
                            if (mobileInteraction.ComponentId.HasValue)
                            {
                                // Use the provided component identifier.
                                interactionComponentLookup.AddOrReplace(GetComponentCacheKey(mobileInteraction), mobileInteraction.ComponentId.Value);
                            }
                            else if (mobileInteraction.ComponentName.IsNotNullOrWhiteSpace())
                            {
                                int interactionComponentId;

                                // Get or create a new component with the details we have.
                                if (mobileInteraction.ComponentEntityId.HasValue)
                                {
                                    interactionComponentId = InteractionComponentCache.GetComponentIdByChannelIdAndEntityId(interactionChannelId.Value, mobileInteraction.ComponentEntityId, mobileInteraction.ComponentName);
                                }
                                else
                                {
                                    var interactionComponent = interactionComponentService.GetComponentByComponentName(interactionChannelId.Value, mobileInteraction.ComponentName);

                                    rockContext.SaveChanges();

                                    interactionComponentId = interactionComponent.Id;
                                }

                                interactionComponentLookup.AddOrReplace(GetComponentCacheKey(mobileInteraction), interactionComponentId);
                            }
                        }
                    }
                }

                //
                // Now wrap the actual interaction creation inside a transaction. We should
                // probably move this so it uses the InteractionTransaction class for better
                // performance. This is so we can inform the client that either everything
                // saved or that nothing saved. No partial saves here.
                //
                rockContext.WrapTransaction(() =>
                {
                    foreach (var mobileSession in sessions)
                    {
                        var interactionGuids         = mobileSession.Interactions.Select(i => i.Guid).ToList();
                        var existingInteractionGuids = interactionService.Queryable()
                                                       .Where(i => interactionGuids.Contains(i.Guid))
                                                       .Select(i => i.Guid)
                                                       .ToList();

                        //
                        // Loop through all interactions that don't already exist and add each one.
                        //
                        foreach (var mobileInteraction in mobileSession.Interactions.Where(i => !existingInteractionGuids.Contains(i.Guid)))
                        {
                            string cacheKey = GetComponentCacheKey(mobileInteraction);

                            if (!interactionComponentLookup.ContainsKey(cacheKey))
                            {
                                // Shouldn't happen, but just in case.
                                continue;
                            }

                            var interactionComponentId = interactionComponentLookup[cacheKey];

                            //
                            // Add the interaction
                            //
                            var interaction = interactionService.CreateInteraction(interactionComponentId,
                                                                                   mobileInteraction.EntityId,
                                                                                   mobileInteraction.Operation,
                                                                                   mobileInteraction.Summary,
                                                                                   mobileInteraction.Data,
                                                                                   person?.PrimaryAliasId,
                                                                                   RockDateTime.ConvertLocalDateTimeToRockDateTime(mobileInteraction.DateTime.LocalDateTime),
                                                                                   mobileSession.Application,
                                                                                   mobileSession.OperatingSystem,
                                                                                   mobileSession.ClientType,
                                                                                   null,
                                                                                   ipAddress,
                                                                                   mobileSession.Guid);

                            interaction.Guid                  = mobileInteraction.Guid;
                            interaction.PersonalDeviceId      = personalDeviceId;
                            interaction.RelatedEntityTypeId   = mobileInteraction.RelatedEntityTypeId;
                            interaction.RelatedEntityId       = mobileInteraction.RelatedEntityId;
                            interaction.ChannelCustom1        = mobileInteraction.ChannelCustom1;
                            interaction.ChannelCustom2        = mobileInteraction.ChannelCustom2;
                            interaction.ChannelCustomIndexed1 = mobileInteraction.ChannelCustomIndexed1;

                            interactionService.Add(interaction);

                            // Attempt to process this as a communication interaction.
                            ProcessCommunicationInteraction(mobileSession, mobileInteraction, rockContext);
                        }
                    }

                    rockContext.SaveChanges();
                });
            }

            return(Ok());
        }