Files
BeWoPlaner/Data/Utils/IntervalFinderHelper.cs

332 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using BeWo.Data.Entities;
using BS.Shared.Core;
using BS.Shared.Extensions;
using DevExpress.XtraScheduler;
using DevExpress.XtraScheduler.Compatibility;
using NHibernate.Criterion;
using NHibernate.SqlCommand;
namespace BeWo.Data.Utils
{
public class IntervalFinderHelper
{
public static List<DateTimeSpan> GenerateIntervalsForCriteria(DateTime intervalStart, DateTime intervalEnd, bool skipWeekends)
{
var result = new List<DateTimeSpan>();
if (intervalEnd <= intervalStart)
{
return result;
}
var start = intervalStart;
var end = start.MergeDatesByDate(intervalEnd);
while (end <= intervalEnd)
{
end = start.MergeDatesByDate(intervalEnd);
if (skipWeekends && (start.Date.DayOfWeek == DayOfWeek.Saturday || start.Date.DayOfWeek == DayOfWeek.Sunday))
{
start = start.AddDays(1);
end = start.MergeDatesByDate(intervalEnd);
continue;
}
result.AddIfNotIn(new DateTimeSpan(start, end));
start = start.AddDays(1);
end = start.MergeDatesByDate(intervalEnd);
}
return result;
}
public static List<ICriterion> CreateIntervalRecurrenceCriterias(List<DateTimeSpan> intervals, bool skipWeekends)
{
var result = new List<ICriterion>();
foreach(var interval in intervals)
{
var start = interval.StartDate;
var end = interval.EndDate;
result.AddIfNotIn(CreateRecurrenceCriterion(start, end));
}
// TODO: KGW
//for (var i = 0; i < totalDays; i++)
//{
// start = start.AddDays(i).MergeDatesByDate(startDate);
// if (skipWeekends && (start.DayOfWeek == DayOfWeek.Saturday || start.DayOfWeek == DayOfWeek.Sunday))
// {
// continue;
// }
// DateTime e;
// var end = start.MergeDatesByDate(endDate);
// while ((e = start.AddMinutes(duration)) <= end && start <= end.AddMinutes(-1 * duration))
// {
// result.AddIfNotIn(CreateRecurrenceCriterion(start, e));
// start = start.AddMinutes(intervalBuffer);
// }
//}
return result;
}
private static ICriterion CreateRecurrenceCriterion(DateTime start, DateTime end)
{
var recurrenceBetween = $"'{start:yyyy-MM-dd HH:mm:ss}' BETWEEN STR_TO_DATE(SUBSTRING(RecurrenceInfo, 24, 19), '%m/%d/%Y %H:%i:%s') AND STR_TO_DATE(SUBSTRING(RecurrenceInfo, 50, 19), '%m/%d/%Y %H:%i:%s')";
var recurrenceAfter = $"'{start:yyyy-MM-dd HH:mm:ss}' > STR_TO_DATE(SUBSTRING(RecurrenceInfo, 24, 19), '%m/%d/%Y %H:%i:%s')";
var result = Restrictions.Or(
Restrictions.And(
Restrictions.Not(Restrictions.Like(nameof(SchedulerAppointment.RecurrenceInfo), "Range", MatchMode.Anywhere)),
Restrictions.Like(nameof(SchedulerAppointment.RecurrenceInfo), "OccurrenceCount=\"10\"", MatchMode.Anywhere)),
Restrictions.Or(
Restrictions.Or(
Restrictions.And(Restrictions.Lt(nameof(SchedulerAppointment.StartDate), end), Restrictions.Ge(nameof(SchedulerAppointment.EndDate), start)),
Restrictions.Or(Restrictions.And(
Restrictions.Like(nameof(SchedulerAppointment.RecurrenceInfo), "End", MatchMode.Anywhere),
Restrictions.And(Restrictions.IsNotNull(nameof(SchedulerAppointment.RecurrenceInfo)), Expression.Sql(new SqlString(recurrenceBetween)))
), Restrictions.And(
Restrictions.And(
Restrictions.IsNotNull(nameof(SchedulerAppointment.RecurrenceInfo)),
Restrictions.Not(Restrictions.Like(nameof(SchedulerAppointment.RecurrenceInfo), "End", MatchMode.Anywhere))),
Expression.Sql(recurrenceAfter)))
),
Restrictions.And(Restrictions.Eq(nameof(SchedulerAppointment.Type), 3), Expression.Sql(new SqlString(recurrenceBetween)))));
return result;
}
public static Dictionary<DateTime, List<DateTimeSpan>> CreateFreeIntervalsDictionary(IEnumerable<DateTimeSpan> freeIntervals)
{
var result = new Dictionary<DateTime, List<DateTimeSpan>>();
var groupedIntervals = from dateTimeSpan in freeIntervals group dateTimeSpan by dateTimeSpan.StartDate.Date;
var unsortedDictionary = new Dictionary<DateTime, List<DateTimeSpan>>();
foreach (var grouping in groupedIntervals)
{
var orderedList = grouping.OrderBy(dateTimeSpan => dateTimeSpan.StartDate).ToList();
unsortedDictionary.AddOrUpdateValueInDictionary(grouping.Key, orderedList);
}
var keys = unsortedDictionary.Keys.OrderBy(key => key.Date).ToList();
foreach (var key in keys)
{
result.Add(key, unsortedDictionary[key]);
}
return result;
}
public static List<DateTimeSpan> CreateIntervals(int duration, DateTime intervalStart, DateTime intervalEnd, bool skipWeekends, int intervalBuffer)
{
var result = new List<DateTimeSpan>();
if (duration > 0 && intervalStart < intervalEnd && intervalBuffer > 10)
{
var timeSpan = intervalEnd - intervalStart;
var totalDays = Convert.ToInt32(Math.Ceiling(timeSpan.TotalDays));
for (var i = 0; i < totalDays; i++)
{
var start = intervalStart.AddDays(i).MergeDatesByDate(intervalStart);
if (skipWeekends && (start.DayOfWeek == DayOfWeek.Saturday || start.DayOfWeek == DayOfWeek.Sunday))
{
continue;
}
DateTime e;
// Beim Intervall (z.B. 04.12.2023 15:00 und 06.12.2023 12:00) wird nur zwischen Start- und Enduhrzeit geprüft.
// Startdatum+Enduhrzeit
// -> end = 04.12.2023 12:00
//var end = start.MergeDatesByDate(intervalEnd);
var end = intervalEnd;
while ((e = start.AddMinutes(duration)) <= end && start <= end.AddMinutes(-1 * duration))
{
result.Add(new DateTimeSpan(start, e));
start = start.AddMinutes(intervalBuffer);
}
}
}
return result;
}
/// <summary>
/// Liefert DateTimeSpan-Objekte für ganze Tage zwischen zwei Datumsangaben zurück.
/// Z.B. 04.12.2023 bis 06.12.2023 liefert drei DateTimeSpan für den 04.12., 05.12. und 06.12. zurück.
/// </summary>
/// <param name="intervalStart">Intervallstart</param>
/// <param name="intervalEnd">Intervallende</param>
/// <returns>Liste der DateTimeSpan-Objekte für die Anzahl der Tage im angegebenen Intervall.</returns>
public static List<DateTimeSpan> GenereateIntervalsForChecking(DateTime intervalStart, DateTime intervalEnd)
{
var start = intervalStart.Date;
var end = intervalEnd.Date;
var result = new List<DateTimeSpan>();
if (end < start)
{
return result;
}
var days = Convert.ToInt32((end - start).TotalDays);
if (days == 0)
{
days = 1;
}
var rest = days % 6;
var limit = (days - rest) / 6;
var s = start;
DateTime e;
for (var i = 0; i < limit; i++)
{
e = s.AddDays(5);
result.Add(new DateTimeSpan(s, e));
s = e.AddDays(1);
}
if (rest > 0)
{
e = s.AddDays(rest);
if (rest == 1)
{
e = e.AddDays(-1);
}
result.Add(new DateTimeSpan(s, e));
}
return result;
}
public static IEnumerable<SchedulerAppointment> CalculateRecurrences(IEnumerable<SchedulerAppointment> appointments, DateTime start, DateTime end, IReadOnlyCollection<RecurrenceInformation> changedOccurrences, IReadOnlyCollection<RecurrenceInformation> deletedOccurrences)
{
var result = new List<SchedulerAppointment>();
var interval = new TimeInterval(start, end);
foreach (var appointment in appointments.Where(app => app.RecurrenceInfo != null && app.Type == 1 && app.StartDate.HasValue && app.EndDate.HasValue))
{
var recurrenceInfo = new RecurrenceInfo();
recurrenceInfo.FromXml(appointment.RecurrenceInfo);
var occurrenceCalculator = OccurrenceCalculator.CreateInstance(recurrenceInfo);
var pattern = StaticAppointmentFactory.CreateAppointment(AppointmentType.Pattern);
pattern.RecurrenceInfo.FromXml(appointment.RecurrenceInfo);
pattern.Start = pattern.RecurrenceInfo.Start;
pattern.End = pattern.RecurrenceInfo.End;
var patternId = pattern.RecurrenceInfo.Id.ToString();
var occurrences = occurrenceCalculator.CalcOccurrences(interval, pattern);
if (occurrences.Count > 0)
{
foreach (var app in occurrences.GetAppointments(interval))
{
if(appointment.EndDate == null || appointment.StartDate == null)
{
continue;
}
var index = app.RecurrenceIndex;
var duration = (appointment.EndDate.Value - appointment.StartDate.Value).TotalMinutes;
var s = app.Start;
var e = app.Start.AddMinutes(duration);
var isOutOfInterval = s.AreInBetweenDates(e, start, end) == false;
var isChangedOccurrence = changedOccurrences.Any(a => a.PatternId.Equals(patternId) && a.Index == index);
var isDeletedOccurrence = deletedOccurrences.Any(a => a.PatternId.Equals(patternId) && a.Index == index);
if (isOutOfInterval || isChangedOccurrence || isDeletedOccurrence)
{
continue;
}
var recurringAppointment = new SchedulerAppointment
{
AllDay = appointment.AllDay,
CustomerList = appointment.CustomerList,
Notice = appointment.Notice,
EmployeeList = appointment.EmployeeList,
EndDate = app.Start.AddMinutes(duration),
FormerBookingSequenceOid = appointment.FormerBookingSequenceOid,
IsPrivate = appointment.IsPrivate,
Location = appointment.Location,
Originator = appointment.Originator,
RecurrenceInfo = app.RecurrenceInfo.ToXml(),
ReminderInfo = appointment.ReminderInfo,
ResourceList = appointment.ResourceList,
StartDate = app.Start,
Subject = appointment.Subject ?? string.Empty,
Type = appointment.Type
};
result.AddIfNotIn(recurringAppointment);
}
}
}
return result;
}
public static IEnumerable<SchedulerAppointment> GetRecurrencesForIntervalFinder(List<SchedulerAppointment> appointments, DateTime startDate, DateTime endDate, IReadOnlyCollection<RecurrenceInformation> changedOccurrences, IReadOnlyCollection<RecurrenceInformation> deletedOccurrences, bool skipWeekends)
{
var result = new List<SchedulerAppointment>();
var dayCount = Convert.ToInt32(Math.Ceiling((endDate - startDate).TotalDays));
if (dayCount <= 0)
{
return result;
}
for (var i = 0; i < dayCount; i++)
{
var start = startDate.AddDays(i);
if (skipWeekends && (start.DayOfWeek == DayOfWeek.Saturday || start.DayOfWeek == DayOfWeek.Sunday))
{
continue;
}
var end = start.MergeDatesByDate(endDate);
var calculatedRecurrences = CalculateRecurrences(appointments, start, end, changedOccurrences, deletedOccurrences);
result.AddRange(calculatedRecurrences);
}
return result;
}
}
}