diff --git a/src/org/ohdsi/usagi/UsagiSearchEngine.java b/src/org/ohdsi/usagi/UsagiSearchEngine.java index 3a5bbd9..b8feff2 100644 --- a/src/org/ohdsi/usagi/UsagiSearchEngine.java +++ b/src/org/ohdsi/usagi/UsagiSearchEngine.java @@ -373,25 +373,10 @@ public List search(String searchTerm, boolean useMlt, Collection< recomputeScores(topDocs.scoreDocs, query); for (ScoreDoc scoreDoc : topDocs.scoreDocs) { - Document document = reader.document(scoreDoc.doc); - int conceptId = Integer.parseInt(document.get("CONCEPT_ID")); + TargetConcept targetConcept = docIdToTargetConcept(scoreDoc.doc); // If matchscore = 0 but it was the one concept that was automatically selected, still allow it: - if (scoreDoc.score > 0 || (filterConceptIds != null && filterConceptIds.size() == 1 && filterConceptIds.contains(conceptId))) { - TargetConcept targetConcept = new TargetConcept(); - targetConcept.term = document.get("TERM"); - targetConcept.conceptId = conceptId; - targetConcept.conceptName = document.get("CONCEPT_NAME"); - targetConcept.conceptClass = document.get("CONCEPT_CLASS"); - targetConcept.vocabulary = document.get("VOCABULARY"); - targetConcept.conceptCode = document.get("CONCEPT_CODE"); - targetConcept.validStartDate = document.get("VALID_START_DATE"); - targetConcept.validEndDate = document.get("VALID_END_DATE"); - targetConcept.invalidReason = document.get("INVALID_REASON"); - for (String domain : document.get("DOMAINS").split("\n")) - targetConcept.domains.add(domain); - targetConcept.additionalInformation = document.get("ADDITIONAL_INFORMATION"); + if (scoreDoc.score > 0 || (filterConceptIds != null && filterConceptIds.size() == 1 && filterConceptIds.contains(targetConcept.conceptId))) results.add(new ScoredConcept(scoreDoc.score, targetConcept)); - } } reorderTies(results); removeDuplicateConcepts(results); @@ -403,6 +388,46 @@ public List search(String searchTerm, boolean useMlt, Collection< return results; } + private TargetConcept docIdToTargetConcept(int docId) { + try { + Document document = reader.document(docId); + int conceptId = Integer.parseInt(document.get("CONCEPT_ID")); + TargetConcept targetConcept = new TargetConcept(); + targetConcept.term = document.get("TERM"); + targetConcept.conceptId = conceptId; + targetConcept.conceptName = document.get("CONCEPT_NAME"); + targetConcept.conceptClass = document.get("CONCEPT_CLASS"); + targetConcept.vocabulary = document.get("VOCABULARY"); + targetConcept.conceptCode = document.get("CONCEPT_CODE"); + targetConcept.validStartDate = document.get("VALID_START_DATE"); + targetConcept.validEndDate = document.get("VALID_END_DATE"); + targetConcept.invalidReason = document.get("INVALID_REASON"); + for (String domain : document.get("DOMAINS").split("\n")) + targetConcept.domains.add(domain); + targetConcept.additionalInformation = document.get("ADDITIONAL_INFORMATION"); + return targetConcept; + } catch (Exception e) { + System.err.println(e.getMessage()); + e.printStackTrace(); + } + return null; + } + + public TargetConcept getTargetConcept(int conceptId) { + try { + Query query = conceptIdQueryParser.parse(Integer.toString(conceptId)); + TopDocs topDocs = searcher.search(query, 1); + if (topDocs.totalHits > 0) + return docIdToTargetConcept(topDocs.scoreDocs[0].doc); + else + return null; + } catch (Exception e) { + System.err.println(e.getMessage()); + e.printStackTrace(); + } + return null; + } + private void removeDuplicateConcepts(List results) { Set seenConceptIds = new HashSet(); Iterator iterator = results.iterator(); @@ -428,7 +453,6 @@ else if (arg1.concept.term.toLowerCase().equals(arg1.concept.conceptName.toLower return result; } }); - } /** diff --git a/src/org/ohdsi/usagi/ui/Global.java b/src/org/ohdsi/usagi/ui/Global.java index 4153806..200d1b6 100644 --- a/src/org/ohdsi/usagi/ui/Global.java +++ b/src/org/ohdsi/usagi/ui/Global.java @@ -19,10 +19,12 @@ import org.ohdsi.usagi.UsagiSearchEngine; import org.ohdsi.usagi.ui.actions.AboutAction; +import org.ohdsi.usagi.ui.actions.ApplyPreviousMappingAction; import org.ohdsi.usagi.ui.actions.ApproveAction; import org.ohdsi.usagi.ui.actions.ApproveAllAction; import org.ohdsi.usagi.ui.actions.ClearAllAction; import org.ohdsi.usagi.ui.actions.ConceptInformationAction; +import org.ohdsi.usagi.ui.actions.ExitAction; import org.ohdsi.usagi.ui.actions.ExportSourceToConceptMapAction; import org.ohdsi.usagi.ui.actions.ImportAction; import org.ohdsi.usagi.ui.actions.OpenAction; @@ -43,6 +45,7 @@ public class Global { public static UsagiStatusBar statusBar; public static OpenAction openAction; + public static ApplyPreviousMappingAction applyPreviousMappingAction; public static ImportAction importAction; public static SaveAction saveAction; public static SaveAsAction saveAsAction; @@ -53,4 +56,5 @@ public class Global { public static AboutAction aboutAction; public static ExportSourceToConceptMapAction exportAction; public static RebuildIndexAction rebuildIndexAction; + public static ExitAction exitAction; } diff --git a/src/org/ohdsi/usagi/ui/ImportDialog.java b/src/org/ohdsi/usagi/ui/ImportDialog.java index ce863ee..3daac6c 100644 --- a/src/org/ohdsi/usagi/ui/ImportDialog.java +++ b/src/org/ohdsi/usagi/ui/ImportDialog.java @@ -412,6 +412,7 @@ public void run() { progressBar.setValue(Math.round(100 * Global.mapping.size() / sourceCodes.size())); } dialog.setVisible(false); + Global.applyPreviousMappingAction.setEnabled(true); Global.saveAction.setEnabled(true); Global.saveAsAction.setEnabled(true); Global.exportAction.setEnabled(true); diff --git a/src/org/ohdsi/usagi/ui/UsagiMain.java b/src/org/ohdsi/usagi/ui/UsagiMain.java index 8fe324f..440571a 100644 --- a/src/org/ohdsi/usagi/ui/UsagiMain.java +++ b/src/org/ohdsi/usagi/ui/UsagiMain.java @@ -32,10 +32,12 @@ import org.ohdsi.usagi.UsagiSearchEngine; import org.ohdsi.usagi.ui.actions.AboutAction; +import org.ohdsi.usagi.ui.actions.ApplyPreviousMappingAction; import org.ohdsi.usagi.ui.actions.ApproveAction; import org.ohdsi.usagi.ui.actions.ApproveAllAction; import org.ohdsi.usagi.ui.actions.ClearAllAction; import org.ohdsi.usagi.ui.actions.ConceptInformationAction; +import org.ohdsi.usagi.ui.actions.ExitAction; import org.ohdsi.usagi.ui.actions.ExportSourceToConceptMapAction; import org.ohdsi.usagi.ui.actions.ImportAction; import org.ohdsi.usagi.ui.actions.OpenAction; @@ -68,6 +70,7 @@ public UsagiMain(String[] args) { Global.conceptInformationDialog = new ConceptInformationDialog(); Global.frame = frame; Global.openAction = new OpenAction(); + Global.applyPreviousMappingAction = new ApplyPreviousMappingAction(); Global.importAction = new ImportAction(); Global.exportAction = new ExportSourceToConceptMapAction(); Global.saveAction = new SaveAction(); @@ -77,7 +80,9 @@ public UsagiMain(String[] args) { Global.aboutAction = new AboutAction(); Global.approveAllAction = new ApproveAllAction(); Global.rebuildIndexAction = new RebuildIndexAction(); + Global.exitAction = new ExitAction(); + Global.applyPreviousMappingAction.setEnabled(false); Global.saveAction.setEnabled(false); Global.saveAsAction.setEnabled(false); Global.exportAction.setEnabled(false); diff --git a/src/org/ohdsi/usagi/ui/UsagiMenubar.java b/src/org/ohdsi/usagi/ui/UsagiMenubar.java index ec959dc..7dda3ad 100644 --- a/src/org/ohdsi/usagi/ui/UsagiMenubar.java +++ b/src/org/ohdsi/usagi/ui/UsagiMenubar.java @@ -30,9 +30,11 @@ public UsagiMenubar() { fileMenu.add(Global.openAction); fileMenu.add(Global.importAction); + fileMenu.add(Global.applyPreviousMappingAction); fileMenu.add(Global.exportAction); fileMenu.add(Global.saveAction); fileMenu.add(Global.saveAsAction); + fileMenu.add(Global.exitAction); JMenu editMenu = new JMenu("Edit"); editMenu.setMnemonic(new Integer(KeyEvent.VK_E)); diff --git a/src/org/ohdsi/usagi/ui/actions/ApplyPreviousMappingAction.java b/src/org/ohdsi/usagi/ui/actions/ApplyPreviousMappingAction.java new file mode 100644 index 0000000..f744185 --- /dev/null +++ b/src/org/ohdsi/usagi/ui/actions/ApplyPreviousMappingAction.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright 2016 Observational Health Data Sciences and Informatics + * + * 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 org.ohdsi.usagi.ui.actions; + +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileNameExtensionFilter; + +import org.ohdsi.usagi.CodeMapping; +import org.ohdsi.usagi.TargetConcept; +import org.ohdsi.usagi.ui.Global; +import org.ohdsi.usagi.ui.Mapping; + +public class ApplyPreviousMappingAction extends AbstractAction { + + private static final long serialVersionUID = 3420357922150237898L; + + public ApplyPreviousMappingAction() { + putValue(Action.NAME, "Apply previous mapping"); + putValue(Action.SHORT_DESCRIPTION, "Apply previous mapping to current code set"); + } + + @Override + public void actionPerformed(ActionEvent arg0) { + JFileChooser fileChooser = new JFileChooser(Global.folder); + FileFilter csvFilter = new FileNameExtensionFilter("CSV files", "csv"); + fileChooser.setFileFilter(csvFilter); + if (fileChooser.showOpenDialog(Global.frame) == JFileChooser.APPROVE_OPTION) { + int mappingsUsed = 0; + int mappingsFailed = 0; + File file = fileChooser.getSelectedFile(); + Mapping mapping = new Mapping(); + mapping.loadFromFile(file.getAbsolutePath()); + Map> codeToTargetConcept = new HashMap>(); + for (CodeMapping codeMapping : mapping) + if (codeMapping.mappingStatus.equals(CodeMapping.MappingStatus.APPROVED)) + codeToTargetConcept.put(codeMapping.sourceCode.sourceCode, codeMapping.targetConcepts); + + for (CodeMapping codeMapping : Global.mapping) { + List targetConcepts = codeToTargetConcept.get(codeMapping.sourceCode.sourceCode); + if (targetConcepts != null) { + List newTargetConcepts = new ArrayList(targetConcepts.size()); + for (TargetConcept targetConcept : targetConcepts) { + TargetConcept newTargetConcept = Global.usagiSearchEngine.getTargetConcept(targetConcept.conceptId); + if (newTargetConcept == null) + mappingsFailed++; + else + newTargetConcepts.add(newTargetConcept); + + } + if (newTargetConcepts.size() > 0) { + codeMapping.targetConcepts = newTargetConcepts; + mappingsUsed++; + } + if (newTargetConcepts.size() == targetConcepts.size()) + codeMapping.mappingStatus = CodeMapping.MappingStatus.APPROVED; + } + } + String message = "The old mapping contained " + codeToTargetConcept.size() + " approved mappings of which " + mappingsUsed + + " were applied to the current mapping. " + mappingsFailed + " old target concepts could not be found in the new vocabulary."; + Global.mappingTablePanel.updateUI(); + Global.mappingDetailPanel.updateUI(); + JOptionPane.showMessageDialog(Global.frame, message); + } + } + +} diff --git a/src/org/ohdsi/usagi/ui/actions/ExitAction.java b/src/org/ohdsi/usagi/ui/actions/ExitAction.java new file mode 100644 index 0000000..b297b7c --- /dev/null +++ b/src/org/ohdsi/usagi/ui/actions/ExitAction.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright 2016 Observational Health Data Sciences and Informatics + * + * 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 org.ohdsi.usagi.ui.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +public class ExitAction extends AbstractAction { + + private static final long serialVersionUID = 3420357922150237898L; + + public ExitAction() { + putValue(Action.NAME, "Exit"); + putValue(Action.SHORT_DESCRIPTION, "Exit Usagi"); + } + + @Override + public void actionPerformed(ActionEvent arg0) { + System.exit(0); + } + +} diff --git a/src/org/ohdsi/usagi/ui/actions/OpenAction.java b/src/org/ohdsi/usagi/ui/actions/OpenAction.java index 78dfca9..8a9ab41 100644 --- a/src/org/ohdsi/usagi/ui/actions/OpenAction.java +++ b/src/org/ohdsi/usagi/ui/actions/OpenAction.java @@ -52,6 +52,7 @@ public void actionPerformed(ActionEvent arg0) { Global.usagiSearchEngine.close(); Global.usagiSearchEngine.createDerivedIndex(Global.mapping.getSourceCodes(), Global.frame); Global.mappingDetailPanel.doSearch(); + Global.applyPreviousMappingAction.setEnabled(true); Global.saveAction.setEnabled(true); Global.saveAsAction.setEnabled(true); Global.exportAction.setEnabled(true);