/// <summary>
        /// Gets the group historical summary for the specified Person during the specified timeframe
        /// </summary>
        /// <param name="personId">The person identifier.</param>
        /// <param name="startDateTime">The start date time.</param>
        /// <param name="stopDateTime">The stop date time.</param>
        /// <param name="groupTypeIds">The group type ids.</param>
        /// <returns></returns>
        public List <GroupHistoricalSummary> GetGroupHistoricalSummary(int personId, DateTime?startDateTime, DateTime?stopDateTime, List <int> groupTypeIds)
        {
            var rockContext = this.Context as RockContext;

            var personGroupMemberIdQuery = new GroupMemberService(rockContext).AsNoFilter().Where(a => a.PersonId == personId).Select(a => a.Id);

            // get all GroupMemberHistorical records for the Person
            var groupMemberHistoricalQuery = this.AsNoFilter().Where(a => personGroupMemberIdQuery.Contains(a.GroupMemberId));

            if (startDateTime.HasValue)
            {
                groupMemberHistoricalQuery = groupMemberHistoricalQuery.Where(a => a.EffectiveDateTime >= startDateTime.Value);
            }

            if (stopDateTime.HasValue)
            {
                groupMemberHistoricalQuery = groupMemberHistoricalQuery.Where(a => a.EffectiveDateTime < stopDateTime.Value);
            }

            if (groupTypeIds?.Any() == true)
            {
                groupMemberHistoricalQuery = groupMemberHistoricalQuery.Where(a => groupTypeIds.Contains(a.Group.GroupTypeId));
            }

            return(this.GetGroupHistoricalSummary(groupMemberHistoricalQuery));
        }
Exemplo n.º 2
0
        public IQueryable<GuestFamily> GetGuestsForFamily( int groupId )
        {
            Guid knownRelationshipGuid = new Guid( Rock.SystemGuid.GroupType.GROUPTYPE_KNOWN_RELATIONSHIPS );
            Guid knownRelationshipOwner = new Guid( Rock.SystemGuid.GroupRole.GROUPROLE_KNOWN_RELATIONSHIPS_OWNER );
            Guid knownRelationshipCanCheckin = new Guid( Rock.SystemGuid.GroupRole.GROUPROLE_KNOWN_RELATIONSHIPS_CAN_CHECK_IN );

            RockContext rockContext = new RockContext();
            GroupMemberService groupMemberService = new GroupMemberService( rockContext );
            PersonService personService = new PersonService( rockContext );

            var familyMembers = groupMemberService.Queryable()
                                    .Where( f => f.GroupId == groupId )
                                    .Select( f => f.PersonId );

            var familyMembersKnownRelationshipGroups = new GroupMemberService( rockContext ).Queryable()
                                    .Where( g => g.Group.GroupType.Guid == knownRelationshipGuid
                                                    && g.GroupRole.Guid == knownRelationshipOwner
                                                    && familyMembers.Contains( g.PersonId ) )
                                    .Select( m => m.GroupId );
            rockContext.Database.Log = s => System.Diagnostics.Debug.WriteLine( s );
            var guests = groupMemberService.Queryable()
                                    .Where( g => g.GroupRole.Guid == knownRelationshipCanCheckin
                                                    && familyMembersKnownRelationshipGroups.Contains( g.GroupId ) )
                                    .Select( g => g.PersonId )
                                    .Distinct().ToList();

            var guestFamilies = new List<GuestFamily>();
            rockContext.Database.Log = null;
            foreach ( var guestPersonId in guests )
            {
                var families = personService.GetFamilies( guestPersonId );

                foreach ( var family in families )
                {
                    if ( !guestFamilies.Select( f => f.Id ).Contains( family.Id ) )
                    {
                        GuestFamily guestFamily = new GuestFamily();
                        guestFamily.Id = family.Id;
                        guestFamily.Guid = family.Guid;
                        guestFamily.Name = family.Name;

                        guestFamily.FamilyMembers = new List<GuestFamilyMember>();
                        foreach ( var familyMember in family.Members )
                        {
                            GuestFamilyMember guestFamilyMember = new GuestFamilyMember();
                            guestFamilyMember.Id = familyMember.PersonId;
                            guestFamilyMember.PersonAliasId = familyMember.Person.PrimaryAliasId.Value;
                            guestFamilyMember.Guid = familyMember.Person.Guid;
                            guestFamilyMember.FirstName = familyMember.Person.NickName;
                            guestFamilyMember.LastName = familyMember.Person.LastName;
                            guestFamilyMember.PhotoUrl = familyMember.Person.PhotoUrl;
                            guestFamilyMember.CanCheckin = guests.Contains( familyMember.PersonId );
                            guestFamilyMember.Role = familyMember.GroupRole.Name;
                            guestFamilyMember.Age = familyMember.Person.Age;
                            guestFamilyMember.Gender = familyMember.Person.Gender;

                            guestFamily.FamilyMembers.Add( guestFamilyMember );
                        }

                        guestFamilies.Add( guestFamily );
                    }
                }
            }

            return guestFamilies.AsQueryable();
        }
