@datagrok/bio 2.17.5 → 2.18.0

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.
Binary file
@@ -0,0 +1,6 @@
1
+ model,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb
2
+ MAINTAINED,21.0,6,160,110,3.90,2.620,16.46,0,1,4,4
3
+ WORSENED,21.0,6,160,110,3.90,2.875,17.02,0,1,4,4
4
+ MAINTAINED,22.8,4,108,93,3.85,2.320,18.61,1,1,4,1
5
+ IMPROVED,21.4,6,258,110,3.08,3.215,19.44,1,0,3,1
6
+ WORSENED,18.7,8,360,175,3.15,3.440,17.02,0,0,3,2
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "Leonid Stolbov",
6
6
  "email": "lstolbov@datagrok.ai"
7
7
  },
8
- "version": "2.17.5",
8
+ "version": "2.18.0",
9
9
  "description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
10
10
  "repository": {
11
11
  "type": "git",
@@ -46,16 +46,16 @@
46
46
  "@biowasm/aioli": "^3.1.0",
47
47
  "@datagrok-libraries/bio": "^5.46.0",
48
48
  "@datagrok-libraries/chem-meta": "^1.2.7",
49
- "@datagrok-libraries/math": "^1.2.3",
50
- "@datagrok-libraries/ml": "^6.7.5",
49
+ "@datagrok-libraries/math": "^1.2.4",
50
+ "@datagrok-libraries/ml": "^6.7.6",
51
51
  "@datagrok-libraries/tutorials": "^1.4.3",
52
- "@datagrok-libraries/utils": "^4.3.7",
52
+ "@datagrok-libraries/utils": "^4.4.0",
53
+ "datagrok-api": "^1.23.0",
53
54
  "@webgpu/types": "^0.1.40",
54
55
  "ajv": "^8.12.0",
55
56
  "ajv-errors": "^3.0.0",
56
57
  "cash-dom": "^8.0.0",
57
58
  "css-loader": "^6.7.3",
58
- "datagrok-api": "^1.21.1",
59
59
  "dayjs": "^1.11.4",
60
60
  "fastest-levenshtein": "^1.0.16",
61
61
  "openchemlib": "^7.2.3",
@@ -67,9 +67,9 @@
67
67
  "devDependencies": {
68
68
  "@datagrok-libraries/helm-web-editor": "^1.1.13",
69
69
  "@datagrok-libraries/js-draw-lite": "^0.0.10",
70
- "@datagrok/chem": "^1.12.3",
70
+ "@datagrok/chem": "^1.13.0",
71
71
  "@datagrok/dendrogram": "^1.2.33",
72
- "@datagrok/helm": "^2.5.10",
72
+ "@datagrok/helm": "^2.7.0",
73
73
  "@types/node": "^17.0.24",
74
74
  "@types/wu": "^2.1.44",
75
75
  "@typescript-eslint/eslint-plugin": "^8.8.1",
package/src/package.ts CHANGED
@@ -964,19 +964,36 @@ export async function manageMonomersView() {
964
964
  await monomerManager.getViewRoot();
965
965
  }
966
966
 
967
- //name: Manage Monomers
967
+ //name: Monomers
968
968
  //tags: app
969
- //meta.browsePath: Peptides | Monomers
970
- export async function manageMonomersApp() {
971
- const monomerManager = await MonomerManager.getInstance();
972
- await monomerManager.getViewRoot();
973
- }
969
+ //meta.browsePath: Peptides
970
+ //meta.icon: files/icons/monomers.png
971
+ //output: view v
972
+ export async function manageLibrariesApp(): Promise<DG.View> {
973
+ return await showManageLibrariesView(false);
974
+ }
975
+
976
+ //name: Monomer Manager Tree Browser
977
+ //input: dynamic treeNode
978
+ //input: view browseView
979
+ export async function manageLibrariesAppTreeBrowser(treeNode: DG.TreeViewGroup, browseView: DG.BrowseView) {
980
+ const libraries = (await (await MonomerLibManager.getInstance()).getFileManager()).getValidLibraryPaths();
981
+ libraries.forEach((libName) => {
982
+ const libNode = treeNode.item(libName);
983
+ // eslint-disable-next-line rxjs/no-ignored-subscription, rxjs/no-async-subscribe
984
+ libNode.onSelected.subscribe(async () => {
985
+ const monomerManager = await MonomerManager.getNewInstance();
986
+ browseView.preview = await monomerManager.getViewRoot(libName, false);
987
+ });
974
988
 
975
- //name: Manage Libraries
976
- //tags: app
977
- //meta.browsePath: Peptides | Monomers
978
- export async function manageLibrariesApp(): Promise<void> {
979
- await showManageLibrariesView();
989
+ libNode.root.addEventListener('dblclick', async (e) => {
990
+ e.preventDefault();
991
+ e.stopImmediatePropagation();
992
+ const monomerManager = await MonomerManager.getInstance();
993
+ await monomerManager.getViewRoot(libName, true);
994
+ monomerManager.resetCurrentRowFollowing();
995
+ });
996
+ });
980
997
  }
981
998
 
982
999
  //name: saveAsFasta
@@ -1,3 +1,5 @@
1
+ /* eslint-disable max-lines */
2
+ /* eslint-disable max-lines-per-function */
1
3
  import * as grok from 'datagrok-api/grok';
2
4
  import * as ui from 'datagrok-api/ui';
3
5
  import * as DG from 'datagrok-api/dg';
@@ -271,6 +273,7 @@ MWRSWY-CKHPMWRSWY-CKHP`;
271
273
  testSpgi = 'testSpgi',
272
274
  testSpgi100 = 'testSpgi100',
273
275
  testUrl = 'testUrl',
276
+ fastaNegativeWords = 'fasta_negative_words'
274
277
  }
275
278
 
276
279
  const samples: { [key: string]: string } = {
@@ -295,6 +298,7 @@ MWRSWY-CKHPMWRSWY-CKHP`;
295
298
  [Samples.testSpgi100]: 'System:AppData/Bio/tests/testSpgi100.csv',
296
299
  [Samples.testSpgi]: 'System:AppData/Bio/tests/SPGI-derived.csv',
297
300
  [Samples.testUrl]: 'System:AppData/Bio/tests/testUrl.csv',
301
+ [Samples.fastaNegativeWords]: 'System:AppData/Bio/tests/fasta_negative_words.csv',
298
302
  };
299
303
 
300
304
  const _samplesDfs: { [key: string]: Promise<DG.DataFrame> } = {};
@@ -505,6 +509,10 @@ MWRSWY-CKHPMWRSWY-CKHP`;
505
509
  test('samplesTestUrl', async () => {
506
510
  await _testDf(readSamples(Samples.testUrl), {} /* no positive */, seqHelper);
507
511
  });
512
+
513
+ test('samplesFastaNegativeWords', async () => {
514
+ await _testDf(readSamples(Samples.fastaNegativeWords), {} /* no positive */, seqHelper);
515
+ });
508
516
  });
509
517
 
510
518
  export async function _testNegList(list: string[]): Promise<void> {
@@ -24,8 +24,8 @@ export async function showManageLibrariesDialog(): Promise<void> {
24
24
  await DialogWrapper.showDialog();
25
25
  }
26
26
 
27
- export async function showManageLibrariesView() {
28
- await LibManagerView.showView();
27
+ export async function showManageLibrariesView(addView = true) {
28
+ return await LibManagerView.showView(addView);
29
29
  }
30
30
 
31
31
  export async function getMonomerLibraryManagerLink(): Promise<DG.Widget> {
@@ -68,8 +68,9 @@ class MonomerLibraryManagerWidget {
68
68
  private async createWidget() {
69
69
  const content = await this.getWidgetContent();
70
70
  const monomerLibHelper = await getMonomerLibHelper();
71
+ // eslint-disable-next-line rxjs/no-ignored-subscription
71
72
  monomerLibHelper.eventManager.addLibraryFileRequested$.subscribe(
72
- async () => await this.promptToAddLibraryFiles()
73
+ () => this.promptToAddLibraryFiles()
73
74
  );
74
75
  return new DG.Widget(content);
75
76
  }
@@ -109,11 +110,13 @@ class LibraryControlsManager {
109
110
  private fileManager: IMonomerLibFileManager,
110
111
  private readonly userLibSettings: UserLibSettings,
111
112
  ) {
113
+ // eslint-disable-next-line rxjs/no-ignored-subscription
112
114
  this.fileManager.eventManager.updateUIControlsRequested$.subscribe(() => {
113
115
  this.updateControlsForm();
114
116
  });
115
- this.fileManager.eventManager.librarySelectionRequested$.subscribe(async ([fileName, isSelected]) => {
116
- await this.updateLibrarySelectionStatus(isSelected, fileName);
117
+ // eslint-disable-next-line rxjs/no-ignored-subscription
118
+ this.fileManager.eventManager.librarySelectionRequested$.subscribe(([fileName, isSelected]) => {
119
+ this.updateLibrarySelectionStatus(isSelected, fileName);
117
120
  });
118
121
  }
119
122
 
@@ -222,6 +225,7 @@ class DialogWrapper {
222
225
  static async showDialog(): Promise<void> {
223
226
  if (!DialogWrapper._instance) {
224
227
  DialogWrapper._instance = new DialogWrapper();
228
+ // eslint-disable-next-line rxjs/no-ignored-subscription
225
229
  DialogWrapper._instance.closeDialogSubject$.subscribe(
226
230
  () => { DialogWrapper._instance.dialog = undefined; }
227
231
  );
@@ -251,7 +255,10 @@ class DialogWrapper {
251
255
  'Upload new HELM monomer library'
252
256
  );
253
257
  dialog.add(widget);
254
- dialog.onClose.subscribe(() => this.closeDialogSubject$.next());
258
+ const sub = dialog.onClose.subscribe(() => {
259
+ this.closeDialogSubject$.next();
260
+ sub.unsubscribe();
261
+ });
255
262
  return dialog;
256
263
  }
257
264
  }
@@ -259,10 +266,11 @@ class DialogWrapper {
259
266
  class LibManagerView {
260
267
  private constructor() {};
261
268
  private static _instance: LibManagerView;
269
+ static viewName = 'Manage Monomer Libraries';
262
270
  private _view: DG.View;
263
271
  private _duplicateManager: DuplicateMonomerManager;
264
272
  private libManager: MonomerLibManager;
265
- private async getView() {
273
+ private async getView(addView = true) {
266
274
  const eventManager = MonomerLibFileEventManager.getInstance();
267
275
  const widget = (await MonomerLibraryManagerWidget.getInstance()).widget;
268
276
  const addButton = ui.bigButton('Add',
@@ -275,7 +283,10 @@ class LibManagerView {
275
283
  this._duplicateManager.root],
276
284
  {style: {width: '100%', height: '100%'}},
277
285
  true);
278
- this._view = grok.shell.newView('Manage Monomer Libraries', [v]);
286
+ this._view = DG.View.fromRoot(v);
287
+ this._view.name = LibManagerView.viewName;
288
+ if (addView)
289
+ grok.shell.addView(this._view);
279
290
 
280
291
  ui.tools.waitForElementInDom(v).then(() => {
281
292
  setTimeout(() => {
@@ -300,24 +311,32 @@ class LibManagerView {
300
311
  }
301
312
  }));
302
313
  });
314
+ return this._view;
303
315
  //grok.shell.dockManager.dock(this._duplicateManager.root, DG.DOCK_TYPE.RIGHT, null, '', 0.4);
304
316
  }
305
317
 
306
- static async showView() {
318
+ static async showView(addView = true) {
307
319
  if (!LibManagerView._instance)
308
320
  LibManagerView._instance = new LibManagerView();
309
321
  if (!LibManagerView._instance._duplicateManager)
310
322
  LibManagerView._instance._duplicateManager = await DuplicateMonomerManager.getInstance();
311
323
  if (!LibManagerView._instance.libManager)
312
324
  LibManagerView._instance.libManager = await MonomerLibManager.getInstance();
313
- if (LibManagerView._instance._view &&
325
+ if (addView && LibManagerView._instance._view &&
314
326
  Array.from(grok.shell.views).find((v) => v.id && v.id === LibManagerView._instance._view.id)) {
315
327
  grok.shell.v = LibManagerView._instance._view;
316
328
  await LibManagerView._instance._duplicateManager.refresh();
317
- return;
329
+ return LibManagerView._instance._view;
318
330
  }
319
- LibManagerView._instance.getView();
331
+ // something can conflict with browse view, so need to make sure that we close all existing views
332
+ LibManagerView.closeExistingViews();
333
+ return LibManagerView._instance.getView(addView);
320
334
  }
335
+
336
+ private static closeExistingViews() {
337
+ Array.from(grok.shell.views).filter((v) => v.name === LibManagerView.viewName).forEach((v) => v.close());
338
+ }
339
+
321
340
  async mergeSelectedLibs() {
322
341
  const libraryExistsError = 'Library with this name already exists';
323
342
  const libManager = await MonomerLibManager.getInstance();
@@ -51,9 +51,14 @@ export const MONOMER_DF_COLUMNS = {
51
51
 
52
52
 
53
53
  export class MonomerManager implements IMonomerManager {
54
- private adjustColWidths() {
54
+ private adjustTable() {
55
+ if (this.tv?.dataFrame) {
56
+ grok.data.detectSemanticTypes(this.tv.dataFrame);
57
+ this.tv.dataFrame.meta.detectSemanticTypes();
58
+ }
55
59
  setTimeout(() => {
56
60
  if (this.tv?.grid) {
61
+ this.tv!.grid.props.allowEdit = false;
57
62
  this.tv!.grid.col(MONOMER_DF_COLUMN_NAMES.NAME)!.width = 100;
58
63
  this.tv!.grid.col(MONOMER_DF_COLUMN_NAMES.SYMBOL)!.width = 70;
59
64
  }
@@ -75,7 +80,7 @@ export class MonomerManager implements IMonomerManager {
75
80
  const df = await this.getMonomersDf(this.libInput.value!);
76
81
  if (this.tv?.dataFrame) {
77
82
  this.tv.dataFrame = df;
78
- this.adjustColWidths();
83
+ this.adjustTable();
79
84
  if (scrollToRowSymbol != undefined) {
80
85
  setTimeout(() => {
81
86
  const col = df.col(MONOMER_DF_COLUMN_NAMES.SYMBOL)!;
@@ -99,6 +104,13 @@ export class MonomerManager implements IMonomerManager {
99
104
  return this.instance;
100
105
  }
101
106
 
107
+ public static async getNewInstance(): Promise<MonomerManager> {
108
+ const monManager = await MonomerLibManager.getInstance();
109
+ await monManager.awaitLoaded();
110
+ await monManager.loadLibrariesPromise;
111
+ return new MonomerManager(monManager);
112
+ }
113
+
102
114
  async createNewMonomerLib(libName: string, _monomers: Monomer[]): Promise<void> {
103
115
  this.tv?.grid && ui.setUpdateIndicator(this.tv.grid.root, true);
104
116
  try {
@@ -168,11 +180,11 @@ export class MonomerManager implements IMonomerManager {
168
180
  return this._newMonomerForm;
169
181
  }
170
182
 
171
- private async getMonomersTableView(fileName?: string): Promise<DG.TableView> {
183
+ private async getMonomersTableView(fileName?: string, addView = true): Promise<DG.TableView> {
172
184
  const df = await this.getMonomersDf(fileName);
173
- this.tv = DG.TableView.create(df, true);
174
- //const f = tv.filters();
175
- this.adjustColWidths();
185
+ this.tv = DG.TableView.create(df, addView);
186
+
187
+ this.adjustTable();
176
188
  this.tv.subs.push(
177
189
  grok.events.onContextMenu.subscribe(({args}) => {
178
190
  if (!args || !args.menu || !args.context || args.context.type !== DG.VIEWER.GRID || !args.context.tableView ||
@@ -205,21 +217,28 @@ export class MonomerManager implements IMonomerManager {
205
217
  return this.tv;
206
218
  }
207
219
 
220
+ private static closeAllMonomerManagers() {
221
+ Array.from(grok.shell.tableViews ?? []).filter((v) => v.name === MonomerManager.VIEW_NAME).forEach((v) => v.close());
222
+ }
223
+
208
224
  private findActiveManagerView() {
209
225
  if (!this.tv)
210
226
  return null;
211
227
  const tv = Array.from(grok.shell.tableViews ?? []).find((tv) => tv.id === this.tv!.id);
212
228
  if (tv)
213
229
  grok.shell.v = tv;
230
+ else
231
+ MonomerManager.closeAllMonomerManagers();
232
+
214
233
  return tv ?? null;
215
234
  }
216
235
 
217
236
  private _skipLibInputOnchange: boolean = false;
218
237
 
219
- async getViewRoot(libName?: string) {
238
+ async getViewRoot(libName?: string, addView = true) {
220
239
  const availableMonLibs = (await this.monomerLibManamger.getFileManager()).getValidLibraryPaths();
221
240
  this._newMonomerForm.molSketcher.resize();
222
- if ((this.tv = this.findActiveManagerView()) && (libName ?? this.libInput.value)) {
241
+ if (addView && (this.tv = this.findActiveManagerView()) && (libName ?? this.libInput.value)) {
223
242
  // get monomer library list
224
243
  try {
225
244
  this._skipLibInputOnchange = true;
@@ -233,12 +252,12 @@ export class MonomerManager implements IMonomerManager {
233
252
  }
234
253
  const df = await this.getMonomersDf(libName);
235
254
  this.tv.dataFrame = df;
236
- this.adjustColWidths();
255
+ this.adjustTable();
237
256
  return this.tv;
238
257
  }
239
258
 
240
259
  libName ??= availableMonLibs[0];
241
- this.tv = await this.getMonomersTableView(libName);
260
+ this.tv = await this.getMonomersTableView(libName, addView);
242
261
 
243
262
  // remove project save button and download from ribbons
244
263
  let ribbons = this.tv.getRibbonPanels();
@@ -300,7 +319,7 @@ export class MonomerManager implements IMonomerManager {
300
319
  if (this._skipLibInputOnchange) return;
301
320
  const df = await this.getMonomersDf(this.libInput.value!);
302
321
  this.tv!.dataFrame = df;
303
- this.adjustColWidths();
322
+ this.adjustTable();
304
323
  } catch (e) {
305
324
  console.error(e);
306
325
  }
@@ -308,7 +327,9 @@ export class MonomerManager implements IMonomerManager {
308
327
  this.libInput.addOptions(ui.icons.add(() => { this.createNewLibDialog(); }, 'Create new monomer library...'));
309
328
  const monForm = this._newMonomerForm.form;
310
329
  monForm.prepend(this.libInput.root);
311
- this.tv.dockManager.dock(monForm, DG.DOCK_TYPE.LEFT, null, undefined, 0.4);
330
+ ui.tools.waitForElementInDom(this.tv.root).then(() => {
331
+ this.tv!.dockManager.dock(monForm, DG.DOCK_TYPE.LEFT, null, undefined, 0.4);
332
+ });
312
333
  return this.tv;
313
334
  }
314
335
 
@@ -403,6 +424,10 @@ export class MonomerManager implements IMonomerManager {
403
424
  this.tv?.grid && ui.setUpdateIndicator(this.tv.grid.root, false);
404
425
  }
405
426
  }
427
+
428
+ public resetCurrentRowFollowing() {
429
+ this._newMonomerForm.molChanged = false;
430
+ }
406
431
  }
407
432
 
408
433
  // some monomers might be in form of cap groups in place of r-groups (with supplied rgroups info), this function will convert them to r-groups
@@ -522,6 +547,7 @@ class MonomerForm implements INewMonomerForm {
522
547
  rgroupsGridRoot: HTMLElement;
523
548
  private _molChanged: boolean = false;
524
549
  get molChanged() { return this._molChanged; }
550
+ set molChanged(v: boolean) { this._molChanged = v; }
525
551
  private saveValidationResult?: string | null = null;
526
552
  private triggerMolChange: boolean = true; // makes sure that change is not triggered by copying the molecule from grid
527
553
  inputsTabControl: DG.TabControl;