fix: improve template deviation logic and remove it from the flutter_availability UI files

This commit is contained in:
Freek van de Ven 2024-08-06 09:53:36 +02:00 committed by Freek van de Ven
parent 1671c90a78
commit ea6fbc4668
7 changed files with 519 additions and 185 deletions

View file

@ -240,7 +240,7 @@ class AvailabilityViewModel {
var startDate = DateTime(0, 0, 0, startTime!.hour, startTime!.minute);
var endDate = DateTime(0, 0, 0, endTime!.hour, endTime!.minute);
return template.availabilityDeviatesFromTemplate(
return template.availabilityTimeDeviatesFromTemplate(
availability,
startDate,
endDate,

View file

@ -1,7 +1,6 @@
import "package:flutter/material.dart";
import "package:flutter_availability/flutter_availability.dart";
import "package:flutter_availability/src/ui/widgets/calendar_grid.dart";
import "package:flutter_availability/src/util/availability_deviation.dart";
import "package:flutter_availability/src/util/scope.dart";
///
@ -170,11 +169,21 @@ List<CalendarDay> _mapAvailabilitiesToCalendarDays(
) =>
availabilitySnapshot.data?.map(
(availability) {
var templateIsDeviated = availability.template != null &&
isTemplateDeviated(
availability.availabilityModel,
availability.template!,
);
var availabilityModel = availability.availabilityModel;
var templateTimeIsDeviated =
availability.template?.availabilityTimeDeviatesFromTemplate(
availabilityModel,
availabilityModel.startDate,
availabilityModel.endDate,
) ??
false;
var templateBreaksAreDeviated =
availability.template?.availabilityBreaksDeviatesFromTemplate(
availabilityModel,
) ??
false;
var templateIsDeviated =
templateTimeIsDeviated || templateBreaksAreDeviated;
return CalendarDay(
date: DateUtils.dateOnly(availability.availabilityModel.startDate),
color: availability.template != null

View file

@ -48,6 +48,7 @@ class _TemplateLegendState extends State<TemplateLegend> {
false;
var templatesVisible = templatesAvailable && _templateDrawerOpen;
if (!templatesAvailable &&
!featureSet.require(AvailabilityFeature.templates)) {
return const SizedBox.shrink();
@ -193,10 +194,8 @@ class _TemplateLegendState extends State<TemplateLegend> {
const SizedBox(height: 12),
if (templatesLoading) ...[
options.loadingIndicatorBuilder(context),
] else ...[
if (templates.isEmpty) ...[
createNewTemplateButton,
],
] else if (templates.isEmpty) ...[
createNewTemplateButton,
],
],
],

View file

@ -1,24 +0,0 @@
import "package:flutter_availability/src/util/utils.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
/// Determines if the availability is deviated from the template that it is
/// based on
bool isTemplateDeviated(
AvailabilityModel availability,
AvailabilityTemplateModel template,
) {
var dayOfWeek = availability.startDate.weekday;
var templateStartDate =
template.getStartTimeForDayOfWeek(WeekDay.values[dayOfWeek - 1]);
var templateEndDate =
template.getEndTimeForDayOfWeek(WeekDay.values[dayOfWeek - 1]);
if (templateStartDate == null || templateEndDate == null) {
return true;
}
var templateIsDeviated =
!isAtSameTime(availability.startDate, templateStartDate) ||
!isAtSameTime(availability.endDate, templateEndDate);
return templateIsDeviated;
}

View file

@ -1,143 +0,0 @@
import "package:flutter_availability/src/util/availability_deviation.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
import "package:flutter_test/flutter_test.dart";
void main() {
group("isTemplateDeviated", () {
group("day templates", () {
test("returns false when availability matches the day template", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0),
endDate: DateTime(2023, 7, 9, 17, 0),
breaks: [],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Day Template",
color: 0,
templateType: AvailabilityTemplateType.day,
templateData: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [],
),
);
var result = isTemplateDeviated(availability, template);
expect(result, isFalse);
});
test("returns true when availability does not match the day template",
() {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 10, 0),
endDate: DateTime(2023, 7, 9, 18, 0),
breaks: [],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Day Template",
color: 0,
templateType: AvailabilityTemplateType.day,
templateData: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [],
),
);
var result = isTemplateDeviated(availability, template);
expect(result, isTrue);
});
});
group("week templates", () {
test("returns false when availability matches the week template", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0),
endDate: DateTime(2023, 7, 9, 17, 0),
breaks: [],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(
sunday: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [],
),
),
);
var result = isTemplateDeviated(availability, template);
expect(result, isFalse);
});
test("returns true when availability does not match the week template",
() {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 10, 0), // sunday
endDate: DateTime(2023, 7, 9, 18, 0), // sunday
breaks: [],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(
sunday: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [],
),
),
);
var result = isTemplateDeviated(availability, template);
expect(result, isTrue);
});
test(
"returns true when the template is missing the "
"day of the availability", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 10, 10, 0), // monday
endDate: DateTime(2023, 7, 10, 18, 0), // monday
breaks: [],
);
var template = const AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData(data: {}),
);
var result = isTemplateDeviated(availability, template);
expect(result, isTrue);
});
test("returns true when the template is fully null", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 10, 10, 0),
endDate: DateTime(2023, 7, 10, 18, 0),
breaks: [],
);
var template = const AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData(data: {}),
);
var result = isTemplateDeviated(availability, template);
expect(result, isTrue);
});
});
});
}

