com.marand.thinkmed.medications.pharmacist.impl.PharmacistTaskHandlerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.marand.thinkmed.medications.pharmacist.impl.PharmacistTaskHandlerImpl.java

Source

/*
 * Copyright (c) 2010-2014 Marand d.o.o. (www.marand.com)
 *
 * This file is part of Think!Med Clinical Medication Management.
 *
 * Think!Med Clinical Medication Management is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Think!Med Clinical Medication Management is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Think!Med Clinical Medication Management.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.marand.thinkmed.medications.pharmacist.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.google.common.base.Preconditions;
import com.marand.ispek.bpm.service.BpmService;
import com.marand.maf.core.Opt;
import com.marand.maf.core.Pair;
import com.marand.maf.core.StringUtils;
import com.marand.openehr.util.DataValueUtils;
import com.marand.thinkmed.bpm.medications.process.PharmacySupplyProcess;
import com.marand.thinkmed.bpm.medications.process.PreparePerfusionSyringeProcess;
import com.marand.thinkmed.medications.PharmacistReviewTaskStatusEnum;
import com.marand.thinkmed.medications.PrescriptionChangeTypeEnum;
import com.marand.thinkmed.medications.TaskTypeEnum;
import com.marand.thinkmed.medications.TherapyAssigneeEnum;
import com.marand.thinkmed.medications.dao.openehr.MedicationsOpenEhrDao;
import com.marand.thinkmed.medications.dto.pharmacist.review.MedicationSupplyTypeEnum;
import com.marand.thinkmed.medications.dto.pharmacist.task.DispenseMedicationTaskSimpleDto;
import com.marand.thinkmed.medications.dto.pharmacist.task.MedicationSupplyTaskSimpleDto;
import com.marand.thinkmed.medications.pharmacist.PharmacistTaskCreator;
import com.marand.thinkmed.medications.pharmacist.PharmacistTaskHandler;
import com.marand.thinkmed.medications.pharmacist.PharmacistTaskProvider;
import com.marand.thinkmed.medications.task.DispenseMedicationTaskDef;
import com.marand.thinkmed.medications.task.MedicationsTasksProvider;
import com.marand.thinkmed.medications.task.MedsTaskDef;
import com.marand.thinkmed.medications.task.PerfusionSyringeTaskDef;
import com.marand.thinkmed.medications.task.SupplyReminderTaskDef;
import com.marand.thinkmed.medications.task.SupplyRequestStatus;
import com.marand.thinkmed.medications.task.SupplyReviewTaskDef;
import com.marand.thinkmed.medications.task.SupplyTaskDef;
import com.marand.thinkmed.medications.task.TherapyTaskDef;
import com.marand.thinkmed.process.TaskCompletedType;
import com.marand.thinkmed.process.dto.AbstractTaskDto;
import com.marand.thinkmed.process.dto.TaskDetailsEnum;
import com.marand.thinkmed.process.dto.TaskDto;
import com.marand.thinkmed.process.service.ProcessService;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Hours;
import org.joda.time.Interval;
import org.springframework.beans.factory.annotation.Required;

/**
 * @author Klavdij Lapajne
 */

public class PharmacistTaskHandlerImpl implements PharmacistTaskHandler {
    private ProcessService processService;
    private BpmService bpmService;
    private PharmacistTaskProvider pharmacistTaskProvider;
    private PharmacistTaskCreator pharmacistTaskCreator;
    private MedicationsOpenEhrDao medicationsOpenEhrDao;
    private MedicationsTasksProvider medicationsTasksProvider;

    @Required
    public void setProcessService(final ProcessService processService) {
        this.processService = processService;
    }

    @Required
    public void setPharmacistTaskProvider(final PharmacistTaskProvider pharmacistTaskProvider) {
        this.pharmacistTaskProvider = pharmacistTaskProvider;
    }

    @Required
    public void setBpmService(final BpmService bpmService) {
        this.bpmService = bpmService;
    }

    @Required
    public void setPharmacistTaskCreator(final PharmacistTaskCreator pharmacistTaskCreator) {
        this.pharmacistTaskCreator = pharmacistTaskCreator;
    }

    @Required
    public void setMedicationsOpenEhrDao(final MedicationsOpenEhrDao medicationsOpenEhrDao) {
        this.medicationsOpenEhrDao = medicationsOpenEhrDao;
    }

    @Required
    public void setMedicationsTasksProvider(final MedicationsTasksProvider medicationsTasksProvider) {
        this.medicationsTasksProvider = medicationsTasksProvider;
    }

