feat(flutter_availability_data_interface): add implementation for apply method on availability templates

This commit is contained in:
Joey Boerwinkel 2024-07-03 10:30:54 +02:00 committed by FlutterJoey
parent a8af7fd6ac
commit 6748a2440c
2 changed files with 220 additions and 6 deletions

View file

@ -23,6 +23,8 @@ class AvailabilityTemplateModel {
this.id,
});
/// Automatically parses [templateData] based on the dynamic data map and
/// template type.
factory AvailabilityTemplateModel.fromType({
required String userId,
required String name,
@ -63,7 +65,12 @@ class AvailabilityTemplateModel {
/// The specific data for this template
final TemplateData templateData;
/// returns the [templateData] in its serialized form
Map<String, dynamic> get rawTemplateData => templateData.toMap();
/// applies the template to a range of dates
List<AvailabilityModel> apply(DateTime start, DateTime end) =>
templateData.apply(userId: userId, start: start, end: end);
}
/// Used as the key for defining week-based templates
@ -114,6 +121,7 @@ abstract interface class TemplateData {
/// Applies the current template to all days found between [start] and [end],
/// inclusive
List<AvailabilityModel> apply({
required String userId,
required DateTime start,
required DateTime end,
});
@ -147,7 +155,7 @@ class WeekTemplateData implements TemplateData {
if (thursday != null) WeekDay.thursday: thursday,
if (friday != null) WeekDay.friday: friday,
if (saturday != null) WeekDay.saturday: saturday,
if (sunday != null) WeekDay.monday: sunday,
if (sunday != null) WeekDay.sunday: sunday,
},
);
@ -188,11 +196,21 @@ class WeekTemplateData implements TemplateData {
@override
List<AvailabilityModel> apply({
required String userId,
required DateTime start,
required DateTime end,
}) {
// TODO(Joey): Implement the apply method
throw UnimplementedError();
var dates = _getDatesBetween(start, end);
return [
for (var date in dates)
if (data.containsKey(WeekDay.fromDateTime(date)))
...data[WeekDay.fromDateTime(date)]!.apply(
start: date,
end: date,
userId: userId,
),
];
}
}
@ -245,11 +263,30 @@ class DayTemplateData implements TemplateData {
@override
List<AvailabilityModel> apply({
required String userId,
required DateTime start,
required DateTime end,
}) {
// TODO(Joey): Implement the apply method
throw UnimplementedError();
var dates = _getDatesBetween(start, end);
return [
for (var date in dates) ...[
AvailabilityModel(
userId: userId,
startDate: date.mergeTime(startTime),
endDate: date.mergeTime(endTime),
breaks: [
for (var templateBreak in breaks) ...[
AvailabilityBreakModel(
startTime: date.mergeTime(templateBreak.startTime),
endTime: date.mergeTime(templateBreak.endTime),
duration: templateBreak.isTight ? null : templateBreak.duration,
),
],
],
),
],
];
}
@override
@ -263,3 +300,17 @@ class DayTemplateData implements TemplateData {
],
};
}
List<DateTime> _getDatesBetween(DateTime startDate, DateTime endDate) {
var diff = endDate.difference(startDate).inDays;
return [
for (var i = 0; i <= diff; i++) ...[
DateTime(startDate.year, startDate.month, startDate.day + i),
],
];
}
extension _MergeTime on DateTime {
DateTime mergeTime(DateTime time) =>
DateTime(year, month, day, time.hour, time.minute);
}

View file

@ -3,6 +3,126 @@ import "package:flutter_test/flutter_test.dart";
void main() {
group("AvailabilityTemplate", () {
group("Apply", () {
test("applying a week template should generate correct availability", () {
DateTime asTime(int hours, [int minutes = 0]) =>
DateTime(1, 1, 1, hours, minutes);
AvailabilityBreakModel createBreak(int startHour) =>
AvailabilityBreakModel(
startTime: asTime(startHour),
endTime: asTime(startHour + 1),
duration: const Duration(minutes: 30),
);
DayTemplateData createTemplate(int startHour) => DayTemplateData(
startTime: asTime(startHour),
endTime: asTime(startHour + 7),
breaks: [createBreak(startHour + 3)],
);
var monday = createTemplate(1);
var tuesday = createTemplate(2);
var wednesday = createTemplate(3);
var thursday = createTemplate(4);
var friday = createTemplate(5);
var saturday = createTemplate(6);
var sunday = createTemplate(7);
var sut = AvailabilityTemplateModel(
userId: "",
name: "",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(
monday: monday,
tuesday: tuesday,
wednesday: wednesday,
thursday: thursday,
friday: friday,
saturday: saturday,
sunday: sunday,
),
);
var startOfRangeToApply = DateTime(1999, 1, 1);
var endOfRangeToApply = DateTime(2024, 12, 31);
var availabilities = sut.apply(startOfRangeToApply, endOfRangeToApply);
AvailabilityModel findAvailabilityByDate(DateTime date) =>
availabilities
.where((value) => value.startDate.date == date.date)
.first;
var firstDay = startOfRangeToApply;
expect(findAvailabilityByDate(firstDay), isA<AvailabilityModel>());
var lastDay = endOfRangeToApply;
expect(findAvailabilityByDate(lastDay), isA<AvailabilityModel>());
var leapDayMillenial = DateTime(2000, 2, 29);
expect(
findAvailabilityByDate(leapDayMillenial).matchesTemplate(tuesday),
isTrue,
);
var savingsTimeSwitch = DateTime(2020, 10, 25);
expect(
findAvailabilityByDate(savingsTimeSwitch).matchesTemplate(sunday),
isTrue,
);
var dayAfterSavingsTime = DateTime(2020, 10, 26);
expect(
findAvailabilityByDate(dayAfterSavingsTime).matchesTemplate(monday),
isTrue,
);
var aMondayInJuly = DateTime(2024, 7, 8);
expect(
findAvailabilityByDate(aMondayInJuly).matchesTemplate(monday),
isTrue,
);
var aTuesdayInJuly = DateTime(2024, 7, 9);
expect(
findAvailabilityByDate(aTuesdayInJuly).matchesTemplate(tuesday),
isTrue,
);
var aWednesdayInJuly = DateTime(2024, 7, 10);
expect(
findAvailabilityByDate(aWednesdayInJuly).matchesTemplate(wednesday),
isTrue,
);
var aThursdayInJuly = DateTime(2024, 7, 11);
expect(
findAvailabilityByDate(aThursdayInJuly).matchesTemplate(thursday),
isTrue,
);
var aFridayInJuly = DateTime(2024, 7, 12);
expect(
findAvailabilityByDate(aFridayInJuly).matchesTemplate(friday),
isTrue,
);
var aSaturdayInJuly = DateTime(2024, 7, 13);
expect(
findAvailabilityByDate(aSaturdayInJuly).matchesTemplate(saturday),
isTrue,
);
var aSundayInJuly = DateTime(2024, 7, 14);
expect(
findAvailabilityByDate(aSundayInJuly).matchesTemplate(sunday),
isTrue,
);
});
});
group("Serialization", () {
test("week template should serialize and deserialize correctly", () {
var baseDate = DateTime(1994, 10, 18, 10, 0);
@ -62,3 +182,46 @@ void main() {
});
});
}
extension on AvailabilityModel {
bool matchesTemplate(DayTemplateData template) {
var startDateMatches = template.startTime.timeMatches(startDate);
var endDateMatches = template.endTime.timeMatches(endDate);
var breaksMatch = template.breaks.matches(breaks);
return startDateMatches && endDateMatches && breaksMatch;
}
}
extension on List<AvailabilityBreakModel> {
bool matches(List<AvailabilityBreakModel> other) {
if (other.length != length) {
return false;
}
for (var otherBreak in other) {
if (!any((e) => e.matches(otherBreak))) {
return false;
}
}
return true;
}
}
extension on AvailabilityBreakModel {
bool matches(AvailabilityBreakModel other) {
var startTimeMatches = other.startTime.timeMatches(startTime);
var endTimeMatches = other.endTime.timeMatches(endTime);
var durationMatches = other.duration == duration;
return startTimeMatches && endTimeMatches && durationMatches;
}
}
extension on DateTime {
DateTime get date => DateTime(year, month, day);
bool timeMatches(DateTime other) =>
other.hour == hour && other.minute == minute;
}