예제 #1
0
        /// <summary>
        /// Old DDay.iCal does it WRONG, because doesn't respect Timezones DST rules or offsets,
        /// just append the TZ to them but does no calculations, so all DST changes and offsets comes
        /// from the local machine.
        /// </summary>
        /// <returns></returns>
        static string DDayRecurrentExample(string tz)
        {
            // This time created is being used as the local time on the time zone later specified, so
            // is NOT the local machine time and is a different UTC value depending on the TZ
            var now   = new DateTime(2016, 1, 1, 12, 0, 0);
            var later = now.AddHours(1);

            var rrule = new DDay.iCal.RecurrencePattern(DDay.iCal.FrequencyType.Monthly, 1)
            {
                Count = 12
            };

            var e = new DDay.iCal.Event
            {
                DTStart = new DDay.iCal.iCalDateTime(now, tz),
                DTEnd   = new DDay.iCal.iCalDateTime(later, tz)
            };

            e.RecurrenceRules.Add(rrule);

            var calendar = new DDay.iCal.iCalendar();

            calendar.Events.Add(e);

            //var serializer = new CalendarSerializer(new SerializationContext());
            //return serializer.SerializeToString(calendar);

            var end = new DateTime(2017, 1, 1, 12, 0, 0);

            return(e.GetOccurrences(now, end).Aggregate("", (ret, a) =>
            {
                return ret + a.Period.StartTime.UTC.ToString() + "\n";
            }));
        }
