/// <summary>
        /// Private method called by Execute() to process the job.
        /// </summary>
        private void ProcessJob(IJobExecutionContext context)
        {
            JobDataMap dataMap = context.JobDetail.JobDataMap;

            int notificationsSent  = 0;
            int errorsEncountered  = 0;
            int absentMembersCount = 0;
            int sendFailed         = 0;

            // get groups set to sync
            RockContext rockContext = new RockContext();

            Guid?groupTypeGuid       = dataMap.GetString(AttributeKey.GroupType).AsGuidOrNull();
            Guid?systemEmailGuid     = dataMap.GetString(AttributeKey.NotificationEmail).AsGuidOrNull();
            Guid?groupRoleFilterGuid = dataMap.GetString(AttributeKey.GroupRoleFilter).AsGuidOrNull();
            int  minimumAbsences     = dataMap.GetString(AttributeKey.MinimumAbsences).AsInteger();

            // get system email
            var emailService = new SystemCommunicationService(rockContext);

            SystemCommunication systemEmail = null;

            if (!systemEmailGuid.HasValue || systemEmailGuid == Guid.Empty)
            {
                context.Result = "Job failed. Unable to find System Email";
                throw new Exception("No system email found.");
            }

            if (minimumAbsences == default(int))
            {
                context.Result = "Job failed. The is no minimum absense count entered.";
                throw new Exception("No minimum absense count found.");
            }
            systemEmail = emailService.Get(systemEmailGuid.Value);

            // get group members
            if (!groupTypeGuid.HasValue || groupTypeGuid == Guid.Empty)
            {
                context.Result = "Job failed. Unable to find group type";
                throw new Exception("No group type found.");
            }

            var groupMemberQry = new GroupMemberService(rockContext).Queryable("Group, Group.Members.GroupRole")
                                 .Where(m => m.Group.GroupType.Guid == groupTypeGuid.Value);

            if (groupRoleFilterGuid.HasValue)
            {
                groupMemberQry = groupMemberQry.Where(m => m.GroupRole.Guid == groupRoleFilterGuid.Value);
            }

            var groupMembers = groupMemberQry.GroupBy(m => m.Group);
            var errorList    = new List <string>();

            foreach (var groupGroupMember in groupMembers)
            {
                var group           = groupGroupMember.Key;
                var filteredPersons = groupGroupMember.Select(a => a.PersonId);
                // get list of leaders
                var groupLeaders = group.Members.Where(m => m.GroupRole.IsLeader == true && m.Person != null && m.Person.Email != null && m.Person.Email != string.Empty);

                if (!groupLeaders.Any())
                {
                    errorList.Add("Unable to send emails to members in group " + group.Name + " because there is no group leader");
                    continue;
                }

                // Get all the occurrences for this group
                var occurrences = new AttendanceOccurrenceService(rockContext)
                                  .Queryable("Attendees.PersonAlias.Person")
                                  .Where(a => a.DidNotOccur.HasValue && !a.DidNotOccur.Value && a.GroupId == group.Id)
                                  .OrderByDescending(a => a.OccurrenceDate)
                                  .Take(minimumAbsences)
                                  .ToList();

                if (occurrences.Count == minimumAbsences)
                {
                    var absentPersons = occurrences
                                        .SelectMany(a => a.Attendees)
                                        .Where(a => a.DidAttend.HasValue && !a.DidAttend.Value && filteredPersons.Contains(a.PersonAlias.PersonId))
                                        .GroupBy(a => a.PersonAlias.Person)
                                        .Where(a => a.Count() == minimumAbsences)
                                        .Select(a => a.Key)
                                        .ToList();

                    if (absentPersons.Count > 0)
                    {
                        var recipients = new List <RockEmailMessageRecipient>();
                        foreach (var leader in groupLeaders)
                        {
                            // create merge object
                            var mergeFields = new Dictionary <string, object>();
                            mergeFields.Add("AbsentMembers", absentPersons);
                            mergeFields.Add("Group", group);
                            mergeFields.Add("Person", leader.Person);
                            recipients.Add(new RockEmailMessageRecipient(leader.Person, mergeFields));
                        }

                        var errorMessages = new List <string>();
                        var emailMessage  = new RockEmailMessage(systemEmail.Guid);
                        emailMessage.SetRecipients(recipients);
                        var sendSuccess = emailMessage.Send(out errorMessages);
                        if (!sendSuccess)
                        {
                            sendFailed++;
                        }

                        errorsEncountered += errorMessages.Count;
                        errorList.AddRange(errorMessages);

                        // be conservative: only mark as notified if we are sure the email didn't fail
                        if (errorMessages.Any())
                        {
                            continue;
                        }

                        absentMembersCount += absentPersons.Count;
                        notificationsSent  += recipients.Count();
                    }
                }
            }

            context.Result = string.Format("Sent {0} emails to leaders for {1} absent members. {2} errors encountered. {3} times Send reported a fail.", notificationsSent, absentMembersCount, errorsEncountered, sendFailed);

            if (errorList.Any())
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine();
                sb.Append("Errors in GroupLeaderAbsenceNotifications: ");
                errorList.ForEach(e => { sb.AppendLine(); sb.Append(e); });
                string errors = sb.ToString();
                context.Result += errors;
                throw new Exception(errors);
            }
        }