    @Override
    public void deleteSupplyTask(final List<String> taskIds, final String userId) {
        for (final String taskId : taskIds) {
            //variables must not exist on the task, so they get set on the process
            final Map<String, Object> variables = new HashMap<>();
            variables.put(PharmacySupplyProcess.confirmResupply.name(), false);
            variables.put(PharmacySupplyProcess.createResupplyReminder.name(), false);
            processService.setVariables(taskId, variables);
            deleteMedicationTaskIfPartOfProcess(taskId, userId, TaskCompletedType.DELETED.getBpmName());
        }
    }

    @Override
    public void dismissSupplyTask(final List<String> taskIds, final String userId) {
        for (final String taskId : taskIds) {
            final Map<String, Object> variables = new HashMap<>();
            variables.put(SupplyReminderTaskDef.IS_DISMISSED.getName(), true);
            processService.setVariablesLocal(taskId, variables);
        }
    }

    @Override
    public void deleteNurseSupplyTask(final String patientId, final String taskId, final String userId) {
        final TaskDto taskDto = processService.loadTask(taskId, false);
        final String originalTherapyId = (String) taskDto.getVariables()
                .get(TherapyTaskDef.ORIGINAL_THERAPY_ID.getName());
        final List<TaskDto> nurseSupplyTasks = pharmacistTaskProvider.findNurseSupplyTasksForTherapy(patientId,
                originalTherapyId);

        boolean isSupplyDispensed = true;
        final List<String> taskIds = new ArrayList<>();
        for (final TaskDto nurseSupplyTask : nurseSupplyTasks) {
            taskIds.add(nurseSupplyTask.getId());
            if (nurseSupplyTask.getTaskExecutionStrategyId().equals(TaskTypeEnum.DISPENSE_MEDICATION.getName())) {
                isSupplyDispensed = false; //dispense task still not closed
            }
        }

        if (isSupplyDispensed) {
            throw new IllegalStateException("Can't delete task. Requested supply already dispensed!");
        } else {
            deleteSupplyTask(taskIds, userId);
        }
    }

    @Override
    public void confirmSupplyReminderTask(final String taskId, final String therapyIdAtConfirmation,
            final MedicationSupplyTypeEnum supplyTypeEnum, final Integer supplyInDays, final String userId,
            final String comment) {
        final Map<String, Object> variables = new HashMap<>();
        variables.put(PharmacySupplyProcess.supplyType.name(), supplyTypeEnum.name());
        variables.put(PharmacySupplyProcess.supplyInDays.name(), supplyInDays);
        variables.put(PharmacySupplyProcess.createResupplyReminder.name(), true);
        variables.put(PharmacySupplyProcess.confirmResupply.name(), true);
        variables.put(PharmacySupplyProcess.dispenseMedication.name(),
                supplyTypeEnum != MedicationSupplyTypeEnum.PATIENTS_OWN);
        final TaskDto taskDto = processService.loadTaskWithCustomDetails(taskId, false,
                EnumSet.of(TaskDetailsEnum.PROCESS_INFO));
        bpmService.setProcessVariables(taskDto.getProcessBusinessKey(), PharmacySupplyProcess.class, variables);

        final Map<String, Object> localVariables = new HashMap<>();
        localVariables.put(SupplyTaskDef.SUPPLY_REQUEST_COMMENT.getName(), comment);
        localVariables.put(SupplyTaskDef.TASK_CLOSED_WITH_THERAPY_ID.getName(), therapyIdAtConfirmation);
        setPerformerAndCompleteMedicationTask(taskId, userId, localVariables);
    }

    @Override
    public void editSupplyReminderTask(final String taskId, final MedicationSupplyTypeEnum supplyTypeEnum,
            final Integer supplyInDays, final String comment, final DateTime when) {
        final Map<String, Object> localVariables = new HashMap<>();
        localVariables.put(SupplyReminderTaskDef.SUPPLY_REQUEST_COMMENT.getName(), comment);
        localVariables.put(SupplyReminderTaskDef.DAYS_SUPPLY.getName(), supplyInDays);
        localVariables.put(SupplyReminderTaskDef.SUPPLY_TYPE.getName(), supplyTypeEnum.name());
        localVariables.put(SupplyReminderTaskDef.IS_DISMISSED.getName(), false);
        processService.setVariablesLocal(taskId, localVariables);
        processService.setDueDate(taskId, when.plusDays(supplyInDays));
    }

