Coverage Summary for Class: ProcessConverter (org.kitodo.data.elasticsearch.index.converter)
Class |
Class, %
|
Method, %
|
Line, %
|
ProcessConverter |
100%
(1/1)
|
87,5%
(14/16)
|
85,9%
(79/92)
|
/*
* (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
*
* This file is part of the Kitodo project.
*
* It is licensed under GNU General Public License version 3 or later.
*
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*/
package org.kitodo.data.elasticsearch.index.converter;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.kitodo.data.database.beans.Comment;
import org.kitodo.data.database.beans.Process;
import org.kitodo.data.database.beans.Task;
import org.kitodo.data.database.enums.CommentType;
import org.kitodo.data.database.enums.CorrectionComments;
import org.kitodo.data.database.enums.TaskStatus;
import org.kitodo.data.database.exceptions.DAOException;
import org.kitodo.data.database.persistence.TaskDAO;
/**
* This class provides static methods that derive basic information from a process,
* e.g. the current task progress status.
*
* <p>The derived information is needed as for indexing, such that processes can be
* filtered or sorted by these derived attributes.</p>
*/
public class ProcessConverter {
private static final Logger logger = LogManager.getLogger(ProcessConverter.class);
/**
* Returns tasks of a process that have the "in_work" status.
*
* @param process the process
* @return the list of in processing tasks
*/
private static List<Task> getTasksInWork(Process process) {
return process.getTasks().stream()
.filter(t -> TaskStatus.INWORK.equals(t.getProcessingStatus())).collect(Collectors.toList());
}
/**
* Returns tasks of a process that have "done" status.
*
* @param process the process
* @return the list of done tasks
*/
private static List<Task> getCompletedTasks(Process process) {
return process.getTasks().stream()
.filter(t -> TaskStatus.DONE.equals(t.getProcessingStatus())).collect(Collectors.toList());
}
/**
* Returns the last task that is currently or was recently worked on.
* @param process the process
* @return the last processed task (either done or in work)
*/
private static Task getLastProcessedTask(Process process) {
List<Task> tasks = getTasksInWork(process);
if (tasks.isEmpty()) {
tasks = getCompletedTasks(process);
}
tasks = tasks.stream()
.filter(t -> Objects.nonNull(t.getProcessingUser()) && Objects.nonNull(t.getProcessingBegin()))
.collect(Collectors.toList());
if (tasks.isEmpty()) {
return null;
} else {
tasks.sort(Comparator.comparing(Task::getProcessingBegin));
return tasks.get(0);
}
}
/**
* Find and adds all tasks of children processes to the list of tasks.
*
* @param process the process
* @param tasks the task list to be populated with tasks
*/
private static void findTasksOfChildProcessesRecursive(Process process, List<Task> tasks) {
tasks.addAll(process.getTasks());
List<Process> children = process.getChildren();
for (Process child : children) {
findTasksOfChildProcessesRecursive(child, tasks);
}
}
/**
* Returns a list of tasks that are the basis of calculating the status of the process.
*
* <p>For processes that have children, their respective tasks are also included.</p>
*
* @param process the process
* @param considerChildren whether to include tasks of children processes
* @return the list of tasks of the process (and potentially its children)
*/
private static List<Task> getListOfTasksForProgressCalculation(Process process, boolean considerChildren) {
// consider the tasks of the process for progress calculation
List<Task> tasks = new ArrayList<>(process.getTasks());
// if the process has children, also consider these tasks for progress calculation
if (considerChildren) {
List<Process> children = process.getChildren();
for (Process child : children) {
findTasksOfChildProcessesRecursive(child, tasks);
}
}
return tasks;
}
/**
* Counts how many tasks have a certain status.
*
* @param tasks the list of tasks
* @return a map providing the count for each task status (done, open, locked, inwork)
*/
private static Map<TaskStatus, Integer> countTasksStatusOfProcessViaBeans(List<Task> tasks) {
Map<TaskStatus, Integer> results = new HashMap<>();
int open = 0;
int inProcessing = 0;
int closed = 0;
int locked = 0;
for (Task task : tasks) {
if (task.getProcessingStatus().equals(TaskStatus.DONE)) {
closed++;
} else if (task.getProcessingStatus().equals(TaskStatus.OPEN)) {
open++;
} else if (task.getProcessingStatus().equals(TaskStatus.LOCKED)) {
locked++;
} else {
inProcessing++;
}
}
results.put(TaskStatus.DONE, closed);
results.put(TaskStatus.INWORK, inProcessing);
results.put(TaskStatus.OPEN, open);
results.put(TaskStatus.LOCKED, locked);
return results;
}
private static Map<TaskStatus, Integer> countTaskStatusOfProcess(Process process, boolean considerChildren) {
if (!considerChildren) {
// if children are of no concern, just use basic counting via beans
return countTasksStatusOfProcessViaBeans(getListOfTasksForProgressCalculation(process, considerChildren));
}
try {
// use custom SQL query to count task status including tasks of ancestor processes for performance
return new TaskDAO().countTaskStatusForProcessAndItsAncestors(process);
} catch (DAOException e) {
logger.warn("error counting task status via custom SQL query, continue with slow calculation", e);
return countTasksStatusOfProcessViaBeans(getListOfTasksForProgressCalculation(process, considerChildren));
}
}
/**
* Return the user name of the user that handled the last task of the given process (either the newest task
* INWORK or the newest DONE task, if no task is INWORK). Return null if no task is INWORK or DONE.
*
* @param process the process
* @return name of processing user
*/
public static String getLastEditingUser(Process process) {
Task lastTask = getLastProcessedTask(process);
if (Objects.isNull(lastTask)) {
return null;
} else {
return lastTask.getProcessingUser().getFullName();
}
}
/**
* Return processing begin of last processed task of given process.
*
* @param process the process
* @return processing begin of last processed task
*/
public static Date getLastProcessingBegin(Process process) {
Task lastTask = getLastProcessedTask(process);
if (Objects.isNull(lastTask)) {
return null;
} else {
return lastTask.getProcessingBegin();
}
}
/**
* Return processing end of last processed task of given process.
*
* @param process the process
* @return processing end of last processed task
*/
public static Date getLastProcessingEnd(Process process) {
Task lastTask = getLastProcessedTask(process);
if (Objects.isNull(lastTask) || TaskStatus.INWORK.equals(lastTask.getProcessingStatus())) {
return null;
} else {
return lastTask.getProcessingEnd();
}
}
/**
* Return whether there is a correction comment and whether it has been corrected as status.
*
* @param process the process being checked for its correction comment status
* @return an enum representing the status
*/
public static CorrectionComments getCorrectionCommentStatus(Process process) {
if (Objects.isNull(process)) {
return CorrectionComments.NO_CORRECTION_COMMENTS;
}
List<Comment> correctionComments = process.getComments()
.stream().filter(c -> CommentType.ERROR.equals(c.getType())).collect(Collectors.toList());
if (correctionComments.size() < 1) {
return CorrectionComments.NO_CORRECTION_COMMENTS;
} else if (correctionComments.stream().anyMatch(c -> !c.isCorrected())) {
return CorrectionComments.OPEN_CORRECTION_COMMENTS;
} else {
return CorrectionComments.NO_OPEN_CORRECTION_COMMENTS;
}
}
/**
* Returns the percentaged task progress of a process as a map of doubles.
*
* @param process the process
* @param considerChildren whether to also count tasks of children processes
* @return a map providing the percentage of tasks having a certain status (done, open, locked, inwork)
*/
public static Map<TaskStatus, Double> getTaskProgressPercentageOfProcess(Process process, boolean considerChildren) {
Map<TaskStatus, Integer> counts = countTaskStatusOfProcess(process, considerChildren);
int total = counts.values().stream().mapToInt(Integer::intValue).sum();
// report processes without any tasks as if they had a single locked task
if (total == 0) {
counts.put(TaskStatus.LOCKED, 1);
total = 1;
}
Map<TaskStatus, Double> percentages = new HashMap<>();
percentages.put(TaskStatus.DONE, 100.0 * (double)counts.get(TaskStatus.DONE) / (double) total);
percentages.put(TaskStatus.INWORK, 100.0 * (double)counts.get(TaskStatus.INWORK) / (double) total);
percentages.put(TaskStatus.OPEN, 100.0 * (double)counts.get(TaskStatus.OPEN) / (double) total);
percentages.put(TaskStatus.LOCKED, 100.0 * (double)counts.get(TaskStatus.LOCKED) / (double) total);
return percentages;
}
/**
* Return a string representing the combined progress of a process.
*
* <p>It consists of 3-digit percentage numbers (e.g. 000, 025, 100)
* for each task status (DONE, INWORK, OPEN, LOCKED). For example, the status
* "000000025075" means that 25% of tasks are open, and 75% of tasks are locked.</p>
*
* @param percentages the task percentages as calculated with getTaskProgressPercentageOfProcess
* @return string the string representing the combined progress of the process
*/
public static String getCombinedProgressFromTaskPercentages(Map<TaskStatus, Double> percentages) {
DecimalFormat decimalFormat = new DecimalFormat("#000");
return decimalFormat.format(percentages.get(TaskStatus.DONE))
+ decimalFormat.format(percentages.get(TaskStatus.INWORK))
+ decimalFormat.format(percentages.get(TaskStatus.OPEN))
+ decimalFormat.format(percentages.get(TaskStatus.LOCKED));
}
/**
* Return a string representing the combined progress of a process.
*
* <p>See `ProcessConverter.getCombinedProgressFromTaskPercentages`</p>
*
* @param process the process
* @param considerChildren whether to also count tasks of children processes
* @return string the string representing the combined progress of the process
*/
public static String getCombinedProgressAsString(Process process, boolean considerChildren) {
return getCombinedProgressFromTaskPercentages(
getTaskProgressPercentageOfProcess(process, considerChildren)
);
}
}