Skip to content

Commit 6cd8a29

Browse files
author
Markus Fleischauer
committed
Merge branch '727-add-api-endpoint-to-add-structure-to-de-novo-list' into 'master'
Resolve "Add API Endpoint to add structure to de novo list" Closes #727 See merge request bright-giant/sirius/sirius-frontend!355
2 parents 9687bda + b00aea0 commit 6cd8a29

File tree

32 files changed

+205525
-1010
lines changed

32 files changed

+205525
-1010
lines changed

.github/workflows/distribute.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
branches:
55
- stable
66
- master
7+
- 727-add-api-endpoint-to-add-structure-to-de-novo-list
78
# - stable-service-api
89
paths-ignore:
910
- '**/README.md'

ms_persistence_oss/ms_persistence_document_storage_oss/src/main/java/de/unijena/bioinf/ms/persistence/storage/SiriusProjectDocumentDatabase.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ default Optional<CsiStructureSearchResult> findCsiStructureSearchResult(long ali
159159
return result;
160160
}
161161

162+
@SneakyThrows
163+
default Stream<DenovoStructureMatch> findDeNovoStructureSearchResult(long alignedFeatureId) {
164+
return findByFeatureIdStr(alignedFeatureId, DenovoStructureMatch.class);
165+
}
166+
162167
@SneakyThrows
163168
default Optional<Ms2Experiment> fetchMsDataAndConfigsAsMsExperiment(@Nullable final AlignedFeatures feature) {
164169
if (feature == null)

sirius_cli/src/main/java/de/unijena/bioinf/projectspace/Instance.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ default void deleteDetectedAdducts(){
147147
void deleteCanopusResult();
148148

149149
void saveMsNovelistResult(@NotNull List<FCandidate<?>> msNovelistResultsPerFormula);
150+
void addMsNovelistResult(@NotNull List<FCandidate<?>> msNovelistResultsPerFormula);
150151
boolean hasMsNovelistResult();
151152
void deleteMsNovelistResult();
152153
}

sirius_cli/src/main/java/de/unijena/bioinf/projectspace/NoSQLInstance.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,55 @@ public void saveMsNovelistResult(@NotNull List<FCandidate<?>> msNovelistResults)
781781
}
782782
}
783783

784+
public void addMsNovelistResult(@NotNull List<FCandidate<?>> msNovelistResults) {
785+
try {
786+
//get all existing DenovoStructureMatch
787+
List<DenovoStructureMatch> existingMatches = project().findDeNovoStructureSearchResult(id).toList();
788+
Set<String> inchiKeys = existingMatches.stream().map(DenovoStructureMatch::getCandidateInChiKey).collect(Collectors.toSet());
789+
790+
List<DenovoStructureMatch> newMatches = msNovelistResults.stream()
791+
.filter(fc -> fc.hasAnnotation(FingerIdResult.class))
792+
.flatMap(fc -> fc.getAnnotationOrThrow(FingerIdResult.class).getAnnotation(MsNovelistFingerblastResult.class)
793+
.map(msnRes -> {
794+
int i = 0;
795+
List<DenovoStructureMatch> m = new ArrayList<>(msnRes.getResults().size());
796+
for (Scored<FingerprintCandidate> c : msnRes.getResults()) {
797+
if (inchiKeys.contains(c.getCandidate().getInchiKey2D())) continue; //todo do we need to check for duplicates of just add everything?
798+
m.add(DenovoStructureMatch.builder()
799+
.alignedFeatureId(id)
800+
.formulaId((long) fc.getId())
801+
.csiScore(c.getScore())
802+
.tanimotoSimilarity(c.getCandidate().getTanimoto())
803+
.modelScore(msnRes.getRnnScore(i++))
804+
.candidateInChiKey(c.getCandidate().getInchiKey2D())
805+
.candidate(c.getCandidate())
806+
.build());
807+
}
808+
return m.stream();
809+
}
810+
).orElseGet(Stream::empty))
811+
.sorted(Comparator.comparing(StructureMatch::getCsiScore).reversed())
812+
.collect(Collectors.toList());
813+
814+
//adding ranks
815+
List<DenovoStructureMatch> allMatches = Stream.concat(existingMatches.stream(), newMatches.stream()).sorted(Comparator.comparing(StructureMatch::getCsiScore).reversed()).toList();
816+
final AtomicInteger rank = new AtomicInteger(1);
817+
allMatches.forEach(m -> m.setStructureRank(rank.getAndIncrement()));
818+
//insert matches
819+
project().getStorage().insertAll(newMatches);
820+
//update rank of existing matches
821+
project().getStorage().upsertAll(existingMatches);
822+
823+
//always update to allow for updated flags after custom db removal or adding //todo more efficient solution preferred
824+
int inserted = project().getStorage().upsertAll(newMatches.stream().map(DenovoStructureMatch::getCandidate).toList());
825+
upsertComputedSubtools(cs -> cs.setDeNovoSearch(true));
826+
log.debug("Inserted: {} of {} DeNovo candidates.", inserted, newMatches.size());
827+
} catch (Exception e) {
828+
// deleteMsNovelistResult(); //don't delete
829+
throw new RuntimeException(e);
830+
}
831+
}
832+
784833
@Override
785834
public boolean hasMsNovelistResult() {
786835
return getComputedSubtools().isDeNovoSearch();

sirius_cli/src/main/java/de/unijena/bioinf/projectspace/SiriusProjectSpaceInstance.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,11 @@ public synchronized void saveMsNovelistResult(@NotNull List<FCandidate<?>> resul
795795
}
796796
}
797797

