Esempio n. 1
0
        /// <summary>
        /// Executes the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        public void Execute(IJobExecutionContext context)
        {
            var exceptionMsgs = new List <string>();

            JobDataMap dataMap         = context.JobDetail.JobDataMap;
            Guid?      groupGuid       = dataMap.GetString("EligibleFollowers").AsGuidOrNull();
            Guid?      systemEmailGuid = dataMap.GetString("EmailTemplate").AsGuidOrNull();
            int        followingSuggestionsEmailsSent       = 0;
            int        followingSuggestionsSuggestionsTotal = 0;

            if (groupGuid.HasValue && systemEmailGuid.HasValue)
            {
                using (var rockContext = new RockContext())
                {
                    var followingService = new FollowingService(rockContext);

                    // The people who are eligible to get following suggestions based on the group type setting for this job
                    var eligiblePersonIds = new GroupMemberService(rockContext)
                                            .Queryable().AsNoTracking()
                                            .Where(m =>
                                                   m.Group != null &&
                                                   m.Group.Guid.Equals(groupGuid.Value) &&
                                                   m.GroupMemberStatus == GroupMemberStatus.Active &&
                                                   m.Person != null &&
                                                   m.Person.Email != null &&
                                                   m.Person.Email != string.Empty &&
                                                   m.Person.EmailPreference != EmailPreference.DoNotEmail &&
                                                   m.Person.IsEmailActive)
                                            .Select(m => m.PersonId)
                                            .Distinct();

                    // check to see if there are any event types that require notification
                    var followerPersonIds = new List <int>();
                    if (new FollowingEventTypeService(rockContext)
                        .Queryable().AsNoTracking()
                        .Any(e => e.IsNoticeRequired))
                    {
                        // if so, include all eligible people
                        followerPersonIds = eligiblePersonIds.ToList();
                    }
                    else
                    {
                        // if not, filter the list of eligible people down to only those that actually have subscribed to one or more following events
                        followerPersonIds = new FollowingEventSubscriptionService(rockContext)
                                            .Queryable().AsNoTracking()
                                            .Where(f => eligiblePersonIds.Contains(f.PersonAlias.PersonId))
                                            .Select(f => f.PersonAlias.PersonId)
                                            .Distinct()
                                            .ToList();
                    }

                    if (followerPersonIds.Any())
                    {
                        // Get the primary person alias id for each of the followers
                        var primaryAliasIds = new Dictionary <int, int>();
                        new PersonAliasService(rockContext)
                        .Queryable().AsNoTracking()
                        .Where(a =>
                               followerPersonIds.Contains(a.PersonId) &&
                               a.PersonId == a.AliasPersonId)
                        .ToList()
                        .ForEach(a => primaryAliasIds.AddOrIgnore(a.PersonId, a.Id));

                        // Get current date/time.
                        var timestamp = RockDateTime.Now;

                        var suggestionTypes = new FollowingSuggestionTypeService(rockContext)
                                              .Queryable().AsNoTracking()
                                              .Where(s => s.IsActive)
                                              .OrderBy(s => s.Name)
                                              .ToList();

                        var components        = new Dictionary <int, SuggestionComponent>();
                        var suggestedEntities = new Dictionary <int, Dictionary <int, IEntity> >();

                        foreach (var suggestionType in suggestionTypes)
                        {
                            try
                            {
                                // Get the suggestion type component
                                var suggestionComponent = suggestionType.GetSuggestionComponent();
                                if (suggestionComponent != null)
                                {
                                    components.Add(suggestionType.Id, suggestionComponent);

                                    var suggestionTypeComponent = new SuggestionTypeComponent(suggestionType);

                                    // Get the entitytype for this suggestion type
                                    var suggestionEntityType = EntityTypeCache.Get(suggestionComponent.FollowedType);
                                    if (suggestionEntityType != null)
                                    {
                                        var entityIds = new List <int>();

                                        // Call the components method to return all of it's suggestions
                                        suggestionTypeComponent.PersonEntitySuggestions = suggestionComponent.GetSuggestions(suggestionType, followerPersonIds);

                                        // If any suggestions were returned by the component
                                        if (suggestionTypeComponent.PersonEntitySuggestions.Any())
                                        {
                                            int    entityTypeId = suggestionEntityType.Id;
                                            string reasonNote   = suggestionType.ReasonNote;

                                            // Get the existing followings for any of the followers
                                            suggestionTypeComponent.ExistingFollowings = new Dictionary <int, List <int> >();
                                            foreach (var following in followingService.Queryable("PersonAlias").AsNoTracking().Where(f => f.EntityTypeId == entityTypeId && string.IsNullOrEmpty(f.PurposeKey) && followerPersonIds.Contains(f.PersonAlias.PersonId)))
                                            {
                                                suggestionTypeComponent.ExistingFollowings.AddOrIgnore(following.PersonAlias.PersonId, new List <int>());
                                                suggestionTypeComponent.ExistingFollowings[following.PersonAlias.PersonId].Add(following.EntityId);
                                            }

                                            // Loop through each follower
                                            foreach (var followerPersonId in suggestionTypeComponent.PersonEntitySuggestions.Select(s => s.PersonId).Distinct())
                                            {
                                                ProcessFollowerPersonId(followerPersonId, suggestionTypeComponent, primaryAliasIds, entityIds, entityTypeId, timestamp);
                                            }
                                        }

                                        // If any entities are being suggested for this type, query database for them and save to dictionary
                                        if (entityIds.Any())
                                        {
                                            if (suggestionEntityType.AssemblyName != null)
                                            {
                                                // get the actual type of what is being followed
                                                Type entityType = suggestionEntityType.GetEntityType();
                                                if (entityType != null)
                                                {
                                                    // Get generic queryable method and query all the entities that are being followed
                                                    Type[]             modelType          = { entityType };
                                                    Type               genericServiceType = typeof(Rock.Data.Service <>);
                                                    Type               modelServiceType   = genericServiceType.MakeGenericType(modelType);
                                                    Rock.Data.IService serviceInstance    = Activator.CreateInstance(modelServiceType, new object[] { rockContext }) as IService;
                                                    MethodInfo         qryMethod          = serviceInstance.GetType().GetMethod("Queryable", new Type[] { });
                                                    var entityQry  = qryMethod.Invoke(serviceInstance, new object[] { }) as IQueryable <IEntity>;
                                                    var entityList = entityQry.AsNoTracking().Where(q => entityIds.Contains(q.Id)).ToList();
                                                    if (entityList != null && entityList.Any())
                                                    {
                                                        var entities = new Dictionary <int, IEntity>();
                                                        entityList.ForEach(e => entities.Add(e.Id, e));
                                                        suggestedEntities.Add(suggestionType.Id, entities);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                exceptionMsgs.Add(string.Format("An exception occurred calculating suggestions for the '{0}' suggestion type:{1}    {2}", suggestionType.Name, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + "   ")));
                                ExceptionLogService.LogException(ex, System.Web.HttpContext.Current);
                            }
                        }

                        var allSuggestions = new FollowingSuggestedService(rockContext)
                                             .Queryable("PersonAlias")
                                             .Where(s => s.Status == FollowingSuggestedStatus.PendingNotification)
                                             .ToList();

                        var suggestionPersonIds = allSuggestions
                                                  .Where(s => followerPersonIds.Contains(s.PersonAlias.PersonId))
                                                  .Select(s => s.PersonAlias.PersonId)
                                                  .Distinct()
                                                  .ToList();

                        foreach (var person in new PersonService(rockContext)
                                 .Queryable().AsNoTracking()
                                 .Where(p => suggestionPersonIds.Contains(p.Id))
                                 .ToList())
                        {
                            try
                            {
                                var personSuggestionNotices = new List <FollowingSuggestionNotices>();

                                foreach (var suggestionType in suggestionTypes)
                                {
                                    var component = components.ContainsKey(suggestionType.Id) ? components[suggestionType.Id] : null;
                                    if (component != null && suggestedEntities.ContainsKey(suggestionType.Id))
                                    {
                                        var entities = new List <IEntity>();
                                        foreach (var suggestion in allSuggestions
                                                 .Where(s =>
                                                        s.PersonAlias.PersonId == person.Id &&
                                                        s.SuggestionTypeId == suggestionType.Id)
                                                 .ToList())
                                        {
                                            if (suggestedEntities[suggestionType.Id].ContainsKey(suggestion.EntityId))
                                            {
                                                entities.Add(suggestedEntities[suggestionType.Id][suggestion.EntityId]);
                                                suggestion.LastPromotedDateTime = timestamp;
                                                suggestion.Status = FollowingSuggestedStatus.Suggested;
                                            }
                                        }

                                        var notices = new List <string>();
                                        foreach (var entity in component.SortEntities(entities))
                                        {
                                            notices.Add(component.FormatEntityNotification(suggestionType, entity));
                                        }

                                        if (notices.Any())
                                        {
                                            personSuggestionNotices.Add(new FollowingSuggestionNotices(suggestionType, notices));
                                        }
                                    }
                                }

                                if (personSuggestionNotices.Any())
                                {
                                    // Send the notice
                                    var mergeFields = new Dictionary <string, object>();
                                    mergeFields.Add("Person", person);
                                    mergeFields.Add("Suggestions", personSuggestionNotices.OrderBy(s => s.SuggestionType.Order).ToList());

                                    var emailMessage = new RockEmailMessage(systemEmailGuid.Value);
                                    emailMessage.AddRecipient(new RockEmailMessageRecipient(person, mergeFields));
                                    var errors = new List <string>();
                                    emailMessage.Send(out errors);
                                    exceptionMsgs.AddRange(errors);

                                    followingSuggestionsEmailsSent       += 1;
                                    followingSuggestionsSuggestionsTotal += personSuggestionNotices.Count();
                                }

                                rockContext.SaveChanges();
                            }
                            catch (Exception ex)
                            {
                                exceptionMsgs.Add(string.Format("An exception occurred sending suggestions to '{0}':{1}    {2}", person.FullName, Environment.NewLine, ex.Messages().AsDelimited(Environment.NewLine + "   ")));
                                ExceptionLogService.LogException(ex, System.Web.HttpContext.Current);
                            }
                        }
                    }
                }
            }

            context.Result = string.Format("A total of {0} following suggestions sent to {1} people", followingSuggestionsSuggestionsTotal, followingSuggestionsEmailsSent);

            if (exceptionMsgs.Any())
            {
                throw new Exception("One or more exceptions occurred calculating suggestions..." + Environment.NewLine + exceptionMsgs.AsDelimited(Environment.NewLine));
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Checks to see if the PersonAlias entity should be added to the FollowingSuggestion table. If the entity is not a PersonAlias no action is taken.
        /// If the Entity is a PersonAlias then this method will check if the associated Person has another PersonAlias that is being followed.
        /// If there is another PersonAlias being followed then the Following and FollowingSuggestion are updated with this EntityId (PersonAliasId)
        /// which is the PrimaryPersonAliasId.
        /// </summary>
        /// <param name="followerPersonId">The follower person identifier.</param>
        /// <param name="suggestionTypeComponent">The suggestion type component.</param>
        /// <param name="entityIdToBeSavedAsSuggestions">The entity identifier to be saved as suggestions.</param>
        /// <param name="entityTypeId">The entity type identifier.</param>
        /// <param name="suggestionContext">The suggestion context.</param>
        /// <param name="followedEntityId">The followed entity identifier.</param>
        private void ProcessFollowingSuggestionAndPersonAliasEntity(int followerPersonId, SuggestionTypeComponent suggestionTypeComponent, List <int> entityIdToBeSavedAsSuggestions, int entityTypeId, RockContext suggestionContext, int followedEntityId)
        {
            bool addSuggestion;

            ProcessFollowingSuggestionAndPersonAliasEntity(followerPersonId, suggestionTypeComponent, entityIdToBeSavedAsSuggestions, entityTypeId, suggestionContext, followedEntityId, out addSuggestion);
        }
Esempio n. 3
0
        /// <summary>
        /// Checks to see if the PersonAlias entity should be added to the FollowingSuggestion table. If the entity is not a PersonAlias then addSuggestion will be true.
        /// If the Entity is a PersonAlias then this method will check if the associated Person has another PersonAlias that is being followed.
        /// If there is another PersonAlias being followed then the Following and FollowingSuggestion are updated with this EntityId (PersonAliasId)
        /// which is the PrimaryPersonAliasId.
        /// </summary>
        /// <param name="followerPersonId">The follower person identifier.</param>
        /// <param name="suggestionTypeComponent">The suggestion type component.</param>
        /// <param name="entityIdToBeSavedAsSuggestions">The entity ids.</param>
        /// <param name="entityTypeId">The entity type identifier.</param>
        /// <param name="suggestionContext">The suggestion context.</param>
        /// <param name="followedEntityId">The followed entity identifier.</param>
        /// <param name="addSuggestion">if set to <c>true</c> then the suggestion should be inserted.</param>
        /// <returns>
        /// True if the PersonAlias following suggestion should be inserted or if the EntityType is not a PersonAlias.
        /// </returns>
        private void ProcessFollowingSuggestionAndPersonAliasEntity(int followerPersonId, SuggestionTypeComponent suggestionTypeComponent, List <int> entityIdToBeSavedAsSuggestions, int entityTypeId, RockContext suggestionContext, int followedEntityId, out bool addSuggestion)
        {
            addSuggestion = true;

            // If the Entity is not a PersonAlias no other checks are needed just return with addSuggestion = true
            if (!IsEntityTypePersonAlias(entityTypeId))
            {
                return;
            }

            // since this is a person alias see if a different alias is being used to follow
            var followedPersonId      = new PersonAliasService(suggestionContext).Get(followedEntityId).PersonId;
            var followedPersonAliases = new PersonAliasService(suggestionContext)
                                        .Queryable()
                                        .Where(a => a.PersonId == followedPersonId && a.Id != followedEntityId)
                                        .Select(a => a.Id)
                                        .ToList();

            if (!followedPersonAliases.Any())
            {
                // There are no alternate person alias' there is nothing else to check so just return with addSuggestion = true
                return;
            }

            int existingFollowingPersonAliasId = suggestionTypeComponent.ExistingFollowings
                                                 .GetValueOrDefault(followerPersonId, new List <int>())
                                                 .Where(f => followedPersonAliases.Contains(f))
                                                 .FirstOrDefault();

            // Update the existing following record to use the primary PersonAlias ID and remove it from the list.
            if (existingFollowingPersonAliasId != 0)
            {
                var personAliasEntityTypeId = EntityTypeCache.GetId(Rock.SystemGuid.EntityType.PERSON_ALIAS) ?? 0;
                addSuggestion = false;
                entityIdToBeSavedAsSuggestions.Remove(followedEntityId);

                using (var followingContext = new RockContext())
                {
                    var following = new FollowingService(followingContext)
                                    .GetByEntityAndPerson(personAliasEntityTypeId, existingFollowingPersonAliasId, followerPersonId)
                                    .FirstOrDefault();

                    following.EntityId = followedEntityId;

                    var suggested = new FollowingSuggestedService(followingContext)
                                    .GetByEntityAndPerson(personAliasEntityTypeId, existingFollowingPersonAliasId, followerPersonId)
                                    .FirstOrDefault();

                    if (suggested != null)
                    {
                        // Get the Followed Person's Primary PersonAliasId from FollowingSuggestions if it exists, PersonEntitySuggestions is already filtered for EntityType
                        int existingFollowingSuggestedPrimaryPersonAliasId = suggestionTypeComponent.PersonEntitySuggestions
                                                                             .Where(s => s.PersonId == followerPersonId && s.EntityId == followedEntityId)
                                                                             .Select(s => s.EntityId)
                                                                             .FirstOrDefault();

                        if (existingFollowingSuggestedPrimaryPersonAliasId != 0)
                        {
                            // Since the Primary PersonAlias is already in FollowingSuggestions then just delete this one as a duplicate
                            new FollowingSuggestedService(followingContext).Delete(suggested);
                        }
                        else
                        {
                            // Update the outdated PersonAliasId to the primary PersonAliasId which is the followedEntityId
                            suggested.EntityId = followedEntityId;
                        }
                    }

                    followingContext.SaveChanges();
                }
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Processes the follower PersonId for the SuggestionType
        /// </summary>
        /// <param name="followerPersonId">The follower person identifier.</param>
        /// <param name="suggestionTypeComponent">The suggestion type component.</param>
        /// <param name="primaryAliasIds">The primary alias ids.</param>
        /// <param name="entityIds">The entity ids.</param>
        /// <param name="entityTypeId">The entity type identifier.</param>
        /// <param name="timestamp">The timestamp.</param>
        private void ProcessFollowerPersonId(int followerPersonId, SuggestionTypeComponent suggestionTypeComponent, Dictionary <int, int> primaryAliasIds, List <int> entityIds, int entityTypeId, DateTime timestamp)
        {
            using (var suggestionContext = new RockContext())
            {
                var followingSuggestedService = new FollowingSuggestedService(suggestionContext);

                // Read all the existing suggestions for this type and the returned followers
                var existingSuggestions = followingSuggestedService
                                          .Queryable("PersonAlias")
                                          .Where(s => s.SuggestionTypeId == suggestionTypeComponent.FollowingSuggestionType.Id &&
                                                 s.PersonAlias.PersonId == followerPersonId)
                                          .ToList();

                // Look  through the returned suggestions
                foreach (var followedEntityId in suggestionTypeComponent.PersonEntitySuggestions.Where(s => s.PersonId == followerPersonId).Select(s => s.EntityId))
                {
                    // Make sure person isn't already following this entity
                    if (!suggestionTypeComponent.ExistingFollowings.ContainsKey(followerPersonId) ||
                        !suggestionTypeComponent.ExistingFollowings[followerPersonId].Contains(followedEntityId))
                    {
                        // If this person had a primary alias id
                        if (primaryAliasIds.ContainsKey(followerPersonId))
                        {
                            entityIds.Add(followedEntityId);

                            // Look for existing suggestion for this person and entity
                            var suggestion = existingSuggestions
                                             .Where(s => s.EntityId == followedEntityId)
                                             .OrderByDescending(s => s.StatusChangedDateTime)
                                             .FirstOrDefault();

                            // If not found add it if needed
                            if (suggestion == null)
                            {
                                bool addSuggestion;
                                ProcessFollowingSuggestionAndPersonAliasEntity(followerPersonId, suggestionTypeComponent, entityIds, entityTypeId, suggestionContext, followedEntityId, out addSuggestion);

                                if (addSuggestion)
                                {
                                    // This is a new entity ID so insert it
                                    suggestion = new FollowingSuggested
                                    {
                                        EntityTypeId          = entityTypeId,
                                        EntityId              = followedEntityId,
                                        PersonAliasId         = primaryAliasIds[followerPersonId],
                                        SuggestionTypeId      = suggestionTypeComponent.FollowingSuggestionType.Id,
                                        Status                = FollowingSuggestedStatus.PendingNotification,
                                        StatusChangedDateTime = timestamp
                                    };

                                    followingSuggestedService.Add(suggestion);
                                }
                            }
                            else
                            {
                                ProcessFollowingSuggestionAndPersonAliasEntity(followerPersonId, suggestionTypeComponent, entityIds, entityTypeId, suggestionContext, followedEntityId);

                                // If found, and it has not been ignored, and it's time to promote again, update the promote date
                                if (suggestion.Status != FollowingSuggestedStatus.Ignored &&
                                    suggestionTypeComponent.FollowingSuggestionType.ReminderDays.HasValue &&
                                    (
                                        !suggestion.LastPromotedDateTime.HasValue ||
                                        suggestion.LastPromotedDateTime.Value.AddDays(suggestionTypeComponent.FollowingSuggestionType.ReminderDays.Value) <= timestamp
                                    ))
                                {
                                    if (suggestion.Status != FollowingSuggestedStatus.PendingNotification)
                                    {
                                        suggestion.StatusChangedDateTime = timestamp;
                                        suggestion.Status = FollowingSuggestedStatus.PendingNotification;
                                    }
                                }
                            }
                        }
                    }
                }

                // Save the suggestions for this type
                suggestionContext.SaveChanges();
            }
        }