Ejemplo n.º 1
0
 /// <summary>
 /// Toggles on or off one check-in GroupLocationSchedule (it will deselct others)
 /// </summary>
 /// <param name="chGroup">Checkin Group</param>
 /// <param name="chLocation">Checkin Location</param>
 /// <param name="chSchedule">Checkin Schedule</param>
 /// <param name="alreadySelected">Is the Group Location Schedule already selected</param>
 private void ToggleClass(CheckInGroup chGroup, CheckInLocation chLocation, CheckInSchedule chSchedule, bool alreadySelected)
 {
     foreach (var groupType in CurrentCheckInState.CheckIn.CurrentPerson.GroupTypes)
     {
         groupType.Selected = true;
         foreach (var group in groupType.Groups)
         {
             group.Selected = false;
             foreach (var location in group.Locations)
             {
                 location.Selected = false;
                 foreach (var schedule in location.Schedules)
                 {
                     schedule.Selected = false;
                 }
             }
         }
     }
     if (!alreadySelected)
     {
         chGroup.Selected    = true;
         chLocation.Selected = true;
         chSchedule.Selected = true;
         btnCheckin.Visible  = true;
     }
     SaveState();
     ShowPersonCheckin();
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Load" /> event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs" /> object that contains the event data.</param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            RockPage.AddScriptLink("~/Scripts/iscroll.js");
            RockPage.AddScriptLink("~/Scripts/CheckinClient/checkin-core.js");

            if (CurrentWorkflow == null || CurrentCheckInState == null)
            {
                NavigateToHomePage();
            }
            else
            {
                if (!Page.IsPostBack)
                {
                    ClearSelection();

                    CheckInPerson person = null;
                    CheckInGroup  group  = null;

                    person = CurrentCheckInState.CheckIn.Families.Where(f => f.Selected)
                             .SelectMany(f => f.People.Where(p => p.Selected))
                             .FirstOrDefault();

                    if (person != null)
                    {
                        group = person.GroupTypes.Where(t => t.Selected)
                                .SelectMany(t => t.Groups.Where(g => g.Selected))
                                .FirstOrDefault();
                    }

                    if (group == null)
                    {
                        GoBack();
                    }

                    lTitle.Text    = person.ToString();
                    lSubTitle.Text = group.ToString();

                    var availLocations = group.Locations.Where(l => !l.ExcludedByFilter).ToList();
                    if (availLocations.Count == 1)
                    {
                        if (UserBackedUp)
                        {
                            GoBack();
                        }
                        else
                        {
                            availLocations.FirstOrDefault().Selected = true;
                            ProcessSelection();
                        }
                    }
                    else
                    {
                        rSelection.DataSource = availLocations;
                        rSelection.DataBind();
                    }
                }
            }
        }
Ejemplo n.º 3
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)
            {
                bool loadAll = GetAttributeValue(action, "LoadAll").AsBoolean();

                foreach (var family in checkInState.CheckIn.GetFamilies(true))
                {
                    foreach (var person in family.GetPeople(!loadAll))
                    {
                        var memberGroupIds = new GroupMemberService(rockContext)
                                             .Queryable()
                                             .AsNoTracking()
                                             .Where(m => m.GroupMemberStatus == GroupMemberStatus.Active && m.PersonId == person.Person.Id)
                                             .Select(m => m.GroupId)
                                             .ToList();

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

                            if (kioskGroupType != null)
                            {
                                foreach (var kioskGroup in kioskGroupType.KioskGroups.Where(g => g.IsCheckInActive))
                                {
                                    bool validGroup = true;

                                    var configuredKioskGroup = checkInState.ConfiguredGroups;
                                    if (configuredKioskGroup.Any())
                                    {
                                        validGroup = configuredKioskGroup.Contains(kioskGroup.Group.Id);
                                    }

                                    if (validGroup && groupType.GroupType.AttendanceRule == AttendanceRule.AlreadyBelongs)
                                    {
                                        validGroup = memberGroupIds.Contains(kioskGroup.Group.Id);
                                    }

                                    if (validGroup && !groupType.Groups.Any(g => g.Group.Id == kioskGroup.Group.Id))
                                    {
                                        var checkInGroup = new CheckInGroup();
                                        checkInGroup.Group = kioskGroup.Group.Clone(false);
                                        checkInGroup.Group.CopyAttributesFrom(kioskGroup.Group);
                                        groupType.Groups.Add(checkInGroup);
                                    }
                                }
                            }
                        }
                    }
                }

                return(true);
            }

            return(false);
        }
Ejemplo n.º 4
0
 private void SelectLocation(CheckInPerson person, CheckInGroupType gt, CheckInGroup g, CheckInLocation l)
 {
     l.Selected = !l.Selected;
     if (l.Selected)
     {
         person.Selected = true;
         gt.Selected     = true;
         g.Selected      = true;
         foreach (var s in l.Schedules)
         {
             s.Selected = true;
         }
     }
     else
     {
         foreach (var s in l.Schedules)
         {
             s.Selected = false;
         }
         if (!g.Locations.Where(_l => _l.Selected).Any())
         {
             g.Selected = false;
         }
         if (!gt.Groups.Where(_g => _g.Selected).Any())
         {
             gt.Selected = false;
         }
         if (!person.GroupTypes.Where(_gt => _gt.Selected).Any())
         {
             person.Selected = false;
         }
     }
     SaveState();
     BuildMemberCards();
 }
