private void FilterLocationList(CheckInGroupType checkInGroupType, bool remove, CheckinConfigurationHelper.LocationSelectionStrategy attributeLocationSelectionStrategy, List <CheckInSchedule> selectedSchedules)
        {
            // Order the list
            var checkinGroups = checkInGroupType.Groups.OrderBy(g => g.Group.Order).ToList();

            foreach (var checkinGroup in checkinGroups)
            {
                // Get a list of locations that have not reached their threshold.
                var locationListQuery = checkinGroup.Locations
                                        .Where(l => l.Location.SoftRoomThreshold == null || KioskLocationAttendance.Get(l.Location.Id).CurrentCount <= l.Location.SoftRoomThreshold.Value);

                List <CheckInLocation> locationList = new List <CheckInLocation>();

                if (attributeLocationSelectionStrategy == CheckinConfigurationHelper.LocationSelectionStrategy.Balance)
                {
                    locationList = locationListQuery.OrderBy(l => KioskLocationAttendance.Get(l.Location.Id).CurrentCount).ToList();
                }
                else if (attributeLocationSelectionStrategy == CheckinConfigurationHelper.LocationSelectionStrategy.FillInOrder)
                {
                    locationList = locationListQuery.OrderBy(l => l.Order).ToList();
                }

                if (selectedSchedules.Count() == 1)
                {
                    // If we only have one schedule then we can just remove the other locations and not care about the schedules.
                    FilterLocations(checkinGroup, locationList, remove);
                }
                else
                {
                    // Now we have to care about schedules.
                    FilterLocationSchedules(checkinGroup, locationList, selectedSchedules, remove);
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Handles the ItemDataBound event of the rLocations control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Web.UI.WebControls.RepeaterItemEventArgs"/> instance containing the event data.</param>
        protected void rLocations_ItemDataBound(object sender, System.Web.UI.WebControls.RepeaterItemEventArgs e)
        {
            object locationDataItem = e.Item.DataItem;

            if (locationDataItem != null)
            {
                var lbOpen   = e.Item.FindControl("lbOpen") as LinkButton;
                var lbClose  = e.Item.FindControl("lbClose") as LinkButton;
                var isActive = ( bool )locationDataItem.GetPropertyValue("IsActive");

                if (isActive)
                {
                    lbClose.RemoveCssClass("btn-danger");
                    lbClose.RemoveCssClass("active");
                    lbOpen.AddCssClass("btn-success");
                    lbOpen.AddCssClass("active");
                }
                else
                {
                    lbOpen.RemoveCssClass("btn-success");
                    lbOpen.RemoveCssClass("active");
                    lbClose.AddCssClass("btn-danger");
                    lbClose.AddCssClass("active");
                }

                var lLocationName = e.Item.FindControl("lLocationName") as Literal;
                lLocationName.Text = locationDataItem.GetPropertyValue("Name") as string;

                var lLocationCount = e.Item.FindControl("lLocationCount") as Literal;
                lLocationCount.Text = KioskLocationAttendance.Get(( int )locationDataItem.GetPropertyValue("LocationId")).CurrentCount.ToString();
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Formats the count.
        /// </summary>
        /// <param name="locationId">The location id.</param>
        /// <returns></returns>
        protected string FormatCount(int locationId)
        {
            string currentCountFormat = GetAttributeValue("CurrentCountFormat");

            if (!string.IsNullOrWhiteSpace(currentCountFormat) && currentCountFormat.Contains("{0}"))
            {
                return(string.Format(" <span class='checkin-sub-title'>{0}</span>",
                                     string.Format(currentCountFormat, KioskLocationAttendance.Read(locationId).CurrentCount)));
            }

            return(string.Empty);
        }
Ejemplo n.º 4
0
        /// <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, Model.WorkflowAction action, Object entity, out List <string> errorMessages)
        {
            var checkInState = GetCheckInState(entity, out errorMessages);

            if (checkInState != null)
            {
                var family = checkInState.CheckIn.CurrentFamily;
                if (family != null)
                {
                    var remove = GetAttributeValue(action, "Remove").AsBoolean();

                    foreach (var person in family.People)
                    {
                        foreach (var groupType in person.GroupTypes)
                        {
                            foreach (var group in groupType.Groups)
                            {
                                foreach (var location in group.Locations.ToList())
                                {
                                    if (location.Location.SoftRoomThreshold.HasValue)
                                    {
                                        var locAttendance = KioskLocationAttendance.Get(location.Location.Id);
                                        if (locAttendance != null &&
                                            !locAttendance.DistinctPersonIds.Contains(person.Person.Id) &&
                                            location.Location.SoftRoomThreshold.Value <= locAttendance.CurrentCount)
                                        {
                                            if (remove)
                                            {
                                                group.Locations.Remove(location);
                                            }
                                            else
                                            {
                                                location.ExcludedByFilter = true;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                return(true);
            }

            return(false);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Get the option text.
        /// </summary>
        private string GetOptionText(CheckInPersonSummary option)
        {
            var format      = GetAttributeValue(AttributeKey.OptionFormat);
            var mergeFields = new Dictionary <string, object>
            {
                { "GroupType", option.GroupType },
                { "Group", option.Group },
                { "Location", option.Location },
                { "Schedule", option.Schedule },
                { "DisplayLocationCount", CurrentCheckInState.CheckInType.DisplayLocationCount },
                { "LocationCount", KioskLocationAttendance.Get(option.Location.Location.Id).CurrentCount }
            };

            var optionText = format.ResolveMergeFields(mergeFields);

            return(optionText);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Reads the attendance cache by schedule.
        /// </summary>
        /// <param name="locationId">The location identifier.</param>
        /// <param name="scheduleId">The schedule identifier.</param>
        /// <returns></returns>
        public static int ReadAttendanceBySchedule(int locationId, int?scheduleId)
        {
            var attendanceCount = 0;
            var attendanceCache = KioskLocationAttendance.Get(locationId);

            if (attendanceCache != null)
            {
                if (scheduleId != null)
                {
                    attendanceCount += attendanceCache.Groups.SelectMany(g => g.Schedules).Where(s => s.ScheduleId == (int)scheduleId).Sum(s => s.CurrentCount);
                }
                else
                {
                    attendanceCount = attendanceCache.CurrentCount;
                }
            }

            return(attendanceCount);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Refreshes the view.
        /// </summary>
        private void RefreshView()
        {
            hfRefreshTimerSeconds.Value = "10";
            ManagerLoggedIn             = false;
            pnlActive.Visible           = false;
            pnlManagerLogin.Visible     = false;
            pnlManager.Visible          = false;
            btnManager.Visible          = true;

            lblActiveWhen.Text = string.Empty;

            if (CurrentKioskId == null || CurrentLocationId == null)
            {
                NavigateToParentPage();
                return;
            }

            pnlActive.Visible = true;

            var currentPeople = KioskLocationAttendance.Read(CurrentLocationId.Value);

            var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(this.RockPage, this.CurrentPerson);

            // add link to detail page
            Dictionary <string, object> linkedPages = new Dictionary <string, object>();

            mergeFields.Add("PersonIds", currentPeople.DistinctPersonIds);
            mergeFields.Add("LocationCount", currentPeople.CurrentCount);
            mergeFields.Add("CurrentKioskId", CurrentKioskId);
            mergeFields.Add("CurrentLocationId", CurrentLocationId);

            lOutput.Text = GetAttributeValue("LavaTemplate").ResolveMergeFields(mergeFields);

            // show debug info
            if (GetAttributeValue("EnableDebug").AsBoolean() && IsUserAuthorized(Authorization.EDIT))
            {
                lDebug.Visible = true;
                lDebug.Text    = mergeFields.lavaDebugInfo();
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Handles the ItemDataBound event of the rLocations control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Web.UI.WebControls.RepeaterItemEventArgs"/> instance containing the event data.</param>
        protected void rLocations_ItemDataBound(object sender, System.Web.UI.WebControls.RepeaterItemEventArgs e)
        {
            var locationGridItem = e.Item.DataItem as LocationGridItem;

            if (locationGridItem != null)
            {
                if (hfAllowOpenClose.Value.AsBoolean())
                {
                    var lbOpen   = e.Item.FindControl("lbOpen") as LinkButton;
                    var lbClose  = e.Item.FindControl("lbClose") as LinkButton;
                    var isActive = locationGridItem.IsActive;

                    if (isActive)
                    {
                        lbClose.RemoveCssClass("btn-danger");
                        lbClose.RemoveCssClass("active");
                        lbOpen.AddCssClass("btn-success");
                        lbOpen.AddCssClass("active");
                    }
                    else
                    {
                        lbOpen.RemoveCssClass("btn-success");
                        lbOpen.RemoveCssClass("active");
                        lbClose.AddCssClass("btn-danger");
                        lbClose.AddCssClass("active");
                    }
                }
                else
                {
                    var divLocationToggle = e.Item.FindControl("divLocationToggle") as HtmlGenericControl;
                    divLocationToggle.Visible = false;
                }

                var lLocationName = e.Item.FindControl("lLocationName") as Literal;
                lLocationName.Text = locationGridItem.Name;

                var lLocationCount = e.Item.FindControl("lLocationCount") as Literal;
                lLocationCount.Text = KioskLocationAttendance.Get(locationGridItem.LocationId).CurrentCount.ToString();
            }
        }
        /// <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, Model.WorkflowAction action, Object entity, out List <string> errorMessages)
        {
            var checkInState = GetCheckInState(entity, out errorMessages);

            if (checkInState != null)
            {
                Guid checkinGroupAttributeGuid = GetAttributeValue(action, "CheckinGroupAttribute").AsGuid();
                if (checkinGroupAttributeGuid == Guid.Empty)
                {
                    throw new Exception("CheckInGroupAttribute not set. Set attribute to continue.");
                }
                string checkinGroupAttributeKey = AttributeCache.Read(checkinGroupAttributeGuid, rockContext).Key;

                string sessionAttributeKey = GetAttributeValue(action, "SessionAttributeKey");

                AttendanceCode attendanceCode = null;
                DateTime       startDateTime  = RockDateTime.Now;

                bool reuseCodeForFamily = checkInState.CheckInType != null && checkInState.CheckInType.ReuseSameCode;
                int  securityCodeLength = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeAlphaNumericLength : 3;

                AttendanceCodeService attendanceCodeService = new AttendanceCodeService(rockContext);
                AttendanceService     attendanceService     = new AttendanceService(rockContext);
                GroupMemberService    groupMemberService    = new GroupMemberService(rockContext);
                PersonAliasService    personAliasService    = new PersonAliasService(rockContext);
                GroupService          groupService          = new GroupService(rockContext);

                var family = checkInState.CheckIn.CurrentFamily;
                if (family != null)
                {
                    foreach (var person in family.GetPeople(true))
                    {
                        if (reuseCodeForFamily && attendanceCode != null)
                        {
                            person.SecurityCode = attendanceCode.Code;
                        }
                        else
                        {
                            attendanceCode      = AttendanceCodeService.GetNew(securityCodeLength);
                            person.SecurityCode = attendanceCode.Code;
                        }

                        foreach (var groupType in person.GetGroupTypes(true))
                        {
                            foreach (var group in groupType.GetGroups(true))
                            {
                                var referenceGroupGuid = group.Group.GetAttributeValue(checkinGroupAttributeKey).AsGuid();
                                var referenceGroup     = groupService.Get(referenceGroupGuid);
                                if (referenceGroup == null)
                                {
                                    group.Selected = false;
                                    continue;
                                }
                                GroupMember groupMember = groupMemberService.GetByGroupIdAndPersonId(referenceGroup.Id, person.Person.Id).FirstOrDefault();
                                if (groupMember == null)
                                {
                                    group.Selected = false;
                                    continue;
                                }
                                groupMember.LoadAttributes();
                                int sessions = groupMember.GetAttributeValue(sessionAttributeKey).AsInteger();

                                foreach (var location in group.GetLocations(true))
                                {
                                    foreach (var schedule in location.GetSchedules(true))
                                    {
                                        if (sessions == 0)
                                        {
                                            continue;
                                        }

                                        // Only create one attendance record per day for each person/schedule/group/location
                                        var attendance = attendanceService.Get(startDateTime, location.Location.Id, schedule.Schedule.Id, group.Group.Id, person.Person.Id);
                                        if (attendance == null)
                                        {
                                            var primaryAlias = personAliasService.GetPrimaryAlias(person.Person.Id);
                                            if (primaryAlias != null)
                                            {
                                                attendance                   = rockContext.Attendances.Create();
                                                attendance.LocationId        = location.Location.Id;
                                                attendance.CampusId          = location.CampusId;
                                                attendance.ScheduleId        = schedule.Schedule.Id;
                                                attendance.GroupId           = group.Group.Id;
                                                attendance.PersonAlias       = primaryAlias;
                                                attendance.PersonAliasId     = primaryAlias.Id;
                                                attendance.DeviceId          = checkInState.Kiosk.Device.Id;
                                                attendance.SearchTypeValueId = checkInState.CheckIn.SearchType.Id;
                                                attendanceService.Add(attendance);
                                            }

                                            //decrement sessions and save
                                            sessions--;
                                            groupMember.SetAttributeValue(sessionAttributeKey, sessions);
                                            groupMember.SaveAttributeValues();
                                        }
                                        else
                                        {
                                            foreach (var cPerson in checkInState.CheckIn.Families.SelectMany(f => f.People))
                                            {
                                                cPerson.Selected = false;
                                                cPerson.GroupTypes.ForEach(gt => gt.Selected = false);
                                            }
                                            return(true);
                                        }

                                        attendance.AttendanceCodeId = attendanceCode.Id;
                                        attendance.StartDateTime    = startDateTime;
                                        attendance.EndDateTime      = null;
                                        attendance.DidAttend        = true;

                                        KioskLocationAttendance.AddAttendance(attendance);
                                    }
                                }
                            }
                        }
                    }
                }

                rockContext.SaveChanges();
                return(true);
            }
            return(false);
        }
Ejemplo n.º 10
0
        /// <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, WorkflowAction action, Object entity, out List <string> errorMessages)
        {
            var checkInState = GetCheckInState(entity, out errorMessages);

            if (checkInState != null)
            {
                bool loadAll = GetAttributeValue(action, "LoadAll").AsBoolean();

                foreach (var family in checkInState.CheckIn.GetFamilies(true))
                {
                    foreach (var person in family.GetPeople(!loadAll))
                    {
                        foreach (var groupType in person.GetGroupTypes(!loadAll).ToList())
                        {
                            var kioskGroupType = checkInState.Kiosk.ActiveGroupTypes(checkInState.ConfiguredGroupTypes)
                                                 .Where(g => g.GroupType.Id == groupType.GroupType.Id)
                                                 .FirstOrDefault();

                            if (kioskGroupType != null)
                            {
                                foreach (var group in groupType.GetGroups(!loadAll))
                                {
                                    var closedGroupLocationIds = new AttendanceOccurrenceService(rockContext)
                                                                 .Queryable()
                                                                 .AsNoTracking()
                                                                 .Where(o =>
                                                                        o.GroupId == group.Group.Id &&
                                                                        o.OccurrenceDate == RockDateTime.Today)
                                                                 .WhereAttributeValue(rockContext, "rocks.kfs.OccurrenceClosed", "True")
                                                                 .Select(l => l.LocationId)
                                                                 .ToList();

                                    var loadBalance = group.Group.GetAttributeValue("rocks.kfs.LoadBalanceLocations").AsBoolean();
                                    if (loadBalance && loadAll)
                                    {
                                        group.Locations.Clear();
                                    }

                                    var locationAttendance = new Dictionary <CheckInLocation, int>();

                                    foreach (var kioskGroup in kioskGroupType.KioskGroups
                                             .Where(g => g.Group.Id == group.Group.Id && g.IsCheckInActive)
                                             .ToList())
                                    {
                                        foreach (var kioskLocation in kioskGroup.KioskLocations.Where(l => l.IsCheckInActive && l.IsActiveAndNotFull && !closedGroupLocationIds.Contains(l.Location.Id)))
                                        {
                                            if (!group.Locations.Any(l => l.Location.Id == kioskLocation.Location.Id))
                                            {
                                                var checkInLocation = new CheckInLocation();
                                                checkInLocation.Location = kioskLocation.Location.Clone(false);
                                                checkInLocation.Location.CopyAttributesFrom(kioskLocation.Location);
                                                checkInLocation.CampusId = kioskLocation.CampusId;
                                                checkInLocation.Order    = kioskLocation.Order;
                                                locationAttendance.Add(checkInLocation, KioskLocationAttendance.Get(checkInLocation.Location.Id).CurrentCount);
                                            }
                                        }
                                    }

                                    if (loadBalance)
                                    {
                                        var sortedLocationAttendance = locationAttendance.ToList();
                                        sortedLocationAttendance.Sort((x, y) => x.Key.Location.Name.CompareTo(y.Key.Location.Name));
                                        sortedLocationAttendance.Sort((x, y) => x.Value.CompareTo(y.Value));
                                        var order = 0;
                                        foreach (var checkInLocationPair in sortedLocationAttendance)
                                        {
                                            var checkInLocation = checkInLocationPair.Key;
                                            checkInLocation.Order = order;
                                            group.Locations.Add(checkInLocation);

                                            order++;
                                        }
                                    }
                                    else
                                    {
                                        group.Locations.AddRange(locationAttendance.Select(l => l.Key).ToList());
                                    }
                                }
                            }
                        }
                    }
                }

                return(true);
            }

            return(false);
        }
Ejemplo n.º 11
0
        /// <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, Model.WorkflowAction action, Object entity, out List <string> errorMessages)
        {
            var checkInState = GetCheckInState(entity, out errorMessages);

            if (checkInState != null)
            {
                AttendanceCode attendanceCode = null;
                DateTime       startDateTime  = RockDateTime.Now;

                bool reuseCodeForFamily = GetAttributeValue(action, "ReuseCodeForFamily").AsBoolean();

                int securityCodeLength = 3;
                if (!int.TryParse(GetAttributeValue(action, "SecurityCodeLength"), out securityCodeLength))
                {
                    securityCodeLength = 3;
                }

                var attendanceCodeService = new AttendanceCodeService(rockContext);
                var attendanceService     = new AttendanceService(rockContext);
                var groupMemberService    = new GroupMemberService(rockContext);
                var personAliasService    = new PersonAliasService(rockContext);

                foreach (var family in checkInState.CheckIn.Families.Where(f => f.Selected))
                {
                    foreach (var person in family.People.Where(p => p.Selected))
                    {
                        if (reuseCodeForFamily && attendanceCode != null)
                        {
                            person.SecurityCode = attendanceCode.Code;
                        }
                        else
                        {
                            attendanceCode      = AttendanceCodeService.GetNew(securityCodeLength);
                            person.SecurityCode = attendanceCode.Code;
                        }

                        foreach (var groupType in person.GroupTypes.Where(g => g.Selected))
                        {
                            foreach (var group in groupType.Groups.Where(g => g.Selected))
                            {
                                foreach (var location in group.Locations.Where(l => l.Selected))
                                {
                                    if (groupType.GroupType.AttendanceRule == AttendanceRule.AddOnCheckIn &&
                                        groupType.GroupType.DefaultGroupRoleId.HasValue &&
                                        !groupMemberService.GetByGroupIdAndPersonId(group.Group.Id, person.Person.Id, true).Any())
                                    {
                                        var groupMember = new GroupMember();
                                        groupMember.GroupId     = group.Group.Id;
                                        groupMember.PersonId    = person.Person.Id;
                                        groupMember.GroupRoleId = groupType.GroupType.DefaultGroupRoleId.Value;
                                        groupMemberService.Add(groupMember);
                                    }

                                    foreach (var schedule in location.Schedules.Where(s => s.Selected))
                                    {
                                        // Only create one attendance record per day for each person/schedule/group/location
                                        var attendance = attendanceService.Get(startDateTime, location.Location.Id, schedule.Schedule.Id, group.Group.Id, person.Person.Id);
                                        if (attendance == null)
                                        {
                                            var primaryAlias = personAliasService.GetPrimaryAlias(person.Person.Id);
                                            if (primaryAlias != null)
                                            {
                                                attendance = rockContext.Attendances.Create();
                                                attendance.Occurrence.LocationId = location.Location.Id;
                                                attendance.CampusId = location.CampusId;
                                                attendance.Occurrence.ScheduleId = schedule.Schedule.Id;
                                                attendance.Occurrence.GroupId    = group.Group.Id;
                                                attendance.PersonAlias           = primaryAlias;
                                                attendance.PersonAliasId         = primaryAlias.Id;
                                                attendance.DeviceId          = checkInState.Kiosk.Device.Id;
                                                attendance.SearchTypeValueId = checkInState.CheckIn.SearchType.Id;
                                                attendanceService.Add(attendance);
                                            }
                                        }

                                        attendance.AttendanceCodeId = attendanceCode.Id;
                                        attendance.StartDateTime    = startDateTime;
                                        attendance.EndDateTime      = null;
                                        attendance.DidAttend        = false;

                                        KioskLocationAttendance.AddAttendance(attendance);
                                    }
                                }
                            }
                        }
                    }
                }

                rockContext.SaveChanges();
                return(true);
            }
            errorMessages.Add($"Attempted to run {this.GetType().GetFriendlyTypeName()} in check-in, but the check-in state was null.");
            return(false);
        }
Ejemplo n.º 12
0
        /// <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>
        public override bool Execute(RockContext rockContext, Model.WorkflowAction action, object entity, out List <string> errorMessages)
        {
            var checkInState = GetCheckInState(entity, out errorMessages);

            if (checkInState == null)
            {
                return(false);
            }

            AttendanceCode attendanceCode = null;

            bool reuseCodeForFamily             = checkInState.CheckInType != null && checkInState.CheckInType.ReuseSameCode;
            int  securityCodeAlphaNumericLength = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeAlphaNumericLength : 3;
            int  securityCodeAlphaLength        = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeAlphaLength : 0;
            int  securityCodeNumericLength      = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeNumericLength : 0;
            bool securityCodeNumericRandom      = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeNumericRandom : true;

            bool enablePresence = checkInState.CheckInType != null && checkInState.CheckInType.EnablePresence;

            var attendanceCodeService = new AttendanceCodeService(rockContext);
            var attendanceService     = new AttendanceService(rockContext);
            var groupMemberService    = new GroupMemberService(rockContext);
            var personAliasService    = new PersonAliasService(rockContext);
            var attendanceRecords     = new List <Attendance>();

            AttendanceCheckInSession attendanceCheckInSession = new AttendanceCheckInSession()
            {
                DeviceId        = checkInState.DeviceId,
                ClientIpAddress = RockPage.GetClientIpAddress()
            };

            checkInState.Messages.Clear();

            var family = checkInState.CheckIn.CurrentFamily;

            if (family != null)
            {
                var currentOccurrences = new List <OccurrenceRecord>();
                foreach (var person in family.GetPeople(true))
                {
                    if (reuseCodeForFamily && attendanceCode != null)
                    {
                        person.SecurityCode = attendanceCode.Code;
                    }
                    else
                    {
                        attendanceCode      = AttendanceCodeService.GetNew(securityCodeAlphaNumericLength, securityCodeAlphaLength, securityCodeNumericLength, securityCodeNumericRandom);
                        person.SecurityCode = attendanceCode.Code;
                    }

                    foreach (var groupType in person.GetGroupTypes(true))
                    {
                        foreach (var group in groupType.GetGroups(true))
                        {
                            if (groupType.GroupType.AttendanceRule == AttendanceRule.AddOnCheckIn &&
                                groupType.GroupType.DefaultGroupRoleId.HasValue &&
                                !groupMemberService.GetByGroupIdAndPersonId(group.Group.Id, person.Person.Id, true).Any())
                            {
                                var groupMember = new GroupMember();
                                groupMember.GroupId     = group.Group.Id;
                                groupMember.PersonId    = person.Person.Id;
                                groupMember.GroupRoleId = groupType.GroupType.DefaultGroupRoleId.Value;
                                groupMemberService.Add(groupMember);
                            }

                            foreach (var location in group.GetLocations(true))
                            {
                                bool isCheckedIntoLocation = false;
                                foreach (var schedule in location.GetSchedules(true))
                                {
                                    var startDateTime = schedule.CampusCurrentDateTime;

                                    // If we're enforcing strict location thresholds, then before we create an attendance record
                                    // we need to check the location-schedule's current count.
                                    if (GetAttributeValue(action, AttributeKey.EnforceStrictLocationThreshold).AsBoolean() && location.Location.SoftRoomThreshold.HasValue)
                                    {
                                        EnforceStrictLocationThreshold(action, checkInState, attendanceService, currentOccurrences, person, group, location, schedule, startDateTime);
                                    }

                                    // Only create one attendance record per day for each person/schedule/group/location
                                    var attendance = attendanceService.Get(startDateTime, location.Location.Id, schedule.Schedule.Id, group.Group.Id, person.Person.Id);
                                    if (attendance == null)
                                    {
                                        var primaryAlias = personAliasService.GetPrimaryAlias(person.Person.Id);
                                        if (primaryAlias != null)
                                        {
                                            attendance = attendanceService.AddOrUpdate(
                                                primaryAlias.Id,
                                                startDateTime.Date,
                                                group.Group.Id,
                                                location.Location.Id,
                                                schedule.Schedule.Id,
                                                location.CampusId,
                                                checkInState.Kiosk.Device.Id,
                                                checkInState.CheckIn.SearchType?.Id,
                                                checkInState.CheckIn.SearchValue,
                                                family.Group.Id,
                                                attendanceCode.Id);

                                            attendance.PersonAlias = primaryAlias;
                                        }
                                    }

                                    attendance.AttendanceCheckInSession = attendanceCheckInSession;

                                    attendance.DeviceId                  = checkInState.Kiosk.Device.Id;
                                    attendance.SearchTypeValueId         = checkInState.CheckIn.SearchType?.Id;
                                    attendance.SearchValue               = checkInState.CheckIn.SearchValue;
                                    attendance.CheckedInByPersonAliasId  = checkInState.CheckIn.CheckedInByPersonAliasId;
                                    attendance.SearchResultGroupId       = family.Group.Id;
                                    attendance.AttendanceCodeId          = attendanceCode.Id;
                                    attendance.StartDateTime             = startDateTime;
                                    attendance.EndDateTime               = null;
                                    attendance.CheckedOutByPersonAliasId = null;
                                    attendance.DidAttend                 = true;
                                    attendance.Note        = group.Notes;
                                    attendance.IsFirstTime = person.FirstTime;

                                    /*
                                     *  7/16/2020 - JH
                                     *  If EnablePresence is true for this Check-in configuration, it will be the responsibility of the room
                                     *  attendants to mark a given Person as present, so do not set the 'Present..' property values below.
                                     *  Otherwise, set the values to match those of the Check-in values: the Person checking them in will
                                     *  have simultaneously marked them as present.
                                     *
                                     *  Also, note that we sometimes reuse Attendance records (i.e. the Person was already checked into this
                                     *  schedule/group/location, might have already been checked out, and also might have been previously
                                     *  marked as present). In this case, the same 'Present..' rules apply, but we might need to go so far
                                     *  as to null-out the previously set 'Present..' property values, hence the conditional operators below.
                                     */
                                    attendance.PresentDateTime        = enablePresence ? ( DateTime? )null : startDateTime;
                                    attendance.PresentByPersonAliasId = enablePresence ? null : checkInState.CheckIn.CheckedInByPersonAliasId;

                                    KioskLocationAttendance.AddAttendance(attendance);
                                    isCheckedIntoLocation = true;

                                    // Keep track of attendance (Ids) for use by other actions later in the workflow pipeline
                                    attendanceRecords.Add(attendance);
                                }

                                // If the person was NOT checked into the location for any schedule then remove the location
                                if (!isCheckedIntoLocation)
                                {
                                    group.Locations.Remove(location);
                                }
                            }
                        }
                    }
                }
            }

            if (checkInState.CheckInType.AchievementTypes.Any())
            {
                // Get any achievements that were in-progress *prior* to adding these attendance records
                var configuredAchievementTypeIds    = checkInState.CheckInType.AchievementTypes.Select(a => a.Id).ToList();
                var attendanceRecordsPersonAliasIds = attendanceRecords.Where(a => a.PersonAliasId.HasValue).Select(a => a.PersonAliasId.Value).ToArray();
                var successfullyCompletedAchievementsPriorToSaveChanges = GetSuccessfullyCompletedAchievementAttempts(rockContext, attendanceRecordsPersonAliasIds, configuredAchievementTypeIds);

                rockContext.SaveChanges();

                AchievementAttemptService.AchievementAttemptWithPersonAlias[] achievementAttemptsAfterSaveChanges =
                    GetAchievementAttemptsWithPersonAliasQuery(rockContext, attendanceRecordsPersonAliasIds, configuredAchievementTypeIds).AsNoTracking().ToArray();

                checkInState.CheckIn.SuccessfullyCompletedAchievementsPriorToCheckin = successfullyCompletedAchievementsPriorToSaveChanges;
                checkInState.CheckIn.AchievementsStateAfterCheckin = achievementAttemptsAfterSaveChanges;
            }
            else
            {
                rockContext.SaveChanges();
            }

            // Now that the records are persisted, take the Ids and save them to the temp CheckInFamliy object
            family.AttendanceIds = attendanceRecords.Select(a => a.Id).ToList();
            family.AttendanceCheckinSessionGuid = attendanceCheckInSession.Guid;
            attendanceRecords = null;

            return(true);
        }
Ejemplo n.º 13
0
        /// <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);
            }

            int  peopleWithoutAssignments = 0;
            bool roomBalance          = GetAttributeValue(action, "RoomBalance").AsBoolean();
            int  balanceOverride      = GetAttributeValue(action, "DifferentialOverride").AsIntegerOrNull() ?? 10;
            int  previousMonthsNumber = GetAttributeValue(action, "PreviousMonthsAttendance").AsIntegerOrNull() ?? 3;
            int  maxAssignments       = GetAttributeValue(action, "MaxAssignments").AsIntegerOrNull() ?? 5;

            var cutoffDate        = Rock.RockDateTime.Today.AddMonths(previousMonthsNumber * -1);
            var attendanceService = new AttendanceService(rockContext);

            var family = checkInState.CheckIn.Families.FirstOrDefault(f => f.Selected);

            if (family != null)
            {
                // get the number of people checking in, including visitors or first-timers
                peopleWithoutAssignments = family.People.Where(p => p.Selected).Count();

                foreach (var previousAttender in family.People.Where(p => p.Selected && !p.FirstTime))
                {
                    var personGroupTypeIds = previousAttender.GroupTypes.Select(gt => gt.GroupType.Id);

                    var lastDateAttendances = attendanceService.Queryable()
                                              .Where(a =>
                                                     a.PersonAlias.PersonId == previousAttender.Person.Id &&
                                                     personGroupTypeIds.Contains(a.Group.GroupTypeId) &&
                                                     a.StartDateTime >= cutoffDate && a.DidAttend == true)
                                              .OrderByDescending(a => a.StartDateTime).Take(maxAssignments)
                                              .ToList();

                    if (lastDateAttendances.Any())
                    {
                        bool createdMatchingAssignment = false;
                        var  isSpecialNeeds            = previousAttender.Person.GetAttributeValue("IsSpecialNeeds").AsBoolean();

                        var lastAttended = lastDateAttendances.Max(a => a.StartDateTime).Date;
                        foreach (var groupAttendance in lastDateAttendances.Where(a => a.StartDateTime >= lastAttended))
                        {
                            // Start with filtered groups unless they have abnormal age and grade parameters (1%)
                            var groupType = previousAttender.GroupTypes.FirstOrDefault(gt => gt.GroupType.Id == groupAttendance.Group.GroupTypeId && (!gt.ExcludedByFilter || isSpecialNeeds));
                            if (groupType != null)
                            {
                                CheckInGroup group = null;
                                if (groupType.Groups.Count == 1)
                                {
                                    // Only a single group is open
                                    group = groupType.Groups.FirstOrDefault(g => !g.ExcludedByFilter || isSpecialNeeds);
                                }
                                else
                                {
                                    // Pick the group they last attended
                                    group = groupType.Groups.FirstOrDefault(g => g.Group.Id == groupAttendance.GroupId && (!g.ExcludedByFilter || isSpecialNeeds));

                                    if (group != null && roomBalance && !isSpecialNeeds)
                                    {
                                        var currentAttendance   = group.Locations.Select(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount).Sum();
                                        var lowestAttendedGroup = groupType.Groups.Where(g => !g.ExcludedByFilter)
                                                                  .Select(g => new { Group = g, Attendance = g.Locations.Select(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount).Sum() })
                                                                  .OrderBy(g => g.Attendance)
                                                                  .FirstOrDefault();

                                        if (lowestAttendedGroup != null && lowestAttendedGroup.Attendance < (currentAttendance - balanceOverride))
                                        {
                                            group = lowestAttendedGroup.Group;
                                        }
                                    }
                                }

                                if (group != null)
                                {
                                    CheckInLocation location = null;
                                    if (group.Locations.Count == 1)
                                    {
                                        // Only a single location is open
                                        location = group.Locations.FirstOrDefault(l => !l.ExcludedByFilter || isSpecialNeeds);
                                    }
                                    else
                                    {
                                        // Pick the location they last attended
                                        location = group.Locations.FirstOrDefault(l => l.Location.Id == groupAttendance.LocationId && (!l.ExcludedByFilter || isSpecialNeeds));

                                        if (location != null && roomBalance && !isSpecialNeeds)
                                        {
                                            var currentAttendance      = KioskLocationAttendance.Read(location.Location.Id).CurrentCount;
                                            var lowestAttendedLocation = group.Locations.Where(l => !l.ExcludedByFilter)
                                                                         .Select(l => new { Location = l, Attendance = KioskLocationAttendance.Read(location.Location.Id).CurrentCount })
                                                                         .OrderBy(l => l.Attendance)
                                                                         .FirstOrDefault();

                                            if (lowestAttendedLocation != null && lowestAttendedLocation.Attendance < (currentAttendance - balanceOverride))
                                            {
                                                location = lowestAttendedLocation.Location;
                                            }
                                        }
                                    }

                                    if (location != null)
                                    {
                                        CheckInSchedule schedule = null;
                                        if (location.Schedules.Count == 1)
                                        {
                                            schedule = location.Schedules.FirstOrDefault(s => !s.ExcludedByFilter || isSpecialNeeds);
                                        }
                                        else if (groupAttendance.ScheduleId != null)
                                        {
                                            schedule = location.Schedules.FirstOrDefault(s => s.Schedule.Id == groupAttendance.ScheduleId && (!s.ExcludedByFilter || isSpecialNeeds));
                                        }
                                        else
                                        {
                                            // if the schedule doesn't exactly match but everything else does, select it
                                            schedule = location.Schedules.FirstOrDefault(s => (!s.ExcludedByFilter && !isSpecialNeeds));
                                        }

                                        if (schedule != null)
                                        {
                                            schedule.Selected            = true;
                                            schedule.PreSelected         = true;
                                            schedule.LastCheckIn         = groupAttendance.StartDateTime;
                                            location.Selected            = true;
                                            location.PreSelected         = true;
                                            location.LastCheckIn         = groupAttendance.StartDateTime;
                                            group.Selected               = true;
                                            group.PreSelected            = true;
                                            group.LastCheckIn            = groupAttendance.StartDateTime;
                                            groupType.Selected           = true;
                                            groupType.PreSelected        = true;
                                            group.LastCheckIn            = groupAttendance.StartDateTime;
                                            groupType.LastCheckIn        = groupAttendance.StartDateTime;
                                            groupType.Selected           = true;
                                            groupType.PreSelected        = true;
                                            previousAttender.PreSelected = true;
                                            previousAttender.LastCheckIn = groupAttendance.StartDateTime;
                                            createdMatchingAssignment    = true;
                                        }
                                    }
                                }
                            }
                        }

                        if (createdMatchingAssignment)
                        {
                            peopleWithoutAssignments--;
                        }
                    }
                }
            }

            // true condition will continue to the next auto-assignment
            // false condition will stop processing auto-assignments
            if (action.Activity.AttributeValues.Any() && action.Activity.AttributeValues.ContainsKey("ContinueAssignments"))
            {
                var continueAssignments = peopleWithoutAssignments > 0;
                action.Activity.AttributeValues["ContinueAssignments"].Value = continueAssignments.ToString();
            }

            return(true);
        }
Ejemplo n.º 14
0
        /// <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);
            }

            bool roomBalance        = GetAttributeValue(action, "RoomBalance").AsBoolean();
            bool useGroupMembership = GetAttributeValue(action, "PrioritizeGroupMembership").AsBoolean();
            int  balanceOverride    = GetAttributeValue(action, "DifferentialOverride").AsIntegerOrNull() ?? 10;

            var family = checkInState.CheckIn.Families.FirstOrDefault(f => f.Selected);

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

                    // variable must be a string to compare to group attribute value
                    var specialNeedsValue = person.Person.GetAttributeValue("IsSpecialNeeds").ToStringSafe();
                    var isSpecialNeeds    = specialNeedsValue.AsBoolean();

                    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("AgeRange") &&
                                                                      g.Group.AttributeValues["AgeRange"].Value != null &&
                                                                      g.Group.AttributeValues.ContainsKey("IsSpecialNeeds") == isSpecialNeeds
                                                                      )
                                                    .ToList()
                                                    .Select(g => new
                                    {
                                        Group    = g,
                                        AgeRange = g.Group.AttributeValues["AgeRange"].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 (isSpecialNeeds)
                                                    {
                                                        closestNeedsGroup = closestAgeGroup;
                                                    }
                                                }
                                            }
                                        }
                                        else if (isSpecialNeeds)
                                        {
                                            // special needs was checked but no 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("GradeRange") && g.Group.AttributeValues["GradeRange"].Value != null)
                                                          .ToList()
                                                          .Select(g => new
                                        {
                                            Group        = g,
                                            GradeOffsets = g.Group.AttributeValues["GradeRange"].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: Ability, then Grade, then Age, then 1st available
                                    bestGroup = closestNeedsGroup ?? closestGradeGroup ?? closestAgeGroup ?? validGroups.FirstOrDefault(g => !g.ExcludedByFilter);

                                    // room balance if they fit into multiple groups
                                    if (bestGroup != null && roomBalance)
                                    {
                                        var currentGroupAttendance = bestGroup.Locations.Select(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount).Sum();
                                        var lowestGroup            = validGroups.Where(g => !g.ExcludedByFilter)
                                                                     .Select(g => new { Group = g, Attendance = g.Locations.Select(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount).Sum() })
                                                                     .OrderBy(g => g.Attendance)
                                                                     .FirstOrDefault();

                                        if (lowestGroup != null && lowestGroup.Attendance < (currentGroupAttendance - balanceOverride))
                                        {
                                            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 orderedLocations = validLocations.Where(l => !l.ExcludedByFilter && l.Schedules.Any(s => s.Schedule.IsCheckInActive));

                                    if (roomBalance)
                                    {
                                        orderedLocations = orderedLocations.OrderBy(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount);
                                    }

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

                                // check how many schedules exist without getting the whole list
                                int numValidSchedules = validSchedules.Take(2).Count();
                                if (numValidSchedules > 0)
                                {
                                    var bestSchedule = validSchedules.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.PreSelected        = true;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(true);
        }
Ejemplo n.º 15
0
        /// <summary>
        /// Handles the Click event of the btnManager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void btnManager_Click(object sender, EventArgs e)
        {
            ManagerLoggedIn         = false;
            pnlNotActive.Visible    = false;
            pnlNotActiveYet.Visible = false;
            pnlClosed.Visible       = false;
            pnlActive.Visible       = false;
            pnlManager.Visible      = false;
            btnManager.Visible      = false;

            tbPIN.Text = string.Empty;

            // Get room counts
            List <int> locations = new List <int>();

            foreach (var groupType in CurrentCheckInState.Kiosk.FilteredGroupTypes(CurrentCheckInState.ConfiguredGroupTypes))
            {
                var lUl = new HtmlGenericControl("ul");
                lUl.AddCssClass("kioskmanager-count-locations");
                phCounts.Controls.Add(lUl);

                foreach (var location in groupType.KioskGroups.SelectMany(g => g.KioskLocations).OrderBy(l => l.Location.Name).Distinct())
                {
                    if (!locations.Contains(location.Location.Id))
                    {
                        locations.Add(location.Location.Id);
                        var locationAttendance = KioskLocationAttendance.Get(location.Location.Id);

                        if (locationAttendance != null)
                        {
                            var lLi = new HtmlGenericControl("li");
                            lUl.Controls.Add(lLi);
                            lLi.InnerHtml = string.Format("<strong>{0}</strong>: {1}", locationAttendance.LocationName, locationAttendance.CurrentCount);

                            var gUl = new HtmlGenericControl("ul");
                            gUl.AddCssClass("kioskmanager-count-groups");
                            lLi.Controls.Add(gUl);

                            foreach (var groupAttendance in locationAttendance.Groups)
                            {
                                var gLi = new HtmlGenericControl("li");
                                gUl.Controls.Add(gLi);
                                gLi.InnerHtml = string.Format("<strong>{0}</strong>: {1}", groupAttendance.GroupName, groupAttendance.CurrentCount);

                                var sUl = new HtmlGenericControl("ul");
                                sUl.AddCssClass("kioskmanager-count-schedules");
                                gLi.Controls.Add(sUl);

                                foreach (var scheduleAttendance in groupAttendance.Schedules.Where(s => s.IsActive))
                                {
                                    var sLi = new HtmlGenericControl("li");
                                    sUl.Controls.Add(sLi);
                                    sLi.InnerHtml = string.Format("<strong>{0}</strong>: {1}", scheduleAttendance.ScheduleName, scheduleAttendance.CurrentCount);
                                }
                            }
                        }
                    }
                }
            }

            pnlManagerLogin.Visible = true;

            // set manager timer to 10 minutes
            hfRefreshTimerSeconds.Value = "600";
        }
        /// <summary>
        /// Formats the count.
        /// </summary>
        /// <param name="locationId">The location id.</param>
        /// <returns></returns>
        protected string FormatCount(int locationId)
        {
            if (CurrentCheckInType != null && CurrentCheckInType.DisplayLocationCount)
            {
                return(string.Format(" <span class='checkin-sub-title'> Count: {0}</span>", KioskLocationAttendance.Read(locationId).CurrentCount));
            }

            return(string.Empty);
        }
Ejemplo n.º 17
0
        private void RefreshView()
        {
            pnlNotActive.Visible    = false;
            pnlNotActiveYet.Visible = false;
            pnlClosed.Visible       = false;
            pnlActive.Visible       = false;

            lblActiveWhen.Text = string.Empty;

            if (CurrentCheckInState == null || IsMobileAndExpiredDevice())
            {
                NavigateToPreviousPage();
                return;
            }

            if (CurrentCheckInState.Kiosk.FilteredGroupTypes(CurrentCheckInState.ConfiguredGroupTypes).Count == 0)
            {
                pnlNotActive.Visible = true;
            }
            else if (!CurrentCheckInState.Kiosk.HasLocations(CurrentCheckInState.ConfiguredGroupTypes))
            {
                DateTimeOffset activeAt = CurrentCheckInState.Kiosk.FilteredGroupTypes(CurrentCheckInState.ConfiguredGroupTypes).Select(g => g.NextActiveTime).Min();
                lblActiveWhen.Text      = activeAt.ToString();
                pnlNotActiveYet.Visible = true;
            }
            else if (!CurrentCheckInState.Kiosk.HasActiveLocations(CurrentCheckInState.ConfiguredGroupTypes))
            {
                pnlClosed.Visible = true;
            }
            else
            {
                pnlActive.Visible = true;
            }

            List <int> locations = new List <int>();

            foreach (var groupType in CurrentCheckInState.Kiosk.FilteredGroupTypes(CurrentCheckInState.ConfiguredGroupTypes))
            {
                foreach (var location in groupType.KioskGroups.SelectMany(g => g.KioskLocations).Distinct())
                {
                    if (!locations.Contains(location.Location.Id))
                    {
                        locations.Add(location.Location.Id);
                        var locationAttendance = KioskLocationAttendance.Read(location.Location.Id);

                        if (locationAttendance != null)
                        {
                            var lUl = new HtmlGenericControl("ul");
                            lUl.AddCssClass("checkin-count-locations");
                            phCounts.Controls.Add(lUl);

                            var lLi = new HtmlGenericControl("li");
                            lUl.Controls.Add(lLi);
                            lLi.InnerHtml = string.Format("{0}: <strong>{1}</strong>", locationAttendance.LocationName, locationAttendance.CurrentCount);

                            foreach (var groupAttendance in locationAttendance.Groups)
                            {
                                var gUl = new HtmlGenericControl("ul");
                                gUl.AddCssClass("checkin-count-groups");
                                lLi.Controls.Add(gUl);

                                var gLi = new HtmlGenericControl("li");
                                gUl.Controls.Add(gLi);
                                gLi.InnerHtml = string.Format("{0}: <strong>{1}</strong>", groupAttendance.GroupName, groupAttendance.CurrentCount);

                                foreach (var scheduleAttendance in groupAttendance.Schedules)
                                {
                                    var sUl = new HtmlGenericControl("ul");
                                    sUl.AddCssClass("checkin-count-schedules");
                                    gLi.Controls.Add(sUl);

                                    var sLi = new HtmlGenericControl("li");
                                    sUl.Controls.Add(sLi);
                                    sLi.InnerHtml = string.Format("{0}: <strong>{1}</strong>", scheduleAttendance.ScheduleName, scheduleAttendance.CurrentCount);
                                }
                            }
                        }
                    }
                }
            }
        }
Ejemplo n.º 18
0
        /// <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, Model.WorkflowAction action, Object entity, out List <string> errorMessages)
        {
            var checkInState = GetCheckInState(entity, out errorMessages);

            if (checkInState != null)
            {
                AttendanceCode attendanceCode = null;

                bool reuseCodeForFamily             = checkInState.CheckInType != null && checkInState.CheckInType.ReuseSameCode;
                int  securityCodeAlphaNumericLength = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeAlphaNumericLength : 3;
                int  securityCodeAlphaLength        = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeAlphaLength : 0;
                int  securityCodeNumericLength      = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeNumericLength : 0;
                bool securityCodeNumericRandom      = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeNumericRandom : true;


                var attendanceCodeService = new AttendanceCodeService(rockContext);
                var attendanceService     = new AttendanceService(rockContext);
                var groupMemberService    = new GroupMemberService(rockContext);
                var personAliasService    = new PersonAliasService(rockContext);
                var attendanceRecords     = new List <Attendance>();

                checkInState.Messages.Clear();

                var family = checkInState.CheckIn.CurrentFamily;
                if (family != null)
                {
                    var currentOccurences = new List <OccurenceRecord>();
                    foreach (var person in family.GetPeople(true))
                    {
                        if (reuseCodeForFamily && attendanceCode != null)
                        {
                            person.SecurityCode = attendanceCode.Code;
                        }
                        else
                        {
                            attendanceCode      = AttendanceCodeService.GetNew(securityCodeAlphaNumericLength, securityCodeAlphaLength, securityCodeNumericLength, securityCodeNumericRandom);
                            person.SecurityCode = attendanceCode.Code;
                        }

                        foreach (var groupType in person.GetGroupTypes(true))
                        {
                            foreach (var group in groupType.GetGroups(true))
                            {
                                if (groupType.GroupType.AttendanceRule == AttendanceRule.AddOnCheckIn &&
                                    groupType.GroupType.DefaultGroupRoleId.HasValue &&
                                    !groupMemberService.GetByGroupIdAndPersonId(group.Group.Id, person.Person.Id, true).Any())
                                {
                                    var groupMember = new GroupMember();
                                    groupMember.GroupId     = group.Group.Id;
                                    groupMember.PersonId    = person.Person.Id;
                                    groupMember.GroupRoleId = groupType.GroupType.DefaultGroupRoleId.Value;
                                    groupMemberService.Add(groupMember);
                                }

                                foreach (var location in group.GetLocations(true))
                                {
                                    bool isCheckedIntoLocation = false;
                                    foreach (var schedule in location.GetSchedules(true))
                                    {
                                        var startDateTime = schedule.CampusCurrentDateTime;

                                        // If we're enforcing strict location thresholds, then before we create an attendance record
                                        // we need to check the location-schedule's current count.
                                        if (GetAttributeValue(action, "EnforceStrictLocationThreshold").AsBoolean() && location.Location.SoftRoomThreshold.HasValue)
                                        {
                                            var thresHold = location.Location.SoftRoomThreshold.Value;
                                            if (checkInState.ManagerLoggedIn && location.Location.FirmRoomThreshold.HasValue && location.Location.FirmRoomThreshold.Value > location.Location.SoftRoomThreshold.Value)
                                            {
                                                thresHold = location.Location.FirmRoomThreshold.Value;
                                            }

                                            var currentOccurence = GetCurrentOccurence(currentOccurences, location, schedule, startDateTime.Date);

                                            // The totalAttended is the number of people still checked in (not people who have been checked-out)
                                            // not counting the current person who may already be checked in,
                                            // + the number of people we have checked in so far (but haven't been saved yet).
                                            var attendanceQry = attendanceService.GetByDateOnLocationAndSchedule(startDateTime.Date, location.Location.Id, schedule.Schedule.Id)
                                                                .AsNoTracking()
                                                                .Where(a => a.EndDateTime == null);

                                            // Only process if the current person is NOT already checked-in to this location and schedule
                                            if (!attendanceQry.Where(a => a.PersonAlias.PersonId == person.Person.Id).Any())
                                            {
                                                var totalAttended = attendanceQry.Count() + (currentOccurence == null ? 0 : currentOccurence.Count);

                                                // If over capacity, remove the schedule and add a warning message.
                                                if (totalAttended >= thresHold)
                                                {
                                                    // Remove the schedule since the location was full for this schedule.
                                                    location.Schedules.Remove(schedule);

                                                    var message = new CheckInMessage()
                                                    {
                                                        MessageType = MessageType.Warning
                                                    };

                                                    var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null);
                                                    mergeFields.Add("Person", person.Person);
                                                    mergeFields.Add("Group", group.Group);
                                                    mergeFields.Add("Location", location.Location);
                                                    mergeFields.Add("Schedule", schedule.Schedule);
                                                    message.MessageText = GetAttributeValue(action, "NotChecked-InMessageFormat").ResolveMergeFields(mergeFields);

                                                    // Now add it to the check-in state message list for others to see.
                                                    checkInState.Messages.Add(message);
                                                    continue;
                                                }
                                                else
                                                {
                                                    // Keep track of anyone who was checked in so far.
                                                    if (currentOccurence == null)
                                                    {
                                                        currentOccurence = new OccurenceRecord()
                                                        {
                                                            Date       = startDateTime.Date,
                                                            LocationId = location.Location.Id,
                                                            ScheduleId = schedule.Schedule.Id
                                                        };
                                                        currentOccurences.Add(currentOccurence);
                                                    }

                                                    currentOccurence.Count += 1;
                                                }
                                            }
                                        }

                                        // Only create one attendance record per day for each person/schedule/group/location
                                        var attendance = attendanceService.Get(startDateTime, location.Location.Id, schedule.Schedule.Id, group.Group.Id, person.Person.Id);
                                        if (attendance == null)
                                        {
                                            var primaryAlias = personAliasService.GetPrimaryAlias(person.Person.Id);
                                            if (primaryAlias != null)
                                            {
                                                attendance = attendanceService.AddOrUpdate(primaryAlias.Id, startDateTime.Date, group.Group.Id,
                                                                                           location.Location.Id, schedule.Schedule.Id, location.CampusId,
                                                                                           checkInState.Kiosk.Device.Id, checkInState.CheckIn.SearchType.Id,
                                                                                           checkInState.CheckIn.SearchValue, family.Group.Id, attendanceCode.Id);

                                                attendance.PersonAlias = primaryAlias;
                                            }
                                        }

                                        attendance.DeviceId                 = checkInState.Kiosk.Device.Id;
                                        attendance.SearchTypeValueId        = checkInState.CheckIn.SearchType.Id;
                                        attendance.SearchValue              = checkInState.CheckIn.SearchValue;
                                        attendance.CheckedInByPersonAliasId = checkInState.CheckIn.CheckedInByPersonAliasId;
                                        attendance.SearchResultGroupId      = family.Group.Id;
                                        attendance.AttendanceCodeId         = attendanceCode.Id;
                                        attendance.StartDateTime            = startDateTime;
                                        attendance.EndDateTime              = null;
                                        attendance.DidAttend                = true;
                                        attendance.Note = group.Notes;

                                        KioskLocationAttendance.AddAttendance(attendance);
                                        isCheckedIntoLocation = true;

                                        // Keep track of attendance (Ids) for use by other actions later in the workflow pipeline
                                        attendanceRecords.Add(attendance);
                                    }

                                    // If the person was NOT checked into the location for any schedule then remove the location
                                    if (!isCheckedIntoLocation)
                                    {
                                        group.Locations.Remove(location);
                                    }
                                }
                            }
                        }
                    }
                }

                rockContext.SaveChanges();

                // Now that the records are persisted, take the Ids and save them to the temp CheckInFamliy object
                family.AttendanceIds = attendanceRecords.Select(a => a.Id).ToList();
                attendanceRecords    = null;

                return(true);
            }

            return(false);
        }
Ejemplo n.º 19
0
        /// <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);
            }

            int selectFromDaysBack    = checkInState.CheckInType.AutoSelectDaysBack;
            var roomBalanceGroupTypes = GetAttributeValue(action, "RoomBalanceGrouptypes").SplitDelimitedValues().AsGuidList();
            int roomBalanceOverride   = GetAttributeValue(action, "BalancingOverride").AsIntegerOrNull() ?? 5;
            int maxAssignments        = GetAttributeValue(action, "MaxAssignments").AsIntegerOrNull() ?? 5;
            var excludedLocations     = GetAttributeValue(action, "ExcludedLocations").SplitDelimitedValues(whitespace: false)
                                        .Select(s => s.Trim());

            // get the admin-selected attribute key 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;
            }

            // log a warning if the attribute is 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));
            }

            var family = checkInState.CheckIn.Families.FirstOrDefault(f => f.Selected);

            if (family != null)
            {
                var cutoffDate        = RockDateTime.Today.AddDays(selectFromDaysBack * -1);
                var attendanceService = new AttendanceService(rockContext);

                // only process people who have been here before
                foreach (var previousAttender in family.People.Where(p => p.Selected && !p.FirstTime))
                {
                    // get a list of this person's available grouptypes
                    var availableGroupTypeIds = previousAttender.GroupTypes.Select(gt => gt.GroupType.Id).ToList();

                    var lastDateAttendances = attendanceService.Queryable().Where(a =>
                                                                                  a.PersonAlias.PersonId == previousAttender.Person.Id &&
                                                                                  availableGroupTypeIds.Contains(a.Group.GroupTypeId) &&
                                                                                  a.StartDateTime >= cutoffDate && a.DidAttend == true)
                                              .OrderByDescending(a => a.StartDateTime).Take(maxAssignments)
                                              .ToList();

                    if (lastDateAttendances.Any())
                    {
                        var lastAttended   = lastDateAttendances.Max(a => a.StartDateTime).Date;
                        var numAttendances = lastDateAttendances.Count(a => a.StartDateTime >= lastAttended);
                        foreach (var groupAttendance in lastDateAttendances.Where(a => a.StartDateTime >= lastAttended))
                        {
                            bool currentlyCheckedIn = false;
                            var  serviceCutoff      = groupAttendance.StartDateTime;
                            if (serviceCutoff > RockDateTime.Now.Date)
                            {
                                // calculate the service window to determine if people are still checked in
                                var serviceTime  = groupAttendance.StartDateTime.Date + groupAttendance.Schedule.NextStartDateTime.Value.TimeOfDay;
                                var serviceStart = serviceTime.AddMinutes((groupAttendance.Schedule.CheckInStartOffsetMinutes ?? 0) * -1.0);
                                serviceCutoff      = serviceTime.AddMinutes((groupAttendance.Schedule.CheckInEndOffsetMinutes ?? 0));
                                currentlyCheckedIn = RockDateTime.Now > serviceStart && RockDateTime.Now < serviceCutoff;
                            }

                            // override exists in case they are currently checked in or have special needs
                            bool useCheckinOverride = currentlyCheckedIn || previousAttender.Person.GetAttributeValue(personSpecialNeedsKey).AsBoolean();

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

                            // Start with filtered groups unless they have abnormal age and grade parameters (1%)
                            var groupType = previousAttender.GroupTypes.FirstOrDefault(gt => gt.GroupType.Id == groupAttendance.Group.GroupTypeId && (!gt.ExcludedByFilter || useCheckinOverride));
                            if (groupType != null)
                            {
                                CheckInGroup group = null;
                                if (groupType.Groups.Count == 1)
                                {
                                    // Only a single group is open
                                    group = groupType.Groups.FirstOrDefault(g => !g.ExcludedByFilter || useCheckinOverride);
                                }
                                else
                                {
                                    // Pick the group they last attended, as long as it's open or what they're currently checked into
                                    group = groupType.Groups.FirstOrDefault(g => g.Group.Id == groupAttendance.GroupId && (!g.ExcludedByFilter || useCheckinOverride));

                                    // room balance only on new check-ins
                                    if (group != null && roomBalanceGroupTypeIds.Contains(group.Group.GroupTypeId) && !useCheckinOverride)
                                    {
                                        //TODO: use KioskLocationAttendance and group.AvailableForSchedule to room balance by service time attendance, not the entire day
                                        var currentAttendance   = group.Locations.Select(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount).Sum();
                                        var lowestAttendedGroup = groupType.Groups.Where(g => !g.ExcludedByFilter && !excludedLocations.Contains(g.Group.Name))
                                                                  .Select(g => new { Group = g, Attendance = g.Locations.Select(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount).Sum() })
                                                                  .OrderBy(g => g.Attendance)
                                                                  .FirstOrDefault();

                                        if (lowestAttendedGroup != null && lowestAttendedGroup.Attendance < (currentAttendance - roomBalanceOverride + 1))
                                        {
                                            group = lowestAttendedGroup.Group;
                                        }
                                    }
                                }

                                if (group != null)
                                {
                                    CheckInLocation location = null;
                                    if (group.Locations.Count == 1)
                                    {
                                        // Only a single location is open
                                        location = group.Locations.FirstOrDefault(l => !l.ExcludedByFilter || useCheckinOverride);
                                    }
                                    else
                                    {
                                        // Pick the location they last attended, as long as it's open or what they're currently checked into
                                        location = group.Locations.FirstOrDefault(l => l.Location.Id == groupAttendance.LocationId && (!l.ExcludedByFilter || useCheckinOverride));

                                        // room balance only on new check-ins
                                        if (location != null && roomBalanceGroupTypeIds.Contains(group.Group.GroupTypeId) && !useCheckinOverride)
                                        {
                                            //TODO: use KioskLocationAttendance and location.AvailableForSchedule to room balance by service time attendance, not the entire day
                                            var currentAttendance      = KioskLocationAttendance.Read(location.Location.Id).CurrentCount;
                                            var lowestAttendedLocation = group.Locations.Where(l => !l.ExcludedByFilter && !excludedLocations.Contains(l.Location.Name))
                                                                         .Select(l => new { Location = l, Attendance = KioskLocationAttendance.Read(l.Location.Id).CurrentCount })
                                                                         .OrderBy(l => l.Attendance)
                                                                         .FirstOrDefault();

                                            if (lowestAttendedLocation != null && lowestAttendedLocation.Attendance < (currentAttendance - roomBalanceOverride + 1))
                                            {
                                                location = lowestAttendedLocation.Location;
                                            }
                                        }
                                    }

                                    if (location != null)
                                    {
                                        CheckInSchedule schedule = null;
                                        if (location.Schedules.Count == 1)
                                        {
                                            schedule = location.Schedules.FirstOrDefault(s => !s.ExcludedByFilter || useCheckinOverride);
                                        }
                                        else
                                        {
                                            // if assigning to multiple services or currently checked in (not SN, otherwise they would get the wrong auto-schedule)
                                            if (numAttendances > 1 || currentlyCheckedIn)
                                            {
                                                // pick what they last attended last
                                                schedule = location.Schedules.FirstOrDefault(s => s.Schedule.Id == groupAttendance.ScheduleId && (!s.ExcludedByFilter || useCheckinOverride));
                                            }

                                            // otherwise pick the earliest available schedule
                                            schedule = schedule ?? location.Schedules.OrderBy(s => s.Schedule.StartTimeOfDay).FirstOrDefault(s => !s.ExcludedByFilter);
                                        }

                                        if (schedule != null)
                                        {
                                            // it's impossible to currently be checked in unless these match exactly
                                            if (group.Group.Id == groupAttendance.GroupId && location.Location.Id == groupAttendance.LocationId && schedule.Schedule.Id == groupAttendance.ScheduleId)
                                            {
                                                // Checkout would've removed the attendance or set the EndDateTime
                                                var endOfCheckinWindow = groupAttendance.EndDateTime ?? serviceCutoff;
                                                schedule.LastCheckIn         = endOfCheckinWindow;
                                                location.LastCheckIn         = endOfCheckinWindow;
                                                group.LastCheckIn            = endOfCheckinWindow;
                                                groupType.LastCheckIn        = endOfCheckinWindow;
                                                previousAttender.LastCheckIn = endOfCheckinWindow;
                                            }

                                            // finished finding assignment, verify everything is selected
                                            schedule.Selected            = true;
                                            schedule.PreSelected         = true;
                                            location.Selected            = true;
                                            location.PreSelected         = true;
                                            group.Selected               = true;
                                            group.PreSelected            = true;
                                            groupType.Selected           = true;
                                            groupType.PreSelected        = true;
                                            groupType.Selected           = true;
                                            groupType.PreSelected        = true;
                                            previousAttender.Selected    = true;
                                            previousAttender.PreSelected = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(true);
        }
Ejemplo n.º 20
0
        /// <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, Model.WorkflowAction action, Object entity, out List <string> errorMessages)
        {
            var checkInState = GetCheckInState(entity, out errorMessages);

            if (checkInState != null)
            {
                AttendanceCode attendanceCode = null;

                bool reuseCodeForFamily             = checkInState.CheckInType != null && checkInState.CheckInType.ReuseSameCode;
                int  securityCodeAlphaNumericLength = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeAlphaNumericLength : 3;
                int  securityCodeAlphaLength        = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeAlphaLength : 0;
                int  securityCodeNumericLength      = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeNumericLength : 0;
                bool securityCodeNumericRandom      = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeNumericRandom : true;


                var attendanceCodeService = new AttendanceCodeService(rockContext);
                var attendanceService     = new AttendanceService(rockContext);
                var groupMemberService    = new GroupMemberService(rockContext);
                var personAliasService    = new PersonAliasService(rockContext);


                var family = checkInState.CheckIn.CurrentFamily;
                if (family != null)
                {
                    foreach (var person in family.GetPeople(true))
                    {
                        if (reuseCodeForFamily && attendanceCode != null)
                        {
                            person.SecurityCode = attendanceCode.Code;
                        }
                        else
                        {
                            attendanceCode      = AttendanceCodeService.GetNew(securityCodeAlphaNumericLength, securityCodeAlphaLength, securityCodeNumericLength, securityCodeNumericRandom);
                            person.SecurityCode = attendanceCode.Code;
                        }

                        foreach (var groupType in person.GetGroupTypes(true))
                        {
                            foreach (var group in groupType.GetGroups(true))
                            {
                                if (groupType.GroupType.AttendanceRule == AttendanceRule.AddOnCheckIn &&
                                    groupType.GroupType.DefaultGroupRoleId.HasValue &&
                                    !groupMemberService.GetByGroupIdAndPersonId(group.Group.Id, person.Person.Id, true).Any())
                                {
                                    var groupMember = new GroupMember();
                                    groupMember.GroupId     = group.Group.Id;
                                    groupMember.PersonId    = person.Person.Id;
                                    groupMember.GroupRoleId = groupType.GroupType.DefaultGroupRoleId.Value;
                                    groupMemberService.Add(groupMember);
                                }

                                foreach (var location in group.GetLocations(true))
                                {
                                    foreach (var schedule in location.GetSchedules(true))
                                    {
                                        var startDateTime = schedule.CampusCurrentDateTime;

                                        // Only create one attendance record per day for each person/schedule/group/location
                                        var attendance = attendanceService.Get(startDateTime, location.Location.Id, schedule.Schedule.Id, group.Group.Id, person.Person.Id);
                                        if (attendance == null)
                                        {
                                            var primaryAlias = personAliasService.GetPrimaryAlias(person.Person.Id);
                                            if (primaryAlias != null)
                                            {
                                                attendance = attendanceService.AddOrUpdate(primaryAlias.Id, startDateTime.Date, group.Group.Id,
                                                                                           location.Location.Id, schedule.Schedule.Id, location.CampusId,
                                                                                           checkInState.Kiosk.Device.Id, checkInState.CheckIn.SearchType.Id,
                                                                                           checkInState.CheckIn.SearchValue, family.Group.Id, attendanceCode.Id);

                                                attendance.PersonAlias = primaryAlias;
                                            }
                                        }

                                        attendance.DeviceId                 = checkInState.Kiosk.Device.Id;
                                        attendance.SearchTypeValueId        = checkInState.CheckIn.SearchType.Id;
                                        attendance.SearchValue              = checkInState.CheckIn.SearchValue;
                                        attendance.CheckedInByPersonAliasId = checkInState.CheckIn.CheckedInByPersonAliasId;
                                        attendance.SearchResultGroupId      = family.Group.Id;
                                        attendance.AttendanceCodeId         = attendanceCode.Id;
                                        attendance.StartDateTime            = startDateTime;
                                        attendance.EndDateTime              = null;
                                        attendance.DidAttend                = true;
                                        attendance.Note = group.Notes;

                                        KioskLocationAttendance.AddAttendance(attendance);
                                    }
                                }
                            }
                        }
                    }
                }

                rockContext.SaveChanges();
                return(true);
            }

            return(false);
        }
Ejemplo n.º 21
0
        /// <summary>
        /// Executes the specified workflow.
        /// </summary>
        /// <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(Model.WorkflowAction action, Object entity, out List <string> errorMessages)
        {
            var checkInState = GetCheckInState(entity, out errorMessages);

            if (checkInState != null)
            {
                DateTime startDateTime = DateTime.Now;

                int securityCodeLength = 3;
                if (!int.TryParse(GetAttributeValue(action, "SecurityCodeLength"), out securityCodeLength))
                {
                    securityCodeLength = 3;
                }

                using (var uow = new Rock.Data.UnitOfWorkScope())
                {
                    var attendanceCodeService = new AttendanceCodeService();
                    var attendanceService     = new AttendanceService();
                    var groupMemberService    = new GroupMemberService();

                    foreach (var family in checkInState.CheckIn.Families.Where(f => f.Selected))
                    {
                        foreach (var person in family.People.Where(p => p.Selected))
                        {
                            var attendanceCode = attendanceCodeService.GetNew(securityCodeLength);
                            person.SecurityCode = attendanceCode.Code;

                            foreach (var groupType in person.GroupTypes.Where(g => g.Selected))
                            {
                                foreach (var group in groupType.Groups.Where(g => g.Selected))
                                {
                                    foreach (var location in group.Locations.Where(l => l.Selected))
                                    {
                                        if (groupType.GroupType.AttendanceRule == AttendanceRule.AddOnCheckIn &&
                                            groupType.GroupType.DefaultGroupRoleId.HasValue &&
                                            !groupMemberService.GetByGroupIdAndPersonId(group.Group.Id, person.Person.Id, true).Any())
                                        {
                                            var groupMember = new GroupMember();
                                            groupMember.GroupId     = group.Group.Id;
                                            groupMember.PersonId    = person.Person.Id;
                                            groupMember.GroupRoleId = groupType.GroupType.DefaultGroupRoleId.Value;
                                            groupMemberService.Add(groupMember, null);
                                            groupMemberService.Save(groupMember, null);
                                        }

                                        foreach (var schedule in location.Schedules.Where(s => s.Selected))
                                        {
                                            // Only create one attendance record per day for each person/schedule/group/location
                                            var attendance = attendanceService.Get(startDateTime, location.Location.Id, schedule.Schedule.Id, group.Group.Id, person.Person.Id);
                                            if (attendance == null)
                                            {
                                                attendance                   = ((Rock.Data.RockContext)uow.DbContext).Attendances.Create();
                                                attendance.LocationId        = location.Location.Id;
                                                attendance.ScheduleId        = schedule.Schedule.Id;
                                                attendance.GroupId           = group.Group.Id;
                                                attendance.PersonId          = person.Person.Id;
                                                attendance.DeviceId          = checkInState.Kiosk.Device.Id;
                                                attendance.SearchTypeValueId = checkInState.CheckIn.SearchType.Id;
                                                attendanceService.Add(attendance, null);
                                            }

                                            attendance.AttendanceCodeId = attendanceCode.Id;
                                            attendance.StartDateTime    = startDateTime;
                                            attendance.EndDateTime      = null;
                                            attendance.DidAttend        = true;
                                            attendanceService.Save(attendance, null);

                                            KioskLocationAttendance.AddAttendance(attendance);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                return(true);
            }

            return(false);
        }
Ejemplo n.º 22
0
        /// <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))
                                    {
                                        var currentGroupAttendance = bestGroup.Locations.Select(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount).Sum();
                                        var lowestGroup            = validGroups.Where(g => !g.ExcludedByFilter && !excludedLocations.Contains(g.Group.Name))
                                                                     .Select(g => new { Group = g, Attendance = g.Locations.Select(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount).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))
                                    {
                                        filteredLocations = filteredLocations.OrderBy(l => KioskLocationAttendance.Read(l.Location.Id).CurrentCount);
                                    }

                                    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);
        }
Ejemplo n.º 23
0
        /// <summary>
        /// Handles the Click event of the btnManager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void btnManager_Click(object sender, EventArgs e)
        {
            var rockContext = new RockContext();

            pnlActive.Visible  = false;
            pnlManager.Visible = false;
            ManagerLoggedIn    = false;
            tbPIN.Text         = string.Empty;

            // Get room counts
            List <int> locations = new List <int>();

            var lUl = new HtmlGenericControl("ul");

            lUl.AddCssClass("kioskmanager-count-locations");
            phCounts.Controls.Add(lUl);
            var device = new DeviceService(rockContext).Get(CurrentKioskId.Value);

            if (device != null)
            {
                var deviceLocations = new List <Location>();
                var locationService = new LocationService(rockContext);
                foreach (var location in device.Locations)
                {
                    deviceLocations.Add(location);
                    deviceLocations.AddRange(locationService.GetAllDescendents(location.Id));
                }

                deviceLocations = deviceLocations
                                  .Where(l => l.LocationTypeValue == null || l.LocationTypeValue.Guid == Rock.SystemGuid.DefinedValue.LOCATION_TYPE_ROOM.AsGuid())
                                  .Distinct()
                                  .ToList();

                foreach (var location in deviceLocations)
                {
                    if (!locations.Contains(location.Id))
                    {
                        locations.Add(location.Id);
                        var locationAttendance = KioskLocationAttendance.Read(location.Id);

                        if (locationAttendance != null)
                        {
                            var lLi = new HtmlGenericControl("li");
                            lUl.Controls.Add(lLi);
                            lLi.InnerHtml = string.Format("<strong>{0}</strong>: {1}", locationAttendance.LocationName, locationAttendance.CurrentCount);

                            var gUl = new HtmlGenericControl("ul");
                            gUl.AddCssClass("kioskmanager-count-groups");
                            lLi.Controls.Add(gUl);

                            // ARRAN: If we only want to show the count of people in the room, and not further split it into groups and schedules, we need to remove this.
                            // I've tested both with and without it, so removing it is ok.
                            foreach (var groupAttendance in locationAttendance.Groups)
                            {
                                var gLi = new HtmlGenericControl("li");
                                gUl.Controls.Add(gLi);
                                gLi.InnerHtml = string.Format("<strong>{0}</strong>: {1}", groupAttendance.GroupName, groupAttendance.CurrentCount);

                                var sUl = new HtmlGenericControl("ul");
                                sUl.AddCssClass("kioskmanager-count-schedules");
                                gLi.Controls.Add(sUl);

                                foreach (var scheduleAttendance in groupAttendance.Schedules.Where(s => s.IsActive))
                                {
                                    var sLi = new HtmlGenericControl("li");
                                    sUl.Controls.Add(sLi);
                                    sLi.InnerHtml = string.Format("<strong>{0}</strong>: {1}", scheduleAttendance.ScheduleName, scheduleAttendance.CurrentCount);
                                }
                            }
                            // Remove up to here
                        }
                    }
                }
            }

            pnlManagerLogin.Visible = true;

            // set manager timer to 10 minutes
            hfRefreshTimerSeconds.Value = "600";
        }