Skip to content

Commit

Permalink
Merge pull request #50 from monarch-initiative/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
kingmanzhang committed Mar 20, 2018
2 parents fa702fe + 7961e43 commit 39a320f
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 33 deletions.
13 changes: 8 additions & 5 deletions docs/source/Configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@ Mandatory Settings

* Download the Loinc Core Table from `loinc.org <https://loinc.org/downloads/loinc/>`_. Follow instructions from the loinc.org website. You need to register before you can download the document. The current version is *Loinc Version 2.63* (2017/12/15 release).

* Configure the path to the Loinc core table. From the menu bar, click **"Edit"** - **"Set path to Loinc Core Table file"** and point to the Loinc Core Table file downloaded from last step.
* Configure the path to the Loinc core table. From the menu bar, click **"Configuration"** - **"Set path to Loinc Core Table file"** and point to the Loinc Core Table file downloaded from last step.

* Download HPO file. From the menu bar, click **"Edit"** - **"Download HPO file"**. The files (HPO in .obo and .owl formats) will be automatically downloaded.
* Download HPO file. From the menu bar, click **"Configuration"** - **"Download HPO file"**. The files (HPO in .obo and .owl formats) will be automatically downloaded.

**Restart the app to apply all settings**


Optional Settings
-----------------
The following setting are recommended. Not specifying them will not
affect the operation of the app.

* Change the directory for auto-saved data. The default directory for auto-saved data is located at ~/.loinc2hpo/Data. If you want to change this setting, from the menu bar, click **Configuration** - **Set path to Autosaved Data** to change the directory for autosaved data.
* Change the directory for auto-saved data. The default directory for auto-saved data is located at ~/.loinc2hpo/Data. If you want to change this setting, from the menu bar, click **Configuration** - **Set path to Autosaved Data** to change the directory for autosaved data.

* Set biocurator ID. From the menu bar, click **"Edit"** - **"Set biocurator ID"**, specify your biocurator ID. If you are not assigned one, create one for yourself with the following format: organization name first, then `:`, then your name/id.
note: this step is mandatory if you want to use and push your annotation to `loinc2hpoAnnotation <https://github.com/TheJacksonLaboratory/loinc2hpoAnnotation>`_. Follow instructions there to set up the path properly.

* Once you are done, click **"Edit"** - **"Show settings"** to view all your settings. The first two settings should **NOT** be null in order for the app to work correctly.
* Set biocurator ID. From the menu bar, click **"Configuration"** - **"Set biocurator ID"**, specify your biocurator ID. If you are not assigned one, create one for yourself with the following format: organization name first, then `:`, then your name/id.

* Once you are done, click **"Edit"** - **"Show settings"** to view all your settings. The first two settings should **NOT** be null in order for the app to work correctly.

Change Settings
---------------
Expand Down
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Loinc2hpo is a JavaFX app designed to convert FHIR observations into HPO terms.
- help retrieve patient observations
- convert patient observations into HPO terms

Mapping LOINC to HPO terms requires a significant effort from biocurators. Loinc2hpo tries to simply the process by automatically finding the best matching terms using Sparql query. With this app, collaborators can easily split LOINC codes of interest into smaller tasks and share their annotations.
Mapping LOINC to HPO terms requires significant efforts from biocurators. Loinc2hpo tries to simplify the process by automatically finding the best matching terms using Sparql query. With this app, collaborators can easily split LOINC codes of interest into smaller tasks and share their annotations.