Ejemplo n.º 5
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)
            {
                var groupService = new GroupService(rockContext);

                foreach (var family in checkInState.CheckIn.Families.Where(f => f.Selected).ToList())
                {
                    foreach (var person in family.People)
                    {
                        var checkinGroupTypes = new List <CheckInGroupType>();

                        foreach (var id in checkInState.ConfiguredGroupTypes)
                        {
                            var cgt = new CheckInGroupType();
                            cgt.GroupType = GroupTypeCache.Get(id);
                            checkinGroupTypes.Add(cgt);
                            var groups = groupService.Queryable().Where(g => g.GroupTypeId == id);
                            List <CheckInGroup> checkinGroups = new List <CheckInGroup>();
                            foreach (var group in groups)
                            {
                                var cg = new CheckInGroup();
                                cg.Group = group;
                                group.LoadAttributes();
                                checkinGroups.Add(cg);
                                var groupLocations = group.GroupLocations;
                                List <CheckInLocation> checkinLocations = new List <CheckInLocation>();
                                foreach (var groupLocation in groupLocations)
                                {
                                    var cl = new CheckInLocation();
                                    cl.Location = groupLocation.Location;
                                    checkinLocations.Add(cl);
                                    var schedules = new List <CheckInSchedule>();
                                    foreach (var schedule in groupLocation.Schedules.ToList())
                                    {
                                        if (!schedule.WasCheckInActive(RockDateTime.Now))
                                        {
                                            continue;
                                        }
                                        var cs = new CheckInSchedule();
                                        cs.Schedule = schedule;
                                        schedules.Add(cs);
                                    }
                                    cl.Schedules = schedules;
                                }
                                cg.Locations = checkinLocations;
                            }
                            cgt.Groups = checkinGroups;
                        }
                        person.GroupTypes = checkinGroupTypes;
                    }
                }
                return(true);
            }
            errorMessages.Add($"Attempted to run {this.GetType().GetFriendlyTypeName()} in check-in, but the check-in state was null.");
            return(false);
        }
Ejemplo n.º 6
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)
            {
                var groupService = new GroupService(rockContext);

                foreach (var family in checkInState.CheckIn.Families.Where(f => f.Selected).ToList())
                {
                    foreach (var person in family.People)
                    {
                        var checkinGroupTypes = new List <CheckInGroupType>();

                        foreach (var id in checkInState.ConfiguredGroupTypes)
                        {
                            var cgt = new CheckInGroupType();
                            cgt.GroupType = GroupTypeCache.Get(id);
                            checkinGroupTypes.Add(cgt);
                            var groups = groupService.Queryable().Where(g => g.GroupTypeId == id);
                            List <CheckInGroup> checkinGroups = new List <CheckInGroup>();
                            foreach (var group in groups.Where(g => g.IsActive))
                            {
                                var cg = new CheckInGroup();
                                cg.Group = group;
                                group.LoadAttributes();
                                checkinGroups.Add(cg);
                                var groupLocations = group.GroupLocations;
                                List <CheckInLocation> checkinLocations = new List <CheckInLocation>();
                                foreach (var groupLocation in groupLocations)
                                {
                                    var cl = new CheckInLocation();
                                    cl.Location = groupLocation.Location;
                                    cl.CampusId = cl.Location.CampusId;
                                    checkinLocations.Add(cl);
                                    var schedules = new List <CheckInSchedule>();
                                    foreach (var schedule in groupLocation.Schedules)
                                    {
                                        var cs = new CheckInSchedule();
                                        cs.Schedule = schedule;
                                        schedules.Add(cs);
                                    }
                                    cl.Schedules = schedules;
                                }
                                cg.Locations = checkinLocations;
                            }
                            cgt.Groups = checkinGroups;
                        }
                        person.GroupTypes = checkinGroupTypes;
                    }
                }
                return(true);
            }
            return(false);
        }
        private void FilterLocationSchedules(CheckInGroup checkinGroup, List <CheckInLocation> locationList, List <CheckInSchedule> selectedSchedules, bool remove)
        {
            // Check the locations in the sorted or for the first one that has all the schedules available and use it if it exists.
            var locationForSchedules = locationList.Where(l => l.Schedules.Select(s => s.Schedule.Id).Intersect(selectedSchedules.Select(ss => ss.Schedule.Id)).Count() == selectedSchedules.Count).FirstOrDefault();

            if (locationForSchedules != null)
            {
                locationList.Remove(locationForSchedules);
                foreach (var location in locationList)
                {
                    if (remove)
                    {
                        checkinGroup.Locations.Remove(location);
                    }
                    else
                    {
                        location.ExcludedByFilter = true;
                    }
                }

                return;
            }

            // There is no location that has all of the selected schedules for this person so we need to choose each schedule location in the sorted list order.
            // As a practical matter this will probably not be more than two. The choosing will be done by removing the schedules from the locations that are not needed.
            foreach (var selectedSchedule in selectedSchedules)
            {
                var foundFirstMatch = false;
                foreach (var location in locationList)
                {
                    if (location.Schedules.Contains(selectedSchedule))
                    {
                        if (foundFirstMatch)
                        {
                            foreach (var checkinLocation in checkinGroup.Locations)
                            {
                                if (remove)
                                {
                                    checkinLocation.Schedules.Remove(selectedSchedule);
                                }
                                else
                                {
                                    selectedSchedule.ExcludedByFilter = true;
                                }
                            }
                        }
                        else
                        {
                            foundFirstMatch = true;
                        }
                    }
                }
            }
        }
