Beispiel #1
0
        /// <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();
            }
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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;
        }
Beispiel #5
0
        /// <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);
            }
        }
Beispiel #6
0
        /// <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()));
        }
Beispiel #7
0
        /// <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));
        }
Beispiel #8
0
 /// <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));
 }
Beispiel #9
0
        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));
            }
        }
Beispiel #11
0
        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));
        }
Beispiel #12
0
        /// <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();
        }
Beispiel #13
0
        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);
        }
Beispiel #15
0
        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));
        }
Beispiel #16
0
        /// <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)));
            }
        }
Beispiel #19
0
        /// <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);
        }
Beispiel #20
0
 /// <summary>
 /// Gets the cache object associated with this Entity
 /// </summary>
 /// <returns></returns>
 public IEntityCache GetCacheObject()
 {
     return(StreakTypeCache.Get(Id));
 }
Beispiel #21
0
 /// <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);
 }
Beispiel #22
0
        /// <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() }
            });
        }
Beispiel #23
0
        /// <summary>
        /// Gets the streak type cache.
        /// </summary>
        /// <returns></returns>
        private StreakTypeCache GetStreakTypeCache()
        {
            var streakType = GetStreakType();

            return(streakType == null ? null : StreakTypeCache.Get(streakType.Id));
        }