.. toctree::
:maxdepth: 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.io.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
Expand Down Expand Up @@ -150,9 +151,9 @@ public static Map<LoincId, UniversalLoinc2HPOAnnotation> fromTSV(String path, Ma
String note = elements[6].equals("null") ? null : elements[6];
boolean flag = Boolean.parseBoolean(elements[7]);
double version = Double.parseDouble(elements[8]);
LocalDate createdOn = elements[9].equals("null") ? null : LocalDate.parse(elements[9]);
LocalDateTime createdOn = elements[9].equals("null") ? null : LocalDateTime.parse(elements[9]);
String createdBy = elements[10].equals("null")? null : elements[10];
LocalDate lastEditedOn = elements[11].equals("null")? null : LocalDate.parse(elements[11]);
LocalDateTime lastEditedOn = elements[11].equals("null")? null : LocalDateTime.parse(elements[11]);
String lastEditedBy = elements[12].equals("null")? null : elements[12];

if (!deserializedMap.containsKey(loincId)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
Expand Down Expand Up @@ -48,9 +49,9 @@ public class UniversalLoinc2HPOAnnotation implements Serializable {

@JsonProperty("version")
private double version = 0.0;
private LocalDate createdOn = null;
private LocalDateTime createdOn = null;
private String createdBy = null;
private LocalDate lastEditedOn = null;
private LocalDateTime lastEditedOn = null;
private String lastEditedBy = null;

@JsonProperty("loinc id")
Expand Down Expand Up @@ -227,7 +228,7 @@ public String toString(){
stringBuilder.append("\t" + hpoTermId4LoincTest.isNegated());
stringBuilder.append("\t" + this.note);
stringBuilder.append("\t" + this.flag);
stringBuilder.append("\t" + this.version);
stringBuilder.append("\t" + String.format("%.1f", this.version));
stringBuilder.append("\t" + this.createdOn);
stringBuilder.append("\t" + this.createdBy);
stringBuilder.append("\t" + this.lastEditedOn);
Expand All @@ -247,11 +248,11 @@ public UniversalLoinc2HPOAnnotation setVersion(double version) {
return this;
}

public LocalDate getCreatedOn() {
public LocalDateTime getCreatedOn() {
return createdOn;
}

public UniversalLoinc2HPOAnnotation setCreatedOn(LocalDate createdOn) {
public UniversalLoinc2HPOAnnotation setCreatedOn(LocalDateTime createdOn) {
this.createdOn = createdOn;
return this;
}
Expand All @@ -265,11 +266,11 @@ public UniversalLoinc2HPOAnnotation setCreatedBy(String createdBy) {
return this;
}

public LocalDate getLastEditedOn() {
public LocalDateTime getLastEditedOn() {
return lastEditedOn;
}

public UniversalLoinc2HPOAnnotation setLastEditedOn(LocalDate lastEditedOn) {
public UniversalLoinc2HPOAnnotation setLastEditedOn(LocalDateTime lastEditedOn) {
this.lastEditedOn = lastEditedOn;
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
Expand Down Expand Up @@ -50,6 +51,8 @@


import java.io.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;


Expand Down Expand Up @@ -341,11 +344,13 @@ public void onChanged(Change<? extends String> c) {
}
});

initadvancedAnnotationTable();

}

protected void defaultStartUp() {
initLOINCtable(null);
//initHPOmodelButton(null);
Platform.runLater(()->initHPOmodelButton(null));
}

private void noLoincEntryAlert(){
Expand Down Expand Up @@ -415,7 +420,7 @@ private void handleDoubleClickLoincTable(MouseEvent event) {
}

//disable further action if the user is not under Editing mode
if(model.getLoincUnderEditing() != null && model.getLoincUnderEditing().equals(rowData)){
if(model.getLoincUnderEditing() != null && !model.getLoincUnderEditing().equals(rowData)){
PopUps.showInfoMessage("You are currently editing " + rowData.getLOINC_Number() +
". Save or cancel editing current loinc annotation before switching to others",
"Under Editing mode");
Expand Down Expand Up @@ -1047,45 +1052,52 @@ private boolean recordInversed() {
PopUps.showInfoMessage("No loinc entry is selected. Try clicking \"Initialize Loinc Table\"", "No Loinc selection Error");
return;
}
LoincId loincCode = loincTableView.getSelectionModel().getSelectedItem().getLOINC_Number();
LoincScale loincScale = LoincScale.string2enum(loincTableView.getSelectionModel().getSelectedItem().getScale());
//specify loinc for the annotation
LoincEntry loincEntry = loincTableView.getSelectionModel().getSelectedItem();
LoincId loincCode = loincEntry.getLOINC_Number();
LoincScale loincScale = LoincScale.string2enum(loincEntry.getScale());

if (createAnnotationButton.getText().equals("Create annotation") && model.getLoincAnnotationMap().containsKey(loincCode)) {
//if the loinc is already annotated, warn user
if (createAnnotationButton.getText().equals("Create annotation")
&& model.getLoincAnnotationMap().containsKey(loincCode)) {
boolean toOverwrite = PopUps.getBooleanFromUser("Do you want to overwrite?",
loincCode + " is already annotated", "Overwrite warning");
if (!toOverwrite) return;
}

logger.debug("advancedAnnotationModeSelected: " + advancedAnnotationModeSelected);
//update annotations right before finalizing the record
if(!advancedAnnotationModeSelected) { //we are last in basic mode, user might have changed data for basic annotation
logger.debug("advancedAnnotationModeSelected: " + advancedAnnotationModeSelected + " recording");
logger.trace("creating the annotation while at the basic mode");
logger.trace("record changes for basic annotation");
model.setTempTerms(recordTempTerms()); //update terms for basic annotation
model.setInversedBasicMode(recordInversed());
logger.debug("annotationTextFieldLeft is recorded: " + annotationTextFieldLeft.getText());
logger.debug("annotationTextFieldMiddle is recorded: " + annotationTextFieldMiddle.getText());
logger.debug("annotationTextFieldRight is recorded: " + annotationTextFieldRight.getText());
} else { //if we are last in the advanced mode, user might have added a new annotation, we add this annotation
logger.trace("creating the annotation while at the advanced mode");
handleAnnotateCodedValue(e);
}

//tempTerms.values().stream().forEach(System.out::println);
//if this function is called at advanced annotation mode, the terms for basic annotation was already saved
//map hpo terms to internal codes
Map<String, String> tempTerms = model.getTempTerms();
String hpoLo = tempTerms.get("hpoLo");
String hpoNormal = tempTerms.get("hpoNormal");
String hpoHi = tempTerms.get("hpoHi");
logger.debug(String.format("hpoLo: %s; hpoNormal: %s; hpoHi: %s", hpoLo, hpoNormal, hpoHi));

//if no annotations for basic AND advanced, do not create it
if ((hpoLo == null || hpoLo.isEmpty()) &&
(hpoNormal == null || hpoNormal.isEmpty()) &&
(hpoHi == null || hpoHi.isEmpty()) &&
tempAdvancedAnnotations.isEmpty()) {
PopUps.showInfoMessage("You have not mapped any HPO terms to this LOINC.", "Abort");
logger.debug("tempAdvancedAnnotations size: " + tempAdvancedAnnotations.size());
return;
}

//We don't have to force every loinc code to have three phenotypes
HpoTerm low = termmap.get(hpoLo);
HpoTerm normal = termmap.get(hpoNormal);
HpoTerm high = termmap.get(hpoHi);
//logger.debug((String.format("Terms found: lo- %s; normal- %s; hi- %s", low.getName(), normal.getName(), high.getName())));

//Warning user that there is something wrong
//it happens when something is wrong with hpo termmap (a name could not be mapped)
Expand Down Expand Up @@ -1113,6 +1125,7 @@ private boolean recordInversed() {


UniversalLoinc2HPOAnnotation loinc2HPOAnnotation = new UniversalLoinc2HPOAnnotation(loincCode, loincScale);
//logger.debug("step 11: \n" + loinc2HPOAnnotation.toString() );

Map<String, Boolean> qcresult = qcAnnotation(hpoLo, hpoNormal, hpoHi);
if (qcresult.get("issueDetected") && !qcresult.get("userconfirmed")) {
Expand All @@ -1124,21 +1137,27 @@ private boolean recordInversed() {
Map<String, Code> internalCode = CodeSystemConvertor.getCodeContainer().getCodeSystemMap().get(Loinc2HPOCodedValue.CODESYSTEM);
if (hpoLo != null && low != null) {
loinc2HPOAnnotation.addAnnotation(internalCode.get("L"), new HpoTermId4LoincTest(low, false));
//logger.debug("step 22: \n" + loinc2HPOAnnotation.toString() );
}
if (hpoNormal != null && normal != null) {
loinc2HPOAnnotation
.addAnnotation(internalCode.get("A"), new HpoTermId4LoincTest(normal, false));
//logger.debug("step 33: \n" + loinc2HPOAnnotation.toString() );
}
if (hpoNormal != null && normal != null && model.isInversedBasicMode()) {
loinc2HPOAnnotation
.addAnnotation(internalCode.get("N"), new HpoTermId4LoincTest(normal, true))
.addAnnotation(internalCode.get("NP"), new HpoTermId4LoincTest(normal, true));
//logger.debug("step 44: \n" + loinc2HPOAnnotation.toString() );

}
if (hpoHi != null && high != null)
if (hpoHi != null && high != null) {
loinc2HPOAnnotation
.addAnnotation(internalCode.get("H"), new HpoTermId4LoincTest(high, false))
.addAnnotation(internalCode.get("P"), new HpoTermId4LoincTest(high, false));
.addAnnotation(internalCode.get("H"), new HpoTermId4LoincTest(high, false))
.addAnnotation(internalCode.get("P"), new HpoTermId4LoincTest(high, false));
//logger.debug("step 55: \n" + loinc2HPOAnnotation.toString() );

}
} catch (Exception ex) {
ex.printStackTrace();
}
Expand All @@ -1149,13 +1168,29 @@ private boolean recordInversed() {
for (Annotation annotation : tempAdvancedAnnotations) {
loinc2HPOAnnotation.addAnnotation(annotation.getCode(), annotation.getHpoTermId4LoincTest());
}
//logger.debug("step 66: \n" + loinc2HPOAnnotation.toString() );
}

loinc2HPOAnnotation.setFlag(flagForAnnotation.isSelected());
loinc2HPOAnnotation.setNote(annotationNoteField.getText());
if (createAnnotationButton.getText().equals("Create annotation")) { //create for the first time
loinc2HPOAnnotation.setCreatedBy(model.getBiocuratorID() == null? "?":model.getBiocuratorID());
loinc2HPOAnnotation.setCreatedOn(LocalDateTime.now().withNano(0));
//logger.debug("step 77: \n" + loinc2HPOAnnotation.toString() );
loinc2HPOAnnotation.setVersion(0.1);
} else { //editing mode
loinc2HPOAnnotation.setLastEditedBy(model.getBiocuratorID() == null? "?":model.getBiocuratorID());
loinc2HPOAnnotation.setLastEditedOn(LocalDateTime.now().withNano(0));
loinc2HPOAnnotation.setCreatedBy(model.getLoincAnnotationMap().get(loincCode).getCreatedBy());
loinc2HPOAnnotation.setCreatedOn(model.getLoincAnnotationMap().get(loincCode).getCreatedOn());
loinc2HPOAnnotation.setVersion(model.getLoincAnnotationMap().get(loincCode).getVersion() + 0.1);
//logger.debug("step 88: \n" + loinc2HPOAnnotation.toString() );
}


if (loinc2HPOAnnotation != null) {
logger.info(loinc2HPOAnnotation.getCodes().size() + " annotations");
//logger.debug("step 99: \n" + loinc2HPOAnnotation.toString() );
this.model.addLoincTest(loinc2HPOAnnotation);
advancedAnnotationModeSelected = false;
model.setTempTerms(new HashMap<>());//clear the temp term in model
Expand Down Expand Up @@ -1282,6 +1317,7 @@ private Map<String, Boolean> qcAnnotation(String HpoLow, String HpoNorm, String
boolean issueDetected = false;
boolean userConfirmed = false;

/**
if ((HpoLow == null || HpoLow.trim().isEmpty()) &&
(HpoNorm == null || HpoNorm.trim().isEmpty()) &&
(HpoHigh == null || HpoHigh.trim().isEmpty())) {
Expand All @@ -1290,6 +1326,7 @@ private Map<String, Boolean> qcAnnotation(String HpoLow, String HpoNorm, String
userConfirmed = PopUps.getBooleanFromUser("Are you sure you want to create an annotation without any HPO terms?",
"Annotation without HPO terms", "No HPO Alert");
}
**/

if (HpoLow != null && HpoNorm != null && !HpoLow.trim().isEmpty() && stringEquals(HpoLow, HpoNorm)) {
//alert: low and norm are same!
Expand Down Expand Up @@ -1618,7 +1655,7 @@ private void handleAnnotateCodedValue(ActionEvent e){
}
tempAdvancedAnnotations.add(annotation);
//add annotated value to the advanced table view
initadvancedAnnotationTable();
//initadvancedAnnotationTable();
accordion.setExpandedPane(advancedAnnotationTitledPane);
inverseChecker.setSelected(false);
model.setTempAdvancedAnnotation(new HashMap<>());
Expand All @@ -1627,12 +1664,14 @@ private void handleAnnotateCodedValue(ActionEvent e){

@FXML
private void handleDeleteCodedAnnotation(ActionEvent event) {
event.consume();
logger.debug("user wants to delete an annotation");
logger.debug("tempAdvancedAnnotations size: " + tempAdvancedAnnotations.size());
Annotation selectedToDelete = advancedAnnotationTable.getSelectionModel().getSelectedItem();
if (selectedToDelete != null) {
tempAdvancedAnnotations.remove(selectedToDelete);
}
event.consume();
logger.debug("tempAdvancedAnnotations size: " + tempAdvancedAnnotations.size());
}


Expand Down Expand Up @@ -1868,6 +1907,7 @@ protected void editCurrentAnnotation(UniversalLoinc2HPOAnnotation loincAnnotatio
setLoincIdSelected(loincAnnotation.getLoincId());
model.setLoincUnderEditing(model.getLoincEntryMap().get(loincAnnotation.getLoincId()));

//populate annotation textfields for basic mode
Map<String, Code> internalCode = CodeSystemConvertor.getCodeContainer().getCodeSystemMap().get(Loinc2HPOCodedValue.CODESYSTEM);
Code codeLow = internalCode.get("L");
Code codeHigh = internalCode.get("H");
Expand All @@ -1890,8 +1930,10 @@ protected void editCurrentAnnotation(UniversalLoinc2HPOAnnotation loincAnnotatio
inverseChecker.setSelected(isnegated);
}

//populated advanced annotations table view
//remember: advanced annotation == not using internal codes
for (Map.Entry<Code, HpoTermId4LoincTest> entry : loincAnnotation.getCandidateHpoTerms().entrySet()) {
if (entry.getKey().getSystem() != Loinc2HPOCodedValue.CODESYSTEM) {
if (!entry.getKey().getSystem().equals(Loinc2HPOCodedValue.CODESYSTEM)) {
tempAdvancedAnnotations.add(new Annotation(entry.getKey(), entry.getValue()));
}
}
Expand Down

0 comments on commit 39a320f

Please sign in to comment.