Skip to content

Commit

Permalink
Merge pull request #799 from CareTogether/date-only-monitoring-requir…
Browse files Browse the repository at this point in the history
…ements

Calculate monitoring requirements as dateonly (Fixes #558)
  • Loading branch information
LarsKemmann authored Dec 4, 2024
2 parents a702425 + 232aabd commit b1f2480
Show file tree
Hide file tree
Showing 19 changed files with 1,936 additions and 1,263 deletions.
12 changes: 12 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"csharpier": {
"version": "0.29.2",
"commands": [
"dotnet-csharpier"
]
}
}
}
6 changes: 6 additions & 0 deletions .csharpierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"printWidth": 120,
"useTabs": false,
"tabWidth": 4,
"endOfLine": "auto"
}
21 changes: 21 additions & 0 deletions src/CareTogether.Core/Engines/PolicyEvaluation/ChildLocation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace CareTogether.Engines.PolicyEvaluation;

public sealed record ChildLocation(
Guid ChildLocationFamilyId,
DateOnly Date,
bool Paused // means "from now on, we stop checking for completion until resuming"
)
: IComparable<ChildLocation>
{
public int CompareTo(ChildLocation? other)
{
return other == null
? 1
: DateTime.Compare(
new DateTime(Date, new TimeOnly()),
new DateTime(other.Date, new TimeOnly())
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
using System;

//NOTE: If there are ever more than two options, IsMissing should become an enum.
public sealed record DateOfInterest(DateOnly Date, bool IsMissing);
1,156 changes: 716 additions & 440 deletions src/CareTogether.Core/Engines/PolicyEvaluation/ReferralCalculations.cs

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions src/CareTogether.Core/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,25 @@ public static async IAsyncEnumerable<U> SelectManyAsync<T, U>(this IEnumerable<T
await foreach (var result in selector(value))
yield return result;
}

public static IEnumerable<T> TakeWhilePlusOne<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
var current = enumerator.Current;
if (predicate(current))
{
yield return current;
}
else
{
yield return current;
break;
}
}
}
}
}
}
50 changes: 50 additions & 0 deletions src/Timelines/DateOnlyTimeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public DateOnlyTimeline(ImmutableList<DateRange> ranges)
}
}

public DateOnly Start =>
Ranges.First().Start;

public DateOnly End =>
Ranges.Last().End;

public static DateOnlyTimeline? UnionOf(ImmutableList<DateOnlyTimeline?> timelines)
{
Expand Down Expand Up @@ -275,6 +280,51 @@ public override int GetHashCode()
hashCode.Add(range.GetHashCode());
return hashCode.ToHashCode();
}

/// <summary>
/// Returns a new DateOnlyTimeline that includes up to the specified number of days from the start of this timeline.
/// For discontinuous timelines, this will include ranges in order until the total length is reached.
/// If the requested length is greater than or equal to the total days in this timeline, returns this timeline unchanged.
/// </summary>
/// <param name="requestedLength">The maximum number of days to include in the new timeline</param>
/// <returns>A new DateOnlyTimeline containing up to the specified number of days from the start of this timeline</returns>
/// <exception cref="ArgumentException">Thrown when requestedLength is not positive</exception>
public DateOnlyTimeline? TakeDays(int requestedLength)
{
if (requestedLength <= 0)
throw new ArgumentException("Requested length must be positive.", nameof(requestedLength));

// If total length is already within limit, return unchanged
var totalLength = Ranges.Sum(r => r.TotalDaysInclusive);
if (totalLength <= requestedLength)
return this;

var remainingTotalLength = requestedLength;
var newRanges = new List<DateRange>();

foreach (var range in Ranges)
{
if (remainingTotalLength <= 0)
break;

// Only take part of this range up to remaining length
var newRange = range.TakeDays(remainingTotalLength);
newRanges.Add(newRange);
remainingTotalLength -= newRange.TotalDaysInclusive;
}

return new DateOnlyTimeline(newRanges.ToImmutableList());
}

/// <summary>
/// Gets the total number of days in this timeline, inclusive of both the start and end dates of each range.
/// For discontinuous timelines, this is the sum of the lengths of each range.
/// </summary>
/// <returns>The total number of days in this timeline</returns>
public int TotalDaysInclusive()
{
return Ranges.Sum(range => range.TotalDaysInclusive);
}
}

