Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -782,77 +782,6 @@ public void setOcrdWorkflowId(String ocrdWorkflowId) {
this.ocrdWorkflowId = ocrdWorkflowId;
}

/**
* Returns the percentage of tasks in the process that are completed. The
* total of tasks awaiting preconditions, startable, in progress, and
* completed is {@code 100.0d}.
*
* @return percentage of tasks completed
*/
public Double getProgressClosed() {
if (CollectionUtils.isEmpty(tasks) && !hasChildren()) {
return 0.0;
}
return getTaskProgress(MAX_AGE_NANOSS).get(TaskStatus.DONE);
}

/**
* Returns the percentage of tasks in the process that are currently being
* processed. The progress total of tasks waiting for preconditions,
* startable, in progress, and completed is {@code 100.0d}.
*
* @return percentage of tasks in progress
*/
public Double getProgressInProcessing() {
if (CollectionUtils.isEmpty(tasks) && !hasChildren()) {
return 0.0;
}
return getTaskProgress(MAX_AGE_NANOSS).get(TaskStatus.INWORK);
}

/**
* Returns the percentage of the process's tasks that are now ready to be
* processed but have not yet been started. The progress total of tasks
* waiting for preconditions, startable, in progress, and completed is
* {@code 100.0d}.
*
* @return percentage of startable tasks
*/
public Double getProgressOpen() {
if (CollectionUtils.isEmpty(tasks) && !hasChildren()) {
return 0.0;
}
return getTaskProgress(MAX_AGE_NANOSS).get(TaskStatus.OPEN);
}

private Map<TaskStatus, Double> getTaskProgress(long maxAgeNanos) {
long now = System.nanoTime();
if (Objects.isNull(this.taskProgress) || now - taskProgress.getLeft() > maxAgeNanos) {
Map<TaskStatus, Double> taskProgress = ProcessConverter.getTaskProgressPercentageOfProcess(this, true);
this.taskProgress = Pair.of(System.nanoTime(), taskProgress);
} else {
this.taskProgress = Pair.of(now, taskProgress.getValue());
}
Map<TaskStatus, Double> value = taskProgress.getValue();
return value;
}

/**
* Returns a coded overview of the progress of the process. The larger the
* number, the more advanced the process is, so it can be used to sort by
* progress. The numeric code consists of twelve digits, each three digits
* from 000 to 100 indicate the percentage of tasks completed, currently in
* progress, ready to start and not yet ready, in that order. For example,
* 000000025075 means that 25% of the tasks are ready to be started and 75%
* of the tasks are not yet ready to be started because previous tasks have
* not yet been processed.
*
* @return overview of the processing status
*/
public String getProgressCombined() {
return ProcessConverter.getCombinedProgressFromTaskPercentages(getTaskProgress(MAX_AGE_NANOSS));
}

/**
* Returns the record number of the parent process, if any. Is {@code 0} if
* there is no parent process above.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

package org.kitodo.data.database.persistence;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -158,7 +160,7 @@ public Map<TaskStatus, Integer> countTaskStatusForProcessAndItsAncestors(Process
if (Objects.isNull(process.getId())) {
throw new DAOException("can not count task status for process that has id of null");
}

// initialize counts
Map<TaskStatus, Integer> counts = new HashMap<>();
counts.put(TaskStatus.OPEN, 0);
Expand Down Expand Up @@ -195,4 +197,69 @@ public Map<TaskStatus, Integer> countTaskStatusForProcessAndItsAncestors(Process
throw new DAOException(e);
}
}

/**
* Loads task status counts for the given processes including all their descendant processes.
*
* <p>The result maps each root process ID to a count per TaskStatus.</p>
*
* @param processIds the IDs of the root processes to query
* @return a map of process ID to task status counts
*/
@SuppressWarnings("unchecked")
public Map<Integer, EnumMap<TaskStatus, Integer>> loadTaskStatusCountsForProcesses(
List<Integer> processIds) throws DAOException {
Map<Integer, EnumMap<TaskStatus, Integer>> result = new HashMap<>();
if (processIds == null || processIds.isEmpty()) {
return result;
}
Stopwatch stopwatch = new Stopwatch(this,"loadTaskStatusCountsForProcesses",
"processIds", processIds.toString());
try (Session session = HibernateUtil.getSession()) {
NativeQuery<Object[]> query = session.createNativeQuery(
"WITH RECURSIVE process_tree (root_id, id) AS ("
+ " SELECT p.id, p.id FROM process p WHERE p.id IN (:ids) "
+ " UNION ALL "
+ " SELECT pt.root_id, c.id "
+ " FROM process c "
+ " JOIN process_tree pt ON c.parent_id = pt.id"
+ ") "
+ "SELECT "
+ " pt.root_id AS root_id, "
+ " t.processingStatus AS processingStatus, "
+ " COUNT(t.id) AS cnt "
+ "FROM process_tree pt "
+ "LEFT JOIN task t ON t.process_id = pt.id "
+ "GROUP BY pt.root_id, t.processingStatus"
);
query.setParameter("ids", processIds);
query.addScalar("root_id", StandardBasicTypes.INTEGER);
query.addScalar("processingStatus", StandardBasicTypes.INTEGER);
query.addScalar("cnt", StandardBasicTypes.INTEGER);

List<Object[]> rows = query.list();
for (Object[] row : rows) {
Integer rootId = (Integer) row[0];
Integer statusValue = (Integer) row[1];
Integer count = (Integer) row[2];

EnumMap<TaskStatus, Integer> map =
result.computeIfAbsent(rootId, id -> createEmptyStatusMap());
if (statusValue != null) {
map.put(TaskStatus.getStatusFromValue(statusValue), count);
}
}
return stopwatch.stop(result);
} catch (PersistenceException e) {
throw new DAOException(e);
}
}