    @Override
    public void confirmSupplyReviewTask(final String patientId, final String taskId,
            final String therapyIdAtConfirmation, final boolean alreadyDispensed,
            final boolean updateSupplyRequestData, @Nullable final MedicationSupplyTypeEnum supplyTypeEnum,
            @Nullable final Integer supplyInDays, @Nullable final String comment, final String userId,
            final DateTime when) {
        if (updateSupplyRequestData) {
            Preconditions.checkArgument(!alreadyDispensed, "Trying to update already dispensed tasks!");
            Preconditions.checkNotNull(supplyTypeEnum, "Trying to update supply tasks with supplyTypeEnum == null");
            Preconditions.checkNotNull(supplyInDays, "Trying to update supply tasks with supplyInDays == null");
        }
        final Set<TaskTypeEnum> supplyTaskTypes = EnumSet.of(TaskTypeEnum.SUPPLY_REMINDER,
                TaskTypeEnum.SUPPLY_REVIEW, TaskTypeEnum.DISPENSE_MEDICATION);

        final TaskDto taskDto = processService.loadTask(taskId, false);

        final String originalTherapyId = (String) taskDto.getVariables()
                .get(TherapyTaskDef.ORIGINAL_THERAPY_ID.getName());
        final List<MedicationSupplyTaskSimpleDto> tasks = pharmacistTaskProvider.findSupplySimpleTasksForTherapy(
                null, Collections.singleton(patientId), supplyTaskTypes, originalTherapyId);

        if (!alreadyDispensed) {
            // if not already dispensed set data on dispense medication task
            final String dispenseTaskId = getTaskIdForSimpleTaskOfType(TaskTypeEnum.DISPENSE_MEDICATION,
                    TherapyAssigneeEnum.NURSE, tasks);
            if (dispenseTaskId == null) {
                throw new IllegalStateException("Related dispense task is not in active state!"); // if !alreadyDispensed, dispense task must be found here
            }
            final Map<String, Object> variables = new HashMap<>();
            variables.put(DispenseMedicationTaskDef.REQUEST_STATUS.getName(), SupplyRequestStatus.VERIFIED.name());
            if (updateSupplyRequestData) {
                variables.put(DispenseMedicationTaskDef.SUPPLY_TYPE.getName(), supplyTypeEnum.name());
                variables.put(DispenseMedicationTaskDef.DAYS_SUPPLY.getName(), supplyInDays);
                if (comment != null && !comment.isEmpty()) {
                    variables.put(DispenseMedicationTaskDef.SUPPLY_REQUEST_COMMENT.getName(), comment);
                }
            }
            processService.setVariablesLocal(dispenseTaskId, variables);
        }
        if (updateSupplyRequestData) {
            final String reminderTaskId = getTaskIdForSimpleTaskOfType(TaskTypeEnum.SUPPLY_REMINDER, null, tasks);

            // if reminder task exists set data on the task ... don't create a new one
            // if it doesn't, set data on process and tell it to create a new task
            if (reminderTaskId != null) {
                final Map<String, Object> processVariables = new HashMap<>();
                processVariables.put(PharmacySupplyProcess.createResupplyReminder.name(), false);
                processVariables.put(PharmacySupplyProcess.requesterRole.name(),
                        TherapyAssigneeEnum.PHARMACIST.name());
                bpmService.setProcessVariables(taskDto.getProcessBusinessKey(), PharmacySupplyProcess.class,
                        processVariables);
                editSupplyReminderTask(reminderTaskId, supplyTypeEnum, supplyInDays, comment, when);
            } else {
                final Map<String, Object> processVariables = new HashMap<>();
                processVariables.put(PharmacySupplyProcess.createResupplyReminder.name(), true);
                processVariables.put(PharmacySupplyProcess.requesterRole.name(),
                        TherapyAssigneeEnum.PHARMACIST.name());
                processVariables.put(PharmacySupplyProcess.supplyType.name(), supplyTypeEnum.name());
                processVariables.put(PharmacySupplyProcess.supplyInDays.name(), supplyInDays);
                processVariables.put(PharmacySupplyProcess.supplyRequestComment.name(), comment);
                bpmService.setProcessVariables(taskDto.getProcessBusinessKey(), PharmacySupplyProcess.class,
                        processVariables);
            }
        }

        final Map<String, Object> localVariables = new HashMap<>();
        localVariables.put(SupplyTaskDef.SUPPLY_REQUEST_COMMENT.getName(), comment);
        localVariables.put(SupplyTaskDef.TASK_CLOSED_WITH_THERAPY_ID.getName(), therapyIdAtConfirmation);
        setPerformerAndCompleteMedicationTask(taskId, userId, localVariables);
    }