798+
@Override
799+
public void addMsNovelistResult(@NotNull List<FCandidate<?>> msNovelistResultsPerFormula) {
800+
throw new UnsupportedOperationException("Not supported.");
801+
}
802+
798803
@Override
799804
public synchronized void saveCanopusResult(@NotNull List<FCandidate<?>> canopusResults) {
800805
canopusResults.forEach(fc -> {

sirius_cli/src/main/resources/logging.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ java.util.logging.FileHandler.level=WARNING
4343
# Naming style for the output file:
4444
# (The output file is placed in the directory
4545
# defined by the "user.home" System property.)
46-
java.util.logging.FileHandler.pattern=%h/.sirius-6.3/sirius.log
46+
java.util.logging.FileHandler.pattern=%h/.sirius-6.2/sirius.log
4747
# Limiting size of output file in bytes:
4848
java.util.logging.FileHandler.limit=500000
4949
# Number of output files to cycle through, by appending an

sirius_cli/src/main/resources/sirius_frontend.build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#here you can provide properties that may be needed during build- AND during runtime and should not be editable by the user at runtime
2-
de.unijena.bioinf.siriusFrontend.version=6.2.3-SNAPSHOT
2+
de.unijena.bioinf.siriusFrontend.version=6.2.4-SNAPSHOT
33
#
44
de.unijena.bioinf.sirius.http.job.fingerprint.limit = 5000
55
de.unijena.bioinf.sirius.http.job.canopus.limit = 5000
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package de.unijena.bioinf.ms.gui.dialogs;
2+
3+
import de.unijena.bioinf.ms.gui.utils.loading.Loadable;
4+
import de.unijena.bioinf.ms.gui.utils.loading.LoadablePanel;
5+
6+
import javax.swing.*;
7+
import java.awt.*;
8+
import java.util.function.Supplier;
9+
10+
public class LoadablePanelDialog extends JDialog implements Loadable {
11+
12+
protected final LoadablePanel loadablePanel;
13+
protected final JPanel contentContainer;
14+
15+
public LoadablePanelDialog(Window owner, String title) {
16+
super(owner, title, DEFAULT_MODALITY_TYPE);
17+
this.setLayout(new BorderLayout());
18+
19+
contentContainer = new JPanel(new BorderLayout());
20+
loadablePanel = new LoadablePanel(contentContainer);
21+
add(loadablePanel, BorderLayout.CENTER);
22+
}
23+
24+
/**
25+
* Starts loading the panel from the given supplier in the background.
26+
*/
27+
public void loadPanel(Supplier<JPanel> panelSupplier) {
28+
loadablePanel.runInBackgroundAndLoad(() -> {
29+
JPanel panel = panelSupplier.get();
30+
contentContainer.add(panel, BorderLayout.CENTER);
31+
});
32+
}
33+
34+
@Override
35+
public void setVisible(boolean b) {
36+
if (b) {
37+
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
38+
setPreferredSize(new Dimension(
39+
Math.min(screenSize.width, (int) Math.floor(0.8 * getOwner().getWidth())),
40+
Math.min(screenSize.height, (int) Math.floor(0.8 * getOwner().getHeight())))
41+
);
42+
pack();
43+
setLocationRelativeTo(getParent());
44+
setResizable(false);
45+
}
46+
super.setVisible(b);
47+
}
48+
49+
@Override
50+
public boolean setLoading(boolean loading, boolean absolute) {
51+
return loadablePanel.setLoading(loading, absolute);
52+
}
53+
}

sirius_gui/src/main/java/de/unijena/bioinf/ms/gui/fingerid/CandidateListDetailView.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@
3636
import de.unijena.bioinf.ms.gui.SiriusGui;
3737
import de.unijena.bioinf.ms.gui.compute.jjobs.Jobs;
3838
import de.unijena.bioinf.ms.gui.configs.Icons;
39+
import de.unijena.bioinf.ms.gui.dialogs.LoadablePanelDialog;
3940
import de.unijena.bioinf.ms.gui.fingerid.candidate_filters.FMetFilter;
4041
import de.unijena.bioinf.ms.gui.fingerid.candidate_filters.MolecularPropertyMatcherEditor;
4142
import de.unijena.bioinf.ms.gui.fingerid.candidate_filters.SmartFilterMatcherEditor;
4243
import de.unijena.bioinf.ms.gui.mainframe.result_panel.ResultPanel;
43-
import de.unijena.bioinf.ms.gui.spectral_matching.SubstructureMatchingDialog;
44+
import de.unijena.bioinf.ms.gui.mainframe.result_panel.tabs.SubstructurePanel;
4445
import de.unijena.bioinf.ms.gui.table.ActionList;
4546
import de.unijena.bioinf.ms.gui.utils.GuiUtils;
4647
import de.unijena.bioinf.ms.gui.utils.PlaceholderTextField;
@@ -78,7 +79,7 @@ public class CandidateListDetailView extends CandidateListView implements MouseL
7879
protected StructureSearcher structureSearcher;
7980
protected Thread structureSearcherThread;
8081

81-
protected JMenuItem CopyInchiKey, CopyInchi, OpenInBrowser1, OpenInBrowser2, highlight, annotateSpectrum, CopySmiles;
82+
protected JMenuItem CopyInchiKey, CopyInchi, OpenInBrowser1, OpenInBrowser2, highlight, annotateSpectrum, CopySmiles, sketchStructure;
8283
protected JPopupMenu popupMenu;
8384

8485
protected int highlightAgree = -1;
@@ -91,6 +92,9 @@ public class CandidateListDetailView extends CandidateListView implements MouseL
9192
private final ResultPanel resultPanel;
9293
private final SiriusGui gui;
9394

95+
// Cached dialog for faster opening
96+
private SketcherDialog sketcherDialog;
97+
9498
/**
9599
* @param wasComputed function to validate whether the corresponding subtool that should provide the results was run. If the function returns false NOT_COMPUTED state is shown.
96100
*/
@@ -160,8 +164,10 @@ public void componentResized(ComponentEvent e) {
160164
OpenInBrowser2 = new JMenuItem("Open in all databases");
161165
highlight = new JMenuItem("Highlight matching substructures");
162166
annotateSpectrum = new JMenuItem("Show annotated spectrum");
167+
sketchStructure = new JMenuItem("Modify structure");
163168
CopyInchi.addActionListener(this);
164169
CopyInchiKey.addActionListener(this);
170+
sketchStructure.addActionListener(this);
165171
OpenInBrowser1.addActionListener(this);
166172
OpenInBrowser2.addActionListener(this);
167173
highlight.addActionListener(this);
@@ -176,6 +182,8 @@ public void componentResized(ComponentEvent e) {
176182
popupMenu.add(CopySmiles);
177183
popupMenu.add(CopyInchiKey);
178184
popupMenu.add(CopyInchi);
185+
popupMenu.addSeparator();
186+
popupMenu.add(sketchStructure);
179187
initializeFunctionalMetabolomicsFunctionality();
180188
setVisible(true);
181189
}
@@ -249,6 +257,16 @@ public void actionPerformed(ActionEvent e) {
249257
clipboard.setContents(new StringSelection(c.getInChiKey()), null);
250258
} else if (e.getSource() == CopyInchi) {
251259
clipboard.setContents(new StringSelection(c.getInChI().in2D), null);
260+
} else if (e.getSource() == sketchStructure) {
261+
Jobs.runEDTLater(() -> {
262+
if (sketcherDialog == null) {
263+
sketcherDialog = new SketcherDialog(SwingUtilities.getWindowAncestor(CandidateListDetailView.this), gui, c);
264+
sketcherDialog.setVisible(true);
265+
} else {
266+
sketcherDialog.updateMolecule(c);
267+
sketcherDialog.setVisible(true);
268+
}
269+
});
252270
} else if (e.getSource() == OpenInBrowser1) {
253271
try {
254272
GuiUtils.openURLInSystemBrowser(SwingUtilities.getWindowAncestor(this), new URI("https://www.ncbi.nlm.nih.gov/pccompound?term=%22" + c.getInChiKey() + "%22[InChIKey]"), gui);
@@ -357,9 +375,12 @@ public void mouseClicked(MouseEvent e) {
357375
}
358376

359377
private void clickOnMore(final FingerprintCandidateBean candidateBean) {
360-
Jobs.runEDTLater(() -> new SubstructureMatchingDialog(
361-
(Frame) SwingUtilities.getWindowAncestor(CandidateListDetailView.this), gui, candidateBean)
362-
.setVisible(true));
378+
Jobs.runEDTLater(() -> {
379+
LoadablePanelDialog substructureDialog = new LoadablePanelDialog(SwingUtilities.getWindowAncestor(CandidateListDetailView.this), "Reference spectra");
380+
substructureDialog.loadPanel(() -> new SubstructurePanel(gui, candidateBean));
381+
substructureDialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
382+
substructureDialog.setVisible(true);
383+
});
363384
}
364385

365386
private void clickOnDBLabel(DatabaseLabel label, FingerprintCandidateBean candidate) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package de.unijena.bioinf.ms.gui.fingerid;
2+
3+
import com.teamdev.jxbrowser.js.JsPromise;
4+
import de.unijena.bioinf.ms.gui.SiriusGui;
5+
import de.unijena.bioinf.ms.gui.dialogs.LoadablePanelDialog;
6+
import de.unijena.bioinf.ms.gui.mainframe.result_panel.tabs.SketcherPanel;
7+
import org.jetbrains.annotations.Nullable;
8+
9+
import javax.swing.*;
10+
import java.awt.*;
11+
import java.awt.event.WindowEvent;
12+
13+
public class SketcherDialog extends LoadablePanelDialog {
14+
15+
private SketcherPanel sketcherPanel;
16+
17+
public SketcherDialog(Window owner, SiriusGui siriusGui, @Nullable FingerprintCandidateBean structureCandidate) {
18+
super(owner, "Structure Sketcher");
19+
20+
JPanel southPanel = new JPanel();
21+
add(southPanel, BorderLayout.SOUTH);
22+
23+
JButton addButton = new JButton("Add");
24+
addButton.addActionListener(e -> sketcherPanel.tryUploadCurrentStructure());
25+
26+
JButton doneButton = new JButton("Add and close");
27+
doneButton.addActionListener(e -> {
28+
JsPromise promise = sketcherPanel.tryUploadCurrentStructure();
29+
promise.then(result -> {
30+
if (Boolean.TRUE.equals(result[0])) {
31+
dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
32+
}
33+
return promise;
34+
});
35+
});
36+
37+
loadPanel(() -> {
38+
sketcherPanel = new SketcherPanel(siriusGui, structureCandidate);
39+
southPanel.add(addButton);
40+
southPanel.add(doneButton);
41+
return sketcherPanel;
42+
});
43+
}
44+
45+
public void updateMolecule(FingerprintCandidateBean c) {
46+
runInBackgroundAndLoad(() -> sketcherPanel.updateSelectedFeatureSketcher(c.getParentFeatureId(), c.getSmiles()));
47+
}
48+
}

0 commit comments

Comments
 (0)