TSK-1143C mismatch between forward and backward calculation between planned and due

This commit is contained in:
BerndBreier 2020-03-20 16:48:35 +01:00
parent 7b409451dc
commit fa429cef97
1 changed files with 299 additions and 56 deletions

View File

@ -58,6 +58,28 @@ class ServiceLevelHandler {
} }
} }
// use the same algorithm as setPlannedPropertyOfTasksImpl to refresh
// priority and duration of affected tasks, just don't use a fix
// planned date but the individual planned date of the tasks
public void refreshPriorityAndDueDatesOfTasks(
List<MinimalTaskSummary> tasks, boolean serviceLevelChanged, boolean priorityChanged) {
List<AttachmentSummaryImpl> attachments = getAttachmentSummaries(tasks);
List<ClassificationSummary> allInvolvedClassifications =
findAllClassificationsReferencedByTasksAndAttachments(tasks, attachments);
if (serviceLevelChanged) {
List<ClassificationWithServiceLevelResolved> allInvolvedClassificationsWithDuration =
resolveDurationsInClassifications(allInvolvedClassifications);
updateTaskDueDatesOnClassificationUpdate(
tasks, attachments, allInvolvedClassificationsWithDuration);
}
if (priorityChanged) {
updateTaskPriorityOnClassificationUpdate(tasks, attachments, allInvolvedClassifications);
}
}
// Algorithm: // Algorithm:
// - load all relevant tasks and their attachmentSummaries // - load all relevant tasks and their attachmentSummaries
// - load all classifications referenced by these tasks / attachments // - load all classifications referenced by these tasks / attachments
@ -69,7 +91,7 @@ class ServiceLevelHandler {
BulkLog bulkLog = new BulkLog(); BulkLog bulkLog = new BulkLog();
List<AttachmentSummaryImpl> attachments = getAttachmentSummaries(tasks); List<AttachmentSummaryImpl> attachments = getAttachmentSummaries(tasks);
List<ClassificationSummary> allInvolvedClassifications = List<ClassificationSummary> allInvolvedClassifications =
findAllInvolvedClassifications(tasks, attachments); findAllClassificationsReferencedByTasksAndAttachments(tasks, attachments);
List<ClassificationWithServiceLevelResolved> allInvolvedClassificationsWithDuration = List<ClassificationWithServiceLevelResolved> allInvolvedClassificationsWithDuration =
resolveDurationsInClassifications(allInvolvedClassifications); resolveDurationsInClassifications(allInvolvedClassifications);
Map<Duration, List<String>> durationToTaskIdsMap = Map<Duration, List<String>> durationToTaskIdsMap =
@ -119,7 +141,8 @@ class ServiceLevelHandler {
} }
DurationPrioHolder determineTaskPrioDuration(TaskImpl newTaskImpl, boolean onlyPriority) { DurationPrioHolder determineTaskPrioDuration(TaskImpl newTaskImpl, boolean onlyPriority) {
Set<ClassificationSummary> classificationsInvolved = getInvolvedClassifications(newTaskImpl); Set<ClassificationSummary> classificationsInvolved =
getClassificationsReferencedByATask(newTaskImpl);
List<ClassificationWithServiceLevelResolved> resolvedClassifications = new ArrayList<>(); List<ClassificationWithServiceLevelResolved> resolvedClassifications = new ArrayList<>();
if (onlyPriority) { if (onlyPriority) {
@ -148,6 +171,77 @@ class ServiceLevelHandler {
return task; return task;
} }
private void updateTaskPriorityOnClassificationUpdate(
List<MinimalTaskSummary> existingTasks,
List<AttachmentSummaryImpl> attachments,
List<ClassificationSummary> allInvolvedClassifications) {
Map<Integer, List<String>> priorityToTaskIdsMap =
getPriorityToTasksIdsMap(existingTasks, attachments, allInvolvedClassifications);
TaskImpl referenceTask = new TaskImpl();
referenceTask.setModified(Instant.now());
priorityToTaskIdsMap.forEach(
(prio, taskIdList) -> {
referenceTask.setPriority(prio.intValue());
if (!taskIdList.isEmpty()) {
taskMapper.updatePriorityOfTasks(taskIdList, referenceTask);
}
});
}
private Map<Integer, List<String>> getPriorityToTasksIdsMap(
List<MinimalTaskSummary> existingTasks,
List<AttachmentSummaryImpl> attachments,
List<ClassificationSummary> allInvolvedClassifications) {
Map<String, Integer> classificationIdToPriorityMap =
allInvolvedClassifications.stream()
.collect(
Collectors.toMap(ClassificationSummary::getId, ClassificationSummary::getPriority));
Map<String, Set<String>> taskIdToClassificationIdsMap =
getTaskIdToClassificationsMap(existingTasks, attachments);
List<TaskIdPriority> taskIdPriorities =
existingTasks.stream()
.map(
t ->
new TaskIdPriority(
t.getTaskId(),
determinePriorityForATask(
t, classificationIdToPriorityMap, taskIdToClassificationIdsMap)))
.collect(Collectors.toList());
return taskIdPriorities.stream()
.collect(
groupingBy(
TaskIdPriority::getPriority,
Collectors.mapping(TaskIdPriority::getTaskId, Collectors.toList())));
}
private int determinePriorityForATask(
MinimalTaskSummary minimalTaskSummary,
Map<String, Integer> classificationIdToPriorityMap,
Map<String, Set<String>> taskIdToClassificationIdsMap) {
int actualPriority = 0;
for (String classificationId :
taskIdToClassificationIdsMap.get(minimalTaskSummary.getTaskId())) {
int prio = classificationIdToPriorityMap.get(classificationId);
if (prio > actualPriority) {
actualPriority = prio;
}
}
return actualPriority;
}
private BulkLog updateTaskDueDatesOnClassificationUpdate(
List<MinimalTaskSummary> existingTasks,
List<AttachmentSummaryImpl> attachments,
List<ClassificationWithServiceLevelResolved> allInvolvedClassificationsWithDuration) {
Map<InstantDurationHolder, List<TaskDuration>> tasksPerPlannedAndDuration =
getTasksPerPlannedAndDuration(
existingTasks, attachments, allInvolvedClassificationsWithDuration);
return updateDuePropertyOfAffectedTasks(tasksPerPlannedAndDuration);
}
private TaskImpl updatePlannedDueOnTaskUpdate( private TaskImpl updatePlannedDueOnTaskUpdate(
TaskImpl newTaskImpl, TaskImpl oldTaskImpl, DurationPrioHolder durationPrioHolder) TaskImpl newTaskImpl, TaskImpl oldTaskImpl, DurationPrioHolder durationPrioHolder)
throws InvalidArgumentException { throws InvalidArgumentException {
@ -277,6 +371,37 @@ class ServiceLevelHandler {
return newTaskImpl; return newTaskImpl;
} }
private BulkLog updateDuePropertyOfAffectedTasks(
Map<InstantDurationHolder, List<TaskDuration>> tasksPerPlannedAndDuration) {
BulkLog bulkLog = new BulkLog();
tasksPerPlannedAndDuration.forEach(
(instDurHld, taskDurationList) ->
bulkLog.addAllErrors(
updateDuePropertyOfTasksWithIdenticalDueDate(instDurHld, taskDurationList)));
return bulkLog;
}
private BulkOperationResults<String, TaskanaException>
updateDuePropertyOfTasksWithIdenticalDueDate(
InstantDurationHolder instDurHld, List<TaskDuration> taskDurationList) {
BulkLog bulkLog = new BulkLog();
TaskImpl referenceTask = new TaskImpl();
referenceTask.setPlanned(instDurHld.getPlanned());
referenceTask.setModified(Instant.now());
referenceTask.setDue(
converter.addWorkingDaysToInstant(referenceTask.getPlanned(), instDurHld.getDuration()));
List<String> taskIdsToUpdate =
taskDurationList.stream().map(TaskDuration::getTaskId).collect(Collectors.toList());
long numTasksUpdated = taskMapper.updateTaskDueDates(taskIdsToUpdate, referenceTask);
if (numTasksUpdated != taskIdsToUpdate.size()) {
BulkLog checkResult =
checkResultsOfTasksUpdateAndAddErrorsToBulkLog(
taskIdsToUpdate, referenceTask, numTasksUpdated);
bulkLog.addAllErrors(checkResult);
}
return bulkLog;
}
private BulkLog updatePlannedPropertyOfAffectedTasks( private BulkLog updatePlannedPropertyOfAffectedTasks(
Instant planned, Map<Duration, List<String>> durationToTaskIdsMap) { Instant planned, Map<Duration, List<String>> durationToTaskIdsMap) {
BulkLog bulkLog = new BulkLog(); BulkLog bulkLog = new BulkLog();
@ -285,9 +410,8 @@ class ServiceLevelHandler {
referenceTask.setModified(Instant.now()); referenceTask.setModified(Instant.now());
for (Map.Entry<Duration, List<String>> entry : durationToTaskIdsMap.entrySet()) { for (Map.Entry<Duration, List<String>> entry : durationToTaskIdsMap.entrySet()) {
List<String> taskIdsToUpdate = entry.getValue(); List<String> taskIdsToUpdate = entry.getValue();
long days = converter.convertWorkingDaysToDays(planned, entry.getKey().toDays()); referenceTask.setDue(
Instant due = planned.plus(Duration.ofDays(days)); converter.addWorkingDaysToInstant(referenceTask.getPlanned(), entry.getKey()));
referenceTask.setDue(due);
long numTasksUpdated = taskMapper.updateTaskDueDates(taskIdsToUpdate, referenceTask); long numTasksUpdated = taskMapper.updateTaskDueDates(taskIdsToUpdate, referenceTask);
if (numTasksUpdated != taskIdsToUpdate.size()) { if (numTasksUpdated != taskIdsToUpdate.size()) {
BulkLog checkResult = BulkLog checkResult =
@ -332,10 +456,11 @@ class ServiceLevelHandler {
List<AttachmentSummaryImpl> attachments, List<AttachmentSummaryImpl> attachments,
List<ClassificationWithServiceLevelResolved> List<ClassificationWithServiceLevelResolved>
allInvolvedClassificationsWithServiceLevelResolved) { allInvolvedClassificationsWithServiceLevelResolved) {
List<TaskDuration> resultingTaskDurations = new ArrayList<>(); List<TaskDuration> resultingTaskDurations = new ArrayList<>();
// Map taskId -> Set Of involved classification Ids // Map taskId -> Set Of involved classification Ids
Map<String, Set<String>> taskIdToClassificationIdsMap = Map<String, Set<String>> taskIdToClassificationIdsMap =
findAllClassificationIdsPerTask(minimalTaskSummariesAuthorizedFor, attachments); getTaskIdToClassificationsMap(minimalTaskSummariesAuthorizedFor, attachments);
// Map classificationId -> Duration // Map classificationId -> Duration
Map<String, Duration> classificationIdToDurationMap = Map<String, Duration> classificationIdToDurationMap =
allInvolvedClassificationsWithServiceLevelResolved.stream() allInvolvedClassificationsWithServiceLevelResolved.stream()
@ -347,7 +472,7 @@ class ServiceLevelHandler {
Duration duration = Duration duration =
determineMinimalDurationForATask( determineMinimalDurationForATask(
taskIdToClassificationIdsMap.get(task.getTaskId()), classificationIdToDurationMap); taskIdToClassificationIdsMap.get(task.getTaskId()), classificationIdToDurationMap);
TaskDuration taskDuration = new TaskDuration(task.getTaskId(), duration); TaskDuration taskDuration = new TaskDuration(task.getTaskId(), duration, task.getPlanned());
resultingTaskDurations.add(taskDuration); resultingTaskDurations.add(taskDuration);
} }
return resultingTaskDurations.stream() return resultingTaskDurations.stream()
@ -357,11 +482,47 @@ class ServiceLevelHandler {
Collectors.mapping(TaskDuration::getTaskId, Collectors.toList()))); Collectors.mapping(TaskDuration::getTaskId, Collectors.toList())));
} }
private Map<InstantDurationHolder, List<TaskDuration>> getTasksPerPlannedAndDuration(
List<MinimalTaskSummary> minimalTaskSummaries,
List<AttachmentSummaryImpl> attachments,
List<ClassificationWithServiceLevelResolved>
allInvolvedClassificationsWithServiceLevelResolved) {
Map<String, Duration> durationPerClassificationId =
getClassificationIdToDurationMap(allInvolvedClassificationsWithServiceLevelResolved);
List<TaskDuration> resultingTaskDurations = new ArrayList<>();
// Map taskId -> Set Of involved classification Ids
Map<String, Set<String>> taskIdClassificationIdsMap =
getTaskIdToClassificationsMap(minimalTaskSummaries, attachments);
for (MinimalTaskSummary task : minimalTaskSummaries) {
Duration duration =
determineMinimalDurationForATask(
taskIdClassificationIdsMap.get(task.getTaskId()), durationPerClassificationId);
TaskDuration taskDuration = new TaskDuration(task.getTaskId(), duration, task.getPlanned());
resultingTaskDurations.add(taskDuration);
}
return resultingTaskDurations.stream().collect(groupingBy(TaskDuration::getPlannedDuration));
}
private Map<String, Duration> getClassificationIdToDurationMap(
List<ClassificationWithServiceLevelResolved>
allInvolvedClassificationsWithServiceLevelResolved) {
// Map classificationId -> Duration
return allInvolvedClassificationsWithServiceLevelResolved.stream()
.collect(
Collectors.toMap(
ClassificationWithServiceLevelResolved::getClassificationId,
ClassificationWithServiceLevelResolved::getDurationFromClassification));
}
private Duration determineMinimalDurationForATask( private Duration determineMinimalDurationForATask(
Set<String> classificationIds, Map<String, Duration> durationPerClassificationId) { Set<String> classificationIds, Map<String, Duration> classificationIdDurationMap) {
Duration result = MAX_DURATION; Duration result = MAX_DURATION;
for (String classificationId : classificationIds) { for (String classificationId : classificationIds) {
Duration actualDuration = durationPerClassificationId.get(classificationId); Duration actualDuration = classificationIdDurationMap.get(classificationId);
if (result.compareTo(actualDuration) > 0) { if (result.compareTo(actualDuration) > 0) {
result = actualDuration; result = actualDuration;
} }
@ -370,7 +531,7 @@ class ServiceLevelHandler {
} }
// returns a map <taskId -> Set of ClassificationIds> // returns a map <taskId -> Set of ClassificationIds>
private Map<String, Set<String>> findAllClassificationIdsPerTask( private Map<String, Set<String>> getTaskIdToClassificationsMap(
List<MinimalTaskSummary> minimalTaskSummaries, List<AttachmentSummaryImpl> attachments) { List<MinimalTaskSummary> minimalTaskSummaries, List<AttachmentSummaryImpl> attachments) {
Map<String, Set<String>> resultingTaskIdToClassificationIdsMap = new HashMap<>(); Map<String, Set<String>> resultingTaskIdToClassificationIdsMap = new HashMap<>();
for (MinimalTaskSummary task : minimalTaskSummaries) { for (MinimalTaskSummary task : minimalTaskSummaries) {
@ -410,7 +571,7 @@ class ServiceLevelHandler {
: attachmentMapper.findAttachmentSummariesByTaskIds(existingTaskIdsAuthorizedFor); : attachmentMapper.findAttachmentSummariesByTaskIds(existingTaskIdsAuthorizedFor);
} }
private List<ClassificationSummary> findAllInvolvedClassifications( private List<ClassificationSummary> findAllClassificationsReferencedByTasksAndAttachments(
List<MinimalTaskSummary> existingTasksAuthorizedFor, List<MinimalTaskSummary> existingTasksAuthorizedFor,
List<AttachmentSummaryImpl> attachments) { List<AttachmentSummaryImpl> attachments) {
Set<String> classificationIds = Set<String> classificationIds =
@ -453,7 +614,7 @@ class ServiceLevelHandler {
return new DurationPrioHolder(duration, priority); return new DurationPrioHolder(duration, priority);
} }
private Set<ClassificationSummary> getInvolvedClassifications(TaskImpl taskImpl) { private Set<ClassificationSummary> getClassificationsReferencedByATask(TaskImpl taskImpl) {
Set<ClassificationSummary> classifications = Set<ClassificationSummary> classifications =
taskImpl.getAttachments() == null taskImpl.getAttachments() == null
? new HashSet<>() ? new HashSet<>()
@ -514,7 +675,132 @@ class ServiceLevelHandler {
&& newClassificationIds.containsAll(oldClassificationIds); && newClassificationIds.containsAll(oldClassificationIds);
} }
static class ClassificationWithServiceLevelResolved { static class BulkLog extends BulkOperationResults<String, TaskanaException> {
BulkLog() {
super();
}
}
static final class DurationPrioHolder {
private Duration duration;
private int priority;
DurationPrioHolder(Duration duration, int priority) {
this.duration = duration;
this.priority = priority;
}
Duration getDuration() {
return duration;
}
int getPriority() {
return priority;
}
}
private static final class TaskDuration {
private final String taskId;
private final Duration duration;
private final Instant planned;
TaskDuration(String id, Duration serviceLevel, Instant planned) {
this.taskId = id;
this.duration = serviceLevel;
this.planned = planned;
}
String getTaskId() {
return taskId;
}
Duration getDuration() {
return duration;
}
Instant getPlanned() {
return planned;
}
InstantDurationHolder getPlannedDuration() {
return new InstantDurationHolder(planned, duration);
}
}
private static final class TaskIdPriority {
private String taskId;
private int priority;
TaskIdPriority(String taskId, int priority) {
this.taskId = taskId;
this.priority = priority;
}
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public Integer getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
}
private static final class InstantDurationHolder {
private Instant planned;
private Duration duration;
InstantDurationHolder(Instant planned, Duration duration) {
this.planned = planned;
this.duration = duration;
}
public Instant getPlanned() {
return planned;
}
public void setPlanned(Instant planned) {
this.planned = planned;
}
public Duration getDuration() {
return duration;
}
public void setDuration(Duration duration) {
this.duration = duration;
}
@Override
public int hashCode() {
return Objects.hash(planned, duration);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
InstantDurationHolder other = (InstantDurationHolder) obj;
return Objects.equals(planned, other.planned) && Objects.equals(duration, other.duration);
}
}
private static final class ClassificationWithServiceLevelResolved {
private final int priority; private final int priority;
private final String classificationId; private final String classificationId;
private final Duration duration; private final Duration duration;
@ -537,47 +823,4 @@ class ServiceLevelHandler {
return priority; return priority;
} }
} }
static class TaskDuration {
private final String taskId;
private final Duration duration;
TaskDuration(String id, Duration serviceLevel) {
this.taskId = id;
this.duration = serviceLevel;
}
String getTaskId() {
return taskId;
}
Duration getDuration() {
return duration;
}
}
static class BulkLog extends BulkOperationResults<String, TaskanaException> {
BulkLog() {
super();
}
}
static class DurationPrioHolder {
private Duration duration;
private int priority;
DurationPrioHolder(Duration duration, int priority) {
this.duration = duration;
this.priority = priority;
}
Duration getDuration() {
return duration;
}
int getPriority() {
return priority;
}
}
} }