Ejemplo n.º 8
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)
            {
                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))
                        {
                            var kioskGroupType = checkInState.Kiosk.ActiveGroupTypes(checkInState.ConfiguredGroupTypes)
                                                 .Where(g => g.GroupType.Id == groupType.GroupType.Id)
                                                 .FirstOrDefault();

                            if (kioskGroupType != null)
                            {
                                foreach (var kioskGroup in kioskGroupType.KioskGroups)
                                {
                                    bool validGroup = true;
                                    if (groupType.GroupType.AttendanceRule == AttendanceRule.AlreadyBelongs)
                                    {
                                        validGroup = new GroupMemberService(rockContext).Queryable()
                                                     .Any(m =>
                                                          m.GroupId == kioskGroup.Group.Id &&
                                                          m.GroupMemberStatus == GroupMemberStatus.Active &&
                                                          m.PersonId == person.Person.Id);
                                    }

                                    if (validGroup && !groupType.Groups.Any(g => g.Group.Id == kioskGroup.Group.Id))
                                    {
                                        var checkInGroup = new CheckInGroup();
                                        checkInGroup.Group = kioskGroup.Group.Clone(false);
                                        checkInGroup.Group.CopyAttributesFrom(kioskGroup.Group);
                                        groupType.Groups.Add(checkInGroup);
                                    }
                                }
                            }
                        }
                    }
                }

                return(true);
            }
            errorMessages.Add($"Attempted to run {this.GetType().GetFriendlyTypeName()} in check-in, but the check-in state was null.");
            return(false);
        }
Ejemplo n.º 9
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)
            {
                bool loadAll = false;
                if (bool.TryParse(GetAttributeValue(action, "LoadAll"), out loadAll) && loadAll)
                {
                    loadAll = true;
                }

                foreach (var family in checkInState.CheckIn.Families.Where(f => f.Selected).ToList())
                {
                    foreach (var person in family.People.Where(p => p.Selected || loadAll).ToList())
                    {
                        foreach (var groupType in person.GroupTypes.Where(g => g.Selected || loadAll).ToList())
                        {
                            var kioskGroupType = checkInState.Kiosk.FilteredGroupTypes(checkInState.ConfiguredGroupTypes).Where(g => g.GroupType.Id == groupType.GroupType.Id).FirstOrDefault();
                            if (kioskGroupType != null)
                            {
                                foreach (var kioskGroup in kioskGroupType.KioskGroups)
                                {
                                    bool validGroup = true;
                                    if (groupType.GroupType.AttendanceRule == AttendanceRule.AlreadyBelongs)
                                    {
                                        validGroup = new GroupMemberService(rockContext).Queryable()
                                                     .Any(m =>
                                                          m.GroupId == kioskGroup.Group.Id &&
                                                          m.PersonId == person.Person.Id);
                                    }

                                    if (validGroup && !groupType.Groups.Any(g => g.Group.Id == kioskGroup.Group.Id))
                                    {
                                        var checkInGroup = new CheckInGroup();
                                        checkInGroup.Group = kioskGroup.Group.Clone(false);
                                        checkInGroup.Group.CopyAttributesFrom(kioskGroup.Group);
                                        groupType.Groups.Add(checkInGroup);
                                    }
                                }
                            }
                        }
                    }
                }

                return(true);
            }

            return(false);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Handles the Delete event of the gPersonList control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="RowEventArgs" /> instance containing the event data.</param>
        protected void gPersonList_Delete(object sender, RowEventArgs e)
        {
            var dataKeyValues = gPersonList.DataKeys[e.RowIndex].Values;
            var personId      = Convert.ToInt32(dataKeyValues["PersonId"]);
            var locationId    = Convert.ToInt32(dataKeyValues["LocationId"]);
            var scheduleId    = Convert.ToInt32(dataKeyValues["ScheduleId"]);

            var selectedPerson = CurrentCheckInState.CheckIn.Families.Where(f => f.Selected).FirstOrDefault()
                                 .People.Where(p => p.Person.Id == personId).FirstOrDefault();
            var selectedGroups = selectedPerson.GroupTypes.Where(gt => gt.Selected)
                                 .SelectMany(gt => gt.Groups.Where(g => g.Selected));
            CheckInGroup selectedGroup = selectedGroups.Where(g => g.Selected &&
                                                              g.Locations.Any(l => l.Location.Id == locationId &&
                                                                              l.Schedules.Any(s => s.Schedule.Id == scheduleId))).FirstOrDefault();
            CheckInLocation selectedLocation = selectedGroup.Locations.Where(l => l.Selected &&
                                                                             l.Location.Id == locationId &&
                                                                             l.Schedules.Any(s => s.Schedule.Id == scheduleId)).FirstOrDefault();
            CheckInSchedule selectedSchedule = selectedLocation.Schedules.Where(s => s.Selected &&
                                                                                s.Schedule.Id == scheduleId).FirstOrDefault();

            selectedSchedule.Selected    = false;
            selectedSchedule.PreSelected = false;

            // clear checkin rows without anything selected
            if (!selectedLocation.Schedules.Any(s => s.Selected))
            {
                selectedLocation.Selected    = false;
                selectedLocation.PreSelected = false;
            }

            if (!selectedGroup.Locations.Any(l => l.Selected))
            {
                selectedGroup.Selected    = false;
                selectedGroup.PreSelected = false;
            }

            if (!selectedGroups.Any())
            {
                selectedPerson.Selected    = false;
                selectedPerson.PreSelected = false;
            }

            BindGrid();
        }
