All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.uhndata.cards.qsets.api.QuestionnaireSet Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package io.uhndata.cards.qsets.api;

import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

import javax.jcr.Node;
import javax.jcr.RepositoryException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A Questionnaire Set is a collection of related questionnaires. Other than the list of actual questionnaires to be
 * filled in, it also contains rules about how often the questionnaire set can/should be filled in, and may have an
 * associated date if this particular instance has one, for example if it's a survey filled in by a patient before,
 * during, or after a visit to a clinic. A questionnaire set will not be created if it "conflicts" with existing
 * questionnaire sets. An instance of this interface is usually associated with a {@code cards:QuestionnaireSet} node,
 * but it can store any list of questionnaires, not just the ones strictly listed in the definition, for example the
 * list of questionnaire for which forms already exist, or the list of questionnaires which need to be created given a
 * {@code cards:QuestionnaireSet} definition and the forms already present for the subject.
 *
 * @version $Id: fa4296f6dd1d6285b117ccc2a480d2af2ea629be $
 * @since 0.9.25
 */
public interface QuestionnaireSet extends Collection
{
    // Constants for the QuestionnaireSet data type

    /** The primary node type for a Questionnaire Set. */
    String NODETYPE = "cards:QuestionnaireSet";

    /** The name of the property of a QuestionnaireSet node that specifies the conflict mode. */
    String CONFLICT_MODE_PROPERTY = "conflictMode";

    /**
     * The name of the property of a QuestionnaireSet node that specifies if conflicts only apply to the same type of
     * set, or any set.
     */
    String FREQUENCY_IGNORE_SET_PROPERTY = "frequencyIgnoreClinic";

    /**
     * The conflict mode influences when an existing form causes a conflict with a new questionnaire set.
     *
     * @version $Id: fa4296f6dd1d6285b117ccc2a480d2af2ea629be $
     */
    enum ConflictMode
    {
        /**
         * This ignores the listed conflicts and actual forms, a conflict simply means that another questionnaire set
         * instance within the expected timeframe exists.
         */
        CONFLICT_ANY("any"),
        /**
         * Look at the listed questionnaire conflicts when considering if an existing questionnaire set instance causes
         * a conflict.
         */
        CONFLICT_ANY_LISTED("anyListed");

        private static final Logger LOGGER = LoggerFactory.getLogger(ConflictMode.class);

        private final String label;

        ConflictMode(final String label)
        {
            this.label = label;
        }

        /**
         * Returns the conflict mode for the given label.
         *
         * @param label a string, one of the labels of the supported conflict modes
         * @return a conflict mode, {@code CONFLICT_ANY_LISTED} by default
         */
        public static ConflictMode parse(String label)
        {
            return Stream.of(values()).filter(mode -> mode.label.equals(label)).findFirst().orElse(CONFLICT_ANY_LISTED);
        }

        /**
         * Extract the conflict mode specified in the given node.
         *
         * @param definition a JCR node of type {@code cards:QuestionnaireSet}
         * @return a conflict mode, {@code CONFLICT_ANY_LISTED} if not specified in the node
         */
        public static ConflictMode valueOf(Node definition)
        {
            try {
                if (definition != null && definition.hasProperty(CONFLICT_MODE_PROPERTY)) {
                    return parse(definition.getProperty(CONFLICT_MODE_PROPERTY).getString());
                }
            } catch (RepositoryException e) {
                LOGGER.warn("Invalid conflict mode for {}", definition);
            }
            return CONFLICT_ANY_LISTED;
        }
    }

    /**
     * The JCR node defining this questionnaire set.
     *
     * @return a JCR node of type {@code cards:QuestionnaireSet}, may be {@code null} if no definition is associated
     *         with this particular set
     */
    Node getDefinition();

    /**
     * Retrieve all conflict definitions for this questionnaire set.
     *
     * @return a set of conflict definitions, may be empty
     */
    Set getConflicts();

