@datagrok/bio 2.13.3 → 2.13.6

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.
Files changed (105) hide show
  1. package/.eslintrc.json +1 -1
  2. package/CHANGELOG.md +23 -0
  3. package/detectors.js +52 -38
  4. package/dist/111.js +2 -0
  5. package/dist/111.js.map +1 -0
  6. package/dist/234.js +2 -0
  7. package/dist/234.js.map +1 -0
  8. package/dist/242.js +2 -0
  9. package/dist/242.js.map +1 -0
  10. package/dist/{286.js → 248.js} +1 -1
  11. package/dist/248.js.map +1 -0
  12. package/dist/284.js +3 -0
  13. package/dist/284.js.map +1 -0
  14. package/dist/317.js +2 -0
  15. package/dist/317.js.map +1 -0
  16. package/dist/589.js +2 -0
  17. package/dist/589.js.map +1 -0
  18. package/dist/603.js +2 -0
  19. package/dist/603.js.map +1 -0
  20. package/dist/682.js +2 -0
  21. package/dist/682.js.map +1 -0
  22. package/dist/705.js +2 -0
  23. package/dist/705.js.map +1 -0
  24. package/dist/{590.js → 731.js} +2 -2
  25. package/dist/731.js.map +1 -0
  26. package/dist/778.js +2 -0
  27. package/dist/778.js.map +1 -0
  28. package/dist/793.js +2 -0
  29. package/dist/793.js.map +1 -0
  30. package/dist/950.js +2 -0
  31. package/dist/950.js.map +1 -0
  32. package/dist/package-test.js +6 -7
  33. package/dist/package-test.js.map +1 -1
  34. package/dist/package.js +6 -7
  35. package/dist/package.js.map +1 -1
  36. package/files/cache_config.json +7 -0
  37. package/package.json +12 -11
  38. package/src/analysis/sequence-activity-cliffs.ts +1 -1
  39. package/src/function-edtiors/split-to-monomers-editor.ts +6 -7
  40. package/src/package-types.ts +14 -7
  41. package/src/package.ts +6 -6
  42. package/src/substructure-search/substructure-search.ts +9 -10
  43. package/src/tests/WebLogo-positions-test.ts +6 -6
  44. package/src/tests/activity-cliffs-tests.ts +5 -2
  45. package/src/tests/bio-tests.ts +6 -6
  46. package/src/tests/checkInputColumn-tests.ts +3 -3
  47. package/src/tests/converters-test.ts +1 -1
  48. package/src/tests/detectors-tests.ts +25 -13
  49. package/src/tests/fasta-export-tests.ts +2 -2
  50. package/src/tests/mm-distance-tests.ts +1 -1
  51. package/src/tests/msa-tests.ts +2 -2
  52. package/src/tests/renderers-test.ts +5 -5
  53. package/src/tests/scoring.ts +26 -5
  54. package/src/tests/seq-handler-get-region.ts +4 -4
  55. package/src/tests/sequence-space-test.ts +1 -1
  56. package/src/tests/substructure-filters-tests.ts +4 -1
  57. package/src/tests/to-atomic-level-tests.ts +1 -1
  58. package/src/utils/cell-renderer.ts +4 -4
  59. package/src/utils/context-menu.ts +1 -1
  60. package/src/utils/convert.ts +7 -4
  61. package/src/utils/get-region-func-editor.ts +11 -16
  62. package/src/utils/get-region.ts +5 -5
  63. package/src/utils/macromolecule-column-widget.ts +1 -1
  64. package/src/utils/monomer-lib/lib-manager.ts +20 -8
  65. package/src/utils/monomer-lib/library-file-manager/file-manager.ts +28 -24
  66. package/src/utils/monomer-lib/library-file-manager/file-validator.ts +2 -1
  67. package/src/utils/monomer-lib/library-file-manager/ui.ts +3 -6
  68. package/src/utils/multiple-sequence-alignment-ui.ts +10 -11
  69. package/src/utils/multiple-sequence-alignment.ts +2 -2
  70. package/src/utils/pepsea.ts +1 -1
  71. package/src/utils/save-as-fasta.ts +5 -5
  72. package/src/viewers/vd-regions-viewer.ts +2 -2
  73. package/src/widgets/bio-substructure-filter.ts +7 -7
  74. package/src/widgets/package-settings-editor-widget.ts +6 -6
  75. package/src/widgets/representations.ts +1 -1
  76. package/tsconfig.json +4 -4
  77. package/dist/23.js +0 -2
  78. package/dist/23.js.map +0 -1
  79. package/dist/231.js +0 -2
  80. package/dist/231.js.map +0 -1
  81. package/dist/282.js +0 -2
  82. package/dist/282.js.map +0 -1
  83. package/dist/286.js.map +0 -1
  84. package/dist/356.js +0 -2
  85. package/dist/356.js.map +0 -1
  86. package/dist/36.js +0 -2
  87. package/dist/36.js.map +0 -1
  88. package/dist/40.js +0 -2
  89. package/dist/40.js.map +0 -1
  90. package/dist/413.js +0 -2
  91. package/dist/413.js.map +0 -1
  92. package/dist/42.js +0 -2
  93. package/dist/42.js.map +0 -1
  94. package/dist/427.js +0 -2
  95. package/dist/427.js.map +0 -1
  96. package/dist/545.js +0 -3
  97. package/dist/545.js.map +0 -1
  98. package/dist/590.js.map +0 -1
  99. package/dist/65.js +0 -2
  100. package/dist/65.js.map +0 -1
  101. package/dist/796.js +0 -2
  102. package/dist/796.js.map +0 -1
  103. package/dist/package-test.js.LICENSE.txt +0 -1
  104. package/dist/package.js.LICENSE.txt +0 -1
  105. /package/dist/{545.js.LICENSE.txt → 284.js.LICENSE.txt} +0 -0
