medsavant.uhn.cancer.UserCommentApp.java Source code

Java tutorial

Introduction

Here is the source code for medsavant.uhn.cancer.UserCommentApp.java

Source

/**
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This software 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 Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */
package medsavant.uhn.cancer;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.rmi.RemoteException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ut.biolab.medsavant.MedSavantClient;
import org.ut.biolab.medsavant.client.app.api.AppNotInitializedException;
import org.ut.biolab.medsavant.client.app.api.AppRoleManagerBuilder;
import org.ut.biolab.medsavant.client.app.api.AppRoleManagerBuilder.AppRoleManager;
import org.ut.biolab.medsavant.client.project.ProjectController;
import org.ut.biolab.medsavant.client.reference.ReferenceController;
import org.ut.biolab.medsavant.client.view.MedSavantFrame;
import org.ut.biolab.medsavant.client.view.component.WaitPanel;
import org.ut.biolab.medsavant.client.view.images.IconFactory;
import org.ut.biolab.medsavant.client.view.login.LoginController;
import org.ut.biolab.medsavant.shared.appapi.MedSavantVariantInspectorApp;
import org.ut.biolab.medsavant.shared.format.UserRole;
import org.ut.biolab.medsavant.shared.model.UserComment;
import org.ut.biolab.medsavant.shared.model.UserCommentGroup;
import org.ut.biolab.medsavant.shared.model.OntologyTerm;
import org.ut.biolab.medsavant.shared.model.OntologyType;
import org.ut.biolab.medsavant.shared.model.SessionExpiredException;
import org.ut.biolab.medsavant.shared.model.UserLevel;
import org.ut.biolab.medsavant.shared.vcf.VariantRecord;
import savant.api.util.DialogUtils;

public class UserCommentApp extends MedSavantVariantInspectorApp {

    private static final Log LOG = LogFactory.getLog(UserCommentApp.class);
    private static final OntologyType DEFAULT_ONTOLOGY_TYPE = OntologyType.HPO;
    private static final String NAME = "Cancer Workflow - User Comments";
    private static final String TITLE = "User Comments for this position ";
    private static final int ICON_WIDTH = 16;
    private static final int ICON_HEIGHT = 16;
    private static final int COMMENT_SEPARATOR_HEIGHT = 10;
    private static final int ONTOLOGY_SEPARATOR_HEIGHT = 20;
    private static final ImageIcon REPLY_TO_ONTOLOGY_ICON = IconFactory.getInstance()
            .getIcon(IconFactory.StandardIcon.ACTION_ON_TOOLBAR);
    private static final ImageIcon EDIT_STATUS_BUTTON_ICON = IconFactory.getInstance()
            .getIcon(IconFactory.StandardIcon.EDIT);
    private static final int COMMENTTEXT_PREFERRED_WIDTH = 200;
    private static final int COMMENTTEXT_PREFERRED_HEIGHT = 100;

    private Set<UserRole> rolesForThisUser; //All roles associated to this user. (user running the app).

    private final JPanel panel;

    public static OntologyType getDefaultOntologyType() {
        return DEFAULT_ONTOLOGY_TYPE;
    }

    public static final String GENETIC_COUNSELLOR_ROLENAME = "Genetic Counsellor";
    private static final String GENETIC_COUNSELLOR_DESCRIPTION = "Genetic Counsellor";
    private static final Set<UserLevel> GENETIC_COUNSELLOR_USERLEVELS = EnumSet.of(UserLevel.ADMIN);

    public static final String TECHNICIAN_ROLENAME = "Technician";
    private static final String TECHNICIAN_DESCRIPTION = "Technician";
    private static final Set<UserLevel> TECHNICIAN_USERLEVELS = EnumSet.allOf(UserLevel.class);

    public static final String RESIDENT_ROLENAME = "Resident";
    private static final String RESIDENT_DESCRIPTION = "Resident";
    private static final Set<UserLevel> RESIDENT_USERLEVELS = EnumSet.of(UserLevel.ADMIN, UserLevel.USER);

    private static AppRoleManager roleManager;

    static AppRoleManager getRoleManager() {
        return roleManager;
    }

