/* * Copyright 2011-2012 University of Toronto * * Licensed 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 medsavant.enrichment.app; import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.Point2D.Float; import java.awt.BorderLayout; import java.awt.Font; import java.awt.FontMetrics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.RoundRectangle2D; import java.io.File; import java.io.FileWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.swing.*; import au.com.bytecode.opencsv.CSVWriter; import pedviz.algorithms.Sugiyama; import pedviz.graph.Graph; import pedviz.graph.Node; import pedviz.loader.CsvGraphLoader; import pedviz.view.GraphView2D; import pedviz.view.NodeEvent; import pedviz.view.NodeListener; import pedviz.view.NodeView; import pedviz.view.rules.Rule; import pedviz.view.rules.ShapeRule; import pedviz.view.symbols.Symbol2D; import pedviz.view.symbols.SymbolSexFemale; import pedviz.view.symbols.SymbolSexMale; import pedviz.view.symbols.SymbolSexUndesignated; import org.ut.biolab.medsavant.MedSavantClient; import medsavant.enrichment.app.AggregatePanel; import org.ut.biolab.medsavant.client.filter.FilterController; import org.ut.biolab.medsavant.client.login.LoginController; import org.ut.biolab.medsavant.client.patient.PedigreeBasicRule; import org.ut.biolab.medsavant.client.patient.PedigreeFields; import org.ut.biolab.medsavant.client.project.ProjectController; import org.ut.biolab.medsavant.client.reference.ReferenceController; import org.ut.biolab.medsavant.client.settings.DirectorySettings; import org.ut.biolab.medsavant.client.util.MedSavantWorker; import org.ut.biolab.medsavant.client.view.util.ViewUtil; import org.ut.biolab.medsavant.client.view.component.WaitPanel; /** * * @author mfiume */ public class FamilyAggregatePanel extends AggregatePanel implements PedigreeFields { private static final String FIELD_NUMVARIANTS = "VARIANTS"; private PedigreeGrabber pedigreeGrabber; private FamilyVariantIntersectionAggregator aggregator; private Graph pedigree; private Node overNode = null; private NodeView overNodeView = null; private List<Integer> selectedNodes = new ArrayList<Integer>(); private GraphView2D graphView; private Map<String, Integer> individualVariantIntersection; private String familyID; private final JPanel banner; private final JComboBox familyLister; private final JPanel pedigreePanel; private final JProgressBar progress; public FamilyAggregatePanel(String page) { super(page); setLayout(new BorderLayout()); banner = ViewUtil.getSubBannerPanel("Family"); familyLister = new JComboBox(); pedigreePanel = new JPanel(); pedigreePanel.setLayout(new BorderLayout()); banner.add(familyLister); banner.add(ViewUtil.getMediumSeparator()); banner.add(Box.createHorizontalGlue()); progress = new JProgressBar(); progress.setStringPainted(true); banner.add(progress); add(banner, BorderLayout.NORTH); add(pedigreePanel, BorderLayout.CENTER); familyLister.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { showFamilyAggregates((String) familyLister.getSelectedItem()); } }); new FamilyListGetter().execute(); } public void updateFamilyDropDown(List<String> familyList) { for (String fam : familyList) { familyLister.addItem(fam); } } private void stopThreads() { try { pedigreeGrabber.cancel(true); } catch (Exception e) { } try { aggregator.cancel(true); } catch (Exception e) { } progress.setString("stopped"); } @Override public void recalculate() { showFamilyAggregates((String)familyLister.getSelectedItem()); } private class FamilyListGetter extends MedSavantWorker<List<String>> { public FamilyListGetter() { super(pageName); } @Override protected List<String> doInBackground() throws Exception { return MedSavantClient.PatientManager.getFamilyIDs(LoginController.getInstance().getSessionID(), ProjectController.getInstance().getCurrentProjectID()); } @Override protected void showProgress(double fraction) {} @Override protected void showSuccess(List<String> result) { updateFamilyDropDown(result); } } private void showFamilyAggregates(String famID) { pedigreePanel.removeAll(); pedigreePanel.add(new WaitPanel("Getting pedigree")); familyID = famID; stopThreads(); progress.setIndeterminate(true); pedigreeGrabber = new PedigreeGrabber(famID); pedigreeGrabber.execute(); aggregator = new FamilyVariantIntersectionAggregator(famID); aggregator.execute(); } private class FamilyVariantIntersectionAggregator extends MedSavantWorker<Map<String, Integer>> { private final String familyId; public FamilyVariantIntersectionAggregator(String familyId) { super(pageName); this.familyId = familyId; } @Override protected Map<String, Integer> doInBackground() throws Exception { return MedSavantClient.VariantManager.getNumVariantsInFamily( LoginController.getInstance().getSessionID(), ProjectController.getInstance().getCurrentProjectID(), ReferenceController.getInstance().getCurrentReferenceID(), familyId, FilterController.getInstance().getAllFilterConditions()); } @Override protected void showProgress(double fraction) {} @Override protected void showSuccess(Map<String, Integer> result) { setIndividualVariantIntersection(result); } } private class PedigreeGrabber extends MedSavantWorker<File> { private final String familyId; public PedigreeGrabber(String familyId) { super(pageName); this.familyId = familyId; } @Override protected File doInBackground() throws Exception { List<Object[]> results = MedSavantClient.PatientManager.getFamily(LoginController.getInstance().getSessionID(), ProjectController.getInstance().getCurrentProjectID(), familyId); File outfile = new File(DirectorySettings.getTmpDirectory(), "pedigree" + familyId + ".csv"); CSVWriter w = new CSVWriter(new FileWriter(outfile), ',', CSVWriter.NO_QUOTE_CHARACTER); w.writeNext(new String[]{ HOSPITAL_ID, MOM, DAD, PATIENT_ID, GENDER, AFFECTED }); for (Object[] row : results) { String[] srow = new String[row.length]; for (int i = 0; i < row.length; i++) { srow[i] = row[i].toString(); } w.writeNext(srow); } w.close(); return outfile; } @Override protected void showProgress(double fraction) {} @Override protected void showSuccess(File result) { Graph pedigree = new Graph(); CsvGraphLoader loader = new CsvGraphLoader(result.getAbsolutePath(), ","); loader.setSettings(HOSPITAL_ID, MOM, DAD); loader.load(pedigree); setPedigree(pedigree); } } private synchronized void setIndividualVariantIntersection(Map<String, Integer> map) { this.individualVariantIntersection = map; updateResultView(); } private synchronized void setPedigree(Graph pedigree) { Sugiyama s = new Sugiyama(pedigree); s.run(); GraphView2D view = new GraphView2D(s.getLayoutedGraph()); view.addRule(new ShapeRule(GENDER, "1", new SymbolSexMale())); view.addRule(new ShapeRule(GENDER, "2", new SymbolSexFemale())); view.addRule(new ShapeRule(GENDER, "0", new SymbolSexUndesignated())); view.addRule(new ShapeRule(GENDER, "null", new SymbolSexUndesignated())); view.addRule(new PedigreeBasicRule()); view.addRule(new NumVariantRule()); this.pedigree = pedigree; this.graphView = view; //add ability to click view.addNodeListener(new NodeListener() { @Override public void onNodeEvent(NodeEvent ne) { if (ne.getType() == NodeEvent.MOUSE_ENTER) { overNode = ne.getNode(); overNodeView = ne.getNodeView(); } else if (ne.getType() == NodeEvent.MOUSE_LEAVE) { overNode = null; overNodeView = null; } } }); view.getComponent().addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (overNode != null) { int patID = Integer.parseInt((String)overNode.getUserData(PATIENT_ID)); if (SwingUtilities.isRightMouseButton(e)) { int[] patIDs; if (selectedNodes != null && !selectedNodes.isEmpty()) { patIDs = new int[selectedNodes.size()]; for(int i = 0; i < selectedNodes.size(); i++) { patIDs[i] = selectedNodes.get(i); } } else { patIDs = new int[]{patID}; } JPopupMenu popup = org.ut.biolab.medsavant.client.patient.PatientUtils.createPopup(patIDs); popup.show(e.getComponent(), e.getX(), e.getY()); } else if (SwingUtilities.isLeftMouseButton(e)) { if (!selectedNodes.contains(patID)) { selectedNodes.add(patID); overNodeView.setBorderColor(ViewUtil.detailSelectedBackground); } else { selectedNodes.remove(patID); overNodeView.setBorderColor(Color.black); } } } else { if (SwingUtilities.isRightMouseButton(e) && familyID != null) { JPopupMenu popup = org.ut.biolab.medsavant.client.patient.PatientUtils.createPopup(familyID); popup.show(e.getComponent(), e.getX(), e.getY()); } } pedigreePanel.repaint(); } }); updateResultView(); } private synchronized void updateResultView() { if (graphView != null) { this.pedigreePanel.removeAll(); this.pedigreePanel.setLayout(new BorderLayout()); this.pedigreePanel.add(graphView.getComponent(), BorderLayout.CENTER); this.pedigreePanel.updateUI(); if (this.individualVariantIntersection != null) { for (Node n : pedigree.getAllNodes()) { String id = n.getId().toString(); n.setUserData(FIELD_NUMVARIANTS, individualVariantIntersection.get(id)); } this.pedigreePanel.updateUI(); progress.setIndeterminate(false); progress.setValue(100); progress.setString("complete"); } } } private static class NumVariantRule extends Rule { @Override public void applyRule(NodeView nv) { nv.addSymbol(new NumVariantsSymbol()); } private static class NumVariantsSymbol extends Symbol2D { @Override public int getPriority() { return 0; } @Override public void drawSymbol(Graphics2D gd, Float position, float size, Color color, Color color1, NodeView nv) { Object o = nv.getNode().getUserData(FIELD_NUMVARIANTS); String toWrite; if (o != null) { Integer count = (Integer) o; toWrite = ViewUtil.numToString(count); } else { toWrite = "no DNA"; } gd.setFont(new Font("Arial", Font.BOLD, 1)); FontMetrics fm = gd.getFontMetrics(); int width = fm.stringWidth(toWrite); int height = fm.getAscent(); float startX = (float) position.getX() - size / 2;// (float) (position.getX()-(double)width/2); float startY = (float) (position.getY() + (double) size / 2 + height + 0.1 + height); float pad = 0.07F; gd.setColor(Color.red); gd.fill(new RoundRectangle2D.Float(startX - pad, startY - height - pad + 0.1F, size + 2*pad, height + 2*pad,1F,1F)); gd.setColor(Color.white); gd.drawString(toWrite, startX+ size*0.1F, startY); } } } }