    @Override
    public void handleReviewTaskOnTherapiesChange(@Nonnull final String patientId,
            final DateTime hospitalizationStart, @Nonnull final DateTime when, final String lastEditorName,
            final DateTime lastEditTime, final PrescriptionChangeTypeEnum changeType,
            @Nonnull final PharmacistReviewTaskStatusEnum status) {
        Preconditions.checkNotNull(patientId, "patientId");
        Preconditions.checkNotNull(when, "when");
        Preconditions.checkNotNull(status, "status");

        final DateTime dueTime = Opt.of(medicationsTasksProvider.getNextAdministrationTask(patientId, when))
                .map(AbstractTaskDto::getDueTime).orElse(null);

        final Opt<String> taskId = pharmacistTaskProvider.findPharmacistReviewTaskId(patientId);
        if (taskId.isPresent()) {
            pharmacistTaskCreator.updatePharmacistReviewTask(taskId.get(), dueTime, lastEditorName, lastEditTime);
        } else {
            pharmacistTaskCreator
                    .createPharmacistReviewTask(patientId, dueTime, lastEditorName, lastEditTime,
                            Opt.of(changeType).orElseGet(
                                    () -> getPharmacistTherapyChangeType(patientId, hospitalizationStart, when)),
                            status);
        }
    }

    private PrescriptionChangeTypeEnum getPharmacistTherapyChangeType(final String patientId,
            final DateTime hospitalizationStart, final DateTime when) {
        if (hospitalizationStart == null) {
            return PrescriptionChangeTypeEnum.NEW_ADMISSION_PRESCRIPTION;
        }

        return medicationsOpenEhrDao
                .findMedicationInstructions(patientId, new Interval(hospitalizationStart, Days.THREE), null)
                .stream().filter(Objects::nonNull).findFirst().map(Pair::getFirst)
                .map(c -> new Interval(DataValueUtils.getDateTime(c.getCompositionEventContext().getStartTime()),
                        Hours.ONE))
                .map(interval -> interval.contains(when) ? PrescriptionChangeTypeEnum.NEW_ADMISSION_PRESCRIPTION
                        : PrescriptionChangeTypeEnum.ADDITION_TO_EXISTING_PRESCRIPTION)
                .orElse(PrescriptionChangeTypeEnum.NEW_ADMISSION_PRESCRIPTION);
    }

    @Override
    public void confirmPharmacistDispenseTask(final String patientId, final String taskId,
            final String therapyIdAtConfirmation, final TherapyAssigneeEnum requesterRole,
            final SupplyRequestStatus supplyRequestStatus, final String userId) {
        if (requesterRole == TherapyAssigneeEnum.NURSE && supplyRequestStatus == SupplyRequestStatus.UNVERIFIED) {
            final TaskDto taskDto = processService.loadTask(taskId, false);
            final String originalTherapyId = (String) taskDto.getVariables()
                    .get(TherapyTaskDef.ORIGINAL_THERAPY_ID.getName());

            final List<MedicationSupplyTaskSimpleDto> tasks = pharmacistTaskProvider
                    .findSupplySimpleTasksForTherapy(null, Collections.singleton(patientId),
                            EnumSet.of(TaskTypeEnum.SUPPLY_REVIEW), originalTherapyId);

            //we should get one or zero tasks
            final String supplyReviewTaskId = tasks.isEmpty() ? null : tasks.get(0).getTaskId();
            if (supplyReviewTaskId == null) {
                throw new IllegalStateException("Related review task is not in active state!"); // if UNVERIFIED, review task must be found here
            }
            processService.setVariableLocal(supplyReviewTaskId, SupplyReviewTaskDef.ALREADY_DISPENSED.getName(),
                    true);
        }

        final Map<String, Object> localVariables = new HashMap<>();
        localVariables.put(SupplyTaskDef.TASK_CLOSED_WITH_THERAPY_ID.getName(), therapyIdAtConfirmation);
        setPerformerAndCompleteMedicationTask(taskId, userId, localVariables);
    }

    @Override
    public void deletePerfusionSyringeTask(final String taskId, final String userId) {
        final Map<String, Object> variables = new HashMap<>();
        variables.put(PreparePerfusionSyringeProcess.cancelPreparation.name(), true);
        processService.setVariables(taskId, variables);
        deleteMedicationTaskIfPartOfProcess(taskId, userId, TaskCompletedType.DELETED.getBpmName());
    }

    @Override
    public void undoPerfusionSyringeTask(final String taskId, final String userId) {
        final Map<String, Object> variables = new HashMap<>();
        variables.put(PreparePerfusionSyringeProcess.undoState.name(), true);
        processService.setVariables(taskId, variables);
        deleteMedicationTaskIfPartOfProcess(taskId, userId, TaskCompletedType.DELETED.getBpmName()); //TODO PERFUSION - ADD UNDO AS REASON
    }