    public UserCommentApp() throws AppNotInitializedException {
        System.out.println("User Comment App initialized.");
        panel = new JPanel();
        panel.setLayout(new BorderLayout());
        JPanel innerPanel = new WaitPanel("Loading...");
        panel.add(innerPanel, BorderLayout.CENTER);
        try {
            if (roleManager == null) {
                AppRoleManagerBuilder armBuilder = new AppRoleManagerBuilder();
                roleManager = armBuilder
                        .addRole(GENETIC_COUNSELLOR_ROLENAME, GENETIC_COUNSELLOR_DESCRIPTION,
                                GENETIC_COUNSELLOR_USERLEVELS)
                        .addRole(RESIDENT_ROLENAME, RESIDENT_DESCRIPTION, RESIDENT_USERLEVELS)
                        .addDefaultRole(TECHNICIAN_ROLENAME, TECHNICIAN_DESCRIPTION, TECHNICIAN_USERLEVELS)
                        .autoAssignRolesToExistingUsers(true).build();
            }
        } catch (AppNotInitializedException anie) {
            LOG.error(anie);
            DialogUtils.displayError("The app '" + getTitle()
                    + "' requires initialization by the project administrator.  Some features will be disabled.");
        } catch (Exception ex) {
            ex.printStackTrace();
            LOG.error("Couldn't fetch this user's roles from database");
            DialogUtils.displayError("Error setting up user roles for the App " + getTitle());
        }
    }

    private void replyToOntology(VariantRecord vr, OntologyTerm ot) {
        JDialog acd = new AddNewCommentDialog(MedSavantFrame.getInstance(), vr, ot);
        acd.setVisible(true);
    }

    private void editStatus(UserCommentGroup lcg, UserComment lc) {
        JDialog scd = new SetCommentStatusDialog(MedSavantFrame.getInstance(), lcg, lc);
        scd.setVisible(true);
    }

