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> /// 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); }
/// <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> /// 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> /// 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())); }
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> /// Gets the cache object associated with this Entity /// </summary> /// <returns></returns> public IEntityCache GetCacheObject() { return(StreakTypeCache.Get(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> /// 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)); }