private static EnumMap<TaskStatus, Integer> createEmptyStatusMap() {
EnumMap<TaskStatus, Integer> map = new EnumMap<>(TaskStatus.class);
for (TaskStatus s : TaskStatus.values()) {
map.put(s, 0);
}
return map;
}
}
14 changes: 0 additions & 14 deletions Kitodo/src/main/java/org/kitodo/production/forms/ProcessForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -1054,20 +1054,6 @@ public void setFilter(String filter) {
stopwatch.stop();
}

/**
* Returns a String containing titles of all current tasks of the given process, e.g. "OPEN" tasks and tasks
* "INWORK".
*
* @param process
* process for which current task titles are returned
* @return String containing titles of current tasks of given process
*/
public String getCurrentTaskTitles(Process process) {
Stopwatch stopwatch = new Stopwatch(this.getClass(), process, "getCurrentTaskTitles");
return stopwatch.stop(ServiceManager.getProcessService().createProgressTooltip(process));

}

/**
* Get all parent processes recursively for the given process.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand All @@ -29,7 +30,9 @@
import org.kitodo.config.ConfigCore;
import org.kitodo.config.enums.ParameterCore;
import org.kitodo.data.database.beans.Process;
import org.kitodo.data.database.enums.TaskStatus;
import org.kitodo.data.database.exceptions.DAOException;
import org.kitodo.data.database.persistence.TaskDAO;
import org.kitodo.export.ExportDms;
import org.kitodo.production.enums.ChartMode;
import org.kitodo.production.enums.ObjectType;
Expand Down Expand Up @@ -63,10 +66,12 @@ public class ProcessListBaseView extends BaseForm {
DeleteProcessDialog deleteProcessDialog = new DeleteProcessDialog();

private final HashMap<Integer, Boolean> exportable = new HashMap<>();
private static final String NL = "\n";

boolean allSelected = false;
HashSet<Integer> excludedProcessIds = new HashSet<>();


/**
* Constructor.
*/
Expand Down Expand Up @@ -164,6 +169,158 @@ public void showDurationOfTasks() {
stopwatch.stop();
}

private LazyProcessModel getLazyProcessModel() {
return (LazyProcessModel) this.lazyBeanModel;
}

/**
* Checks whether the given process has any tasks at all.
*
* @param process the process to check
* @return true if at least one task exists, otherwise false
*/
public boolean hasAnyTasks(Process process) {
Map<TaskStatus, Integer> counts = getCachedTaskStatusCounts(process);
return counts.values().stream().mapToInt(Integer::intValue).sum() > 0;
}

/**
* Checks whether the given process has child processes.
*
* @param process the process to check
* @return true if the process has children, otherwise false
*/
public boolean hasChildren(Process process) {
return getLazyProcessModel().getProcessesWithChildren().contains(process.getId());
}