    //Define horizontal panel with ontology title, and button to
    //post to the ontology.
    private JPanel getOntologyTitlePanel(final VariantRecord vr, final OntologyTerm ot) {
        String header = ot.getID() + " - " + ot.getName(); //ontology title.

        JPanel ontologyTitlePanel = new JPanel();
        ontologyTitlePanel.setLayout(new BoxLayout(ontologyTitlePanel, BoxLayout.X_AXIS));
        ontologyTitlePanel.add(new JLabel(header));
        ontologyTitlePanel.add(Box.createHorizontalGlue());

        JButton replyToOntologyButton = new JButton(new ImageIcon(REPLY_TO_ONTOLOGY_ICON.getImage()
                .getScaledInstance(ICON_WIDTH, ICON_HEIGHT, java.awt.Image.SCALE_SMOOTH)));

        replyToOntologyButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                replyToOntology(vr, ot);
            }
        });

        ontologyTitlePanel.add(replyToOntologyButton);
        return ontologyTitlePanel;
    }

    private JSeparator getCommentSeparator() {
        JSeparator js = new JSeparator();
        js.setPreferredSize(new Dimension(panel.getWidth(), COMMENT_SEPARATOR_HEIGHT));
        return js;
    }

    private JSeparator getOntologySeparator() {
        JSeparator js = new JSeparator();
        js.setPreferredSize(new Dimension(panel.getWidth(), ONTOLOGY_SEPARATOR_HEIGHT));
        return js;
    }

    private JPanel getHeaderPanel(UserComment lc) {
        JPanel headerPanel = new JPanel();
        headerPanel.setLayout(new BoxLayout(headerPanel, BoxLayout.Y_AXIS));
        JPanel leftJustPanel = new JPanel();
        leftJustPanel.setLayout(new BoxLayout(leftJustPanel, BoxLayout.X_AXIS));
        leftJustPanel.add(new JLabel("Posted " + lc.getModificationDate().toString() + " by: "));
        leftJustPanel.add(Box.createHorizontalGlue());
        headerPanel.add(leftJustPanel);

        leftJustPanel = new JPanel();
        leftJustPanel.setLayout(new BoxLayout(leftJustPanel, BoxLayout.X_AXIS));
        leftJustPanel.add(new JLabel(lc.getUser()));
        leftJustPanel.add(Box.createHorizontalGlue());
        headerPanel.add(leftJustPanel);
        headerPanel.add(Box.createHorizontalGlue());
        return headerPanel;
    }

    private JPanel getStatusIconPanel(final UserCommentGroup lcg, final UserComment lc) {
        JPanel sip = new JPanel();
        sip.setLayout(new BoxLayout(sip, BoxLayout.X_AXIS));

        JPanel statusIconPanel = new StatusIconPanel(ICON_WIDTH, ICON_HEIGHT, false, lc.isApproved(),
                lc.isIncluded(), lc.isDeleted());

        sip.add(statusIconPanel);
        sip.add(Box.createHorizontalGlue());

        //technicains and admins can change status, but technicians can't.
        if (roleManager.checkRole(GENETIC_COUNSELLOR_ROLENAME) || roleManager.checkRole(RESIDENT_ROLENAME)) {
            JButton statusEditButton = new JButton(new ImageIcon(EDIT_STATUS_BUTTON_ICON.getImage()
                    .getScaledInstance(ICON_WIDTH, ICON_HEIGHT, java.awt.Image.SCALE_SMOOTH)));

            statusEditButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    editStatus(lcg, lc);
                }
            });
            sip.add(statusEditButton);
        }

        return sip;
    }

    private JPanel getCommentPanel(UserComment lc) {
        JTextArea commentText = new JTextArea();
        commentText.setText(lc.getCommentText());
        commentText.setEditable(false);
        commentText.setLineWrap(true);
        commentText.setPreferredSize(new Dimension(COMMENTTEXT_PREFERRED_WIDTH, COMMENTTEXT_PREFERRED_HEIGHT));
        JScrollPane jsp = new JScrollPane(commentText);

        JPanel outerCommentPanel = new JPanel();
        outerCommentPanel.setLayout(new BoxLayout(outerCommentPanel, BoxLayout.X_AXIS));
        outerCommentPanel.add(jsp);
        outerCommentPanel.add(Box.createHorizontalGlue());
        return outerCommentPanel;
    }

    private static final int STATUS_COMMENT_INDENT_WIDTH = 20;

    private JPanel oldStatusPanel;

    private void updateStatusPanel(UserComment oldComment) {
        //Write the from-to status to the last comment's status panel.
        if (oldComment != null && oldComment.getOriginalComment() != null) {//status change comment.                        
            JPanel statusIconPanel = new StatusIconPanel(ICON_WIDTH, ICON_HEIGHT, false,
                    oldComment.getOriginalComment().isApproved(), oldComment.getOriginalComment().isIncluded(),
                    oldComment.getOriginalComment().isDeleted());

            oldStatusPanel.add(new JLabel(" to "));
            oldStatusPanel.add(statusIconPanel);
            oldStatusPanel.add(Box.createHorizontalGlue());
            oldStatusPanel = null;
        }
    }

    private JPanel getCommentBlock(UserCommentGroup lcg, UserComment lc/*, UserComment oldComment*/) {

        JPanel innerPanel = new JPanel();
        innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS));

        JPanel headerPanel = getHeaderPanel(lc);
        JPanel commentPanel = getCommentPanel(lc);

        if (lc.getOriginalComment() == null) { //root level comment.
            innerPanel.add(headerPanel);
            innerPanel.add(getStatusIconPanel(lcg, lc));
            innerPanel.add(commentPanel);
            innerPanel.add(getCommentSeparator());

            return innerPanel;
        } else { //status comment.
            JPanel outerPanel = new JPanel();
            outerPanel.setLayout(new BoxLayout(outerPanel, BoxLayout.X_AXIS));
            JSeparator js = new JSeparator();
            js.setPreferredSize(new Dimension(STATUS_COMMENT_INDENT_WIDTH, 1));
            outerPanel.add(js);

            innerPanel.add(headerPanel);

            //System.out.println("Constructing statusIconPanel with "+lc.isApproved()+","+lc.isIncluded()+","+lc.isPendingReview());
            JPanel statusIconPanel = new StatusIconPanel(ICON_WIDTH, ICON_HEIGHT, false, lc.isApproved(),
                    lc.isIncluded(), lc.isDeleted());

            JPanel centeredLabel = new JPanel();
            centeredLabel.setLayout(new BoxLayout(centeredLabel, BoxLayout.X_AXIS));
            centeredLabel.add(Box.createHorizontalGlue());
            centeredLabel.add(new JLabel("Status Change:"));
            centeredLabel.add(Box.createHorizontalGlue());
            innerPanel.add(centeredLabel);
            oldStatusPanel = new JPanel();
            oldStatusPanel.setLayout(new BoxLayout(oldStatusPanel, BoxLayout.X_AXIS));
            oldStatusPanel.add(Box.createHorizontalGlue());
            oldStatusPanel.add(statusIconPanel);
            innerPanel.add(oldStatusPanel);
            innerPanel.add(commentPanel);
            innerPanel.add(getCommentSeparator());
            outerPanel.add(innerPanel);
            return outerPanel;
        }
    }

    private JPanel getMainCommentPanel(Map<OntologyTerm, Collection<UserComment>> otCommentMap,
            final UserCommentGroup lcg, final VariantRecord vr) {
        JPanel innerPanel = new JPanel(); //Todo: may need to specify width of this panel?                
        innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS));
        for (final Map.Entry<OntologyTerm, Collection<UserComment>> e : otCommentMap.entrySet()) {
            OntologyTerm ot = e.getKey();
            Collection<UserComment> userComments = e.getValue();

            JPanel otPanel = new JPanel();
            otPanel.setLayout(new BoxLayout(otPanel, BoxLayout.Y_AXIS));
            //otPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

            //Define horizontal panel with ontology title and reply to ontology icon.
            JPanel ontologyTitlePanel = getOntologyTitlePanel(vr, ot);
            otPanel.add(ontologyTitlePanel);
            UserComment oldComment = null;
            for (final UserComment userComment : userComments) {
                if (userComment.isDeleted()) {
                    continue;
                }
                updateStatusPanel(oldComment);
                otPanel.add(getCommentBlock(lcg, userComment));
                oldComment = userComment;
            }
            updateStatusPanel(oldComment);
            otPanel.add(getOntologySeparator());
            innerPanel.add(otPanel);
        }

        return innerPanel;
    }

    private void newComment(VariantRecord vr) {
        JDialog acd = new AddNewCommentDialog(MedSavantFrame.getInstance(), vr);
        acd.setVisible(true);
    }

    private JPanel getNoCommentsPanel() {
        JPanel innerPanel = new JPanel();
        innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS));
        innerPanel.add(Box.createVerticalGlue());
        innerPanel.add(new JLabel("No Comments"));
        innerPanel.add(Box.createVerticalGlue());
        return innerPanel;
    }

    @Override
    public void setVariantRecord(final VariantRecord variantRecord) {
        try {
            //Get comment group associated with this variant.
            UserCommentGroup lcg = MedSavantClient.VariantManager.getUserCommentGroup(
                    LoginController.getSessionID(), ProjectController.getInstance().getCurrentProjectID(),
                    ReferenceController.getInstance().getCurrentReferenceID(), variantRecord);
            boolean hasComments = false;
            JPanel innerPanel = null;
            if (lcg != null) {
                //Build a mapping from ontology terms to all comments pertaining to that ontology term.
                //Iterating through this map will return the ontology terms in alphabetical order, and the comments
                //within each ontology will be ordered by their insertion id.
                Map<OntologyTerm, Collection<UserComment>> otCommentMap = new TreeMap<OntologyTerm, Collection<UserComment>>();

                for (Iterator<UserComment> li = lcg.iterator(); li.hasNext();) {
                    UserComment lc = li.next();
                    Collection<UserComment> ontologyComments = otCommentMap.get(lc.getOntologyTerm());
                    if (ontologyComments == null) {
                        ontologyComments = new ArrayList<UserComment>();
                        hasComments = true;
                    }
                    ontologyComments.add(lc);

                    otCommentMap.put(lc.getOntologyTerm(), ontologyComments);
                }
                if (hasComments) {
                    innerPanel = getMainCommentPanel(otCommentMap, lcg, variantRecord);
                }
            }

            if (innerPanel == null) {
                innerPanel = getNoCommentsPanel();
            }

            JButton newCommentButton = new JButton("New Comment");
            newCommentButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    newComment(variantRecord);
                }
            });

            JPanel cp = new JPanel();
            cp.setLayout(new BoxLayout(cp, BoxLayout.X_AXIS));
            cp.add(Box.createHorizontalGlue());
            if (roleManager.checkRole(GENETIC_COUNSELLOR_ROLENAME) || roleManager.checkRole(RESIDENT_ROLENAME)) {
                cp.add(newCommentButton);
            }
            cp.add(Box.createHorizontalGlue());
            innerPanel.add(cp);
            final JScrollPane jsp = new JScrollPane(innerPanel);

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {

                    panel.removeAll();
                    panel.add(jsp);
                    panel.revalidate();
                    panel.repaint();
                }
            });

        } catch (SessionExpiredException see) {
            LOG.error(see.getMessage(), see);
        } catch (SQLException sqe) {
            LOG.error(sqe.getMessage(), sqe);

        } catch (RemoteException rex) {
            LOG.error(rex.getMessage(), rex);
        }
    }

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public JPanel getInfoPanel() {
        return panel;
    }

    @Override
    public String getTitle() {
        return TITLE;
    }

}