/// <summary> /// Binds the grid. /// </summary> private void BindGrid() { if (HasFilterErrors()) { return; } var checkinAreaFilter = CheckinManagerHelper.GetCheckinAreaFilter(this); CampusCache campus = GetCampusFromContext(); var selectedScheduleIds = lbSchedules.SelectedValues.AsIntegerList(); if (selectedScheduleIds.Any()) { btnShowFilter.AddCssClass("criteria-exists bg-warning"); } else { btnShowFilter.RemoveCssClass("criteria-exists bg-warning"); } CheckinManagerHelper.SaveRoomListFilterToCookie(selectedScheduleIds.ToArray()); var rockContext = new RockContext(); var groupService = new GroupService(rockContext); var groupTypeService = new GroupTypeService(rockContext); IEnumerable <CheckinAreaPath> checkinAreaPaths; if (checkinAreaFilter != null) { checkinAreaPaths = groupTypeService.GetCheckinAreaDescendantsPath(checkinAreaFilter.Id); } else { checkinAreaPaths = groupTypeService.GetAllCheckinAreaPaths(); } var selectedGroupTypeIds = checkinAreaPaths.Select(a => a.GroupTypeId).Distinct().ToArray(); var groupLocationService = new GroupLocationService(rockContext); var groupLocationsQuery = groupLocationService.Queryable() .Where(gl => selectedGroupTypeIds.Contains(gl.Group.GroupTypeId) && gl.Group.IsActive && (!gl.Group.IsArchived)); var parentLocationIdParameter = PageParameter(PageParameterKey.ParentLocationId).AsIntegerOrNull(); var locationIdParameter = PageParameter(PageParameterKey.LocationId).AsIntegerOrNull(); var locationGridField = gRoomList.ColumnsOfType <RockLiteralField>().FirstOrDefault(a => a.ID == "lRoomName"); if (locationGridField != null && !locationGridField.Visible) { locationGridField.Visible = true; } List <int> locationIds; if (locationIdParameter.HasValue) { // If LocationId is specified in the URL, list only items for the specified location. // Also, hide the Location Grid Column and set the PanelTitle as the location's name // This will take precedence over the selected campus+locations and/or <seealso cref="ParentLocationId"/> var locationService = new LocationService(rockContext); lPanelTitle.Text = locationService.GetSelect(locationIdParameter.Value, s => s.Name); locationIds = new List <int>(); locationIds.Add(locationIdParameter.Value); if (locationGridField != null) { // since a LocationId parameter was specified, the LocationGrid field doesn't need to be shown locationGridField.Visible = false; } } else if (parentLocationIdParameter.HasValue) { // If parentLocationId is specified, show the direct (first level) child locations of the specified ParentLocationId. // This will take precedence over the selected campus+locations. var locationService = new LocationService(rockContext); locationIds = locationService.Queryable() .Where(a => a.ParentLocationId.HasValue && a.ParentLocationId.Value == parentLocationIdParameter.Value) .Select(a => a.Id).ToList(); lPanelTitle.Text = string.Format("{0} Child Locations", locationService.GetSelect(parentLocationIdParameter.Value, s => s.Name)); } else { // Limit locations (rooms) to locations within the selected campus. locationIds = new LocationService(rockContext).GetAllDescendentIds(campus.LocationId.Value).ToList(); locationIds.Add(campus.LocationId.Value, true); lPanelTitle.Text = "Room List"; } groupLocationsQuery = groupLocationsQuery.Where(a => locationIds.Contains(a.LocationId)); if (selectedScheduleIds.Any()) { groupLocationsQuery = groupLocationsQuery.Where(a => a.Schedules.Any(s => s.IsActive && s.CheckInStartOffsetMinutes.HasValue && selectedScheduleIds.Contains(s.Id))); } else { groupLocationsQuery = groupLocationsQuery.Where(a => a.Schedules.Any(s => s.IsActive && s.CheckInStartOffsetMinutes.HasValue)); } var groupLocationList = groupLocationsQuery.Select(a => new GroupLocationInfo { LocationId = a.LocationId, LocationName = a.Location.Name, ParentGroupId = a.Group.ParentGroupId, ParentGroupName = a.Group.ParentGroup.Name, GroupId = a.Group.Id, GroupName = a.Group.Name, GroupTypeId = a.Group.GroupTypeId }).ToList(); var startDateTime = RockDateTime.Today; DateTime currentDateTime; if (campus != null) { currentDateTime = campus.CurrentDateTime; } else { currentDateTime = RockDateTime.Now; } // Get all Attendance records for the current day and location. var attendanceQuery = new AttendanceService(rockContext).Queryable().Where(a => a.StartDateTime >= startDateTime && a.DidAttend == true && a.StartDateTime <= currentDateTime && a.PersonAliasId.HasValue && a.Occurrence.GroupId.HasValue && a.Occurrence.LocationId.HasValue && a.Occurrence.ScheduleId.HasValue); // Limit attendances (rooms) to the groupLocations' LocationId and GroupIds that we'll be showing var groupLocationLocationIds = groupLocationList.Select(a => a.LocationId).Distinct().ToList(); var groupLocationGroupsIds = groupLocationList.Select(a => a.GroupId).Distinct().ToList(); attendanceQuery = attendanceQuery.Where(a => groupLocationLocationIds.Contains(a.Occurrence.LocationId.Value) && groupLocationGroupsIds.Contains(a.Occurrence.GroupId.Value)); attendanceQuery = attendanceQuery.Where(a => selectedGroupTypeIds.Contains(a.Occurrence.Group.GroupTypeId)); if (selectedScheduleIds.Any()) { attendanceQuery = attendanceQuery.Where(a => selectedScheduleIds.Contains(a.Occurrence.ScheduleId.Value)); } var rosterAttendeeAttendanceList = RosterAttendeeAttendance.Select(attendanceQuery).ToList(); var groupTypeIdsWithAllowCheckout = selectedGroupTypeIds .Select(a => GroupTypeCache.Get(a)) .Where(a => a != null) .Where(gt => gt.GetCheckInConfigurationAttributeValue(Rock.SystemKey.GroupTypeAttributeKey.CHECKIN_GROUPTYPE_ALLOW_CHECKOUT).AsBoolean()) .Select(a => a.Id) .Distinct().ToList(); var groupTypeIdsWithEnablePresence = selectedGroupTypeIds .Select(a => GroupTypeCache.Get(a)) .Where(a => a != null) .Where(gt => gt.GetCheckInConfigurationAttributeValue(Rock.SystemKey.GroupTypeAttributeKey.CHECKIN_GROUPTYPE_ENABLE_PRESENCE).AsBoolean()) .Select(a => a.Id) .Distinct(); var scheduleIds = rosterAttendeeAttendanceList.Select(a => a.ScheduleId.Value).Distinct().ToList(); var scheduleList = new ScheduleService(rockContext).GetByIds(scheduleIds).ToList(); var scheduleIdsWasScheduleOrCheckInActiveForCheckOut = new HashSet <int>(scheduleList.Where(a => a.WasScheduleOrCheckInActiveForCheckOut(currentDateTime)).Select(a => a.Id).ToList()); rosterAttendeeAttendanceList = rosterAttendeeAttendanceList.Where(a => { var allowCheckout = groupTypeIdsWithAllowCheckout.Contains(a.GroupTypeId); if (!allowCheckout) { /* * If AllowCheckout is false, remove all Attendees whose schedules are not currently active. Per the 'WasSchedule...ActiveForCheckOut()' * method below: "Check-out can happen while check-in is active or until the event ends (start time + duration)." This will help to keep * the list of 'Present' attendees cleaned up and accurate, based on the room schedules, since the volunteers have no way to manually mark * an Attendee as 'Checked-out'. * * If, on the other hand, AllowCheckout is true, it will be the volunteers' responsibility to click the [Check-out] button when an * Attendee leaves the room, in order to keep the list of 'Present' Attendees in order. This will also allow the volunteers to continue * 'Checking-out' Attendees in the case that the parents are running late in picking them up. */ return(scheduleIdsWasScheduleOrCheckInActiveForCheckOut.Contains(a.ScheduleId.Value)); } else { return(true); } }).ToList(); var attendancesByLocationId = rosterAttendeeAttendanceList .GroupBy(a => a.LocationId.Value).ToDictionary(k => k.Key, v => v.ToList()); _attendancesByLocationIdAndGroupId = attendancesByLocationId.ToDictionary( k => k.Key, v => v.Value.GroupBy(x => x.GroupId.Value).ToDictionary(x => x.Key, xx => xx.ToList())); _checkinAreaPathsLookupByGroupTypeId = checkinAreaPaths.ToDictionary(k => k.GroupTypeId, v => v); _showOnlyParentGroup = this.GetAttributeValue(AttributeKey.ShowOnlyParentGroup).AsBoolean(); var roomList = new List <RoomInfo>(); foreach (var groupLocation in groupLocationList) { AddToRoomList(roomList, groupLocation); } List <RoomInfo> sortedRoomList; if (_showOnlyParentGroup) { sortedRoomList = roomList.OrderBy(a => a.LocationName).ToList(); } else { sortedRoomList = new List <RoomInfo>(); sortedRoomList.AddRange(roomList.OfType <RoomInfoByGroup>().OrderBy(a => a.LocationName).ThenBy(a => a.GroupName).ToList()); } var checkedInCountField = gRoomList.ColumnsOfType <RockLiteralField>().FirstOrDefault(a => a.ID == "lCheckedInCount"); var presentCountField = gRoomList.ColumnsOfType <RockLiteralField>().FirstOrDefault(a => a.ID == "lPresentCount"); var checkedOutCountField = gRoomList.ColumnsOfType <RockLiteralField>().FirstOrDefault(a => a.ID == "lCheckedOutCount"); checkedOutCountField.Visible = groupTypeIdsWithAllowCheckout.Any(); // Always show Present Count regardless of the 'Enable Presence' setting. (A person gets automatically marked present if 'Enable Presence' is disabled.) presentCountField.Visible = true; if (groupTypeIdsWithEnablePresence.Any()) { // Presence is enabled, so records could be in the 'Checked-in' state // and Present column should be labeled 'Present'. checkedInCountField.Visible = true; presentCountField.HeaderText = "Present"; } else { // https://app.asana.com/0/0/1199637795718017/f // 'Enable Presence' is disabled, so a person automatically gets marked present. // So, no records will be in the 'Checked-In (but no present)' state. // Also, a user thinks of 'Present' as 'Checked-In' if they don't use the 'Enable Presence' feature checkedInCountField.Visible = false; presentCountField.HeaderText = "Checked-In"; } if (_showOnlyParentGroup) { gRoomList.DataKeyNames = new string[1] { "LocationId" }; } else { gRoomList.DataKeyNames = new string[2] { "LocationId", "GroupId" }; } gRoomList.DataSource = sortedRoomList; gRoomList.DataBind(); }