/// <summary> /// Create interaction data /// </summary> private static void CreateInteractionData() { _interactionComponent = InteractionComponentCache.All().FirstOrDefault(i => i.InteractionChannel.Guid == SystemGuid.InteractionChannel.ROCK_RMS.AsGuid()); var existing = _interactionService.Queryable().Where(i => _personAliasIds.Contains(i.PersonAliasId.Value) && i.InteractionComponentId == _interactionComponent.Id ); _interactionService.DeleteRange(existing); for (var i = 0; i < COUNT; i++) { _interactionService.Add(new Interaction { InteractionComponentId = _interactionComponent.Id, PersonAliasId = _personAliasIds[i % NUMBER_OF_ALIASES], InteractionDateTime = RockDateTime.Today, ForeignKey = KEY, ForeignId = i }); } _rockContext.SaveChanges(true); }
/// <summary> /// Should the achievement type process attempts if the given source entity has been modified in some way. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="sourceEntity">The source entity.</param> /// <returns></returns> public override bool ShouldProcess(AchievementTypeCache achievementTypeCache, IEntity sourceEntity) { var interaction = sourceEntity as Interaction; if (interaction == null) { return(false); } var channel = GetInteractionChannelCache(achievementTypeCache); if (channel == null) { return(true); } var component = GetInteractionComponentCache(achievementTypeCache); if (component == null) { component = InteractionComponentCache.Get(interaction.InteractionComponentId); return(component.InteractionChannelId == channel.Id); } return(interaction.InteractionComponentId == component.Id); }
/// <summary> /// Gets the models from the delimited values. /// </summary> /// <param name="value">The value.</param> /// <param name="interactionChannel">The interactionChannel</param> /// <param name="interactionComponent">The interactionComponent</param> private void GetModelsFromAttributeValue(string value, out InteractionChannelCache interactionChannel, out InteractionComponentCache interactionComponent) { interactionChannel = null; interactionComponent = null; ParseDelimitedGuids(value, out var interactionChannelGuid, out var interactionComponentGuid); if (interactionChannelGuid.HasValue || interactionComponentGuid.HasValue) { if (interactionChannelGuid.HasValue) { interactionChannel = InteractionChannelCache.Get(interactionChannelGuid.Value); } if (interactionComponentGuid.HasValue) { interactionComponent = InteractionComponentCache.Get(interactionComponentGuid.Value); } } }
/// <summary> /// Reads new values entered by the user for the field ( as Guid ) /// </summary> /// <param name="control">Parent control that controls were added to in the CreateEditControl() method</param> /// <param name="configurationValues">The configuration values.</param> /// <returns></returns> public override string GetEditValue(Control control, Dictionary <string, ConfigurationValue> configurationValues) { var interactionChannelInteractionComponentPicker = control as InteractionChannelInteractionComponentPicker; if (interactionChannelInteractionComponentPicker != null) { var rockContext = new RockContext(); Guid?interactionChannelGuid = null; Guid?interactionComponentGuid = null; if (interactionChannelInteractionComponentPicker.InteractionChannelId.HasValue) { var channel = InteractionChannelCache.Get(interactionChannelInteractionComponentPicker.InteractionChannelId.Value); if (channel != null) { interactionChannelGuid = channel.Guid; } } if (interactionChannelInteractionComponentPicker.InteractionComponentId.HasValue) { var component = InteractionComponentCache.Get(interactionChannelInteractionComponentPicker.InteractionComponentId.Value); if (component != null) { interactionComponentGuid = component.Guid; } } if (interactionChannelGuid.HasValue || interactionComponentGuid.HasValue) { return(string.Format("{0}|{1}", interactionChannelGuid, interactionComponentGuid)); } } return(null); }
/// <summary> /// BulkInserts Interaction Records /// </summary> /// <remarks> /// If any PersonAliasId references a PersonAliasId record that doesn't exist, the field value will be set to null. /// Also, if the InteractionComponent Id (or Guid) is specified, but references a Interaction Component record that doesn't exist /// the Interaction will not be recorded. /// </remarks> /// <param name="interactionsImport">The interactions import.</param> internal static void BulkInteractionImport(InteractionsImport interactionsImport) { if (interactionsImport == null) { throw new Exception("InteractionsImport must be assigned a value."); } var interactionImportList = interactionsImport.Interactions; if (interactionImportList == null || !interactionImportList.Any()) { // if there aren't any return return; } /* 2020-05-14 MDP * Make sure that all the PersonAliasIds in the import exist in the database. * For performance reasons, look them up all at one and keep a list of valid ones. * * If there are any PersonAliasIds that aren't valid, * we decided that just set the PersonAliasId to null (we want ignore bad data). */ HashSet <int> validPersonAliasIds = interactionsImport.GetValidPersonAliasIds(); List <Interaction> interactionsToInsert = new List <Interaction>(); foreach (InteractionImport interactionImport in interactionImportList) { if (interactionImport.Interaction == null) { throw new ArgumentNullException("InteractionImport.Interaction can not be null"); } // Determine which Channel this should be set to if (interactionImport.InteractionChannelId.HasValue) { // make sure it is a valid Id interactionImport.InteractionChannelId = InteractionChannelCache.Get(interactionImport.InteractionChannelId.Value)?.Id; } // Determine which Channel Type Medium this should be set to if (interactionImport.InteractionChannelChannelTypeMediumValueId.HasValue) { // make sure it is a valid Id interactionImport.InteractionChannelChannelTypeMediumValueId = DefinedValueCache.Get(interactionImport.InteractionChannelChannelTypeMediumValueId.Value)?.Id; } if (!interactionImport.InteractionChannelChannelTypeMediumValueId.HasValue) { if (interactionImport.InteractionChannelChannelTypeMediumValueGuid.HasValue) { interactionImport.InteractionChannelChannelTypeMediumValueId = DefinedValueCache.GetId(interactionImport.InteractionChannelChannelTypeMediumValueGuid.Value); } } if (!interactionImport.InteractionChannelId.HasValue) { if (interactionImport.InteractionChannelGuid.HasValue) { interactionImport.InteractionChannelId = InteractionChannelCache.GetId(interactionImport.InteractionChannelGuid.Value); } // if InteractionChannelId is still null, lookup (or create) an InteractionChannel from InteractionChannelForeignKey (if it is specified) if (interactionImport.InteractionChannelId == null && interactionImport.InteractionChannelForeignKey.IsNotNullOrWhiteSpace()) { interactionImport.InteractionChannelId = InteractionChannelCache.GetCreateChannelIdByForeignKey(interactionImport.InteractionChannelForeignKey, interactionImport.InteractionChannelName, interactionImport.InteractionChannelChannelTypeMediumValueId); } else { /* 2020-05-14 MDP * Discussed this and decided that if we tried InteractionChannelId and InteractionChannelGuid, and InteractionChannelForeignKey was not specified, * we'll just skip over this record */ continue; } } // Determine which Component this should be set to if (interactionImport.InteractionComponentId.HasValue) { // make sure it is a valid Id interactionImport.InteractionComponentId = InteractionComponentCache.Get(interactionImport.InteractionComponentId.Value)?.Id; } if (!interactionImport.InteractionComponentId.HasValue) { if (interactionImport.InteractionComponentGuid.HasValue) { interactionImport.InteractionComponentId = InteractionComponentCache.GetId(interactionImport.InteractionComponentGuid.Value); } // if InteractionComponentId is still null, lookup (or create) an InteractionComponent from the ForeignKey and ChannelId if (interactionImport.InteractionComponentForeignKey.IsNotNullOrWhiteSpace()) { interactionImport.InteractionComponentId = InteractionComponentCache.GetComponentIdByForeignKeyAndChannelId( interactionImport.InteractionComponentForeignKey, interactionImport.InteractionChannelId.Value, interactionImport.InteractionComponentName); } else { /* 2020-05-14 MDP * Discussed this and decided that and if we tried InteractionComponentId and InteractionComponentGuid, and InteractionComponentForeignKey was not specified, * we'll just skip over this record */ continue; } } } foreach (InteractionImport interactionImport in interactionImportList.Where(a => a.InteractionComponentId.HasValue)) { Interaction interaction = new Interaction { InteractionComponentId = interactionImport.InteractionComponentId.Value }; interaction.InteractionDateTime = interactionImport.Interaction.InteractionDateTime; // if operation is over 25, truncate it interaction.Operation = interactionImport.Interaction.Operation.Truncate(25); interaction.InteractionComponentId = interactionImport.InteractionComponentId.Value; interaction.EntityId = interactionImport.Interaction.EntityId; if (interactionImport.Interaction.RelatedEntityTypeId.HasValue) { /* 2020-05-14 MDP * We want to ignore bad data, so first see if the RelatedEntityTypeId exists by looking it up in a cache. * If it doesn't exist, it'll set RelatedEntityTypeId to null (so that we don't get a database constraint error) */ interaction.RelatedEntityTypeId = EntityTypeCache.Get(interactionImport.Interaction.RelatedEntityTypeId.Value)?.Id; } interaction.RelatedEntityId = interactionImport.Interaction.RelatedEntityId; if (interactionImport.Interaction.PersonAliasId.HasValue) { /* 2020-05-14 MDP * We want to ignore bad data, so see if the specified PersonAliasId exists in the validPersonAliasIds that we lookup up * If it doesn't exist, we'll leave interaction.PersonAliasId null (so that we don't get a database constraint error) */ if (validPersonAliasIds.Contains(interactionImport.Interaction.PersonAliasId.Value)) { interaction.PersonAliasId = interactionImport.Interaction.PersonAliasId.Value; } } // BulkImport doesn't include Session information TODO??? interaction.InteractionSessionId = null; // if the summary is over 500 chars, truncate with addEllipsis=true interaction.InteractionSummary = interactionImport.Interaction.InteractionSummary.Truncate(500, true); interaction.InteractionData = interactionImport.Interaction.InteractionData; interaction.PersonalDeviceId = interactionImport.Interaction.PersonalDeviceId; interaction.InteractionEndDateTime = interactionImport.Interaction.InteractionEndDateTime; // Campaign related fields, we'll truncate those if they are too long interaction.Source = interactionImport.Interaction.Source.Truncate(25); interaction.Medium = interactionImport.Interaction.Medium.Truncate(25); interaction.Campaign = interactionImport.Interaction.Campaign.Truncate(50); interaction.Content = interactionImport.Interaction.Content.Truncate(50); interaction.Term = interactionImport.Interaction.Term.Truncate(50); interaction.ForeignId = interactionImport.Interaction.ForeignId; interaction.ForeignKey = interactionImport.Interaction.ForeignKey; interaction.ForeignGuid = interactionImport.Interaction.ForeignGuid; interaction.ChannelCustom1 = interactionImport.Interaction.ChannelCustom1.Truncate(500, true); interaction.ChannelCustom2 = interactionImport.Interaction.ChannelCustom2.Truncate(2000, true); interaction.ChannelCustomIndexed1 = interactionImport.Interaction.ChannelCustomIndexed1.Truncate(500, true); interaction.InteractionLength = interactionImport.Interaction.InteractionLength; interaction.InteractionTimeToServe = interactionImport.Interaction.InteractionTimeToServe; interactionsToInsert.Add(interaction); } using (var rockContext = new RockContext()) { rockContext.BulkInsert(interactionsToInsert); } // This logic is normally handled in the Interaction.PostSave method, but since the BulkInsert bypasses those // model hooks, streaks need to be updated here. Also, it is not necessary for this logic to complete before this // transaction can continue processing and exit, so update the streak using a task. // Only launch this task if there are StreakTypes configured that have interactions. Otherwise several // database calls are made only to find out there are no streak types defined. if (StreakTypeCache.All().Any(s => s.IsInteractionRelated)) { // Ids do not exit for the interactions in the collection since they were bulk imported. // Read their ids from their guids and append the id. var insertedGuids = interactionsToInsert.Select(i => i.Guid).ToList(); var interactionIds = new InteractionService(new RockContext()).Queryable() .Where(i => insertedGuids.Contains(i.Guid)) .Select(i => new { i.Id, i.Guid }) .ToList(); foreach (var interactionId in interactionIds) { var interaction = interactionsToInsert.Where(i => i.Guid == interactionId.Guid).FirstOrDefault(); if (interaction != null) { interaction.Id = interactionId.Id; } } // Launch task interactionsToInsert.ForEach(i => Task.Run(() => StreakTypeService.HandleInteractionRecord(i.Id))); } }
/// <summary> /// Gets the component. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="channel">The channel.</param> /// <param name="entityId">The entity identifier.</param> /// <param name="identifier">The identifier.</param> /// <returns></returns> private InteractionComponentCache GetComponent(RockContext rockContext, InteractionChannelCache channel, int?entityId, string identifier) { if (channel != null) { if (entityId.HasValue) { // Find by the Entity Id int?interactionComponentId = new InteractionComponentService(rockContext) .Queryable() .AsNoTracking() .Where(c => c.EntityId.HasValue && c.InteractionChannelId == channel.Id && c.EntityId.Value == entityId.Value) .Select(c => c.Id) .Cast <int?>() .FirstOrDefault(); if (interactionComponentId != null) { return(InteractionComponentCache.Get(interactionComponentId.Value)); } } if (identifier.IsNotNullOrWhiteSpace()) { // Find by Id int?id = identifier.AsIntegerOrNull(); if (id.HasValue) { var component = InteractionComponentCache.Get(id.Value); if (component != null && component.InteractionChannelId == channel.Id) { return(component); } } // Find by Guid Guid?guid = identifier.AsGuidOrNull(); if (guid.HasValue) { var component = InteractionComponentCache.Get(guid.Value); if (component != null && component.InteractionChannelId == channel.Id) { return(component); } } if (!id.HasValue && !guid.HasValue) { // Find by Name int?interactionComponentId = new InteractionComponentService(rockContext) .Queryable() .AsNoTracking() .Where(c => c.InteractionChannelId == channel.Id) .Where(c => c.Name.Equals(identifier, StringComparison.OrdinalIgnoreCase)) .Select(c => c.Id) .Cast <int?>() .FirstOrDefault(); if (interactionComponentId != null) { return(InteractionComponentCache.Get(interactionComponentId.Value)); } // If still no match, and we have a name, create a new channel using (var newRockContext = new RockContext()) { var interactionComponent = new InteractionComponent(); interactionComponent.Name = identifier; interactionComponent.InteractionChannelId = channel.Id; new InteractionComponentService(newRockContext).Add(interactionComponent); newRockContext.SaveChanges(); return(InteractionComponentCache.Get(interactionComponent.Id)); } } } } return(null); }
/// <summary> /// Updates any Cache Objects that are associated with this entity /// </summary> /// <param name="entityState">State of the entity.</param> /// <param name="dbContext">The database context.</param> public void UpdateCache(EntityState entityState, Rock.Data.DbContext dbContext) { InteractionComponentCache.UpdateCachedEntity(this.Id, this.SaveState); }
/// <summary> /// Gets the cache object associated with this Entity /// </summary> /// <returns></returns> public IEntityCache GetCacheObject() { return(InteractionComponentCache.Get(this.Id)); }
/// <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); }
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 }); }
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); }
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()); }
/// <summary> /// Gets the interaction component cache. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <returns></returns> private InteractionComponentCache GetInteractionComponentCache(AchievementTypeCache achievementTypeCache) { var guid = GetInteractionComponentGuid(achievementTypeCache); return(guid.HasValue ? InteractionComponentCache.Get(guid.Value) : null); }