/// <summary> /// Should the achievement type process attempts if the given source entity has been modified in some way. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="sourceEntity">The source entity.</param> /// <returns></returns> public override bool ShouldProcess(AchievementTypeCache achievementTypeCache, IEntity sourceEntity) { var interaction = sourceEntity as Interaction; if (interaction == null) { return(false); } var channel = GetInteractionChannelCache(achievementTypeCache); if (channel == null) { return(true); } var component = GetInteractionComponentCache(achievementTypeCache); if (component == null) { component = InteractionComponentCache.Get(interaction.InteractionComponentId); return(component.InteractionChannelId == channel.Id); } return(interaction.InteractionComponentId == component.Id); }
/// <summary> /// Gets the interaction channel unique identifier. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <returns></returns> private Guid?GetInteractionChannelGuid(AchievementTypeCache achievementTypeCache) { var delimited = GetAttributeValue(achievementTypeCache, AttributeKey.InteractionChannelComponent); var guids = delimited.SplitDelimitedValues().AsGuidOrNullList(); return(guids.FirstOrDefault()); }
/// <summary> /// Gets the transaction dates for the specified person. /// </summary> /// <param name="achievementTypeCache">The achievementTypeCache.</param> /// <param name="personId">The person alias identifier.</param> /// <param name="minDate">The minimum date.</param> /// <param name="maxDate">The maximum date.</param> /// <returns></returns> private List <DateTime?> GetOrderedFinancialTransactionDatesByPerson(AchievementTypeCache achievementTypeCache, int personId, DateTime minDate, DateTime maxDate) { var rockContext = new RockContext(); var query = GetSourceEntitiesQuery(achievementTypeCache, rockContext) as IQueryable <FinancialTransaction>; var dayAfterMaxDate = maxDate.AddDays(1); // Get the Person's Giving ID var givingId = new PersonService(rockContext).GetSelect(personId, p => p.GivingId); // fetch all the possible PersonAliasIds that have this GivingID to help optimize the SQL var personAliasQuery = new PersonAliasService(rockContext) .Queryable() .AsNoTracking() .Where(pa => pa.Person.GivingId == givingId) .Select(pa => pa.Id); return(query .AsNoTracking() .Where(i => i.AuthorizedPersonAliasId.HasValue && personAliasQuery.Contains(i.AuthorizedPersonAliasId.Value) && i.TransactionDateTime >= minDate && i.TransactionDateTime < dayAfterMaxDate) .Select(i => i.TransactionDateTime) .OrderBy(d => d) .ToList()); }
/// <summary> /// Renders the specified writer. /// </summary> /// <param name="badge">The badge.</param> /// <param name="writer">The writer.</param> public override void Render(BadgeCache badge, System.Web.UI.HtmlTextWriter writer) { var achievementTypeGuid = GetAchievementTypeGuid(badge); if (!achievementTypeGuid.HasValue) { return; } var achievementType = AchievementTypeCache.Get(achievementTypeGuid.Value); if (achievementType == null) { return; } var achiever = Entity; if (achievementType.AchieverEntityTypeId == EntityTypeCache.Get <PersonAlias>().Id&& Person != null) { // Translate this person badge to the person alias achievement achiever = Person.PrimaryAlias; } else if (achievementType.AchieverEntityTypeId != Entity.TypeId) { // This badge is not compatabile with this achievement return; } var domElementKey = GenerateBadgeKey(badge); var html = GetHtmlTemplate(domElementKey); writer.Write($"{html}"); }
/// <summary> /// Gets the java script. /// </summary> /// <param name="badge"></param> /// <returns></returns> protected override string GetJavaScript(BadgeCache badge) { var achievementTypeGuid = GetAchievementTypeGuid(badge); if (!achievementTypeGuid.HasValue) { return(null); } var achievementType = AchievementTypeCache.Get(achievementTypeGuid.Value); if (achievementType == null) { return(null); } var achiever = Entity; if (achievementType.AchieverEntityTypeId == EntityTypeCache.Get <PersonAlias>().Id&& Person != null) { // Translate this person badge to the person alias achievement achiever = Person.PrimaryAlias; } else if (achievementType.AchieverEntityTypeId != Entity.TypeId) { // This badge is not compatabile with this achievement return(null); } var domElementKey = GenerateBadgeKey(badge); return(GetScript(achievementType.Id, achiever.Id, domElementKey)); }
/// <summary> /// Gets the interaction dates. /// </summary> /// <param name="achievementTypeCache">The achievementTypeCache.</param> /// <param name="personAliasId">The person alias identifier.</param> /// <param name="minDate">The minimum date.</param> /// <param name="maxDate">The maximum date.</param> /// <returns></returns> private List <DateTime> GetInteractionDatesByPerson(AchievementTypeCache achievementTypeCache, int personAliasId, DateTime minDate, DateTime maxDate) { var rockContext = new RockContext(); var query = GetSourceEntitiesQuery(achievementTypeCache, rockContext) as IQueryable <Interaction>; var dayAfterMaxDate = maxDate.AddDays(1); var personAliasService = new PersonAliasService(rockContext); var personAliasQuery = personAliasService .Queryable() .AsNoTracking() .Where(pa => pa.Id == personAliasId) .SelectMany(pa => pa.Person.Aliases) .Select(pa => pa.Id); return(query .AsNoTracking() .Where(i => i.PersonAliasId.HasValue && personAliasQuery.Contains(i.PersonAliasId.Value) && i.InteractionDateTime >= minDate && i.InteractionDateTime < dayAfterMaxDate) .Select(i => i.InteractionDateTime) .ToList() .OrderBy(d => d) .ToList()); }
public void AccumulativeAchievementProcess() { var attemptsQuery = new AchievementAttemptService(_rockContext).Queryable() .AsNoTracking() .Where(saa => saa.AchievementTypeId == _achievementTypeId && _personAliasIds.Contains(saa.AchieverEntityId)) .OrderBy(saa => saa.AchievementAttemptStartDateTime); // There should be no attempts Assert.That.AreEqual(0, attemptsQuery.Count()); var achievementTypeCache = AchievementTypeCache.Get(_achievementTypeId); var interaction = new InteractionService(_rockContext).Queryable().FirstOrDefault(i => i.ForeignKey == KEY); var component = AchievementContainer.GetComponent(ComponentEntityTypeName); component.Process(_rockContext, achievementTypeCache, interaction); _rockContext.SaveChanges(); var attempts = attemptsQuery.ToList(); Assert.That.IsNotNull(attempts); Assert.That.AreEqual(1, attempts.Count); // The database stores progress with only 9 digits beyond the decimal var progress = decimal.Divide(COUNT, NUMBER_TO_ACHIEVE); var progressDifference = Math.Abs(progress - attempts[0].Progress); Assert.That.AreEqual(RockDateTime.Today, attempts[0].AchievementAttemptStartDateTime); Assert.That.AreEqual(RockDateTime.Today, attempts[0].AchievementAttemptEndDateTime); Assert.That.IsTrue(progressDifference < .000000001m); Assert.That.IsFalse(attempts[0].IsClosed); Assert.That.IsFalse(attempts[0].IsSuccessful); }
/// <summary> /// Update the open attempt record if there are changes. /// </summary> /// <param name="openAttempt"></param> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="transaction">The financial transaction.</param> private void UpdateOpenAttempt(AchievementAttempt openAttempt, AchievementTypeCache achievementTypeCache, FinancialTransaction transaction) { // Validate the attribute values var numberToAccumulate = GetAttributeValue(achievementTypeCache, AttributeKey.NumberToAccumulate).AsInteger(); if (numberToAccumulate <= 0) { ExceptionLogService.LogException($"{GetType().Name}.UpdateOpenAttempt cannot process because the NumberToAccumulate attribute is less than 1"); return; } // Calculate the date range where the open attempt can be validly fulfilled var attributeMaxDate = GetAttributeValue(achievementTypeCache, AttributeKey.EndDateTime).AsDateTime(); var minDate = openAttempt.AchievementAttemptStartDateTime; var maxDate = CalculateMaxDateForAchievementAttempt(minDate, attributeMaxDate); // Get the transaction dates var transactionDates = GetOrderedFinancialTransactionDatesByPerson(achievementTypeCache, transaction.AuthorizedPersonAlias.PersonId, minDate, maxDate); var newCount = transactionDates.Count(); if (newCount == 0) { return; } var lastInteractionDate = transactionDates.LastOrDefault(); var progress = CalculateProgress(newCount, numberToAccumulate); var isSuccessful = progress >= 1m; openAttempt.AchievementAttemptEndDateTime = lastInteractionDate; openAttempt.Progress = progress; openAttempt.IsClosed = isSuccessful && !achievementTypeCache.AllowOverAchievement; openAttempt.IsSuccessful = isSuccessful; }
/// <summary> /// Gets the interaction channel unique identifier. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <returns></returns> private Guid?GetStepProgramGuid(AchievementTypeCache achievementTypeCache) { var delimited = GetAttributeValue(achievementTypeCache, AttributeKey.StepProgram); var guids = delimited.SplitDelimitedValues().AsGuidOrNullList(); return(guids.FirstOrDefault()); }
/// <summary> /// Gets the progress statement for the achiever for this achievement. Flat means that the unmet prerequisites are not computed. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="achieverEntityId">The achiever entity identifier.</param> /// <returns></returns> private ProgressStatement GetFlatProgressStatement(AchievementTypeCache achievementTypeCache, int achieverEntityId) { var rockContext = Context as RockContext; var attemptService = new AchievementAttemptService(rockContext); var attemptsQuery = attemptService.Queryable().AsNoTracking(); var attempts = attemptService.GetOrderedAchieverAttempts(attemptsQuery, achievementTypeCache, achieverEntityId); var progressStatement = new ProgressStatement(achievementTypeCache); // If there are no attempts, no other information can be derived if (!attempts.Any()) { return(progressStatement); } var mostRecentAttempt = attempts.First(); var bestAttempt = attempts.OrderByDescending(saa => saa.Progress).First(); progressStatement.SuccessCount = attempts.Count(saa => saa.IsSuccessful); progressStatement.AttemptCount = attempts.Count(); progressStatement.BestAttempt = bestAttempt; progressStatement.MostRecentAttempt = mostRecentAttempt; return(progressStatement); }
/// <summary> /// Gets the interaction component unique identifier. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <returns></returns> private Guid?GetInteractionComponentGuid(AchievementTypeCache achievementTypeCache) { var delimited = GetAttributeValue(achievementTypeCache, AttributeKey.StepProgram); var guids = delimited.SplitDelimitedValues().AsGuidOrNullList(); return(guids.Count == 2 ? guids[1] : null); }
/// <summary> /// Processes attempts for the specified streak type achievement type identifier. This adds new attempts and updates existing attempts. /// </summary> /// <param name="achievementTypeId">The streak type achievement type identifier.</param> public static void Process(int achievementTypeId) { var achievementTypeCache = AchievementTypeCache.Get(achievementTypeId); if (achievementTypeCache == null) { throw new ArgumentException($"The AchievementTypeCache did not resolve for record id {achievementTypeId}"); } var achievementComponent = achievementTypeCache.AchievementComponent; if (achievementComponent == null) { throw new ArgumentException($"The AchievementComponent did not resolve for record id {achievementTypeId}"); } var sourceEntitiesQuery = achievementComponent.GetSourceEntitiesQuery(achievementTypeCache, new RockContext()) .AsNoTracking() .ToList(); foreach (var sourceEntity in sourceEntitiesQuery) { // Process each streak in it's own data context to avoid the data context changes getting too big and slow var rockContext = new RockContext(); achievementComponent.Process(rockContext, achievementTypeCache, sourceEntity); rockContext.SaveChanges(); } }
/// <summary> /// Gets the achievement types. /// </summary> /// <returns></returns> private List <AchievementTypeCache> GetAchievementTypes() { if (_achievementTypes != null) { return(_achievementTypes); } var filters = new List <KeyValuePair <string, string> >(); foreach (string key in Request.QueryString.Keys) { if (key.IsNullOrWhiteSpace()) { continue; } var value = Request.QueryString[key]; filters.Add(new KeyValuePair <string, string>(key, value)); } _achievementTypes = AchievementTypeCache.All() .Where(at => { if (!filters.Any()) { return(true); } var component = at.AchievementComponent; return(component != null && component.IsRelevantToAllFilters(at, filters)); }) .OrderBy(at => at.Id) .ToList(); return(_achievementTypes); }
/// <summary> /// Determines whether this achievement type applies given the set of filters. The filters could be the query string /// of a web request. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="filters">The filters.</param> /// <returns> /// <c>true</c> if [is relevant to all filters] [the specified filters]; otherwise, <c>false</c>. /// </returns> public override bool IsRelevantToAllFilters(AchievementTypeCache achievementTypeCache, List <KeyValuePair <string, string> > filters) { if (filters.Count == 0) { return(true); } if (filters.Count > 2) { return(false); } return(filters.All(f => { if (f.Key.Equals("AccountId", StringComparison.OrdinalIgnoreCase)) { return f.Value.AsInteger() == GetFinancialAccount(achievementTypeCache)?.Id; } if (f.Key.Equals("FinancialAccountId", StringComparison.OrdinalIgnoreCase)) { return f.Value.AsInteger() == GetFinancialAccount(achievementTypeCache)?.Id; } return false; })); }
/// <summary> /// Gets the financial account unique identifier. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <returns></returns> private Guid?GetFinancialAccountGuid(AchievementTypeCache achievementTypeCache) { var delimited = GetAttributeValue(achievementTypeCache, AttributeKey.FinancialAccount); var guids = delimited.SplitDelimitedValues().AsGuidOrNullList(); return(guids.FirstOrDefault()); }
/// <summary> /// Gets the progress statements for the achiever for all active achievements. /// </summary> /// <param name="achieverEntityTypeId">The achiever entity type identifier.</param> /// <param name="achieverEntityId">The achiever identifier.</param> /// <returns></returns> public List <ProgressStatement> GetProgressStatements(int achieverEntityTypeId, int achieverEntityId) { var achievementTypes = AchievementTypeCache.All() .Where(at => at.IsActive && at.AchieverEntityTypeId == achieverEntityTypeId) .ToList(); var orderedAchievementTypes = SortAccordingToPrerequisites(achievementTypes); var progressStatementsDictionary = new Dictionary <int, ProgressStatement>(); var progressStatements = new List <ProgressStatement>(); foreach (var achievementType in orderedAchievementTypes) { var progressStatement = GetFlatProgressStatement(achievementType, achieverEntityId); progressStatementsDictionary[achievementType.Id] = progressStatement; progressStatements.Add(progressStatement); foreach (var prerequisite in achievementType.PrerequisiteAchievementTypes) { var prerequisiteProgressStatement = progressStatementsDictionary[prerequisite.Id]; if (prerequisiteProgressStatement.SuccessCount == 0) { progressStatement.UnmetPrerequisites.Add(prerequisiteProgressStatement); } } } return(progressStatements); }
/// <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(IAchievementTypePicker 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 achievementTypes = AchievementTypeCache.All() .Where(stat => stat.IsActive) .OrderBy(stat => stat.Name) .ToList(); foreach (var achievementType in achievementTypes) { var li = new ListItem(achievementType.Name, achievementType.Id.ToString()); li.Selected = selectedItems.Contains(achievementType.Id); picker.Items.Add(li); } }
/// <summary> /// Returns a collection of Achievement Types that can be selected as prerequisites of a new Achievement Type. /// An Achievement Type cannot be a prerequisite of itself, or of any Achievement Type that has it as a prerequisite. /// </summary> /// <returns></returns> public static List <AchievementTypeCache> GetEligiblePrerequisiteAchievementTypeCachesForNewAchievement(int achieverEntityTypeId) { // Get achievement types of which the specified achievement type is not already a prerequisite. return(AchievementTypeCache.All() .Where(at => at.IsActive && at.AchieverEntityTypeId == achieverEntityTypeId) .ToList()); }
/// <summary> /// Initializes a new instance of the <see cref="PersonAchievementType"/> class. /// </summary> /// <param name="person">The person.</param> /// <param name="achievementType">Type of the achievement.</param> /// <param name="achievementAttempts">The achievement attempts.</param> /// <param name="justCompletedAchievementAttempt">The just completed achievement attempt.</param> public PersonAchievementType(Person person, AchievementTypeCache achievementType, AchievementAttempt[] achievementAttempts, AchievementAttempt justCompletedAchievementAttempt) { Person = person; AchievementType = achievementType; JustCompletedAchievementAttempt = justCompletedAchievementAttempt; CurrentInProgressAchievement = achievementAttempts.Where(a => !a.IsSuccessful && !a.IsClosed).FirstOrDefault(); AchievementAttempts = achievementAttempts; }
/// <summary> /// Initializes a new instance of the <see cref="ProgressStatement" /> class. /// </summary> /// <param name="achievementTypeCache">The streak type achievement type cache.</param> public ProgressStatement(AchievementTypeCache achievementTypeCache) { UnmetPrerequisites = new List <ProgressStatement>(); AchievementTypeId = achievementTypeCache.Id; AchievementTypeName = achievementTypeCache.Name; AchievementTypeDescription = achievementTypeCache.Description; Attributes = achievementTypeCache.AttributeValues?.Where(kvp => kvp.Key != "Active" && kvp.Key != "Order") .ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Value); }
/// <summary> /// Gets the name of the source. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <returns></returns> private string GetSourceName(AchievementTypeCache achievementTypeCache) { var component = achievementTypeCache.AchievementComponent; if (component != null) { return(component.GetSourceName(achievementTypeCache)); } return(string.Empty); }
/// <summary> /// Returns a collection of Achievement Types that can be selected as prerequisites of the specified Achievement Type. /// An Achievement Type cannot be a prerequisite of itself, or of any Achievement Type that has it as a prerequisite. /// </summary> /// <param name="achievementTypeCache">The Achievement Type for which prerequisites are required.</param> /// <returns></returns> public static List <AchievementTypeCache> GetEligiblePrerequisiteAchievementTypeCaches(AchievementTypeCache achievementTypeCache) { // Get achievement types of which the specified achievement type is not already a prerequisite. return(AchievementTypeCache.All() .Where(at => at.IsActive && at.AchieverEntityTypeId == achievementTypeCache.AchieverEntityTypeId && at.Id != achievementTypeCache.Id && !at.Prerequisites.Any(p => p.PrerequisiteAchievementTypeId == achievementTypeCache.Id)) .ToList()); }
/// <summary> /// Gets the badge markup for this achiever for this achievement. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="achieverEntityId">The achiever entity identifier.</param> /// <returns></returns> public string GetBadgeMarkup(AchievementTypeCache achievementTypeCache, int achieverEntityId) { var achievementComponent = achievementTypeCache.AchievementComponent; if (achievementComponent == null) { throw new ArgumentException($"The AchievementComponent did not resolve for {achievementTypeCache}"); } return(achievementComponent.GetBadgeMarkup(achievementTypeCache, achieverEntityId)); }
/// <summary> /// Gets the name of the source that these achievements are measured from. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <returns></returns> public override string GetSourceName(AchievementTypeCache achievementTypeCache) { var streakTypeCache = GetStreakTypeCache(achievementTypeCache); if (streakTypeCache != null) { return(streakTypeCache.Name); } return(string.Empty); }
/// <summary> /// Gets the name of the source that these achievements are measured from. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <returns></returns> public override string GetSourceName(AchievementTypeCache achievementTypeCache) { var stepProgram = GetStepProgramCache(achievementTypeCache); if (stepProgram != null) { return(stepProgram.Name); } return(string.Empty); }
/// <summary> /// Should the achievement type process attempts if the given source entity has been modified in some way. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="sourceEntity">The source entity.</param> /// <returns></returns> public override bool ShouldProcess(AchievementTypeCache achievementTypeCache, IEntity sourceEntity) { var streak = sourceEntity as Streak; if (streak == null) { return(false); } return(streak.StreakTypeId == GetStreakTypeCache(achievementTypeCache)?.Id); }
/// <summary> /// Executes this instance. /// </summary> public override void Execute(Message message) { var type = Type.GetType(message.EntityTypeName); var entity = Reflection.GetIEntityForEntityType(type, message.EntityGuid); if (entity == null) { return; } AchievementTypeCache.ProcessAchievements(entity); }
/// <summary> /// Gets the progress statement for the achiever for this achievement. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="achieverEntityId">The achiever entity identifier.</param> /// <returns></returns> public ProgressStatement GetProgressStatement(AchievementTypeCache achievementTypeCache, int achieverEntityId) { var progressStatement = GetFlatProgressStatement(achievementTypeCache, achieverEntityId); foreach (var prerequisite in achievementTypeCache.PrerequisiteAchievementTypes) { var prerequisiteProgressStatement = GetFlatProgressStatement(prerequisite, achieverEntityId); progressStatement.UnmetPrerequisites.Add(prerequisiteProgressStatement); } return(progressStatement); }
/// <summary> /// Executes this instance. /// </summary> /// <exception cref="System.NotImplementedException"></exception> public void Execute() { if (SourceEntities == null || !SourceEntities.Any()) { return; } foreach (var sourceEntity in SourceEntities) { AchievementTypeCache.ProcessAchievements(sourceEntity); } }
/// <summary> /// Returns a dictionary of the items available for selection. /// </summary> /// <returns></returns> protected override Dictionary <Guid, string> OnGetItemList() { return(AchievementTypeCache.All() .Where(s => s.IsActive) .OrderBy(s => s.Name) .Select(s => new { s.Guid, s.Name, }) .ToDictionary(s => s.Guid, s => s.Name)); }