Ejemplo n.º 11
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)
            {
                bool loadAll = false;
                if (bool.TryParse(GetAttributeValue(action, "LoadAll"), out loadAll) && loadAll)
                {
                    loadAll = true;
                }

                foreach (var family in checkInState.CheckIn.Families.Where(f => f.Selected).ToList())
                {
                    foreach (var person in family.People.Where(p => p.Selected || loadAll).ToList())
                    {
                        foreach (var groupType in person.GroupTypes.Where(g => g.Selected || loadAll).ToList())
                        {
                            var kioskGroupType = checkInState.Kiosk.FilteredGroupTypes(checkInState.ConfiguredGroupTypes).Where(g => g.GroupType.Id == groupType.GroupType.Id).FirstOrDefault();
                            if (kioskGroupType != null)
                            {
                                foreach (var kioskGroup in kioskGroupType.KioskGroups)
                                {
                                    if (!groupType.Groups.Any(g => g.Group.Id == kioskGroup.Group.Id))
                                    {
                                        var checkInGroup = new CheckInGroup();
                                        checkInGroup.Group = kioskGroup.Group.Clone(false);
                                        checkInGroup.Group.CopyAttributesFrom(kioskGroup.Group);
                                        groupType.Groups.Add(checkInGroup);
                                    }
                                }
                            }
                        }
                    }
                }

                return(true);
            }

            return(false);
        }
        private void FilterLocations(CheckInGroup checkinGroup, List <CheckInLocation> locationList, bool remove)
        {
            var foundFirstMatch = false;

            foreach (var location in locationList)
            {
                if (foundFirstMatch)
                {
                    if (remove)
                    {
                        checkinGroup.Locations.Remove(location);
                    }
                    else
                    {
                        location.ExcludedByFilter = true;
                    }
                }
                else
                {
                    foundFirstMatch = true;
                }
            }
        }