@@ -2,7 +2,9 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
- import {category, test, expectFloat, before, after} from '@datagrok-libraries/utils/src/test';
5
+ import wu from 'wu';
6
+
7
+ import {category, test, expectFloat, before, after, expect} from '@datagrok-libraries/utils/src/test';
6
8
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
7
9
  import {IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
8
10
 
@@ -13,6 +15,7 @@ import {
13
15
  import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
14
16
 
15
17
  category('Scoring', () => {
18
+ /* eslint-disable max-len */
16
19
  const sequence = 'sequence';
17
20
  const expectedSimilarity = 'expected_similarity';
18
21
  const expectedIdentity = 'expected_identity';
@@ -22,11 +25,13 @@ PEPTIDE1{Aca.Orn.gGlu.Pqa.D-His_1Bn.dH.hHis.4Abz.D-Tic.D-Dap.Y.Iva.meS.F.P.F.D-1
22
25
  PEPTIDE1{Iva.Gly_allyl.gGlu.Pqa.D-Dip.dH.hHis.4Abz.D-aHyp.D-Dap.Y.Iva.I.Tyr_26diMe.P.Asu.meC}$$$$,0.68,0.53
23
26
  PEPTIDE1{[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal]}$$$$V2.0,0.34,0.0`
24
27
  );
25
- /* eslint-enable max-len */
26
28
  const seqCol: DG.Column<string> = table.getCol(sequence);
27
- seqCol.setTag(DG.TAGS.UNITS, NOTATION.HELM);
29
+ seqCol.meta.units = NOTATION.HELM;
28
30
  seqCol.semType = DG.SEMTYPE.MACROMOLECULE;
29
31
  const reference = seqCol.get(0)!;
32
+ const shortReference = 'PEPTIDE1{Iva.Gly_allyl.gGlu.Pqa.D-Dip.dH.hHis.4Abz.D-aHyp.D-Dap.Y.Iva}$$$$';
33
+ const longReference = 'PEPTIDE1{Iva.Gly_allyl.gGlu.Pqa.D-Dip.dH.hHis.4Abz.D-aHyp.D-Dap.Y.Iva.I.Tyr_26diMe.P.Asu.meC.I.Tyr_26diMe.P.Asu.meC}$$$$';
34
+ /* eslint-enable max-len */
30
35
 
31
36
  let monomerLibHelper: IMonomerLibHelper;
32
37
  /** Backup actual user's monomer libraries settings */
@@ -49,15 +54,31 @@ PEPTIDE1{[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[1Nal].[
49
54
  test('Identity', async () => {
50
55
  const scoresCol = await sequenceIdentityScoring(table, seqCol, reference);
51
56
  for (let i = 0; i < scoresCol.length; i++) {
52
- expectFloat(scoresCol.get(i)!, table.get(expectedIdentity, i), 0.01,
57
+ const resScore = scoresCol.get(i)!;
58
+ const tgtScore = table.get(expectedIdentity, i);
59
+ expectFloat(resScore, tgtScore, 0.01,
53
60
  `Wrong identity score for sequence at position ${i}`);
54
61
  }
55
62
  });
56
63
 
64
+ test('Identity-shortReference', async () => {
65
+ const scoresCol = await sequenceIdentityScoring(table, seqCol, shortReference);
66
+ expect(wu.count(0).take(scoresCol.length).map((rowI) => scoresCol.get(rowI))
67
+ .every((v) => v != null && !isNaN(v)), true);
68
+ });
69
+
70
+ test('Identity-longReference', async () => {
71
+ const scoresCol = await sequenceIdentityScoring(table, seqCol, longReference);
72
+ expect(wu.count(0).take(scoresCol.length).map((rowI) => scoresCol.get(rowI))
73
+ .every((v) => v != null && !isNaN(v)), true);
74
+ });
75
+
57
76
  test('Similarity', async () => {
58
77
  const scoresCol = await sequenceSimilarityScoring(table, seqCol, reference);
59
78
  for (let i = 0; i < scoresCol.length; i++) {
60
- expectFloat(scoresCol.get(i)!, table.get(expectedSimilarity, i), 0.01,
79
+ const resScore = scoresCol.get(i)!;
80
+ const tgtScore = table.get(expectedSimilarity, i);
81
+ expectFloat(resScore, tgtScore, 0.01,
61
82
  `Wrong similarity score for sequence at position ${i}`);
62
83
  }
63
84
  });
@@ -82,8 +82,8 @@ PEPTIDE1{[Cys_SEt].T.*.*}$$$$`,
82
82
  const tgtDf = DG.DataFrame.fromCsv(testData.tgtCsv);
83
83
  const tgtSeqCol = tgtDf.getCol('seq');
84
84
 
85
- expect(srcSeqCol.getTag(DG.TAGS.UNITS), testData.units);
86
- expect(resSeqCol.getTag(DG.TAGS.UNITS), testData.units);
85
+ expect(srcSeqCol.meta.units, testData.units);
86
+ expect(resSeqCol.meta.units, testData.units);
87
87
  expect(srcSeqCol.getTag(TAGS.alphabet), testData.alphabet);
88
88
  expect(resSeqCol.getTag(TAGS.alphabet), testData.alphabet);
89
89
  expectArray(resSeqCol.toList(), tgtSeqCol.toList());
@@ -105,8 +105,8 @@ PEPTIDE1{[Cys_SEt].T.*.*}$$$$`,
105
105
  const tgtDf = DG.DataFrame.fromCsv(testData.tgtCsv);
106
106
  const tgtSeqCol = tgtDf.getCol('seq');
107
107
 
108
- expect(srcSeqCol.getTag(DG.TAGS.UNITS), testData.units);
109
- expect(resSeqCol.getTag(DG.TAGS.UNITS), testData.units);
108
+ expect(srcSeqCol.meta.units, testData.units);
109
+ expect(resSeqCol.meta.units, testData.units);
110
110
  expect(srcSeqCol.getTag(TAGS.alphabet), testData.alphabet);
111
111
  expect(resSeqCol.getTag(TAGS.alphabet), testData.alphabet);
112
112
  expectArray(resSeqCol.toList(), tgtSeqCol.toList());
@@ -21,7 +21,7 @@ category('sequenceSpace', async () => {
21
21
  await _testSequenceSpaceReturnsResult(testFastaDf, DimReductionMethods.UMAP, 'sequence');
22
22
  //grok.shell.closeTable(testFastaDf);
23
23
  //testFastaTableView.close();
24
- });
24
+ }, {benchmark: true});
25
25
 
26
26
  test('sequenceSpaceWithEmptyRows', async () => {
27
27
  testHelmWithEmptyRows = await readDataframe('tests/100_3_clustests_empty_vals.csv');
@@ -5,15 +5,16 @@ import * as DG from 'datagrok-api/dg';
5
5
  import $ from 'cash-dom';
6
6
  import wu from 'wu';
7
7
 
8
-
9
8
  import {after, before, category, test, expect, delay, testEvent, awaitCheck} from '@datagrok-libraries/utils/src/test';
10
9
  import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
11
10
  import {
12
11
  getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
13
12
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
14
13
  import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
14
+ import {getHelmHelper, IHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
15
15
 
16
16
  import {awaitGrid, readDataframe} from './utils';
17
+
17
18
  import {
18
19
  BioSubstructureFilter, FastaBioFilter, SeparatorBioFilter, SeparatorFilterProps
19
20
  } from '../widgets/bio-substructure-filter';
@@ -24,11 +25,13 @@ import {_package} from '../package-test';
24
25
 
25
26
 
26
27
  category('bio-substructure-filters', async () => {
28
+ let helmHelper: IHelmHelper;
27
29
  let monomerLibHelper: IMonomerLibHelper;
28
30
  /** Backup actual user's monomer libraries settings */
29
31
  let userLibSettings: UserLibSettings;
30
32
 
31
33
  before(async () => {
34
+ helmHelper = await getHelmHelper(); // init Helm package
32
35
  monomerLibHelper = await getMonomerLibHelper();
33
36
  userLibSettings = await getUserLibSettings();
34
37
 
@@ -205,7 +205,7 @@ PEPTIDE1{Lys_Boc.hHis.Aca.Cys_SEt.T.dK.Thr_PO3H2.Aca.Tyr_PO3H2.Thr_PO3H2.Aca.Tyr
205
205
  const srcDf = DG.DataFrame.fromCsv(srcCsv);
206
206
  const seqCol = srcDf.getCol('seq');
207
207
  seqCol.semType = DG.SEMTYPE.MACROMOLECULE;
208
- seqCol.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
208
+ seqCol.meta.units = NOTATION.FASTA;
209
209
  seqCol.setTag(bioTAGS.alphabet, ALPHABET.PT);
210
210
  const sh = SeqHandler.forColumn(seqCol);
211
211
  const resCol = (await _testToAtomicLevel(srcDf, 'seq', monomerLibHelper))!;
@@ -154,7 +154,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
154
154
  const msaGapLength = 8;
155
155
 
156
156
  // Cell renderer settings
157
- let maxLengthOfMonomer: number = (_package.properties ? _package.properties.MaxMonomerLength : 4) ?? 50;
157
+ let maxLengthOfMonomer: number = (_package.properties ? _package.properties.maxMonomerLength : 4) ?? 50;
158
158
  if (mmcrTAGS.maxMonomerLength in tableCol.tags) {
159
159
  const v = parseInt(tableCol.getTag(mmcrTAGS.maxMonomerLength));
160
160
  maxLengthOfMonomer = !isNaN(v) && v ? v : 50;
@@ -209,7 +209,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
209
209
  g.textBaseline = 'top';
210
210
 
211
211
  //TODO: can this be replaced/merged with splitSequence?
212
- const units = tableCol.getTag(DG.TAGS.UNITS);
212
+ const units = tableCol.meta.units;
213
213
  const aligned: string = tableCol.getTag(bioTAGS.aligned);
214
214
 
215
215
  const palette = getPaletteByType(paletteType);
@@ -298,7 +298,7 @@ export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
298
298
  const tableCol = gridCell.tableColumn as DG.Column<string>;
299
299
  const s: string = cell.value ?? '';
300
300
  const separator = tableCol.tags[bioTAGS.separator];
301
- const units: string = tableCol.tags[DG.TAGS.UNITS];
301
+ const units: string = tableCol.meta.units!;
302
302
  w = getUpdatedWidth(grid, g, x, w, dpr);
303
303
  //TODO: can this be replaced/merged with splitSequence?
304
304
  const [s1, s2] = s.split('#');
@@ -345,7 +345,7 @@ export function drawMoleculeDifferenceOnCanvas(
345
345
  g.textBaseline = 'top';
346
346
 
347
347
  let palette: SeqPalette = UnknownSeqPalettes.Color;
348
- if (units != 'HELM')
348
+ if (units !== 'HELM')
349
349
  palette = getPaletteByType(units.substring(units.length - 2));
350
350
 
351
351
  const vShift = 7;
@@ -17,7 +17,7 @@ export function addCopyMenuUI(cell: DG.Cell, menu: DG.Menu): void {
17
17
  const srcCol = cell.column;
18
18
  const srcRowIdx = cell.rowIndex;
19
19
  const srcSh = SeqHandler.forColumn(srcCol);
20
- const separator = tgtNotation === NOTATION.SEPARATOR ? _package.properties.DefaultSeparator : undefined;
20
+ const separator = tgtNotation === NOTATION.SEPARATOR ? _package.properties.defaultSeparator : undefined;
21
21
  const joiner = srcSh.getJoiner({notation: tgtNotation as NOTATION, separator});
22
22
  const srcSS = srcSh.getSplitted(srcRowIdx);
23
23
  const tgtSeq = joiner(srcSS);
@@ -52,7 +52,8 @@ export function convert(col?: DG.Column): void {
52
52
  separatorInput.value = '/'; // helm monomers can have - in the name like D-aThr;
53
53
  dialogHeader.textContent = 'Current notation: ' + currentNotation;
54
54
  filteredNotations = notations.filter((e) => e !== currentNotation);
55
- targetNotationInput = ui.choiceInput('Convert to', filteredNotations[0], filteredNotations, toggleSeparator);
55
+ targetNotationInput = ui.input.choice('Convert to', {value: filteredNotations[0], items: filteredNotations,
56
+ onValueChanged: toggleSeparator});
56
57
  toggleSeparator();
57
58
  convertDialog?.clear();
58
59
  convertDialog?.add(ui.div([
@@ -63,12 +64,13 @@ export function convert(col?: DG.Column): void {
63
64
  ]));
64
65
  };
65
66
 
66
- const targetColumnInput = ui.columnInput('Column', grok.shell.t, srcCol, toggleColumn);
67
+ const targetColumnInput = ui.input.column('Column', {table: grok.shell.t, value: srcCol,
68
+ onValueChanged: (input) => toggleColumn(input.value)});
67
69
 
68
70
  const separatorArray = ['-', '.', '/'];
69
71
  let filteredNotations = notations.filter((e) => e !== currentNotation);
70
72
 
71
- const separatorInput = ui.choiceInput('Separator', separatorArray[0], separatorArray);
73
+ const separatorInput = ui.input.choice('Separator', {value: separatorArray[0], items: separatorArray});
72
74
 
73
75
  // hide the separator input for non-SEPARATOR target notations
74
76
  const toggleSeparator = () => {
@@ -77,7 +79,8 @@ export function convert(col?: DG.Column): void {
77
79
  else
78
80
  $(separatorInput.root).show();
79
81
  };
80
- let targetNotationInput = ui.choiceInput('Convert to', filteredNotations[0], filteredNotations, toggleSeparator);
82
+ let targetNotationInput = ui.input.choice('Convert to', {value: filteredNotations[0], items: filteredNotations,
83
+ onValueChanged: toggleSeparator});
81
84
 
82
85
  // set correct visibility on init
83
86
  toggleSeparator();
@@ -37,25 +37,20 @@ export class GetRegionFuncEditor {
37
37
  ) {
38
38
  const getDesc = (paramName: string) => this.call.inputParams[paramName].property.description;
39
39
 
40
- this.inputs.table = ui.tableInput('Table',
41
- this.call.inputParams['table'].value ?? grok.shell.tv.dataFrame, undefined,
42
- () => {});
40
+ this.inputs.table = ui.input.table('Table', {value: this.call.inputParams['table'].value ?? grok.shell.tv.dataFrame});
43
41
 
44
42
  const seqColValue = this.call.inputParams['sequence'].value ??
45
43
  this.inputs.table.value!.columns.bySemType(DG.SEMTYPE.MACROMOLECULE);
46
- const seqColOptions = {filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE};
47
- this.inputs.sequence = ui.columnInput('Sequence', grok.shell.tv.dataFrame, seqColValue,
48
- this.sequenceInputChanged.bind(this), seqColOptions);
49
- this.inputs.start = ui.choiceInput(
50
- 'Start', undefined, [], this.startInputChanged.bind(this)) as unknown as DG.InputBase<string>;
51
- this.inputs.end = ui.choiceInput(
52
- 'End', undefined, [], this.endInputChanged.bind(this)) as unknown as DG.InputBase<string>;
53
-
54
- this.inputs.region = ui.choiceInput<SeqRegion>('Region', null as unknown as SeqRegion, [],
55
- this.regionInputChanged.bind(this)) as DG.InputBase<SeqRegion>;
56
-
57
- this.inputs.name = ui.stringInput('Column name', this.getDefaultName(),
58
- this.nameInputChanged.bind(this), {clearIcon: true});
44
+ this.inputs.sequence = ui.input.column('Sequence', {table: grok.shell.tv.dataFrame, value: seqColValue,
45
+ onValueChanged: this.sequenceInputChanged.bind(this), filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE});
46
+ this.inputs.start = ui.input.choice('Start', {onValueChanged: this.startInputChanged.bind(this)}) as unknown as DG.InputBase<string>;
47
+ this.inputs.end = ui.input.choice('End', {onValueChanged: this.endInputChanged.bind(this)}) as unknown as DG.InputBase<string>;
48
+
49
+ this.inputs.region = ui.input.choice<SeqRegion>('Region', {value: null as unknown as SeqRegion, items: [],
50
+ onValueChanged: this.regionInputChanged.bind(this)}) as DG.InputBase<SeqRegion>;
51
+
52
+ this.inputs.name = ui.input.string('Column name', {value: this.getDefaultName(),
53
+ onValueChanged: this.nameInputChanged.bind(this), clearIcon: true});
59
54
  this.inputs.name.onInput(this.nameInputInput.bind(this)); // To catch clear event
60
55
 
61
56
  // tooltips
@@ -8,11 +8,11 @@ import {TaskBarProgressIndicator} from 'datagrok-api/dg';
8
8
  export function getRegionUI(col: DG.Column<string>): void {
9
9
  const sh = SeqHandler.forColumn(col);
10
10
 
11
- const nameInput = ui.stringInput('Name', '');
12
- const startPositionInput = ui.choiceInput('Start Position', sh.posList[0], sh.posList,
13
- () => { /* TODO: update name placeholder with getDefaultName() */ });
14
- const endPositionInput = ui.choiceInput('End Position', sh.posList[sh.posList.length], sh.posList,
15
- () => { /* TODO: update name placeholder with getDefaultName() */ });
11
+ const nameInput = ui.input.string('Name', {value: ''});
12
+ const startPositionInput = ui.input.choice('Start Position', {value: sh.posList[0], items: sh.posList,
13
+ onValueChanged: () => { /* TODO: update name placeholder with getDefaultName() */ }});
14
+ const endPositionInput = ui.input.choice('End Position', {value: sh.posList[sh.posList.length], items: sh.posList,
15
+ onValueChanged: () => { /* TODO: update name placeholder with getDefaultName() */ }});
16
16
 
17
17
  const getDefaultName = (): string => {
18
18
  return `${col.name}:${startPositionInput.value}-${endPositionInput.value}`;
@@ -25,7 +25,7 @@ export class MacromoleculeColumnWidget extends DG.Widget {
25
25
 
26
26
  async init(): Promise<void> {
27
27
  const sh = SeqHandler.forColumn(this.seqCol);
28
- const pkgTooltipWebLogo = _package.properties.TooltipWebLogo;
28
+ const pkgTooltipWebLogo = _package.properties.tooltipWebLogo;
29
29
  const colTooltipWebLogo = this.seqCol.getTag(wlTAGS.tooltipWebLogo);
30
30
 
31
31
  if (pkgTooltipWebLogo !== false && !['false', 'off', 'disable', 'disabled'].includes(colTooltipWebLogo)) {
@@ -4,6 +4,7 @@ import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
 
6
6
  import {delay} from '@datagrok-libraries/utils/src/test';
7
+ import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
7
8
  import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
8
9
  import {
9
10
  getUserLibSettings, setUserLibSettings, LIB_PATH
@@ -49,7 +50,9 @@ export class MonomerLibManager implements IMonomerLibHelper {
49
50
  }
50
51
 
51
52
  /** Protect constructor to prevent multiple instantiation. */
52
- protected constructor() {}
53
+ protected constructor(
54
+ private readonly logger: ILogger,
55
+ ) {}
53
56
 
54
57
  /** Singleton monomer library
55
58
  * @return {MonomerLibManager} MonomerLibHelper instance
@@ -64,7 +67,8 @@ export class MonomerLibManager implements IMonomerLibHelper {
64
67
  async getFileManager(): Promise<MonomerLibFileManager> {
65
68
  if (this._fileManagerPromise === undefined) {
66
69
  this._fileManagerPromise = (async () => {
67
- const fileManager: MonomerLibFileManager = await MonomerLibFileManager.create(this, this._eventManager);
70
+ const fileManager: MonomerLibFileManager =
71
+ await MonomerLibFileManager.create(this, this._eventManager, this.logger);
68
72
  return fileManager;
69
73
  })();
70
74
  }
@@ -82,6 +86,7 @@ export class MonomerLibManager implements IMonomerLibHelper {
82
86
  // WARNING: This function is not allowed to throw any exception,
83
87
  // because it will prevent further handling monomer library settings
84
88
  // through blocking this.loadLibrariesPromise
89
+ const pi = DG.TaskBarProgressIndicator.create('Loading monomers ...');
85
90
  try {
86
91
  const [libFileNameList, settings]: [string[], UserLibSettings] = await Promise.all([
87
92
  (await this.getFileManager()).getValidLibraryPaths(),
@@ -95,14 +100,19 @@ export class MonomerLibManager implements IMonomerLibHelper {
95
100
  return isFileIncluded && isExplicit;
96
101
  });
97
102
 
103
+ let completedLibCount: number = 0;
98
104
  const libs: IMonomerLib[] = await Promise.all(filteredLibFnList
99
105
  .map((libFileName) => {
100
106
  //TODO handle whether files are in place
101
- return this.readLibrary(LIB_PATH, libFileName).catch((err: any) => {
102
- const errMsg: string = `Loading monomers from '${libFileName}' error: ` +
103
- `${err instanceof Error ? err.message : err.toString()}`;
104
- return new MonomerLib({}, libFileName, errMsg);
105
- });
107
+ return this.readLibrary(LIB_PATH, libFileName)
108
+ .catch((err: any) => {
109
+ const errMsg: string = `Loading monomers from '${libFileName}' error: ` +
110
+ `${err instanceof Error ? err.message : err.toString()}`;
111
+ return new MonomerLib({}, libFileName, errMsg);
112
+ }).finally(() => {
113
+ pi.update(Math.round(100 * (++completedLibCount) / filteredLibFnList.length),
114
+ `Loading monomer libs ${completedLibCount}/${filteredLibFnList.length}`);
115
+ });
106
116
  }));
107
117
  this._monomerLib.updateLibs(libs, reload);
108
118
  } catch (err: any) {
@@ -112,6 +122,8 @@ export class MonomerLibManager implements IMonomerLibHelper {
112
122
 
113
123
  const errStack = err instanceof Error ? err.stack : undefined;
114
124
  _package.logger.error(errMsg, undefined, errStack);
125
+ } finally {
126
+ pi.close();
115
127
  }
116
128
  });
117
129
  }
@@ -149,7 +161,7 @@ export class MonomerLibManager implements IMonomerLibHelper {
149
161
  let res = window.$monomerLibHelperPromise;
150
162
  if (res === undefined) {
151
163
  res = window.$monomerLibHelperPromise = (async () => {
152
- const instance = new MonomerLibManager();
164
+ const instance = new MonomerLibManager(_package.logger);
153
165
  instance._eventManager = MonomerLibFileEventManager.getInstance();
154
166
  return instance;
155
167
  })();
@@ -6,6 +6,7 @@ import * as DG from 'datagrok-api/dg';
6
6
  import {JSONSchemaType} from 'ajv';
7
7
 
8
8
  import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
9
+ import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
9
10
  import {LIB_PATH} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
10
11
  import {
11
12
  HELM_REQUIRED_FIELD as REQ,
@@ -31,6 +32,7 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
31
32
  private readonly fileValidator: MonomerLibFileValidator,
32
33
  private readonly libHelper: IMonomerLibHelper,
33
34
  public readonly eventManager: MonomerLibFileEventManager,
35
+ private readonly logger: ILogger,
34
36
  ) {
35
37
  this.eventManager.updateValidLibraryFileListRequested$.subscribe(async () => {
36
38
  await this.updateValidLibraryList();
@@ -46,19 +48,20 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
46
48
 
47
49
  /** For internal use only, get {@link IMonomerLibHelper.getFileManager} */
48
50
  public static async create(
49
- libHelper: IMonomerLibHelper, eventManager: MonomerLibFileEventManager
51
+ libHelper: IMonomerLibHelper, eventManager: MonomerLibFileEventManager, logger: ILogger,
50
52
  ): Promise<MonomerLibFileManager> {
51
53
  const helmSchemaRaw = await grok.dapi.files.readAsText(HELM_JSON_SCHEMA_PATH);
52
54
  const helmSchema = JSON.parse(helmSchemaRaw) as JSONSchemaType<any>;
53
55
 
54
56
  const fileValidator = new MonomerLibFileValidator(helmSchema);
55
- return new MonomerLibFileManager(fileValidator, libHelper, eventManager);
57
+ return new MonomerLibFileManager(fileValidator, libHelper, eventManager, logger);
56
58
  }
57
59
 
58
60
  /** Add standard .json monomer library */
59
61
  async addLibraryFile(fileContent: string, fileName: string): Promise<void> {
60
62
  try {
61
- if (await this.libraryFileExists(fileName)) {
63
+ const alreadyFileExists = await grok.dapi.files.exists(LIB_PATH + `${fileName}`);
64
+ if (alreadyFileExists) {
62
65
  grok.shell.error(`File ${fileName} already exists`);
63
66
  return;
64
67
  }
@@ -122,21 +125,17 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
122
125
  return await this.eventManager.getValidLibraryPathsAsynchronously();
123
126
  }
124
127
 
125
- private async libraryFileExists(fileName: string): Promise<boolean> {
126
- return await grok.dapi.files.exists(LIB_PATH + `${fileName}`);
127
- }
128
-
129
128
  private async updateValidLibraryList(): Promise<void> {
130
129
  const logPrefix: string = `${this.toLog()}.updateValidLibraryList()`;
131
- _package.logger.debug(`${logPrefix}, start`);
132
- return this.filesPromise = this.filesPromise.then(async () => {
133
- _package.logger.debug(`${logPrefix}, IN`);
130
+ this.logger.debug(`${logPrefix}, start`);
131
+ this.filesPromise = this.filesPromise.then(async () => {
132
+ this.logger.debug(`${logPrefix}, IN`);
134
133
  const invalidFiles = [] as string[];
135
134
  // console.log(`files before validation:`, this.libraryEventManager.getValidFilesPathList());
136
135
  const filePaths = await this.getFilePathsAtDefaultLocation();
137
136
 
138
137
  if (!this.fileListHasChanged(filePaths)) {
139
- _package.logger.debug(`${logPrefix}, end, not changed`);
138
+ this.logger.debug(`${logPrefix}, end, not changed`);
140
139
  return;
141
140
  }
142
141
 
@@ -160,18 +159,19 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
160
159
  // console.log(`files after validation:`, this.libraryEventManager.getValidFilesPathList());
161
160
 
162
161
  if (validLibraryPaths.some((el) => !el.endsWith('.json')))
163
- _package.logger.warning(`Wrong validation: ${validLibraryPaths}`);
162
+ this.logger.warning(`Wrong validation: ${validLibraryPaths}`);
164
163
 
165
164
  if (invalidFiles.length > 0) {
166
165
  const message = `Invalid monomer library files in ${LIB_PATH}` +
167
166
  `, consider fixing or removing them: ${invalidFiles.join(', ')}`;
168
167
 
169
- _package.logger.warning(message);
168
+ this.logger.warning(message);
170
169
  // grok.shell.warning(message);
171
170
  }
172
- _package.logger.debug(`${logPrefix}, OUT`);
171
+ this.logger.debug(`${logPrefix}, OUT`);
173
172
  });
174
- _package.logger.debug(`${logPrefix}, end`);
173
+ this.logger.debug(`${logPrefix}, end`);
174
+ return this.filesPromise;
175
175
  }
176
176
 
177
177
  private fileListHasChanged(newList: string[]): boolean {
@@ -191,21 +191,25 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
191
191
 
192
192
  /** Get relative paths for files in LIB_PATH */
193
193
  private async getFilePathsAtDefaultLocation(): Promise<string[]> {
194
+ const logPrefix = `${this.toLog()}.getFilePathsAtDefaultLocation()`;
195
+ this.logger.debug(`${logPrefix}, start`);
194
196
  const list = await grok.dapi.files.list(LIB_PATH);
195
197
  const paths = list.map((fileInfo) => {
196
198
  return fileInfo.fullPath;
197
199
  });
198
200
 
199
- // console.log(`retreived paths:`, paths);
200
-
201
- // WARNING: an extra sanity check,
202
- // caused by unexpected behavior of grok.dapi.files.list() when it returns non-existent paths
201
+ const checkForUi = false;
203
202
  const existingPaths = [] as string[];
204
- for (const path of paths) {
205
- const exists = await grok.dapi.files.exists(path);
206
- if (exists)
207
- existingPaths.push(path);
208
- }
203
+ if (checkForUi) {
204
+ // WARNING: an extra sanity check,
205
+ // caused by unexpected behavior of grok.dapi.files.list() when it returns non-existent paths
206
+ for (const path of paths) {
207
+ const exists = await grok.dapi.files.exists(path);
208
+ if (exists)
209
+ existingPaths.push(path);
210
+ }
211
+ } else
212
+ existingPaths.push(...paths);
209
213
 
210
214
  return existingPaths.map((path) => {
211
215
  // Get relative path (to LIB_PATH)
@@ -14,7 +14,8 @@ export class MonomerLibFileValidator {
14
14
  constructor(
15
15
  private helmMonomerSchema: JSONSchemaType<any>
16
16
  ) {
17
- const ajv = new Ajv2020({allErrors: true, strictTuples: false});
17
+ // HELMMonomerSchema.json / #/properties/id uses a union type (string added by Maria Dolotova)
18
+ const ajv = new Ajv2020({allErrors: true, strictTuples: false, allowUnionTypes: true});
18
19
  addErrors(ajv);
19
20
  this.validateMonomerSchema = ajv.compile(this.helmMonomerSchema);
20
21
  }
@@ -139,12 +139,9 @@ class LibraryControlsManager {
139
139
  const logPrefix = `${this.toLog()}.createLibInput()`;
140
140
  _package.logger.debug(`${logPrefix}, libFileName = '${libFileName}', start`);
141
141
  const isMonomerLibrarySelected = !this.userLibSettings.exclude.includes(libFileName);
142
- const libInput = ui.boolInput(
143
- libFileName,
144
- isMonomerLibrarySelected,
145
- (isSelected: boolean) => {
146
- this.fileManager.eventManager.updateLibrarySelectionStatus(libFileName, isSelected);
147
- });
142
+ const libInput = ui.input.bool(libFileName, {value: isMonomerLibrarySelected, onValueChanged: (input) => {
143
+ this.fileManager.eventManager.updateLibrarySelectionStatus(libFileName, input.value);
144
+ }});
148
145
  ui.tooltip.bind(libInput.root, `Include monomers from ${libFileName}`);
149
146
  const deleteIcon = ui.iconFA('trash-alt', () => this.promptForLibraryDeletion(libFileName));
150
147
  ui.tooltip.bind(deleteIcon, `Delete ${libFileName}`);
@@ -38,18 +38,18 @@ export async function multipleSequenceAlignmentUI(
38
38
  }
39
39
 
40
40
  // UI for PepSea alignment
41
- const methodInput = ui.choiceInput('Method', options.pepsea.method, pepseaMethods);
41
+ const methodInput = ui.input.choice('Method', {value: options.pepsea.method, items: pepseaMethods});
42
42
  methodInput.setTooltip('Alignment method');
43
43
 
44
44
  // UI for Kalign alignment
45
- const terminalGapInput = ui.floatInput('Terminal gap', options?.kalign?.terminalGap ?? null);
45
+ const terminalGapInput = ui.input.float('Terminal gap', {value: options?.kalign?.terminalGap});
46
46
  terminalGapInput.setTooltip('Penalty for opening a gap at the beginning or end of the sequence');
47
47
  const kalignVersionDiv = ui.p(`Kalign version: ${kalignVersion}`, 'kalign-version');
48
48
 
49
49
  // shared UI
50
- const gapOpenInput = ui.floatInput('Gap open', options.pepsea.gapOpen);
50
+ const gapOpenInput = ui.input.float('Gap open', {value: options.pepsea.gapOpen});
51
51
  gapOpenInput.setTooltip('Gap opening penalty at group-to-group alignment');
52
- const gapExtendInput = ui.floatInput('Gap extend', options.pepsea.gapExtend);
52
+ const gapExtendInput = ui.input.float('Gap extend', {value: options.pepsea.gapExtend});
53
53
  gapExtendInput.setTooltip('Gap extension penalty to skip the alignment');
54
54
 
55
55
  const msaParamsDiv = ui.inputs([gapOpenInput, gapExtendInput, terminalGapInput]);
@@ -69,25 +69,24 @@ export async function multipleSequenceAlignmentUI(
69
69
  let performAlignment: (() => Promise<DG.Column<string> | null>) | undefined;
70
70
 
71
71
  let prevSeqCol = seqCol;
72
- const colInput = ui.columnInput(
73
- 'Sequence', table, seqCol,
74
- async (valueCol: DG.Column) => {
75
- if (!valueCol || valueCol.semType !== DG.SEMTYPE.MACROMOLECULE) {
72
+ const colInput = ui.input.column(
73
+ 'Sequence', {table: table, value: seqCol, onValueChanged: async (input: DG.InputBase<DG.Column<string>>): Promise<void> => {
74
+ if (!input.value || input.value.semType !== DG.SEMTYPE.MACROMOLECULE) {
76
75
  okBtn.disabled = true;
77
76
  await delay(0); // to
78
77
  colInput.value = prevSeqCol as DG.Column<string>;
79
78
  return;
80
79
  }
81
- prevSeqCol = valueCol;
80
+ prevSeqCol = input.value;
82
81
  okBtn.disabled = false;
83
82
  performAlignment = await onColInputChange(
84
83
  colInput.value, table, pepseaInputRootStyles, kalignInputRootStyles,
85
84
  methodInput, clustersColInput, gapOpenInput, gapExtendInput, terminalGapInput,
86
85
  );
87
- }, {filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE} as ColumnInputOptions
86
+ }, filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE} as ColumnInputOptions
88
87
  ) as DG.InputBase<DG.Column<string>>;
89
88
  colInput.setTooltip('Sequences column to use for alignment');
90
- const clustersColInput = ui.columnInput('Clusters', table, options.clustersCol);
89
+ const clustersColInput = ui.input.column('Clusters', {table: table, value: options.clustersCol!});
91
90
  clustersColInput.nullable = true;
92
91
 
93
92
  const dlg = ui.dialog('MSA')
@@ -103,13 +103,13 @@ export async function runKalign(srcCol: DG.Column<string>, isAligned: boolean =
103
103
  }
104
104
 
105
105
  // units
106
- const srcUnits = srcCol.getTag(DG.TAGS.UNITS);
106
+ const srcUnits = srcCol.meta.units;
107
107
  //aligned
108
108
  const tgtAligned = ALIGNMENT.SEQ_MSA;
109
109
  //alphabet
110
110
  const srcAlphabet = srcCol.getTag(bioTAGS.alphabet);
111
111
 
112
- tgtCol.setTag(DG.TAGS.UNITS, srcUnits);
112
+ tgtCol.meta.units = srcUnits;
113
113
  tgtCol.setTag(bioTAGS.aligned, tgtAligned);
114
114
  tgtCol.setTag(bioTAGS.alphabet, srcAlphabet);
115
115
  tgtCol.semType = DG.SEMTYPE.MACROMOLECULE;
@@ -91,7 +91,7 @@ export async function runPepsea(srcCol: DG.Column<string>, unUsedName: string,
91
91
  }
92
92
 
93
93
  const alignedSequencesCol: DG.Column<string> = DG.Column.fromStrings(unUsedName, alignedSequences);
94
- alignedSequencesCol.setTag(DG.TAGS.UNITS, NOTATION.SEPARATOR);
94
+ alignedSequencesCol.meta.units = NOTATION.SEPARATOR;
95
95
  alignedSequencesCol.setTag(bioTAGS.separator, C.PEPSEA.SEPARATOR);
96
96
  alignedSequencesCol.setTag(bioTAGS.aligned, ALIGNMENT.SEQ_MSA);
97
97
  alignedSequencesCol.setTag(bioTAGS.alphabet, ALPHABET.UN);
@@ -20,8 +20,8 @@ export function saveAsFastaUI() {
20
20
  .find((gcol: DG.GridColumn) => gcol.name.toLowerCase().indexOf('id') !== -1);
21
21
  const idDefaultValue = defaultIdGCol ? [defaultIdGCol.name] : [];
22
22
 
23
- const idGColListInput = ui.multiChoiceInput('Seq id columns', idDefaultValue,
24
- idGColList.map((gcol: DG.GridColumn) => gcol.name));
23
+ const idGColListInput = ui.input.multiChoice('Seq id columns', {value: idDefaultValue,
24
+ items: idGColList.map((gcol: DG.GridColumn) => gcol.name)});
25
25
 
26
26
  const seqGColList: DG.GridColumn[] = wu.count(0).take(grid.columns.length)/* range rom 0 to grid.columns.length */
27
27
  .map((colI: number) => grid.columns.byIndex(colI)!)
@@ -35,10 +35,10 @@ export function saveAsFastaUI() {
35
35
  }).toArray();
36
36
 
37
37
  const seqDefaultValue = seqGColList.length > 0 ? seqGColList[0].name : [];
38
- const seqColInput = ui.choiceInput('Seq column', seqDefaultValue,
39
- seqGColList.map((gCol: DG.GridColumn) => gCol.name));
38
+ const seqColInput = ui.input.choice('Seq column', {value: seqDefaultValue,
39
+ items: seqGColList.map((gCol: DG.GridColumn) => gCol.name)});
40
40
 
41
- const lineWidthInput = ui.intInput('FASTA line width', FASTA_LINE_WIDTH);
41
+ const lineWidthInput = ui.input.int('FASTA line width', {value: FASTA_LINE_WIDTH});
42
42
 
43
43
  ui.dialog({title: 'Save as FASTA'})
44
44
  .add(ui.inputs([