/**
* Returns the titles of open and in-work tasks for the given process.
*
* <p>For parent processes, no task titles are returned.</p>
*
* @param process the process to get task titles for
* @return formatted task titles or an empty string if none exist
*/
public String getCurrentTaskTitles(Process process) {
Map<TaskStatus, List<String>> titles =
getLazyProcessModel().getTaskTitleCache().get(process.getId());

if (hasChildren(process)) {
return null;
}

if (Objects.isNull(titles) || titles.isEmpty()) {
return "";
}

StringBuilder sb = new StringBuilder();
appendTitles(sb, TaskStatus.OPEN, titles);
appendTitles(sb, TaskStatus.INWORK, titles);
return sb.toString();
}

/**
* Appends task titles of the given status to the provided StringBuilder.
*
* @param sb the StringBuilder to append to
* @param status the task status to append
* @param titles task titles grouped by status
*/
private void appendTitles(StringBuilder sb,
TaskStatus status,
Map<TaskStatus, List<String>> titles) {
List<String> list = titles.get(status);
if (Objects.isNull(list) || list.isEmpty()) {
return;
}
if (sb.length() > 0) {
sb.append(NL);
}
sb.append(Helper.getTranslation(status.getTitle())).append(":");
for (String t : list) {
sb.append(NL).append(" - ").append(Helper.getTranslation(t));
}
}

/**
* Returns cached task status counts for the given process.
*
* <p>If no cached data exists, a fallback database query is executed.</p>
*
* @param process the process to get task status counts for
* @return a map of task status to count
*/
public Map<TaskStatus, Integer> getCachedTaskStatusCounts(Process process) {
LazyProcessModel model = getLazyProcessModel();
EnumMap<TaskStatus, Integer> cached = model.getTaskStatusCounts(process);

if (Objects.nonNull(cached)) {
return cached;
}
// fallback (should rarely happen)
try {
return new TaskDAO().countTaskStatusForProcessAndItsAncestors(process);
} catch (DAOException e) {
logger.warn("Fallback task status counting failed", e);
return Map.of();
}
}

/**
* Calculates the percentage of tasks with the given status.
*
* @param process the process to calculate progress for
* @param status the task status to calculate
* @return progress percentage for the given status
*/
public double progress(Process process, TaskStatus status) {
Map<TaskStatus, Integer> counts = getCachedTaskStatusCounts(process);
int total = counts.values().stream().mapToInt(Integer::intValue).sum();
if (total == 0) {
counts.put(TaskStatus.LOCKED, 1);
total = 1;
}
return 100.0 * counts.getOrDefault(status, 0) / total;
}

/**
* Returns the percentage of tasks in the process that are completed. The
* total of tasks awaiting preconditions, startable, in progress, and
* completed is {@code 100.0d}.
*
* @param process the process
* @return percentage of tasks completed
*/
public double progressClosed(Process process) {
return progress(process, TaskStatus.DONE);
}

/**
* Returns the percentage of tasks in the process that are currently being
* processed. The progress total of tasks waiting for preconditions,
* startable, in progress, and completed is {@code 100.0d}.
*
* @param process the process
* @return percentage of tasks in progress
*/
public double progressInProcessing(Process process) {
return progress(process, TaskStatus.INWORK);
}

/**
* Returns the percentage of the process's tasks that are now ready to be
* processed but have not yet been started. The progress total of tasks
* waiting for preconditions, startable, in progress, and completed is
* {@code 100.0d}.
*
* @param process the process
* @return percentage of startable tasks
*/
public double progressOpen(Process process) {
return progress(process, TaskStatus.OPEN);
}

/**
* Shows the state of volumes from the selected processes.
*/
Expand Down Expand Up @@ -622,7 +779,12 @@ public boolean canBeExported(Process process) {
Stopwatch stopwatch = new Stopwatch(this.getClass(), process, "canBeExported");
try {
if (!exportable.containsKey(process.getId())) {
exportable.put(process.getId(), ProcessService.canBeExported(process));
boolean processHasChildren = hasChildren(process);
if (processHasChildren) {
exportable.put(process.getId(), true);
} else {
exportable.put(process.getId(), ProcessService.canBeExported(process, false));
}
}
return stopwatch.stop(exportable.get(process.getId()));
} catch (DAOException e) {
Expand Down
Loading