Ejemplo n.º 13
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)
            {
                var family = checkInState.CheckIn.Families.Where(f => f.Selected).FirstOrDefault();
                if (family != null)
                {
                    foreach (var person in family.People.Where(f => f.Selected))
                    {
                        char[] delimiter = { ',' };
                        if (person.GroupTypes.Any())
                        {
                            CheckInGroupType groupType;
                            if (person.GroupTypes.Count > 1)
                            {
                                // check grouptypes for a grade range
                                var gradeFilter = person.GroupTypes.Where(gt => gt.GroupType.Attributes.ContainsKey("GradeRange")).Select(g =>
                                                                                                                                          new
                                {
                                    GroupType  = g,
                                    GradeRange = g.GroupType.GetAttributeValue("GradeRange").Split(delimiter, StringSplitOptions.None)
                                                 .Select(av => av.AsType <int?>())
                                }).ToList();

                                // #TODO: Test the upper value of grade and age ranges
                                var groupTypeMatchGrade = gradeFilter.Aggregate((x, y) => Math.Abs(Convert.ToDouble(x.GradeRange.First() - person.Person.Grade))
                                                                                < Math.Abs(Convert.ToDouble(y.GradeRange.First() - person.Person.Grade)) ? x : y)
                                                          .GroupType;

                                // check grouptypes for an age range
                                var ageFilter = person.GroupTypes.Where(g => g.GroupType.Attributes.ContainsKey("AgeRange")).Select(g =>
                                                                                                                                    new
                                {
                                    GroupType = g,
                                    AgeRange  = g.GroupType.GetAttributeValue("AgeRange").Split(delimiter, StringSplitOptions.None)
                                                .Select(av => av.AsType <double?>())
                                }).ToList();

                                var groupTypeMatchAge = ageFilter.Aggregate((x, y) => Math.Abs(Convert.ToDouble(x.AgeRange.First() - person.Person.Age))
                                                                            < Math.Abs(Convert.ToDouble(y.AgeRange.First() - person.Person.Age)) ? x : y)
                                                        .GroupType;

                                groupType = groupTypeMatchGrade ?? groupTypeMatchAge;
                            }
                            else
                            {   // only one grouptype is available
                                groupType = person.GroupTypes.FirstOrDefault();
                            }

                            if (groupType != null && groupType.Groups.Any())
                            {
                                groupType.PreSelected = true;
                                groupType.Selected    = true;

                                var group = groupType.Groups.Where(g => g.Selected).FirstOrDefault();
                                if (group == null && groupType.Groups.Any())
                                {
                                    //  check groups by grade
                                    var gradeGroups = groupType.Groups.Where(g => g.Group.Attributes.ContainsKey("GradeRange")).Select(g =>
                                                                                                                                       new {
                                        Group      = g,
                                        GradeRange = g.Group.GetAttributeValue("GradeRange").Split(delimiter, StringSplitOptions.None)
                                                     .Select(av => av.AsType <int?>())
                                    }).ToList();

                                    CheckInGroup groupMatchGrade = null;
                                    if (gradeGroups.Count > 0)
                                    {
                                        groupMatchGrade = gradeGroups.Aggregate((x, y) => Math.Abs(Convert.ToDouble(x.GradeRange.First() - person.Person.Grade))
                                                                                < Math.Abs(Convert.ToDouble(y.GradeRange.First() - person.Person.Grade)) ? x : y)
                                                          .Group;
                                    }

                                    // check groups by age
                                    var ageGroups = groupType.Groups.Where(g => g.Group.Attributes.ContainsKey("AgeRange")).Select(g =>
                                                                                                                                   new {
                                        Group    = g,
                                        AgeRange = g.Group.GetAttributeValue("AgeRange").Split(delimiter, StringSplitOptions.None)
                                                   .Select(av => av.AsType <double?>())
                                    }).ToList();

                                    CheckInGroup groupMatchAge = null;
                                    if (ageGroups.Count > 0)
                                    {
                                        groupMatchAge = ageGroups.Aggregate((x, y) => Math.Abs(Convert.ToDouble(x.AgeRange.First() - person.Person.Age))
                                                                            < Math.Abs(Convert.ToDouble(y.AgeRange.First() - person.Person.Age)) ? x : y)
                                                        .Group;
                                    }

                                    group = groupMatchGrade ?? groupMatchAge ?? groupType.Groups.FirstOrDefault();
                                }

                                if (group != null && group.Locations.Any())
                                {
                                    group.PreSelected = true;
                                    group.Selected    = true;
                                    var location = group.Locations.Where(l => l.Selected).FirstOrDefault();
                                    if (location == null)
                                    {
                                        // this works when a group is only meeting at one location per campus
                                        int primaryGroupLocationId = new GroupLocationService().Queryable().Where(gl => gl.GroupId == group.Group.Id)
                                                                     .Select(gl => gl.LocationId).ToList().FirstOrDefault();
                                        location = group.Locations.Where(l => l.Location.Id == primaryGroupLocationId).FirstOrDefault();
                                    }

                                    if (location != null && location.Schedules.Any())
                                    {
                                        location.PreSelected = true;
                                        location.Selected    = true;
                                        var schedule = location.Schedules.Where(s => s.Selected).FirstOrDefault();
                                        if (schedule == null)
                                        {
                                            schedule             = location.Schedules.FirstOrDefault();
                                            schedule.PreSelected = true;
                                            schedule.Selected    = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                return(true);
            }

            return(false);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Enforces the strict location threshold by removing attendances that would have ended up going into full location+schedules.
        /// Note: The is also checked earlier in the check-in process, so this catches ones that might have just gotten full in the last few seconds.
        /// </summary>
        /// <param name="action">The action.</param>
        /// <param name="checkInState">State of the check in.</param>
        /// <param name="attendanceService">The attendance service.</param>
        /// <param name="currentOccurrences">The current occurrences.</param>
        /// <param name="person">The person.</param>
        /// <param name="group">The group.</param>
        /// <param name="location">The location.</param>
        /// <param name="schedule">The schedule.</param>
        /// <param name="startDateTime">The start date time.</param>
        private void EnforceStrictLocationThreshold(WorkflowAction action, CheckInState checkInState, AttendanceService attendanceService, List <OccurrenceRecord> currentOccurrences, CheckInPerson person, CheckInGroup group, CheckInLocation location, CheckInSchedule schedule, DateTime startDateTime)
        {
            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 currentOccurrence = GetCurrentOccurrence(currentOccurrences, 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() + (currentOccurrence == null ? 0 : currentOccurrence.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(MergeFieldKey.Person, person.Person);
                    mergeFields.Add(MergeFieldKey.Group, group.Group);
                    mergeFields.Add(MergeFieldKey.Location, location.Location);
                    mergeFields.Add(MergeFieldKey.Schedule, schedule.Schedule);
                    message.MessageText = GetAttributeValue(action, AttributeKey.NotCheckedInMessageFormat).ResolveMergeFields(mergeFields);

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

                        currentOccurrences.Add(currentOccurrence);
                    }

                    currentOccurrence.Count += 1;
                }
            }
        }
Ejemplo n.º 15
0
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            RockPage.AddScriptLink("~/Scripts/CheckinClient/checkin-core.js");

            if (CurrentWorkflow == null || CurrentCheckInState == null)
            {
                NavigateToHomePage();
            }
            else
            {
                if (!Page.IsPostBack)
                {
                    ClearSelection();

                    var personSchedules   = new List <CheckInSchedule>();
                    var distinctSchedules = new List <CheckInSchedule>();
                    if (CurrentCheckInType != null && CurrentCheckInType.TypeOfCheckin == TypeOfCheckin.Family)
                    {
                        CheckInFamily family = CurrentCheckInState.CheckIn.CurrentFamily;
                        if (family != null)
                        {
                            foreach (var schedule in family.GetPeople(true).SelectMany(p => p.PossibleSchedules).ToList())
                            {
                                personSchedules.Add(schedule);
                                if (!distinctSchedules.Any(s => s.Schedule.Id == schedule.Schedule.Id))
                                {
                                    distinctSchedules.Add(schedule);
                                }
                            }
                        }
                        else
                        {
                            GoBack();
                        }

                        lTitle.Text   = GetTitleText();
                        lbSelect.Text = "Next";
                        lbSelect.Attributes.Add("data-loading-text", "Loading...");
                    }
                    else
                    {
                        CheckInPerson   person   = CurrentCheckInState.CheckIn.Families.Where(f => f.Selected).SelectMany(f => f.People.Where(p => p.Selected)).FirstOrDefault();
                        CheckInGroup    group    = null;
                        CheckInLocation location = null;

                        if (person != null)
                        {
                            group = person.GroupTypes.Where(t => t.Selected).SelectMany(t => t.Groups.Where(g => g.Selected)).FirstOrDefault();

                            if (group != null)
                            {
                                location = group.Locations.Where(l => l.Selected).FirstOrDefault();
                            }
                        }

                        if (location == null)
                        {
                            GoBack();
                        }

                        lTitle.Text   = GetTitleText();
                        lbSelect.Text = "Check In";
                        lbSelect.Attributes.Add("data-loading-text", "Printing...");

                        personSchedules   = location.Schedules.Where(s => !s.ExcludedByFilter).ToList();
                        distinctSchedules = personSchedules;
                    }

                    lCaption.Text = GetAttributeValue(AttributeKey.Caption);

                    if (distinctSchedules.Count == 1)
                    {
                        personSchedules.ForEach(s => s.Selected = true);
                        ProcessSelection(maWarning);
                    }
                    else
                    {
                        string script = string.Format(@"
    <script>
        function GetTimeSelection() {{
            var ids = '';
            $('div.checkin-timelist button.active').each( function() {{
                ids += $(this).attr('schedule-id') + ',';
            }});
            if (ids == '') {{
                bootbox.alert('Please select at least one time');
                return false;
            }}
            else
            {{
                $('#{0}').button('loading')
                $('#{1}').val(ids);
                return true;
            }}
        }}
    </script>
", lbSelect.ClientID, hfTimes.ClientID);
                        Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "SelectTime", script);

                        rSelection.DataSource = distinctSchedules
                                                .OrderBy(s => s.StartTime.Value.TimeOfDay)
                                                .ThenBy(s => s.Schedule.Name)
                                                .ToList();

                        rSelection.DataBind();
                    }
                }
            }
        }
Ejemplo n.º 16
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.º 17
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))
                                    {
                                        int?bestScheduleId     = null;
                                        var availableSchedules = validGroups.SelectMany(g => g.Locations.SelectMany(l => l.Schedules)).DistinctBy(s => s.Schedule.Id).ToList();
                                        if (availableSchedules.Any())
                                        {
                                            bestScheduleId = availableSchedules.OrderBy(s => s.StartTime).Select(s => s.Schedule.Id).FirstOrDefault();
                                        }

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

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

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

                                validLocations = bestGroup.Locations;
                            }

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

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

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

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

                                    bestLocation = filteredLocations.FirstOrDefault();
                                }

                                if (bestLocation != null && bestLocation.Schedules.Any())
                                {
                                    // finished finding assignment, verify we can select everything
                                    var bestSchedule = bestLocation.Schedules.OrderBy(s => s.Schedule.StartTimeOfDay).FirstOrDefault();
                                    if (bestSchedule != null)
                                    {
                                        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);
        }
        /// <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()).ToList();

            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();

                    // order by most recent attendance
                    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 assignmentsGiven = 0;
                        // get the most recent day, then create assignments starting with the earliest attendance record
                        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).OrderBy(a => a.Schedule.StartTimeOfDay))
                        {
                            bool currentlyCheckedIn = false;
                            var  serviceCutoff      = groupAttendance.StartDateTime;
                            if (serviceCutoff > RockDateTime.Now.Date && groupAttendance.Schedule != null)
                            {
                                // calculate the service window to determine if people are still checked in
                                var serviceTime  = groupAttendance.StartDateTime.Date + groupAttendance.Schedule.StartTimeOfDay;
                                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)
                            {
                                // assigning the right schedule depends on prior attendance & currently available schedules being sorted
                                var orderedSchedules = groupType.Groups.SelectMany(g => g.Locations.SelectMany(l => l.Schedules))
                                                       .DistinctBy(s => s.Schedule.Id).OrderBy(s => s.Schedule.StartTimeOfDay)
                                                       .Select(s => s.Schedule.Id).ToList();

                                int?currentScheduleId = null;
                                if (orderedSchedules.Count == 1)
                                {
                                    currentScheduleId = orderedSchedules.FirstOrDefault();
                                }
                                else if (currentlyCheckedIn)
                                {
                                    // always pick the schedule they're currently checked into
                                    currentScheduleId = orderedSchedules.Where(s => s == groupAttendance.ScheduleId).FirstOrDefault();
                                }
                                else
                                {
                                    // sort the earliest schedule for the current grouptype, then skip the number of assignments already given (multiple services)
                                    currentScheduleId = groupType.AvailableForSchedule
                                                        .OrderBy(d => orderedSchedules.IndexOf(d))
                                                        .Skip(assignmentsGiven).FirstOrDefault();
                                }

                                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 and only for the current service
                                    if (group != null && currentScheduleId != null && roomBalanceGroupTypeIds.Contains(group.Group.GroupTypeId) && !excludedLocations.Contains(group.Group.Name) && !useCheckinOverride)
                                    {
                                        // make sure balanced rooms are open for the current service
                                        var currentAttendance = group.Locations.Where(l => l.AvailableForSchedule.Contains((int)currentScheduleId))
                                                                .Select(l => Helpers.ReadAttendanceBySchedule(l.Location.Id, currentScheduleId)).Sum();

                                        var lowestAttendedGroup = groupType.Groups.Where(g => g.AvailableForSchedule.Contains((int)currentScheduleId))
                                                                  .Where(g => !g.ExcludedByFilter && !excludedLocations.Contains(g.Group.Name))
                                                                  .Select(g => new { Group = g, Attendance = g.Locations.Select(l => Helpers.ReadAttendanceBySchedule(l.Location.Id, currentScheduleId)).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 and only for the current service
                                        if (location != null && currentScheduleId != null && roomBalanceGroupTypeIds.Contains(group.Group.GroupTypeId) && !excludedLocations.Contains(location.Location.Name) && !useCheckinOverride)
                                        {
                                            var currentAttendance = Helpers.ReadAttendanceBySchedule(location.Location.Id, currentScheduleId);

                                            var lowestAttendedLocation = group.Locations.Where(l => l.AvailableForSchedule.Contains((int)currentScheduleId))
                                                                         .Where(l => !l.ExcludedByFilter && !excludedLocations.Contains(l.Location.Name))
                                                                         .Select(l => new { Location = l, Attendance = Helpers.ReadAttendanceBySchedule(l.Location.Id, currentScheduleId) })
                                                                         .OrderBy(l => l.Attendance)
                                                                         .FirstOrDefault();

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

                                    if (location != null)
                                    {
                                        // the current schedule could exist on multiple locations, so pick the one owned by this location
                                        // if the current schedule just closed, get the first available schedule at this location
                                        CheckInSchedule schedule = location.Schedules.OrderByDescending(s => s.Schedule.Id == currentScheduleId).FirstOrDefault();
                                        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 feature either removes the attendance or sets 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;
                                            previousAttender.Selected    = true;
                                            previousAttender.PreSelected = true;
                                            assignmentsGiven++;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

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

            if (checkInState != null)
            {
                var checkinAttributeKey  = "";
                var checkinAttributeGuid = GetAttributeValue(action, "CheckinGroupsAttribute");
                if (!string.IsNullOrWhiteSpace(checkinAttributeGuid))
                {
                    var attributeCache = AttributeCache.Read(checkinAttributeGuid.AsGuid());
                    if (attributeCache != null)
                    {
                        checkinAttributeKey = attributeCache.Key;
                    }
                }
                foreach (var family in checkInState.CheckIn.GetFamilies(true))
                {
                    foreach (var person in family.GetPeople(false))
                    {
                        if (person.Person.Attributes == null)
                        {
                            person.Person.LoadAttributes();
                        }
                        var groupGuidsString = person.Person.GetAttributeValue(checkinAttributeKey);
                        if (!string.IsNullOrWhiteSpace(groupGuidsString))
                        {
                            var guids = groupGuidsString
                                        .Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
                                        .Select(g => g.AsGuid())
                                        .ToList();

                            foreach (var groupType in person.GroupTypes)
                            {
                                var kioskGroup = checkInState.Kiosk.ActiveGroupTypes(checkInState.ConfiguredGroupTypes)
                                                 .Where(g => g.GroupType.Id == groupType.GroupType.Id)
                                                 .FirstOrDefault();

                                if (kioskGroup != null)
                                {
                                    var injectGroups = kioskGroup.KioskGroups
                                                       .Where(g =>
                                                              g.IsCheckInActive &&
                                                              guids.Contains(g.Group.Guid)
                                                              );

                                    if (!injectGroups.Any())
                                    {
                                        continue;
                                    }

                                    foreach (var injectGroup in injectGroups)
                                    {
                                        var checkInGroup = new CheckInGroup();
                                        checkInGroup.Group = injectGroup.Group.Clone(false);
                                        checkInGroup.Group.CopyAttributesFrom(injectGroup.Group);
                                        groupType.Groups.Insert(0, checkInGroup);
                                    }
                                    groupType.Groups = groupType.Groups.DistinctBy(g => g.Group.Id).ToList();
                                }
                            }
                        }
                    }
                }
                return(true);
            }

            return(false);
        }
Ejemplo n.º 21
0
        private CheckInStatus DehydrateStatus(CheckInStatus status)
        {
            var checkinstatus = new CheckInStatus
            {
                SearchType  = status.SearchType,
                SearchValue = status.SearchValue,
                Families    = new List <CheckInFamily>()
            };

            foreach (var family in status.Families)
            {
                var checkinFamily = new CheckInFamily()
                {
                    Selected      = family.Selected,
                    Caption       = family.Caption,
                    SubCaption    = family.SubCaption,
                    AttendanceIds = family.AttendanceIds,
                    FirstNames    = family.FirstNames,
                    Group         = new Group()
                    {
                        Id = family.Group.Id
                    },
                    People = new List <CheckInPerson>()
                };
                checkinstatus.Families.Add(checkinFamily);

                foreach (var person in family.People)
                {
                    var checkInPerson = new CheckInPerson
                    {
                        GroupTypes   = new List <CheckInGroupType>(),
                        FirstTime    = person.FirstTime,
                        FamilyMember = person.FamilyMember,
                        Person       = new Person {
                            Id = person.Person.Id
                        },
                        PreSelected      = person.PreSelected,
                        SecurityCode     = person.SecurityCode,
                        ExcludedByFilter = person.ExcludedByFilter,
                        Selected         = person.Selected
                    };
                    checkinFamily.People.Add(checkInPerson);

                    foreach (var grouptype in person.GroupTypes)
                    {
                        var checkinGroupType = new CheckInGroupType
                        {
                            GroupType        = grouptype.GroupType,
                            Selected         = grouptype.Selected,
                            PreSelected      = grouptype.PreSelected,
                            ExcludedByFilter = grouptype.ExcludedByFilter,
                            Groups           = new List <CheckInGroup>()
                        };
                        checkInPerson.GroupTypes.Add(checkinGroupType);

                        foreach (var group in grouptype.Groups)
                        {
                            var checkinGroup = new CheckInGroup
                            {
                                Group = new Group {
                                    Id = group.Group.Id
                                },
                                Selected         = group.Selected,
                                PreSelected      = group.PreSelected,
                                ExcludedByFilter = group.ExcludedByFilter,
                                Locations        = new List <CheckInLocation>()
                            };
                            checkinGroupType.Groups.Add(group);

                            foreach (var location in group.Locations)
                            {
                                var checkinLocation = new CheckInLocation
                                {
                                    Location = new Location {
                                        Id = location.Location.Id
                                    },
                                    Selected         = location.Selected,
                                    PreSelected      = location.PreSelected,
                                    ExcludedByFilter = location.ExcludedByFilter,
                                    CampusId         = location.CampusId,
                                    Schedules        = new List <CheckInSchedule>()
                                };
                                checkinGroup.Locations.Add(checkinLocation);

                                foreach (var schedule in location.Schedules)
                                {
                                    var checkinSchedule = new CheckInSchedule
                                    {
                                        Schedule = new Schedule {
                                            Id = schedule.Schedule.Id
                                        },
                                        Selected         = schedule.Selected,
                                        PreSelected      = schedule.PreSelected,
                                        ExcludedByFilter = schedule.ExcludedByFilter,
                                        CampusId         = schedule.CampusId,
                                        StartTime        = schedule.StartTime
                                    };
                                    checkinLocation.Schedules.Add(checkinSchedule);
                                }
                            }
                        }
                    }
                }
            }
            return(checkinstatus);
        }
Ejemplo n.º 22
0
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            RockPage.AddScriptLink("~/Scripts/iscroll.js");
            RockPage.AddScriptLink("~/Scripts/CheckinClient/checkin-core.js");

            if (CurrentWorkflow == null || CurrentCheckInState == null)
            {
                NavigateToHomePage();
            }
            else
            {
                if (!Page.IsPostBack)
                {
                    ClearSelection();

                    CheckInPerson   person   = null;
                    CheckInGroup    group    = null;
                    CheckInLocation location = null;

                    person = CurrentCheckInState.CheckIn.Families.Where(f => f.Selected)
                             .SelectMany(f => f.People.Where(p => p.Selected))
                             .FirstOrDefault();

                    if (person != null)
                    {
                        group = person.GroupTypes.Where(t => t.Selected)
                                .SelectMany(t => t.Groups.Where(g => g.Selected))
                                .FirstOrDefault();

                        if (group != null)
                        {
                            location = group.Locations.Where(l => l.Selected)
                                       .FirstOrDefault();
                        }
                    }

                    if (location == null)
                    {
                        GoBack();
                    }

                    lTitle.Text    = person.ToString();
                    lSubTitle.Text = string.Format("{0} - {1}", group.ToString(), location.ToString());

                    var availSchedules = location.Schedules.Where(s => !s.ExcludedByFilter).ToList();
                    if (availSchedules.Count == 1)
                    {
                        availSchedules.FirstOrDefault().Selected = true;
                        ProcessSelection(maWarning);
                    }
                    else
                    {
                        string script = string.Format(@"
    <script>
        function GetTimeSelection() {{
            var ids = '';
            $('div.checkin-timelist button.active').each( function() {{
                ids += $(this).attr('schedule-id') + ',';
            }});
            if (ids == '') {{
                bootbox.alert('Please select at least one time');
                return false;
            }}
            else
            {{
                $('#{0}').button('loading')
                $('#{1}').val(ids);
                return true;
            }}
        }}
    </script>
", lbSelect.ClientID, hfTimes.ClientID);
                        Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "SelectTime", script);

                        rSelection.DataSource = availSchedules
                                                .OrderBy(s => s.StartTime.Value.TimeOfDay)
                                                .ThenBy(s => s.Schedule.Name)
                                                .ToList();

                        rSelection.DataBind();
                    }
                }
            }
        }
        private void addLabel(CheckInLabel checkInLabel, CheckInState checkInState, CheckInGroupType groupType, CheckInGroup group, RockContext rockContext)
        {
            var PrinterIPs = new Dictionary <int, string>();

            if (checkInLabel.PrintTo == PrintTo.Default)
            {
                checkInLabel.PrintTo = groupType.GroupType.AttendancePrintTo;
            }
            else if (checkInLabel.PrintTo == PrintTo.Location && group.Locations.Any())
            {
                var deviceId = group.Locations.FirstOrDefault().Location.PrinterDeviceId;
                if (deviceId != null)
                {
                    checkInLabel.PrinterDeviceId = deviceId;
                }
            }
            else
            {
                var device = checkInState.Kiosk.Device;
                if (device != null)
                {
                    checkInLabel.PrinterDeviceId = device.PrinterDeviceId;
                }
            }


            if (checkInLabel.PrinterDeviceId.HasValue)
            {
                if (PrinterIPs.ContainsKey(checkInLabel.PrinterDeviceId.Value))
                {
                    checkInLabel.PrinterAddress = PrinterIPs[checkInLabel.PrinterDeviceId.Value];
                }
                else
                {
                    var printerDevice = new DeviceService(rockContext).Get(checkInLabel.PrinterDeviceId.Value);
                    if (printerDevice != null)
                    {
                        PrinterIPs.Add(printerDevice.Id, printerDevice.IPAddress);
                        checkInLabel.PrinterAddress = printerDevice.IPAddress;
                    }
                }
            }

            groupType.Labels.Insert(0, checkInLabel);
        }
Ejemplo n.º 24
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);
        }