pycompound 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- app.py +470 -144
- pycompound/build_library.py +2 -9
- pycompound/plot_spectra.py +17 -42
- pycompound/processing.py +0 -9
- pycompound/similarity_measures.py +0 -3
- pycompound/spec_lib_matching.py +295 -102
- pycompound/spec_lib_matching_CLI.py +2 -7
- pycompound/tuning_CLI.py +2 -3
- {pycompound-0.1.0.dist-info → pycompound-0.1.2.dist-info}/METADATA +1 -1
- pycompound-0.1.2.dist-info/RECORD +14 -0
- pycompound-0.1.0.dist-info/RECORD +0 -14
- {pycompound-0.1.0.dist-info → pycompound-0.1.2.dist-info}/WHEEL +0 -0
- {pycompound-0.1.0.dist-info → pycompound-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {pycompound-0.1.0.dist-info → pycompound-0.1.2.dist-info}/top_level.txt +0 -0
app.py
CHANGED
|
@@ -4,6 +4,8 @@ from pycompound.spec_lib_matching import run_spec_lib_matching_on_HRMS_data
|
|
|
4
4
|
from pycompound.spec_lib_matching import run_spec_lib_matching_on_NRMS_data
|
|
5
5
|
from pycompound.spec_lib_matching import tune_params_on_HRMS_data
|
|
6
6
|
from pycompound.spec_lib_matching import tune_params_on_NRMS_data
|
|
7
|
+
from pycompound.spec_lib_matching import tune_params_on_HRMS_data_shiny
|
|
8
|
+
from pycompound.spec_lib_matching import tune_params_on_NRMS_data_shiny
|
|
7
9
|
from pycompound.plot_spectra import generate_plots_on_HRMS_data
|
|
8
10
|
from pycompound.plot_spectra import generate_plots_on_NRMS_data
|
|
9
11
|
from pathlib import Path
|
|
@@ -18,8 +20,45 @@ import matplotlib.pyplot as plt
|
|
|
18
20
|
import pandas as pd
|
|
19
21
|
import numpy as np
|
|
20
22
|
import netCDF4 as nc
|
|
21
|
-
from pyteomics import mgf
|
|
22
|
-
|
|
23
|
+
from pyteomics import mgf, mzml
|
|
24
|
+
import ast
|
|
25
|
+
from numbers import Real
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_LOG_QUEUE: asyncio.Queue[str] = asyncio.Queue()
|
|
30
|
+
|
|
31
|
+
def _run_with_redirects(fn, writer, *args, **kwargs):
|
|
32
|
+
with redirect_stdout(writer), redirect_stderr(writer):
|
|
33
|
+
return fn(*args, **kwargs)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def strip_text(s):
|
|
37
|
+
return [x.strip() for x in s.strip('[]').split(',') if x.strip()]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def strip_numeric(s):
|
|
41
|
+
return [float(x.strip()) for x in s.strip('[]').split(',') if x.strip()]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def strip_weights(s):
|
|
45
|
+
obj = ast.literal_eval(s) if isinstance(s, (str, bytes)) else s
|
|
46
|
+
keys = ['Cosine', 'Shannon', 'Renyi', 'Tsallis']
|
|
47
|
+
|
|
48
|
+
if isinstance(obj, (list, tuple)):
|
|
49
|
+
if len(obj) == 4 and all(isinstance(x, Real) for x in obj):
|
|
50
|
+
tuples = [obj]
|
|
51
|
+
else:
|
|
52
|
+
tuples = list(obj)
|
|
53
|
+
else:
|
|
54
|
+
raise ValueError(f"Expected a 4-tuple or a sequence of 4-tuples, got {type(obj).__name__}")
|
|
55
|
+
|
|
56
|
+
out = []
|
|
57
|
+
for t in tuples:
|
|
58
|
+
if not (isinstance(t, (list, tuple)) and len(t) == 4):
|
|
59
|
+
raise ValueError(f"Each item must be a 4-tuple, got: {t!r}")
|
|
60
|
+
out.append(dict(zip(keys, t)))
|
|
61
|
+
return out
|
|
23
62
|
|
|
24
63
|
|
|
25
64
|
def build_library(input_path=None, output_path=None):
|
|
@@ -152,40 +191,45 @@ def extract_first_column_ids(file_path: str, max_ids: int = 20000):
|
|
|
152
191
|
return []
|
|
153
192
|
|
|
154
193
|
|
|
194
|
+
def _open_plot_window(session, png_bytes: bytes, title: str = "plot.png"):
|
|
195
|
+
"""Send PNG bytes to browser and open in a new window as a data URL."""
|
|
196
|
+
b64 = base64.b64encode(png_bytes).decode("ascii")
|
|
197
|
+
data_url = f"data:image/png;base64,{b64}"
|
|
198
|
+
session.send_custom_message("open-plot-window", {"png": data_url, "title": title})
|
|
199
|
+
|
|
200
|
+
|
|
155
201
|
def plot_spectra_ui(platform: str):
|
|
156
|
-
# Base inputs common to all platforms
|
|
157
202
|
base_inputs = [
|
|
158
203
|
ui.input_file("query_data", "Upload query dataset (mgf, mzML, cdf, msp, or csv):"),
|
|
159
204
|
ui.input_file("reference_data", "Upload reference dataset (mgf, mzML, cdf, msp, or csv):"),
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
205
|
+
ui.input_selectize(
|
|
206
|
+
"spectrum_ID1",
|
|
207
|
+
"Select spectrum ID 1 (default is the first spectrum in the library):",
|
|
208
|
+
choices=[],
|
|
209
|
+
multiple=False,
|
|
210
|
+
options={"placeholder": "Upload a library..."},
|
|
211
|
+
),
|
|
212
|
+
ui.input_selectize(
|
|
213
|
+
"spectrum_ID2",
|
|
214
|
+
"Select spectrum ID 2 (default is the first spectrum in the library):",
|
|
215
|
+
choices=[],
|
|
216
|
+
multiple=False,
|
|
217
|
+
options={"placeholder": "Upload a library..."},
|
|
218
|
+
),
|
|
174
219
|
ui.input_select("similarity_measure", "Select similarity measure:", ["cosine","shannon","renyi","tsallis","mixture","jaccard","dice","3w_jaccard","sokal_sneath","binary_cosine","mountford","mcconnaughey","driver_kroeber","simpson","braun_banquet","fager_mcgowan","kulczynski","intersection","hamming","hellinger"]),
|
|
220
|
+
ui.input_text('weights', 'Weights for mixture similarity measure (cosine, shannon, renyi, tsallis):', '0.25, 0.25, 0.25, 0.25'),
|
|
175
221
|
ui.input_select(
|
|
176
222
|
"high_quality_reference_library",
|
|
177
|
-
"Indicate whether the reference library is considered high quality. "
|
|
178
|
-
"If True, filtering and noise removal are only applied to the query spectra.",
|
|
223
|
+
"Indicate whether the reference library is considered high quality. If True, filtering and noise removal are only applied to the query spectra.",
|
|
179
224
|
[False, True],
|
|
180
225
|
),
|
|
181
226
|
]
|
|
182
227
|
|
|
183
|
-
# Extra inputs depending on platform
|
|
184
228
|
if platform == "HRMS":
|
|
185
229
|
extra_inputs = [
|
|
186
230
|
ui.input_text(
|
|
187
231
|
"spectrum_preprocessing_order",
|
|
188
|
-
"Sequence of characters for preprocessing order (C, F, M, N, L, W). M must be included, C before M if used.",
|
|
232
|
+
"Sequence of characters for preprocessing order (C (centroiding), F (filtering), M (matching), N (noise removal), L (low-entropy transformation), W (weight factor transformation)). M must be included, C before M if used.",
|
|
189
233
|
"FCNMWL",
|
|
190
234
|
),
|
|
191
235
|
ui.input_numeric("window_size_centroiding", "Centroiding window-size:", 0.5),
|
|
@@ -195,12 +239,11 @@ def plot_spectra_ui(platform: str):
|
|
|
195
239
|
extra_inputs = [
|
|
196
240
|
ui.input_text(
|
|
197
241
|
"spectrum_preprocessing_order",
|
|
198
|
-
"Sequence of characters for preprocessing order (F, N, L, W).",
|
|
242
|
+
"Sequence of characters for preprocessing order (F (filtering), N (noise removal), L (low-entropy transformation), W (weight factor transformation)).",
|
|
199
243
|
"FNLW",
|
|
200
244
|
)
|
|
201
245
|
]
|
|
202
246
|
|
|
203
|
-
# Numeric inputs
|
|
204
247
|
numeric_inputs = [
|
|
205
248
|
ui.input_numeric("mz_min", "Minimum m/z for filtering:", 0),
|
|
206
249
|
ui.input_numeric("mz_max", "Maximum m/z for filtering:", 99999999),
|
|
@@ -213,68 +256,77 @@ def plot_spectra_ui(platform: str):
|
|
|
213
256
|
ui.input_numeric("entropy_dimension", "Entropy dimension (Renyi/Tsallis only):", 1.1),
|
|
214
257
|
]
|
|
215
258
|
|
|
216
|
-
# Y-axis transformation select input
|
|
217
259
|
select_input = ui.input_select(
|
|
218
260
|
"y_axis_transformation",
|
|
219
261
|
"Transformation to apply to intensity axis:",
|
|
220
262
|
["normalized", "none", "log10", "sqrt"],
|
|
221
263
|
)
|
|
222
264
|
|
|
223
|
-
# Run and Back buttons
|
|
224
265
|
run_button_plot_spectra = ui.download_button("run_btn_plot_spectra", "Run", style="font-size:16px; padding:15px 30px; width:200px; height:80px")
|
|
225
266
|
back_button = ui.input_action_button("back", "Back to main menu", style="font-size:16px; padding:15px 30px; width:200px; height:80px")
|
|
226
267
|
|
|
227
|
-
# Layout base_inputs and extra_inputs in columns
|
|
228
268
|
if platform == "HRMS":
|
|
229
269
|
inputs_columns = ui.layout_columns(
|
|
230
|
-
ui.div(base_inputs[0:
|
|
231
|
-
ui.div([base_inputs[
|
|
270
|
+
ui.div(base_inputs[0:6], style="display:flex; flex-direction:column; gap:10px;"),
|
|
271
|
+
ui.div([base_inputs[6:7], *extra_inputs], style="display:flex; flex-direction:column; gap:10px;"),
|
|
232
272
|
ui.div(numeric_inputs[0:5], style="display:flex; flex-direction:column; gap:10px;"),
|
|
233
273
|
ui.div([numeric_inputs[5:10], select_input], style="display:flex; flex-direction:column; gap:10px;"),
|
|
234
|
-
col_widths=(3,
|
|
274
|
+
col_widths=(3,3,3,3),
|
|
235
275
|
)
|
|
236
276
|
elif platform == "NRMS":
|
|
237
277
|
inputs_columns = ui.layout_columns(
|
|
238
|
-
ui.div(base_inputs[0:
|
|
239
|
-
ui.div([base_inputs[
|
|
278
|
+
ui.div(base_inputs[0:6], style="display:flex; flex-direction:column; gap:10px;"),
|
|
279
|
+
ui.div([base_inputs[6:7], *extra_inputs], style="display:flex; flex-direction:column; gap:10px;"),
|
|
240
280
|
ui.div(numeric_inputs[0:5], style="display:flex; flex-direction:column; gap:10px;"),
|
|
241
281
|
ui.div([numeric_inputs[5:10], select_input], style="display:flex; flex-direction:column; gap:10px;"),
|
|
242
|
-
col_widths=(3,
|
|
282
|
+
col_widths=(3,3,3,3),
|
|
243
283
|
)
|
|
244
284
|
|
|
245
|
-
# Combine everything
|
|
246
285
|
return ui.div(
|
|
247
286
|
ui.TagList(
|
|
248
287
|
ui.h2("Plot Spectra"),
|
|
249
288
|
inputs_columns,
|
|
250
289
|
run_button_plot_spectra,
|
|
251
290
|
back_button,
|
|
252
|
-
ui.div(ui.output_text("plot_query_status"), style="margin-top:8px; font-size:14px")
|
|
291
|
+
ui.div(ui.output_text("plot_query_status"), style="margin-top:8px; font-size:14px"),
|
|
292
|
+
ui.div(ui.output_text("plot_reference_status"), style="margin-top:8px; font-size:14px")
|
|
253
293
|
),
|
|
254
294
|
)
|
|
255
295
|
|
|
256
296
|
|
|
257
297
|
|
|
258
298
|
def run_spec_lib_matching_ui(platform: str):
|
|
259
|
-
# Base inputs common to all platforms
|
|
260
299
|
base_inputs = [
|
|
261
300
|
ui.input_file("query_data", "Upload query dataset (mgf, mzML, cdf, msp, or csv):"),
|
|
262
301
|
ui.input_file("reference_data", "Upload reference dataset (mgf, mzML, cdf, msp, or csv):"),
|
|
263
302
|
ui.input_select("similarity_measure", "Select similarity measure:", ["cosine","shannon","renyi","tsallis","mixture","jaccard","dice","3w_jaccard","sokal_sneath","binary_cosine","mountford","mcconnaughey","driver_kroeber","simpson","braun_banquet","fager_mcgowan","kulczynski","intersection","hamming","hellinger"]),
|
|
303
|
+
ui.input_text('weights', 'Weights for mixture similarity measure (cosine, shannon, renyi, tsallis):', '0.25, 0.25, 0.25, 0.25'),
|
|
304
|
+
ui.input_selectize(
|
|
305
|
+
"spectrum_ID1",
|
|
306
|
+
"Select spectrum ID 1 (only applicable for plotting; default is the first spectrum in the query library):",
|
|
307
|
+
choices=[],
|
|
308
|
+
multiple=False,
|
|
309
|
+
options={"placeholder": "Upload a library..."},
|
|
310
|
+
),
|
|
311
|
+
ui.input_selectize(
|
|
312
|
+
"spectrum_ID2",
|
|
313
|
+
"Select spectrum ID 2 (only applicable for plotting; default is the first spectrum in the reference library):",
|
|
314
|
+
choices=[],
|
|
315
|
+
multiple=False,
|
|
316
|
+
options={"placeholder": "Upload a library..."},
|
|
317
|
+
),
|
|
264
318
|
ui.input_select(
|
|
265
319
|
"high_quality_reference_library",
|
|
266
|
-
"Indicate whether the reference library is considered high quality. "
|
|
267
|
-
"If True, filtering and noise removal are only applied to the query spectra.",
|
|
320
|
+
"Indicate whether the reference library is considered high quality. If True, filtering and noise removal are only applied to the query spectra.",
|
|
268
321
|
[False, True],
|
|
269
|
-
)
|
|
322
|
+
)
|
|
270
323
|
]
|
|
271
324
|
|
|
272
|
-
# Extra inputs depending on platform
|
|
273
325
|
if platform == "HRMS":
|
|
274
326
|
extra_inputs = [
|
|
275
327
|
ui.input_text(
|
|
276
328
|
"spectrum_preprocessing_order",
|
|
277
|
-
"Sequence of characters for preprocessing order (C, F, M, N, L, W). M must be included, C before M if used.",
|
|
329
|
+
"Sequence of characters for preprocessing order (C (centroiding), F (filtering), M (matching), N (noise removal), L (low-entropy transformation), W (weight factor transformation)). M must be included, C before M if used.",
|
|
278
330
|
"FCNMWL",
|
|
279
331
|
),
|
|
280
332
|
ui.input_numeric("window_size_centroiding", "Centroiding window-size:", 0.5),
|
|
@@ -284,12 +336,11 @@ def run_spec_lib_matching_ui(platform: str):
|
|
|
284
336
|
extra_inputs = [
|
|
285
337
|
ui.input_text(
|
|
286
338
|
"spectrum_preprocessing_order",
|
|
287
|
-
"Sequence of characters for preprocessing order (F, N, L, W).",
|
|
339
|
+
"Sequence of characters for preprocessing order (F (filtering), N (noise removal), L (low-entropy transformation), W (weight factor transformation)).",
|
|
288
340
|
"FNLW",
|
|
289
341
|
)
|
|
290
342
|
]
|
|
291
343
|
|
|
292
|
-
# Numeric inputs
|
|
293
344
|
numeric_inputs = [
|
|
294
345
|
ui.input_numeric("mz_min", "Minimum m/z for filtering:", 0),
|
|
295
346
|
ui.input_numeric("mz_max", "Maximum m/z for filtering:", 99999999),
|
|
@@ -300,30 +351,29 @@ def run_spec_lib_matching_ui(platform: str):
|
|
|
300
351
|
ui.input_numeric("wf_int", "Intensity weight factor:", 1.0),
|
|
301
352
|
ui.input_numeric("LET_threshold", "Low-entropy threshold:", 0.0),
|
|
302
353
|
ui.input_numeric("entropy_dimension", "Entropy dimension (Renyi/Tsallis only):", 1.1),
|
|
303
|
-
ui.input_numeric("n_top_matches_to_save", "Number of top matches to save:",
|
|
354
|
+
ui.input_numeric("n_top_matches_to_save", "Number of top matches to save:", 3),
|
|
304
355
|
]
|
|
305
356
|
|
|
306
357
|
|
|
307
|
-
|
|
308
|
-
|
|
358
|
+
run_button_spec_lib_matching = ui.download_button("run_btn_spec_lib_matching", "Run Spectral Library Matching", style="font-size:16px; padding:15px 30px; width:200px; height:80px")
|
|
359
|
+
run_button_plot_spectra_within_spec_lib_matching = ui.download_button("run_btn_plot_spectra_within_spec_lib_matching", "Plot Spectra", style="font-size:16px; padding:15px 30px; width:200px; height:80px")
|
|
309
360
|
back_button = ui.input_action_button("back", "Back to main menu", style="font-size:16px; padding:15px 30px; width:200px; height:80px")
|
|
310
361
|
|
|
311
|
-
# Layout base_inputs and extra_inputs in columns
|
|
312
362
|
if platform == "HRMS":
|
|
313
363
|
inputs_columns = ui.layout_columns(
|
|
314
|
-
ui.div(base_inputs[0:
|
|
315
|
-
ui.div([base_inputs[
|
|
364
|
+
ui.div(base_inputs[0:6], style="display:flex; flex-direction:column; gap:10px;"),
|
|
365
|
+
ui.div([base_inputs[6:7], *extra_inputs], style="display:flex; flex-direction:column; gap:10px;"),
|
|
316
366
|
ui.div(numeric_inputs[0:5], style="display:flex; flex-direction:column; gap:10px;"),
|
|
317
367
|
ui.div(numeric_inputs[5:10], style="display:flex; flex-direction:column; gap:10px;"),
|
|
318
|
-
col_widths=(3,
|
|
368
|
+
col_widths=(3,3,3,3)
|
|
319
369
|
)
|
|
320
370
|
elif platform == "NRMS":
|
|
321
371
|
inputs_columns = ui.layout_columns(
|
|
322
|
-
ui.div(base_inputs[0:
|
|
323
|
-
ui.div([base_inputs[
|
|
372
|
+
ui.div(base_inputs[0:6], style="display:flex; flex-direction:column; gap:10px;"),
|
|
373
|
+
ui.div([base_inputs[6:7], *extra_inputs], style="display:flex; flex-direction:column; gap:10px;"),
|
|
324
374
|
ui.div(numeric_inputs[0:5], style="display:flex; flex-direction:column; gap:10px;"),
|
|
325
375
|
ui.div(numeric_inputs[5:10], style="display:flex; flex-direction:column; gap:10px;"),
|
|
326
|
-
col_widths=(3,
|
|
376
|
+
col_widths=(3,3,3,3)
|
|
327
377
|
)
|
|
328
378
|
|
|
329
379
|
log_panel = ui.card(
|
|
@@ -332,19 +382,99 @@ def run_spec_lib_matching_ui(platform: str):
|
|
|
332
382
|
style="max-height:300px; overflow:auto"
|
|
333
383
|
)
|
|
334
384
|
|
|
335
|
-
# Combine everything
|
|
336
385
|
return ui.div(
|
|
337
386
|
ui.TagList(
|
|
338
387
|
ui.h2("Run Spectral Library Matching"),
|
|
339
388
|
inputs_columns,
|
|
340
389
|
run_button_spec_lib_matching,
|
|
390
|
+
run_button_plot_spectra_within_spec_lib_matching,
|
|
341
391
|
back_button,
|
|
342
|
-
log_panel
|
|
392
|
+
log_panel
|
|
343
393
|
),
|
|
344
394
|
)
|
|
345
395
|
|
|
346
396
|
|
|
347
397
|
|
|
398
|
+
def run_parameter_tuning_ui(platform: str):
|
|
399
|
+
base_inputs = [
|
|
400
|
+
ui.input_file("query_data", "Upload query dataset (mgf, mzML, cdf, msp, or csv):"),
|
|
401
|
+
ui.input_file("reference_data", "Upload reference dataset (mgf, mzML, cdf, msp, or csv):"),
|
|
402
|
+
ui.input_selectize("similarity_measure", "Select similarity measure(s):", ["cosine","shannon","renyi","tsallis","mixture","jaccard","dice","3w_jaccard","sokal_sneath","binary_cosine","mountford","mcconnaughey","driver_kroeber","simpson","braun_banquet","fager_mcgowan","kulczynski","intersection","hamming","hellinger"], multiple=True, selected='cosine'),
|
|
403
|
+
ui.input_text('weights', 'Weights for mixture similarity measure (cosine, shannon, renyi, tsallis):', '((0.25, 0.25, 0.25, 0.25))'),
|
|
404
|
+
ui.input_text("high_quality_reference_library", "Indicate whether the reference library is considered high quality. If True, filtering and noise removal are only applied to the query spectra.", '[True]')
|
|
405
|
+
]
|
|
406
|
+
|
|
407
|
+
if platform == "HRMS":
|
|
408
|
+
extra_inputs = [
|
|
409
|
+
ui.input_text(
|
|
410
|
+
"spectrum_preprocessing_order",
|
|
411
|
+
"Sequence of characters for preprocessing order (C (centroiding), F (filtering), M (matching), N (noise removal), L (low-entropy transformation), W (weight factor transformation)). M must be included, C before M if used.",
|
|
412
|
+
"[FCNMWL,CWM]",
|
|
413
|
+
),
|
|
414
|
+
ui.input_text("window_size_centroiding", "Centroiding window-size:", "[0.5]"),
|
|
415
|
+
ui.input_text("window_size_matching", "Matching window-size:", "[0.1,0.5]"),
|
|
416
|
+
]
|
|
417
|
+
else:
|
|
418
|
+
extra_inputs = [
|
|
419
|
+
ui.input_text(
|
|
420
|
+
"spectrum_preprocessing_order",
|
|
421
|
+
"Sequence of characters for preprocessing order (F (filtering), N (noise removal), L (low-entropy transformation), W (weight factor transformation)).",
|
|
422
|
+
"[FNLW,WNL]",
|
|
423
|
+
)
|
|
424
|
+
]
|
|
425
|
+
|
|
426
|
+
numeric_inputs = [
|
|
427
|
+
ui.input_text("mz_min", "Minimum m/z for filtering:", '[0]'),
|
|
428
|
+
ui.input_text("mz_max", "Maximum m/z for filtering:", '[99999999]'),
|
|
429
|
+
ui.input_text("int_min", "Minimum intensity for filtering:", '[0]'),
|
|
430
|
+
ui.input_text("int_max", "Maximum intensity for filtering:", '[999999999]'),
|
|
431
|
+
ui.input_text("noise_threshold", "Noise removal threshold:", '[0.0]'),
|
|
432
|
+
ui.input_text("wf_mz", "Mass/charge weight factor:", '[0.0]'),
|
|
433
|
+
ui.input_text("wf_int", "Intensity weight factor:", '[1.0]'),
|
|
434
|
+
ui.input_text("LET_threshold", "Low-entropy threshold:", '[0.0]'),
|
|
435
|
+
ui.input_text("entropy_dimension", "Entropy dimension (Renyi/Tsallis only):", '[1.1]')
|
|
436
|
+
]
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
run_button_parameter_tuning = ui.download_button("run_btn_parameter_tuning", "Tune parameters", style="font-size:16px; padding:15px 30px; width:200px; height:80px")
|
|
440
|
+
back_button = ui.input_action_button("back", "Back to main menu", style="font-size:16px; padding:15px 30px; width:200px; height:80px")
|
|
441
|
+
|
|
442
|
+
if platform == "HRMS":
|
|
443
|
+
inputs_columns = ui.layout_columns(
|
|
444
|
+
ui.div(base_inputs[0:6], style="display:flex; flex-direction:column; gap:10px;"),
|
|
445
|
+
ui.div([base_inputs[6:7], *extra_inputs], style="display:flex; flex-direction:column; gap:10px;"),
|
|
446
|
+
ui.div(numeric_inputs[0:5], style="display:flex; flex-direction:column; gap:10px;"),
|
|
447
|
+
ui.div(numeric_inputs[5:9], style="display:flex; flex-direction:column; gap:10px;"),
|
|
448
|
+
col_widths=(3, 3, 3, 3),
|
|
449
|
+
)
|
|
450
|
+
elif platform == "NRMS":
|
|
451
|
+
inputs_columns = ui.layout_columns(
|
|
452
|
+
ui.div(base_inputs[0:6], style="display:flex; flex-direction:column; gap:10px;"),
|
|
453
|
+
ui.div([base_inputs[6:7], *extra_inputs], style="display:flex; flex-direction:column; gap:10px;"),
|
|
454
|
+
ui.div(numeric_inputs[0:5], style="display:flex; flex-direction:column; gap:10px;"),
|
|
455
|
+
ui.div(numeric_inputs[5:9], style="display:flex; flex-direction:column; gap:10px;"),
|
|
456
|
+
col_widths=(3, 3, 3, 3),
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
log_panel = ui.card(
|
|
460
|
+
ui.card_header("Identification log"),
|
|
461
|
+
ui.output_text_verbatim("match_log"),
|
|
462
|
+
style="max-height:300px; overflow:auto"
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
return ui.div(
|
|
466
|
+
ui.TagList(
|
|
467
|
+
ui.h2("Tune parameters"),
|
|
468
|
+
inputs_columns,
|
|
469
|
+
run_button_parameter_tuning,
|
|
470
|
+
back_button,
|
|
471
|
+
log_panel
|
|
472
|
+
),
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
|
|
348
478
|
app_ui = ui.page_fluid(
|
|
349
479
|
ui.output_ui("main_ui"),
|
|
350
480
|
ui.output_text("status_output")
|
|
@@ -361,8 +491,15 @@ def server(input, output, session):
|
|
|
361
491
|
|
|
362
492
|
run_status_plot_spectra = reactive.Value("")
|
|
363
493
|
run_status_spec_lib_matching = reactive.Value("")
|
|
494
|
+
run_status_plot_spectra_within_spec_lib_matching = reactive.Value("")
|
|
495
|
+
run_status_parameter_tuning = reactive.Value("")
|
|
496
|
+
is_tuning_running = reactive.Value(False)
|
|
364
497
|
match_log_rv = reactive.Value("")
|
|
365
498
|
is_matching_rv = reactive.Value(False)
|
|
499
|
+
is_any_job_running = reactive.Value(False)
|
|
500
|
+
latest_csv_path_rv = reactive.Value("")
|
|
501
|
+
latest_df_rv = reactive.Value(None)
|
|
502
|
+
is_running_rv = reactive.Value(False)
|
|
366
503
|
|
|
367
504
|
query_ids_rv = reactive.Value([])
|
|
368
505
|
query_file_path_rv = reactive.Value(None)
|
|
@@ -377,6 +514,96 @@ def server(input, output, session):
|
|
|
377
514
|
converted_reference_path_rv = reactive.Value(None)
|
|
378
515
|
|
|
379
516
|
|
|
517
|
+
def _reset_plot_spectra_state():
|
|
518
|
+
query_status_rv.set("")
|
|
519
|
+
reference_status_rv.set("")
|
|
520
|
+
query_ids_rv.set([])
|
|
521
|
+
reference_ids_rv.set([])
|
|
522
|
+
query_file_path_rv.set(None)
|
|
523
|
+
reference_file_path_rv.set(None)
|
|
524
|
+
query_result_rv.set(None)
|
|
525
|
+
reference_result_rv.set(None)
|
|
526
|
+
converted_query_path_rv.set(None)
|
|
527
|
+
converted_reference_path_rv.set(None)
|
|
528
|
+
try:
|
|
529
|
+
ui.update_selectize("spectrum_ID1", choices=[], selected=None)
|
|
530
|
+
ui.update_selectize("spectrum_ID2", choices=[], selected=None)
|
|
531
|
+
except Exception:
|
|
532
|
+
pass
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _reset_spec_lib_matching_state():
|
|
536
|
+
match_log_rv.set("")
|
|
537
|
+
is_matching_rv.set(False)
|
|
538
|
+
is_any_job_running.set(False)
|
|
539
|
+
try:
|
|
540
|
+
ui.update_selectize("spectrum_ID1", choices=[], selected=None)
|
|
541
|
+
ui.update_selectize("spectrum_ID2", choices=[], selected=None)
|
|
542
|
+
except Exception:
|
|
543
|
+
pass
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
def _reset_parameter_tuning_state():
|
|
547
|
+
match_log_rv.set("")
|
|
548
|
+
is_tuning_running.set(False)
|
|
549
|
+
is_any_job_running.set(False)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
@reactive.effect
|
|
553
|
+
@reactive.event(input.back)
|
|
554
|
+
def _clear_on_back_from_pages():
|
|
555
|
+
page = current_page()
|
|
556
|
+
if page == "plot_spectra":
|
|
557
|
+
_reset_plot_spectra_state()
|
|
558
|
+
elif page == "run_spec_lib_matching":
|
|
559
|
+
_reset_spec_lib_matching_state()
|
|
560
|
+
elif page == "run_parameter_tuning":
|
|
561
|
+
_reset_parameter_tuning_state()
|
|
562
|
+
|
|
563
|
+
@reactive.effect
|
|
564
|
+
def _clear_on_enter_pages():
|
|
565
|
+
page = current_page()
|
|
566
|
+
if page == "plot_spectra":
|
|
567
|
+
_reset_plot_spectra_state()
|
|
568
|
+
elif page == "run_spec_lib_matching":
|
|
569
|
+
_reset_spec_lib_matching_state()
|
|
570
|
+
elif page == "run_parameter_tuning":
|
|
571
|
+
_reset_parameter_tuning_state()
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def _drain_queue_nowait(q: asyncio.Queue) -> list[str]:
|
|
575
|
+
out = []
|
|
576
|
+
try:
|
|
577
|
+
while True:
|
|
578
|
+
out.append(q.get_nowait())
|
|
579
|
+
except asyncio.QueueEmpty:
|
|
580
|
+
pass
|
|
581
|
+
return out
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
class ReactiveWriter(io.TextIOBase):
|
|
585
|
+
def __init__(self, loop: asyncio.AbstractEventLoop):
|
|
586
|
+
self._loop = loop
|
|
587
|
+
def write(self, s: str):
|
|
588
|
+
if not s:
|
|
589
|
+
return 0
|
|
590
|
+
self._loop.call_soon_threadsafe(_LOG_QUEUE.put_nowait, s)
|
|
591
|
+
return len(s)
|
|
592
|
+
def flush(self):
|
|
593
|
+
pass
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
@reactive.effect
|
|
597
|
+
async def _pump_logs():
|
|
598
|
+
if not (is_any_job_running.get() or is_tuning_running.get() or is_matching_rv.get()):
|
|
599
|
+
return
|
|
600
|
+
reactive.invalidate_later(0.05)
|
|
601
|
+
msgs = _drain_queue_nowait(_LOG_QUEUE)
|
|
602
|
+
if msgs:
|
|
603
|
+
match_log_rv.set(match_log_rv.get() + "".join(msgs))
|
|
604
|
+
await reactive.flush()
|
|
605
|
+
|
|
606
|
+
|
|
380
607
|
def process_database(file_path: str):
|
|
381
608
|
suffix = Path(file_path).suffix.lower()
|
|
382
609
|
return {"path": file_path, "suffix": suffix}
|
|
@@ -385,13 +612,14 @@ def server(input, output, session):
|
|
|
385
612
|
def plot_query_status():
|
|
386
613
|
return query_status_rv.get() or ""
|
|
387
614
|
|
|
615
|
+
@render.text
|
|
616
|
+
def plot_reference_status():
|
|
617
|
+
return reference_status_rv.get() or ""
|
|
618
|
+
|
|
388
619
|
|
|
389
620
|
@reactive.effect
|
|
390
621
|
@reactive.event(input.query_data)
|
|
391
622
|
async def _on_query_upload():
|
|
392
|
-
if current_page() != "plot_spectra":
|
|
393
|
-
return
|
|
394
|
-
|
|
395
623
|
files = input.query_data()
|
|
396
624
|
req(files and len(files) > 0)
|
|
397
625
|
|
|
@@ -414,9 +642,6 @@ def server(input, output, session):
|
|
|
414
642
|
@reactive.effect
|
|
415
643
|
@reactive.event(input.reference_data)
|
|
416
644
|
async def _on_reference_upload():
|
|
417
|
-
if current_page() != "plot_spectra":
|
|
418
|
-
return
|
|
419
|
-
|
|
420
645
|
files = input.reference_data()
|
|
421
646
|
req(files and len(files) > 0)
|
|
422
647
|
|
|
@@ -441,24 +666,6 @@ def server(input, output, session):
|
|
|
441
666
|
return match_log_rv.get()
|
|
442
667
|
|
|
443
668
|
|
|
444
|
-
class ReactiveWriter(io.TextIOBase):
|
|
445
|
-
def __init__(self, rv):
|
|
446
|
-
self.rv = rv
|
|
447
|
-
def write(self, s: str):
|
|
448
|
-
if not s:
|
|
449
|
-
return 0
|
|
450
|
-
self.rv.set(self.rv.get() + s)
|
|
451
|
-
try:
|
|
452
|
-
loop = asyncio.get_running_loop()
|
|
453
|
-
loop.create_task(reactive.flush())
|
|
454
|
-
except RuntimeError:
|
|
455
|
-
pass
|
|
456
|
-
return len(s)
|
|
457
|
-
def flush(self):
|
|
458
|
-
pass
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
669
|
@reactive.Effect
|
|
463
670
|
def _():
|
|
464
671
|
if input.plot_spectra() > plot_clicks.get():
|
|
@@ -467,6 +674,9 @@ def server(input, output, session):
|
|
|
467
674
|
elif input.run_spec_lib_matching() > match_clicks.get():
|
|
468
675
|
current_page.set("run_spec_lib_matching")
|
|
469
676
|
match_clicks.set(input.run_spec_lib_matching())
|
|
677
|
+
elif input.run_parameter_tuning() > match_clicks.get():
|
|
678
|
+
current_page.set("run_parameter_tuning")
|
|
679
|
+
match_clicks.set(input.run_parameter_tuning())
|
|
470
680
|
elif hasattr(input, "back") and input.back() > back_clicks.get():
|
|
471
681
|
current_page.set("main_menu")
|
|
472
682
|
back_clicks.set(input.back())
|
|
@@ -474,8 +684,6 @@ def server(input, output, session):
|
|
|
474
684
|
|
|
475
685
|
@render.image
|
|
476
686
|
def image():
|
|
477
|
-
from pathlib import Path
|
|
478
|
-
|
|
479
687
|
dir = Path(__file__).resolve().parent
|
|
480
688
|
img: ImgData = {"src": str(dir / "www/emblem.png"), "width": "320px", "height": "250px"}
|
|
481
689
|
return img
|
|
@@ -512,6 +720,7 @@ def server(input, output, session):
|
|
|
512
720
|
),
|
|
513
721
|
ui.input_action_button("plot_spectra", "Plot two spectra before and after preprocessing transformations.", style="font-size:18px; padding:20px 40px; width:550px; height:100px; margin-top:10px; margin-right:50px"),
|
|
514
722
|
ui.input_action_button("run_spec_lib_matching", "Run spectral library matching to perform compound identification on a query library of spectra.", style="font-size:18px; padding:20px 40px; width:550px; height:100px; margin-top:10px; margin-right:50px"),
|
|
723
|
+
ui.input_action_button("run_parameter_tuning", "Tune parameters to maximize accuracy of compound identification given a query library with known spectrum IDs.", style="font-size:18px; padding:20px 40px; width:450px; height:120px; margin-top:10px; margin-right:50px"),
|
|
515
724
|
ui.div(
|
|
516
725
|
"References:",
|
|
517
726
|
style="margin-top:35px; text-align:left; font-size:24px; font-weight:bold"
|
|
@@ -562,15 +771,14 @@ def server(input, output, session):
|
|
|
562
771
|
return plot_spectra_ui(input.chromatography_platform())
|
|
563
772
|
elif current_page() == "run_spec_lib_matching":
|
|
564
773
|
return run_spec_lib_matching_ui(input.chromatography_platform())
|
|
774
|
+
elif current_page() == "run_parameter_tuning":
|
|
775
|
+
return run_parameter_tuning_ui(input.chromatography_platform())
|
|
565
776
|
|
|
566
777
|
|
|
567
778
|
|
|
568
779
|
@reactive.effect
|
|
569
780
|
@reactive.event(input.query_data)
|
|
570
781
|
async def _populate_ids_from_query_upload():
|
|
571
|
-
if current_page() != "plot_spectra":
|
|
572
|
-
return
|
|
573
|
-
|
|
574
782
|
files = input.query_data()
|
|
575
783
|
if not files:
|
|
576
784
|
return
|
|
@@ -578,7 +786,6 @@ def server(input, output, session):
|
|
|
578
786
|
in_path = Path(files[0]["datapath"])
|
|
579
787
|
suffix = in_path.suffix.lower()
|
|
580
788
|
|
|
581
|
-
# Decide what CSV to read IDs from
|
|
582
789
|
try:
|
|
583
790
|
if suffix == ".csv":
|
|
584
791
|
csv_path = in_path
|
|
@@ -587,17 +794,14 @@ def server(input, output, session):
|
|
|
587
794
|
query_status_rv.set(f"Converting {in_path.name} → CSV …")
|
|
588
795
|
await reactive.flush()
|
|
589
796
|
|
|
590
|
-
# Choose an output temp path next to the upload
|
|
591
797
|
tmp_csv_path = in_path.with_suffix(".converted.csv")
|
|
592
798
|
|
|
593
799
|
out_obj = await asyncio.to_thread(build_library, str(in_path), str(tmp_csv_path))
|
|
594
800
|
|
|
595
|
-
# out_obj may be a path (str/PathLike) OR a DataFrame. Normalize to a path.
|
|
596
801
|
if isinstance(out_obj, (str, os.PathLike, Path)):
|
|
597
802
|
csv_path = Path(out_obj)
|
|
598
803
|
elif isinstance(out_obj, pd.DataFrame):
|
|
599
|
-
|
|
600
|
-
out_obj.to_csv(tmp_csv_path, index=False)
|
|
804
|
+
out_obj.to_csv(tmp_csv_path, index=False, sep='\t')
|
|
601
805
|
csv_path = tmp_csv_path
|
|
602
806
|
else:
|
|
603
807
|
raise TypeError(f"build_library returned unsupported type: {type(out_obj)}")
|
|
@@ -607,16 +811,12 @@ def server(input, output, session):
|
|
|
607
811
|
query_status_rv.set(f"Reading IDs from: {csv_path.name} …")
|
|
608
812
|
await reactive.flush()
|
|
609
813
|
|
|
610
|
-
# Extract IDs from the CSV’s first column
|
|
611
814
|
ids = await asyncio.to_thread(extract_first_column_ids, str(csv_path))
|
|
612
815
|
query_ids_rv.set(ids)
|
|
613
816
|
|
|
614
|
-
# Update dropdowns
|
|
615
817
|
ui.update_selectize("spectrum_ID1", choices=ids, selected=(ids[0] if ids else None))
|
|
616
818
|
|
|
617
|
-
query_status_rv.set(
|
|
618
|
-
f"✅ Loaded {len(ids)} IDs from {csv_path.name}" if ids else f"⚠️ No IDs found in {csv_path.name}"
|
|
619
|
-
)
|
|
819
|
+
query_status_rv.set(f"✅ Loaded {len(ids)} IDs from {csv_path.name}" if ids else f"⚠️ No IDs found in {csv_path.name}")
|
|
620
820
|
await reactive.flush()
|
|
621
821
|
|
|
622
822
|
except Exception as e:
|
|
@@ -628,9 +828,6 @@ def server(input, output, session):
|
|
|
628
828
|
@reactive.effect
|
|
629
829
|
@reactive.event(input.reference_data)
|
|
630
830
|
async def _populate_ids_from_reference_upload():
|
|
631
|
-
if current_page() != "plot_spectra":
|
|
632
|
-
return
|
|
633
|
-
|
|
634
831
|
files = input.reference_data()
|
|
635
832
|
if not files:
|
|
636
833
|
return
|
|
@@ -638,7 +835,6 @@ def server(input, output, session):
|
|
|
638
835
|
in_path = Path(files[0]["datapath"])
|
|
639
836
|
suffix = in_path.suffix.lower()
|
|
640
837
|
|
|
641
|
-
# Decide what CSV to read IDs from
|
|
642
838
|
try:
|
|
643
839
|
if suffix == ".csv":
|
|
644
840
|
csv_path = in_path
|
|
@@ -647,17 +843,14 @@ def server(input, output, session):
|
|
|
647
843
|
reference_status_rv.set(f"Converting {in_path.name} → CSV …")
|
|
648
844
|
await reactive.flush()
|
|
649
845
|
|
|
650
|
-
# Choose an output temp path next to the upload
|
|
651
846
|
tmp_csv_path = in_path.with_suffix(".converted.csv")
|
|
652
847
|
|
|
653
848
|
out_obj = await asyncio.to_thread(build_library, str(in_path), str(tmp_csv_path))
|
|
654
849
|
|
|
655
|
-
# out_obj may be a path (str/PathLike) OR a DataFrame. Normalize to a path.
|
|
656
850
|
if isinstance(out_obj, (str, os.PathLike, Path)):
|
|
657
851
|
csv_path = Path(out_obj)
|
|
658
852
|
elif isinstance(out_obj, pd.DataFrame):
|
|
659
|
-
|
|
660
|
-
out_obj.to_csv(tmp_csv_path, index=False)
|
|
853
|
+
out_obj.to_csv(tmp_csv_path, index=False, sep='\t')
|
|
661
854
|
csv_path = tmp_csv_path
|
|
662
855
|
else:
|
|
663
856
|
raise TypeError(f"build_library returned unsupported type: {type(out_obj)}")
|
|
@@ -667,11 +860,9 @@ def server(input, output, session):
|
|
|
667
860
|
reference_status_rv.set(f"Reading IDs from: {csv_path.name} …")
|
|
668
861
|
await reactive.flush()
|
|
669
862
|
|
|
670
|
-
# Extract IDs from the CSV’s first column
|
|
671
863
|
ids = await asyncio.to_thread(extract_first_column_ids, str(csv_path))
|
|
672
864
|
reference_ids_rv.set(ids)
|
|
673
865
|
|
|
674
|
-
# Update dropdowns
|
|
675
866
|
ui.update_selectize("spectrum_ID2", choices=ids, selected=(ids[0] if ids else None))
|
|
676
867
|
|
|
677
868
|
reference_status_rv.set(
|
|
@@ -685,65 +876,47 @@ def server(input, output, session):
|
|
|
685
876
|
raise
|
|
686
877
|
|
|
687
878
|
|
|
688
|
-
|
|
689
879
|
@render.download(filename=lambda: f"plot.png")
|
|
690
880
|
def run_btn_plot_spectra():
|
|
691
881
|
spectrum_ID1 = input.spectrum_ID1() or None
|
|
692
882
|
spectrum_ID2 = input.spectrum_ID2() or None
|
|
693
883
|
|
|
884
|
+
weights = [float(weight.strip()) for weight in input.weights().split(",") if weight.strip()]
|
|
885
|
+
weights = {'Cosine':weights[0], 'Shannon':weights[1], 'Renyi':weights[2], 'Tsallis':weights[3]}
|
|
886
|
+
|
|
694
887
|
if input.chromatography_platform() == "HRMS":
|
|
695
|
-
fig = generate_plots_on_HRMS_data(query_data=input.query_data()[0]['datapath'], reference_data=input.reference_data()[0]['datapath'], spectrum_ID1=spectrum_ID1, spectrum_ID2=spectrum_ID2, similarity_measure=input.similarity_measure(), spectrum_preprocessing_order=input.spectrum_preprocessing_order(), high_quality_reference_library=input.high_quality_reference_library(), mz_min=input.mz_min(), mz_max=input.mz_max(), int_min=input.int_min(), int_max=input.int_max(), window_size_centroiding=input.window_size_centroiding(), window_size_matching=input.window_size_matching(), noise_threshold=input.noise_threshold(), wf_mz=input.wf_mz(), wf_intensity=input.wf_int(), LET_threshold=input.LET_threshold(), entropy_dimension=input.entropy_dimension(), y_axis_transformation=input.y_axis_transformation(), return_plot=True)
|
|
696
|
-
|
|
888
|
+
fig = generate_plots_on_HRMS_data(query_data=input.query_data()[0]['datapath'], reference_data=input.reference_data()[0]['datapath'], spectrum_ID1=spectrum_ID1, spectrum_ID2=spectrum_ID2, similarity_measure=input.similarity_measure(), weights=weights, spectrum_preprocessing_order=input.spectrum_preprocessing_order(), high_quality_reference_library=input.high_quality_reference_library(), mz_min=input.mz_min(), mz_max=input.mz_max(), int_min=input.int_min(), int_max=input.int_max(), window_size_centroiding=input.window_size_centroiding(), window_size_matching=input.window_size_matching(), noise_threshold=input.noise_threshold(), wf_mz=input.wf_mz(), wf_intensity=input.wf_int(), LET_threshold=input.LET_threshold(), entropy_dimension=input.entropy_dimension(), y_axis_transformation=input.y_axis_transformation(), return_plot=True)
|
|
889
|
+
plt.show()
|
|
697
890
|
elif input.chromatography_platform() == "NRMS":
|
|
698
891
|
fig = generate_plots_on_NRMS_data(query_data=input.query_data()[0]['datapath'], reference_data=input.reference_data()[0]['datapath'], spectrum_ID1=spectrum_ID1, spectrum_ID2=spectrum_ID2, similarity_measure=input.similarity_measure(), spectrum_preprocessing_order=input.spectrum_preprocessing_order(), high_quality_reference_library=input.high_quality_reference_library(), mz_min=input.mz_min(), mz_max=input.mz_max(), int_min=input.int_min(), int_max=input.int_max(), noise_threshold=input.noise_threshold(), wf_mz=input.wf_mz(), wf_intensity=input.wf_int(), LET_threshold=input.LET_threshold(), entropy_dimension=input.entropy_dimension(), y_axis_transformation=input.y_axis_transformation(), return_plot=True)
|
|
892
|
+
plt.show()
|
|
699
893
|
with io.BytesIO() as buf:
|
|
700
894
|
fig.savefig(buf, format="png", dpi=150, bbox_inches="tight")
|
|
895
|
+
plt.close()
|
|
701
896
|
yield buf.getvalue()
|
|
702
897
|
|
|
703
898
|
|
|
704
|
-
@render.text
|
|
705
|
-
def status_output():
|
|
706
|
-
return run_status_plot_spectra.get()
|
|
707
|
-
return run_status_spec_lib_matching.get()
|
|
708
899
|
|
|
709
|
-
|
|
710
|
-
class ReactiveWriter(io.TextIOBase):
|
|
711
|
-
def __init__(self, rv: reactive.Value, loop: asyncio.AbstractEventLoop):
|
|
712
|
-
self.rv = rv
|
|
713
|
-
self.loop = loop
|
|
714
|
-
|
|
715
|
-
def write(self, s: str):
|
|
716
|
-
if not s:
|
|
717
|
-
return 0
|
|
718
|
-
def _apply():
|
|
719
|
-
self.rv.set(self.rv.get() + s)
|
|
720
|
-
self.loop.create_task(reactive.flush())
|
|
721
|
-
|
|
722
|
-
self.loop.call_soon_threadsafe(_apply)
|
|
723
|
-
return len(s)
|
|
724
|
-
|
|
725
|
-
def flush(self):
|
|
726
|
-
pass
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
@render.download(filename="identification_output.csv")
|
|
900
|
+
@render.download(filename="identification_output.txt")
|
|
730
901
|
async def run_btn_spec_lib_matching():
|
|
731
|
-
|
|
732
|
-
match_log_rv.set("Starting identification...\n")
|
|
902
|
+
match_log_rv.set("Running identification...\n")
|
|
733
903
|
await reactive.flush()
|
|
734
904
|
|
|
735
|
-
# 2) normalize inputs (same as before)
|
|
736
905
|
hq = input.high_quality_reference_library()
|
|
737
906
|
if isinstance(hq, str):
|
|
738
907
|
hq = hq.lower() == "true"
|
|
739
908
|
elif isinstance(hq, (int, float)):
|
|
740
909
|
hq = bool(hq)
|
|
741
910
|
|
|
911
|
+
weights = [float(weight.strip()) for weight in input.weights().split(",") if weight.strip()]
|
|
912
|
+
weights = {'Cosine':weights[0], 'Shannon':weights[1], 'Renyi':weights[2], 'Tsallis':weights[3]}
|
|
913
|
+
|
|
742
914
|
common_kwargs = dict(
|
|
743
915
|
query_data=input.query_data()[0]["datapath"],
|
|
744
916
|
reference_data=input.reference_data()[0]["datapath"],
|
|
745
917
|
likely_reference_ids=None,
|
|
746
918
|
similarity_measure=input.similarity_measure(),
|
|
919
|
+
weights=weights,
|
|
747
920
|
spectrum_preprocessing_order=input.spectrum_preprocessing_order(),
|
|
748
921
|
high_quality_reference_library=hq,
|
|
749
922
|
mz_min=input.mz_min(), mz_max=input.mz_max(),
|
|
@@ -752,16 +925,15 @@ def server(input, output, session):
|
|
|
752
925
|
wf_mz=input.wf_mz(), wf_intensity=input.wf_int(),
|
|
753
926
|
LET_threshold=input.LET_threshold(), entropy_dimension=input.entropy_dimension(),
|
|
754
927
|
n_top_matches_to_save=input.n_top_matches_to_save(),
|
|
755
|
-
print_id_results=True,
|
|
756
|
-
output_identification=str(Path.cwd() / "identification_output.
|
|
757
|
-
output_similarity_scores=str(Path.cwd() / "similarity_scores.
|
|
928
|
+
print_id_results=True,
|
|
929
|
+
output_identification=str(Path.cwd() / "identification_output.txt"),
|
|
930
|
+
output_similarity_scores=str(Path.cwd() / "similarity_scores.txt"),
|
|
758
931
|
return_ID_output=True,
|
|
759
932
|
)
|
|
760
933
|
|
|
761
934
|
loop = asyncio.get_running_loop()
|
|
762
|
-
rw = ReactiveWriter(
|
|
935
|
+
rw = ReactiveWriter(loop)
|
|
763
936
|
|
|
764
|
-
# 3) run the heavy function in a thread so the event loop can repaint
|
|
765
937
|
try:
|
|
766
938
|
with redirect_stdout(rw), redirect_stderr(rw):
|
|
767
939
|
if input.chromatography_platform() == "HRMS":
|
|
@@ -772,9 +944,7 @@ def server(input, output, session):
|
|
|
772
944
|
**common_kwargs
|
|
773
945
|
)
|
|
774
946
|
else:
|
|
775
|
-
df_out = await asyncio.to_thread(
|
|
776
|
-
run_spec_lib_matching_on_NRMS_data, **common_kwargs
|
|
777
|
-
)
|
|
947
|
+
df_out = await asyncio.to_thread(run_spec_lib_matching_on_NRMS_data, **common_kwargs)
|
|
778
948
|
match_log_rv.set(match_log_rv.get() + "\n✅ Identification finished.\n")
|
|
779
949
|
await reactive.flush()
|
|
780
950
|
except Exception as e:
|
|
@@ -782,8 +952,164 @@ def server(input, output, session):
|
|
|
782
952
|
await reactive.flush()
|
|
783
953
|
raise
|
|
784
954
|
|
|
785
|
-
|
|
786
|
-
|
|
955
|
+
yield df_out.to_csv(index=True, sep='\t')
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
@render.download(filename="plot.png")
|
|
960
|
+
def run_btn_plot_spectra_within_spec_lib_matching():
|
|
961
|
+
req(input.query_data(), input.reference_data())
|
|
962
|
+
|
|
963
|
+
spectrum_ID1 = input.spectrum_ID1() or None
|
|
964
|
+
spectrum_ID2 = input.spectrum_ID2() or None
|
|
965
|
+
|
|
966
|
+
hq = input.high_quality_reference_library()
|
|
967
|
+
if isinstance(hq, str):
|
|
968
|
+
hq = hq.lower() == "true"
|
|
969
|
+
elif isinstance(hq, (int, float)):
|
|
970
|
+
hq = bool(hq)
|
|
971
|
+
|
|
972
|
+
weights = [float(weight.strip()) for weight in input.weights().split(",") if weight.strip()]
|
|
973
|
+
weights = {'Cosine':weights[0], 'Shannon':weights[1], 'Renyi':weights[2], 'Tsallis':weights[3]}
|
|
974
|
+
|
|
975
|
+
common = dict(
|
|
976
|
+
query_data=input.query_data()[0]['datapath'],
|
|
977
|
+
reference_data=input.reference_data()[0]['datapath'],
|
|
978
|
+
spectrum_ID1=spectrum_ID1,
|
|
979
|
+
spectrum_ID2=spectrum_ID2,
|
|
980
|
+
similarity_measure=input.similarity_measure(),
|
|
981
|
+
weights=weights,
|
|
982
|
+
spectrum_preprocessing_order=input.spectrum_preprocessing_order(),
|
|
983
|
+
high_quality_reference_library=hq,
|
|
984
|
+
mz_min=input.mz_min(), mz_max=input.mz_max(),
|
|
985
|
+
int_min=input.int_min(), int_max=input.int_max(),
|
|
986
|
+
noise_threshold=input.noise_threshold(),
|
|
987
|
+
wf_mz=input.wf_mz(), wf_intensity=input.wf_int(),
|
|
988
|
+
LET_threshold=input.LET_threshold(), entropy_dimension=input.entropy_dimension(),
|
|
989
|
+
y_axis_transformation="normalized",
|
|
990
|
+
return_plot=True
|
|
991
|
+
)
|
|
992
|
+
|
|
993
|
+
if input.chromatography_platform() == "HRMS":
|
|
994
|
+
fig = generate_plots_on_HRMS_data(
|
|
995
|
+
window_size_centroiding=input.window_size_centroiding(),
|
|
996
|
+
window_size_matching=input.window_size_matching(),
|
|
997
|
+
**common
|
|
998
|
+
)
|
|
999
|
+
plt.show()
|
|
1000
|
+
else:
|
|
1001
|
+
fig = generate_plots_on_NRMS_data(**common)
|
|
1002
|
+
plt.show()
|
|
1003
|
+
|
|
1004
|
+
with io.BytesIO() as buf:
|
|
1005
|
+
fig.savefig(buf, format="png", dpi=150, bbox_inches="tight")
|
|
1006
|
+
plt.close()
|
|
1007
|
+
yield buf.getvalue()
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
@render.download(filename="parameter_tuning_output.txt")
|
|
1011
|
+
async def run_btn_parameter_tuning():
|
|
1012
|
+
is_any_job_running.set(True)
|
|
1013
|
+
is_tuning_running.set(True)
|
|
1014
|
+
match_log_rv.set("Running grid search of all parameters specified...\n")
|
|
1015
|
+
await reactive.flush()
|
|
1016
|
+
|
|
1017
|
+
similarity_measure_tmp = list(input.similarity_measure())
|
|
1018
|
+
high_quality_reference_library_tmp = [x.strip().lower() == "true" for x in input.high_quality_reference_library().strip().strip("[]").split(",") if x.strip()]
|
|
1019
|
+
spectrum_preprocessing_order_tmp = strip_text(input.spectrum_preprocessing_order())
|
|
1020
|
+
mz_min_tmp = strip_numeric(input.mz_min())
|
|
1021
|
+
mz_max_tmp = strip_numeric(input.mz_max())
|
|
1022
|
+
int_min_tmp = strip_numeric(input.int_min())
|
|
1023
|
+
int_max_tmp = strip_numeric(input.int_max())
|
|
1024
|
+
noise_threshold_tmp = strip_numeric(input.noise_threshold())
|
|
1025
|
+
wf_mz_tmp = strip_numeric(input.wf_mz())
|
|
1026
|
+
wf_int_tmp = strip_numeric(input.wf_int())
|
|
1027
|
+
LET_threshold_tmp = strip_numeric(input.LET_threshold())
|
|
1028
|
+
entropy_dimension_tmp = strip_numeric(input.entropy_dimension())
|
|
1029
|
+
weights_tmp = strip_weights(input.weights())
|
|
1030
|
+
|
|
1031
|
+
common_kwargs = dict(
|
|
1032
|
+
query_data=input.query_data()[0]["datapath"],
|
|
1033
|
+
reference_data=input.reference_data()[0]["datapath"],
|
|
1034
|
+
output_path=str(Path.cwd() / "parameter_tuning_output.txt"),
|
|
1035
|
+
return_output=True,
|
|
1036
|
+
)
|
|
1037
|
+
|
|
1038
|
+
loop = asyncio.get_running_loop()
|
|
1039
|
+
rw = ReactiveWriter(loop)
|
|
1040
|
+
|
|
1041
|
+
try:
|
|
1042
|
+
if input.chromatography_platform() == "HRMS":
|
|
1043
|
+
window_size_centroiding_tmp = strip_numeric(input.window_size_centroiding())
|
|
1044
|
+
window_size_matching_tmp = strip_numeric(input.window_size_matching())
|
|
1045
|
+
grid = {
|
|
1046
|
+
'similarity_measure': similarity_measure_tmp,
|
|
1047
|
+
'weight': weights_tmp,
|
|
1048
|
+
'spectrum_preprocessing_order': spectrum_preprocessing_order_tmp,
|
|
1049
|
+
'mz_min': mz_min_tmp,
|
|
1050
|
+
'mz_max': mz_max_tmp,
|
|
1051
|
+
'int_min': int_min_tmp,
|
|
1052
|
+
'int_max': int_max_tmp,
|
|
1053
|
+
'noise_threshold': noise_threshold_tmp,
|
|
1054
|
+
'wf_mz': wf_mz_tmp,
|
|
1055
|
+
'wf_int': wf_int_tmp,
|
|
1056
|
+
'LET_threshold': LET_threshold_tmp,
|
|
1057
|
+
'entropy_dimension': entropy_dimension_tmp,
|
|
1058
|
+
'high_quality_reference_library': high_quality_reference_library_tmp,
|
|
1059
|
+
'window_size_centroiding': window_size_centroiding_tmp,
|
|
1060
|
+
'window_size_matching': window_size_matching_tmp,
|
|
1061
|
+
}
|
|
1062
|
+
df_out = await asyncio.to_thread(_run_with_redirects, tune_params_on_HRMS_data_shiny, rw, **common_kwargs, grid=grid)
|
|
1063
|
+
else:
|
|
1064
|
+
grid = {
|
|
1065
|
+
'similarity_measure': similarity_measure_tmp,
|
|
1066
|
+
'weight': weights_tmp,
|
|
1067
|
+
'spectrum_preprocessing_order': spectrum_preprocessing_order_tmp,
|
|
1068
|
+
'mz_min': mz_min_tmp,
|
|
1069
|
+
'mz_max': mz_max_tmp,
|
|
1070
|
+
'int_min': int_min_tmp,
|
|
1071
|
+
'int_max': int_max_tmp,
|
|
1072
|
+
'noise_threshold': noise_threshold_tmp,
|
|
1073
|
+
'wf_mz': wf_mz_tmp,
|
|
1074
|
+
'wf_int': wf_int_tmp,
|
|
1075
|
+
'LET_threshold': LET_threshold_tmp,
|
|
1076
|
+
'entropy_dimension': entropy_dimension_tmp,
|
|
1077
|
+
'high_quality_reference_library': high_quality_reference_library_tmp,
|
|
1078
|
+
}
|
|
1079
|
+
df_out = await asyncio.to_thread(_run_with_redirects, tune_params_on_NRMS_data_shiny, rw, **common_kwargs, grid=grid)
|
|
1080
|
+
|
|
1081
|
+
match_log_rv.set(match_log_rv.get() + "\n✅ Parameter tuning finished.\n")
|
|
1082
|
+
except Exception as e:
|
|
1083
|
+
match_log_rv.set(match_log_rv.get() + f"\n❌ Error: {e}\n")
|
|
1084
|
+
raise
|
|
1085
|
+
finally:
|
|
1086
|
+
is_tuning_running.set(False)
|
|
1087
|
+
is_any_job_running.set(False)
|
|
1088
|
+
await reactive.flush()
|
|
1089
|
+
|
|
1090
|
+
yield df_out.to_csv(index=False).encode("utf-8", sep='\t')
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
@reactive.effect
|
|
1097
|
+
async def _pump_reactive_writer_logs():
|
|
1098
|
+
if not is_tuning_running.get():
|
|
1099
|
+
return
|
|
1100
|
+
|
|
1101
|
+
reactive.invalidate_later(0.1)
|
|
1102
|
+
msgs = _drain_queue_nowait(_LOG_QUEUE)
|
|
1103
|
+
if msgs:
|
|
1104
|
+
match_log_rv.set(match_log_rv.get() + "".join(msgs))
|
|
1105
|
+
await reactive.flush()
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
@render.text
|
|
1109
|
+
def status_output():
|
|
1110
|
+
return run_status_plot_spectra.get()
|
|
1111
|
+
return run_status_spec_lib_matching.get()
|
|
1112
|
+
return run_status_parameter_tuning.get()
|
|
787
1113
|
|
|
788
1114
|
|
|
789
1115
|
app = App(app_ui, server)
|