Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change calculation for one time recurrence to consider the delay as 'full days' #769

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ public async Task<ReferralStatus> CalculateReferralStatusAsync(
ReferralEntry referralEntry)
{
var policy = await policiesResource.GetCurrentPolicy(organizationId, locationId);
var config = await policiesResource.GetConfigurationAsync(organizationId);

var location = config.Locations.Find(item => item.Id == locationId);
TimeZoneInfo locationTimeZone = location?.timeZone ?? TimeZoneInfo.FindSystemTimeZoneById("America/New_York");

return ReferralCalculations.CalculateReferralStatus(
policy.ReferralPolicy, referralEntry, DateTime.UtcNow);
policy.ReferralPolicy, referralEntry, DateTime.UtcNow, locationTimeZone);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace CareTogether.Engines.PolicyEvaluation
internal static class ReferralCalculations
{
public static ReferralStatus CalculateReferralStatus(
ReferralPolicy referralPolicy, ReferralEntry referralEntry, DateTime utcNow)
ReferralPolicy referralPolicy, ReferralEntry referralEntry, DateTime utcNow, TimeZoneInfo locationTimeZone)
{
var missingIntakeRequirements = referralPolicy.RequiredIntakeActionNames.Where(requiredAction =>
!SharedCalculations.RequirementMetOrExempted(requiredAction,
Expand All @@ -34,7 +34,7 @@ public static ReferralStatus CalculateReferralStatus(
.Single(p => p.ArrangementType == arrangement.Value.ArrangementType);

return CalculateArrangementStatus(arrangement.Value,
arrangementPolicy, utcNow);
arrangementPolicy, utcNow, locationTimeZone);
});

return new ReferralStatus(
Expand All @@ -45,12 +45,12 @@ public static ReferralStatus CalculateReferralStatus(


internal static ArrangementStatus CalculateArrangementStatus(ArrangementEntry arrangement, ArrangementPolicy arrangementPolicy,
DateTime utcNow)
DateTime utcNow, TimeZoneInfo locationTimeZone)
{
var missingSetupRequirements = CalculateMissingSetupRequirements(
arrangementPolicy, arrangement, utcNow);
var missingMonitoringRequirements = CalculateMissingMonitoringRequirements(
arrangementPolicy, arrangement, utcNow);
arrangementPolicy, arrangement, utcNow, locationTimeZone);
var missingCloseoutRequirements = CalculateMissingCloseoutRequirements(
arrangementPolicy, arrangement, utcNow);
var missingFunctionAssignments = CalculateMissingFunctionAssignments(arrangementPolicy.ArrangementFunctions,
Expand Down Expand Up @@ -160,7 +160,7 @@ internal static ImmutableList<MissingArrangementRequirement> CalculateMissingSet
}

internal static ImmutableList<MissingArrangementRequirement> CalculateMissingMonitoringRequirements(
ArrangementPolicy arrangementPolicy, ArrangementEntry arrangement, DateTime utcNow)
ArrangementPolicy arrangementPolicy, ArrangementEntry arrangement, DateTime utcNow, TimeZoneInfo locationTimeZone)
{
var arrangementLevelResults = arrangementPolicy.RequiredMonitoringActions
.SelectMany(monitoringRequirement =>
Expand All @@ -172,7 +172,7 @@ internal static ImmutableList<MissingArrangementRequirement> CalculateMissingMon
.Where(x => x.RequirementName == monitoringRequirement.ActionName)
.Select(x => x.CompletedAtUtc)
.OrderBy(x => x).ToImmutableList(),
arrangement.ChildLocationHistory, utcNow)
arrangement.ChildLocationHistory, utcNow, locationTimeZone)
: ImmutableList<DateTime>.Empty)
.Where(missingDueDate => !arrangement.ExemptedRequirements.Any(exempted =>
exempted.RequirementName == monitoringRequirement.ActionName &&
Expand Down Expand Up @@ -206,7 +206,7 @@ internal static ImmutableList<MissingArrangementRequirement> CalculateMissingMon
.Where(x => x.RequirementName == monitoringRequirement.ActionName)
.Select(x => x.CompletedAtUtc)
.OrderBy(x => x).ToImmutableList(),
arrangement.ChildLocationHistory, utcNow)
arrangement.ChildLocationHistory, utcNow, locationTimeZone)
: ImmutableList<DateTime>.Empty)
.Where(missingDueDate => !fva.ExemptedRequirements.Any(exempted =>
exempted.RequirementName == monitoringRequirement.ActionName &&
Expand Down Expand Up @@ -242,7 +242,7 @@ internal static ImmutableList<MissingArrangementRequirement> CalculateMissingMon
.Where(x => x.RequirementName == monitoringRequirement.ActionName)
.Select(x => x.CompletedAtUtc)
.OrderBy(x => x).ToImmutableList(),
arrangement.ChildLocationHistory, utcNow)
arrangement.ChildLocationHistory, utcNow, locationTimeZone)
: ImmutableList<DateTime>.Empty)
.Where(missingDueDate => !iva.ExemptedRequirements.Any(exempted =>
exempted.RequirementName == monitoringRequirement.ActionName &&
Expand All @@ -264,16 +264,55 @@ internal static ImmutableList<MissingArrangementRequirement> CalculateMissingMon
.ToImmutableList();
}

// The delay defined in the policies should be considered as "full days" delay. So a delay of 2 days means the
// requirement can be met at any time in the following 2 days after the startedAt date.
// For that reason, we convert the datetimes to the client (location) timezone during calculation,
// to avoid a completion falling in the next day for example.
internal static ImmutableList<DateTime> CalculateMissingMonitoringRequirementInstances(
RecurrencePolicy recurrence, Guid? filterToFamilyId,
DateTime arrangementStartedAtUtc, DateTime? arrangementEndedAtUtc,
ImmutableList<DateTime> completions, ImmutableSortedSet<ChildLocationHistoryEntry> childLocationHistory,
DateTime utcNow)
RecurrencePolicy recurrence, Guid? filterToFamilyId,
DateTime arrangementStartedAtUtc, DateTime? arrangementEndedAtUtc,
ImmutableList<DateTime> completions, ImmutableSortedSet<ChildLocationHistoryEntry> childLocationHistory,
DateTime utcNow, TimeZoneInfo locationTimeZone)
{
//////////////////////////////////////////////////
//TODO: Move these timezone conversions out of the ReferralCalculations class and into the policy evaluation engine,
// where they can be performed by a helper class.

// INPUTS: Given in UTC with time --> convert to location time --> extract date-only.
// OUTPUTS: Calculated in date-only --> convert to location time @ midnight --> return as UTC.

var currentLocationTime = TimeZoneInfo.ConvertTimeFromUtc(utcNow, locationTimeZone);

var arrangementStartedAtInLocationTime = TimeZoneInfo.ConvertTimeFromUtc(arrangementStartedAtUtc, locationTimeZone);
var arrangementStartedDate = DateOnly.FromDateTime(arrangementStartedAtInLocationTime);

var completionDates = completions
.Select(completionWithTime =>
{
var completionInLocationTime = TimeZoneInfo.ConvertTimeFromUtc(completionWithTime, locationTimeZone);
var completionDate = DateOnly.FromDateTime(completionInLocationTime);
return completionDate;
})
.ToImmutableList();

var today = DateOnly.FromDateTime(utcNow);

ImmutableList<DateTime> WrapWithTimeInUtc(ImmutableList<DateOnly> dates)
{
return dates.Select(date =>
{
var dateInLocationTime = new DateTime(date, TimeOnly.MinValue);
var dateTimeInUtc = TimeZoneInfo.ConvertTimeToUtc(dateInLocationTime, locationTimeZone);
return dateTimeInUtc;
}).ToImmutableList();
}
//////////////////////////////////////////////////

return recurrence switch
{
OneTimeRecurrencePolicy oneTime => CalculateMissingMonitoringRequirementInstancesForOneTimeRecurrence(
oneTime, arrangementStartedAtUtc, arrangementEndedAtUtc, completions, utcNow),
OneTimeRecurrencePolicy oneTime => WrapWithTimeInUtc(
CalculateMissingMonitoringRequirementInstancesForOneTimeRecurrence(
oneTime, arrangementStartedDate, completionDates, today)),
DurationStagesRecurrencePolicy durationStages =>
CalculateMissingMonitoringRequirementInstancesForDurationRecurrence(
durationStages, arrangementStartedAtUtc, arrangementEndedAtUtc, utcNow, completions),
Expand All @@ -290,23 +329,37 @@ internal static ImmutableList<DateTime> CalculateMissingMonitoringRequirementIns
};
}