예제 #2
0
        /// <summary>
        /// Executes the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        public virtual void Execute(IJobExecutionContext context)
        {
            JobDataMap dataMap   = context.JobDetail.JobDataMap;
            var        groupType = GroupTypeCache.Read(dataMap.GetString("GroupType").AsGuid());
            int        attendanceRemindersSent = 0;

            if (groupType.TakesAttendance && groupType.SendAttendanceReminder)
            {
                // Get the occurrence dates that apply
                var dates = new List <DateTime>();
                dates.Add(RockDateTime.Today);
                try
                {
                    string[] reminderDays = dataMap.GetString("SendReminders").Split(',');
                    foreach (string reminderDay in reminderDays)
                    {
                        if (reminderDay.Trim() != string.Empty)
                        {
                            var reminderDate = RockDateTime.Today.AddDays(0 - Convert.ToInt32(reminderDay));
                            if (!dates.Contains(reminderDate))
                            {
                                dates.Add(reminderDate);
                            }
                        }
                    }
                }
                catch { }

                var rockContext        = new RockContext();
                var groupService       = new GroupService(rockContext);
                var groupMemberService = new GroupMemberService(rockContext);
                var scheduleService    = new ScheduleService(rockContext);
                var attendanceService  = new AttendanceService(rockContext);

                var startDate = dates.Min();
                var endDate   = dates.Max().AddDays(1);

                // Find all 'occurrences' for the groups that occur on the affected dates
                var occurrences = new Dictionary <int, List <DateTime> >();
                foreach (var group in groupService
                         .Queryable("Schedule").AsNoTracking()
                         .Where(g =>
                                g.GroupTypeId == groupType.Id &&
                                g.IsActive &&
                                g.Schedule != null &&
                                g.Members.Any(m =>
                                              m.GroupMemberStatus == GroupMemberStatus.Active &&
                                              m.GroupRole.IsLeader &&
                                              m.Person.Email != null &&
                                              m.Person.Email != "")))
                {
                    // Add the group
                    occurrences.Add(group.Id, new List <DateTime>());

                    // Check for a iCal schedule
                    DDay.iCal.Event calEvent = group.Schedule.GetCalenderEvent();
                    if (calEvent != null)
                    {
                        // If schedule has an iCal schedule, get occurrences between first and last dates
                        foreach (var occurrence in calEvent.GetOccurrences(startDate, endDate))
                        {
                            var startTime = occurrence.Period.StartTime.Value;
                            if (dates.Contains(startTime.Date))
                            {
                                occurrences[group.Id].Add(startTime);
                            }
                        }
                    }
                    else
                    {
                        // if schedule does not have an iCal, then check for weekly schedule and calculate occurrences starting with first attendance or current week
                        if (group.Schedule.WeeklyDayOfWeek.HasValue)
                        {
                            foreach (var date in dates)
                            {
                                if (date.DayOfWeek == group.Schedule.WeeklyDayOfWeek.Value)
                                {
                                    var startTime = date;
                                    if (group.Schedule.WeeklyTimeOfDay.HasValue)
                                    {
                                        startTime = startTime.Add(group.Schedule.WeeklyTimeOfDay.Value);
                                    }
                                    occurrences[group.Id].Add(startTime);
                                }
                            }
                        }
                    }
                }

                // Remove any occurrences during group type exclusion date ranges
                foreach (var exclusion in groupType.GroupScheduleExclusions)
                {
                    if (exclusion.Start.HasValue && exclusion.End.HasValue)
                    {
                        foreach (var keyVal in occurrences)
                        {
                            foreach (var occurrenceDate in keyVal.Value.ToList())
                            {
                                if (occurrenceDate >= exclusion.Start.Value &&
                                    occurrenceDate < exclusion.End.Value.AddDays(1))
                                {
                                    keyVal.Value.Remove(occurrenceDate);
                                }
                            }
                        }
                    }
                }

                // Remove any 'occurrenes' that already have attendance data entered
                foreach (var occurrence in attendanceService
                         .Queryable().AsNoTracking()
                         .Where(a =>
                                a.StartDateTime >= startDate &&
                                a.StartDateTime < endDate &&
                                occurrences.Keys.Contains(a.GroupId.Value) &&
                                a.ScheduleId.HasValue)
                         .Select(a => new
                {
                    GroupId = a.GroupId.Value,
                    a.StartDateTime
                })
                         .Distinct()
                         .ToList())
                {
                    occurrences[occurrence.GroupId].RemoveAll(d => d.Date == occurrence.StartDateTime.Date);
                }

                // Get the groups that have occurrences
                var groupIds = occurrences.Where(o => o.Value.Any()).Select(o => o.Key).ToList();

                // Get the leaders of those groups
                var leaders = groupMemberService
                              .Queryable("Group,Person").AsNoTracking()
                              .Where(m =>
                                     groupIds.Contains(m.GroupId) &&
                                     m.GroupMemberStatus == GroupMemberStatus.Active &&
                                     m.GroupRole.IsLeader &&
                                     m.Person.Email != null &&
                                     m.Person.Email != "")
                              .ToList();

                // Loop through the leaders
                foreach (var leader in leaders)
                {
                    foreach (var group in occurrences.Where(o => o.Key == leader.GroupId))
                    {
                        var mergeObjects = Rock.Lava.LavaHelper.GetCommonMergeFields(null, leader.Person);
                        mergeObjects.Add("Person", leader.Person);
                        mergeObjects.Add("Group", leader.Group);
                        mergeObjects.Add("Occurrence", group.Value.Max());

                        var recipients = new List <RecipientData>();
                        recipients.Add(new RecipientData(leader.Person.Email, mergeObjects));

                        Email.Send(dataMap.GetString("SystemEmail").AsGuid(), recipients);
                        attendanceRemindersSent++;
                    }
                }
            }

            context.Result = string.Format("{0} attendance reminders sent", attendanceRemindersSent);
        }