public sealed class DateOnlyTimeline<T> : IEquatable<DateOnlyTimeline<T>>
Expand Down
20 changes: 20 additions & 0 deletions src/Timelines/DateRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,26 @@ public DateRange(DateOnly start, DateOnly end)
End = end;
}

/// <summary>
/// Gets the total number of days in this date range, inclusive of both the start and end dates.
/// </summary>
public int TotalDaysInclusive =>
End.DayNumber - Start.DayNumber + 1;

/// <summary>
/// Returns a new DateRange that includes up to the specified number of days from the start of this range.
/// If the requested length is greater than or equal to the total days in this range, returns this range unchanged.
/// </summary>
/// <param name="requestedLength">The maximum number of days to include in the new range</param>
/// <returns>A new DateRange containing up to the specified number of days from the start of this range</returns>
public DateRange TakeDays(int requestedLength)
{
if (TotalDaysInclusive <= requestedLength)
return this;

return new DateRange(Start, Start.AddDays(requestedLength - 1));
}


public bool Contains(DateOnly value) =>
value >= Start && value <= End;
Expand Down
12 changes: 12 additions & 0 deletions src/caretogether-pwa/src/GeneratedClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5969,6 +5969,8 @@ export interface IFamilyRoleRequirementCompletionStatus {

export class DateOnlyTimeline implements IDateOnlyTimeline {
ranges?: DateRange[];
start?: Date;
end?: Date;

constructor(data?: IDateOnlyTimeline) {
if (data) {
Expand All @@ -5986,6 +5988,8 @@ export class DateOnlyTimeline implements IDateOnlyTimeline {
for (let item of _data["ranges"])
this.ranges!.push(DateRange.fromJS(item));
}
this.start = _data["start"] ? new Date(_data["start"].toString()) : <any>undefined;
this.end = _data["end"] ? new Date(_data["end"].toString()) : <any>undefined;
}
}

Expand All @@ -6003,17 +6007,22 @@ export class DateOnlyTimeline implements IDateOnlyTimeline {
for (let item of this.ranges)
data["ranges"].push(item.toJSON());
}
data["start"] = this.start ? formatDate(this.start) : <any>undefined;
data["end"] = this.end ? formatDate(this.end) : <any>undefined;
return data;
}
}

export interface IDateOnlyTimeline {
ranges?: DateRange[];
start?: Date;
end?: Date;
}

export class DateRange implements IDateRange {
start?: Date;
end?: Date;
totalDaysInclusive?: number;

constructor(data?: IDateRange) {
if (data) {
Expand All @@ -6028,6 +6037,7 @@ export class DateRange implements IDateRange {
if (_data) {
this.start = _data["start"] ? new Date(_data["start"].toString()) : <any>undefined;
this.end = _data["end"] ? new Date(_data["end"].toString()) : <any>undefined;
this.totalDaysInclusive = _data["totalDaysInclusive"];
}
}

Expand All @@ -6042,13 +6052,15 @@ export class DateRange implements IDateRange {
data = typeof data === 'object' ? data : {};
data["start"] = this.start ? formatDate(this.start) : <any>undefined;
data["end"] = this.end ? formatDate(this.end) : <any>undefined;
data["totalDaysInclusive"] = this.totalDaysInclusive;
return data;
}
}

export interface IDateRange {
start?: Date;
end?: Date;
totalDaysInclusive?: number;
}

export class FamilyRequirementStatusDetail implements IFamilyRequirementStatusDetail {
Expand Down
12 changes: 12 additions & 0 deletions swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -3545,6 +3545,14 @@
"items": {
"$ref": "#/components/schemas/DateRange"
}
},
"start": {
"type": "string",
"format": "date"
},
"end": {
"type": "string",
"format": "date"
}
}
},
Expand All @@ -3559,6 +3567,10 @@
"end": {
"type": "string",
"format": "date"
},
"totalDaysInclusive": {
"type": "integer",
"format": "int32"
}
}
},
Expand Down
Loading

0 comments on commit b1f2480

Please sign in to comment.