From 5815e38fa75956de6715569967afe7e7fc8dcd43 Mon Sep 17 00:00:00 2001 From: Benjamin Eckstein <13351939+benjamineckstein@users.noreply.github.com> Date: Wed, 5 Feb 2020 11:58:57 +0100 Subject: [PATCH] TSK-991: Fix Architecturtest and split DaysToWorkingDays converter for report and common --- .../util/DaysToWorkingDaysConverter.java | 183 ++++++++++ .../internal/DaysToWorkingDaysConverter.java | 327 ------------------ .../DaysToWorkingDaysReportConverter.java | 197 +++++++++++ .../TimeIntervalReportBuilderImpl.java | 4 +- .../DaysToWorkingDaysPreProcessor.java | 7 +- .../task/internal/TaskServiceImpl.java | 8 +- .../UpdateClassificationAccTest.java | 8 +- .../acceptance/task/CreateTaskAccTest.java | 8 +- .../task/UpdateTaskAttachmentsAccTest.java | 20 +- .../java/pro/taskana/ArchitectureTest.java | 3 +- .../util}/DaysToWorkingDaysConverterTest.java | 66 ++-- .../DaysToWorkingDaysReportConverterTest.java | 309 +++++++++++++++++ 12 files changed, 741 insertions(+), 399 deletions(-) create mode 100644 lib/taskana-core/src/main/java/pro/taskana/common/internal/util/DaysToWorkingDaysConverter.java delete mode 100644 lib/taskana-core/src/main/java/pro/taskana/report/internal/DaysToWorkingDaysConverter.java create mode 100644 lib/taskana-core/src/main/java/pro/taskana/report/internal/DaysToWorkingDaysReportConverter.java rename lib/taskana-core/src/test/java/pro/taskana/{report/internal => common/internal/util}/DaysToWorkingDaysConverterTest.java (91%) create mode 100644 lib/taskana-core/src/test/java/pro/taskana/report/internal/DaysToWorkingDaysReportConverterTest.java diff --git a/lib/taskana-core/src/main/java/pro/taskana/common/internal/util/DaysToWorkingDaysConverter.java b/lib/taskana-core/src/main/java/pro/taskana/common/internal/util/DaysToWorkingDaysConverter.java new file mode 100644 index 000000000..6aeffb9d5 --- /dev/null +++ b/lib/taskana-core/src/main/java/pro/taskana/common/internal/util/DaysToWorkingDaysConverter.java @@ -0,0 +1,183 @@ +package pro.taskana.common.internal.util; + +import static java.time.temporal.ChronoUnit.DAYS; + +import java.time.DayOfWeek; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import pro.taskana.common.api.exceptions.InvalidArgumentException; + +/** + * The DaysToWorkingDaysConverter provides a method to convert an age in days into an age in working + * days. + */ +public final class DaysToWorkingDaysConverter { + + private static boolean germanHolidaysEnabled; + private static Set customHolidays = new HashSet<>(); + private Instant referenceDate; + private LocalDate easterSunday; + + private DaysToWorkingDaysConverter(Instant referenceDate) { + easterSunday = + getEasterSunday(LocalDateTime.ofInstant(referenceDate, ZoneId.systemDefault()).getYear()); + this.referenceDate = referenceDate; + } + + public Instant getReferenceDate() { + return referenceDate; + } + + /** + * Initializes the DaysToWorkingDaysConverter for the current day. + * + * @return an instance of the DaysToWorkingDaysConverter + * @throws InvalidArgumentException thrown if columnHeaders is null + */ + public static DaysToWorkingDaysConverter initialize() throws InvalidArgumentException { + return initialize(Instant.now()); + } + + /** + * Initializes the DaysToWorkingDaysConverter for a referenceDate. + * + * @param referenceDate a {@link Instant} that represents the current day of the table + * @return an instance of the DaysToWorkingDaysConverter + * @throws InvalidArgumentException thrown if columnHeaders or referenceDate is null + */ + public static DaysToWorkingDaysConverter initialize(Instant referenceDate) + throws InvalidArgumentException { + + if (referenceDate == null) { + throw new InvalidArgumentException("ReferenceDate can´t be used as NULL-Parameter"); + } + + return new DaysToWorkingDaysConverter(referenceDate); + } + + public static void setGermanPublicHolidaysEnabled(boolean germanPublicHolidaysEnabled) { + germanHolidaysEnabled = germanPublicHolidaysEnabled; + } + + public static void setCustomHolidays(List holidays) { + customHolidays = new HashSet<>(holidays == null ? Collections.emptyList() : holidays); + } + + public long convertWorkingDaysToDays(Instant startTime, long numberOfDays) { + int direction = numberOfDays >= 0 ? 1 : -1; + long limit = Math.abs(numberOfDays); + return LongStream.iterate(0, i -> i + direction) + .filter(day -> isWorkingDay(day, startTime)) + .skip(limit) + .findFirst() + .orElse(0); + } + + /** + * Computes the date of Easter Sunday for a given year. + * + * @param year for which the date of Easter Sunday should be calculated + * @return the date of Easter Sunday for the given year + */ + static LocalDate getEasterSunday(int year) { + // Formula to compute Easter Sunday by Gauss. + int a = year % 19; + int b = year % 4; + int c = year % 7; + int k = year / 100; + int p = (13 + 8 * k) / 25; + int q = k / 4; + int m = (15 - p + k - q) % 30; + int n = (4 + k - q) % 7; + int d = (19 * a + m) % 30; + + int e = (2 * b + 4 * c + 6 * d + n) % 7; + + if (d == 29 && e == 6) { + return LocalDate.of(year, 3, 15).plusDays(d + e); + } + if (d == 28 && e == 6 && (11 * m + 11) % 30 < 19) { + return LocalDate.of(year, 3, 15).plusDays(d + e); + } + return LocalDate.of(year, 3, 22).plusDays(d + e); + } + + public boolean isWorkingDay(long day, Instant referenceDate) { + LocalDateTime dateToCheck = + LocalDateTime.ofInstant(referenceDate, ZoneId.systemDefault()).plusDays(day); + + return !isWeekend(dateToCheck) && !isHoliday(dateToCheck.toLocalDate()); + } + + public boolean isWeekend(LocalDateTime dateToCheck) { + return dateToCheck.getDayOfWeek().equals(DayOfWeek.SATURDAY) + || dateToCheck.getDayOfWeek().equals(DayOfWeek.SUNDAY); + } + + public boolean isHoliday(LocalDate date) { + if (germanHolidaysEnabled && isGermanHoliday(date)) { + return true; + } + // Custom holidays that can be configured in the TaskanaEngineConfiguration + return customHolidays.contains(date); + } + + public boolean isGermanHoliday(LocalDate date) { + // Fix and movable holidays that are valid throughout Germany: New years day, Labour Day, Day of + // German + // Unity, Christmas, + if (Stream.of(GermanFixHolidays.values()).anyMatch(day -> day.matches(date))) { + return true; + } + + // Easter holidays Good Friday, Easter Monday, Ascension Day, Whit Monday. + long diffFromEasterSunday = DAYS.between(easterSunday, date); + long goodFriday = -2; + long easterMonday = 1; + long ascensionDay = 39; + long whitMonday = 50; + + return LongStream.of(goodFriday, easterMonday, ascensionDay, whitMonday) + .anyMatch(diff -> diff == diffFromEasterSunday); + } + + @Override + public String toString() { + return "DaysToWorkingDaysConverter{" + + "dateCreated=" + + referenceDate + + ", easterSunday=" + + easterSunday + + '}'; + } + + /** Enumeration of German holidays. */ + private enum GermanFixHolidays { + NEWYEAR(1, 1), + LABOURDAY(5, 1), + GERMANUNITY(10, 3), + CHRISTMAS1(12, 25), + CHRISTMAS2(12, 26); + + private int month; + private int day; + + GermanFixHolidays(int month, int day) { + this.month = month; + this.day = day; + } + + public boolean matches(LocalDate date) { + return date.getDayOfMonth() == day && date.getMonthValue() == month; + } + } +} diff --git a/lib/taskana-core/src/main/java/pro/taskana/report/internal/DaysToWorkingDaysConverter.java b/lib/taskana-core/src/main/java/pro/taskana/report/internal/DaysToWorkingDaysConverter.java deleted file mode 100644 index a1a76689f..000000000 --- a/lib/taskana-core/src/main/java/pro/taskana/report/internal/DaysToWorkingDaysConverter.java +++ /dev/null @@ -1,327 +0,0 @@ -package pro.taskana.report.internal; - -import static java.time.temporal.ChronoUnit.DAYS; - -import java.time.DayOfWeek; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.LongStream; -import java.util.stream.Stream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import pro.taskana.common.api.exceptions.InvalidArgumentException; -import pro.taskana.common.internal.util.LoggerUtils; -import pro.taskana.report.internal.header.TimeIntervalColumnHeader; - -/** - * The DaysToWorkingDaysConverter provides a method to convert an age in days into an age in working - * days. Before the method convertDaysToWorkingDays() can be used, the DaysToWorkingDaysConverter - * has to be initialized. For a list of {@link TimeIntervalColumnHeader}s the converter creates a - * "table" with integer that represents the age in days from the largest lower limit until the - * smallest upper limit of the timeIntervalColumnHeaders. This table is valid for a whole day until - * the converter is initialized with bigger limits. - */ -public final class DaysToWorkingDaysConverter { - - private static final Logger LOGGER = LoggerFactory.getLogger(TaskMonitorServiceImpl.class); - private static boolean germanHolidaysEnabled; - private static Set customHolidays = new HashSet<>(); - private List positiveDaysToWorkingDays; - private List negativeDaysToWorkingDays; - private Instant dateCreated; - private LocalDate easterSunday; - - private DaysToWorkingDaysConverter( - List columnHeaders, Instant referenceDate) { - easterSunday = - getEasterSunday(LocalDateTime.ofInstant(referenceDate, ZoneId.systemDefault()).getYear()); - dateCreated = referenceDate; - positiveDaysToWorkingDays = generatePositiveDaysToWorkingDays(columnHeaders, referenceDate); - negativeDaysToWorkingDays = generateNegativeDaysToWorkingDays(columnHeaders, referenceDate); - } - - public static DaysToWorkingDaysConverter initialize() throws InvalidArgumentException { - return initialize(Collections.singletonList(new TimeIntervalColumnHeader(0)), Instant.now()); - } - - /** - * Initializes the DaysToWorkingDaysConverter for a list of {@link TimeIntervalColumnHeader}s and - * the current day. A new table is only created if there are bigger limits or the date has - * changed. - * - * @param columnHeaders a list of {@link TimeIntervalColumnHeader}s that determines the size of - * the table - * @return an instance of the DaysToWorkingDaysConverter - * @throws InvalidArgumentException thrown if columnHeaders is null - */ - public static DaysToWorkingDaysConverter initialize( - List columnHeaders) throws InvalidArgumentException { - return initialize(columnHeaders, Instant.now()); - } - - /** - * Initializes the DaysToWorkingDaysConverter for a list of {@link TimeIntervalColumnHeader}s and - * a referenceDate. A new table is only created if there are bigger limits or the date has - * changed. - * - * @param columnHeaders a list of {@link TimeIntervalColumnHeader}s that determines the size of - * the table - * @param referenceDate a {@link Instant} that represents the current day of the table - * @return an instance of the DaysToWorkingDaysConverter - * @throws InvalidArgumentException thrown if columnHeaders or referenceDate is null - */ - public static DaysToWorkingDaysConverter initialize( - List columnHeaders, Instant referenceDate) - throws InvalidArgumentException { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug( - "Initialize DaysToWorkingDaysConverter with columnHeaders: {}", - LoggerUtils.listToString(columnHeaders)); - } - if (columnHeaders == null) { - throw new InvalidArgumentException( - "TimeIntervalColumnHeaders can´t be used as NULL-Parameter"); - } - if (referenceDate == null) { - throw new InvalidArgumentException("ReferenceDate can´t be used as NULL-Parameter"); - } - - return new DaysToWorkingDaysConverter(columnHeaders, referenceDate); - } - - public static void setGermanPublicHolidaysEnabled(boolean germanPublicHolidaysEnabled) { - germanHolidaysEnabled = germanPublicHolidaysEnabled; - } - - public static void setCustomHolidays(List holidays) { - customHolidays = new HashSet<>(holidays == null ? Collections.emptyList() : holidays); - } - - /** - * Converts an integer, that represents the age in days, to the age in working days by using the - * table that was created by initialization. If the age in days is beyond the limits of the table, - * the integer will be returned unchanged. - * - * @param ageInDays represents the age in days - * @return the age in working days - */ - public int convertDaysToWorkingDays(int ageInDays) { - - int minDay = -(negativeDaysToWorkingDays.size() - 1); - int maxDay = positiveDaysToWorkingDays.size() - 1; - - if (ageInDays >= minDay && ageInDays <= 0) { - return negativeDaysToWorkingDays.get(-ageInDays); - } - if (ageInDays > 0 && ageInDays <= maxDay) { - return positiveDaysToWorkingDays.get(ageInDays); - } - - return ageInDays; - } - - /** - * Converts an integer, that represents the age in working days, to the age in days by using the - * table that was created by initialization. Because one age in working days could match to more - * than one age in days, the return value is a list of all days that match to the input parameter. - * If the age in working days is beyond the limits of the table, the integer will be returned - * unchanged. - * - * @param ageInWorkingDays represents the age in working days - * @return a list of age in days - */ - public ArrayList convertWorkingDaysToDays(int ageInWorkingDays) { - - ArrayList list = new ArrayList<>(); - - int minWorkingDay = negativeDaysToWorkingDays.get(negativeDaysToWorkingDays.size() - 1); - int maxWorkingDay = positiveDaysToWorkingDays.get(positiveDaysToWorkingDays.size() - 1); - - if (ageInWorkingDays >= minWorkingDay && ageInWorkingDays < 0) { - for (int ageInDays = 0; ageInDays < negativeDaysToWorkingDays.size(); ageInDays++) { - if (negativeDaysToWorkingDays.get(ageInDays) == ageInWorkingDays) { - list.add(-ageInDays); - } - } - return list; - } - if (ageInWorkingDays > 0 && ageInWorkingDays <= maxWorkingDay) { - for (int ageInDays = 0; ageInDays < positiveDaysToWorkingDays.size(); ageInDays++) { - if (positiveDaysToWorkingDays.get(ageInDays) == ageInWorkingDays) { - list.add(ageInDays); - } - } - return list; - } - - if (ageInWorkingDays == 0) { - list.add(0); - for (int ageInDays = 1; ageInDays < positiveDaysToWorkingDays.size(); ageInDays++) { - if (positiveDaysToWorkingDays.get(ageInDays) == ageInWorkingDays) { - list.add(ageInDays); - } - } - for (int ageInDays = 1; ageInDays < negativeDaysToWorkingDays.size(); ageInDays++) { - if (negativeDaysToWorkingDays.get(ageInDays) == ageInWorkingDays) { - list.add(-ageInDays); - } - } - return list; - } - - // If ageInWorkingDays is beyond the limits of the table, the value is returned unchanged. - list.add(ageInWorkingDays); - return list; - } - - public long convertWorkingDaysToDays(Instant startTime, long numberOfDays) { - int direction = numberOfDays >= 0 ? 1 : -1; - long limit = Math.abs(numberOfDays); - return LongStream.iterate(0, i -> i + direction) - .filter(day -> isWorkingDay(day, startTime)) - .skip(limit) - .findFirst() - .orElse(0); - } - - /** - * Computes the date of Easter Sunday for a given year. - * - * @param year for which the date of Easter Sunday should be calculated - * @return the date of Easter Sunday for the given year - */ - static LocalDate getEasterSunday(int year) { - // Formula to compute Easter Sunday by Gauss. - int a = year % 19; - int b = year % 4; - int c = year % 7; - int k = year / 100; - int p = (13 + 8 * k) / 25; - int q = k / 4; - int m = (15 - p + k - q) % 30; - int n = (4 + k - q) % 7; - int d = (19 * a + m) % 30; - - int e = (2 * b + 4 * c + 6 * d + n) % 7; - - if (d == 29 && e == 6) { - return LocalDate.of(year, 3, 15).plusDays(d + e); - } - if (d == 28 && e == 6 && (11 * m + 11) % 30 < 19) { - return LocalDate.of(year, 3, 15).plusDays(d + e); - } - return LocalDate.of(year, 3, 22).plusDays(d + e); - } - - private List generateNegativeDaysToWorkingDays( - List columnHeaders, Instant referenceDate) { - int minUpperLimit = TimeIntervalColumnHeader.getSmallestUpperLimit(columnHeaders); - - List daysToWorkingDays = new ArrayList<>(); - daysToWorkingDays.add(0); - int day = -1; - int workingDay = 0; - while (workingDay > minUpperLimit) { - workingDay -= (isWorkingDay(day--, referenceDate)) ? 1 : 0; - daysToWorkingDays.add(workingDay); - } - return daysToWorkingDays; - } - - private List generatePositiveDaysToWorkingDays( - List columnHeaders, Instant referenceDate) { - int maxLowerLimit = TimeIntervalColumnHeader.getLargestLowerLimit(columnHeaders); - ArrayList daysToWorkingDays = new ArrayList<>(); - daysToWorkingDays.add(0); - - int day = 1; - int workingDay = 0; - while (workingDay < maxLowerLimit) { - workingDay += (isWorkingDay(day++, referenceDate)) ? 1 : 0; - daysToWorkingDays.add(workingDay); - } - return daysToWorkingDays; - } - - private boolean isWorkingDay(long day, Instant referenceDate) { - LocalDateTime dateToCheck = - LocalDateTime.ofInstant(referenceDate, ZoneId.systemDefault()).plusDays(day); - - return !isWeekend(dateToCheck) && !isHoliday(dateToCheck.toLocalDate()); - } - - private boolean isWeekend(LocalDateTime dateToCheck) { - return dateToCheck.getDayOfWeek().equals(DayOfWeek.SATURDAY) - || dateToCheck.getDayOfWeek().equals(DayOfWeek.SUNDAY); - } - - private boolean isHoliday(LocalDate date) { - if (germanHolidaysEnabled && isGermanHoliday(date)) { - return true; - } - // Custom holidays that can be configured in the TaskanaEngineConfiguration - return customHolidays.contains(date); - } - - private boolean isGermanHoliday(LocalDate date) { - // Fix and movable holidays that are valid throughout Germany: New years day, Labour Day, Day of - // German - // Unity, Christmas, - if (Stream.of(GermanFixHolidays.values()).anyMatch(day -> day.matches(date))) { - return true; - } - - // Easter holidays Good Friday, Easter Monday, Ascension Day, Whit Monday. - long diffFromEasterSunday = DAYS.between(easterSunday, date); - long goodFriday = -2; - long easterMonday = 1; - long ascensionDay = 39; - long whitMonday = 50; - - return LongStream.of(goodFriday, easterMonday, ascensionDay, whitMonday) - .anyMatch(diff -> diff == diffFromEasterSunday); - } - - @Override - public String toString() { - return "DaysToWorkingDaysConverter{" - + "positiveDaysToWorkingDays=" - + positiveDaysToWorkingDays - + ", negativeDaysToWorkingDays=" - + negativeDaysToWorkingDays - + ", dateCreated=" - + dateCreated - + ", easterSunday=" - + easterSunday - + '}'; - } - - /** Enumeration of German holidays. */ - private enum GermanFixHolidays { - NEWYEAR(1, 1), - LABOURDAY(5, 1), - GERMANUNITY(10, 3), - CHRISTMAS1(12, 25), - CHRISTMAS2(12, 26); - - private int month; - private int day; - - GermanFixHolidays(int month, int day) { - this.month = month; - this.day = day; - } - - public boolean matches(LocalDate date) { - return date.getDayOfMonth() == day && date.getMonthValue() == month; - } - } -} diff --git a/lib/taskana-core/src/main/java/pro/taskana/report/internal/DaysToWorkingDaysReportConverter.java b/lib/taskana-core/src/main/java/pro/taskana/report/internal/DaysToWorkingDaysReportConverter.java new file mode 100644 index 000000000..2940faafe --- /dev/null +++ b/lib/taskana-core/src/main/java/pro/taskana/report/internal/DaysToWorkingDaysReportConverter.java @@ -0,0 +1,197 @@ +package pro.taskana.report.internal; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import pro.taskana.common.api.exceptions.InvalidArgumentException; +import pro.taskana.common.internal.util.DaysToWorkingDaysConverter; +import pro.taskana.common.internal.util.LoggerUtils; +import pro.taskana.report.internal.header.TimeIntervalColumnHeader; + +/** + * The DaysToWorkingDaysReportConverter provides a method to convert an age in days into an age in + * working days. Before the method convertDaysToWorkingDays() can be used, the + * DaysToWorkingDaysConverter has to be initialized. For a list of {@link TimeIntervalColumnHeader}s + * the converter creates a "table" with integer that represents the age in days from the largest + * lower limit until the smallest upper limit of the timeIntervalColumnHeaders. This table is valid + * for a whole day until the converter is initialized with bigger limits. + */ +public class DaysToWorkingDaysReportConverter { + + private static final Logger LOGGER = + LoggerFactory.getLogger(DaysToWorkingDaysReportConverter.class); + private List positiveDaysToWorkingDays; + private List negativeDaysToWorkingDays; + private DaysToWorkingDaysConverter daysToWorkingDaysConverter; + + DaysToWorkingDaysReportConverter( + List columnHeaders, + DaysToWorkingDaysConverter daysToWorkingDaysConverter) { + + this.daysToWorkingDaysConverter = daysToWorkingDaysConverter; + positiveDaysToWorkingDays = + generatePositiveDaysToWorkingDays( + columnHeaders, daysToWorkingDaysConverter.getReferenceDate()); + negativeDaysToWorkingDays = + generateNegativeDaysToWorkingDays( + columnHeaders, daysToWorkingDaysConverter.getReferenceDate()); + } + + public static DaysToWorkingDaysReportConverter initialize( + List columnHeaders) throws InvalidArgumentException { + return initialize(columnHeaders, Instant.now()); + } + + /** + * Initializes the DaysToWorkingDaysConverter for a list of {@link TimeIntervalColumnHeader}s and + * a referenceDate. A new table is only created if there are bigger limits or the date has + * changed. + * + * @param columnHeaders a list of {@link TimeIntervalColumnHeader}s that determines the size of + * the table + * @param referenceDate a {@link Instant} that represents the current day of the table + * @return an instance of the DaysToWorkingDaysConverter + * @throws InvalidArgumentException thrown if columnHeaders or referenceDate is null + */ + public static DaysToWorkingDaysReportConverter initialize( + List columnHeaders, Instant referenceDate) + throws InvalidArgumentException { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug( + "Initialize DaysToWorkingDaysConverter with columnHeaders: {}", + LoggerUtils.listToString(columnHeaders)); + } + if (columnHeaders == null) { + throw new InvalidArgumentException( + "TimeIntervalColumnHeaders can´t be used as NULL-Parameter"); + } + if (referenceDate == null) { + throw new InvalidArgumentException("ReferenceDate can´t be used as NULL-Parameter"); + } + DaysToWorkingDaysConverter daysToWorkingDaysConverter = + DaysToWorkingDaysConverter.initialize(referenceDate); + + return new DaysToWorkingDaysReportConverter(columnHeaders, daysToWorkingDaysConverter); + } + + protected List generateNegativeDaysToWorkingDays( + List columnHeaders, Instant referenceDate) { + int minUpperLimit = TimeIntervalColumnHeader.getSmallestUpperLimit(columnHeaders); + + List daysToWorkingDays = new ArrayList<>(); + daysToWorkingDays.add(0); + int day = -1; + int workingDay = 0; + while (workingDay > minUpperLimit) { + workingDay -= (daysToWorkingDaysConverter.isWorkingDay(day--, referenceDate)) ? 1 : 0; + daysToWorkingDays.add(workingDay); + } + return daysToWorkingDays; + } + + protected List generatePositiveDaysToWorkingDays( + List columnHeaders, Instant referenceDate) { + int maxLowerLimit = TimeIntervalColumnHeader.getLargestLowerLimit(columnHeaders); + ArrayList daysToWorkingDays = new ArrayList<>(); + daysToWorkingDays.add(0); + + int day = 1; + int workingDay = 0; + while (workingDay < maxLowerLimit) { + workingDay += (daysToWorkingDaysConverter.isWorkingDay(day++, referenceDate)) ? 1 : 0; + daysToWorkingDays.add(workingDay); + } + return daysToWorkingDays; + } + + /** + * Converts an integer, that represents the age in days, to the age in working days by using the + * table that was created by initialization. If the age in days is beyond the limits of the table, + * the integer will be returned unchanged. + * + * @param ageInDays represents the age in days + * @return the age in working days + */ + public int convertDaysToWorkingDays(int ageInDays) { + + int minDay = -(negativeDaysToWorkingDays.size() - 1); + int maxDay = positiveDaysToWorkingDays.size() - 1; + + if (ageInDays >= minDay && ageInDays <= 0) { + return negativeDaysToWorkingDays.get(-ageInDays); + } + if (ageInDays > 0 && ageInDays <= maxDay) { + return positiveDaysToWorkingDays.get(ageInDays); + } + + return ageInDays; + } + + /** + * Converts an integer, that represents the age in working days, to the age in days by using the + * table that was created by initialization. Because one age in working days could match to more + * than one age in days, the return value is a list of all days that match to the input parameter. + * If the age in working days is beyond the limits of the table, the integer will be returned + * unchanged. + * + * @param ageInWorkingDays represents the age in working days + * @return a list of age in days + */ + public ArrayList convertWorkingDaysToDays(int ageInWorkingDays) { + + ArrayList list = new ArrayList<>(); + + int minWorkingDay = negativeDaysToWorkingDays.get(negativeDaysToWorkingDays.size() - 1); + int maxWorkingDay = positiveDaysToWorkingDays.get(positiveDaysToWorkingDays.size() - 1); + + if (ageInWorkingDays >= minWorkingDay && ageInWorkingDays < 0) { + for (int ageInDays = 0; ageInDays < negativeDaysToWorkingDays.size(); ageInDays++) { + if (negativeDaysToWorkingDays.get(ageInDays) == ageInWorkingDays) { + list.add(-ageInDays); + } + } + return list; + } + if (ageInWorkingDays > 0 && ageInWorkingDays <= maxWorkingDay) { + for (int ageInDays = 0; ageInDays < positiveDaysToWorkingDays.size(); ageInDays++) { + if (positiveDaysToWorkingDays.get(ageInDays) == ageInWorkingDays) { + list.add(ageInDays); + } + } + return list; + } + + if (ageInWorkingDays == 0) { + list.add(0); + for (int ageInDays = 1; ageInDays < positiveDaysToWorkingDays.size(); ageInDays++) { + if (positiveDaysToWorkingDays.get(ageInDays) == ageInWorkingDays) { + list.add(ageInDays); + } + } + for (int ageInDays = 1; ageInDays < negativeDaysToWorkingDays.size(); ageInDays++) { + if (negativeDaysToWorkingDays.get(ageInDays) == ageInWorkingDays) { + list.add(-ageInDays); + } + } + return list; + } + + // If ageInWorkingDays is beyond the limits of the table, the value is returned unchanged. + list.add(ageInWorkingDays); + return list; + } + + @Override + public String toString() { + return "DaysToWorkingDaysReportConverter [positiveDaysToWorkingDays=" + + positiveDaysToWorkingDays + + ", negativeDaysToWorkingDays=" + + negativeDaysToWorkingDays + + ", daysToWorkingDaysConverter=" + + daysToWorkingDaysConverter + + "]"; + } +} diff --git a/lib/taskana-core/src/main/java/pro/taskana/report/internal/TimeIntervalReportBuilderImpl.java b/lib/taskana-core/src/main/java/pro/taskana/report/internal/TimeIntervalReportBuilderImpl.java index c8c566190..230485cec 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/report/internal/TimeIntervalReportBuilderImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/report/internal/TimeIntervalReportBuilderImpl.java @@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; import pro.taskana.common.api.exceptions.InvalidArgumentException; import pro.taskana.common.api.exceptions.NotAuthorizedException; import pro.taskana.common.internal.InternalTaskanaEngine; +import pro.taskana.common.internal.util.DaysToWorkingDaysConverter; import pro.taskana.common.internal.util.LoggerUtils; import pro.taskana.report.api.ClassificationReport; import pro.taskana.report.api.TimeIntervalReportBuilder; @@ -191,7 +192,8 @@ abstract class TimeIntervalReportBuilderImpl< private List convertWorkingDaysToDays( List selectedItems, List columnHeaders) throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = DaysToWorkingDaysConverter.initialize(columnHeaders); + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize(columnHeaders); for (SelectedItem selectedItem : selectedItems) { selectedItem.setLowerAgeLimit( Collections.min(instance.convertWorkingDaysToDays(selectedItem.getLowerAgeLimit()))); diff --git a/lib/taskana-core/src/main/java/pro/taskana/report/internal/preprocessor/DaysToWorkingDaysPreProcessor.java b/lib/taskana-core/src/main/java/pro/taskana/report/internal/preprocessor/DaysToWorkingDaysPreProcessor.java index e1027fbef..090a3ae63 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/report/internal/preprocessor/DaysToWorkingDaysPreProcessor.java +++ b/lib/taskana-core/src/main/java/pro/taskana/report/internal/preprocessor/DaysToWorkingDaysPreProcessor.java @@ -3,8 +3,9 @@ package pro.taskana.report.internal.preprocessor; import java.util.List; import pro.taskana.common.api.exceptions.InvalidArgumentException; +import pro.taskana.common.internal.util.DaysToWorkingDaysConverter; import pro.taskana.report.api.structure.QueryItemPreprocessor; -import pro.taskana.report.internal.DaysToWorkingDaysConverter; +import pro.taskana.report.internal.DaysToWorkingDaysReportConverter; import pro.taskana.report.internal.header.TimeIntervalColumnHeader; import pro.taskana.report.internal.item.AgeQueryItem; @@ -16,13 +17,13 @@ import pro.taskana.report.internal.item.AgeQueryItem; public class DaysToWorkingDaysPreProcessor implements QueryItemPreprocessor { - private DaysToWorkingDaysConverter instance; + private DaysToWorkingDaysReportConverter instance; public DaysToWorkingDaysPreProcessor( List columnHeaders, boolean activate) throws InvalidArgumentException { if (activate) { - instance = DaysToWorkingDaysConverter.initialize(columnHeaders); + instance = DaysToWorkingDaysReportConverter.initialize(columnHeaders); } } diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java index 0181220c7..b0be3d182 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java @@ -4,7 +4,6 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -32,6 +31,7 @@ import pro.taskana.common.api.exceptions.TaskanaException; import pro.taskana.common.internal.CustomPropertySelector; import pro.taskana.common.internal.InternalTaskanaEngine; import pro.taskana.common.internal.security.CurrentUserContext; +import pro.taskana.common.internal.util.DaysToWorkingDaysConverter; import pro.taskana.common.internal.util.IdGenerator; import pro.taskana.common.internal.util.LoggerUtils; import pro.taskana.history.api.events.task.ClaimCancelledEvent; @@ -39,8 +39,6 @@ import pro.taskana.history.api.events.task.ClaimedEvent; import pro.taskana.history.api.events.task.CompletedEvent; import pro.taskana.history.api.events.task.CreatedEvent; import pro.taskana.history.internal.HistoryEventProducer; -import pro.taskana.report.internal.DaysToWorkingDaysConverter; -import pro.taskana.report.internal.header.TimeIntervalColumnHeader; import pro.taskana.task.api.Attachment; import pro.taskana.task.api.CallbackState; import pro.taskana.task.api.ObjectReference; @@ -95,9 +93,7 @@ public class TaskServiceImpl implements TaskService { AttachmentMapper attachmentMapper) { super(); try { - this.converter = - DaysToWorkingDaysConverter.initialize( - Collections.singletonList(new TimeIntervalColumnHeader(0)), Instant.now()); + this.converter = DaysToWorkingDaysConverter.initialize(); } catch (InvalidArgumentException e) { throw new SystemException( "Internal error. Cannot initialize DaysToWorkingDaysConverter", e.getCause()); diff --git a/lib/taskana-core/src/test/java/acceptance/classification/UpdateClassificationAccTest.java b/lib/taskana-core/src/test/java/acceptance/classification/UpdateClassificationAccTest.java index 6071617e1..d772903ca 100644 --- a/lib/taskana-core/src/test/java/acceptance/classification/UpdateClassificationAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/classification/UpdateClassificationAccTest.java @@ -13,7 +13,6 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -28,8 +27,7 @@ import pro.taskana.common.api.exceptions.DomainNotFoundException; import pro.taskana.common.api.exceptions.InvalidArgumentException; import pro.taskana.common.api.exceptions.NotAuthorizedException; import pro.taskana.common.internal.jobs.JobRunner; -import pro.taskana.report.internal.DaysToWorkingDaysConverter; -import pro.taskana.report.internal.header.TimeIntervalColumnHeader; +import pro.taskana.common.internal.util.DaysToWorkingDaysConverter; import pro.taskana.security.JaasExtension; import pro.taskana.security.WithAccessId; import pro.taskana.task.api.Task; @@ -244,9 +242,7 @@ public class UpdateClassificationAccTest extends AbstractAccTest { // TODO - resume old behaviour after attachment query is possible. TaskService taskService = taskanaEngine.getTaskService(); - DaysToWorkingDaysConverter converter = - DaysToWorkingDaysConverter.initialize( - Collections.singletonList(new TimeIntervalColumnHeader(0)), Instant.now()); + DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(Instant.now()); List tasksWithP1D = new ArrayList<>( diff --git a/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java index 764b4a587..f0059004b 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java @@ -12,7 +12,6 @@ import acceptance.AbstractAccTest; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; @@ -30,8 +29,7 @@ import pro.taskana.common.api.exceptions.InvalidArgumentException; import pro.taskana.common.api.exceptions.NotAuthorizedException; import pro.taskana.common.internal.TaskanaEngineProxyForTest; import pro.taskana.common.internal.security.CurrentUserContext; -import pro.taskana.report.internal.DaysToWorkingDaysConverter; -import pro.taskana.report.internal.header.TimeIntervalColumnHeader; +import pro.taskana.common.internal.util.DaysToWorkingDaysConverter; import pro.taskana.security.JaasExtension; import pro.taskana.security.WithAccessId; import pro.taskana.task.api.Attachment; @@ -548,9 +546,7 @@ class CreateTaskAccTest extends AbstractAccTest { assertEquals(99, readTask.getPriority()); - DaysToWorkingDaysConverter converter = - DaysToWorkingDaysConverter.initialize( - Collections.singletonList(new TimeIntervalColumnHeader(0)), Instant.now()); + DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(Instant.now()); long calendarDays = converter.convertWorkingDaysToDays(readTask.getPlanned(), 1); assertEquals(readTask.getDue(), readTask.getPlanned().plus(Duration.ofDays(calendarDays))); diff --git a/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAttachmentsAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAttachmentsAccTest.java index 361e2e945..e3a85c08f 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAttachmentsAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAttachmentsAccTest.java @@ -12,7 +12,6 @@ import acceptance.AbstractAccTest; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -26,8 +25,7 @@ import pro.taskana.common.api.exceptions.ConcurrencyException; import pro.taskana.common.api.exceptions.InvalidArgumentException; import pro.taskana.common.api.exceptions.NotAuthorizedException; import pro.taskana.common.internal.security.CurrentUserContext; -import pro.taskana.report.internal.DaysToWorkingDaysConverter; -import pro.taskana.report.internal.header.TimeIntervalColumnHeader; +import pro.taskana.common.internal.util.DaysToWorkingDaysConverter; import pro.taskana.security.JaasExtension; import pro.taskana.security.WithAccessId; import pro.taskana.task.api.Attachment; @@ -166,9 +164,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest { assertThat(task.getAttachments().get(0).getChannel(), equalTo(newChannel)); assertEquals(999, task.getPriority()); - DaysToWorkingDaysConverter converter = - DaysToWorkingDaysConverter.initialize( - Collections.singletonList(new TimeIntervalColumnHeader(0)), Instant.now()); + DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(Instant.now()); long calendarDays = converter.convertWorkingDaysToDays(task.getDue(), 1); assertEquals(task.getDue(), task.getPlanned().plus(Duration.ofDays(calendarDays))); } @@ -322,9 +318,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest { assertThat(task.getAttachments().get(0).getChannel(), equalTo(newChannel)); assertEquals(999, task.getPriority()); - DaysToWorkingDaysConverter converter = - DaysToWorkingDaysConverter.initialize( - Collections.singletonList(new TimeIntervalColumnHeader(0)), Instant.now()); + DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(Instant.now()); long calendarDays = converter.convertWorkingDaysToDays(task.getDue(), 1); assertEquals(task.getDue(), task.getPlanned().plus(Duration.ofDays(calendarDays))); @@ -358,9 +352,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest { task = taskService.updateTask(task); task = taskService.getTask(task.getId()); assertEquals(101, task.getPriority()); - DaysToWorkingDaysConverter converter = - DaysToWorkingDaysConverter.initialize( - Collections.singletonList(new TimeIntervalColumnHeader(0)), Instant.now()); + DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(Instant.now()); long calendarDays = converter.convertWorkingDaysToDays(task.getDue(), 1); assertEquals(task.getDue(), task.getPlanned().plus(Duration.ofDays(calendarDays))); @@ -552,9 +544,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest { assertEquals(99, readTask.getPriority()); - DaysToWorkingDaysConverter converter = - DaysToWorkingDaysConverter.initialize( - Collections.singletonList(new TimeIntervalColumnHeader(0)), Instant.now()); + DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(Instant.now()); long calendarDays = converter.convertWorkingDaysToDays(readTask.getPlanned(), 1); assertEquals(readTask.getDue(), readTask.getPlanned().plus(Duration.ofDays(calendarDays))); diff --git a/lib/taskana-core/src/test/java/pro/taskana/ArchitectureTest.java b/lib/taskana-core/src/test/java/pro/taskana/ArchitectureTest.java index 5e840ad55..6978f15ce 100644 --- a/lib/taskana-core/src/test/java/pro/taskana/ArchitectureTest.java +++ b/lib/taskana-core/src/test/java/pro/taskana/ArchitectureTest.java @@ -146,8 +146,7 @@ class ArchitectureTest { Stream.of( "..workbasket..", "..history..", - // tasks are to be fixed ... - // "..task..", + "..task..", "..classification.."); return packagesToTest .map( diff --git a/lib/taskana-core/src/test/java/pro/taskana/report/internal/DaysToWorkingDaysConverterTest.java b/lib/taskana-core/src/test/java/pro/taskana/common/internal/util/DaysToWorkingDaysConverterTest.java similarity index 91% rename from lib/taskana-core/src/test/java/pro/taskana/report/internal/DaysToWorkingDaysConverterTest.java rename to lib/taskana-core/src/test/java/pro/taskana/common/internal/util/DaysToWorkingDaysConverterTest.java index c27d3a684..a1449c269 100644 --- a/lib/taskana-core/src/test/java/pro/taskana/report/internal/DaysToWorkingDaysConverterTest.java +++ b/lib/taskana-core/src/test/java/pro/taskana/common/internal/util/DaysToWorkingDaysConverterTest.java @@ -1,9 +1,9 @@ -package pro.taskana.report.internal; +package pro.taskana.common.internal.util; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static pro.taskana.report.internal.DaysToWorkingDaysConverter.getEasterSunday; +import static pro.taskana.common.internal.util.DaysToWorkingDaysConverter.getEasterSunday; import java.time.Instant; import java.time.LocalDate; @@ -14,6 +14,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import pro.taskana.common.api.exceptions.InvalidArgumentException; +import pro.taskana.common.internal.util.DaysToWorkingDaysConverter; +import pro.taskana.report.internal.DaysToWorkingDaysReportConverter; import pro.taskana.report.internal.header.TimeIntervalColumnHeader; /** Test for the DaysToWorkingDaysConverter. */ @@ -29,10 +31,9 @@ class DaysToWorkingDaysConverterTest { @Test void testConvertWorkingDaysToDaysForTasks() throws InvalidArgumentException { - List reportItems = singletonList(new TimeIntervalColumnHeader(0)); Instant thursday0201 = Instant.parse("2018-02-01T07:00:00.000Z"); DaysToWorkingDaysConverter converter = - DaysToWorkingDaysConverter.initialize(reportItems, thursday0201); + DaysToWorkingDaysConverter.initialize(thursday0201); long days = converter.convertWorkingDaysToDays(thursday0201, -7); // = tuesday (sat + sun) assertEquals(-9, days); @@ -76,10 +77,9 @@ class DaysToWorkingDaysConverterTest { @Test void testConvertWorkingDaysToDaysForKarFreitag() throws InvalidArgumentException { - List reportItems = singletonList(new TimeIntervalColumnHeader(0)); Instant thursday0201 = Instant.parse("2018-02-01T07:00:00.000Z"); DaysToWorkingDaysConverter converter = - DaysToWorkingDaysConverter.initialize(reportItems, thursday0201); + DaysToWorkingDaysConverter.initialize(thursday0201); Instant gruenDonnerstag2018 = Instant.parse("2018-03-29T01:00:00.000Z"); long days = converter.convertWorkingDaysToDays(gruenDonnerstag2018, 0); assertEquals(0, days); @@ -94,7 +94,7 @@ class DaysToWorkingDaysConverterTest { List reportItems = singletonList(new TimeIntervalColumnHeader(0)); Instant thursday0201 = Instant.parse("2018-02-01T07:00:00.000Z"); DaysToWorkingDaysConverter converter = - DaysToWorkingDaysConverter.initialize(reportItems, thursday0201); + DaysToWorkingDaysConverter.initialize(thursday0201); Instant freitag0427 = Instant.parse("2018-04-27T19:00:00.000Z"); long days = converter.convertWorkingDaysToDays(freitag0427, 0); @@ -107,11 +107,11 @@ class DaysToWorkingDaysConverterTest { @Test void testInitializeForDifferentDates() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance1 = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance1 = + DaysToWorkingDaysReportConverter.initialize( getShortListOfColumnHeaders(), Instant.parse("2018-02-04T00:00:00.000Z")); - DaysToWorkingDaysConverter instance2 = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance2 = + DaysToWorkingDaysReportConverter.initialize( getShortListOfColumnHeaders(), Instant.parse("2018-02-05T00:00:00.000Z")); assertNotEquals(instance1, instance2); @@ -119,8 +119,8 @@ class DaysToWorkingDaysConverterTest { @Test void testConvertDaysToWorkingDays() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-02-06T00:00:00.000Z")); assertEquals(-16, instance.convertDaysToWorkingDays(-16)); @@ -144,8 +144,8 @@ class DaysToWorkingDaysConverterTest { @Test void testConvertWorkingDaysToDays() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-02-27T00:00:00.000Z")); assertEquals(singletonList(-13), instance.convertWorkingDaysToDays(-13)); @@ -178,8 +178,8 @@ class DaysToWorkingDaysConverterTest { @Test void testConvertWorkingDaysToDaysAtWeekend() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-03-10T00:00:00.000Z")); assertEquals(singletonList(-13), instance.convertWorkingDaysToDays(-13)); @@ -212,8 +212,8 @@ class DaysToWorkingDaysConverterTest { @Test void testConvertWorkingDaysToDaysOnEasterSunday() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-04-01T00:00:00.000Z")); assertEquals(singletonList(-13), instance.convertWorkingDaysToDays(-13)); @@ -246,8 +246,8 @@ class DaysToWorkingDaysConverterTest { @Test void testEasterHolidays() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-03-28T00:00:00.000Z")); assertEquals(0, instance.convertDaysToWorkingDays(0)); @@ -261,8 +261,8 @@ class DaysToWorkingDaysConverterTest { @Test void testWhitsunHolidays() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-05-16T00:00:00.000Z")); assertEquals(0, instance.convertDaysToWorkingDays(0)); @@ -276,8 +276,8 @@ class DaysToWorkingDaysConverterTest { @Test void testLabourDayHoliday() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-04-26T00:00:00.000Z")); assertEquals(0, instance.convertDaysToWorkingDays(0)); @@ -292,8 +292,8 @@ class DaysToWorkingDaysConverterTest { @Test void testAscensionDayHoliday() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-05-07T00:00:00.000Z")); assertEquals(0, instance.convertDaysToWorkingDays(0)); @@ -308,8 +308,8 @@ class DaysToWorkingDaysConverterTest { @Test void testDayOfGermanUnityHoliday() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-10-01T00:00:00.000Z")); assertEquals(0, instance.convertDaysToWorkingDays(0)); @@ -324,8 +324,8 @@ class DaysToWorkingDaysConverterTest { @Test void testChristmasAndNewYearHolidays() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-12-20T00:00:00.000Z")); assertEquals(0, instance.convertDaysToWorkingDays(0)); @@ -347,8 +347,8 @@ class DaysToWorkingDaysConverterTest { @Test void testCustomHolidaysWithDayOfReformationAndAllSaintsDay() throws InvalidArgumentException { - DaysToWorkingDaysConverter instance = - DaysToWorkingDaysConverter.initialize( + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( getLargeListOfColumnHeaders(), Instant.parse("2018-10-26T00:00:00.000Z")); assertEquals(0, instance.convertDaysToWorkingDays(0)); diff --git a/lib/taskana-core/src/test/java/pro/taskana/report/internal/DaysToWorkingDaysReportConverterTest.java b/lib/taskana-core/src/test/java/pro/taskana/report/internal/DaysToWorkingDaysReportConverterTest.java new file mode 100644 index 000000000..eae99d88e --- /dev/null +++ b/lib/taskana-core/src/test/java/pro/taskana/report/internal/DaysToWorkingDaysReportConverterTest.java @@ -0,0 +1,309 @@ +package pro.taskana.report.internal; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.time.Instant; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import pro.taskana.common.api.exceptions.InvalidArgumentException; +import pro.taskana.common.internal.util.DaysToWorkingDaysConverter; +import pro.taskana.report.internal.header.TimeIntervalColumnHeader; + +/** Test for the DaysToWorkingDaysReportConverter. */ +class DaysToWorkingDaysReportConverterTest { + + @BeforeAll + static void setup() { + DaysToWorkingDaysConverter.setGermanPublicHolidaysEnabled(true); + LocalDate dayOfReformation = LocalDate.of(2018, 10, 31); + LocalDate allSaintsDays = LocalDate.of(2018, 11, 1); + DaysToWorkingDaysConverter.setCustomHolidays(Arrays.asList(dayOfReformation, allSaintsDays)); + } + + @Test + void testInitializeForDifferentDates() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance1 = + DaysToWorkingDaysReportConverter.initialize( + getShortListOfColumnHeaders(), Instant.parse("2018-02-04T00:00:00.000Z")); + DaysToWorkingDaysReportConverter instance2 = + DaysToWorkingDaysReportConverter.initialize( + getShortListOfColumnHeaders(), Instant.parse("2018-02-05T00:00:00.000Z")); + + assertNotEquals(instance1, instance2); + } + + @Test + void testConvertDaysToWorkingDays() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-02-06T00:00:00.000Z")); + + assertEquals(-16, instance.convertDaysToWorkingDays(-16)); + assertEquals(-11, instance.convertDaysToWorkingDays(-15)); + + assertEquals(-2, instance.convertDaysToWorkingDays(-4)); + assertEquals(-1, instance.convertDaysToWorkingDays(-3)); + assertEquals(-1, instance.convertDaysToWorkingDays(-2)); + assertEquals(-1, instance.convertDaysToWorkingDays(-1)); + assertEquals(0, instance.convertDaysToWorkingDays(0)); + assertEquals(1, instance.convertDaysToWorkingDays(1)); + assertEquals(2, instance.convertDaysToWorkingDays(2)); + assertEquals(3, instance.convertDaysToWorkingDays(3)); + assertEquals(3, instance.convertDaysToWorkingDays(4)); + assertEquals(3, instance.convertDaysToWorkingDays(5)); + assertEquals(4, instance.convertDaysToWorkingDays(6)); + + assertEquals(11, instance.convertDaysToWorkingDays(15)); + assertEquals(16, instance.convertDaysToWorkingDays(16)); + } + + @Test + void testConvertWorkingDaysToDays() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-02-27T00:00:00.000Z")); + + assertEquals(singletonList(-13), instance.convertWorkingDaysToDays(-13)); + assertEquals(singletonList(-12), instance.convertWorkingDaysToDays(-12)); + + assertEquals(singletonList(-12), instance.convertWorkingDaysToDays(-8)); + assertEquals(singletonList(-11), instance.convertWorkingDaysToDays(-7)); + assertEquals(Arrays.asList(-8, -9, -10), instance.convertWorkingDaysToDays(-6)); + assertEquals(singletonList(-7), instance.convertWorkingDaysToDays(-5)); + assertEquals(singletonList(-6), instance.convertWorkingDaysToDays(-4)); + assertEquals(singletonList(-5), instance.convertWorkingDaysToDays(-3)); + assertEquals(singletonList(-4), instance.convertWorkingDaysToDays(-2)); + assertEquals(Arrays.asList(-1, -2, -3), instance.convertWorkingDaysToDays(-1)); + assertEquals(singletonList(0), instance.convertWorkingDaysToDays(0)); + assertEquals(singletonList(1), instance.convertWorkingDaysToDays(1)); + assertEquals(singletonList(2), instance.convertWorkingDaysToDays(2)); + assertEquals(Arrays.asList(3, 4, 5), instance.convertWorkingDaysToDays(3)); + assertEquals(singletonList(6), instance.convertWorkingDaysToDays(4)); + assertEquals(singletonList(7), instance.convertWorkingDaysToDays(5)); + assertEquals(singletonList(8), instance.convertWorkingDaysToDays(6)); + assertEquals(singletonList(9), instance.convertWorkingDaysToDays(7)); + assertEquals(Arrays.asList(10, 11, 12), instance.convertWorkingDaysToDays(8)); + assertEquals(singletonList(13), instance.convertWorkingDaysToDays(9)); + assertEquals(singletonList(14), instance.convertWorkingDaysToDays(10)); + assertEquals(singletonList(15), instance.convertWorkingDaysToDays(11)); + + assertEquals(singletonList(12), instance.convertWorkingDaysToDays(12)); + assertEquals(singletonList(13), instance.convertWorkingDaysToDays(13)); + } + + @Test + void testConvertWorkingDaysToDaysAtWeekend() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-03-10T00:00:00.000Z")); + + assertEquals(singletonList(-13), instance.convertWorkingDaysToDays(-13)); + assertEquals(singletonList(-12), instance.convertWorkingDaysToDays(-12)); + + assertEquals(singletonList(-10), instance.convertWorkingDaysToDays(-8)); + assertEquals(singletonList(-9), instance.convertWorkingDaysToDays(-7)); + assertEquals(singletonList(-8), instance.convertWorkingDaysToDays(-6)); + assertEquals(Arrays.asList(-5, -6, -7), instance.convertWorkingDaysToDays(-5)); + assertEquals(singletonList(-4), instance.convertWorkingDaysToDays(-4)); + assertEquals(singletonList(-3), instance.convertWorkingDaysToDays(-3)); + assertEquals(singletonList(-2), instance.convertWorkingDaysToDays(-2)); + assertEquals(singletonList(-1), instance.convertWorkingDaysToDays(-1)); + assertEquals(Arrays.asList(0, 1), instance.convertWorkingDaysToDays(0)); + assertEquals(singletonList(2), instance.convertWorkingDaysToDays(1)); + assertEquals(singletonList(3), instance.convertWorkingDaysToDays(2)); + assertEquals(singletonList(4), instance.convertWorkingDaysToDays(3)); + assertEquals(singletonList(5), instance.convertWorkingDaysToDays(4)); + assertEquals(Arrays.asList(6, 7, 8), instance.convertWorkingDaysToDays(5)); + assertEquals(singletonList(9), instance.convertWorkingDaysToDays(6)); + assertEquals(singletonList(10), instance.convertWorkingDaysToDays(7)); + assertEquals(singletonList(11), instance.convertWorkingDaysToDays(8)); + assertEquals(singletonList(12), instance.convertWorkingDaysToDays(9)); + assertEquals(Arrays.asList(13, 14, 15), instance.convertWorkingDaysToDays(10)); + assertEquals(singletonList(16), instance.convertWorkingDaysToDays(11)); + + assertEquals(singletonList(12), instance.convertWorkingDaysToDays(12)); + assertEquals(singletonList(13), instance.convertWorkingDaysToDays(13)); + } + + @Test + void testConvertWorkingDaysToDaysOnEasterSunday() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-04-01T00:00:00.000Z")); + + assertEquals(singletonList(-13), instance.convertWorkingDaysToDays(-13)); + assertEquals(singletonList(-12), instance.convertWorkingDaysToDays(-12)); + + assertEquals(singletonList(-12), instance.convertWorkingDaysToDays(-8)); + assertEquals(singletonList(-11), instance.convertWorkingDaysToDays(-7)); + assertEquals(singletonList(-10), instance.convertWorkingDaysToDays(-6)); + assertEquals(singletonList(-9), instance.convertWorkingDaysToDays(-5)); + assertEquals(Arrays.asList(-6, -7, -8), instance.convertWorkingDaysToDays(-4)); + assertEquals(singletonList(-5), instance.convertWorkingDaysToDays(-3)); + assertEquals(singletonList(-4), instance.convertWorkingDaysToDays(-2)); + assertEquals(singletonList(-3), instance.convertWorkingDaysToDays(-1)); + assertEquals(Arrays.asList(0, 1, -1, -2), instance.convertWorkingDaysToDays(0)); + assertEquals(singletonList(2), instance.convertWorkingDaysToDays(1)); + assertEquals(singletonList(3), instance.convertWorkingDaysToDays(2)); + assertEquals(singletonList(4), instance.convertWorkingDaysToDays(3)); + assertEquals(Arrays.asList(5, 6, 7), instance.convertWorkingDaysToDays(4)); + assertEquals(singletonList(8), instance.convertWorkingDaysToDays(5)); + assertEquals(singletonList(9), instance.convertWorkingDaysToDays(6)); + assertEquals(singletonList(10), instance.convertWorkingDaysToDays(7)); + assertEquals(singletonList(11), instance.convertWorkingDaysToDays(8)); + assertEquals(Arrays.asList(12, 13, 14), instance.convertWorkingDaysToDays(9)); + assertEquals(singletonList(15), instance.convertWorkingDaysToDays(10)); + assertEquals(singletonList(16), instance.convertWorkingDaysToDays(11)); + + assertEquals(singletonList(12), instance.convertWorkingDaysToDays(12)); + assertEquals(singletonList(13), instance.convertWorkingDaysToDays(13)); + } + + @Test + void testEasterHolidays() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-03-28T00:00:00.000Z")); + + assertEquals(0, instance.convertDaysToWorkingDays(0)); + assertEquals(1, instance.convertDaysToWorkingDays(1)); + assertEquals(1, instance.convertDaysToWorkingDays(2)); + assertEquals(1, instance.convertDaysToWorkingDays(3)); + assertEquals(1, instance.convertDaysToWorkingDays(4)); + assertEquals(1, instance.convertDaysToWorkingDays(5)); + assertEquals(2, instance.convertDaysToWorkingDays(6)); + } + + @Test + void testWhitsunHolidays() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-05-16T00:00:00.000Z")); + + assertEquals(0, instance.convertDaysToWorkingDays(0)); + assertEquals(1, instance.convertDaysToWorkingDays(1)); + assertEquals(2, instance.convertDaysToWorkingDays(2)); + assertEquals(2, instance.convertDaysToWorkingDays(3)); + assertEquals(2, instance.convertDaysToWorkingDays(4)); + assertEquals(2, instance.convertDaysToWorkingDays(5)); + assertEquals(3, instance.convertDaysToWorkingDays(6)); + } + + @Test + void testLabourDayHoliday() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-04-26T00:00:00.000Z")); + + assertEquals(0, instance.convertDaysToWorkingDays(0)); + assertEquals(1, instance.convertDaysToWorkingDays(1)); + assertEquals(1, instance.convertDaysToWorkingDays(2)); + assertEquals(1, instance.convertDaysToWorkingDays(3)); + assertEquals(2, instance.convertDaysToWorkingDays(4)); + assertEquals(2, instance.convertDaysToWorkingDays(5)); + assertEquals(3, instance.convertDaysToWorkingDays(6)); + assertEquals(4, instance.convertDaysToWorkingDays(7)); + } + + @Test + void testAscensionDayHoliday() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-05-07T00:00:00.000Z")); + + assertEquals(0, instance.convertDaysToWorkingDays(0)); + assertEquals(1, instance.convertDaysToWorkingDays(1)); + assertEquals(2, instance.convertDaysToWorkingDays(2)); + assertEquals(2, instance.convertDaysToWorkingDays(3)); + assertEquals(3, instance.convertDaysToWorkingDays(4)); + assertEquals(3, instance.convertDaysToWorkingDays(5)); + assertEquals(3, instance.convertDaysToWorkingDays(6)); + assertEquals(4, instance.convertDaysToWorkingDays(7)); + } + + @Test + void testDayOfGermanUnityHoliday() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-10-01T00:00:00.000Z")); + + assertEquals(0, instance.convertDaysToWorkingDays(0)); + assertEquals(1, instance.convertDaysToWorkingDays(1)); + assertEquals(1, instance.convertDaysToWorkingDays(2)); + assertEquals(2, instance.convertDaysToWorkingDays(3)); + assertEquals(3, instance.convertDaysToWorkingDays(4)); + assertEquals(3, instance.convertDaysToWorkingDays(5)); + assertEquals(3, instance.convertDaysToWorkingDays(6)); + assertEquals(4, instance.convertDaysToWorkingDays(7)); + } + + @Test + void testChristmasAndNewYearHolidays() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-12-20T00:00:00.000Z")); + + assertEquals(0, instance.convertDaysToWorkingDays(0)); + assertEquals(1, instance.convertDaysToWorkingDays(1)); + assertEquals(1, instance.convertDaysToWorkingDays(2)); + assertEquals(1, instance.convertDaysToWorkingDays(3)); + assertEquals(2, instance.convertDaysToWorkingDays(4)); + assertEquals(2, instance.convertDaysToWorkingDays(5)); + assertEquals(2, instance.convertDaysToWorkingDays(6)); + assertEquals(3, instance.convertDaysToWorkingDays(7)); + assertEquals(4, instance.convertDaysToWorkingDays(8)); + assertEquals(4, instance.convertDaysToWorkingDays(9)); + assertEquals(4, instance.convertDaysToWorkingDays(10)); + assertEquals(5, instance.convertDaysToWorkingDays(11)); + assertEquals(5, instance.convertDaysToWorkingDays(12)); + assertEquals(6, instance.convertDaysToWorkingDays(13)); + assertEquals(7, instance.convertDaysToWorkingDays(14)); + } + + @Test + void testCustomHolidaysWithDayOfReformationAndAllSaintsDay() throws InvalidArgumentException { + DaysToWorkingDaysReportConverter instance = + DaysToWorkingDaysReportConverter.initialize( + getLargeListOfColumnHeaders(), Instant.parse("2018-10-26T00:00:00.000Z")); + + assertEquals(0, instance.convertDaysToWorkingDays(0)); + assertEquals(0, instance.convertDaysToWorkingDays(1)); + assertEquals(0, instance.convertDaysToWorkingDays(2)); + assertEquals(1, instance.convertDaysToWorkingDays(3)); + assertEquals(2, instance.convertDaysToWorkingDays(4)); + assertEquals(2, instance.convertDaysToWorkingDays(5)); + assertEquals(2, instance.convertDaysToWorkingDays(6)); + assertEquals(3, instance.convertDaysToWorkingDays(7)); + } + + private List getShortListOfColumnHeaders() { + List columnHeaders = new ArrayList<>(); + columnHeaders.add(new TimeIntervalColumnHeader(Integer.MIN_VALUE, -3)); + columnHeaders.add(new TimeIntervalColumnHeader(-1, -2)); + columnHeaders.add(new TimeIntervalColumnHeader(0)); + columnHeaders.add(new TimeIntervalColumnHeader(1, 2)); + columnHeaders.add(new TimeIntervalColumnHeader(3, Integer.MAX_VALUE)); + return columnHeaders; + } + + private List getLargeListOfColumnHeaders() { + List columnHeaders = new ArrayList<>(); + columnHeaders.add(new TimeIntervalColumnHeader(Integer.MIN_VALUE, -11)); + columnHeaders.add(new TimeIntervalColumnHeader(-10, -6)); + columnHeaders.add(new TimeIntervalColumnHeader(-5, -2)); + columnHeaders.add(new TimeIntervalColumnHeader(-1)); + columnHeaders.add(new TimeIntervalColumnHeader(0)); + columnHeaders.add(new TimeIntervalColumnHeader(1)); + columnHeaders.add(new TimeIntervalColumnHeader(2, 5)); + columnHeaders.add(new TimeIntervalColumnHeader(6, 10)); + columnHeaders.add(new TimeIntervalColumnHeader(11, Integer.MAX_VALUE)); + return columnHeaders; + } +}