    @Override
    public void confirmPerfusionSyringeTasks(final List<String> taskIds, final String userId) {
        for (final String taskId : taskIds) {
            setPerformerAndCompleteMedicationTask(taskId, userId, null);
        }
    }

    @Override
    public void setDispenseTaskPrintedTimestamp(final String taskId, final DateTime requestTimestamp) {
        final Map<String, Object> variables = new HashMap<>();
        variables.put(DispenseMedicationTaskDef.LAST_PRINTED_TIMESTAMP.getName(), requestTimestamp);
        processService.setVariablesLocal(taskId, variables);
    }

    @Override
    public void editPerfusionSyringeTask(@Nonnull final String taskId, @Nonnull final Integer numberOfSyringes,
            final boolean isUrgent, @Nonnull final DateTime dueDate, final boolean printSystemLabel) {
        StringUtils.checkNotBlank(taskId, "taskId is required");
        Preconditions.checkNotNull(numberOfSyringes, "numberOfSyringes is required");
        Preconditions.checkNotNull(dueDate, "dueDate is required");

        final Map<String, Object> processVariables = new HashMap<>();
        processVariables.put(PreparePerfusionSyringeProcess.numberOfSyringes.name(), numberOfSyringes);
        processVariables.put(PreparePerfusionSyringeProcess.isUrgent.name(), isUrgent);
        processVariables.put(PreparePerfusionSyringeProcess.dueDateTimeMillis.name(), dueDate.getMillis());
        processVariables.put(PreparePerfusionSyringeProcess.printSystemLabel.name(), printSystemLabel);
        final Map<String, Object> localVariables = new HashMap<>();
        localVariables.put(PerfusionSyringeTaskDef.NUMBER_OF_SYRINGES.getName(), numberOfSyringes);
        localVariables.put(PerfusionSyringeTaskDef.IS_URGENT.getName(), isUrgent);
        localVariables.put(PerfusionSyringeTaskDef.PRINT_SYSTEM_LABEL.getName(), printSystemLabel);

        final TaskDto taskDto = processService.loadTaskWithCustomDetails(taskId, false,
                EnumSet.of(TaskDetailsEnum.PROCESS_INFO));
        bpmService.setProcessVariables(taskDto.getProcessBusinessKey(), PreparePerfusionSyringeProcess.class,
                processVariables);
        processService.setVariablesLocal(taskId, localVariables);
        processService.setDueDate(taskId, dueDate);
    }

    @Override
    public void confirmPerfusionSyringeTasksForTherapy(final String patientId, final String userId,
            final TaskTypeEnum taskTypeEnum, final String originalTherapyId) {
        final List<TaskDto> therapyTasks = pharmacistTaskProvider.getTherapyTasks(taskTypeEnum, patientId,
                originalTherapyId);

        final List<String> taskIds = new ArrayList<>();
        for (final TaskDto task : therapyTasks) {
            taskIds.add(task.getId());
        }

        confirmPerfusionSyringeTasks(taskIds, userId);
    }

    private void deleteMedicationTaskIfPartOfProcess(final String taskId, final String userId,
            final String deleteReason) {
        //we can't delete tasks that are part of a running process ... we can close them and set delete reason by hand
        setPerformerAndCompleteMedicationTask(taskId, userId, null);
        processService.setTaskDeleteReason(taskId, deleteReason);
    }

    private void setPerformerAndCompleteMedicationTask(final String taskId, final String userId,
            @Nullable final Map<String, Object> localVariables) {
        final Map<String, Object> localVariablesWithPerformer = localVariables == null ? new HashMap<>()
                : localVariables;
        localVariablesWithPerformer.put(MedsTaskDef.PERFORMER.getName(), userId);
        processService.completeTaskAndSetVariables(taskId, localVariablesWithPerformer, true);
    }

    private String getTaskIdForSimpleTaskOfType(final TaskTypeEnum taskType,
            final TherapyAssigneeEnum requesterRole, final List<MedicationSupplyTaskSimpleDto> tasks) {
        for (final MedicationSupplyTaskSimpleDto task : tasks) {
            if (task.getTaskType() == taskType) {
                boolean isRequesterRoleMatched = true;
                if (task instanceof DispenseMedicationTaskSimpleDto) {
                    final TherapyAssigneeEnum taskRequesterRole = ((DispenseMedicationTaskSimpleDto) task)
                            .getRequesterRole();
                    isRequesterRoleMatched = taskRequesterRole != null && taskRequesterRole == requesterRole;
                }
                if (isRequesterRoleMatched) {
                    return task.getTaskId();
                }
            }
        }
        return null;
    }
}