    /**
     * Add a new conflict to this questionnaire set.
     *
     * @param conflict the conflict definition
     */
    void addConflict(QuestionnaireConflict conflict);

    /**
     * Retrieve the conflict mode for this questionnaire set.
     *
     * @return a conflict mode
     */
    ConflictMode getConflictMode();

    /**
     * Whether forms can constitute a conflict only if they belong to an instance of the same questionnaire set, or any
     * form more recent than the specified frequency is a conflict regardless of its questionnaire set.
     *
     * @return {@code false} if a conflict can only arise from a form for the same questionnaire set, {@code false}
     *         otherwise
     */
    boolean conflictIgnoresSet();

    /**
     * Retrieve all members of this questionnaire set.
     *
     * @return an ordered list of questionnaire references, may be empty
     */
    List getQuestionnaires();

    /**
     * Get an iterator over all the questionnaires in this set.
     *
     * @return an iterator over the questionnaires in this set
     * @see java.util.Collection#iterator()
     */
    @Override
    default Iterator iterator()
    {
        return getQuestionnaires().iterator();
    }

    /**
     * Get an array with all the questionnaires in this set.
     *
     * @see java.util.Collection#toArray()
     */
    @Override
    default Object[] toArray()
    {
        return getQuestionnaires().toArray();
    }

    /**
     * Get an array with all the questionnaires in this set.
     *
     * @see java.util.Collection#toArray(java.lang.Object[])
     */
    @Override
    default  T[] toArray(T[] a)
    {
        return getQuestionnaires().toArray(a);
    }

    /**
     * Retrieve the count of members in this questionnaire set.
     *
     * @return a positive number, may be 0
     */
    @Override
    default int size()
    {
        return getQuestionnaires().size();
    }

    /**
     * Check if the list of questionnaires is empty.
     *
     * @return {@code true} if there are no questionnaires in this set, {@code false} otherwise
     */
    @Override
    default boolean isEmpty()
    {
        return size() == 0;
    }

    /**
     * Check if a questionnaire is part of this questionnaire set.
     *
     * @param questionnairePath a path to a JCR questionnaire node
     * @return {@code true} if the questionnaire belongs to this set, {@code false} otherwise
     */
    boolean containsQuestionnaire(String questionnairePath);

    /**
     * Check if a questionnaire is part of this questionnaire set. This method accepts a {@link QuestionnaireRef}, a JCR
     * {@code Node}, or a String with the path to the questionnaire.
     *
     * @param o a {@code QuestionnaireRef}, {@code Node} or just a path to a questionnaire node
     * @return {@code true} if the questionnaire belongs to this set, {@code false} otherwise
     */
    @Override
    default boolean contains(Object o)
    {
        if (o instanceof QuestionnaireRef) {
            return containsQuestionnaire(((QuestionnaireRef) o).getQuestionnairePath());
        } else if (o instanceof Node) {
            try {
                return containsQuestionnaire(((Node) o).getPath());
            } catch (RepositoryException e) {
                // Shouldn't happen
            }
        } else if (o instanceof String) {
            return containsQuestionnaire((String) o);
        }
        return false;
    }

    /**
     * Check if all of the questionnaires in the provided collection are part of this questionnaire set. This method
     * accepts {@link QuestionnaireRef}, JCR {@code Node}, or String with the path to the questionnaire.
     *
     * @param c a collection of {@code QuestionnaireRef}s, {@code Node}s, or just paths to questionnaire nodes
     * @return {@code true} if all of the questionnaires belongs to this set, {@code false} otherwise
     */
    @Override
    default boolean containsAll(Collection c)
    {
        return c.stream().allMatch(this::contains);
    }

    /**
     * Retrieve the questionnaire reference definition for the given questionnaire.
     *
     * @param questionnairePath a path to a JCR questionnaire node, should be part of the questionnaire set
     * @return the definition for this questionnaire reference, {@code null} if the specified questionnaire isn't part
     *         of this set
     */
    QuestionnaireRef getQuestionnaire(String questionnairePath);

