/// <summary> /// Adds the missing relationship groups. /// Copied from Rock Cleanup Job /// </summary> /// <param name="relationshipGroupType">Type of the relationship group.</param> /// <param name="ownerRoleGuid">The owner role unique identifier.</param> private static void AddMissingRelationshipGroups() { var relationshipGroupType = CachedTypes.KnownRelationshipGroupType; var ownerRoleGuid = CachedTypes.KnownRelationshipOwnerRoleGuid; if ( relationshipGroupType != null ) { var ownerRoleId = relationshipGroupType.Roles .Where( r => r.Guid.Equals( ownerRoleGuid ) ).Select( a => ( int? ) a.Id ).FirstOrDefault(); if ( ownerRoleId.HasValue ) { var rockContext = new RockContext(); var personService = new PersonService( rockContext ); var memberService = new GroupMemberService( rockContext ); var qryGroupOwnerPersonIds = memberService.Queryable( true ) .Where( m => m.GroupRoleId == ownerRoleId.Value ).Select( a => a.PersonId ); var personIdsWithoutKnownRelationshipGroup = personService.Queryable( true, true ).Where( p => !qryGroupOwnerPersonIds.Contains( p.Id ) ).Select( a => a.Id ).ToList(); var groupsToInsert = new List<Group>(); var groupGroupMembersToInsert = new Dictionary<Guid, GroupMember>(); foreach ( var personId in personIdsWithoutKnownRelationshipGroup ) { var groupMember = new GroupMember(); groupMember.PersonId = personId; groupMember.GroupRoleId = ownerRoleId.Value; var group = new Group(); group.Name = relationshipGroupType.Name; group.Guid = Guid.NewGuid(); group.GroupTypeId = relationshipGroupType.Id; groupGroupMembersToInsert.Add( group.Guid, groupMember ); groupsToInsert.Add( group ); } if ( groupsToInsert.Any() ) { // use BulkInsert just in case there are a large number of groups and group members to insert rockContext.BulkInsert( groupsToInsert ); Dictionary<Guid, int> groupIdLookup = new GroupService( rockContext ).Queryable().Where( a => a.GroupTypeId == relationshipGroupType.Id ).Select( a => new { a.Id, a.Guid } ).ToDictionary( k => k.Guid, v => v.Id ); var groupMembersToInsert = new List<GroupMember>(); foreach ( var groupGroupMember in groupGroupMembersToInsert ) { var groupMember = groupGroupMember.Value; groupMember.GroupId = groupIdLookup[groupGroupMember.Key]; groupMembersToInsert.Add( groupMember ); } rockContext.BulkInsert( groupMembersToInsert ); } rockContext.Dispose(); } } }
/// <summary> /// Saves the new user logins. /// </summary> /// <param name="newUserLogins">The new user logins.</param> /// <param name="newStaffMembers">The new staff members.</param> private static void SaveUsers(List <UserLogin> newUserLogins, List <GroupMember> newStaffMembers) { using (var rockContext = new RockContext()) { rockContext.BulkInsert(newUserLogins); rockContext.BulkInsert(newStaffMembers); } }
/// <summary> /// Saves the activities. /// </summary> /// <param name="activityList">The activity list.</param> private static void SaveActivities(List <ConnectionRequestActivity> activityList) { using (var rockContext = new RockContext()) { rockContext.BulkInsert(activityList); } }
/// <summary> /// Saves the prayer requests. /// </summary> /// <param name="prayerList">The prayer list.</param> private static void SavePrayerRequests(List <PrayerRequest> prayerList) { using (var rockContext = new RockContext()) { rockContext.BulkInsert(prayerList); } }
/// <summary> /// Saves the pledges. /// </summary> /// <param name="newPledges">The new pledges.</param> private static void SavePledges(List <FinancialPledge> newPledges) { using (var rockContext = new RockContext()) { rockContext.BulkInsert(newPledges); } }
/// <summary> /// Saves the family address. /// </summary> /// <param name="newGroupLocations">The new group locations.</param> private static void SaveFamilyAddress(List <GroupLocation> newGroupLocations) { using (var rockContext = new RockContext()) { rockContext.BulkInsert(newGroupLocations); } }
/// <summary> /// Saves the bank accounts. /// </summary> /// <param name="newBankAccounts">The new bank accounts.</param> private static void SaveBankAccounts(List <FinancialPersonBankAccount> newBankAccounts) { using (var rockContext = new RockContext()) { rockContext.BulkInsert(newBankAccounts); } }
/// <summary> /// Saves the new group members. /// </summary> /// <param name="newGroupMembers">The new group members.</param> private static void SaveGroupMembers(List <GroupMember> newGroupMembers) { using (var rockContext = new RockContext()) { rockContext.BulkInsert(newGroupMembers); } }
/// <summary> /// Saves the financial batches. /// </summary> /// <param name="newBatches">The new batches.</param> private static void SaveFinancialBatches(List <FinancialBatch> newBatches) { using (var rockContext = new RockContext()) { rockContext.BulkInsert(newBatches); } }
/// <summary> /// Saves the connection requests. /// </summary> /// <param name="communicationList">The communication list.</param> private static void SaveConnectionRequests(List <ConnectionRequest> connectionRequests) { using (var rockContext = new RockContext()) { rockContext.BulkInsert(connectionRequests); } }
/// <summary> /// Saves the benevolence requests. /// </summary> /// <param name="newBenevolences">The new benevolences.</param> private static void SaveBenevolenceRequests(List <BenevolenceRequest> newBenevolences) { if (newBenevolences.Count > 0) { using (var rockContext = new RockContext()) { rockContext.BulkInsert(newBenevolences); } } }
/// <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.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. interactionsToInsert.ForEach(i => Task.Run(() => StreakTypeService.HandleInteractionRecord(i))); }
/// <summary> /// Executes this instance. /// </summary> public void Execute() { var historyRecordsToInsert = new List <History>(); while (HistoryRecordsToInsert.TryDequeue(out History historyRecord)) { historyRecordsToInsert.Add(historyRecord); } if (!historyRecordsToInsert.Any()) { return; } using (var rockContext = new RockContext()) { rockContext.BulkInsert(historyRecordsToInsert); } }
/// <summary> /// Refresh the recipients list. /// </summary> /// <param name="rockContext">The rock context.</param> /// <returns></returns> public void RefreshCommunicationRecipientList(RockContext rockContext) { if (!ListGroupId.HasValue) { return; } var segmentDataViewGuids = this.Segments.SplitDelimitedValues().AsGuidList(); var segmentDataViewIds = new DataViewService(rockContext).GetByGuids(segmentDataViewGuids).Select(a => a.Id).ToList(); var qryCommunicationListMembers = GetCommunicationListMembers(rockContext, ListGroupId, this.SegmentCriteria, segmentDataViewIds); // NOTE: If this is scheduled communication, don't include Members that were added after the scheduled FutureSendDateTime. // However, don't exclude if the date added can't be determined or they will never be sent a scheduled communication. if (this.FutureSendDateTime.HasValue) { var memberAddedCutoffDate = this.FutureSendDateTime; qryCommunicationListMembers = qryCommunicationListMembers.Where(a => (a.DateTimeAdded.HasValue && a.DateTimeAdded.Value < memberAddedCutoffDate) || (a.CreatedDateTime.HasValue && a.CreatedDateTime.Value < memberAddedCutoffDate) || (!a.DateTimeAdded.HasValue && !a.CreatedDateTime.HasValue)); } var communicationRecipientService = new CommunicationRecipientService(rockContext); var recipientsQry = GetRecipientsQry(rockContext); // Get all the List member which is not part of communication recipients yet var newMemberInList = qryCommunicationListMembers .Include(c => c.Person) .Where(a => !recipientsQry.Any(r => r.PersonAlias.PersonId == a.PersonId)) .AsNoTracking() .ToList(); var emailMediumEntityType = EntityTypeCache.Get(SystemGuid.EntityType.COMMUNICATION_MEDIUM_EMAIL.AsGuid()); var smsMediumEntityType = EntityTypeCache.Get(SystemGuid.EntityType.COMMUNICATION_MEDIUM_SMS.AsGuid()); var pushMediumEntityType = EntityTypeCache.Get(SystemGuid.EntityType.COMMUNICATION_MEDIUM_PUSH_NOTIFICATION.AsGuid()); var recipientsToAdd = newMemberInList.Select(m => new CommunicationRecipient { PersonAliasId = m.Person.PrimaryAliasId.Value, Status = CommunicationRecipientStatus.Pending, CommunicationId = Id, MediumEntityTypeId = DetermineMediumEntityTypeId( emailMediumEntityType.Id, smsMediumEntityType.Id, pushMediumEntityType.Id, CommunicationType, m.CommunicationPreference, m.Person.CommunicationPreference) }); rockContext.BulkInsert <CommunicationRecipient>(recipientsToAdd); // Get all pending communication recipients that are no longer part of the group list member, then delete them from the Recipients var missingMemberInList = recipientsQry.Where(a => a.Status == CommunicationRecipientStatus.Pending) .Where(a => !qryCommunicationListMembers.Any(r => r.PersonId == a.PersonAlias.PersonId)); rockContext.BulkDelete <CommunicationRecipient>(missingMemberInList); rockContext.SaveChanges(); }
/// <summary> /// creates or updates the entity set on the basis of campaign connection configuration, and returns the Id of the entitySetId /// </summary> /// <param name="campaignConfiguration">The campaign configuration.</param> /// <returns></returns> public static int GetEntitySet(CampaignItem campaignConfiguration) { var rockContext = new RockContext(); var connectionOpportunityService = new ConnectionOpportunityService(rockContext); var connectionRequestService = new ConnectionRequestService(rockContext); var entitySetService = new Rock.Model.EntitySetService(rockContext); var connectionOpportunity = connectionOpportunityService.Get(campaignConfiguration.OpportunityGuid); // list of person on the basis of Dataview result and optout group. var filteredPersonIds = GetFilteredPersonIds(campaignConfiguration, rockContext); // get the last connection datetime. var lastConnectionDateTime = RockDateTime.Now.AddDays(-campaignConfiguration.DaysBetweenConnection); // if DaysBetweenConnection is 0 then check for connection request for any time period. if (campaignConfiguration.DaysBetweenConnection == default(int)) { lastConnectionDateTime = DateTime.MinValue; } // list of person that has active connection request OR has connection closed in the past number of days between connection. var excludedPersonIds = connectionRequestService .Queryable() .Where(a => a.ConnectionOpportunityId == connectionOpportunity.Id && ( a.ConnectionState == ConnectionState.Active || a.ConnectionState == ConnectionState.FutureFollowUp || ((a.ConnectionState == ConnectionState.Connected || a.ConnectionState == ConnectionState.Inactive) && a.ModifiedDateTime > lastConnectionDateTime))) .Select(a => a.PersonAlias.PersonId) .ToList(); // filtered list of person removing all the personIds found in excludedPersonIds List filteredPersonIds = filteredPersonIds.Where(a => !excludedPersonIds.Contains(a)).ToList(); // get the ordered list of personIds based on the oldest previous connection request and connection opportunity /* 2020-05-06 MDP * If there are many filteredPersonIds, we'll get a SQL Exception, so let's get *all* the Connected connection Requests first, * and then use C# to filter. */ var orderedLastCompletedRequestForPerson = connectionRequestService .Queryable() .Where(a => a.ConnectionOpportunityId == connectionOpportunity.Id && a.ConnectionState == ConnectionState.Connected) .GroupBy(a => a.PersonAlias.PersonId) .Select(a => new { PersonId = a.Key, LastConnectionDateTime = a.OrderByDescending(b => b.ModifiedDateTime).Select(b => b.ModifiedDateTime).FirstOrDefault() }) .OrderBy(a => a.LastConnectionDateTime) .Select(a => a.PersonId).ToList(); // Use C# to filter persons so we can avoid a SQL Exception orderedLastCompletedRequestForPerson = orderedLastCompletedRequestForPerson.Where(a => filteredPersonIds.Contains(a)).ToList(); var random = new Random(); //// get the final ordered list of personIds based on the oldest previous connection request and //// connection opportunity otherwise order randomly for the person who don't have any previous connection request. var orderedPersonIds = filteredPersonIds .OrderBy(a => { var index = orderedLastCompletedRequestForPerson.IndexOf(a); if (index == -1) { return(random.Next(orderedLastCompletedRequestForPerson.Count, int.MaxValue)); } else { return(index); } }).ToList(); EntitySet entitySet = null; if (campaignConfiguration.EntitySetId != default(int)) { entitySet = entitySetService.Get(campaignConfiguration.EntitySetId); } List <Rock.Model.EntitySetItem> entitySetItems = new List <Rock.Model.EntitySetItem>(); var personEntityTypeId = EntityTypeCache.Get <Rock.Model.Person>().Id; if (entitySet == null || entitySet.EntityTypeId != personEntityTypeId) { entitySet = new Rock.Model.EntitySet(); entitySet.EntityTypeId = personEntityTypeId; entitySet.ExpireDateTime = null; entitySetService.Add(entitySet); } else { var entitySetItemQry = new EntitySetItemService(rockContext) .Queryable().AsNoTracking() .Where(i => i.EntitySetId == entitySet.Id); rockContext.BulkDelete(entitySetItemQry); } // Update the EntitySet name entitySet.Name = campaignConfiguration.Name; var orderIndex = 0; foreach (var personId in orderedPersonIds) { try { var item = new Rock.Model.EntitySetItem(); item.Order = orderIndex++; item.EntityId = personId; entitySetItems.Add(item); } catch { // ignore } } rockContext.SaveChanges(); entitySetItems.ForEach(a => { a.EntitySetId = entitySet.Id; }); rockContext.BulkInsert(entitySetItems); return(entitySet.Id); }
/// <summary> /// Called after the save operation has been executed /// </summary> /// <remarks> /// This method is only called if <see cref="M:Rock.Data.EntitySaveHook`1.PreSave" /> returns /// without error. /// </remarks> protected override void PostSave() { var rockContext = ( RockContext )this.RockContext; if (HistoryChanges != null) { foreach (var historyItem in HistoryChanges) { int personId = historyItem.PersonId > 0 ? historyItem.PersonId : Entity.PersonId; // if GroupId is 0, it is probably a Group that wasn't saved yet, so get the GroupId from historyItem.Group.Id instead if (historyItem.GroupId == 0) { historyItem.GroupId = historyItem.Group?.Id; } var changes = HistoryService.GetChanges( typeof(Person), Rock.SystemGuid.Category.HISTORY_PERSON_GROUP_MEMBERSHIP.AsGuid(), personId, historyItem.PersonHistoryChangeList, historyItem.Caption, typeof(Group), historyItem.GroupId, Entity.ModifiedByPersonAliasId, rockContext.SourceOfChange); if (changes.Any()) { Task.Run(async() => { // Wait 1 second to allow all post save actions to complete await Task.Delay(1000); try { using (var insertRockContext = new RockContext()) { insertRockContext.BulkInsert(changes); } } catch (SystemException ex) { ExceptionLogService.LogException(ex, null); } }); } var groupMemberChanges = HistoryService.GetChanges( typeof(GroupMember), Rock.SystemGuid.Category.HISTORY_GROUP_CHANGES.AsGuid(), Entity.Id, historyItem.GroupMemberHistoryChangeList, historyItem.Caption, typeof(Group), historyItem.GroupId, Entity.ModifiedByPersonAliasId, rockContext.SourceOfChange); if (groupMemberChanges.Any()) { Task.Run(async() => { // Wait 1 second to allow all post save actions to complete await Task.Delay(1000); try { using (var insertRockContext = new RockContext()) { insertRockContext.BulkInsert(groupMemberChanges); } } catch (SystemException ex) { ExceptionLogService.LogException(ex, null); } }); } } } base.PostSave(); // if this is a GroupMember record on a Family, ensure that AgeClassification, PrimaryFamily, // GivingLeadId, and GroupSalution is updated // NOTE: This is also done on Person.PostSaveChanges in case Birthdate changes var groupTypeFamilyRoleIds = GroupTypeCache.GetFamilyGroupType()?.Roles?.Select(a => a.Id).ToList(); if (groupTypeFamilyRoleIds?.Any() == true) { if (groupTypeFamilyRoleIds.Contains(Entity.GroupRoleId)) { PersonService.UpdatePersonAgeClassification(Entity.PersonId, rockContext); PersonService.UpdatePrimaryFamily(Entity.PersonId, rockContext); PersonService.UpdateGivingLeaderId(Entity.PersonId, rockContext); GroupService.UpdateGroupSalutations(Entity.GroupId, rockContext); if (_preSaveChangesOldGroupId.HasValue && _preSaveChangesOldGroupId.Value != Entity.GroupId) { // if person was moved to a different family, the old family will need its GroupSalutations updated GroupService.UpdateGroupSalutations(_preSaveChangesOldGroupId.Value, rockContext); } } } if (State == EntityContextState.Added || State == EntityContextState.Modified) { if (Entity.Group != null && Entity.Person != null) { if (Entity.Group?.IsSecurityRoleOrSecurityGroupType() == true) { /* 09/27/2021 MDP * * If this GroupMember record results in making this Person having a higher AccountProtectionProfile level, * update the Person's AccountProtectionProfile. * Note: If this GroupMember record could result in making this Person having a *lower* AccountProtectionProfile level, * don't lower the AccountProtectionProfile here, because other rules have to be considered before * lowering the AccountProtectionProfile level. So we'll let the RockCleanup job take care of making sure the * AccountProtectionProfile is updated after factoring in all the rules. * */ if (Entity.Group.ElevatedSecurityLevel >= Utility.Enums.ElevatedSecurityLevel.Extreme && Entity.Person.AccountProtectionProfile < Utility.Enums.AccountProtectionProfile.Extreme) { Entity.Person.AccountProtectionProfile = Utility.Enums.AccountProtectionProfile.Extreme; rockContext.SaveChanges(); } else if (Entity.Group.ElevatedSecurityLevel >= Utility.Enums.ElevatedSecurityLevel.High && Entity.Person.AccountProtectionProfile < Utility.Enums.AccountProtectionProfile.High) { Entity.Person.AccountProtectionProfile = Utility.Enums.AccountProtectionProfile.High; rockContext.SaveChanges(); } } } } }
/// <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))); } }
public void Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; var rockContext = new RockContext(); var commandTimeout = dataMap.GetString("CommandTimeout").AsIntegerOrNull() ?? 3600; rockContext.Database.CommandTimeout = commandTimeout; AttributeValueService attributeValueService = new AttributeValueService(rockContext); PersonService personService = new PersonService(rockContext); var personAliasQry = new PersonAliasService(rockContext).Queryable(); var familyGroupMemberQry = new GroupMemberService(rockContext).Queryable().Where(gm => gm.Group.GroupTypeId == 10); var groupQry = new GroupService(rockContext).Queryable().Where(g => g.GroupTypeId == 10); var attendanceQry = new AttendanceService(rockContext).Queryable().Where(a => a.DidAttend == true); var currentlyEraAttribue = AttributeCache.Get("CE5739C5-2156-E2AB-48E5-1337C38B935E"); var firstAttendanceAttribute = AttributeCache.Get("8F404727-82A5-4855-9714-62DFAB834BB2"); var eraStartDateAttribute = AttributeCache.Get("A106610C-A7A1-469E-4097-9DE6400FDFC2"); var attendanceVerificationMethod = AttributeCache.Get("7244EF82-FD1E-4B00-B2A1-114A79F09555"); var attendanceVerifiedType = AttributeCache.Get("64C05D55-1697-4773-AE14-5C9596B71FF4"); var historyCategory = CategoryCache.Get(Rock.SystemGuid.Category.HISTORY_PERSON_DEMOGRAPHIC_CHANGES); var personEntityType = EntityTypeCache.Get(typeof(Rock.Model.Person)); var attributeEntityType = EntityTypeCache.Get(typeof(Rock.Model.Attribute)); var inEraQry = attributeValueService.Queryable() .Where(av => av.AttributeId == currentlyEraAttribue.Id && av.Value == "True") .Select(av => av.EntityId); var firstAttQry = attributeValueService.Queryable() .Where(av => av.AttributeId == firstAttendanceAttribute.Id && av.Value != null && av.Value != "") .Select(av => av.EntityId); var eraStartQry = attributeValueService.Queryable() .Where(av => av.AttributeId == eraStartDateAttribute.Id); var take = dataMap.GetString("Take").AsIntegerOrNull() ?? 1000; //Linq! var people = personService.Queryable() //Get all the people .Where(p => inEraQry.Contains(p.Id)) //Who are era .Where(p => !firstAttQry.Contains(p.Id)) //And don't have a first attendance .Take(take) .Join( //Get the ERA Start Date eraStartQry, p => p.Id, a => a.EntityId, (p, a) => new { Person = p, EraStartDate = a.Value }) .GroupJoin( //Get group membership for all family groups familyGroupMemberQry, o => o.Person.Id, gm => gm.PersonId, (o, gm) => new { o.Person, o.EraStartDate, FirstAttendance = gm.Select(gm2 => gm2.Group).SelectMany(g => g.Members.Select(gm3 => gm3.Person)) //Get all family members .GroupJoin( personAliasQry, //Get all person alias ids for all family members p => p.Id, pa => pa.PersonId, (p, pa) => pa.GroupJoin( attendanceQry, //Get all attendance records for all family members pa2 => pa2.Id, a => a.PersonAliasId, (pa2, a) => a) .SelectMany(a => a)) //Compact .SelectMany(a => a) //Compact .OrderBy(a2 => a2.StartDateTime) //Sort .FirstOrDefault() //Get first }) .ToList(); var counter = 0; foreach (var person in people) { try { //Before we continue. We need to remove any existing attribute values. var removeQry = string.Format(@"delete from AttributeValue where EntityId = {0} and AttributeId in ({1},{2},{3})", person.Person.Id, firstAttendanceAttribute.Id, attendanceVerificationMethod.Id, attendanceVerifiedType.Id); rockContext.Database.ExecuteSqlCommand(removeQry); //Lets add in the attribute values! if (person.FirstAttendance != null) { var methodAV = new AttributeValue { IsSystem = false, AttributeId = attendanceVerificationMethod.Id, EntityId = person.Person.Id, Value = "Automatic" }; var methodHistory = new History { IsSystem = false, CategoryId = historyCategory.Id, EntityTypeId = personEntityType.Id, EntityId = person.Person.Id, RelatedEntityTypeId = attributeEntityType.Id, RelatedEntityId = attendanceVerificationMethod.Id, Verb = "MODIFY", ChangeType = "Property", ValueName = "Attendance Verification Method", NewValue = "Automatic" }; var attendanceTypeAV = new AttributeValue { IsSystem = false, AttributeId = attendanceVerifiedType.Id, EntityId = person.Person.Id, Value = "SECC Attendance" }; var attendanceTypeHistory = new History { IsSystem = false, CategoryId = historyCategory.Id, EntityTypeId = personEntityType.Id, EntityId = person.Person.Id, RelatedEntityTypeId = attributeEntityType.Id, RelatedEntityId = attendanceVerifiedType.Id, Verb = "MODIFY", ChangeType = "Property", ValueName = "Attendance Verification Type", NewValue = "SECC Attendance" }; var firstAttendanceAV = new AttributeValue { IsSystem = false, AttributeId = firstAttendanceAttribute.Id, EntityId = person.Person.Id, Value = person.FirstAttendance.StartDateTime.Date.ToString("MM/dd/yyyy") }; var firstAttendanceHistory = new History { IsSystem = false, CategoryId = historyCategory.Id, EntityTypeId = personEntityType.Id, EntityId = person.Person.Id, RelatedEntityTypeId = attributeEntityType.Id, RelatedEntityId = firstAttendanceAttribute.Id, Verb = "MODIFY", ChangeType = "Property", ValueName = "Attendance 1st Verified Date", NewValue = person.FirstAttendance.StartDateTime.Date.ToString("MM/dd/yyyy") }; rockContext.BulkInsert(new List <AttributeValue> { methodAV, attendanceTypeAV, firstAttendanceAV }); rockContext.BulkInsert(new List <History> { methodHistory, attendanceTypeHistory, firstAttendanceHistory }); } else { var methodAV = new AttributeValue { IsSystem = false, AttributeId = attendanceVerificationMethod.Id, EntityId = person.Person.Id, Value = "Automatic" }; var methodHistory = new History { IsSystem = false, CategoryId = historyCategory.Id, EntityTypeId = personEntityType.Id, EntityId = person.Person.Id, RelatedEntityTypeId = attributeEntityType.Id, RelatedEntityId = attendanceVerificationMethod.Id, Verb = "MODIFY", ChangeType = "Property", ValueName = "Attendance Verification Method", NewValue = "Automatic" }; var attendanceTypeAV = new AttributeValue { IsSystem = false, AttributeId = attendanceVerifiedType.Id, EntityId = person.Person.Id, Value = "eRA Start Date" }; var attendanceTypeHistory = new History { IsSystem = false, CategoryId = historyCategory.Id, EntityTypeId = personEntityType.Id, EntityId = person.Person.Id, RelatedEntityTypeId = attributeEntityType.Id, RelatedEntityId = attendanceVerifiedType.Id, Verb = "MODIFY", ChangeType = "Property", ValueName = "Attendance Verification Type", NewValue = "eRA Start Date" }; var firstAttendanceAV = new AttributeValue { IsSystem = false, AttributeId = firstAttendanceAttribute.Id, EntityId = person.Person.Id, Value = person.EraStartDate.AsDateTime().Value.Date.ToString("MM/dd/yyyy") }; var firstAttendanceHistory = new History { IsSystem = false, CategoryId = historyCategory.Id, EntityTypeId = personEntityType.Id, EntityId = person.Person.Id, RelatedEntityTypeId = attributeEntityType.Id, RelatedEntityId = firstAttendanceAttribute.Id, Verb = "MODIFY", ChangeType = "Property", ValueName = "Attendance 1st Verified Date", NewValue = person.EraStartDate.AsDateTime().Value.Date.ToString("MM/dd/yyyy") }; rockContext.BulkInsert(new List <AttributeValue> { methodAV, attendanceTypeAV, firstAttendanceAV }); rockContext.BulkInsert(new List <History> { methodHistory, attendanceTypeHistory, firstAttendanceHistory }); } counter++; } catch (Exception e) { var ex = string.Format("Could not set first Attendance data for {0} - {1}", person.Person.FullName, person.Person.Id); ExceptionLogService.LogException(new Exception(ex, e)); } if (counter % 100 == 0) { var jobId = context.GetJobId(); var jobService = new ServiceJobService(rockContext); var job = jobService.Get(jobId); if (job != null) { job.LastStatusMessage = string.Format("Updated {0} People of {1}", counter, people.Count); rockContext.SaveChanges(false); } } } }
/// <summary> /// Updates the last login, writes to the person's history log, and saves changes to the database /// </summary> /// <param name="userName">Name of the user.</param> public static void UpdateLastLogin(string userName) { if (string.IsNullOrWhiteSpace(userName)) { return; } int? personId = null; bool impersonated = userName.StartsWith("rckipid="); using (var rockContext = new RockContext()) { if (!impersonated) { var userLogin = new UserLoginService(rockContext).Queryable().Where(a => a.UserName == userName).FirstOrDefault(); if (userLogin != null) { userLogin.LastLoginDateTime = RockDateTime.Now; personId = userLogin.PersonId; rockContext.SaveChanges(); } } else { var impersonationToken = userName.Substring(8); personId = new PersonService(rockContext).GetByImpersonationToken(impersonationToken, false, null)?.Id; } } if (personId == null) { return; } var relatedDataBuilder = new System.Text.StringBuilder(); int?relatedEntityTypeId = null; int?relatedEntityId = null; if (impersonated) { var impersonatedByUser = HttpContext.Current?.Session?["ImpersonatedByUser"] as UserLogin; relatedEntityTypeId = EntityTypeCache.GetId <Rock.Model.Person>(); relatedEntityId = impersonatedByUser?.PersonId; if (impersonatedByUser != null) { relatedDataBuilder.Append($" impersonated by { impersonatedByUser.Person.FullName }"); } } if (HttpContext.Current != null && HttpContext.Current.Request != null) { string cleanUrl = PersonToken.ObfuscateRockMagicToken(HttpContext.Current.Request.UrlProxySafe().AbsoluteUri); // obfuscate the URL specified in the returnurl, just in case it contains any sensitive information (like a rckipid) Regex returnurlRegEx = new Regex(@"returnurl=([^&]*)"); cleanUrl = returnurlRegEx.Replace(cleanUrl, "returnurl=XXXXXXXXXXXXXXXXXXXXXXXXXXXX"); string clientIPAddress; try { clientIPAddress = Rock.Web.UI.RockPage.GetClientIpAddress(); } catch { // if we get an exception getting the IP Address, just ignore it clientIPAddress = ""; } relatedDataBuilder.AppendFormat( " to <span class='field-value'>{0}</span>, from <span class='field-value'>{1}</span>", cleanUrl, clientIPAddress); } var historyChangeList = new History.HistoryChangeList(); var historyChange = historyChangeList.AddChange(History.HistoryVerb.Login, History.HistoryChangeType.Record, userName); if (relatedDataBuilder.Length > 0) { historyChange.SetRelatedData(relatedDataBuilder.ToString(), null, null); } var historyList = HistoryService.GetChanges(typeof(Rock.Model.Person), Rock.SystemGuid.Category.HISTORY_PERSON_ACTIVITY.AsGuid(), personId.Value, historyChangeList, null, null, null, null, null); if (historyList.Any()) { Task.Run(async() => { // Wait 1 second to allow all post save actions to complete await Task.Delay(1000); try { using (var rockContext = new RockContext()) { rockContext.BulkInsert(historyList); } } catch (SystemException ex) { ExceptionLogService.LogException(ex, null); } }); } }
/// <summary> /// Updates GroupLocationHistorical for any group locations in groups that have data group history enabled /// </summary> /// <param name="context">The context.</param> public void UpdateGroupLocationHistorical(IJobExecutionContext context) { var rockContext = new RockContext(); var groupLocationHistoricalService = new GroupLocationHistoricalService(rockContext); var groupLocationService = new GroupLocationService(rockContext); var groupLocationsWithHistoryEnabledQuery = groupLocationService.Queryable().Where(a => a.Group.GroupType.EnableGroupHistory == true).AsNoTracking(); var groupLocationsHistoricalCurrentQuery = groupLocationHistoricalService.Queryable().Where(a => a.CurrentRowIndicator == true).AsNoTracking(); // Mark GroupLocationHistorical Rows as History ( CurrentRowIndicator = false, etc ) if any of the tracked field values change var groupLocationHistoricalNoLongerCurrentQuery = groupLocationsHistoricalCurrentQuery.Join( groupLocationsWithHistoryEnabledQuery, glh => glh.GroupLocationId, gl => gl.Id, (glh, gl) => new { GroupLocation = gl, GroupLocationHistorical = glh }) .Where(a => a.GroupLocation.GroupId != a.GroupLocationHistorical.GroupId || (a.GroupLocation.GroupLocationTypeValueId != a.GroupLocation.GroupLocationTypeValueId) || (a.GroupLocation.GroupLocationTypeValueId.HasValue && a.GroupLocation.GroupLocationTypeValue.Value != a.GroupLocationHistorical.GroupLocationTypeName) || a.GroupLocation.LocationId != a.GroupLocationHistorical.LocationId || a.GroupLocation.Location.ModifiedDateTime != a.GroupLocationHistorical.LocationModifiedDateTime || (a.GroupLocation.Schedules.Select(s => new { ScheduleId = s.Id, s.ModifiedDateTime }).Except(a.GroupLocationHistorical.GroupLocationHistoricalSchedules.Select(hs => new { hs.ScheduleId, ModifiedDateTime = hs.ScheduleModifiedDateTime }))).Any() ); var effectiveExpireDateTime = RockDateTime.Now; int groupLocationsLoggedToHistory = 0; int groupLocationsSaveToHistoryCurrent = 0; if (groupLocationHistoricalNoLongerCurrentQuery.Any()) { var groupLocationHistoricalNoLongerCurrent = groupLocationHistoricalNoLongerCurrentQuery.Select(a => a.GroupLocationHistorical).AsNoTracking(); groupLocationsLoggedToHistory = rockContext.BulkUpdate(groupLocationHistoricalNoLongerCurrent, glh => new GroupLocationHistorical { CurrentRowIndicator = false, ExpireDateTime = effectiveExpireDateTime }); } // Insert Group Locations (that have a group with GroupType.EnableGroupHistory) that don't have a CurrentRowIndicator row yet ( or don't have a CurrentRowIndicator because it was stamped with CurrentRowIndicator=false ) var groupLocationsToAddToHistoricalCurrentsQuery = groupLocationsWithHistoryEnabledQuery.Where(gl => !groupLocationsHistoricalCurrentQuery.Any(glh => glh.GroupLocationId == gl.Id)); if (groupLocationsToAddToHistoricalCurrentsQuery.Any()) { List <GroupLocationHistorical> groupLocationHistoricalCurrentsToInsert = groupLocationsToAddToHistoricalCurrentsQuery .Include(a => a.GroupLocationTypeValue) .Include(a => a.Location).ToList() .Select(gl => GroupLocationHistorical.CreateCurrentRowFromGroupLocation(gl, effectiveExpireDateTime)).ToList(); groupLocationsSaveToHistoryCurrent = groupLocationHistoricalCurrentsToInsert.Count(); // get the current max GroupLocatiionHistorical.Id to help narrow down which ones were inserted int groupLocationHistoricalStartId = groupLocationHistoricalService.Queryable().Max(a => ( int? )a.Id) ?? 0; rockContext.BulkInsert(groupLocationHistoricalCurrentsToInsert); // since we used BulkInsert, we'll need to go back and get the Ids and the associated GroupLocation's Schedules for the GroupLocationHistorical records that we just inserted var insertedGroupLocationHistoricalIdsWithSchedules = groupLocationHistoricalService.Queryable() .Where(a => a.Id > groupLocationHistoricalStartId && a.GroupLocation.Schedules.Any()).ToList() .Select(a => new { GroupLocationHistoricalId = a.Id, a.GroupLocation.Schedules }); List <GroupLocationHistoricalSchedule> groupLocationHistoricalScheduleCurrentsToInsert = new List <GroupLocationHistoricalSchedule>(); foreach (var insertedGroupLocationHistoricalIdWithSchedules in insertedGroupLocationHistoricalIdsWithSchedules) { foreach (Schedule schedule in insertedGroupLocationHistoricalIdWithSchedules.Schedules) { groupLocationHistoricalScheduleCurrentsToInsert.Add(new GroupLocationHistoricalSchedule { GroupLocationHistoricalId = insertedGroupLocationHistoricalIdWithSchedules.GroupLocationHistoricalId, ScheduleId = schedule.Id, ScheduleName = schedule.ToString(), ScheduleModifiedDateTime = schedule.ModifiedDateTime }); } } if (groupLocationHistoricalScheduleCurrentsToInsert.Any()) { rockContext.BulkInsert(groupLocationHistoricalScheduleCurrentsToInsert); } } if (groupLocationsLoggedToHistory > 0) { _jobStatusMessages.Add($"Logged {groupLocationsLoggedToHistory} {"group location history snapshot".PluralizeIf( groupLocationsLoggedToHistory != 0 )}"); } if (groupLocationsSaveToHistoryCurrent > 0) { int newGroupLocationsAddedToHistory = groupLocationsSaveToHistoryCurrent - groupLocationsLoggedToHistory; if (newGroupLocationsAddedToHistory > 0) { _jobStatusMessages.Add($"Added {newGroupLocationsAddedToHistory} new {"group location history snapshot".PluralizeIf( newGroupLocationsAddedToHistory != 0 )}"); } } }
/// <summary> /// Logs the interactions. /// </summary> /// <param name="interactionTransactionInfos">The interaction transaction infos to process.</param> /// <param name="rockContext">The rock context.</param> private void LogInteractions(List <InteractionTransactionInfo> interactionTransactionInfos, RockContext rockContext) { List <Interaction> interactionsToInsert = new List <Interaction>(); var interactionService = new InteractionService(rockContext); foreach (var info in interactionTransactionInfos.Where(a => a.InteractionComponentId.HasValue)) { /* * 2020-06-29 - JH * * The 'CreateInteraction(...)' method called below sets the following properties on the Interaction object: * * - InteractionComponentId * - InteractionDateTime (but with the wrong value) * - InteractionSessionId * - Source * - Medium * - Campaign * - Content * - Term */ var interaction = interactionService.CreateInteraction(info.InteractionComponentId.Value, info.UserAgent, info.InteractionData, info.IPAddress, info.BrowserSessionId); // The rest of the properties need to be manually set. interaction.EntityId = info.InteractionEntityId; interaction.Operation = info.InteractionOperation.IsNotNullOrWhiteSpace() ? info.InteractionOperation.Trim() : "View"; interaction.InteractionSummary = info.InteractionSummary?.Trim(); interaction.PersonAliasId = info.PersonAliasId; interaction.InteractionDateTime = info.InteractionDateTime; interaction.InteractionTimeToServe = info.InteractionTimeToServe; interaction.RelatedEntityTypeId = info.InteractionRelatedEntityTypeId; interaction.RelatedEntityId = info.InteractionRelatedEntityId; interaction.ChannelCustom1 = info.InteractionChannelCustom1?.Trim(); interaction.ChannelCustom2 = info.InteractionChannelCustom2?.Trim(); interaction.ChannelCustomIndexed1 = info.InteractionChannelCustomIndexed1?.Trim(); interaction.Source = interaction.Source ?? info.InteractionSource?.Trim(); interaction.Medium = interaction.Medium ?? info.InteractionMedium?.Trim(); interaction.Campaign = interaction.Campaign ?? info.InteractionCampaign?.Trim(); interaction.Content = interaction.Content ?? info.InteractionContent?.Trim(); interaction.Term = interaction.Term ?? info.InteractionTerm?.Trim(); interaction.InteractionLength = info.InteractionLength; interaction.InteractionEndDateTime = info.InteractionEndDateTime; interaction.SetInteractionData(info.InteractionData?.Trim()); interactionsToInsert.Add(interaction); } rockContext.BulkInsert(interactionsToInsert); // This logic is normally handled in the Interaction.PostSave method, but since the BulkInsert bypasses those // model hooks, streaks need to be updated here. Also, it is not necessary for this logic to complete before this // transaction can continue processing and exit, so update the streak using a task. // Only launch this task if there are StreakTypes configured that have interactions. Otherwise several // database calls are made only to find out there are no streak types defined. if (StreakTypeCache.All().Any(s => s.IsInteractionRelated)) { // Ids do not exit for the interactions in the collection since they were bulk imported. // Read their ids from their guids and append the id. var insertedGuids = interactionsToInsert.Select(i => i.Guid).ToList(); var interactionIds = new InteractionService(new RockContext()).Queryable() .Where(i => insertedGuids.Contains(i.Guid)) .Select(i => new { i.Id, i.Guid }) .ToList(); foreach (var interactionId in interactionIds) { var interaction = interactionsToInsert.Where(i => i.Guid == interactionId.Guid).FirstOrDefault(); if (interaction != null) { interaction.Id = interactionId.Id; } } // Launch task interactionsToInsert.ForEach(i => Task.Run(() => StreakTypeService.HandleInteractionRecord(i.Id))); } }
/// <summary> /// Migrates the scheduled transaction notes to history. /// </summary> public void MigrateScheduledTransactionNotesToHistory() { var rockContext = new RockContext(); rockContext.Database.CommandTimeout = _commandTimeout; var noteService = new NoteService(rockContext); var historyCategoryId = CategoryCache.Get(Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid())?.Id; var entityTypeIdScheduledTransaction = EntityTypeCache.GetId(Rock.SystemGuid.EntityType.FINANCIAL_SCHEDULED_TRANSACTION.AsGuid()); var noteTypeIdScheduledTransaction = NoteTypeCache.GetId(Rock.SystemGuid.NoteType.SCHEDULED_TRANSACTION_NOTE.AsGuid()); if (!historyCategoryId.HasValue || !entityTypeIdScheduledTransaction.HasValue || !noteTypeIdScheduledTransaction.HasValue) { return; } var historyService = new HistoryService(rockContext); var historyQuery = historyService.Queryable().Where(a => a.EntityTypeId == entityTypeIdScheduledTransaction.Value); var captionsToConvert = new string[] { "Created Transaction" , "Updated Transaction" , "Cancelled Transaction" , "Reactivated Transaction" }; var notesToConvertToHistory = noteService.Queryable() .Where(a => a.NoteTypeId == noteTypeIdScheduledTransaction.Value && captionsToConvert.Contains(a.Caption) && a.EntityId.HasValue) .Where(a => !historyQuery.Any(h => h.EntityId == a.EntityId)); var notesToConvertToSummaryList = noteService.Queryable() .Where(a => a.NoteTypeId == noteTypeIdScheduledTransaction.Value && a.Caption == "Created Transaction" && !string.IsNullOrEmpty(a.Text) && a.EntityId.HasValue) .AsNoTracking().ToList(); List <History> historyRecordsToInsert = notesToConvertToHistory.AsNoTracking() .ToList() .Select(n => { var historyRecord = new History { CategoryId = historyCategoryId.Value, EntityTypeId = entityTypeIdScheduledTransaction.Value, EntityId = n.EntityId.Value, Guid = Guid.NewGuid(), CreatedByPersonAliasId = n.CreatedByPersonAliasId, ModifiedByPersonAliasId = n.ModifiedByPersonAliasId, CreatedDateTime = n.CreatedDateTime, ModifiedDateTime = n.ModifiedDateTime }; if (n.Caption == "Cancelled Transaction") { historyRecord.Verb = "MODIFY"; historyRecord.ChangeType = "Property"; historyRecord.ValueName = "Is Active"; historyRecord.NewValue = "False"; } else if (n.Caption == "Reactivated Transaction") { historyRecord.Verb = "MODIFY"; historyRecord.ChangeType = "Property"; historyRecord.ValueName = "Is Active"; historyRecord.NewValue = "True"; } else if (n.Caption == "Updated Transaction") { historyRecord.Verb = "MODIFY"; historyRecord.ValueName = "Transaction"; } else { historyRecord.Verb = "ADD"; historyRecord.ChangeType = "Record"; historyRecord.ValueName = "Transaction"; } return(historyRecord); }).ToList(); rockContext.BulkInsert(historyRecordsToInsert); var qryNotesToDelete = noteService.Queryable().Where(a => a.NoteTypeId == noteTypeIdScheduledTransaction && captionsToConvert.Contains(a.Caption)); rockContext.BulkDelete(qryNotesToDelete); foreach (var noteToConvertToSummary in notesToConvertToSummaryList) { using (var updatedSummaryContext = new RockContext()) { var scheduledTransactionService = new FinancialScheduledTransactionService(updatedSummaryContext); var scheduledTransaction = scheduledTransactionService.Get(noteToConvertToSummary.EntityId.Value); if (scheduledTransaction != null && scheduledTransaction.Summary.IsNullOrWhiteSpace()) { scheduledTransaction.Summary = noteToConvertToSummary.Text; updatedSummaryContext.SaveChanges(disablePrePostProcessing: true); } } } }
/// <summary> /// Updates Group Historical for any groups that have data group history enabled /// </summary> /// <param name="context">The context.</param> public void UpdateGroupHistorical(IJobExecutionContext context) { var rockContext = new RockContext(); var groupHistoricalService = new GroupHistoricalService(rockContext); var groupService = new GroupService(rockContext); // Note that this query utilizes .AsNoFilter() to avoid having archived groups filtered out by the GroupConfiguration class. var groupsWithHistoryEnabledQuery = groupService.AsNoFilter() .Where(a => a.GroupType.EnableGroupHistory == true) .AsNoTracking(); var groupHistoricalsCurrentQuery = groupHistoricalService.Queryable().Where(a => a.CurrentRowIndicator == true).AsNoTracking(); // Mark GroupHistorical Rows as History ( CurrentRowIndicator = false, etc ) if any of the tracked field values change var groupHistoricalNoLongerCurrent = groupHistoricalsCurrentQuery.Join( groupsWithHistoryEnabledQuery, gh => gh.GroupId, g => g.Id, (gh, g) => new { Group = g, GroupHistorical = gh }) .Where(a => a.Group.Name != a.GroupHistorical.GroupName || a.Group.GroupType.Name != a.GroupHistorical.GroupTypeName || a.Group.CampusId != a.GroupHistorical.CampusId || a.Group.ParentGroupId != a.GroupHistorical.ParentGroupId || a.Group.ScheduleId != a.GroupHistorical.ScheduleId || (a.Group.ScheduleId.HasValue && (a.Group.Schedule.ModifiedDateTime != a.GroupHistorical.ScheduleModifiedDateTime)) || a.Group.Description != a.GroupHistorical.Description || a.Group.StatusValueId != a.GroupHistorical.StatusValueId || a.Group.IsArchived != a.GroupHistorical.IsArchived || a.Group.ArchivedDateTime != a.GroupHistorical.ArchivedDateTime || a.Group.ArchivedByPersonAliasId != a.GroupHistorical.ArchivedByPersonAliasId || a.Group.IsActive != a.GroupHistorical.IsActive || a.Group.InactiveDateTime != a.GroupHistorical.InactiveDateTime ).Select(a => a.GroupHistorical).AsNoTracking(); var effectiveExpireDateTime = RockDateTime.Now; int groupsLoggedToHistory = 0; int groupsSaveToHistoryCurrent = 0; if (groupHistoricalNoLongerCurrent.Any()) { groupsLoggedToHistory = rockContext.BulkUpdate(groupHistoricalNoLongerCurrent, gh => new GroupHistorical { CurrentRowIndicator = false, ExpireDateTime = effectiveExpireDateTime }); } // Insert Groups (that have GroupType.EnableGroupHistory) that don't have a CurrentRowIndicator row yet ( or don't have a CurrentRowIndicator because it was stamped with CurrentRowIndicator=false ) var groupsToAddToHistoricalCurrentsQuery = groupsWithHistoryEnabledQuery.Where(g => !groupHistoricalsCurrentQuery.Any(gh => gh.GroupId == g.Id)).AsNoTracking(); if (groupsToAddToHistoricalCurrentsQuery.Any()) { List <GroupHistorical> groupHistoricalCurrentsToInsert = groupsToAddToHistoricalCurrentsQuery .Include(a => a.GroupType) .Include(a => a.Schedule) .ToList() .Select(g => GroupHistorical.CreateCurrentRowFromGroup(g, effectiveExpireDateTime)).ToList(); groupsSaveToHistoryCurrent = groupHistoricalCurrentsToInsert.Count(); rockContext.BulkInsert(groupHistoricalCurrentsToInsert); } if (groupsLoggedToHistory > 0) { _jobStatusMessages.Add($"Logged {groupsLoggedToHistory} {"group history snapshot".PluralizeIf( groupsLoggedToHistory != 0 )}"); } if (groupsSaveToHistoryCurrent > 0) { int newGroupsAddedToHistory = groupsSaveToHistoryCurrent - groupsLoggedToHistory; if (newGroupsAddedToHistory > 0) { _jobStatusMessages.Add($"Added {newGroupsAddedToHistory} new {"group history snapshot".PluralizeIf( newGroupsAddedToHistory != 0 )}"); } } }
/// <summary> /// Handles the Click event of the btnCreateCommunication control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void btnCreateCommunication_Click(object sender, EventArgs e) { // Create communication var rockContext = new RockContext(); var communicationService = new Rock.Model.CommunicationService(rockContext); var communication = new Rock.Model.Communication(); communication.IsBulkCommunication = false; communication.Status = CommunicationStatus.Transient; communication.SenderPersonAliasId = this.CurrentPersonAliasId; if (this.Request != null && this.Request.Url != null) { communication.UrlReferrer = this.Request.UrlProxySafe().AbsoluteUri.TrimForMaxLength(communication, "UrlReferrer"); } communicationService.Add(communication); // save communication to get Id rockContext.SaveChanges(); int[] scheduleIds = lbSchedules.SelectedValuesAsInt.ToArray(); int[] locationIds = cblLocations.SelectedValuesAsInt.ToArray(); List <int> parentGroupIds = gpGroups.SelectedValuesAsInt().ToList(); var allGroupIds = new List <int>(); allGroupIds.AddRange(parentGroupIds); if (cbIncludeChildGroups.Checked) { var groupService = new GroupService(rockContext); foreach (var groupId in parentGroupIds) { // just the first level of child groups, not all decendants var childGroupIds = groupService.Queryable() .Where(a => a.ParentGroupId == groupId && a.GroupType.IsSchedulingEnabled && !a.DisableScheduling) .Select(a => a.Id).ToList(); allGroupIds.AddRange(childGroupIds); } } allGroupIds = allGroupIds.Distinct().ToList(); var attendanceOccurrenceService = new AttendanceOccurrenceService(rockContext); var sundayDate = ddlWeek.SelectedValue.AsDateTime() ?? RockDateTime.Now.SundayDate(); var attendanceOccurrenceQuery = attendanceOccurrenceService .Queryable() .Where(a => a.ScheduleId.HasValue && a.LocationId.HasValue && a.GroupId.HasValue) .WhereDeducedIsActive() .Where(a => allGroupIds.Contains(a.GroupId.Value)) .Where(a => locationIds.Contains(a.LocationId.Value)) .Where(a => scheduleIds.Contains(a.ScheduleId.Value)) .Where(a => a.SundayDate == sundayDate); ScheduledAttendanceItemStatus[] selectedInviteStatus = cblInviteStatus.SelectedValues .Select(a => a.ConvertToEnum <ScheduledAttendanceItemStatus>()) .ToArray(); // limit attendees to ones based on the selected invite status var scheduledAttendancesForOccurrenceQuery = attendanceOccurrenceQuery .SelectMany(a => a.Attendees) .WhereHasScheduledAttendanceItemStatus(selectedInviteStatus); var personIds = scheduledAttendancesForOccurrenceQuery.Select(a => a.PersonAlias.PersonId).Distinct().ToList(); if (!personIds.Any()) { nbCommunicationWarning.Text = "No people found to send communication to."; nbCommunicationWarning.Visible = true; return; } nbCommunicationWarning.Visible = false; var personAliasService = new Rock.Model.PersonAliasService(new Rock.Data.RockContext()); // Get the primary aliases List <Rock.Model.PersonAlias> primaryAliasList = new List <PersonAlias>(personIds.Count); // get the data in chunks just in case we have a large list of PersonIds (to avoid a SQL Expression limit error) var chunkedPersonIds = personIds.Take(1000); int skipCount = 0; while (chunkedPersonIds.Any()) { var chunkedPrimaryAliasList = personAliasService.Queryable() .Where(p => p.PersonId == p.AliasPersonId && chunkedPersonIds.Contains(p.PersonId)).AsNoTracking().ToList(); primaryAliasList.AddRange(chunkedPrimaryAliasList); skipCount += 1000; chunkedPersonIds = personIds.Skip(skipCount).Take(1000); } // NOTE: Set CreatedDateTime, ModifiedDateTime, etc manually set we are using BulkInsert var currentDateTime = RockDateTime.Now; var currentPersonAliasId = this.CurrentPersonAliasId; var communicationRecipientList = primaryAliasList.Select(a => new Rock.Model.CommunicationRecipient { CommunicationId = communication.Id, PersonAliasId = a.Id, CreatedByPersonAliasId = currentPersonAliasId, ModifiedByPersonAliasId = currentPersonAliasId, CreatedDateTime = currentDateTime, ModifiedDateTime = currentDateTime }).ToList(); // BulkInsert to quickly insert the CommunicationRecipient records. Note: This is much faster, but will bypass EF and Rock processing. var communicationRecipientRockContext = new RockContext(); communicationRecipientRockContext.BulkInsert(communicationRecipientList); var pageRef = this.RockPage.Site.CommunicationPageReference; string communicationUrl; if (pageRef.PageId > 0) { pageRef.Parameters.AddOrReplace("CommunicationId", communication.Id.ToString()); communicationUrl = pageRef.BuildUrl(); } else { communicationUrl = "~/Communication/{0}"; } if (communicationUrl.Contains("{0}")) { communicationUrl = string.Format(communicationUrl, communication.Id); } UserPreferenceConfiguration userPreferenceConfiguration = this.GetBlockUserPreference(UserPreferenceKey.UserPreferenceConfigurationJSON).FromJsonOrNull <UserPreferenceConfiguration>() ?? new UserPreferenceConfiguration(); userPreferenceConfiguration.GroupIds = gpGroups.SelectedValuesAsInt().ToArray(); userPreferenceConfiguration.IncludeChildGroups = cbIncludeChildGroups.Checked; userPreferenceConfiguration.InviteStatuses = cblInviteStatus.SelectedValues.ToArray(); userPreferenceConfiguration.ScheduleIds = lbSchedules.SelectedValuesAsInt.ToArray(); userPreferenceConfiguration.LocationIds = cblLocations.SelectedValuesAsInt.ToArray(); this.SetBlockUserPreference(UserPreferenceKey.UserPreferenceConfigurationJSON, userPreferenceConfiguration.ToJson()); Page.Response.Redirect(communicationUrl, false); Context.ApplicationInstance.CompleteRequest(); }
/// <summary> /// Updates GroupMemberHistorical for any group members in groups that have data group history enabled /// </summary> /// <param name="context">The context.</param> public void UpdateGroupMemberHistorical(IJobExecutionContext context) { var rockContext = new RockContext(); var groupMemberHistoricalService = new GroupMemberHistoricalService(rockContext); var groupMemberService = new GroupMemberService(rockContext); var groupMembersWithHistoryEnabledQuery = groupMemberService.AsNoFilter().Where(a => a.Group.GroupType.EnableGroupHistory == true).AsNoTracking(); var groupMemberHistoricalsCurrentQuery = groupMemberHistoricalService.Queryable().Where(a => a.CurrentRowIndicator == true).AsNoTracking(); // Mark GroupMemberHistorical Rows as History ( CurrentRowIndicator = false, etc ) if any of the tracked field values change var groupMemberHistoricalNoLongerCurrent = groupMemberHistoricalsCurrentQuery.Join( groupMembersWithHistoryEnabledQuery, gmh => gmh.GroupMemberId, gm => gm.Id, (gmh, gm) => new { GroupMember = gm, GroupMemberHistorical = gmh }) .Where(a => a.GroupMember.GroupRoleId != a.GroupMemberHistorical.GroupRoleId || a.GroupMember.GroupId != a.GroupMemberHistorical.GroupId || a.GroupMember.GroupRole.Name != a.GroupMemberHistorical.GroupRoleName || a.GroupMember.GroupRole.IsLeader != a.GroupMemberHistorical.IsLeader || a.GroupMember.GroupMemberStatus != a.GroupMemberHistorical.GroupMemberStatus || a.GroupMember.IsArchived != a.GroupMemberHistorical.IsArchived || a.GroupMember.ArchivedDateTime != a.GroupMemberHistorical.ArchivedDateTime || a.GroupMember.ArchivedByPersonAliasId != a.GroupMemberHistorical.ArchivedByPersonAliasId || a.GroupMember.InactiveDateTime != a.GroupMemberHistorical.InactiveDateTime ).Select(a => a.GroupMemberHistorical).AsNoTracking(); var effectiveExpireDateTime = RockDateTime.Now; int groupMembersLoggedToHistory = 0; int groupMembersSaveToHistoryCurrent = 0; if (groupMemberHistoricalNoLongerCurrent.Any()) { groupMembersLoggedToHistory = rockContext.BulkUpdate(groupMemberHistoricalNoLongerCurrent, gmh => new GroupMemberHistorical { CurrentRowIndicator = false, ExpireDateTime = effectiveExpireDateTime }); } // Insert Group Members (that have a group with GroupType.EnableGroupHistory) that don't have a CurrentRowIndicator row yet ( or don't have a CurrentRowIndicator because it was stamped with CurrentRowIndicator=false ) var groupMembersToAddToHistoricalCurrentsQuery = groupMembersWithHistoryEnabledQuery.Where(gm => !groupMemberHistoricalsCurrentQuery.Any(gmh => gmh.GroupMemberId == gm.Id)); if (groupMembersToAddToHistoricalCurrentsQuery.Any()) { List <GroupMemberHistorical> groupMemberHistoricalCurrentsToInsert = groupMembersToAddToHistoricalCurrentsQuery .Include(a => a.GroupRole) .ToList() .Select(gm => GroupMemberHistorical.CreateCurrentRowFromGroupMember(gm, effectiveExpireDateTime)).ToList(); groupMembersSaveToHistoryCurrent = groupMemberHistoricalCurrentsToInsert.Count(); rockContext.BulkInsert(groupMemberHistoricalCurrentsToInsert); } if (groupMembersLoggedToHistory > 0) { _jobStatusMessages.Add($"Logged {groupMembersLoggedToHistory} {"group member history snapshot".PluralizeIf( groupMembersLoggedToHistory != 0 )}"); } if (groupMembersSaveToHistoryCurrent > 0) { int newGroupMembersAddedToHistory = groupMembersSaveToHistoryCurrent - groupMembersLoggedToHistory; if (newGroupMembersAddedToHistory > 0) { _jobStatusMessages.Add($"Added {newGroupMembersAddedToHistory} new {"group member history snapshot".PluralizeIf( newGroupMembersAddedToHistory != 0 )}"); } } }