internal static ImmutableList<DateTime> CalculateMissingMonitoringRequirementInstancesForOneTimeRecurrence(
OneTimeRecurrencePolicy recurrence, DateTime arrangementStartedAtUtc, DateTime? arrangementEndedAtUtc,
ImmutableList<DateTime> completions, DateTime utcNow)
internal static ImmutableList<DateOnly> CalculateMissingMonitoringRequirementInstancesForOneTimeRecurrence(
OneTimeRecurrencePolicy recurrence, DateOnly arrangementStartedDate,
ImmutableList<DateOnly> completions, DateOnly today)
{
if (recurrence.Delay.HasValue)
{
var dueDate = arrangementStartedAtUtc + recurrence.Delay.Value;
return completions.Any(completion => completion <= dueDate)
? []
: [dueDate];
var dueDate = arrangementStartedDate.AddDays(recurrence.Delay.Value.Days);

if (completions.Any(completion => completion <= dueDate))
{
return [];
}
else
{
return [dueDate];
}

}
else
return completions.IsEmpty
? [arrangementStartedAtUtc]
: [];
{
if (completions.IsEmpty)
{
return [arrangementStartedDate];
}
else
{
return [];
}
}
}

internal static ImmutableList<DateTime> CalculateMissingMonitoringRequirementInstancesForDurationRecurrence(
DurationStagesRecurrencePolicy recurrence,
DateTime arrangementStartedAtUtc, DateTime? arrangementEndedAtUtc, DateTime utcNow,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public sealed record OrganizationConfiguration(string OrganizationName,
public sealed record LocationConfiguration(Guid Id, string Name,
ImmutableList<string> Ethnicities, ImmutableList<string> AdultFamilyRelationships,
ImmutableList<string>? ArrangementReasons,
ImmutableList<SourcePhoneNumberConfiguration> SmsSourcePhoneNumbers);
ImmutableList<SourcePhoneNumberConfiguration> SmsSourcePhoneNumbers, TimeZoneInfo? timeZone = null);

public sealed record SourcePhoneNumberConfiguration(string SourcePhoneNumber, string Description);

Expand Down
Loading
Loading