/// <summary> /// Returns a IQueryable of T based on the dataViewId /// </summary> /// <typeparam name="T"></typeparam> /// <param name="rockContext">The rock context.</param> /// <param name="dataViewId">The data view identifier.</param> /// <returns></returns> /// <exception cref="System.Exception">Invalid DataView EntityType</exception> /// <exception cref="AggregateException"></exception> /// <exception cref="System.ArgumentException">Invalid DataViewId: {this.DataViewId}</exception> internal static IQueryable <T> QueryFromDataView <T>(RockContext rockContext, int dataViewId) where T : Rock.Data.IEntity { IQueryable <T> entityQuery; var dataView = new DataViewService(rockContext).GetNoTracking(dataViewId); if (dataView != null) { var dataViewGetQueryArgs = new DataViewGetQueryArgs { DbContext = rockContext }; entityQuery = dataView.GetQuery(dataViewGetQueryArgs) as IQueryable <T>; if (entityQuery == null) { throw new Exception("Invalid DataView EntityType"); } } else { throw new ArgumentException($"Invalid DataViewId: {dataViewId} "); } return(entityQuery); }
public bool InDataView(int dataViewId, int entityId) { var rockContext = new RockContext(); var dataView = new DataViewService(rockContext).Get(dataViewId); ValidateDataView(dataView); var dataViewGetQueryArgs = new DataViewGetQueryArgs { DbContext = rockContext }; var qryGroupsInDataView = dataView.GetQuery(dataViewGetQueryArgs) as IQueryable <T>; qryGroupsInDataView = qryGroupsInDataView.Where(d => d.Id == entityId); return(qryGroupsInDataView.Any()); }
/// <summary> /// Renders the specified writer. /// </summary> /// <param name="badge">The badge.</param> /// <param name="writer">The writer.</param> public override void Render(BadgeCache badge, System.Web.UI.HtmlTextWriter writer) { RockContext rockContext = new RockContext(); var dataViewAttributeGuid = GetAttributeValue(badge, "DataView").AsGuid(); var dataViewService = new DataViewService(rockContext); if (dataViewAttributeGuid != Guid.Empty) { var dataView = dataViewService.Get(dataViewAttributeGuid); if (dataView != null) { Stopwatch stopwatch = Stopwatch.StartNew(); var dataViewGetQueryArgs = new DataViewGetQueryArgs { DatabaseTimeoutSeconds = 30 }; var qry = dataView.GetQuery(dataViewGetQueryArgs); var isEntityFound = false; if (qry != null) { isEntityFound = qry.Where(e => e.Id == Entity.Id).Any(); stopwatch.Stop(); DataViewService.AddRunDataViewTransaction(dataView.Id, Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds)); } if (isEntityFound) { Dictionary <string, object> mergeValues = new Dictionary <string, object>(); mergeValues.Add("Person", Person); mergeValues.Add("Entity", Entity); writer.Write(GetAttributeValue(badge, "BadgeContent").ResolveMergeFields(mergeValues)); } } } }
/// <summary> /// Clears the existing data from class var attendances and repopulates it with data for the selected group and filter criteria. /// Data is organized by each person in the group. /// </summary> private List <Attendance> GetAttendanceData() { this.SetBlockUserPreference(UserPreferenceKey.SelectedDateRange, sdrpDateRange.DelimitedValues); this.SetBlockUserPreference(UserPreferenceKey.SelectedViewBy, hfTabs.Value); this.SetBlockUserPreference(UserPreferenceKey.SelectedGroupId, gpGroups.GroupId.ToString()); this.SetBlockUserPreference(UserPreferenceKey.SelectedLocationIds, cblLocations.SelectedValues.AsDelimited(",")); this.SetBlockUserPreference(UserPreferenceKey.SelectedScheduleIds, cblSchedules.SelectedValues.AsDelimited(",")); this.SetBlockUserPreference(UserPreferenceKey.SelectedDataViewId, dvDataViews.SelectedValue); this.SetBlockUserPreference(UserPreferenceKey.SelectedPersonId, ppPerson.SelectedValue.ToString()); // Create URL for selected settings var pageReference = CurrentPageReference; foreach (var setting in GetBlockUserPreferences()) { pageReference.Parameters.AddOrReplace(setting.Key, setting.Value); } Uri uri = new Uri(Request.UrlProxySafe().ToString()); btnCopyToClipboard.Attributes["data-clipboard-text"] = uri.GetLeftPart(UriPartial.Authority) + pageReference.BuildUrl(); btnCopyToClipboard.Disabled = false; // Source data for all tables and graphs using (var rockContext = new RockContext()) { var attendanceService = new AttendanceService(rockContext); var groupAttendances = attendanceService .Queryable() .Include(a => a.PersonAlias) .AsNoTracking() .Where(a => a.RequestedToAttend == true); switch (hfTabs.Value) { case "group": groupAttendances = groupAttendances.Where(a => a.Occurrence.GroupId == gpGroups.GroupId); // add selected locations to the query if (cblLocations.SelectedValues.Any()) { groupAttendances = groupAttendances.Where(a => cblLocations.SelectedValuesAsInt.Contains(a.Occurrence.LocationId ?? -1)); } // add selected schedules to the query if (cblSchedules.SelectedValues.Any()) { groupAttendances = groupAttendances.Where(a => cblSchedules.SelectedValuesAsInt.Contains(a.Occurrence.ScheduleId ?? -1)); } break; case "person": groupAttendances = groupAttendances.Where(a => a.PersonAlias.PersonId == ppPerson.PersonId); break; case "dataview": var dataView = new DataViewService(rockContext).Get(dvDataViews.SelectedValueAsInt().Value); var dataViewGetQueryArgs = new DataViewGetQueryArgs { DbContext = rockContext }; var personsFromDv = dataView.GetQuery(dataViewGetQueryArgs) as IQueryable <Person>; var personAliasIds = personsFromDv.Select(d => d.Aliases.Where(a => a.AliasPersonId == d.Id).Select(a => a.Id).FirstOrDefault()).ToList(); groupAttendances = groupAttendances.Where(a => personAliasIds.Contains(a.PersonAliasId.Value)); break; default: break; } // parse the date range and add to query if (sdrpDateRange.DelimitedValues.IsNotNullOrWhiteSpace()) { var dateRange = SlidingDateRangePicker.CalculateDateRangeFromDelimitedValues(sdrpDateRange.DelimitedValues); if (dateRange.Start.HasValue) { groupAttendances = groupAttendances.Where(a => DbFunctions.TruncateTime(a.StartDateTime) >= dateRange.Start.Value); } if (dateRange.End.HasValue) { groupAttendances = groupAttendances.Where(a => DbFunctions.TruncateTime(a.StartDateTime) <= dateRange.End.Value); } } return(groupAttendances.ToList()); } }
/// <summary> /// Gets the filter person Ids based on dataview and optout group /// </summary> /// <param name="campaignConfiguration">The campaign configuration.</param> /// <param name="rockContext">The rock context.</param> /// <returns></returns> private static List <int> GetFilteredPersonIds(CampaignItem campaignConfiguration, RockContext rockContext) { var dataView = new DataViewService(rockContext).Get(campaignConfiguration.DataViewGuid); var personService = new PersonService(rockContext); int recordStatusInactiveId = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.PERSON_RECORD_STATUS_INACTIVE.AsGuid()).Id; var filteredPersonIds = new List <int>(); var dataViewGetQueryArgs = new DataViewGetQueryArgs(); var personQuery = dataView.GetQuery(dataViewGetQueryArgs).OfType <Rock.Model.Person>().Where(a => a.RecordStatusValueId != recordStatusInactiveId); if (campaignConfiguration.FamilyLimits == FamilyLimits.HeadOfHouse) { var familyMembersQuery = personQuery .Where(a => a.PrimaryFamily != null) .SelectMany(a => a.PrimaryFamily.Members) .Distinct(); //// Get all family group Id and all it's family member in dictionary. //// We will all the family members to both figure out if might be opted out //// and to figure out the head of household var familyWithMembers = familyMembersQuery.AsNoTracking() .Select(a => new { a.GroupId, a.PersonId, PersonIsActive = a.Person.RecordStatusValueId != recordStatusInactiveId, PersonIsDeceased = a.Person.IsDeceased, GroupRoleOrder = a.GroupRole.Order, PersonGender = a.Person.Gender }) .ToList() .GroupBy(a => a.GroupId) .ToDictionary(k => k.Key, v => v); if (campaignConfiguration.OptOutGroupGuid.HasValue) { var optOutGroup = new GroupService(rockContext).Get(campaignConfiguration.OptOutGroupGuid.Value); if (optOutGroup != null) { var personIds = optOutGroup.ActiveMembers().Select(a => a.PersonId).ToList(); // exclude families in which any member is part of optOut Group. familyWithMembers = familyWithMembers.Where(a => !a.Value.Any(b => personIds.Contains(b.PersonId))).ToDictionary(a => a.Key, b => b.Value); } } foreach (var familyId in familyWithMembers.Keys) { /* 2020-05-07 MDP * It is possible that a person is the Head Of Household in more than one family. For example: * -- Alex is in Ted Decker family. Ted Decker is Head of Household * -- Ted Decker is in Grandpa Decker family, and also the head of household for that one too * * We'll deal with that by putting a Distinct on the filteredPersonIds */ // Get all the head of house personIds of leftout family. var headOfHouse = familyWithMembers[familyId] .Where(m => !m.PersonIsDeceased && m.PersonIsActive) .OrderBy(m => m.GroupRoleOrder) .ThenBy(m => m.PersonGender) .Select(a => a.PersonId) .FirstOrDefault(); if (headOfHouse != default(int)) { filteredPersonIds.Add(headOfHouse); } } } else { var personIdList = personQuery.Select(a => a.Id).ToList(); if (campaignConfiguration.OptOutGroupGuid.HasValue) { var optOutGroup = new GroupService(rockContext).Get(campaignConfiguration.OptOutGroupGuid.Value); if (optOutGroup != null) { var personIds = optOutGroup.ActiveMembers().Select(a => a.PersonId).ToList(); personIdList = personIdList.Where(a => !personIds.Contains(a)).ToList(); } } filteredPersonIds = personIdList; } // just in case the same person is in multiple times (for example, head of household in multiple families), get just the distinct person ids filteredPersonIds = filteredPersonIds.Distinct().ToList(); return(filteredPersonIds); }
/// <summary> /// Executes the specified context. /// </summary> /// <param name="context">The context.</param> public virtual void Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; var emailTemplateGuid = dataMap.GetString("SystemEmail").AsGuidOrNull(); var dataViewGuid = dataMap.GetString("DataView").AsGuidOrNull(); if (dataViewGuid == null || emailTemplateGuid == null) { return; } var rockContext = new RockContext(); var dataView = new DataViewService(rockContext).Get(( Guid )dataViewGuid); List <IEntity> resultSet; Exception dataViewException = null; try { var dataViewGetQueryArgs = new DataViewGetQueryArgs { DatabaseTimeoutSeconds = dataMap.GetString("DatabaseTimeout").AsIntegerOrNull() ?? 180 }; var qry = dataView.GetQuery(dataViewGetQueryArgs); resultSet = qry.AsNoTracking().ToList(); } catch (Exception exception) { dataViewException = exception; var sqlTimeoutException = ReportingHelper.FindSqlTimeoutException(exception); if (sqlTimeoutException != null) { var exceptionMessage = $"The dataview did not complete in a timely manner. You can try again or adjust the timeout setting of this job."; dataViewException = new RockDataViewFilterExpressionException(dataView.DataViewFilter, exceptionMessage, sqlTimeoutException); } HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(dataViewException, context2); context.Result = dataViewException.Message; throw dataViewException; } var recipients = new List <RockEmailMessageRecipient>(); if (resultSet.Any()) { foreach (Person person in resultSet) { if (!person.IsEmailActive || person.Email.IsNullOrWhiteSpace() || person.EmailPreference == EmailPreference.DoNotEmail) { continue; } var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null); mergeFields.Add("Person", person); recipients.Add(new RockEmailMessageRecipient(person, mergeFields)); } } var emailMessage = new RockEmailMessage(emailTemplateGuid.Value); emailMessage.SetRecipients(recipients); var emailSendErrors = new List <string>(); emailMessage.Send(out emailSendErrors); context.Result = string.Format("{0} emails sent", recipients.Count()); if (emailSendErrors.Any()) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.Append(string.Format("{0} Errors: ", emailSendErrors.Count())); emailSendErrors.ForEach(e => { sb.AppendLine(); sb.Append(e); }); string errorMessage = sb.ToString(); context.Result += errorMessage; var exception = new Exception(errorMessage); HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(exception, context2); throw exception; } }
/// <summary> /// Shows the report. /// </summary> private void ShowMap() { string mapStylingFormat = @" <style> #map_wrapper {{ height: {0}px; }} #map_canvas {{ width: 100%; height: 100%; border-radius: var(--border-radius-base); }} </style>"; lMapStyling.Text = string.Format(mapStylingFormat, GetAttributeValue("MapHeight")); DefinedValueCache dvcMapStyle = DefinedValueCache.Get(GetAttributeValue("MapStyle").AsGuid()); // add styling to map if (dvcMapStyle != null) { this.StyleCode = dvcMapStyle.GetAttributeValue("DynamicMapStyle"); if (this.StyleCode.IsNullOrWhiteSpace()) { this.StyleCode = "[]"; } } else { this.StyleCode = "[]"; } var polygonColorList = GetAttributeValue("PolygonColors").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); string polygonColors = polygonColorList.AsDelimited(","); string latitude = "39.8282"; string longitude = "-98.5795"; string zoom = "4"; var orgLocation = GlobalAttributesCache.Get().OrganizationLocation; if (orgLocation != null && orgLocation.GeoPoint != null) { latitude = orgLocation.GeoPoint.Latitude.Value.ToString(System.Globalization.CultureInfo.GetCultureInfo("en-US")); longitude = orgLocation.GeoPoint.Longitude.Value.ToString(System.Globalization.CultureInfo.GetCultureInfo("en-US")); zoom = "12"; } var rockContext = new RockContext(); var campuses = CampusCache.All(); var locationService = new LocationService(rockContext); CampusMarkersData = string.Empty; if (cbShowCampusLocations.Checked) { foreach (var campus in campuses) { if (campus.LocationId.HasValue) { var location = locationService.Get(campus.LocationId.Value); if (location != null && location.GeoPoint != null) { CampusMarkersData += string.Format("{{ location: new google.maps.LatLng({0},{1}), campusName:'{2}' }},", location.GeoPoint.Latitude, location.GeoPoint.Longitude, HttpUtility.JavaScriptStringEncode(campus.Name)); } } } CampusMarkersData.TrimEnd(new char[] { ',' }); } // show geofences if a group was specified this.GroupId = this.PageParameter("GroupId").AsIntegerOrNull(); if (!this.GroupId.HasValue && gpGroupToMap.Visible) { // if a page parameter wasn't specified, use the selected group from the filter this.GroupId = gpGroupToMap.SelectedValue.AsIntegerOrNull(); } var groupMemberService = new GroupMemberService(rockContext); var groupLocationTypeHome = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.GROUP_LOCATION_TYPE_HOME.AsGuid()); int groupLocationTypeHomeId = groupLocationTypeHome != null ? groupLocationTypeHome.Id : 0; var groupTypeFamily = GroupTypeCache.GetFamilyGroupType(); int groupTypeFamilyId = groupTypeFamily != null ? groupTypeFamily.Id : 0; DataView dataView = GetDataView(); IQueryable <int> qryPersonIds = null; if (dataView != null) { var dataViewGetQueryArgs = new DataViewGetQueryArgs { DbContext = rockContext }; qryPersonIds = dataView.GetQuery(dataViewGetQueryArgs).OfType <Person>().Select(a => a.Id); } if (qryPersonIds == null) { // if no dataview was specified, show nothing qryPersonIds = new PersonService(rockContext).Queryable().Where(a => false).Select(a => a.Id); } var qryGroupMembers = groupMemberService.Queryable(); var campusIds = cpCampuses.SelectedCampusIds; if (campusIds.Any()) { qryGroupMembers = qryGroupMembers.Where(a => a.Group.CampusId.HasValue && campusIds.Contains(a.Group.CampusId.Value)); } var qryLocationGroupMembers = qryGroupMembers .Where(a => a.Group.GroupTypeId == groupTypeFamilyId) .Where(a => a.Group.IsActive && !a.Group.IsArchived) .Select(a => new { GroupGeoPoint = a.Group.GroupLocations.Where(gl => gl.IsMappedLocation && gl.GroupLocationTypeValueId == groupLocationTypeHomeId && gl.Location.IsActive && gl.Location.GeoPoint != null).Select(x => x.Location.GeoPoint).FirstOrDefault(), a.Group.CampusId, a.PersonId }) .Where(a => (a.GroupGeoPoint != null) && qryPersonIds.Contains(a.PersonId)) .GroupBy(a => new { a.GroupGeoPoint.Latitude, a.GroupGeoPoint.Longitude }) .Select(s => new { s.Key.Longitude, s.Key.Latitude, MemberCount = s.Count() }); var locationList = qryLocationGroupMembers.ToList() .Select(a => new LatLongWeighted(a.Latitude.Value, a.Longitude.Value, a.MemberCount)) .ToList(); List <LatLongWeighted> points = locationList; // cluster points that are close together double?milesPerGrouping = this.GetAttributeValue("PointGrouping").AsDoubleOrNull(); if (!milesPerGrouping.HasValue) { // default to a 1/10th of a mile milesPerGrouping = 0.10; } if (milesPerGrouping.HasValue && milesPerGrouping > 0) { var metersPerLatitudePHX = 110886.79; var metersPerLongitudePHX = 94493.11; double metersPerMile = 1609.34; var squareLengthHeightMeters = metersPerMile * milesPerGrouping.Value; var longitudeRoundFactor = metersPerLongitudePHX / squareLengthHeightMeters; var latitudeRoundFactor = metersPerLatitudePHX / squareLengthHeightMeters; // average the Lat/Lng, but make sure to round to 8 decimal points (otherwise Google Maps will silently not show the points due to too high of decimal precision) points = points.GroupBy(a => new { rLat = Math.Round(a.Lat * latitudeRoundFactor), rLong = Math.Round(a.Lat * longitudeRoundFactor), }).Select(a => new LatLongWeighted(Math.Round(a.Average(x => x.Lat), 8), Math.Round(a.Average(x => x.Long), 8), a.Sum(x => x.Weight))).ToList(); } this.HeatMapData = points.Select(a => a.Weight > 1 ? string.Format("{{ location: new google.maps.LatLng({0}, {1}), weight: {2} }}", a.Lat, a.Long, a.Weight) : string.Format("new google.maps.LatLng({0}, {1})", a.Lat, a.Long)).ToList().AsDelimited(",\n"); hfPolygonColors.Value = polygonColors; hfCenterLatitude.Value = latitude.ToString(); hfCenterLongitude.Value = longitude.ToString(); hfZoom.Value = zoom.ToString(); }
/// <summary> /// Processes the step type. Add steps for everyone in the dataview /// </summary> /// <param name="jobContext">The job context.</param> /// <param name="stepTypeView">The step type view.</param> /// <param name="minDaysBetweenSteps">The minimum days between steps.</param> /// <param name="addedResults">The added results.</param> /// <param name="updatedResults">The updated results.</param> /// <param name="errorMessages">The error message.</param> private void ProcessStepType( IJobExecutionContext jobContext, StepTypeView stepTypeView, int minDaysBetweenSteps, ConcurrentBag <int> addedResults, ConcurrentBag <int> updatedResults, out List <string> errorMessages) { errorMessages = new List <string>(); var rockContextGetList = new RockContext(); rockContextGetList.Database.CommandTimeout = SqlCommandTimeoutSeconds; // Steps are created with a status of "complete", so if we need to know the status id var stepStatusId = stepTypeView.CompletedStepStatusIds.FirstOrDefault(); if (stepStatusId == default) { errorMessages.Add($"The Step Type with id {stepTypeView.StepTypeId} does not have a valid Complete Status to use"); return; } // Get the dataview configured for the step type var dataViewService = new DataViewService(rockContextGetList); var dataview = dataViewService.Get(stepTypeView.AutoCompleteDataViewId); if (dataview == null) { errorMessages.Add($"The dataview {stepTypeView.AutoCompleteDataViewId} for step type {stepTypeView.StepTypeId} did not resolve"); return; } // We can use the dataview to get the person alias id query var dataViewGetQueryArgs = new DataViewGetQueryArgs { DbContext = rockContextGetList, DatabaseTimeoutSeconds = SqlCommandTimeoutSeconds }; IQueryable <IEntity> dataviewQuery; try { dataviewQuery = dataview.GetQuery(dataViewGetQueryArgs); } catch (Exception ex) { errorMessages.Add(ex.Message); ExceptionLogService.LogException(ex); return; } if (dataviewQuery == null) { errorMessages.Add($"Generating a query for dataview {stepTypeView.AutoCompleteDataViewId} for step type {stepTypeView.StepTypeId} was not successful"); return; } // This query contains person ids in the dataview var personIdQuery = dataviewQuery.AsNoTracking().Select(e => e.Id); // Get the query for people that cannot get a new step var personIdsThatCannotGetStepQuery = GetPersonIdsThatCannotGetStepQuery(rockContextGetList, stepTypeView, minDaysBetweenSteps); // Subtract the people that cannot get a new step personIdQuery = personIdQuery.Except(personIdsThatCannotGetStepQuery); // If there are prerequisites, then subtract the people that cannot get the step because of unmet prerequisites if (stepTypeView.PrerequisiteStepTypeIds.Any()) { var personIdsThatHaveMetPrerequisitesQuery = GetPersonIdsThatHaveMetPrerequisitesQuery(rockContextGetList, stepTypeView); personIdQuery = personIdQuery.Intersect(personIdsThatHaveMetPrerequisitesQuery); } // Convert to person aliases ids var personAliasService = new PersonAliasService(rockContextGetList); var personInfoList = personAliasService.GetPrimaryAliasQuery() .Where(a => personIdQuery.Contains(a.PersonId)) .Select(a => new { PersonId = a.PersonId, PrimaryAliasId = a.Id }) .ToList(); // Add or update steps for each of the remaining aliases that have met all the conditions var stepServiceGetList = new StepService(rockContextGetList); var now = RockDateTime.Now; var addedCount = 0; var updatedCount = 0; // Query for existing incomplete steps for the people var existingIncompleteStepIdsByPersonId = stepServiceGetList.Queryable() .Where(s => s.StepTypeId == stepTypeView.StepTypeId && personIdQuery.Contains(s.PersonAlias.PersonId) && !s.CompletedDateTime.HasValue) .Select(a => new { a.PersonAlias.PersonId, StepId = a.Id }) .ToList() .GroupBy(a => a.PersonId) .ToDictionary( k => k.Key, // just in case the Person is has more than one incomplete step for this step type, just use the latest one // it should clean it self up on subsequent runs since the other steps for this person wouldn't have been marked complete yet v => v.Max(s => s.StepId) ); long totalCount = personInfoList.Count; long progressCount = 0; foreach (var personIdInfo in personInfoList) { var personId = personIdInfo.PersonId; var personPrimaryAliasId = personIdInfo.PrimaryAliasId; var existingStepId = existingIncompleteStepIdsByPersonId.GetValueOrNull(personId); using (var rockContextLoop = new RockContext()) { var stepServiceLoop = new StepService(rockContextLoop); Step step; if (existingStepId.HasValue) { step = stepServiceLoop.Get(existingStepId.Value); } else { step = new Step { StepTypeId = stepTypeView.StepTypeId, Caption = stepTypeView.Name, StartDateTime = now, PersonAliasId = personPrimaryAliasId }; } step.CompletedDateTime = now; step.StepStatusId = stepStatusId; if (!existingStepId.HasValue) { stepServiceLoop.AddWithoutValidation(step); addedCount++; } else { updatedCount++; } rockContextLoop.SaveChanges(); } progressCount++; // Update the progress every 5 seconds if ((RockDateTime.Now - _lastProgressUpdate).TotalSeconds >= 5) { try { jobContext.UpdateLastStatusMessage($"Processing {stepTypeView.Name } steps : {progressCount}/{totalCount}"); } catch (Exception ex) { // ignore, but write to debug output System.Diagnostics.Debug.WriteLine($"Error updating LastStatusMessage for ProcessStepType loop: {ex}"); } finally { _lastProgressUpdate = RockDateTime.Now; } } } addedResults.Add(addedCount); updatedResults.Add(updatedCount); }
/// <summary> /// Binds the grid. /// </summary> private void BindGrid() { var dataViewId = hfDataViewId.Value.AsIntegerOrNull(); pnlResultsGrid.Visible = false; pnlView.Visible = false; if (!dataViewId.HasValue) { return; } var dataView = new DataViewService(new RockContext()).Get(dataViewId.Value); if (dataView == null) { return; } if (!dataView.EntityTypeId.HasValue) { return; } var dataViewEntityType = EntityTypeCache.Get(dataView.EntityTypeId.Value); if (dataViewEntityType == null || dataViewEntityType.AssemblyName == null) { return; } Type dataViewEntityTypeType = dataViewEntityType.GetEntityType(); if (dataViewEntityTypeType == null) { return; } pnlView.Visible = true; gDataViewResults.DataSource = null; // Only respect the ShowResults option if fetchRowCount is null var showResults = GetBlockUserPreference(UserPreferenceKey.ShowResults).AsBooleanOrNull() ?? true; if (showResults) { btnToggleResults.Text = "Hide Results <i class='fa fa-chevron-up'></i>"; btnToggleResults.ToolTip = "Hide Results"; btnToggleResults.RemoveCssClass("btn-primary"); btnToggleResults.AddCssClass("btn-default"); } else { btnToggleResults.Text = "Show Results <i class='fa fa-chevron-down'></i>"; btnToggleResults.RemoveCssClass("btn-default"); btnToggleResults.AddCssClass("btn-primary"); btnToggleResults.ToolTip = "Show Results"; } if (!showResults) { return; } if (!dataView.IsAuthorized(Authorization.VIEW, CurrentPerson)) { return; } gDataViewResults.EntityTypeId = dataView.EntityTypeId; bool isPersonDataSet = dataView.EntityTypeId == EntityTypeCache.GetId <Rock.Model.Person>(); if (isPersonDataSet) { gDataViewResults.PersonIdField = "Id"; gDataViewResults.DataKeyNames = new string[] { "Id" }; } else { gDataViewResults.PersonIdField = null; } var entityTypeCache = EntityTypeCache.Get(dataView.EntityTypeId.Value); if (entityTypeCache != null) { gDataViewResults.RowItemText = entityTypeCache.FriendlyName; } pnlResultsGrid.Visible = true; var enableCountingDataViewStatistics = this.GetAttributeValue(AttributeKey.EnableCountingDataViewStatistics).AsBooleanOrNull() ?? true; try { gDataViewResults.CreatePreviewColumns(dataViewEntityTypeType); var dbContext = dataView.GetDbContext(); var dataViewGetQueryArgs = new DataViewGetQueryArgs { SortProperty = gDataViewResults.SortProperty, DbContext = dbContext, DatabaseTimeoutSeconds = GetAttributeValue(AttributeKey.DatabaseTimeoutSeconds).AsIntegerOrNull() ?? 180, DataViewFilterOverrides = new DataViewFilterOverrides { ShouldUpdateStatics = enableCountingDataViewStatistics } }; var qry = dataView.GetQuery(dataViewGetQueryArgs); gDataViewResults.SetLinqDataSource(qry.AsNoTracking()); gDataViewResults.DataBind(); } catch (Exception ex) { this.LogException(ex); var sqlTimeoutException = ReportingHelper.FindSqlTimeoutException(ex); var errorBox = nbGridError; if (sqlTimeoutException != null) { errorBox.NotificationBoxType = NotificationBoxType.Warning; errorBox.Text = "This data view did not complete in a timely manner. You can try again or adjust the timeout setting of this block."; return; } else { if (ex is RockDataViewFilterExpressionException) { RockDataViewFilterExpressionException rockDataViewFilterExpressionException = ex as RockDataViewFilterExpressionException; errorBox.Text = rockDataViewFilterExpressionException.GetFriendlyMessage(dataView); } else { errorBox.Text = "There was a problem with one of the filters for this data view."; } errorBox.NotificationBoxType = NotificationBoxType.Danger; errorBox.Details = ex.Message; errorBox.Visible = true; return; } } gDataViewResults.RowItemText = dataViewEntityType.FriendlyName; if (gDataViewResults.DataSource != null) { gDataViewResults.ExportFilename = dataView.Name; } }
/// <summary> /// Processes the connection type. /// </summary> /// <param name="connectionTypeView">The connection type view.</param> /// <param name="updatedResults">The updated results.</param> /// <param name="errorMessages">The error message.</param> private void ProcessConnectionType( ConnectionTypeView connectionTypeView, ConcurrentBag <int> updatedResults, out List <string> errorMessages) { errorMessages = new List <string>(); var groupViews = new List <GroupView>(); foreach (var connectionStatus in connectionTypeView.ConnectionStatuses) { foreach (var connectionStatusAutomation in connectionStatus.ConnectionStatusAutomations) { var rockContext = new RockContext(); var connectionRequestService = new ConnectionRequestService(rockContext); var connectionRequestQry = connectionRequestService.Queryable().Include(a => a.AssignedGroup.Members).Where(a => a.ConnectionStatusId == connectionStatus.Id); if (connectionStatusAutomation.DataViewId.HasValue) { // Get the dataview configured for the connection request var dataViewService = new DataViewService(rockContext); var dataview = dataViewService.Get(connectionStatusAutomation.DataViewId.Value); if (dataview == null) { errorMessages.Add($"The dataview {connectionStatusAutomation.DataViewId} for Connection Type {connectionTypeView.ConnectionTypeId} did not resolve"); continue; } // Now we'll filter our connection request query to only include the ones that are in the configured data view. var dataViewGetQueryArgs = new DataViewGetQueryArgs { DbContext = rockContext }; IQueryable <ConnectionRequest> dataviewQuery; try { dataviewQuery = dataview.GetQuery(dataViewGetQueryArgs) as IQueryable <ConnectionRequest>; } catch (Exception ex) { errorMessages.Add(ex.Message); ExceptionLogService.LogException(ex); continue; } if (dataviewQuery == null) { errorMessages.Add($"Generating a query for dataview {connectionStatusAutomation.DataViewId} for Status {connectionStatus.Id} in Connection Type {connectionTypeView.ConnectionTypeId} was not successful"); continue; } connectionRequestQry = connectionRequestQry.Where(a => dataviewQuery.Any(b => b.Id == a.Id)); } var eligibleConnectionRequests = new List <ConnectionRequest>(); if (connectionStatusAutomation.GroupRequirementsFilter != GroupRequirementsFilter.Ignore) { var connectionRequests = connectionRequestQry.ToList(); foreach (var connectionRequest in connectionRequests) { // Group Requirement can't be met when either placement group or placement group role id is missing if (connectionRequest.AssignedGroupId.HasValue && connectionRequest.AssignedGroupMemberRoleId.HasValue) { var groupView = GetGroupView(connectionRequest, groupViews, rockContext); if (groupView != null && groupView.HasGroupRequirement) { var isRequirementMet = IsGroupRequirementMet(connectionRequest, groupView, rockContext); // connection request based on if group requirement is met or not is added to list for status update if ((connectionStatusAutomation.GroupRequirementsFilter == GroupRequirementsFilter.DoesNotMeet && !isRequirementMet) || (connectionStatusAutomation.GroupRequirementsFilter == GroupRequirementsFilter.MustMeet && isRequirementMet)) { eligibleConnectionRequests.Add(connectionRequest); } } } } } else { eligibleConnectionRequests = connectionRequestQry.ToList(); } var updatedCount = 0; foreach (var connectionRequest in eligibleConnectionRequests) { connectionRequest.ConnectionStatusId = connectionStatusAutomation.DestinationStatusId; updatedCount++; } rockContext.SaveChanges(); updatedResults.Add(updatedCount); } } }
/// <summary> /// Executes the specified workflow. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="action">The workflow action.</param> /// <param name="entity">The entity.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> /// <exception cref="System.NotImplementedException"></exception> public override bool Execute(RockContext rockContext, Rock.Model.WorkflowAction action, Object entity, out List <string> errorMessages) { var checkInState = GetCheckInState(entity, out errorMessages); if (checkInState == null) { return(false); } var dataViewAttributeKey = string.Empty; var dataViewAttributeGuid = GetAttributeValue(action, "DataViewGroupAttribute").AsGuid(); if (dataViewAttributeGuid != Guid.Empty) { dataViewAttributeKey = AttributeCache.Get(dataViewAttributeGuid, rockContext).Key; } var dataViewService = new DataViewService(rockContext); var family = checkInState.CheckIn.CurrentFamily; if (family != null) { var remove = GetAttributeValue(action, "Remove").AsBoolean(); foreach (var person in family.People) { foreach (var groupType in person.GroupTypes.ToList()) { foreach (var group in groupType.Groups.ToList()) { if (group.ExcludedByFilter == true) { continue; } var dataviewGuids = group.Group.GetAttributeValue(dataViewAttributeKey); if (string.IsNullOrWhiteSpace(dataviewGuids)) { continue; } foreach (var dataviewGuid in dataviewGuids.SplitDelimitedValues()) { DataView dataview = dataViewService.Get(dataviewGuid.AsGuid()); if (dataview == null) { continue; } if (dataview.PersistedScheduleIntervalMinutes.HasValue && dataview.PersistedLastRefreshDateTime.HasValue) { //Get record from persisted. var persistedValuesQuery = rockContext.DataViewPersistedValues.Where(a => a.DataViewId == dataview.Id); if (!persistedValuesQuery.Any(v => v.EntityId == person.Person.Id)) { if (remove) { groupType.Groups.Remove(group); } else { group.ExcludedByFilter = true; } break; } } else { //Qry dataview var dataViewGetQueryArgs = new DataViewGetQueryArgs { DatabaseTimeoutSeconds = 30 }; var approvedPeopleQry = dataview.GetQuery(dataViewGetQueryArgs); if (approvedPeopleQry != null) { var approvedPeopleList = approvedPeopleQry.Select(e => e.Id).ToList(); if (approvedPeopleList != null && !approvedPeopleList.Contains(person.Person.Id)) { if (remove) { groupType.Groups.Remove(group); } else { group.ExcludedByFilter = true; } break; } } } } } } } } return(true); }
public DataSet GetContributionPersonGroupAddress([FromBody] ContributionStatementOptions options) { Dictionary <string, object> parameters = new Dictionary <string, object>(); var startDate = options.StartDate; var endDate = options.EndDate ?? DateTime.MaxValue; // The SQL date type has a more limited range than C# DateTime. We need to conform the date values to fit // in the SQL date range or an error will be thrown in the stored procedure. if (startDate < SqlDateTime.MinValue.Value) { startDate = SqlDateTime.MinValue.Value; } if (endDate > SqlDateTime.MaxValue.Value) { endDate = SqlDateTime.MaxValue.Value; } if (options.AccountIds != null) { parameters.Add("accountIds", options.AccountIds.AsDelimited(",")); } else { parameters.Add("accountIds", DBNull.Value); } if (options.PersonId.HasValue) { parameters.Add("personId", options.PersonId); } else { parameters.Add("personId", DBNull.Value); } if (options.IncludeIndividualsWithNoAddress) { parameters.Add("includeIndividualsWithNoAddress", options.IncludeIndividualsWithNoAddress); } else { parameters.Add("includeIndividualsWithNoAddress", false); } parameters.Add("orderByPostalCode", options.OrderByPostalCode); parameters.Add("startDate", startDate); parameters.Add("endDate", endDate); var result = DbService.GetDataSet("spFinance_ContributionStatementQuery", System.Data.CommandType.StoredProcedure, parameters); if (result.Tables.Count > 0) { var dataTable = result.Tables[0]; dataTable.TableName = "contribution_person_group_address"; if (options.DataViewId.HasValue) { var dataView = new DataViewService(new RockContext()).Get(options.DataViewId.Value); if (dataView != null) { var dataViewGetQueryArgs = new DataViewGetQueryArgs(); var personList = dataView.GetQuery(dataViewGetQueryArgs).OfType <Rock.Model.Person>().Select(a => new { a.Id, a.GivingGroupId }).ToList(); HashSet <int> personIds = new HashSet <int>(personList.Select(a => a.Id)); HashSet <int> groupsIds = new HashSet <int>(personList.Where(a => a.GivingGroupId.HasValue).Select(a => a.GivingGroupId.Value).Distinct()); foreach (var row in dataTable.Rows.OfType <DataRow>().ToList()) { var personId = row["PersonId"]; var groupId = row["GroupId"]; if (personId != null && personId is int) { if (!personIds.Contains(( int )personId)) { dataTable.Rows.Remove(row); } } else if (groupId != null && groupId is int) { if (!groupsIds.Contains(( int )groupId)) { dataTable.Rows.Remove(row); } } } } } } return(result); }
/// <summary> /// Job that will sync groups. /// /// Called by the <see cref="IScheduler" /> when a /// <see cref="ITrigger" /> fires that is associated with /// the <see cref="IJob" />. /// </summary> public virtual void Execute(IJobExecutionContext context) { // Get the job setting(s) JobDataMap dataMap = context.JobDetail.JobDataMap; bool requirePasswordReset = dataMap.GetBoolean("RequirePasswordReset"); var commandTimeout = dataMap.GetString("CommandTimeout").AsIntegerOrNull() ?? 180; // Counters for displaying results int groupsSynced = 0; int groupsChanged = 0; string groupName = string.Empty; string dataViewName = string.Empty; var errors = new List <string>(); try { // get groups set to sync var activeSyncList = new List <GroupSyncInfo>(); using (var rockContext = new RockContext()) { // Get groups that are not archived and are still active. activeSyncList = new GroupSyncService(rockContext) .Queryable() .AsNoTracking() .AreNotArchived() .AreActive() .NeedToBeSynced() .Select(x => new GroupSyncInfo { SyncId = x.Id, GroupName = x.Group.Name }) .ToList(); } foreach (var syncInfo in activeSyncList) { int syncId = syncInfo.SyncId; bool hasSyncChanged = false; context.UpdateLastStatusMessage($"Syncing group {syncInfo.GroupName}"); // Use a fresh rockContext per sync so that ChangeTracker doesn't get bogged down using (var rockContext = new RockContext()) { // increase the timeout just in case the data view source is slow rockContext.Database.CommandTimeout = commandTimeout; rockContext.SourceOfChange = "Group Sync"; // Get the Sync var sync = new GroupSyncService(rockContext) .Queryable() .Include(a => a.Group) .Include(a => a.SyncDataView) .AsNoTracking() .FirstOrDefault(s => s.Id == syncId); if (sync == null || sync.SyncDataView.EntityTypeId != EntityTypeCache.Get(typeof(Person)).Id) { // invalid sync or invalid SyncDataView continue; } dataViewName = sync.SyncDataView.Name; groupName = sync.Group.Name; Stopwatch stopwatch = Stopwatch.StartNew(); // Get the person id's from the data view (source) var dataViewGetQueryArgs = new DataViewGetQueryArgs { DbContext = rockContext, DatabaseTimeoutSeconds = commandTimeout }; List <int> sourcePersonIds; try { var dataViewQry = sync.SyncDataView.GetQuery(dataViewGetQueryArgs); sourcePersonIds = dataViewQry.Select(q => q.Id).ToList(); } catch (Exception ex) { // If any error occurred trying get the 'where expression' from the sync-data-view, // just skip trying to sync that particular group's Sync Data View for now. var errorMessage = $"An error occurred while trying to GroupSync group '{groupName}' and data view '{dataViewName}' so the sync was skipped. Error: {ex.Message}"; errors.Add(errorMessage); ExceptionLogService.LogException(new Exception(errorMessage, ex)); continue; } stopwatch.Stop(); DataViewService.AddRunDataViewTransaction(sync.SyncDataView.Id, Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds)); // Get the person id's in the group (target) for the role being synced. // Note: targetPersonIds must include archived group members // so we don't try to delete anyone who's already archived, and // it must include deceased members so we can remove them if they // are no longer in the data view. var existingGroupMemberPersonList = new GroupMemberService(rockContext) .Queryable(true, true).AsNoTracking() .Where(gm => gm.GroupId == sync.GroupId) .Where(gm => gm.GroupRoleId == sync.GroupTypeRoleId) .Select(gm => new { PersonId = gm.PersonId, IsArchived = gm.IsArchived }) .ToList(); var targetPersonIdsToDelete = existingGroupMemberPersonList.Where(t => !sourcePersonIds.Contains(t.PersonId) && t.IsArchived != true).ToList(); if (targetPersonIdsToDelete.Any()) { context.UpdateLastStatusMessage($"Deleting {targetPersonIdsToDelete.Count()} group records in {syncInfo.GroupName} that are no longer in the sync data view"); } int deletedCount = 0; // Delete people from the group/role that are no longer in the data view -- // but not the ones that are already archived. foreach (var targetPerson in targetPersonIdsToDelete) { deletedCount++; if (deletedCount % 100 == 0) { context.UpdateLastStatusMessage($"Deleted {deletedCount} of {targetPersonIdsToDelete.Count()} group member records for group {syncInfo.GroupName}"); } try { // Use a new context to limit the amount of change-tracking required using (var groupMemberContext = new RockContext()) { // Delete the records for that person's group and role. // NOTE: just in case there are duplicate records, delete all group member records for that person and role var groupMemberService = new GroupMemberService(groupMemberContext); foreach (var groupMember in groupMemberService .Queryable(true, true) .Where(m => m.GroupId == sync.GroupId && m.GroupRoleId == sync.GroupTypeRoleId && m.PersonId == targetPerson.PersonId) .ToList()) { groupMemberService.Delete(groupMember); } groupMemberContext.SaveChanges(); // If the Group has an exit email, and person has an email address, send them the exit email if (sync.ExitSystemCommunication != null) { var person = new PersonService(groupMemberContext).Get(targetPerson.PersonId); if (person.CanReceiveEmail(false)) { // Send the exit email var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Group", sync.Group); mergeFields.Add("Person", person); var emailMessage = new RockEmailMessage(sync.ExitSystemCommunication); emailMessage.AddRecipient(new RockEmailMessageRecipient(person, mergeFields)); var emailErrors = new List <string>(); emailMessage.Send(out emailErrors); errors.AddRange(emailErrors); } } } } catch (Exception ex) { ExceptionLogService.LogException(ex); continue; } hasSyncChanged = true; } // Now find all the people in the source list who are NOT already in the target list (as Unarchived) var targetPersonIdsToAdd = sourcePersonIds.Where(s => !existingGroupMemberPersonList.Any(t => t.PersonId == s && t.IsArchived == false)).ToList(); // Make a list of PersonIds that have an Archived group member record // if this person isn't already a member of the list as an Unarchived member, we can Restore the group member for that PersonId instead var archivedTargetPersonIds = existingGroupMemberPersonList.Where(t => t.IsArchived == true).Select(a => a.PersonId).ToList(); context.UpdateLastStatusMessage($"Adding {targetPersonIdsToAdd.Count()} group member records for group {syncInfo.GroupName}"); int addedCount = 0; int notAddedCount = 0; foreach (var personId in targetPersonIdsToAdd) { if ((addedCount + notAddedCount) % 100 == 0) { string notAddedMessage = string.Empty; if (notAddedCount > 0) { notAddedMessage = $"{Environment.NewLine} There are {notAddedCount} members that could not be added due to group requirements."; } context.UpdateLastStatusMessage($"Added {addedCount} of {targetPersonIdsToAdd.Count()} group member records for group {syncInfo.GroupName}. {notAddedMessage}"); } try { // Use a new context to limit the amount of change-tracking required using (var groupMemberContext = new RockContext()) { var groupMemberService = new GroupMemberService(groupMemberContext); var groupService = new GroupService(groupMemberContext); // If this person is currently archived... if (archivedTargetPersonIds.Contains(personId)) { // ...then we'll just restore them; GroupMember archivedGroupMember = groupService.GetArchivedGroupMember(sync.Group, personId, sync.GroupTypeRoleId); if (archivedGroupMember == null) { // shouldn't happen, but just in case continue; } archivedGroupMember.GroupMemberStatus = GroupMemberStatus.Active; if (archivedGroupMember.IsValidGroupMember(groupMemberContext)) { addedCount++; groupMemberService.Restore(archivedGroupMember); groupMemberContext.SaveChanges(); } else { notAddedCount++; // Validation errors will get added to the ValidationResults collection. Add those results to the log and then move on to the next person. var ex = new GroupMemberValidationException(string.Join(",", archivedGroupMember.ValidationResults.Select(r => r.ErrorMessage).ToArray())); ExceptionLogService.LogException(ex); continue; } } else { // ...otherwise we will add a new person to the group with the role specified in the sync. var newGroupMember = new GroupMember { Id = 0 }; newGroupMember.PersonId = personId; newGroupMember.GroupId = sync.GroupId; newGroupMember.GroupMemberStatus = GroupMemberStatus.Active; newGroupMember.GroupRoleId = sync.GroupTypeRoleId; if (newGroupMember.IsValidGroupMember(groupMemberContext)) { addedCount++; groupMemberService.Add(newGroupMember); groupMemberContext.SaveChanges(); } else { notAddedCount++; // Validation errors will get added to the ValidationResults collection. Add those results to the log and then move on to the next person. var ex = new GroupMemberValidationException(string.Join(",", newGroupMember.ValidationResults.Select(r => r.ErrorMessage).ToArray())); ExceptionLogService.LogException(ex); continue; } } // If the Group has a welcome email, and person has an email address, send them the welcome email and possibly create a login if (sync.WelcomeSystemCommunication != null) { var person = new PersonService(groupMemberContext).Get(personId); if (person.CanReceiveEmail(false)) { // If the group is configured to add a user account for anyone added to the group, and person does not yet have an // account, add one for them. string newPassword = string.Empty; bool createLogin = sync.AddUserAccountsDuringSync; // Only create a login if requested, no logins exist and we have enough information to generate a user name. if (createLogin && !person.Users.Any() && !string.IsNullOrWhiteSpace(person.NickName) && !string.IsNullOrWhiteSpace(person.LastName)) { newPassword = System.Web.Security.Membership.GeneratePassword(9, 1); string username = Rock.Security.Authentication.Database.GenerateUsername(person.NickName, person.LastName); UserLogin login = UserLoginService.Create( groupMemberContext, person, AuthenticationServiceType.Internal, EntityTypeCache.Get(Rock.SystemGuid.EntityType.AUTHENTICATION_DATABASE.AsGuid()).Id, username, newPassword, true, requirePasswordReset); } // Send the welcome email var mergeFields = new Dictionary <string, object>(); mergeFields.Add("Group", sync.Group); mergeFields.Add("Person", person); mergeFields.Add("NewPassword", newPassword); mergeFields.Add("CreateLogin", createLogin); var emailMessage = new RockEmailMessage(sync.WelcomeSystemCommunication); emailMessage.AddRecipient(new RockEmailMessageRecipient(person, mergeFields)); var emailErrors = new List <string>(); emailMessage.Send(out emailErrors); errors.AddRange(emailErrors); } } } } catch (Exception ex) { ExceptionLogService.LogException(ex); continue; } hasSyncChanged = true; } // Increment Groups Changed Counter (if people were deleted or added to the group) if (hasSyncChanged) { groupsChanged++; } // Increment the Groups Synced Counter groupsSynced++; } // Update last refresh datetime in different context to avoid side-effects. using (var rockContext = new RockContext()) { var sync = new GroupSyncService(rockContext) .Queryable() .FirstOrDefault(s => s.Id == syncId); sync.LastRefreshDateTime = RockDateTime.Now; rockContext.SaveChanges(); } } // Format the result message var resultMessage = string.Empty; if (groupsSynced == 0) { resultMessage = "No groups to sync"; } else if (groupsSynced == 1) { resultMessage = "1 group was synced"; } else { resultMessage = string.Format("{0} groups were synced", groupsSynced); } resultMessage += string.Format(" and {0} groups were changed", groupsChanged); if (errors.Any()) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.Append("Errors: "); errors.ForEach(e => { sb.AppendLine(); sb.Append(e); }); string errorMessage = sb.ToString(); resultMessage += errorMessage; throw new Exception(errorMessage); } context.Result = resultMessage; } catch (System.Exception ex) { HttpContext context2 = HttpContext.Current; ExceptionLogService.LogException(ex, context2); throw; } }
/// <summary> /// Perform the job using the parameters supplied in the execution context. /// </summary> /// <param name="context"></param> public void Execute(IJobExecutionContext context) { // Get the configuration settings for this job instance. var dataMap = context.JobDetail.JobDataMap; var workflowTypeGuid = dataMap.GetString(AttributeKey.Workflow).AsGuidOrNull(); var dataViewGuid = dataMap.GetString(AttributeKey.DataView).AsGuidOrNull(); if (dataViewGuid == null) { throw new Exception("Data view not selected"); } var rockContext = new RockContext(); var dataViewService = new DataViewService(rockContext); var dataView = dataViewService.Get(dataViewGuid.Value); if (dataView == null) { throw new Exception("Data view not found"); } // Get the set of entity key values returned by the Data View. Stopwatch stopwatch = Stopwatch.StartNew(); var dataViewGetQueryArgs = new DataViewGetQueryArgs { DbContext = rockContext }; var qry = dataView.GetQuery(dataViewGetQueryArgs); var modelType = dataView.EntityType.GetType(); if (qry == null) { throw new Exception("Data view results not found"); } if (modelType == null) { throw new Exception("Entity type of data view not found"); } var entityIds = qry.Select(e => e.Id).ToList(); stopwatch.Stop(); DataViewService.AddRunDataViewTransaction(dataView.Id, Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds)); var entityTypeId = dataView.EntityTypeId.Value; var entityTypeName = modelType.GetFriendlyTypeName(); int workflowsLaunched = 0; // For each entity, create a new transaction to launch a workflow. foreach (var entityId in entityIds) { var transaction = new LaunchEntityWorkflowTransaction(workflowTypeGuid.Value, string.Empty, entityTypeId, entityId); Rock.Transactions.RockQueue.TransactionQueue.Enqueue(transaction); workflowsLaunched++; } context.Result = string.Format("{0} workflows launched", workflowsLaunched); }