Exemplo n.º 3
0
        /// <summary>
        /// Gets occurrence data for the selected group
        /// </summary>
        /// <param name="group">The group.</param>
        /// <param name="fromDateTime">From date time.</param>
        /// <param name="toDateTime">To date time.</param>
        /// <param name="locationIds">The location ids.</param>
        /// <param name="scheduleIds">The schedule ids.</param>
        /// <param name="loadSummaryData">if set to <c>true</c> [load summary data].</param>
        /// <param name="campusId">The campus identifier.</param>
        /// <returns></returns>
        public List <ScheduleOccurrence> GetGroupOccurrences(Group group, DateTime?fromDateTime, DateTime?toDateTime,
                                                             List <int> locationIds, List <int> scheduleIds, bool loadSummaryData, int?campusId)
        {
            var occurrences = new List <ScheduleOccurrence>();

            if (group != null)
            {
                var rockContext       = (RockContext)this.Context;
                var attendanceService = new AttendanceService(rockContext);
                var scheduleService   = new ScheduleService(rockContext);
                var locationService   = new LocationService(rockContext);

                using (new Rock.Data.QueryHintScope(rockContext, QueryHintType.RECOMPILE))
                {
                    // Set up an 'occurrences' query for the group
                    var qry = attendanceService
                              .Queryable().AsNoTracking()
                              .Where(a => a.GroupId == group.Id);

                    // Filter by date range
                    if (fromDateTime.HasValue)
                    {
                        var fromDate = fromDateTime.Value.Date;
                        qry = qry.Where(a => DbFunctions.TruncateTime(a.StartDateTime) >= (fromDate));
                    }
                    if (toDateTime.HasValue)
                    {
                        var toDate = toDateTime.Value.Date;
                        qry = qry.Where(a => DbFunctions.TruncateTime(a.StartDateTime) < (toDate));
                    }

                    // Location Filter
                    if (locationIds.Any())
                    {
                        qry = qry.Where(a => locationIds.Contains(a.LocationId ?? 0));
                    }

                    // Schedule Filter
                    if (scheduleIds.Any())
                    {
                        qry = qry.Where(a => scheduleIds.Contains(a.ScheduleId ?? 0));
                    }

                    // Get the unique combination of location/schedule/date for the selected group
                    var occurrenceDates = qry
                                          .Select(a => new
                    {
                        a.LocationId,
                        a.ScheduleId,
                        Date = DbFunctions.TruncateTime(a.StartDateTime)
                    })
                                          .Distinct()
                                          .ToList();

                    // Get the locations for each unique location id
                    var selectedlocationIds = occurrenceDates.Select(o => o.LocationId).Distinct().ToList();

                    var locations = locationService
                                    .Queryable().AsNoTracking()
                                    .Where(l => selectedlocationIds.Contains(l.Id))
                                    .Select(l => new { l.Id, l.ParentLocationId, l.Name })
                                    .ToList();
                    var locationNames = new Dictionary <int, string>();
                    locations.ForEach(l => locationNames.Add(l.Id, l.Name));

                    // Get the parent location path for each unique location
                    var parentlocationPaths = new Dictionary <int, string>();
                    locations
                    .Where(l => l.ParentLocationId.HasValue)
                    .Select(l => l.ParentLocationId.Value)
                    .Distinct()
                    .ToList()
                    .ForEach(l => parentlocationPaths.Add(l, locationService.GetPath(l)));
                    var locationPaths = new Dictionary <int, string>();
                    locations
                    .Where(l => l.ParentLocationId.HasValue)
                    .ToList()
                    .ForEach(l => locationPaths.Add(l.Id, parentlocationPaths[l.ParentLocationId.Value]));

                    // Get the schedules for each unique schedule id
                    var selectedScheduleIds = occurrenceDates.Select(o => o.ScheduleId).Distinct().ToList();
                    var schedules           = scheduleService
                                              .Queryable().AsNoTracking()
                                              .Where(s => selectedScheduleIds.Contains(s.Id))
                                              .ToList();
                    var scheduleNames      = new Dictionary <int, string>();
                    var scheduleStartTimes = new Dictionary <int, TimeSpan>();
                    schedules
                    .ForEach(s =>
                    {
                        scheduleNames.Add(s.Id, s.Name);
                        scheduleStartTimes.Add(s.Id, s.StartTimeOfDay);
                    });

                    foreach (var occurrence in occurrenceDates.Where(o => o.Date.HasValue))
                    {
                        occurrences.Add(
                            new ScheduleOccurrence(
                                occurrence.Date.Value,
                                occurrence.ScheduleId.HasValue && scheduleStartTimes.ContainsKey(occurrence.ScheduleId.Value) ?
                                scheduleStartTimes[occurrence.ScheduleId.Value] : new TimeSpan(),
                                occurrence.ScheduleId,
                                occurrence.ScheduleId.HasValue && scheduleNames.ContainsKey(occurrence.ScheduleId.Value) ?
                                scheduleNames[occurrence.ScheduleId.Value] : string.Empty,
                                occurrence.LocationId,
                                occurrence.LocationId.HasValue && locationNames.ContainsKey(occurrence.LocationId.Value) ?
                                locationNames[occurrence.LocationId.Value] : string.Empty,
                                occurrence.LocationId.HasValue && locationPaths.ContainsKey(occurrence.LocationId.Value) ?
                                locationPaths[occurrence.LocationId.Value] : string.Empty
                                ));
                    }
                }

                // Load the attendance data for each occurrence
                if (loadSummaryData && occurrences.Any())
                {
                    var minDate       = occurrences.Min(o => o.Date);
                    var maxDate       = occurrences.Max(o => o.Date).AddDays(1);
                    var attendanceQry = attendanceService
                                        .Queryable().AsNoTracking()
                                        .Where(a =>
                                               a.GroupId.HasValue &&
                                               a.GroupId == group.Id &&
                                               a.StartDateTime >= minDate &&
                                               a.StartDateTime < maxDate &&
                                               a.PersonAlias != null &&
                                               a.PersonAliasId.HasValue)
                                        .Select(a => new
                    {
                        a.LocationId,
                        a.ScheduleId,
                        a.StartDateTime,
                        a.DidAttend,
                        a.DidNotOccur,
                        a.PersonAliasId,
                        PersonId = a.PersonAlias.PersonId
                    });

                    if (campusId.HasValue)
                    {
                        var familyGroupType = GroupTypeCache.Read(Rock.SystemGuid.GroupType.GROUPTYPE_FAMILY.AsGuid());
                        var campusQry       = new GroupMemberService(rockContext)
                                              .Queryable()
                                              .Where(g =>
                                                     g.Group != null &&
                                                     g.Group.GroupTypeId == familyGroupType.Id &&
                                                     g.Group.CampusId.HasValue &&
                                                     g.Group.CampusId.Value == campusId.Value
                                                     )
                                              .Select(m => m.PersonId);

                        attendanceQry = attendanceQry
                                        .Where(s => campusQry.Contains(s.PersonId));
                    }

                    var attendances = attendanceQry.ToList();

                    foreach (var summary in attendances
                             .GroupBy(a => new
                    {
                        a.LocationId,
                        a.ScheduleId,
                        Date = a.StartDateTime.Date
                    })
                             .Select(a => new
                    {
                        a.Key.LocationId,
                        a.Key.ScheduleId,
                        a.Key.Date,
                        DidAttendCount = a
                                         .Where(t => t.DidAttend.HasValue && t.DidAttend.Value)
                                         .Select(t => t.PersonAliasId.Value)
                                         .Distinct()
                                         .Count(),
                        DidNotOccurCount = a
                                           .Where(t => t.DidNotOccur.HasValue && t.DidNotOccur.Value)
                                           .Select(t => t.PersonAliasId.Value)
                                           .Distinct()
                                           .Count(),
                        TotalCount = a
                                     .Select(t => t.PersonAliasId)
                                     .Distinct()
                                     .Count()
                    }))
                    {
                        var occurrence = occurrences
                                         .Where(o =>
                                                o.ScheduleId.Equals(summary.ScheduleId) &&
                                                o.LocationId.Equals(summary.LocationId) &&
                                                o.Date.Equals(summary.Date))
                                         .FirstOrDefault();
                        if (occurrence != null)
                        {
                            occurrence.DidAttendCount   = summary.DidAttendCount;
                            occurrence.DidNotOccurCount = summary.DidNotOccurCount;
                            occurrence.TotalCount       = summary.TotalCount;
                        }
                    }
                }

                // Create any missing occurrences from the group's schedule (not location schedules)
                Schedule groupSchedule = null;
                if (group.ScheduleId.HasValue)
                {
                    groupSchedule = group.Schedule;
                    if (groupSchedule == null)
                    {
                        groupSchedule = new ScheduleService(rockContext).Get(group.ScheduleId.Value);
                    }
                }

                if (groupSchedule != null)
                {
                    var newOccurrences = new List <ScheduleOccurrence>();

                    var existingDates = occurrences
                                        .Where(o => o.ScheduleId.Equals(groupSchedule.Id))
                                        .Select(o => o.Date)
                                        .Distinct()
                                        .ToList();

                    var startDate = fromDateTime.HasValue ? fromDateTime.Value : RockDateTime.Today.AddMonths(-2);
                    var endDate   = toDateTime.HasValue ? toDateTime.Value : RockDateTime.Today.AddDays(1);

                    DDay.iCal.Event calEvent = groupSchedule.GetCalenderEvent();
                    if (calEvent != null)
                    {
                        // If schedule has an iCal schedule, get all the past occurrences
                        foreach (var occurrence in calEvent.GetOccurrences(startDate, endDate))
                        {
                            var scheduleOccurrence = new ScheduleOccurrence(
                                occurrence.Period.StartTime.Date, occurrence.Period.StartTime.TimeOfDay, groupSchedule.Id, groupSchedule.Name);
                            if (!existingDates.Contains(scheduleOccurrence.Date))
                            {
                                newOccurrences.Add(scheduleOccurrence);
                            }
                        }
                    }
                    else
                    {
                        // if schedule does not have an iCal, then check for weekly schedule and calculate occurrences starting with first attendance or current week
                        if (groupSchedule.WeeklyDayOfWeek.HasValue)
                        {
                            // default to start with date 2 months earlier
                            startDate = fromDateTime.HasValue ? fromDateTime.Value : RockDateTime.Today.AddMonths(-2);
                            if (existingDates.Any(d => d < startDate))
                            {
                                startDate = existingDates.Min();
                            }

                            // Back up start time to the correct day of week
                            while (startDate.DayOfWeek != groupSchedule.WeeklyDayOfWeek.Value)
                            {
                                startDate = startDate.AddDays(-1);
                            }

                            // Add the start time
                            if (groupSchedule.WeeklyTimeOfDay.HasValue)
                            {
                                startDate = startDate.Add(groupSchedule.WeeklyTimeOfDay.Value);
                            }

                            // Create occurrences up to current time
                            while (startDate < endDate)
                            {
                                if (!existingDates.Contains(startDate.Date))
                                {
                                    var scheduleOccurrence = new ScheduleOccurrence(startDate.Date, startDate.TimeOfDay, groupSchedule.Id, groupSchedule.Name);
                                    newOccurrences.Add(scheduleOccurrence);
                                }

                                startDate = startDate.AddDays(7);
                            }
                        }
                    }

                    if (newOccurrences.Any())
                    {
                        // Filter Exclusions
                        var groupType = GroupTypeCache.Read(group.GroupTypeId);
                        foreach (var exclusion in groupType.GroupScheduleExclusions)
                        {
                            if (exclusion.Start.HasValue && exclusion.End.HasValue)
                            {
                                foreach (var occurrence in newOccurrences.ToList())
                                {
                                    if (occurrence.Date >= exclusion.Start.Value &&
                                        occurrence.Date < exclusion.End.Value.AddDays(1))
                                    {
                                        newOccurrences.Remove(occurrence);
                                    }
                                }
                            }
                        }
                    }

                    foreach (var occurrence in newOccurrences)
                    {
                        occurrences.Add(occurrence);
                    }
                }
            }

            return(occurrences);
        }
        /// <summary>
        /// Binds the group placement grid.
        /// </summary>
        /// <param name="isExporting">if set to <c>true</c> [is exporting].</param>
        private void BindGroupPlacementGrid( bool isExporting = false )
        {
            int? groupId = gpGroupPlacementParentGroup.SelectedValueAsInt();
            int? instanceId = hfRegistrationInstanceId.Value.AsIntegerOrNull();
            if ( instanceId.HasValue )
            {
                using ( var rockContext = new RockContext() )
                {
                    // Start query for registrants
                    var qry = new RegistrationRegistrantService( rockContext )
                        .Queryable( "PersonAlias.Person.PhoneNumbers.NumberTypeValue,Fees.RegistrationTemplateFee,GroupMember.Group" ).AsNoTracking()
                        .Where( r =>
                            r.Registration.RegistrationInstanceId == instanceId.Value &&
                            r.PersonAlias != null &&
                            r.PersonAlias.Person != null );

                    if ( groupId.HasValue )
                    {
                        var validGroupIds = new GroupService( rockContext ).GetAllDescendents( groupId.Value )
                            .Select( g => g.Id )
                            .ToList();

                        var existingPeopleInGroups = new GroupMemberService( rockContext )
                            .Queryable().AsNoTracking()
                            .Where( m => validGroupIds.Contains( m.GroupId ) )
                            .Select( m => m.PersonId )
                            .ToList();

                        qry = qry.Where( r => !existingPeopleInGroups.Contains( r.PersonAlias.PersonId ) );
                    }

                    bool preloadCampusValues = false;
                    var registrantAttributeIds = new List<int>();
                    var personAttributesIds = new List<int>();
                    var groupMemberAttributesIds = new List<int>();

                    if ( RegistrantFields != null )
                    {
                        // Check if campus is used
                        preloadCampusValues = RegistrantFields
                            .Any( f =>
                                f.FieldSource == RegistrationFieldSource.PersonField &&
                                f.PersonFieldType.HasValue &&
                                f.PersonFieldType.Value == RegistrationPersonFieldType.Campus );

                        // Get all the registrant attributes selected
                        var registrantAttributes = RegistrantFields
                            .Where( f =>
                                f.Attribute != null &&
                                f.FieldSource == RegistrationFieldSource.RegistrationAttribute )
                            .Select( f => f.Attribute )
                            .ToList();
                        registrantAttributeIds = registrantAttributes.Select( a => a.Id ).Distinct().ToList();

                        // Get all the person attributes selected
                        var personAttributes = RegistrantFields
                            .Where( f =>
                                f.Attribute != null &&
                                f.FieldSource == RegistrationFieldSource.PersonAttribute )
                            .Select( f => f.Attribute )
                            .ToList();
                        personAttributesIds = personAttributes.Select( a => a.Id ).Distinct().ToList();

                        // Get all the group member attributes selected to be on grid
                        var groupMemberAttributes = RegistrantFields
                            .Where( f =>
                                f.Attribute != null &&
                                f.FieldSource == RegistrationFieldSource.GroupMemberAttribute )
                            .Select( f => f.Attribute )
                            .ToList();
                        groupMemberAttributesIds = groupMemberAttributes.Select( a => a.Id ).Distinct().ToList();
                    }

                    // Sort the query
                    IOrderedQueryable<RegistrationRegistrant> orderedQry = null;
                    SortProperty sortProperty = gGroupPlacements.SortProperty;
                    if ( sortProperty != null )
                    {
                        orderedQry = qry.Sort( sortProperty );
                    }
                    else
                    {
                        orderedQry = qry
                            .OrderBy( r => r.PersonAlias.Person.LastName )
                            .ThenBy( r => r.PersonAlias.Person.NickName );
                    }

                    // Set the grids LinqDataSource which will run query and set results for current page
                    gGroupPlacements.SetLinqDataSource<RegistrationRegistrant>( orderedQry );

                    if ( RegistrantFields != null )
                    {
                        // Get the query results for the current page
                        var currentPageRegistrants = gGroupPlacements.DataSource as List<RegistrationRegistrant>;
                        if ( currentPageRegistrants != null )
                        {
                            // Get all the registrant ids in current page of query results
                            var registrantIds = currentPageRegistrants
                                .Select( r => r.Id )
                                .Distinct()
                                .ToList();

                            // Get all the person ids in current page of query results
                            var personIds = currentPageRegistrants
                                .Select( r => r.PersonAlias.PersonId )
                                .Distinct()
                                .ToList();

                            // Get all the group member ids and the group id in current page of query results
                            var groupMemberIds = new List<int>();
                            GroupLinks = new Dictionary<int, string>();
                            foreach ( var groupMember in currentPageRegistrants
                                .Where( m =>
                                    m.GroupMember != null &&
                                    m.GroupMember.Group != null )
                                .Select( m => m.GroupMember ) )
                            {
                                groupMemberIds.Add( groupMember.Id );
                                GroupLinks.AddOrIgnore( groupMember.GroupId,
                                    isExporting ? groupMember.Group.Name :
                                        string.Format( "<a href='{0}'>{1}</a>",
                                            LinkedPageUrl( "GroupDetailPage", new Dictionary<string, string> { { "GroupId", groupMember.GroupId.ToString() } } ),
                                            groupMember.Group.Name ) );
                            }

                            // If the campus column was selected to be displayed on grid, preload all the people's
                            // campuses so that the databind does not need to query each row
                            if ( preloadCampusValues )
                            {
                                PersonCampusIds = new Dictionary<int, List<int>>();

                                Guid familyGroupTypeGuid = Rock.SystemGuid.GroupType.GROUPTYPE_FAMILY.AsGuid();
                                foreach ( var personCampusList in new GroupMemberService( rockContext )
                                    .Queryable().AsNoTracking()
                                    .Where( m =>
                                        m.Group.GroupType.Guid == familyGroupTypeGuid &&
                                        personIds.Contains( m.PersonId ) )
                                    .GroupBy( m => m.PersonId )
                                    .Select( m => new
                                    {
                                        PersonId = m.Key,
                                        CampusIds = m
                                            .Where( g => g.Group.CampusId.HasValue )
                                            .Select( g => g.Group.CampusId.Value )
                                            .ToList()
                                    } ) )
                                {
                                    PersonCampusIds.Add( personCampusList.PersonId, personCampusList.CampusIds );
                                }
                            }

                            // If there are any attributes that were selected to be displayed, we're going
                            // to try and read all attribute values in one query and then put them into a
                            // custom grid ObjectList property so that the AttributeField columns don't need
                            // to do the LoadAttributes and querying of values for each row/column
                            if ( personAttributesIds.Any() || groupMemberAttributesIds.Any() || registrantAttributeIds.Any() )
                            {
                                // Query the attribute values for all rows and attributes
                                var attributeValues = new AttributeValueService( rockContext )
                                    .Queryable( "Attribute" ).AsNoTracking()
                                    .Where( v =>
                                        v.EntityId.HasValue &&
                                        (
                                            (
                                                personAttributesIds.Contains( v.AttributeId ) &&
                                                personIds.Contains( v.EntityId.Value )
                                            ) ||
                                            (
                                                groupMemberAttributesIds.Contains( v.AttributeId ) &&
                                                groupMemberIds.Contains( v.EntityId.Value )
                                            ) ||
                                            (
                                                registrantAttributeIds.Contains( v.AttributeId ) &&
                                                registrantIds.Contains( v.EntityId.Value )
                                            )
                                        )
                                    )
                                    .ToList();

                                // Get the attributes to add to each row's object
                                var attributes = new Dictionary<string, AttributeCache>();
                                RegistrantFields
                                        .Where( f => f.Attribute != null )
                                        .Select( f => f.Attribute )
                                        .ToList()
                                    .ForEach( a => attributes
                                        .Add( a.Id.ToString() + a.Key, a ) );

                                // Initialize the grid's object list
                                gGroupPlacements.ObjectList = new Dictionary<string, object>();

                                // Loop through each of the current page's registrants and build an attribute
                                // field object for storing attributes and the values for each of the registrants
                                foreach ( var registrant in currentPageRegistrants )
                                {
                                    // Create a row attribute object
                                    var attributeFieldObject = new AttributeFieldObject();

                                    // Add the attributes to the attribute object
                                    attributeFieldObject.Attributes = attributes;

                                    // Add any person attribute values to object
                                    attributeValues
                                        .Where( v =>
                                            personAttributesIds.Contains( v.AttributeId ) &&
                                            v.EntityId.Value == registrant.PersonAlias.PersonId )
                                        .ToList()
                                        .ForEach( v => attributeFieldObject.AttributeValues
                                            .Add( v.AttributeId.ToString() + v.Attribute.Key, new AttributeValueCache( v ) ) );

                                    // Add any group member attribute values to object
                                    if ( registrant.GroupMemberId.HasValue )
                                    {
                                        attributeValues
                                            .Where( v =>
                                                groupMemberAttributesIds.Contains( v.AttributeId ) &&
                                                v.EntityId.Value == registrant.GroupMemberId.Value )
                                            .ToList()
                                            .ForEach( v => attributeFieldObject.AttributeValues
                                                .Add( v.AttributeId.ToString() + v.Attribute.Key, new AttributeValueCache( v ) ) );
                                    }

                                    // Add any registrant attribute values to object
                                    attributeValues
                                        .Where( v =>
                                            registrantAttributeIds.Contains( v.AttributeId ) &&
                                            v.EntityId.Value == registrant.Id )
                                        .ToList()
                                        .ForEach( v => attributeFieldObject.AttributeValues
                                            .Add( v.AttributeId.ToString() + v.Attribute.Key, new AttributeValueCache( v ) ) );

                                    // Add row attribute object to grid's object list
                                    gGroupPlacements.ObjectList.Add( registrant.Id.ToString(), attributeFieldObject );
                                }
                            }
                        }
                    }

                    gGroupPlacements.DataBind();
                }
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Executes the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        public void Execute( IJobExecutionContext context )
        {
            JobDataMap dataMap = context.JobDetail.JobDataMap;
            Guid? groupGuid = dataMap.GetString( "EligibleFollowers" ).AsGuidOrNull();
            Guid? systemEmailGuid = dataMap.GetString( "EmailTemplate" ).AsGuidOrNull();
            int followingEventsSent = 0;

            if ( groupGuid.HasValue && systemEmailGuid.HasValue )
            {
                var exceptionMsgs = new List<string>();

                using ( var rockContext = new RockContext() )
                {
                    var followingService = new FollowingService( rockContext );
                    var followingEventTypeService = new FollowingEventTypeService( rockContext );
                    var followingEventNotificationService = new FollowingEventNotificationService( rockContext );

                    // Get all the active event types
                    var eventTypes = followingEventTypeService
                        .Queryable().AsNoTracking()
                        .Where( e =>
                            e.EntityTypeId.HasValue &&
                            e.IsActive )
                        .OrderBy( e => e.Order )
                        .ToList();

                    // Get the required event types
                    var requiredEventTypes = eventTypes
                        .Where( e => e.IsNoticeRequired )
                        .ToList();

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

                    // Get all the subscriptions for the eligible people
                    var eventSubscriptions = new FollowingEventSubscriptionService( rockContext )
                        .Queryable( "PersonAlias" ).AsNoTracking()
                        .Where( f => eligiblePersonIds.Contains( f.PersonAlias.PersonId ) )
                        .ToList();

                    // Dictionaries used to store information that will be used to create notification
                    var personSubscriptions = new Dictionary<int, List<int>>();                     // Key: personId, Value: list of event type ids that person subscribes to
                    var personFollowings = new Dictionary<int, List<int>>();                        // Key: personId, Value: list of following ids that person follows
                    var eventsThatHappened = new Dictionary<int, Dictionary<int, string>>();        // Key: event type id Value: Dictionary of entity id and formatted event notice for the entity

                    //Get the subscriptions for each person
                    foreach ( int personId in eligiblePersonIds )
                    {
                        var personEventTypes = eventSubscriptions
                            .Where( s => s.PersonAlias.PersonId == personId )
                            .Select( s => s.EventType )
                            .ToList();
                        personEventTypes.AddRange( requiredEventTypes );
                        if ( personEventTypes.Any() )
                        {
                            personSubscriptions.AddOrIgnore( personId, personEventTypes
                                .OrderBy( e => e.Order )
                                .ThenBy( e => e.Name )
                                .Select( e => e.Id )
                                .Distinct()
                                .ToList() );
                        }
                    }

                    // Get a distinct list of each entitytype/entity that is being followed by anyone that subscribes to events
                    var followings = followingService
                        .Queryable( "PersonAlias" ).AsNoTracking()
                        .Where( f => personSubscriptions.Keys.Contains( f.PersonAlias.PersonId ) )
                        .ToList();

                    // group the followings by their type
                    var followedEntityIds = new Dictionary<int, List<int>>();
                    foreach ( var followedEntity in followings
                        .Select( f => new
                        {
                            f.EntityTypeId,
                            f.EntityId
                        } )
                        .Distinct() )
                    {
                        followedEntityIds.AddOrIgnore( followedEntity.EntityTypeId, new List<int>() );
                        followedEntityIds[followedEntity.EntityTypeId].Add( followedEntity.EntityId );
                    }

                    // group the followings by the follower
                    foreach ( int personId in personSubscriptions.Select( s => s.Key ) )
                    {
                        var personFollowing = followings
                            .Where( f => f.PersonAlias.PersonId == personId )
                            .Select( f => f.Id )
                            .ToList();

                        personFollowings.Add( personId, personFollowing );
                    }

                    var timestamp = RockDateTime.Now;

                    // foreach followed entitytype
                    foreach ( var keyVal in followedEntityIds )
                    {
                        // Get the entitytype
                        EntityTypeCache itemEntityType = EntityTypeCache.Read( keyVal.Key );
                        if ( itemEntityType.AssemblyName != null )
                        {
                            // get the actual type of what is being followed
                            Type entityType = itemEntityType.GetEntityType();
                            if ( entityType != null )
                            {
                                var dbContext = Reflection.GetDbContextForEntityType( entityType );
                                if ( dbContext != null )
                                {
                                    var serviceInstance = Reflection.GetServiceForEntityType( entityType, dbContext );
                                    if ( serviceInstance != null )
                                    {
                                        MethodInfo qryMethod = serviceInstance.GetType().GetMethod( "Queryable", new Type[] { } );
                                        var entityQry = qryMethod.Invoke( serviceInstance, new object[] { } ) as IQueryable<IEntity>;

                                        // If looking at person alias following, make sure to exclude deceased people
                                        if ( entityType == typeof( Rock.Model.PersonAlias ) )
                                        {
                                            var personAliasQry = entityQry as IQueryable<PersonAlias>;
                                            if ( personAliasQry != null )
                                            {
                                                entityQry = personAliasQry.Where( p => !p.Person.IsDeceased );
                                            }
                                        }

                                        var entityList = entityQry.Where( q => keyVal.Value.Contains( q.Id ) ).ToList();

                                        // If there are any followed entities of this type
                                        if ( entityList.Any() )
                                        {
                                            // Get the active event types for this entity type
                                            foreach ( var eventType in eventTypes.Where( e => e.FollowedEntityTypeId == keyVal.Key ) )
                                            {
                                                try
                                                {
                                                    // Get the component
                                                    var eventComponent = eventType.GetEventComponent();
                                                    if ( eventComponent != null )
                                                    {
                                                        // Get the previous notificatoins for this event type
                                                        var previousNotifications = followingEventNotificationService
                                                            .Queryable()
                                                            .Where( n => n.FollowingEventTypeId == eventType.Id )
                                                            .ToList();

                                                        // check each entity that is followed (by anyone)
                                                        foreach ( IEntity entity in entityList )
                                                        {
                                                            var previousNotification = previousNotifications
                                                                .Where( n => n.EntityId == entity.Id )
                                                                .FirstOrDefault();
                                                            DateTime? lastNotification = previousNotification != null ? previousNotification.LastNotified : (DateTime?)null;

                                                            // if the event happened
                                                            if ( eventComponent.HasEventHappened( eventType, entity, lastNotification ) )
                                                            {
                                                                // Store the event type id and the entity for later processing of notifications
                                                                eventsThatHappened.AddOrIgnore( eventType.Id, new Dictionary<int, string>() );
                                                                eventsThatHappened[eventType.Id].Add( entity.Id, eventComponent.FormatEntityNotification( eventType, entity ) );

                                                                if ( previousNotification == null )
                                                                {
                                                                    previousNotification = new FollowingEventNotification();
                                                                    previousNotification.FollowingEventTypeId = eventType.Id;
                                                                    previousNotification.EntityId = entity.Id;
                                                                    followingEventNotificationService.Add( previousNotification );
                                                                }
                                                                previousNotification.LastNotified = timestamp;
                                                            }
                                                        }

                                                        rockContext.SaveChanges();
                                                    }

                                                    eventType.LastCheckDateTime = RockDateTime.Now;
                                                }

                                                catch ( Exception ex )
                                                {
                                                    exceptionMsgs.Add( string.Format( "An exception occurred calculating events for the '{0}' suggestion type:{1}    {2}", eventType.Name, Environment.NewLine, ex.Messages().AsDelimited( Environment.NewLine + "   " ) ) );
                                                    ExceptionLogService.LogException( ex, System.Web.HttpContext.Current );
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    // send notificatons
                    var appRoot = Rock.Web.Cache.GlobalAttributesCache.Read( rockContext ).GetValue( "ExternalApplicationRoot" );

                    var possibleRecipients = new PersonService( rockContext )
                        .Queryable().AsNoTracking()
                        .Where( p => personSubscriptions.Keys.Contains( p.Id ) )
                        .ToList();

                    // Loop through the possible recipients that actually subscribe to events
                    foreach ( var personSubscription in personSubscriptions )
                    {
                        // Get the recipient person
                        int personId = personSubscription.Key;
                        var person = possibleRecipients.Where( p => p.Id == personId ).FirstOrDefault();
                        if ( person != null )
                        {
                            try
                            {

                                // Make sure person is actually following anything
                                if ( personFollowings.ContainsKey( personId ) )
                                {
                                    // Dictionary to store the entities that had an event for each event type
                                    var personEventTypeNotices = new List<FollowingEventTypeNotices>();

                                    // Get the event types that person subscribes to
                                    foreach ( var eventType in eventsThatHappened.Where( e => personSubscription.Value.Contains( e.Key ) ) )
                                    {
                                        // Get the EntityTypeId for this event type
                                        int entityTypeId = eventTypes
                                            .Where( e => e.Id == eventType.Key )
                                            .Select( e => e.FollowedEntityTypeId.Value )
                                            .FirstOrDefault();

                                        // Find all the entities with this event type that the person follows
                                        var personFollowedEntityIds = followings
                                            .Where( f =>
                                                personFollowings[personId].Contains( f.Id ) &&
                                                f.EntityTypeId == entityTypeId )
                                            .Select( f => f.EntityId )
                                            .ToList();

                                        // Get any of those entities that had an event happen
                                        var personFollowedEntities = eventType.Value
                                            .Where( e => personFollowedEntityIds.Contains( e.Key ) )
                                            .ToList();

                                        // If any were found
                                        if ( personFollowedEntities.Any() )
                                        {
                                            // Add the entry
                                            var eventTypeObj = eventTypes.Where( e => e.Id == eventType.Key ).FirstOrDefault();
                                            if ( eventTypeObj != null )
                                            {
                                                personEventTypeNotices.Add( new FollowingEventTypeNotices( eventTypeObj, personFollowedEntities.Select( e => e.Value ).ToList() ) );
                                            }
                                        }
                                    }

                                    // If there are any events for any of the entities that this person follows, send a notification
                                    if ( personEventTypeNotices.Any() )
                                    {
                                        // Send the notice
                                        var recipients = new List<RecipientData>();
                                        var mergeFields = new Dictionary<string, object>();
                                        mergeFields.Add( "Person", person );
                                        mergeFields.Add( "EventTypes", personEventTypeNotices.OrderBy( e => e.EventType.Order ).ToList() );
                                        recipients.Add( new RecipientData( person.Email, mergeFields ) );
                                        Email.Send( systemEmailGuid.Value, recipients, appRoot );
                                        followingEventsSent++;
                                    }
                                }
                            }
                            catch ( Exception ex )
                            {
                                exceptionMsgs.Add( string.Format( "An exception occurred sending event notice to '{0}':{1}    {2}", person.FullName, Environment.NewLine, ex.Messages().AsDelimited( Environment.NewLine + "   " ) ) );
                                ExceptionLogService.LogException( ex, System.Web.HttpContext.Current );
                            }
                        }
                    }
                }

                context.Result = string.Format( "{0} following events emails sent", followingEventsSent );

                if ( exceptionMsgs.Any() )
                {
                    throw new Exception( "One or more exceptions occurred calculating following events..." + Environment.NewLine + exceptionMsgs.AsDelimited( Environment.NewLine ) );
                }
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Load" /> event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs" /> object that contains the event data.</param>
        protected override void OnLoad( EventArgs e )
        {
            base.OnLoad( e );

            if ( Page.IsPostBack )
            {
                foreach ( var item in lvMembers.Items )
                {
                    var rblRole = item.FindControl( "rblRole" ) as RadioButtonList;
                    if ( rblRole != null )
                    {
                        int? roleId = rblRole.SelectedValueAsInt();
                        if ( roleId.HasValue )
                        {
                            var role = _groupType.Roles.Where( r => r.Id == roleId.Value ).FirstOrDefault();
                            if ( role != null )
                            {
                                int index = (int)lvMembers.DataKeys[item.DataItemIndex]["Index"];
                                if ( GroupMembers != null )
                                {
                                    var groupMember = GroupMembers.Where( m => m.Index == index ).FirstOrDefault();
                                    if ( groupMember != null )
                                    {
                                        groupMember.RoleGuid = role.Guid;
                                        groupMember.RoleName = role.Name;
                                        groupMember.IsLeader = role.IsLeader;
                                    }
                                }
                            }
                        }
                    }
                }

                if ( !string.IsNullOrWhiteSpace( hfActiveTab.Value ) )
                {
                    SetActiveTab();
                    modalAddPerson.Show();
                }

                BuildAttributes( false );
            }
            else
            {
                if ( _group != null )
                {
                    tbGroupName.Text = _group.Name;

                    // add banner text
                    if ( _isFamilyGroupType && !_group.Name.ToLower().EndsWith( " family" ) )
                    {
                        lBanner.Text = ( _group.Name + " Family" ).FormatAsHtmlTitle();
                    }
                    else
                    {
                        lBanner.Text = _group.Name.FormatAsHtmlTitle();
                    }

                    cpCampus.SelectedCampusId = _group.CampusId;

                    if ( _isFamilyGroupType )
                    {
                        // If all group members have the same record status, display that value
                        if ( _group.Members.Select( m => m.Person.RecordStatusValueId ).Distinct().Count() == 1 )
                        {
                            ddlRecordStatus.SetValue( _group.Members.Select( m => m.Person.RecordStatusValueId ).FirstOrDefault() );
                        }
                        else
                        {
                            ddlRecordStatus.Warning = String.Format( "{0} members have different record statuses", _groupType.Name );
                        }

                        // If all group members have the same inactive reason, set that value
                        if ( _group.Members.Select( m => m.Person.RecordStatusReasonValueId ).Distinct().Count() == 1 )
                        {
                            ddlReason.SetValue( _group.Members.Select( m => m.Person.RecordStatusReasonValueId ).FirstOrDefault() );
                        }
                        else
                        {
                            if ( String.IsNullOrWhiteSpace( ddlRecordStatus.Warning ) )
                            {
                                ddlRecordStatus.Warning = String.Format( "{0} members have different record status reasons", _groupType.Name );
                            }
                            else
                            {
                                ddlRecordStatus.Warning += " and record status reasons";
                            }
                        }
                    }

                    // Get all the group members
                    GroupMembers = new List<GroupMemberInfo>();
                    foreach ( var groupMember in _group.Members )
                    {
                        GroupMembers.Add( new GroupMemberInfo( groupMember, true ) );
                    }

                    // Figure out which ones are in another group
                    var groupMemberPersonIds = GroupMembers.Select( m => m.Id ).ToList();
                    var otherGroupPersonIds = new GroupMemberService( new RockContext() ).Queryable()
                        .Where( m =>
                            groupMemberPersonIds.Contains( m.PersonId ) &&
                            m.Group.GroupTypeId == _groupType.Id &&
                            m.GroupId != _group.Id )
                        .Select( m => m.PersonId )
                        .Distinct();
                    GroupMembers
                        .Where( m => otherGroupPersonIds.Contains( m.Id ) )
                        .ToList()
                        .ForEach( m => m.IsInOtherGroups = true );

                    BindMembers();

                    GroupAddresses = new List<GroupAddressInfo>();
                    foreach ( var groupLocation in _group.GroupLocations
                        .Where( l => l.GroupLocationTypeValue != null )
                        .OrderBy( l => l.GroupLocationTypeValue.Order ) )
                    {
                        GroupAddresses.Add( new GroupAddressInfo( groupLocation ) );
                    }
                    foreach ( var groupLocation in _group.GroupLocations
                        .Where( l => l.GroupLocationTypeValue == null ) )
                    {
                        GroupAddresses.Add( new GroupAddressInfo( groupLocation ) );
                    }

                    BindLocations();

                    BuildAttributes( true );
                }
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Executes the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        public void Execute( IJobExecutionContext context )
        {
            var exceptionMsgs = new List<string>();

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

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

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

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

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

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

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

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

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

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

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

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

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

                                            // Loop through each follower
                                            foreach ( var followerPersonId in personEntitySuggestions
                                                .Select( s => s.PersonId )
                                                .Distinct() )
                                            {

                                                using ( var suggestionContext = new RockContext() )
                                                {
                                                    var followingSuggestedService = new FollowingSuggestedService( suggestionContext );

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

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

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

                                                                // If not found, add one
                                                                if ( suggestion == null )
                                                                {
                                                                    suggestion = new FollowingSuggested();
                                                                    suggestion.EntityTypeId = entityTypeId;
                                                                    suggestion.EntityId = followedEntityId;
                                                                    suggestion.PersonAliasId = primaryAliasIds[followerPersonId];
                                                                    suggestion.SuggestionTypeId = suggestionType.Id;
                                                                    suggestion.Status = FollowingSuggestedStatus.PendingNotification;
                                                                    suggestion.StatusChangedDateTime = timestamp;
                                                                    followingSuggestedService.Add( suggestion );
                                                                }
                                                                else
                                                                {
                                                                    // If found, and it has not been ignored, and it's time to promote again, update the promote date
                                                                    if ( suggestion.Status != FollowingSuggestedStatus.Ignored &&
                                                                        (
                                                                            !suggestionType.ReminderDays.HasValue ||
                                                                            !suggestion.LastPromotedDateTime.HasValue ||
                                                                            suggestion.LastPromotedDateTime.Value.AddDays( suggestionType.ReminderDays.Value ) <= timestamp
                                                                        ) )
                                                                    {
                                                                        if ( suggestion.Status != FollowingSuggestedStatus.PendingNotification )
                                                                        {
                                                                            suggestion.StatusChangedDateTime = timestamp;
                                                                            suggestion.Status = FollowingSuggestedStatus.PendingNotification;
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }

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

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

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

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

                        var appRoot = Rock.Web.Cache.GlobalAttributesCache.Read( rockContext ).GetValue( "PublicApplicationRoot" );

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

                                foreach ( var suggestionType in suggestionTypes )
                                {
                                    var component = components.ContainsKey( suggestionType.Id ) ? components[suggestionType.Id] : null;
                                    if ( component != null && suggestedEntities.ContainsKey( suggestionType.Id ) )
                                    {

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

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

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

                                if ( personSuggestionNotices.Any() )
                                {
                                    // Send the notice
                                    var recipients = new List<RecipientData>();
                                    var mergeFields = new Dictionary<string, object>();
                                    mergeFields.Add( "Person", person );
                                    mergeFields.Add( "Suggestions", personSuggestionNotices.OrderBy( s => s.SuggestionType.Order ).ToList() );
                                    recipients.Add( new RecipientData( person.Email, mergeFields ) );
                                    Email.Send( systemEmailGuid.Value, recipients, appRoot );
                                    followingSuggestionsEmailsSent += recipients.Count();
                                    followingSuggestionsSuggestionsTotal += personSuggestionNotices.Count();
                                }

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

                    }
                }
            }

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

            if ( exceptionMsgs.Any() )
            {
                throw new Exception( "One or more exceptions occurred calculating suggestions..." + Environment.NewLine + exceptionMsgs.AsDelimited( Environment.NewLine ) );
            }
        }
Exemplo n.º 8
0
        /// <summary>
        /// Gets occurrence data for the selected group
        /// </summary>
        /// <param name="group">The group.</param>
        /// <param name="fromDateTime">From date time.</param>
        /// <param name="toDateTime">To date time.</param>
        /// <param name="locationIds">The location ids.</param>
        /// <param name="scheduleIds">The schedule ids.</param>
        /// <param name="loadSummaryData">if set to <c>true</c> [load summary data].</param>
        /// <param name="campusId">The campus identifier.</param>
        /// <returns></returns>
        public List<ScheduleOccurrence> GetGroupOccurrences( Group group, DateTime? fromDateTime, DateTime? toDateTime, 
            List<int> locationIds, List<int> scheduleIds, bool loadSummaryData, int? campusId )
        {
            var occurrences = new List<ScheduleOccurrence>();

            if ( group != null )
            {
                var rockContext = (RockContext)this.Context;
                var attendanceService = new AttendanceService( rockContext );
                var scheduleService = new ScheduleService( rockContext );
                var locationService = new LocationService( rockContext );

                using ( new Rock.Data.QueryHintScope( rockContext, QueryHintType.RECOMPILE ) )
                {

                    // Set up an 'occurrences' query for the group
                    var qry = attendanceService
                    .Queryable().AsNoTracking()
                    .Where( a => a.GroupId == group.Id );

                    // Filter by date range
                    if ( fromDateTime.HasValue )
                    {
                        var fromDate = fromDateTime.Value.Date;
                        qry = qry.Where( a => DbFunctions.TruncateTime( a.StartDateTime ) >= ( fromDate ) );
                    }
                    if ( toDateTime.HasValue )
                    {
                        var toDate = toDateTime.Value.Date;
                        qry = qry.Where( a => DbFunctions.TruncateTime( a.StartDateTime ) < ( toDate ) );
                    }

                    // Location Filter
                    if ( locationIds.Any() )
                    {
                        qry = qry.Where( a => locationIds.Contains( a.LocationId ?? 0 ) );
                    }

                    // Schedule Filter
                    if ( scheduleIds.Any() )
                    {
                        qry = qry.Where( a => scheduleIds.Contains( a.ScheduleId ?? 0 ) );
                    }

                    // Get the unique combination of location/schedule/date for the selected group
                    var occurrenceDates = qry
                    .Select( a => new
                    {
                        a.LocationId,
                        a.ScheduleId,
                        Date = DbFunctions.TruncateTime( a.StartDateTime )
                    } )
                    .Distinct()
                    .ToList();

                    // Get the locations for each unique location id
                    var selectedlocationIds = occurrenceDates.Select( o => o.LocationId ).Distinct().ToList();

                    var locations = locationService
                        .Queryable().AsNoTracking()
                        .Where( l => selectedlocationIds.Contains( l.Id ) )
                        .Select( l => new { l.Id, l.ParentLocationId, l.Name } )
                        .ToList();
                    var locationNames = new Dictionary<int, string>();
                    locations.ForEach( l => locationNames.Add( l.Id, l.Name ) );

                    // Get the parent location path for each unique location
                    var parentlocationPaths = new Dictionary<int, string>();
                    locations
                        .Where( l => l.ParentLocationId.HasValue )
                        .Select( l => l.ParentLocationId.Value )
                        .Distinct()
                        .ToList()
                        .ForEach( l => parentlocationPaths.Add( l, locationService.GetPath( l ) ) );
                    var locationPaths = new Dictionary<int, string>();
                    locations
                        .Where( l => l.ParentLocationId.HasValue )
                        .ToList()
                        .ForEach( l => locationPaths.Add( l.Id, parentlocationPaths[l.ParentLocationId.Value] ) );

                    // Get the schedules for each unique schedule id
                    var selectedScheduleIds = occurrenceDates.Select( o => o.ScheduleId ).Distinct().ToList();
                    var schedules = scheduleService
                        .Queryable().AsNoTracking()
                        .Where( s => selectedScheduleIds.Contains( s.Id ) )
                        .ToList();
                    var scheduleNames = new Dictionary<int, string>();
                    var scheduleStartTimes = new Dictionary<int, TimeSpan>();
                    schedules
                        .ForEach( s =>
                        {
                            scheduleNames.Add( s.Id, s.Name );
                            scheduleStartTimes.Add( s.Id, s.StartTimeOfDay );
                        } );

                    foreach ( var occurrence in occurrenceDates.Where( o => o.Date.HasValue ) )
                    {
                        occurrences.Add(
                            new ScheduleOccurrence(
                                occurrence.Date.Value,
                                occurrence.ScheduleId.HasValue && scheduleStartTimes.ContainsKey( occurrence.ScheduleId.Value ) ?
                                    scheduleStartTimes[occurrence.ScheduleId.Value] : new TimeSpan(),
                                occurrence.ScheduleId,
                                occurrence.ScheduleId.HasValue && scheduleNames.ContainsKey( occurrence.ScheduleId.Value ) ?
                                    scheduleNames[occurrence.ScheduleId.Value] : string.Empty,
                                occurrence.LocationId,
                                occurrence.LocationId.HasValue && locationNames.ContainsKey( occurrence.LocationId.Value ) ?
                                    locationNames[occurrence.LocationId.Value] : string.Empty,
                                occurrence.LocationId.HasValue && locationPaths.ContainsKey( occurrence.LocationId.Value ) ?
                                    locationPaths[occurrence.LocationId.Value] : string.Empty
                            ) );
                    }
                }

                // Load the attendance data for each occurrence
                if ( loadSummaryData && occurrences.Any())
                {
                    var minDate = occurrences.Min( o => o.Date );
                    var maxDate = occurrences.Max( o => o.Date ).AddDays( 1 );
                    var attendanceQry = attendanceService
                        .Queryable().AsNoTracking()
                        .Where( a =>
                            a.GroupId.HasValue &&
                            a.GroupId == group.Id &&
                            a.StartDateTime >= minDate &&
                            a.StartDateTime < maxDate &&
                            a.PersonAlias != null &&
                            a.PersonAliasId.HasValue )
                        .Select( a => new
                        {
                            a.LocationId,
                            a.ScheduleId,
                            a.StartDateTime,
                            a.DidAttend,
                            a.DidNotOccur,
                            a.PersonAliasId,
                            PersonId = a.PersonAlias.PersonId
                        } );

                    if ( campusId.HasValue )
                    {
                        var familyGroupType = GroupTypeCache.Read( Rock.SystemGuid.GroupType.GROUPTYPE_FAMILY.AsGuid() );
                        var campusQry = new GroupMemberService( rockContext )
                            .Queryable()
                            .Where( g =>
                                g.Group != null &&
                                g.Group.GroupTypeId == familyGroupType.Id &&
                                g.Group.CampusId.HasValue &&
                                g.Group.CampusId.Value == campusId.Value
                            )
                            .Select( m => m.PersonId );

                        attendanceQry = attendanceQry
                            .Where( s => campusQry.Contains( s.PersonId ) );
                    }

                    var attendances = attendanceQry.ToList();

                    foreach ( var summary in attendances
                        .GroupBy( a => new
                        {
                            a.LocationId,
                            a.ScheduleId,
                            Date = a.StartDateTime.Date
                        } )
                        .Select( a => new
                        {
                            a.Key.LocationId,
                            a.Key.ScheduleId,
                            a.Key.Date,
                            DidAttendCount = a
                                .Where( t => t.DidAttend.HasValue && t.DidAttend.Value )
                                .Select( t => t.PersonAliasId.Value )
                                .Distinct()
                                .Count(),
                            DidNotOccurCount = a
                                .Where( t => t.DidNotOccur.HasValue && t.DidNotOccur.Value )
                                .Select( t => t.PersonAliasId.Value )
                                .Distinct()
                                .Count(),
                            TotalCount = a
                                .Select( t => t.PersonAliasId )
                                .Distinct()
                                .Count()
                        } ) )
                    {
                        var occurrence = occurrences
                            .Where( o =>
                                o.ScheduleId.Equals( summary.ScheduleId ) &&
                                o.LocationId.Equals( summary.LocationId ) &&
                                o.Date.Equals( summary.Date ) )
                            .FirstOrDefault();
                        if ( occurrence != null )
                        {
                            occurrence.DidAttendCount = summary.DidAttendCount;
                            occurrence.DidNotOccurCount = summary.DidNotOccurCount;
                            occurrence.TotalCount = summary.TotalCount;
                        }
                    }
                }

                // Create any missing occurrences from the group's schedule (not location schedules)
                Schedule groupSchedule = null;
                if ( group.ScheduleId.HasValue )
                {
                    groupSchedule = group.Schedule;
                    if ( groupSchedule == null )
                    {
                        groupSchedule = new ScheduleService( rockContext ).Get( group.ScheduleId.Value );
                    }
                }

                if ( groupSchedule != null )
                {
                    var newOccurrences = new List<ScheduleOccurrence>();

                    var existingDates = occurrences
                        .Where( o => o.ScheduleId.Equals( groupSchedule.Id ) )
                        .Select( o => o.Date )
                        .Distinct()
                        .ToList();

                    var startDate = fromDateTime.HasValue ? fromDateTime.Value : RockDateTime.Today.AddMonths( -2 );
                    var endDate = toDateTime.HasValue ? toDateTime.Value : RockDateTime.Today.AddDays( 1 );

                    if ( !string.IsNullOrWhiteSpace( groupSchedule.iCalendarContent ) )
                    {
                        // If schedule has an iCal schedule, get all the past occurrences
                        foreach ( var occurrence in groupSchedule.GetOccurrences( startDate, endDate ) )
                        {
                            var scheduleOccurrence = new ScheduleOccurrence(
                                occurrence.Period.StartTime.Date, occurrence.Period.StartTime.TimeOfDay, groupSchedule.Id, groupSchedule.Name );
                            if ( !existingDates.Contains( scheduleOccurrence.Date ) )
                            {
                                newOccurrences.Add( scheduleOccurrence );
                            }
                        }
                    }
                    else
                    {
                        // if schedule does not have an iCal, then check for weekly schedule and calculate occurrences starting with first attendance or current week
                        if ( groupSchedule.WeeklyDayOfWeek.HasValue )
                        {

                            // default to start with date 2 months earlier
                            startDate = fromDateTime.HasValue ? fromDateTime.Value : RockDateTime.Today.AddMonths( -2 );
                            if ( existingDates.Any( d => d < startDate ) )
                            {
                                startDate = existingDates.Min();
                            }

                            // Back up start time to the correct day of week
                            while ( startDate.DayOfWeek != groupSchedule.WeeklyDayOfWeek.Value )
                            {
                                startDate = startDate.AddDays( -1 );
                            }

                            // Add the start time
                            if ( groupSchedule.WeeklyTimeOfDay.HasValue )
                            {
                                startDate = startDate.Add( groupSchedule.WeeklyTimeOfDay.Value );
                            }

                            // Create occurrences up to current time
                            while ( startDate < endDate )
                            {
                                if ( !existingDates.Contains( startDate.Date ) )
                                {
                                    var scheduleOccurrence = new ScheduleOccurrence( startDate.Date, startDate.TimeOfDay, groupSchedule.Id, groupSchedule.Name );
                                    newOccurrences.Add( scheduleOccurrence );
                                }

                                startDate = startDate.AddDays( 7 );
                            }
                        }
                    }

                    if ( newOccurrences.Any() )
                    {
                        // Filter Exclusions
                        var groupType = GroupTypeCache.Read( group.GroupTypeId );
                        foreach ( var exclusion in groupType.GroupScheduleExclusions )
                        {
                            if ( exclusion.Start.HasValue && exclusion.End.HasValue )
                            {
                                foreach ( var occurrence in newOccurrences.ToList() )
                                {
                                    if ( occurrence.Date >= exclusion.Start.Value &&
                                        occurrence.Date < exclusion.End.Value.AddDays( 1 ) )
                                    {
                                        newOccurrences.Remove( occurrence );
                                    }
                                }
                            }
                        }
                    }

                    foreach( var occurrence in newOccurrences )
                    {
                        occurrences.Add( occurrence );
                    }

                }
            }

            return occurrences;
        }
        /// <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 roomBalanceGroupTypes = GetAttributeValue( action, "RoomBalanceGrouptypes" ).SplitDelimitedValues().AsGuidList();
            bool useGroupMembership = GetAttributeValue( action, "PrioritizeGroupMembership" ).AsBoolean();
            int roomBalanceOverride = GetAttributeValue( action, "BalancingOverride" ).AsIntegerOrNull() ?? 5;
            var excludedLocations = GetAttributeValue( action, "ExcludedLocations" ).SplitDelimitedValues( false )
                .Select( s => s.Trim() );

            // get admin-selected attribute keys instead of using a hardcoded key
            var personSpecialNeedsKey = string.Empty;
            var personSpecialNeedsGuid = GetAttributeValue( action, "PersonSpecialNeedsAttribute" ).AsGuid();
            if ( personSpecialNeedsGuid != Guid.Empty )
            {
                personSpecialNeedsKey = AttributeCache.Read( personSpecialNeedsGuid, rockContext ).Key;
            }

            var groupSpecialNeedsKey = string.Empty;
            var groupSpecialNeedsGuid = GetAttributeValue( action, "GroupSpecialNeedsAttribute" ).AsGuid();
            if ( personSpecialNeedsGuid != Guid.Empty )
            {
                groupSpecialNeedsKey = AttributeCache.Read( groupSpecialNeedsGuid, rockContext ).Key;
            }

            var groupAgeRangeKey = string.Empty;
            var groupAgeRangeGuid = GetAttributeValue( action, "GroupAgeRangeAttribute" ).AsGuid();
            if ( personSpecialNeedsGuid != Guid.Empty )
            {
                groupAgeRangeKey = AttributeCache.Read( groupAgeRangeGuid, rockContext ).Key;
            }

            var groupGradeRangeKey = string.Empty;
            var groupGradeRangeGuid = GetAttributeValue( action, "GroupGradeRangeAttribute" ).AsGuid();
            if ( personSpecialNeedsGuid != Guid.Empty )
            {
                groupGradeRangeKey = AttributeCache.Read( groupGradeRangeGuid, rockContext ).Key;
            }

            // log a warning if any of the attributes are missing or invalid
            if ( string.IsNullOrWhiteSpace( personSpecialNeedsKey ) )
            {
                action.AddLogEntry( string.Format( "The Person Special Needs attribute is not selected or invalid for '{0}'.", action.ActionType.Name ) );
            }

            if ( string.IsNullOrWhiteSpace( groupSpecialNeedsKey ) )
            {
                action.AddLogEntry( string.Format( "The Group Special Needs attribute is not selected or invalid for '{0}'.", action.ActionType.Name ) );
            }

            if ( string.IsNullOrWhiteSpace( groupAgeRangeKey ) )
            {
                action.AddLogEntry( string.Format( "The Group Age Range attribute is not selected or invalid for '{0}'.", action.ActionType.Name ) );
            }

            if ( string.IsNullOrWhiteSpace( groupGradeRangeKey ) )
            {
                action.AddLogEntry( string.Format( "The Group Grade Range attribute is not selected or invalid for '{0}'.", action.ActionType.Name ) );
            }

            var family = checkInState.CheckIn.Families.FirstOrDefault( f => f.Selected );
            if ( family != null )
            {
                // don't process people who already have assignments
                foreach ( var person in family.People.Where( f => f.Selected && !f.GroupTypes.Any( gt => gt.Selected ) ) )
                {
                    decimal baseVariance = 100;
                    char[] delimiter = { ',' };

                    // check if this person has special needs
                    var hasSpecialNeeds = person.Person.GetAttributeValue( personSpecialNeedsKey ).AsBoolean();

                    // get a list of room balanced grouptype ID's since CheckInGroup model is a shallow clone
                    var roomBalanceGroupTypeIds = person.GroupTypes.Where( gt => roomBalanceGroupTypes.Contains( gt.GroupType.Guid ) )
                        .Select( gt => gt.GroupType.Id ).ToList();

                    if ( person.GroupTypes.Count > 0 )
                    {
                        CheckInGroupType bestGroupType = null;
                        IEnumerable<CheckInGroup> validGroups;
                        if ( person.GroupTypes.Count == 1 )
                        {
                            bestGroupType = person.GroupTypes.FirstOrDefault();
                            validGroups = bestGroupType.Groups;
                        }
                        else
                        {
                            // Start with unfiltered groups since one criteria may not match exactly ( SN > Grade > Age )
                            validGroups = person.GroupTypes.SelectMany( gt => gt.Groups );
                        }

                        // check how many groups exist without getting the whole list
                        int numValidGroups = validGroups.Take( 2 ).Count();
                        if ( numValidGroups > 0 )
                        {
                            CheckInGroup bestGroup = null;
                            IEnumerable<CheckInLocation> validLocations;
                            if ( numValidGroups == 1 )
                            {
                                bestGroup = validGroups.FirstOrDefault();
                                validLocations = bestGroup.Locations;
                            }
                            else
                            {
                                // Select by group assignment first
                                if ( useGroupMembership )
                                {
                                    var personAssignments = new GroupMemberService( rockContext ).GetByPersonId( person.Person.Id )
                                        .Select( gm => gm.Group.Id ).ToList();
                                    if ( personAssignments.Count > 0 )
                                    {
                                        bestGroup = validGroups.FirstOrDefault( g => personAssignments.Contains( g.Group.Id ) );
                                    }
                                }

                                // Select group by best fit
                                if ( bestGroup == null )
                                {
                                    // Check age and special needs
                                    CheckInGroup closestAgeGroup = null;
                                    CheckInGroup closestNeedsGroup = null;

                                    var ageGroups = validGroups.Where( g => g.Group.AttributeValues.ContainsKey( groupAgeRangeKey )
                                            && g.Group.AttributeValues[groupAgeRangeKey].Value != null
                                            && g.Group.AttributeValues.ContainsKey( personSpecialNeedsKey ) == hasSpecialNeeds
                                        )
                                        .ToList()
                                        .Select( g => new
                                        {
                                            Group = g,
                                            AgeRange = g.Group.AttributeValues[groupAgeRangeKey].Value
                                                .Split( delimiter, StringSplitOptions.None )
                                                .Where( av => !string.IsNullOrEmpty( av ) )
                                                .Select( av => av.AsType<decimal>() )
                                        } )
                                        .ToList();

                                    if ( ageGroups.Count > 0 )
                                    {
                                        if ( person.Person.Age != null )
                                        {
                                            baseVariance = 100;
                                            decimal personAge = (decimal)person.Person.AgePrecise;
                                            foreach ( var ageGroup in ageGroups.Where( g => g.AgeRange.Any() ) )
                                            {
                                                var minAge = ageGroup.AgeRange.First();
                                                var maxAge = ageGroup.AgeRange.Last();
                                                var ageVariance = maxAge - minAge;
                                                if ( maxAge >= personAge && minAge <= personAge && ageVariance < baseVariance )
                                                {
                                                    closestAgeGroup = ageGroup.Group;
                                                    baseVariance = ageVariance;

                                                    if ( hasSpecialNeeds )
                                                    {
                                                        closestNeedsGroup = closestAgeGroup;
                                                    }
                                                }
                                            }
                                        }
                                        else if ( hasSpecialNeeds )
                                        {
                                            // person has special needs but not an age, assign to first special needs group
                                            closestNeedsGroup = ageGroups.FirstOrDefault().Group;
                                        }
                                    }

                                    // Check grade
                                    CheckInGroup closestGradeGroup = null;
                                    if ( person.Person.GradeOffset != null )
                                    {
                                        var gradeValues = DefinedTypeCache.Read( new Guid( Rock.SystemGuid.DefinedType.SCHOOL_GRADES ) ).DefinedValues;
                                        var gradeGroups = validGroups.Where( g => g.Group.AttributeValues.ContainsKey( groupGradeRangeKey ) && g.Group.AttributeValues[groupGradeRangeKey].Value != null )
                                            .ToList()
                                            .Select( g => new
                                            {
                                                Group = g,
                                                GradeOffsets = g.Group.AttributeValues[groupGradeRangeKey].Value
                                                    .Split( delimiter, StringSplitOptions.None )
                                                    .Where( av => !string.IsNullOrEmpty( av ) )
                                                    .Select( av => gradeValues.FirstOrDefault( v => v.Guid == new Guid( av ) ) )
                                                    .Select( av => av.Value.AsDecimal() )
                                            } )
                                            .ToList();

                                        // Only check groups that have valid grade offsets
                                        if ( person.Person.GradeOffset != null && gradeGroups.Count > 0 )
                                        {
                                            baseVariance = 100;
                                            decimal gradeOffset = (decimal)person.Person.GradeOffset.Value;
                                            foreach ( var gradeGroup in gradeGroups.Where( g => g.GradeOffsets.Any() ) )
                                            {
                                                var minGradeOffset = gradeGroup.GradeOffsets.First();
                                                var maxGradeOffset = gradeGroup.GradeOffsets.Last();
                                                var gradeVariance = minGradeOffset - maxGradeOffset;
                                                if ( minGradeOffset >= gradeOffset && maxGradeOffset <= gradeOffset && gradeVariance < baseVariance )
                                                {
                                                    closestGradeGroup = gradeGroup.Group;
                                                    baseVariance = gradeVariance;
                                                }
                                            }

                                            /* ======================================================== *
                                                optional scenario: find the next closest grade group
                                            * ========================================================= *
                                                if (grade > max)
                                                    grade - max
                                                else if (grade < min)
                                                    min - grade
                                                else 0;

                                            // add a tiny variance to offset larger groups:
                                                result += ((max - min)/100)
                                            * ========================================================= */
                                        }
                                    }

                                    // Assignment priority: Group Membership, then Ability, then Grade, then Age, then the first non-excluded group
                                    // NOTE: if group member is prioritized (and membership exists) this section is skipped entirely
                                    bestGroup = closestNeedsGroup ?? closestGradeGroup ?? closestAgeGroup ?? validGroups.FirstOrDefault( g => !g.ExcludedByFilter );

                                    // room balance if they fit into multiple groups
                                    if ( bestGroup != null && roomBalanceGroupTypeIds.Contains( bestGroup.Group.GroupTypeId ) )
                                    {
                                        int? bestScheduleId = null;
                                        var availableSchedules = validGroups.SelectMany( g => g.Locations.SelectMany( l => l.Schedules ) ).ToList();
                                        if ( availableSchedules.Any() )
                                        {
                                            bestScheduleId = availableSchedules.OrderBy( s => s.StartTime ).Select( s => s.Schedule.Id ).FirstOrDefault();
                                        }

                                        if ( bestScheduleId != null )
                                        {
                                            validGroups = validGroups.Where( g => g.AvailableForSchedule.Contains( (int)bestScheduleId ) );
                                        }

                                        var currentGroupAttendance = bestGroup.Locations.Select( l => Helpers.ReadAttendanceBySchedule( l.Location.Id, bestScheduleId ) ).Sum();
                                        var lowestGroup = validGroups.Where( g => !g.ExcludedByFilter && !excludedLocations.Contains( g.Group.Name ) )
                                            .Select( g => new { Group = g, Attendance = g.Locations.Select( l => Helpers.ReadAttendanceBySchedule( l.Location.Id, bestScheduleId ) ).Sum() } )
                                            .OrderBy( g => g.Attendance )
                                            .FirstOrDefault();

                                        if ( lowestGroup != null && lowestGroup.Attendance < ( currentGroupAttendance - roomBalanceOverride ) )
                                        {
                                            bestGroup = lowestGroup.Group;
                                        }
                                    }
                                }

                                validLocations = bestGroup.Locations;
                            }

                            // check how many locations exist without getting the whole list
                            int numValidLocations = validLocations.Take( 2 ).Count();
                            if ( numValidLocations > 0 )
                            {
                                CheckInLocation bestLocation = null;
                                IEnumerable<CheckInSchedule> validSchedules;
                                if ( numValidLocations == 1 )
                                {
                                    bestLocation = validLocations.FirstOrDefault();
                                    validSchedules = bestLocation.Schedules;
                                }
                                else
                                {
                                    var filteredLocations = validLocations.Where( l => !l.ExcludedByFilter && !excludedLocations.Contains( l.Location.Name ) && l.Schedules.Any( s => s.Schedule.IsCheckInActive ) );

                                    // room balance if they fit into multiple locations
                                    if ( roomBalanceGroupTypeIds.Contains( bestGroup.Group.GroupTypeId ) )
                                    {
                                        int? bestScheduleId = null;
                                        var availableSchedules = filteredLocations.SelectMany( l => l.Schedules )
                                            .DistinctBy( s => s.Schedule.Id ).ToList();
                                        if ( availableSchedules.Any() )
                                        {
                                            bestScheduleId = availableSchedules.OrderBy( s => s.StartTime ).Select( s => s.Schedule.Id ).FirstOrDefault();
                                        }

                                        if ( bestScheduleId != null )
                                        {
                                            filteredLocations = filteredLocations.Where( l => l.AvailableForSchedule.Contains( (int)bestScheduleId ) );
                                        }

                                        filteredLocations = filteredLocations.OrderBy( l => Helpers.ReadAttendanceBySchedule( l.Location.Id, bestScheduleId ) );
                                    }

                                    bestLocation = filteredLocations.FirstOrDefault();
                                    validSchedules = bestLocation.Schedules;
                                }

                                // check how many schedules exist without getting the whole list
                                int numValidSchedules = validSchedules.Take( 2 ).Count();
                                if ( numValidSchedules > 0 )
                                {
                                    // finished finding assignment, verify everything is selected
                                    var bestSchedule = validSchedules.OrderBy( s => s.Schedule.StartTimeOfDay ).FirstOrDefault();
                                    bestSchedule.Selected = true;
                                    bestSchedule.PreSelected = true;

                                    if ( bestLocation != null )
                                    {
                                        bestLocation.PreSelected = true;
                                        bestLocation.Selected = true;

                                        if ( bestGroup != null )
                                        {
                                            bestGroup.PreSelected = true;
                                            bestGroup.Selected = true;

                                            bestGroupType = person.GroupTypes.FirstOrDefault( gt => gt.GroupType.Id == bestGroup.Group.GroupTypeId );
                                            if ( bestGroupType != null )
                                            {
                                                bestGroupType.Selected = true;
                                                bestGroupType.PreSelected = true;
                                                person.Selected = true;
                                                person.PreSelected = true;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return true;
        }
Exemplo n.º 10
0
        private void BindData()
        {
            using ( var rockContext = new RockContext() )
            {
                var dataView = new DataViewService( rockContext ).Get( _dataViewGuid ?? Guid.Empty );
                if ( dataView != null )
                {
                    var personService = new PersonService( rockContext );

                    // Filter people by dataview
                    var errorMessages = new List<string>();
                    var paramExpression = personService.ParameterExpression;
                    var whereExpression = dataView.GetExpression( personService, paramExpression, out errorMessages );
                    var personQry = personService
                        .Queryable( false, false ).AsNoTracking()
                        .Where( paramExpression, whereExpression, null );

                    var dvPersonIdQry = personQry.Select( p => p.Id );

                    bool filteredQry = false;

                    // Filter by first name
                    string firstName = tbFirstName.Text.Trim();
                    if ( !string.IsNullOrWhiteSpace( firstName ) )
                    {
                        personQry = personQry.Where( p =>
                            p.FirstName.StartsWith( firstName ) ||
                            p.NickName.StartsWith( firstName ) );
                        filteredQry = true;
                    }

                    // Filter by last name
                    string lastName = tbLastName.Text.Trim();
                    if ( !string.IsNullOrWhiteSpace( lastName ) )
                    {
                        personQry = personQry.Where( p =>
                            p.LastName.StartsWith( lastName ) );
                        filteredQry = true;
                    }

                    if ( filteredQry || _showAllPeople )
                    {
                        SetColumnWidths();

                        if ( _optOutGroupGuid.HasValue )
                        {
                            var optOutPersonIdQry = new GroupMemberService( rockContext )
                                .Queryable().AsNoTracking()
                                .Where( g => g.Group.Guid.Equals( _optOutGroupGuid.Value ) )
                                .Select( g => g.PersonId );

                            personQry = personQry.Where( p =>
                                !optOutPersonIdQry.Contains( p.Id ) );
                        }

                        if ( _showFamily )
                        {
                            BindFamilies( rockContext, personQry, dvPersonIdQry );
                        }
                        else
                        {
                            BindPeople( rockContext, personQry );
                        }

                    }
                    else
                    {
                        rptPeople.Visible = false;
                        rptFamilies.Visible = false;
                    }
                }
                else
                {
                    rptPeople.Visible = false;
                    rptFamilies.Visible = false;
                    ShowMessages( new List<string> { "This block requires a valid Data View setting." } );
                }

                if ( CurrentPerson != null && _optOutGroupGuid.HasValue )
                {
                    bool optedOut = new GroupMemberService( rockContext )
                            .Queryable().AsNoTracking()
                            .Any( m =>
                                m.PersonId == CurrentPerson.Id &&
                                m.Group.Guid.Equals( _optOutGroupGuid.Value ) );
                    lbOptInOut.Text = optedOut ? "Opt in to the Directory" : "Opt Out of the Directory";
                }
            }
        }