예제 #3
0
        /// <summary>
        /// Gets occurrence data for the selected group
        /// </summary>
        /// <param name="group">The group.</param>
        /// <param name="fromDateTime">From date time.</param>
        /// <param name="toDateTime">To date time.</param>
        /// <param name="locationIds">The location ids.</param>
        /// <param name="scheduleIds">The schedule ids.</param>
        /// <param name="loadSummaryData">if set to <c>true</c> [load summary data].</param>
        /// <param name="campusId">The campus identifier.</param>
        /// <returns></returns>
        public List <ScheduleOccurrence> GetGroupOccurrences(Group group, DateTime?fromDateTime, DateTime?toDateTime,
                                                             List <int> locationIds, List <int> scheduleIds, bool loadSummaryData, int?campusId)
        {
            var occurrences = new List <ScheduleOccurrence>();

            if (group != null)
            {
                var rockContext       = (RockContext)this.Context;
                var attendanceService = new AttendanceService(rockContext);
                var scheduleService   = new ScheduleService(rockContext);
                var locationService   = new LocationService(rockContext);

                using (new Rock.Data.QueryHintScope(rockContext, QueryHintType.RECOMPILE))
                {
                    // Set up an 'occurrences' query for the group
                    var qry = attendanceService
                              .Queryable().AsNoTracking()
                              .Where(a => a.GroupId == group.Id);

                    // Filter by date range
                    if (fromDateTime.HasValue)
                    {
                        var fromDate = fromDateTime.Value.Date;
                        qry = qry.Where(a => DbFunctions.TruncateTime(a.StartDateTime) >= (fromDate));
                    }
                    if (toDateTime.HasValue)
                    {
                        var toDate = toDateTime.Value.Date;
                        qry = qry.Where(a => DbFunctions.TruncateTime(a.StartDateTime) < (toDate));
                    }

                    // Location Filter
                    if (locationIds.Any())
                    {
                        qry = qry.Where(a => locationIds.Contains(a.LocationId ?? 0));
                    }

                    // Schedule Filter
                    if (scheduleIds.Any())
                    {
                        qry = qry.Where(a => scheduleIds.Contains(a.ScheduleId ?? 0));
                    }

                    // Get the unique combination of location/schedule/date for the selected group
                    var occurrenceDates = qry
                                          .Select(a => new
                    {
                        a.LocationId,
                        a.ScheduleId,
                        Date = DbFunctions.TruncateTime(a.StartDateTime)
                    })
                                          .Distinct()
                                          .ToList();

                    // Get the locations for each unique location id
                    var selectedlocationIds = occurrenceDates.Select(o => o.LocationId).Distinct().ToList();

                    var locations = locationService
                                    .Queryable().AsNoTracking()
                                    .Where(l => selectedlocationIds.Contains(l.Id))
                                    .Select(l => new { l.Id, l.ParentLocationId, l.Name })
                                    .ToList();
                    var locationNames = new Dictionary <int, string>();
                    locations.ForEach(l => locationNames.Add(l.Id, l.Name));

                    // Get the parent location path for each unique location
                    var parentlocationPaths = new Dictionary <int, string>();
                    locations
                    .Where(l => l.ParentLocationId.HasValue)
                    .Select(l => l.ParentLocationId.Value)
                    .Distinct()
                    .ToList()
                    .ForEach(l => parentlocationPaths.Add(l, locationService.GetPath(l)));
                    var locationPaths = new Dictionary <int, string>();
                    locations
                    .Where(l => l.ParentLocationId.HasValue)
                    .ToList()
                    .ForEach(l => locationPaths.Add(l.Id, parentlocationPaths[l.ParentLocationId.Value]));

                    // Get the schedules for each unique schedule id
                    var selectedScheduleIds = occurrenceDates.Select(o => o.ScheduleId).Distinct().ToList();
                    var schedules           = scheduleService
                                              .Queryable().AsNoTracking()
                                              .Where(s => selectedScheduleIds.Contains(s.Id))
                                              .ToList();
                    var scheduleNames      = new Dictionary <int, string>();
                    var scheduleStartTimes = new Dictionary <int, TimeSpan>();
                    schedules
                    .ForEach(s =>
                    {
                        scheduleNames.Add(s.Id, s.Name);
                        scheduleStartTimes.Add(s.Id, s.StartTimeOfDay);
                    });

                    foreach (var occurrence in occurrenceDates.Where(o => o.Date.HasValue))
                    {
                        occurrences.Add(
                            new ScheduleOccurrence(
                                occurrence.Date.Value,
                                occurrence.ScheduleId.HasValue && scheduleStartTimes.ContainsKey(occurrence.ScheduleId.Value) ?
                                scheduleStartTimes[occurrence.ScheduleId.Value] : new TimeSpan(),
                                occurrence.ScheduleId,
                                occurrence.ScheduleId.HasValue && scheduleNames.ContainsKey(occurrence.ScheduleId.Value) ?
                                scheduleNames[occurrence.ScheduleId.Value] : string.Empty,
                                occurrence.LocationId,
                                occurrence.LocationId.HasValue && locationNames.ContainsKey(occurrence.LocationId.Value) ?
                                locationNames[occurrence.LocationId.Value] : string.Empty,
                                occurrence.LocationId.HasValue && locationPaths.ContainsKey(occurrence.LocationId.Value) ?
                                locationPaths[occurrence.LocationId.Value] : string.Empty
                                ));
                    }
                }

                // Load the attendance data for each occurrence
                if (loadSummaryData && occurrences.Any())
                {
                    var minDate       = occurrences.Min(o => o.Date);
                    var maxDate       = occurrences.Max(o => o.Date).AddDays(1);
                    var attendanceQry = attendanceService
                                        .Queryable().AsNoTracking()
                                        .Where(a =>
                                               a.GroupId.HasValue &&
                                               a.GroupId == group.Id &&
                                               a.StartDateTime >= minDate &&
                                               a.StartDateTime < maxDate &&
                                               a.PersonAlias != null &&
                                               a.PersonAliasId.HasValue)
                                        .Select(a => new
                    {
                        a.LocationId,
                        a.ScheduleId,
                        a.StartDateTime,
                        a.DidAttend,
                        a.DidNotOccur,
                        a.PersonAliasId,
                        PersonId = a.PersonAlias.PersonId
                    });

                    if (campusId.HasValue)
                    {
                        var familyGroupType = GroupTypeCache.Read(Rock.SystemGuid.GroupType.GROUPTYPE_FAMILY.AsGuid());
                        var campusQry       = new GroupMemberService(rockContext)
                                              .Queryable()
                                              .Where(g =>
                                                     g.Group != null &&
                                                     g.Group.GroupTypeId == familyGroupType.Id &&
                                                     g.Group.CampusId.HasValue &&
                                                     g.Group.CampusId.Value == campusId.Value
                                                     )
                                              .Select(m => m.PersonId);

                        attendanceQry = attendanceQry
                                        .Where(s => campusQry.Contains(s.PersonId));
                    }

                    var attendances = attendanceQry.ToList();

                    foreach (var summary in attendances
                             .GroupBy(a => new
                    {
                        a.LocationId,
                        a.ScheduleId,
                        Date = a.StartDateTime.Date
                    })
                             .Select(a => new
                    {
                        a.Key.LocationId,
                        a.Key.ScheduleId,
                        a.Key.Date,
                        DidAttendCount = a
                                         .Where(t => t.DidAttend.HasValue && t.DidAttend.Value)
                                         .Select(t => t.PersonAliasId.Value)
                                         .Distinct()
                                         .Count(),
                        DidNotOccurCount = a
                                           .Where(t => t.DidNotOccur.HasValue && t.DidNotOccur.Value)
                                           .Select(t => t.PersonAliasId.Value)
                                           .Distinct()
                                           .Count(),
                        TotalCount = a
                                     .Select(t => t.PersonAliasId)
                                     .Distinct()
                                     .Count()
                    }))
                    {
                        var occurrence = occurrences
                                         .Where(o =>
                                                o.ScheduleId.Equals(summary.ScheduleId) &&
                                                o.LocationId.Equals(summary.LocationId) &&
                                                o.Date.Equals(summary.Date))
                                         .FirstOrDefault();
                        if (occurrence != null)
                        {
                            occurrence.DidAttendCount   = summary.DidAttendCount;
                            occurrence.DidNotOccurCount = summary.DidNotOccurCount;
                            occurrence.TotalCount       = summary.TotalCount;
                        }
                    }
                }

                // Create any missing occurrences from the group's schedule (not location schedules)
                Schedule groupSchedule = null;
                if (group.ScheduleId.HasValue)
                {
                    groupSchedule = group.Schedule;
                    if (groupSchedule == null)
                    {
                        groupSchedule = new ScheduleService(rockContext).Get(group.ScheduleId.Value);
                    }
                }

                if (groupSchedule != null)
                {
                    var newOccurrences = new List <ScheduleOccurrence>();

                    var existingDates = occurrences
                                        .Where(o => o.ScheduleId.Equals(groupSchedule.Id))
                                        .Select(o => o.Date)
                                        .Distinct()
                                        .ToList();

                    var startDate = fromDateTime.HasValue ? fromDateTime.Value : RockDateTime.Today.AddMonths(-2);
                    var endDate   = toDateTime.HasValue ? toDateTime.Value : RockDateTime.Today.AddDays(1);

                    DDay.iCal.Event calEvent = groupSchedule.GetCalenderEvent();
                    if (calEvent != null)
                    {
                        // If schedule has an iCal schedule, get all the past occurrences
                        foreach (var occurrence in calEvent.GetOccurrences(startDate, endDate))
                        {
                            var scheduleOccurrence = new ScheduleOccurrence(
                                occurrence.Period.StartTime.Date, occurrence.Period.StartTime.TimeOfDay, groupSchedule.Id, groupSchedule.Name);
                            if (!existingDates.Contains(scheduleOccurrence.Date))
                            {
                                newOccurrences.Add(scheduleOccurrence);
                            }
                        }
                    }
                    else
                    {
                        // if schedule does not have an iCal, then check for weekly schedule and calculate occurrences starting with first attendance or current week
                        if (groupSchedule.WeeklyDayOfWeek.HasValue)
                        {
                            // default to start with date 2 months earlier
                            startDate = fromDateTime.HasValue ? fromDateTime.Value : RockDateTime.Today.AddMonths(-2);
                            if (existingDates.Any(d => d < startDate))
                            {
                                startDate = existingDates.Min();
                            }

                            // Back up start time to the correct day of week
                            while (startDate.DayOfWeek != groupSchedule.WeeklyDayOfWeek.Value)
                            {
                                startDate = startDate.AddDays(-1);
                            }

                            // Add the start time
                            if (groupSchedule.WeeklyTimeOfDay.HasValue)
                            {
                                startDate = startDate.Add(groupSchedule.WeeklyTimeOfDay.Value);
                            }

                            // Create occurrences up to current time
                            while (startDate < endDate)
                            {
                                if (!existingDates.Contains(startDate.Date))
                                {
                                    var scheduleOccurrence = new ScheduleOccurrence(startDate.Date, startDate.TimeOfDay, groupSchedule.Id, groupSchedule.Name);
                                    newOccurrences.Add(scheduleOccurrence);
                                }

                                startDate = startDate.AddDays(7);
                            }
                        }
                    }

                    if (newOccurrences.Any())
                    {
                        // Filter Exclusions
                        var groupType = GroupTypeCache.Read(group.GroupTypeId);
                        foreach (var exclusion in groupType.GroupScheduleExclusions)
                        {
                            if (exclusion.Start.HasValue && exclusion.End.HasValue)
                            {
                                foreach (var occurrence in newOccurrences.ToList())
                                {
                                    if (occurrence.Date >= exclusion.Start.Value &&
                                        occurrence.Date < exclusion.End.Value.AddDays(1))
                                    {
                                        newOccurrences.Remove(occurrence);
                                    }
                                }
                            }
                        }
                    }

                    foreach (var occurrence in newOccurrences)
                    {
                        occurrences.Add(occurrence);
                    }
                }
            }

            return(occurrences);
        }
        /// <summary>
        /// Gets occurrence data for the selected group
        /// </summary>
        /// <param name="group">The group.</param>
        /// <param name="loadAttendanceData">if set to <c>true</c> [load attendance data].</param>
        /// <returns></returns>
        public List <ScheduleOccurrence> GetGroupOccurrences(Group group, bool loadAttendanceData = true)
        {
            var occurrences = new List <ScheduleOccurrence>();

            if (group != null)
            {
                var rockContext = (RockContext)this.Context;

                var attendanceService = new AttendanceService(rockContext);

                var uniqueStartDates = attendanceService
                                       .Queryable().AsNoTracking()
                                       .Where(a => a.GroupId == group.Id)
                                       .Select(a => a.StartDateTime)
                                       .Distinct()
                                       .ToList()
                                       .Select(dt => dt.Date)
                                       .Distinct()
                                       .ToList();

                Schedule schedule = null;
                if (group.ScheduleId.HasValue)
                {
                    schedule = group.Schedule;
                    if (schedule == null)
                    {
                        schedule = new ScheduleService(rockContext).Get(group.ScheduleId.Value);
                    }
                }

                if (schedule != null)
                {
                    var endDate = RockDateTime.Today.AddDays(1);

                    DDay.iCal.Event calEvent = schedule.GetCalenderEvent();
                    if (calEvent != null)
                    {
                        // If schedule has an iCal schedule, get all the past occurrences
                        foreach (var occurrence in calEvent.GetOccurrences(DateTime.MinValue, endDate))
                        {
                            occurrences.Add(new ScheduleOccurrence(occurrence, schedule.Id));
                        }
                    }
                    else
                    {
                        // if schedule does not have an iCal, then check for weekly schedule and calculate occurrences starting with first attendance or current week
                        if (schedule.WeeklyDayOfWeek.HasValue)
                        {
                            // default to start with date 2 months earlier
                            DateTime startDateTime = RockDateTime.Today.AddMonths(-2);
                            if (uniqueStartDates.Any(d => d < startDateTime))
                            {
                                startDateTime = uniqueStartDates.Min();
                            }

                            // Back up start time to the correct day of week
                            while (startDateTime.DayOfWeek != schedule.WeeklyDayOfWeek.Value)
                            {
                                startDateTime = startDateTime.AddDays(-1);
                            }

                            // Add the start time
                            if (schedule.WeeklyTimeOfDay.HasValue)
                            {
                                startDateTime = startDateTime.Add(schedule.WeeklyTimeOfDay.Value);
                            }

                            // Create occurrences up to current time
                            while (startDateTime < endDate)
                            {
                                occurrences.Add(new ScheduleOccurrence(startDateTime, startDateTime.AddDays(1), schedule.Id));
                                startDateTime = startDateTime.AddDays(7);
                            }
                        }
                    }
                }

                // Add occurrences for any attendance dates that do not fall into a logical occurrence based on the schedule
                var occurrenceDates = occurrences.Select(o => o.StartDateTime.Date).ToList();
                foreach (var date in uniqueStartDates.Where(d => !occurrenceDates.Contains(d)))
                {
                    occurrences.Add(new ScheduleOccurrence(date, date.AddDays(1)));
                }

                if (occurrences.Any())
                {
                    // Filter Exclusions
                    var groupType = GroupTypeCache.Read(group.GroupTypeId);
                    foreach (var exclusion in groupType.GroupScheduleExclusions)
                    {
                        if (exclusion.Start.HasValue && exclusion.End.HasValue)
                        {
                            foreach (var occurrence in occurrences.ToList())
                            {
                                if (occurrence.StartDateTime >= exclusion.Start.Value &&
                                    occurrence.StartDateTime < exclusion.End.Value.AddDays(1))
                                {
                                    occurrences.Remove(occurrence);
                                }
                            }
                        }
                    }


                    if (loadAttendanceData)
                    {
                        var minStartValue = occurrences.Min(o => o.StartDateTime);
                        var maxEndValue   = occurrences.Max(o => o.EndDateTime);

                        var attendanceQry = attendanceService
                                            .Queryable("PersonAlias").AsNoTracking()
                                            .Where(a =>
                                                   a.GroupId == group.Id &&
                                                   a.StartDateTime >= minStartValue &&
                                                   a.StartDateTime < maxEndValue);

                        var attendanceData = attendanceQry.ToList();

                        foreach (var occurrence in occurrences)
                        {
                            occurrence.Attendance = attendanceData
                                                    .Where(a =>
                                                           a.StartDateTime >= occurrence.StartDateTime &&
                                                           a.StartDateTime < occurrence.EndDateTime)
                                                    .ToList();
                        }
                    }
                }
            }

            return(occurrences);
        }