/// <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 )
            {
                return false;
            }

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

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

                // log a warning if the attribute is missing or invalid
                if ( string.IsNullOrWhiteSpace( ageRangeAttributeKey ) )
                {
                    action.AddLogEntry( string.Format( "The Group Age Range attribute is not selected or invalid for '{0}'.", action.ActionType.Name ) );
                }

                foreach ( var person in family.People )
                {
                    var ageAsDouble = person.Person.AgePrecise;
                    decimal? age = null;

                    if ( !ageAsDouble.HasValue && !ageRequired )
                    {
                        continue;
                    }

                    if ( ageAsDouble.HasValue )
                    {
                        age = Convert.ToDecimal( ageAsDouble.Value );
                    }

                    foreach ( var groupType in person.GroupTypes.ToList() )
                    {
                        foreach ( var group in groupType.Groups.ToList() )
                        {
                            var ageRange = group.Group.GetAttributeValue( ageRangeAttributeKey ).ToStringSafe();

                            var ageRangePair = ageRange.Split( new char[] { ',' }, StringSplitOptions.None );
                            string minAgeValue = null;
                            string maxAgeValue = null;

                            if ( ageRangePair.Length == 2 )
                            {
                                minAgeValue = ageRangePair[0];
                                maxAgeValue = ageRangePair[1];
                            }

                            if ( minAgeValue != null )
                            {
                                decimal minAge = 0;

                                if ( decimal.TryParse( minAgeValue, out minAge ) )
                                {
                                    decimal? personAgePrecise = null;

                                    if ( age.HasValue )
                                    {
                                        int groupMinAgePrecision = minAge.GetDecimalPrecision();
                                        personAgePrecise = age.Floor( groupMinAgePrecision );
                                    }

                                    if ( !age.HasValue || personAgePrecise < minAge )
                                    {
                                        if ( remove )
                                        {
                                            groupType.Groups.Remove( group );
                                        }
                                        else
                                        {
                                            group.ExcludedByFilter = true;
                                        }
                                        continue;
                                    }
                                }
                            }

                            if ( maxAgeValue != null )
                            {
                                decimal maxAge = 0;

                                if ( decimal.TryParse( maxAgeValue, out maxAge ) )
                                {
                                    decimal? personAgePrecise = null;

                                    if ( age.HasValue )
                                    {
                                        int groupMaxAgePrecision = maxAge.GetDecimalPrecision();
                                        personAgePrecise = age.Floor( groupMaxAgePrecision );
                                    }

                                    if ( !age.HasValue || personAgePrecise > maxAge )
                                    {
                                        if ( remove )
                                        {
                                            groupType.Groups.Remove( group );
                                        }
                                        else
                                        {
                                            group.ExcludedByFilter = true;
                                        }
                                        continue;
                                    }
                                }
                            }
                        }
                    }
                }
            }

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

            var family = checkInState.CheckIn.CurrentFamily;
            if ( family != null )
            {
                var remove = GetAttributeValue( action, "Remove" ).AsBoolean();
                bool removeSNGroups = GetAttributeValue( action, "RemoveSpecialNeedsGroups" ).AsBoolean( true );
                bool removeNonSNGroups = GetAttributeValue( action, "RemoveNonSpecialNeedsGroups" ).AsBoolean();

                // 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;
                }

                var groupSpecialNeedsKey = string.Empty;
                var groupSpecialNeedsGuid = GetAttributeValue( action, "GroupSpecialNeedsAttribute" ).AsGuid();
                if ( groupSpecialNeedsGuid != Guid.Empty )
                {
                    groupSpecialNeedsKey = AttributeCache.Read( groupSpecialNeedsGuid, 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 ) );
                }

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

                foreach ( var person in family.People )
                {
                    if ( person.Person.Attributes == null )
                    {
                        person.Person.LoadAttributes( rockContext );
                    }

                    bool isSNPerson = person.Person.GetAttributeValue( personSpecialNeedsKey ).AsBoolean();
                    foreach ( var groupType in person.GroupTypes.ToList() )
                    {
                        foreach ( var group in groupType.Groups.ToList() )
                        {
                            bool isSNGroup = group.Group.GetAttributeValue( groupSpecialNeedsKey ).AsBoolean();

                            // If the group is special needs but the person is not, then remove it.
                            if ( removeSNGroups && isSNGroup && !( isSNPerson ) )
                            {
                                if ( remove )
                                {
                                    groupType.Groups.Remove( group );
                                }
                                else
                                {
                                    group.ExcludedByFilter = true;
                                }
                                continue;
                            }

                            // or if the setting is enabled and the person is SN but the group is not.
                            if ( removeNonSNGroups && isSNPerson && !isSNGroup )
                            {
                                if ( remove )
                                {
                                    groupType.Groups.Remove( group );
                                }
                                else
                                {
                                    group.ExcludedByFilter = true;
                                }
                            }
                        }
                    }
                }
            }

            return true;
        }