View file

@ -123,12 +123,18 @@ class AvailabilityTemplateModel {
/// check if an availability's day corresponds to the template with the given
/// [availability] and [start] and [end] dates
bool availabilityDeviatesFromTemplate(
bool availabilityTimeDeviatesFromTemplate(
AvailabilityModel availability,
DateTime start,
DateTime end,
) =>
templateData.availabilityDeviates(availability, start, end);
templateData.availabilityTimeDeviates(availability, start, end);
/// check if an availability's breaks correspond to the template
bool availabilityBreaksDeviatesFromTemplate(
AvailabilityModel availability,
) =>
templateData.availabilityBreaksDeviates(availability);
}
/// Used as the key for defining week-based templates
@ -198,12 +204,15 @@ abstract interface class TemplateData {
void validate();
/// Check if an availability's day corresponds to the template with the given
/// [availability] and [start] and [end] dates
bool availabilityDeviates(
/// [availability]
bool availabilityTimeDeviates(
AvailabilityModel availability,
DateTime start,
DateTime end,
);
/// Check if an availability's breaks correspond to the template
bool availabilityBreaksDeviates(AvailabilityModel availability);
}
/// A week based template data structure
@ -307,12 +316,12 @@ class WeekTemplateData implements TemplateData {
}
@override
bool availabilityDeviates(
bool availabilityTimeDeviates(
AvailabilityModel availability,
DateTime start,
DateTime end,
) {
var dayOfWeek = WeekDay.values[availability.startDate.weekday];
var dayOfWeek = WeekDay.values[availability.startDate.weekday - 1];
var data = _data[dayOfWeek];
if (data == null) {
// if the day of the week is not in the template, it deviates
@ -321,6 +330,17 @@ class WeekTemplateData implements TemplateData {
// compare the start and end with the template
return !start.timeMatches(data.startTime) || !end.timeMatches(data.endTime);
}
@override
bool availabilityBreaksDeviates(AvailabilityModel availability) {
var dayOfWeek = WeekDay.values[availability.startDate.weekday - 1];
var data = _data[dayOfWeek];
if (data == null) {
// if the day of the week is not in the template, it deviates
return true;
}
return checkAvailabilityBreaksDeviates(data.breaks, availability.breaks);
}
}
/// A day based template data structure
@ -452,12 +472,16 @@ class DayTemplateData implements TemplateData {
}
@override
bool availabilityDeviates(
bool availabilityTimeDeviates(
AvailabilityModel availability,
DateTime start,
DateTime end,
) =>
!start.timeMatches(startTime) || !end.timeMatches(endTime);
@override
bool availabilityBreaksDeviates(AvailabilityModel availability) =>
checkAvailabilityBreaksDeviates(breaks, availability.breaks);
}
List<DateTime> _getDatesBetween(DateTime startDate, DateTime endDate) {
@ -473,3 +497,20 @@ extension _MergeTime on DateTime {
DateTime mergeTime(DateTime time) =>
DateTime(year, month, day, time.hour, time.minute);
}
/// Checks if the availability breaks deviate from the template breaks
/// If there are any differences in the breaks, this will return true
/// If all the breaks are the same, this will return false
bool checkAvailabilityBreaksDeviates(
List<AvailabilityBreakModel> templateBreaks,
List<AvailabilityBreakModel> availabilityBreaks,
) =>
templateBreaks.length != availabilityBreaks.length ||
templateBreaks.any(
(templateBreak) => !availabilityBreaks.any(
(breakData) =>
breakData.startTime.timeMatches(templateBreak.startTime) &&
breakData.endTime.timeMatches(templateBreak.endTime) &&
breakData.submittedDuration == templateBreak.submittedDuration,
),
);

View file

@ -182,6 +182,458 @@ void main() {
);
});
});
group("availabilityTimeDeviates", () {
group("day templates", () {
test("returns false when availability matches the day template", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0),
endDate: DateTime(2023, 7, 9, 17, 0),
breaks: [],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Day Template",
color: 0,
templateType: AvailabilityTemplateType.day,
templateData: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [],
),
);
var result = template.availabilityTimeDeviatesFromTemplate(
availability,
availability.startDate,
availability.endDate,
);
expect(result, isFalse);
});
test("returns true when availability does not match the day template",
() {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 10, 0),
endDate: DateTime(2023, 7, 9, 18, 0),
breaks: [],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Day Template",
color: 0,
templateType: AvailabilityTemplateType.day,
templateData: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [],
),
);
var result = template.availabilityTimeDeviatesFromTemplate(
availability,
availability.startDate,
availability.endDate,
);
expect(result, isTrue);
});
});
group("week templates", () {
test("returns false when availability matches the week template", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0),
endDate: DateTime(2023, 7, 9, 17, 0),
breaks: [],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(
sunday: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [],
),
),
);
var result = template.availabilityTimeDeviatesFromTemplate(
availability,
availability.startDate,
availability.endDate,
);
expect(result, isFalse);
});
test("returns true when availability does not match the week template",
() {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 10, 0), // sunday
endDate: DateTime(2023, 7, 9, 18, 0), // sunday
breaks: [],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(
sunday: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [],
),
),
);
var result = template.availabilityTimeDeviatesFromTemplate(
availability,
availability.startDate,
availability.endDate,
);
expect(result, isTrue);
});
test(
"returns true when the template is missing the "
"day of the availability", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 10, 10, 0), // monday
endDate: DateTime(2023, 7, 10, 18, 0), // monday
breaks: [],
);
var template = const AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData(data: {}),
);
var result = template.availabilityTimeDeviatesFromTemplate(
availability,
availability.startDate,
availability.endDate,
);
expect(result, isTrue);
});
test("returns true when the template is fully null", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 10, 10, 0),
endDate: DateTime(2023, 7, 10, 18, 0),
breaks: [],
);
var template = const AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData(data: {}),
);
var result = template.availabilityTimeDeviatesFromTemplate(
availability,
availability.startDate,
availability.endDate,
);
expect(result, isTrue);
});
});
});
});
group("availabilityBreaksDeviates", () {
group("day templates", () {
test("returns false when availability matches the day template breaks",
() {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0),
endDate: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 13, 0),
),
],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Day Template",
color: 0,
templateType: AvailabilityTemplateType.day,
templateData: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 13, 0),
),
],
),
);
var result =
template.availabilityBreaksDeviatesFromTemplate(availability);
expect(result, isFalse);
});
test(
"returns true when availability breaks do not match the day template",
() {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0),
endDate: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 13, 0),
),
],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Day Template",
color: 0,
templateType: AvailabilityTemplateType.day,
templateData: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 30),
endTime: DateTime(2023, 7, 9, 13, 30),
),
],
),
);
var result =
template.availabilityBreaksDeviatesFromTemplate(availability);
expect(result, isTrue);
});
test(
"returns true when availability has more breaks than "
"the day template", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0),
endDate: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 12, 30),
),
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 15, 0),
endTime: DateTime(2023, 7, 9, 15, 30),
),
],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Day Template",
color: 0,
templateType: AvailabilityTemplateType.day,
templateData: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 12, 30),
),
],
),
);
var result =
template.availabilityBreaksDeviatesFromTemplate(availability);
expect(result, isTrue);
});
test(
"returns true when availability has fewer breaks than "
"the day template", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0),
endDate: DateTime(2023, 7, 9, 17, 0),
breaks: [],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Day Template",
color: 0,
templateType: AvailabilityTemplateType.day,
templateData: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 12, 30),
),
],
),
);
var result =
template.availabilityBreaksDeviatesFromTemplate(availability);
expect(result, isTrue);
});
});
group("week templates", () {
test("returns false when availability matches the week template breaks",
() {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0), // Sunday
endDate: DateTime(2023, 7, 9, 17, 0), // Sunday
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 13, 0),
),
],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(
sunday: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 13, 0),
),
],
),
),
);
var result =
template.availabilityBreaksDeviatesFromTemplate(availability);
expect(result, isFalse);
});
test(
"returns true when availability breaks do not match the "
"week template", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0), // Sunday
endDate: DateTime(2023, 7, 9, 17, 0), // Sunday
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 13, 0),
),
],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(
sunday: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 30),
endTime: DateTime(2023, 7, 9, 13, 30),
),
],
),
),
);
var result =
template.availabilityBreaksDeviatesFromTemplate(availability);
expect(result, isTrue);
});
test(
"returns true when availability has more breaks than the "
"week template", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0), // Sunday
endDate: DateTime(2023, 7, 9, 17, 0), // Sunday
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 12, 30),
),
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 15, 0),
endTime: DateTime(2023, 7, 9, 15, 30),
),
],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(
sunday: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 12, 30),
),
],
),
),
);
var result =
template.availabilityBreaksDeviatesFromTemplate(availability);
expect(result, isTrue);
});
test(
"returns true when availability has fewer breaks than "
"the week template", () {
var availability = AvailabilityModel(
userId: "user1",
startDate: DateTime(2023, 7, 9, 9, 0), // Sunday
endDate: DateTime(2023, 7, 9, 17, 0), // Sunday
breaks: [],
);
var template = AvailabilityTemplateModel(
userId: "user1",
name: "Week Template",
color: 0,
templateType: AvailabilityTemplateType.week,
templateData: WeekTemplateData.forDays(
sunday: DayTemplateData(
startTime: DateTime(2023, 7, 9, 9, 0),
endTime: DateTime(2023, 7, 9, 17, 0),
breaks: [
AvailabilityBreakModel(
startTime: DateTime(2023, 7, 9, 12, 0),
endTime: DateTime(2023, 7, 9, 12, 30),
),
],
),
),
);
var result =
template.availabilityBreaksDeviatesFromTemplate(availability);
expect(result, isTrue);
});
});
});
}