/// <summary> /// Check for achievements that may have been earned /// </summary> /// <param name="streakId">The streak identifier.</param> private static void ProcessAchievements(int streakId) { var rockContext = new RockContext(); var streakService = new StreakService(rockContext); var streak = streakService.Get(streakId); if (streak == null) { ExceptionLogService.LogException($"The streak with id {streakId} was not found (it may have been deleted)"); return; } var streakTypeCache = StreakTypeCache.Get(streak.StreakTypeId); /* * 2019-01-13 BJW * * Achievements need to be processed in order according to dependencies (prerequisites). Prerequisites should be processed first so that, * if the prerequisite becomes completed, the dependent achievement will be processed at this time as well. Furthermore, each achievement * needs to be processed and changes saved to the database so that subsequent achievements will see the changes (for example: now met * prerequisites). */ var sortedAchievementTypes = StreakTypeAchievementTypeService.SortAccordingToPrerequisites(streakTypeCache.StreakTypeAchievementTypes); foreach (var streakTypeAchievementTypeCache in sortedAchievementTypes) { var loopRockContext = new RockContext(); var component = streakTypeAchievementTypeCache.AchievementComponent; component.Process(loopRockContext, streakTypeAchievementTypeCache, streak); loopRockContext.SaveChanges(); } }
public virtual IQueryable <Schedule> GetLocationSchedules(int streakTypeId, int locationId) { // Make sure the streak type exists var streakTypeCache = StreakTypeCache.Get(streakTypeId); if (streakTypeCache == null) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "The streakTypeId did not resolve"); throw new HttpResponseException(errorResponse); } // Get the schedules from the service var streakTypeService = Service as StreakTypeService; var schedules = streakTypeService.GetLocationSchedules(streakTypeCache, locationId, out var errorMessage); if (!errorMessage.IsNullOrWhiteSpace()) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, errorMessage); throw new HttpResponseException(errorResponse); } if (schedules == null) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "The schedule retrieval was not successful but no error was specified"); throw new HttpResponseException(errorResponse); } return(schedules); }
public virtual List <StreakData> GetStreakData(string streakTypeIdList, [FromUri] int?personId = null, [FromUri] DateTime?startDate = null, [FromUri] DateTime?endDate = null, [FromUri] bool createObjectArray = false, [FromUri] bool includeBitMaps = false, [FromUri] int?maxStreaksToReturn = null) { if (streakTypeIdList.IsNullOrWhiteSpace()) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "The streak type identifier list is required"); throw new HttpResponseException(errorResponse); } var streakTypeIds = streakTypeIdList.SplitDelimitedValues().AsIntegerList().Distinct().ToList(); if (!streakTypeIds.Any()) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "At least one streak type identifier is required"); throw new HttpResponseException(errorResponse); } // If not specified, use the current person id if (!personId.HasValue) { personId = GetCurrentPersonId(); } // Return a list of the results (one for each id) var streakTypeService = Service as StreakTypeService; var streakDataList = new List <StreakData>(streakTypeIds.Count); foreach (var streakTypeId in streakTypeIds) { // Make sure the streak type exists var streakTypeCache = StreakTypeCache.Get(streakTypeId); if (streakTypeCache == null) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, $"The streak type id '{streakTypeId}' did not resolve"); throw new HttpResponseException(errorResponse); } // Get the data from the service var streakData = streakTypeService.GetStreakData(streakTypeCache, personId.Value, out var errorMessage, startDate, endDate, createObjectArray, includeBitMaps, maxStreaksToReturn); if (!errorMessage.IsNullOrWhiteSpace()) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, errorMessage); throw new HttpResponseException(errorResponse); } if (streakData == null) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "The streak data calculation was not successful but no error was specified"); throw new HttpResponseException(errorResponse); } streakDataList.Add(streakData); } return(streakDataList); }
/// <summary> /// Gets the achievement types. /// </summary> /// <returns></returns> private void SetTitlePrefix() { var streakTypeId = PageParameter(PageParamKey.StreakTypeId).AsIntegerOrNull(); var streakTypeCache = StreakTypeCache.Get(streakTypeId ?? 0); lTitlePrefix.Text = streakTypeCache == null ? string.Empty : streakTypeCache.Name; }
/// <summary> /// Loads the drop down items. /// </summary> /// <param name="picker">The picker.</param> /// <param name="includeEmptyOption">if set to <c>true</c> [include empty option].</param> public static void LoadDropDownItems(IStreakTypePicker picker, bool includeEmptyOption) { var selectedItems = picker.Items.Cast <ListItem>() .Where(i => i.Selected) .Select(i => i.Value).AsIntegerList(); picker.Items.Clear(); if (includeEmptyOption) { // add Empty option first picker.Items.Add(new ListItem()); } var streakTypes = StreakTypeCache.All() .Where(st => st.IsActive) .OrderBy(st => st.Name) .ThenBy(st => st.Id) .ToList(); foreach (var streakType in streakTypes) { var li = new ListItem(streakType.Name, streakType.Id.ToString()); li.Selected = selectedItems.Contains(streakType.Id); picker.Items.Add(li); } }
/// <summary> /// Gets the achievement type cache. /// </summary> /// <returns></returns> private StreakTypeCache GetStreakTypeCache() { var achievementTypeCache = GetAchievementTypeCache(); if (achievementTypeCache != null) { return(achievementTypeCache.StreakTypeCache); } return(StreakTypeCache.Get(PageParameter(PageParameterKey.StreakTypeId).AsInteger())); }
/// <summary> /// Get the streak type described by the attribute value /// </summary> /// <param name="badge"></param> /// <returns></returns> private StreakTypeCache GetStreakTypeCache(BadgeCache badge) { var streakTypeGuid = GetAttributeValue(badge, AttributeKey.StreakType).AsGuidOrNull(); if (!streakTypeGuid.HasValue) { return(null); } return(StreakTypeCache.Get(streakTypeGuid.Value)); }
/// <summary> /// Returns a dictionary of the items available for selection. /// </summary> /// <returns></returns> protected override Dictionary <Guid, string> OnGetItemList() { return(StreakTypeCache.All() .Where(s => s.IsActive) .OrderBy(s => s.Name) .Select(s => new { s.Guid, s.Name, }) .ToDictionary(s => s.Guid, s => s.Name)); }
public virtual HttpResponseMessage Enroll(int streakTypeId, [FromUri] int?personId = null, [FromUri] DateTime?enrollmentDate = null, [FromUri] int?locationId = null) { // Make sure the streak type exists var streakTypeCache = StreakTypeCache.Get(streakTypeId); if (streakTypeCache == null) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "The streakTypeId did not resolve"); throw new HttpResponseException(errorResponse); } // If not specified, use the current person id var rockContext = Service.Context as RockContext; if (!personId.HasValue) { personId = GetPerson(rockContext)?.Id; if (!personId.HasValue) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "The personId for the current user did not resolve"); throw new HttpResponseException(errorResponse); } } // Create the enrollment var streakTypeService = Service as StreakTypeService; var streak = streakTypeService.Enroll(streakTypeCache, personId.Value, out var errorMessage, enrollmentDate, locationId); if (!errorMessage.IsNullOrWhiteSpace()) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, errorMessage); throw new HttpResponseException(errorResponse); } if (streak == null) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "The enrollment was not successful but no error was specified"); throw new HttpResponseException(errorResponse); } // Save to the DB and tell the user the new id rockContext.SaveChanges(); return(ControllerContext.Request.CreateResponse(HttpStatusCode.Created, streak.Id)); }
public virtual HttpResponseMessage MarkAttendanceEngagement(int streakTypeId, [FromBody] AttendanceEngagementArgs attendanceEngagementArgs, [FromUri] int?personId = null, [FromUri] DateTime?dateOfEngagement = null, [FromUri] bool returnAchievements = false) { // Make sure the streak type exists var streakTypeCache = StreakTypeCache.Get(streakTypeId); if (streakTypeCache == null) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "The streak type id did not resolve"); throw new HttpResponseException(errorResponse); } // If not specified, use the current person id if (!personId.HasValue) { personId = GetCurrentPersonId(); } // Mark the engagement var streakTypeService = Service as StreakTypeService; streakTypeService.MarkAttendanceEngagement(streakTypeCache, personId.Value, attendanceEngagementArgs, out var errorMessage, dateOfEngagement); if (!errorMessage.IsNullOrWhiteSpace()) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, errorMessage); throw new HttpResponseException(errorResponse); } // Save to the DB var rockContext = Service.Context as RockContext; var result = rockContext.SaveChanges(new SaveChangesArgs { IsAchievementsEnabled = returnAchievements }); if (returnAchievements) { return(ControllerContext.Request.CreateResponse(HttpStatusCode.Created, new MarkEngagementResponse(result.AchievementAttempts))); } else { return(ControllerContext.Request.CreateResponse(HttpStatusCode.Created)); } }
public virtual HttpResponseMessage MarkEngagement(int streakTypeId, [FromUri] int?personId = null, [FromUri] DateTime?dateOfEngagement = null, [FromUri] int?groupId = null, [FromUri] int?locationId = null, [FromUri] int?scheduleId = null) { // Make sure the streak type exists var streakTypeCache = StreakTypeCache.Get(streakTypeId); if (streakTypeCache == null) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "The streak type id did not resolve"); throw new HttpResponseException(errorResponse); } // If not specified, use the current person id var rockContext = Service.Context as RockContext; if (!personId.HasValue) { personId = GetPerson(rockContext)?.Id; if (!personId.HasValue) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "The personId for the current user did not resolve"); throw new HttpResponseException(errorResponse); } } // Get the data from the service var streakTypeService = Service as StreakTypeService; streakTypeService.MarkEngagement(streakTypeCache, personId.Value, out var errorMessage, dateOfEngagement, groupId, locationId, scheduleId); if (!errorMessage.IsNullOrWhiteSpace()) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, errorMessage); throw new HttpResponseException(errorResponse); } // Save to the DB rockContext.SaveChanges(); return(ControllerContext.Request.CreateResponse(HttpStatusCode.Created)); }
/// <summary> /// Check for achievements that may have been earned /// </summary> /// <param name="streakId">The streak identifier.</param> private static void ProcessAchievements(int streakId) { var rockContext = new RockContext(); var streakService = new StreakService(rockContext); var streak = streakService.Get(streakId); if (streak == null) { ExceptionLogService.LogException($"The streak with id {streakId} was not found (it may have been deleted)"); return; } var streakTypeCache = StreakTypeCache.Get(streak.StreakTypeId); foreach (var streakTypeAchievementTypeCache in streakTypeCache.StreakTypeAchievementTypes) { var component = streakTypeAchievementTypeCache.AchievementComponent; component.Process(rockContext, streakTypeAchievementTypeCache, streak); } rockContext.SaveChanges(); }
public virtual HttpResponseMessage MarkInteractionEngagement(int streakTypeId, [FromBody] InteractionEngagementArgs interactionEngagementArgs, [FromUri] int?personId = null, [FromUri] DateTime?dateOfEngagement = null) { // Make sure the streak type exists var streakTypeCache = StreakTypeCache.Get(streakTypeId); if (streakTypeCache == null) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "The streak type id did not resolve"); throw new HttpResponseException(errorResponse); } // If not specified, use the current person id if (!personId.HasValue) { personId = GetCurrentPersonId(); } // Mark the engagement var streakTypeService = Service as StreakTypeService; streakTypeService.MarkInteractionEngagement(streakTypeCache, personId.Value, interactionEngagementArgs, out var errorMessage, dateOfEngagement); if (!errorMessage.IsNullOrWhiteSpace()) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, errorMessage); throw new HttpResponseException(errorResponse); } // Save to the DB var rockContext = Service.Context as RockContext; rockContext.SaveChanges(); return(ControllerContext.Request.CreateResponse(HttpStatusCode.Created)); }
/// <summary> /// Create new attempt records and return them in a list. All new attempts should be after the most recent successful attempt. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="streak">The streak.</param> /// <param name="mostRecentClosedAttempt">The most recent closed attempt.</param> /// <returns></returns> protected override List <AchievementAttempt> CreateNewAttempts(AchievementTypeCache achievementTypeCache, Streak streak, AchievementAttempt mostRecentClosedAttempt) { var rockContext = new RockContext(); var streakTypeService = new StreakTypeService(rockContext); var streakTypeCache = StreakTypeCache.Get(streak.StreakTypeId); // Validate the attribute values var numberToAchieve = GetAttributeValue(achievementTypeCache, AttributeKey.NumberToAchieve).AsInteger(); if (numberToAchieve <= 0) { ExceptionLogService.LogException($"StreakAchievement.CreateNewAttempts cannot process because the NumberToAchieve attribute is less than 1"); return(null); } var attributeTimespanDays = GetAttributeValue(achievementTypeCache, AttributeKey.TimespanInDays).AsIntegerOrNull(); if (attributeTimespanDays.HasValue && attributeTimespanDays.Value <= 0) { ExceptionLogService.LogException($"StreakAchievement.CreateNewAttempts cannot process because the TimespanInDays attribute is less than 1"); return(null); } // Calculate the date range where new achievements can be validly found var attributeMinDate = GetAttributeValue(achievementTypeCache, AttributeKey.StartDateTime).AsDateTime(); var attributeMaxDate = GetAttributeValue(achievementTypeCache, AttributeKey.EndDateTime).AsDateTime(); var minDate = CalculateMinDateForAchievementAttempt(streak.EnrollmentDate, mostRecentClosedAttempt, attributeMinDate, numberToAchieve); var maxDate = CalculateMaxDateForAchievementAttempt(minDate, attributeMaxDate); // Get the max date that streaks can be broken. This is to avoid breaking streaks while people still have time to // engage in that day or week (because it is the current day or week) var maxDateForStreakBreaking = StreakTypeService.GetMaxDateForStreakBreaking(streakTypeCache); // Track the attempts in a list that will be returned. The int is the streak count for that attempt var attempts = new List <AchievementAttempt>(); var streaks = new List <ComputedStreak>(); // Define what happens for each bit in the date range bool iterationAction(int currentUnit, DateTime currentDate, bool hasOccurrence, bool hasEngagement, bool hasExclusion) { // If there is an engagement and a timespan, then this is a possible attempt. If there is no timespan then only one // attempt needs to be tracked at a time if (hasOccurrence && hasEngagement && (attributeTimespanDays.HasValue || !streaks.Any())) { streaks.Add(new ComputedStreak(currentDate)); } else if (hasOccurrence && !hasEngagement && !hasExclusion && streaks.Any()) { // Break the streaks and close an attempt if there is an unexcused absence var longestStreak = streaks.First(); attempts.Add(GetAttemptFromStreak(longestStreak, numberToAchieve, currentDate <= maxDateForStreakBreaking)); streaks.Clear(); return(false); } for (var i = streaks.Count - 1; i >= 0; i--) { var computedStreak = streaks[i]; if (hasOccurrence && hasEngagement) { // Increment the streak computedStreak.Count++; computedStreak.EndDate = currentDate; // Check for a fulfilled attempt if (computedStreak.Count >= numberToAchieve) { streaks.Clear(); if (achievementTypeCache.AllowOverAchievement) { streaks.Add(computedStreak); i = 0; } else { attempts.Add(GetAttemptFromStreak(computedStreak, numberToAchieve, !achievementTypeCache.AllowOverAchievement)); break; } } } // If there is a timespan and this streak is too old, then the attempt is closed if (attributeTimespanDays.HasValue) { var inclusiveAge = (currentDate - computedStreak.StartDate).Days + 1; if (inclusiveAge >= attributeTimespanDays.Value) { var timedOutAttempt = GetAttemptFromStreak(computedStreak, numberToAchieve, true); attempts.Add(timedOutAttempt); streaks.RemoveAt(i); // Remove more recently started streaks that started before the next valid start date (based // on the deficiency of this timed out attempt) var nextValidStartDate = CalculateMinDateForAchievementAttempt(streak.EnrollmentDate, timedOutAttempt, attributeMinDate, numberToAchieve); for (var j = streaks.Count - 1; j >= i; j--) { var moreRecentStreak = streaks[j]; if (moreRecentStreak.StartDate < nextValidStartDate) { streaks.RemoveAt(j); } } } } } return(false); } // Iterate through the streak date for the date range specified streakTypeService.IterateStreakMap(streakTypeCache, streak.PersonAliasId, minDate, maxDate, iterationAction, out var errorMessage); if (!errorMessage.IsNullOrWhiteSpace()) { ExceptionLogService.LogException($"StreakAchievement.CreateNewAttempts got an error calling StreakTypeService.IterateStreakMap: {errorMessage}"); return(null); } // The longest leftover streak is an open attempt if (streaks.Any()) { var longestStreak = streaks.First(); attempts.Add(GetAttemptFromStreak(longestStreak, numberToAchieve, false)); } return(attempts); }
public virtual HttpResponseMessage MarkEngagement(int streakTypeId, [FromUri] int?personId = null, [FromUri] DateTime?dateOfEngagement = null, [FromUri] int?groupId = null, [FromUri] int?locationId = null, [FromUri] int?scheduleId = null) { /* 1/31/2020 BJW * * Originally, streaks were set-up to be driven by attendance only, which is where this method with all the optional * parameters was introduced. At the date of this note, we are adding support to use interactions to drive streak data. Because generating * an interaction requires a distinct set of arguments from generating an attendance, and because those arguments could become * large when considering the InteractionData field, we opted to use a [FromBody] argument object. Then for standardization sake, * we opted to transform the attendance mark engagement method to also use a [FromBody] argument object. * * In v13, the schedule and group id params should be removed but not actually the whole method. We are giving a long obsolete * warning period since this is an API endpoint that developers won't get a compiler warning about. * * Task: https://app.asana.com/0/1120115219297347/1159048461540337/f */ // Make sure the streak type exists var streakTypeCache = StreakTypeCache.Get(streakTypeId); if (streakTypeCache == null) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "The streak type id did not resolve"); throw new HttpResponseException(errorResponse); } // If not specified, use the current person id if (!personId.HasValue) { personId = GetCurrentPersonId(); } // Mark the engagement var streakTypeService = Service as StreakTypeService; var errorMessage = string.Empty; if (scheduleId.HasValue || groupId.HasValue) { // This condition should be removed in v13 as these params will be removed var attendanceEngagementArgs = new AttendanceEngagementArgs { GroupId = groupId, LocationId = locationId, ScheduleId = scheduleId }; streakTypeService.MarkAttendanceEngagement(streakTypeCache, personId.Value, attendanceEngagementArgs, out errorMessage, dateOfEngagement); } else { streakTypeService.MarkEngagement(streakTypeCache, personId.Value, out errorMessage, dateOfEngagement, locationId); } if (!errorMessage.IsNullOrWhiteSpace()) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, errorMessage); throw new HttpResponseException(errorResponse); } // Save to the DB var rockContext = Service.Context as RockContext; rockContext.SaveChanges(); return(ControllerContext.Request.CreateResponse(HttpStatusCode.Created)); }
/// <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> /// Returns a collection of Achievement Types that can be selected as prerequisites for a new Achievement Type. /// </summary> /// <param name="streakTypeCache">The streak type cache.</param> /// <returns></returns> public static List <StreakTypeAchievementTypeCache> GetEligiblePrerequisiteAchievementTypeCaches(StreakTypeCache streakTypeCache) { return(StreakTypeAchievementTypeCache.All() .Where(stat => stat.IsActive && stat.StreakTypeId == streakTypeCache.Id) .ToList()); }
/// <summary> /// Logs the interactions. /// </summary> /// <param name="interactionTransactionInfos">The interaction transaction infos to process.</param> /// <param name="rockContext">The rock context.</param> private void LogInteractions(List <InteractionTransactionInfo> interactionTransactionInfos, RockContext rockContext) { List <Interaction> interactionsToInsert = new List <Interaction>(); var interactionService = new InteractionService(rockContext); foreach (var info in interactionTransactionInfos.Where(a => a.InteractionComponentId.HasValue)) { /* * 2020-06-29 - JH * * The 'CreateInteraction(...)' method called below sets the following properties on the Interaction object: * * - InteractionComponentId * - InteractionDateTime (but with the wrong value) * - InteractionSessionId * - Source * - Medium * - Campaign * - Content * - Term */ var interaction = interactionService.CreateInteraction(info.InteractionComponentId.Value, info.UserAgent, info.InteractionData, info.IPAddress, info.BrowserSessionId); // The rest of the properties need to be manually set. interaction.EntityId = info.InteractionEntityId; interaction.Operation = info.InteractionOperation.IsNotNullOrWhiteSpace() ? info.InteractionOperation.Trim() : "View"; interaction.InteractionSummary = info.InteractionSummary?.Trim(); interaction.PersonAliasId = info.PersonAliasId; interaction.InteractionDateTime = info.InteractionDateTime; interaction.InteractionTimeToServe = info.InteractionTimeToServe; interaction.RelatedEntityTypeId = info.InteractionRelatedEntityTypeId; interaction.RelatedEntityId = info.InteractionRelatedEntityId; interaction.ChannelCustom1 = info.InteractionChannelCustom1?.Trim(); interaction.ChannelCustom2 = info.InteractionChannelCustom2?.Trim(); interaction.ChannelCustomIndexed1 = info.InteractionChannelCustomIndexed1?.Trim(); interaction.Source = interaction.Source ?? info.InteractionSource?.Trim(); interaction.Medium = interaction.Medium ?? info.InteractionMedium?.Trim(); interaction.Campaign = interaction.Campaign ?? info.InteractionCampaign?.Trim(); interaction.Content = interaction.Content ?? info.InteractionContent?.Trim(); interaction.Term = interaction.Term ?? info.InteractionTerm?.Trim(); interaction.InteractionLength = info.InteractionLength; interaction.InteractionEndDateTime = info.InteractionEndDateTime; interaction.SetInteractionData(info.InteractionData?.Trim()); interactionsToInsert.Add(interaction); } 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 streak type cache. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <returns></returns> protected StreakTypeCache GetStreakTypeCache(AchievementTypeCache achievementTypeCache) { var streakTypeGuid = GetStreakTypeGuid(achievementTypeCache); return(streakTypeGuid.HasValue ? StreakTypeCache.Get(streakTypeGuid.Value) : null); }
/// <summary> /// Gets the cache object associated with this Entity /// </summary> /// <returns></returns> public IEntityCache GetCacheObject() { return(StreakTypeCache.Get(Id)); }
/// <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) { StreakTypeCache.UpdateCachedEntity(Id, entityState); }
/// <summary> /// Save the current record. /// </summary> /// <returns></returns> private void SaveRecord() { // Validate the streak type var streakType = GetStreakType(); if (streakType == null) { nbEditModeMessage.Text = "Streak Type is required."; return; } // Get the other non-required values var streakTypeService = GetStreakTypeService(); var streakTypeCache = StreakTypeCache.Get(streakType.Id); var enrollment = GetStreak(); var enrollmentDate = rdpEnrollmentDate.SelectedDate; var locationId = rlpLocation.Location != null ? rlpLocation.Location.Id : ( int? )null; // Add the new enrollment if we are adding if (enrollment == null) { // Validate the person var personId = rppPerson.PersonId; var personAliasId = rppPerson.PersonAliasId; if (!personId.HasValue || !personAliasId.HasValue) { nbEditModeMessage.Text = "Person is required."; return; } var errorMessage = string.Empty; enrollment = streakTypeService.Enroll(streakTypeCache, personId.Value, out errorMessage, enrollmentDate, locationId); if (!errorMessage.IsNullOrWhiteSpace()) { ShowValidationError(errorMessage); return; } if (enrollment == null) { nbEditModeMessage.Text = "Enrollment failed but no error was specified."; return; } } else { enrollment.LocationId = locationId; enrollment.EnrollmentDate = enrollmentDate ?? RockDateTime.Today; } if (!enrollment.IsValid) { var validationResult = enrollment.ValidationResults.FirstOrDefault(); var message = validationResult == null ? "The values entered are not valid." : validationResult.ErrorMessage; nbEditModeMessage.Text = message; return; } try { var rockContext = GetRockContext(); rockContext.SaveChanges(); if (!enrollment.IsAuthorized(Authorization.VIEW, CurrentPerson)) { enrollment.AllowPerson(Authorization.VIEW, CurrentPerson, rockContext); } if (!enrollment.IsAuthorized(Authorization.EDIT, CurrentPerson)) { enrollment.AllowPerson(Authorization.EDIT, CurrentPerson, rockContext); } if (!enrollment.IsAuthorized(Authorization.ADMINISTRATE, CurrentPerson)) { enrollment.AllowPerson(Authorization.ADMINISTRATE, CurrentPerson, rockContext); } } catch (Exception ex) { ShowBlockException(nbEditModeMessage, ex); return; } // If the save was successful, reload the page using the new record Id. NavigateToPage(RockPage.Guid, new Dictionary <string, string> { { PageParameterKey.StreakTypeId, streakType.Id.ToString() }, { PageParameterKey.StreakId, enrollment.Id.ToString() } }); }
/// <summary> /// Gets the streak type cache. /// </summary> /// <returns></returns> private StreakTypeCache GetStreakTypeCache() { var streakType = GetStreakType(); return(streakType == null ? null : StreakTypeCache.Get(streakType.Id)); }