    /**
     * Add a new questionnaire to this questionnaire set.
     *
     * @param reference the definition for the new questionnaire reference
     */
    void addQuestionnaire(QuestionnaireRef reference);

    /**
     * Add a questionnaire to this questionnaire set.
     *
     * @param reference a {@code QuestionnaireRef}
     * @return {@code true} if the questionnaire was added to the set, {@code false} otherwise
     */
    @Override
    default boolean add(QuestionnaireRef reference)
    {
        addQuestionnaire(reference);
        return true;
    }

    /**
     * Add all of the questionnaires in the provided collection to this questionnaire set.
     *
     * @param questionnaires a collection of {@code QuestionnaireRef}s
     * @return {@code true} if at least one of the questionnaires was added to the set, {@code false} otherwise
     */
    @Override
    default boolean addAll(Collection questionnaires)
    {
        questionnaires.forEach(this::addQuestionnaire);
        return true;
    }

    /**
     * Remove a questionnaire from this questionnaire set.
     *
     * @param questionnairePath a path to a JCR questionnaire node
     * @return {@code true} if the questionnaire was part of the set and was removed, {@code false} otherwise
     */
    boolean removeQuestionnaire(String questionnairePath);

    /**
     * Remove a questionnaire from this questionnaire set. This method accepts a {@link QuestionnaireRef}, a JCR
     * {@code Node}, or a String with the path to the questionnaire.
     *
     * @param o a {@code QuestionnaireRef}, {@code Node} or just a path to a questionnaire node
     * @return {@code true} if the questionnaire was part of the set and was removed, {@code false} otherwise
     */
    @Override
    default boolean remove(Object o)
    {
        if (o instanceof QuestionnaireRef) {
            return removeQuestionnaire(((QuestionnaireRef) o).getQuestionnairePath());
        } else if (o instanceof Node) {
            try {
                return removeQuestionnaire(((Node) o).getPath());
            } catch (RepositoryException e) {
                // Shouldn't happen
            }
        } else if (o instanceof String) {
            return removeQuestionnaire((String) o);
        }
        return false;
    }

    /**
     * Remove all of the questionnaires in the provided collection from this questionnaire set. This method accepts
     * {@link QuestionnaireRef}, JCR {@code Node}, or String with the path to the questionnaire.
     *
     * @param c a collection of {@code QuestionnaireRef}s, {@code Node}s, or just paths to questionnaire nodes
     * @return {@code true} if at least one of the questionnaires were part of the set and were removed, {@code false}
     *         otherwise
     */
    @Override
    default boolean removeAll(Collection c)
    {
        return c.stream().anyMatch(this::remove);
    }

    @Override
    default boolean retainAll(Collection c)
    {
        final Iterator it = iterator();
        while (it.hasNext()) {
            final String path = it.next().getQuestionnairePath();
            if (!c.stream().anyMatch(o -> {
                if (o instanceof QuestionnaireRef) {
                    return path.equals(((QuestionnaireRef) o).getQuestionnairePath());
                } else if (o instanceof Node) {
                    try {
                        return path.equals(((Node) o).getPath());
                    } catch (RepositoryException e) {
                        // Shouldn't happen
                    }
                } else if (o instanceof String) {
                    return path.equals(o);
                }
                return false;
            })) {
                it.remove();
            }
        }
        return false;
    }

    /**
     * Remove all the questionnaires from this set.
     *
     * @see java.util.Collection#clear()
     */
    @Override
    default void clear()
    {
        getQuestionnaires().clear();
    }

    /**
     * Retrieve the date associated with this set, if any.
     *
     * @return a date, or {@code null}
     */
    Calendar getAssociatedDate();

    /**
     * Remove any questionnaires from the questionnaire set which do not need to be created per their frequency
     * requirements and another existing questionnaire set. This changes the questionnaire set internal data.
     *
     * @param other another questionnaire set already existing in the system, with its own associated date
     */
    void pruneConflicts(QuestionnaireSet other);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy