@datagrok/peptides 1.7.2 → 1.8.1

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 (43) hide show
  1. package/.eslintrc.json +2 -2
  2. package/README.md +3 -3
  3. package/dist/563.js +2 -0
  4. package/dist/611.js +2 -0
  5. package/dist/802.js +2 -0
  6. package/dist/96.js +2 -0
  7. package/dist/package-test.js +2 -29778
  8. package/dist/package.js +2 -28285
  9. package/files/icons/logo-summary-viewer.svg +13 -0
  10. package/files/icons/peptide-sar-vertical-viewer.svg +13 -0
  11. package/files/icons/peptide-sar-viewer.svg +19 -0
  12. package/files/icons/peptide-space-viewer.svg +40 -0
  13. package/files/tests/HELM_small.csv +12 -0
  14. package/package.json +7 -8
  15. package/src/demo/fasta.ts +24 -0
  16. package/src/model.ts +381 -325
  17. package/src/package-test.ts +3 -0
  18. package/src/package.ts +54 -30
  19. package/src/tests/algorithms.ts +1 -1
  20. package/src/tests/core.ts +13 -8
  21. package/src/tests/model.ts +152 -0
  22. package/src/tests/peptide-space-test.ts +1 -2
  23. package/src/tests/table-view.ts +158 -0
  24. package/src/tests/viewers.ts +142 -4
  25. package/src/tests/widgets.ts +135 -0
  26. package/src/utils/algorithms.ts +2 -2
  27. package/src/utils/cell-renderer.ts +2 -2
  28. package/src/utils/constants.ts +8 -0
  29. package/src/utils/distance-matrix.worker.ts +16 -0
  30. package/src/utils/misc.ts +4 -4
  31. package/src/utils/peptide-similarity-space.ts +0 -1
  32. package/src/utils/statistics.ts +14 -10
  33. package/src/utils/types.ts +8 -4
  34. package/src/utils/worker-creator.ts +11 -0
  35. package/src/viewers/logo-summary.ts +246 -168
  36. package/src/viewers/peptide-space-viewer.ts +6 -6
  37. package/src/viewers/sar-viewer.ts +108 -110
  38. package/src/widgets/distribution.ts +95 -128
  39. package/src/widgets/mutation-cliffs.ts +2 -2
  40. package/src/widgets/peptides.ts +11 -3
  41. package/src/widgets/settings.ts +94 -24
  42. package/tsconfig.json +1 -1
  43. package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +0 -9077
@@ -5,6 +5,9 @@ import './tests/core';
5
5
  import './tests/peptide-space-test';
6
6
  import './tests/algorithms';
7
7
  import './tests/viewers';
8
+ import './tests/widgets';
9
+ import './tests/table-view';
10
+ import './tests/model';
8
11
 
9
12
  export const _package = new DG.Package();
10
13
  export {tests};
package/src/package.ts CHANGED
@@ -7,23 +7,37 @@ import {analyzePeptidesUI} from './widgets/peptides';
7
7
  import {PeptideSimilaritySpaceWidget} from './utils/peptide-similarity-space';
8
8
  import {manualAlignmentWidget} from './widgets/manual-alignment';
9
9
  import {MonomerPosition, MostPotentResiduesViewer} from './viewers/sar-viewer';
10
-
10
+ import {getTreeHelper, ITreeHelper} from '@datagrok-libraries/bio/src/trees/tree-helper';
11
+ import {IDendrogramService, getDendrogramService} from '@datagrok-libraries/bio/src/trees/dendrogram';
11
12
  import {PeptideSpaceViewer} from './viewers/peptide-space-viewer';
12
- import {LogoSummary} from './viewers/logo-summary';
13
+ import {LogoSummaryTable} from './viewers/logo-summary';
13
14
  import {MonomerWorks} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
14
15
  import {PeptidesModel} from './model';
16
+ import {macromoleculeSarFastaDemoUI} from './demo/fasta';
15
17
 
16
18
  let monomerWorks: MonomerWorks | null = null;
19
+ let treeHelper: ITreeHelper | null = null;
20
+ let dendrogramService: IDendrogramService | null = null;
17
21
 
18
22
  export const _package = new DG.Package();
19
23
 
20
- export function getMonomerWorks(): MonomerWorks | null {
21
- return monomerWorks;
24
+ export function getMonomerWorksInstance(): MonomerWorks {
25
+ return monomerWorks!;
26
+ }
27
+
28
+ export function getTreeHelperInstance(): ITreeHelper {
29
+ return treeHelper!;
30
+ }
31
+
32
+ export function getDendrogramServiceInstance(): IDendrogramService {
33
+ return dendrogramService!;
22
34
  }
23
35
 
24
36
  //tags: init
25
37
  export async function initPeptides(): Promise<void> {
26
38
  monomerWorks ??= new MonomerWorks(await grok.functions.call('Bio:getBioLib'));
39
+ treeHelper ??= await getTreeHelper();
40
+ dendrogramService ??= await getDendrogramService();
27
41
  }
28
42
 
29
43
  async function openDemoData(chosenFile: string): Promise<void> {
@@ -44,24 +58,22 @@ export function Peptides(): void {
44
58
  const wikiLink = ui.link('wiki', 'https://github.com/datagrok-ai/public/blob/master/help/domains/bio/peptides.md');
45
59
  const textLink = ui.inlineText(['For more details, see our ', wikiLink, '.']);
46
60
 
47
- const appDescription = ui.info(
48
- [
49
- ui.list([
50
- '- automatic recognition of peptide sequences',
51
- '- native integration with tons of Datagrok out-of-the box features (visualization, filtering, clustering, ' +
61
+ const appDescription = ui.info([
62
+ ui.list([
63
+ '- automatic recognition of peptide sequences',
64
+ '- native integration with tons of Datagrok out-of-the box features (visualization, filtering, clustering, ' +
52
65
  'multivariate analysis, etc)',
53
- '- custom rendering in the spreadsheet',
54
- '- interactive logo plots',
55
- '- rendering residues',
56
- '- structure-activity relationship:',
57
- ' ',
58
- 'a) highlighting statistically significant changes in activity in the [position, monomer] spreadsheet',
59
- 'b) for the specific [position, monomer], visualizing changes of activity distribution (specific monomer in ' +
66
+ '- custom rendering in the spreadsheet',
67
+ '- interactive logo plots',
68
+ '- rendering residues',
69
+ '- structure-activity relationship:',
70
+ ' ',
71
+ 'a) highlighting statistically significant changes in activity in the [position, monomer] spreadsheet',
72
+ 'b) for the specific [position, monomer], visualizing changes of activity distribution (specific monomer in ' +
60
73
  'this position vs rest of the monomers in this position)',
61
- 'c) interactivity',
62
- ]),
63
- ],
64
- 'Use and analyse peptide sequence data to support your research:',
74
+ 'c) interactivity',
75
+ ]),
76
+ ], 'Use and analyse peptide sequence data to support your research:',
65
77
  );
66
78
 
67
79
  const windows = grok.shell.windows;
@@ -80,7 +92,7 @@ export function Peptides(): void {
80
92
  ]);
81
93
  }
82
94
 
83
- //top-menu: Bio | Peptides...
95
+ //top-menu: Bio | SAR | Peptides...
84
96
  //name: Bio Peptides
85
97
  export function peptidesDialog(): DG.Dialog {
86
98
  const analyzeObject = analyzePeptidesUI(grok.shell.t);
@@ -101,32 +113,36 @@ export function peptidesPanel(col: DG.Column): DG.Widget {
101
113
  return new DG.Widget(analyzeObject.host);
102
114
  }
103
115
 
104
- //name: peptide-sar-viewer
105
- //description: Peptides SAR Viewer
116
+ //name: Monomer-Position
117
+ //description: Peptides Monomer-Position Viewer
106
118
  //tags: viewer
119
+ //meta.icon: files/icons/peptide-sar-viewer.svg
107
120
  //output: viewer result
108
- export function sar(): MonomerPosition {
121
+ export function monomerPosition(): MonomerPosition {
109
122
  return new MonomerPosition();
110
123
  }
111
124
 
112
- //name: peptide-sar-viewer-vertical
113
- //description: Peptides Vertical SAR Viewer
125
+ //name: Most Potent Residues
126
+ //description: Peptides Most Potent Residues Viewer
114
127
  //tags: viewer
128
+ //meta.icon: files/icons/peptide-sar-vertical-viewer.svg
115
129
  //output: viewer result
116
- export function sarVertical(): MostPotentResiduesViewer {
130
+ export function mostPotentResidues(): MostPotentResiduesViewer {
117
131
  return new MostPotentResiduesViewer();
118
132
  }
119
133
 
120
- //name: logo-summary-viewer
134
+ //name: Logo Summary Table
121
135
  //tags: viewer
136
+ //meta.icon: files/icons/logo-summary-viewer.svg
122
137
  //output: viewer result
123
- export function logoSummary(): LogoSummary {
124
- return new LogoSummary();
138
+ export function logoSummaryTable(): LogoSummaryTable {
139
+ return new LogoSummaryTable();
125
140
  }
126
141
 
127
142
  //name: peptide-space-viewer
128
143
  //description: Peptide Space Viewer
129
144
  //tags: viewer
145
+ //meta.icon: files/icons/peptide-space-viewer.svg
130
146
  //output: viewer result
131
147
  export function peptideSpace(): PeptideSpaceViewer {
132
148
  return new PeptideSpaceViewer();
@@ -155,3 +171,11 @@ export async function peptideSpacePanel(col: DG.Column): Promise<DG.Widget> {
155
171
  const widget = new PeptideSimilaritySpaceWidget(col, grok.shell.v as DG.TableView);
156
172
  return widget.draw();
157
173
  }
174
+
175
+ // --- Demo ---
176
+ //name: Macromolecule SAR Analysis
177
+ //description: Macromolecule SAR Analysis demo on peptide sequences in FASTA format
178
+ //meta.demoPath: Bioinformatics | Macromolecule SAR Analysis
179
+ export async function macromoleculeSarFastaDemo(): Promise<void> {
180
+ return macromoleculeSarFastaDemoUI();
181
+ }
@@ -26,7 +26,7 @@ category('Algorithms', () => {
26
26
  });
27
27
 
28
28
  test('MutationCliffs', async () => {
29
- const substInfo: type.SubstitutionsInfo = findMutations(activityCol, monomerColumns, settings);
29
+ const substInfo: type.MutationCliffs = findMutations(activityCol, monomerColumns, settings);
30
30
  expect(substInfo.has('C'), true);
31
31
  expect(substInfo.has('D'), true);
32
32
  expect(substInfo.has('A'), false);
package/src/tests/core.ts CHANGED
@@ -35,11 +35,12 @@ category('Core', () => {
35
35
  simpleAlignedSeqCol.setTag(bioTAGS.aligned, ALIGNMENT.SEQ_MSA);
36
36
  simpleScaledCol = scaleActivity(simpleActivityCol, '-lg');
37
37
 
38
- model = await startAnalysis(simpleActivityCol, simpleAlignedSeqCol, null, simpleTable, simpleScaledCol, '-lg');
38
+ model = await startAnalysis(
39
+ simpleActivityCol, simpleAlignedSeqCol, null, simpleTable, simpleScaledCol, C.SCALING_METHODS.MINUS_LG);
39
40
  expect(model instanceof PeptidesModel, true);
40
41
 
41
42
  if (model != null)
42
- model.mutationCliffsSelection = {'11': ['D']};
43
+ model.monomerPositionSelection = {'11': ['D']};
43
44
  });
44
45
 
45
46
  test('Start analysis: сomplex', async () => {
@@ -55,11 +56,11 @@ category('Core', () => {
55
56
  complexScaledCol = scaleActivity(complexActivityCol, '-lg');
56
57
 
57
58
  model = await startAnalysis(
58
- complexActivityCol, complexAlignedSeqCol, null, complexTable, complexScaledCol, '-lg');
59
+ complexActivityCol, complexAlignedSeqCol, null, complexTable, complexScaledCol, C.SCALING_METHODS.MINUS_LG);
59
60
  expect(model instanceof PeptidesModel, true);
60
61
 
61
62
  if (model != null)
62
- model.mutationCliffsSelection = {'13': ['-']};
63
+ model.monomerPositionSelection = {'13': ['-']};
63
64
  });
64
65
 
65
66
  test('Save and load project', async () => {
@@ -73,7 +74,8 @@ category('Core', () => {
73
74
  simpleAlignedSeqCol.setTag(bioTAGS.aligned, ALIGNMENT.SEQ_MSA);
74
75
  simpleScaledCol = scaleActivity(simpleActivityCol, '-lg');
75
76
 
76
- model = await startAnalysis(simpleActivityCol, simpleAlignedSeqCol, null, simpleTable, simpleScaledCol, '-lg');
77
+ model = await startAnalysis(
78
+ simpleActivityCol, simpleAlignedSeqCol, null, simpleTable, simpleScaledCol, C.SCALING_METHODS.MINUS_LG);
77
79
  let v = grok.shell.getTableView('Peptides analysis');
78
80
  const d = v.dataFrame;
79
81
  const layout = v.saveLayout();
@@ -107,7 +109,8 @@ category('Core', () => {
107
109
  const sequenceCol = df.getCol('HELM');
108
110
  sequenceCol.semType = DG.SEMTYPE.MACROMOLECULE;
109
111
  sequenceCol.setTag(DG.TAGS.UNITS, NOTATION.HELM);
110
- const model = await startAnalysis(activityCol, sequenceCol, clustersCol, df, scaledActivityCol, 'none');
112
+ const model = await startAnalysis(
113
+ activityCol, sequenceCol, clustersCol, df, scaledActivityCol, C.SCALING_METHODS.NONE);
111
114
 
112
115
  for (let i = 0; i < 5; ++i)
113
116
  DG.time('Cluster stats', () => model?.calculateClusterStatistics());
@@ -121,7 +124,8 @@ category('Core', () => {
121
124
  const sequenceCol = df.getCol('HELM');
122
125
  sequenceCol.semType = DG.SEMTYPE.MACROMOLECULE;
123
126
  sequenceCol.setTag(DG.TAGS.UNITS, NOTATION.HELM);
124
- const model = await startAnalysis(activityCol, sequenceCol, clustersCol, df, scaledActivityCol, 'none');
127
+ const model = await startAnalysis(
128
+ activityCol, sequenceCol, clustersCol, df, scaledActivityCol, C.SCALING_METHODS.NONE);
125
129
 
126
130
  for (let i = 0; i < 5; ++i)
127
131
  DG.time('Monomer position stats', () => model?.calculateMonomerPositionStatistics());
@@ -138,7 +142,8 @@ category('Core', () => {
138
142
 
139
143
  for (let i = 0; i < 5; ++i) {
140
144
  await DG.timeAsync('Analysis start', async () => {
141
- const model = await startAnalysis(activityCol, sequenceCol, clustersCol, df, scaledActivityCol, 'none');
145
+ const model = await startAnalysis(
146
+ activityCol, sequenceCol, clustersCol, df, scaledActivityCol, C.SCALING_METHODS.NONE);
142
147
  if (model)
143
148
  grok.shell.closeTable(model.df);
144
149
  });
@@ -0,0 +1,152 @@
1
+ import * as DG from 'datagrok-api/dg';
2
+
3
+ import {category, test, before, expect, expectFloat, delay} from '@datagrok-libraries/utils/src/test';
4
+ import {_package} from '../package-test';
5
+ import {PeptidesModel, VIEWER_TYPE, getAggregatedColName} from '../model';
6
+ import {startAnalysis} from '../widgets/peptides';
7
+ import {scaleActivity} from '../utils/misc';
8
+ import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
9
+ import {COLUMNS_NAMES, SCALING_METHODS} from '../utils/constants';
10
+ import {LogoSummaryTable} from '../viewers/logo-summary';
11
+
12
+ category('Model: Settings', () => {
13
+ let df: DG.DataFrame;
14
+ let model: PeptidesModel;
15
+ let activityCol: DG.Column<number>;
16
+ let sequenceCol: DG.Column<string>;
17
+ let clusterCol: DG.Column<any>;
18
+ let scaledActivityCol: DG.Column<number>;
19
+
20
+ const mutationCliffsDefaultParams = {maxMutations: 1, minActivityDelta: 0};
21
+ const mutationCliffsTestParams = {maxMutations: 2, minActivityDelta: 0.5};
22
+
23
+ before(async () => {
24
+ df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
25
+ activityCol = df.getCol('activity');
26
+ sequenceCol = df.getCol('sequence');
27
+ sequenceCol.semType = DG.SEMTYPE.MACROMOLECULE;
28
+ sequenceCol.setTag(DG.TAGS.UNITS, NOTATION.HELM);
29
+ scaledActivityCol = scaleActivity(activityCol, SCALING_METHODS.NONE);
30
+ clusterCol = df.getCol('cluster');
31
+ const tempModel = await startAnalysis(activityCol, sequenceCol, clusterCol, df, scaledActivityCol,
32
+ SCALING_METHODS.NONE);
33
+ if (tempModel === null)
34
+ throw new Error('Model is null');
35
+ model = tempModel;
36
+ });
37
+
38
+ test('Activity scaling', async () => {
39
+ const getError = (row: number, method: SCALING_METHODS): string =>
40
+ `Activity mismatch at row ${row} for scaling method '${method}'`;
41
+ const tolerance = 0.0001;
42
+ const origActivityData =
43
+ model.df.getCol(model.settings.activityColumnName!).getRawData();
44
+ const scaledActivity = model.df.getCol(COLUMNS_NAMES.ACTIVITY_SCALED);
45
+ const dfLen = model.df.rowCount;
46
+
47
+ // Check initial 'none' scaling
48
+ let scaledActivityData = scaledActivity.getRawData();
49
+ for (let i = 0; i < dfLen; i++)
50
+ expectFloat(scaledActivityData[i], origActivityData[i], tolerance, getError(i, SCALING_METHODS.NONE));
51
+
52
+ // Check 'lg' scaling
53
+ model.settings = {scaling: SCALING_METHODS.LG};
54
+ scaledActivityData = scaledActivity.getRawData();
55
+ for (let i = 0; i < dfLen; i++)
56
+ expectFloat(scaledActivityData[i], Math.log10(origActivityData[i]), tolerance, getError(i, SCALING_METHODS.LG));
57
+
58
+ // Check '-lg' scaling
59
+ model.settings = {scaling: SCALING_METHODS.MINUS_LG};
60
+ scaledActivityData = scaledActivity.getRawData();
61
+ for (let i = 0; i < dfLen; i++) {
62
+ expectFloat(scaledActivityData[i], -Math.log10(origActivityData[i]), tolerance,
63
+ getError(i, SCALING_METHODS.MINUS_LG));
64
+ }
65
+
66
+ // Check 'none' scaling
67
+ model.settings = {scaling: SCALING_METHODS.NONE};
68
+ scaledActivityData = scaledActivity.getRawData();
69
+ for (let i = 0; i < dfLen; i++)
70
+ expectFloat(scaledActivityData[i], origActivityData[i], tolerance, getError(i, SCALING_METHODS.NONE));
71
+ });
72
+
73
+ test('Bidirectional analysis', async () => {
74
+ // Check that bidirectional analysis is disabled by default
75
+ expect(model.settings.isBidirectional, false, 'Bidirectional analysis is enabled by default');
76
+
77
+ // Check that bidirectional analysis can be enabled
78
+ model.settings = {isBidirectional: true};
79
+ expect(model.settings.isBidirectional, true, 'Bidirectional analysis is disabled after enabling');
80
+
81
+ // Check that bidirectional analysis can be disabled
82
+ model.settings = {isBidirectional: false};
83
+ expect(model.settings.isBidirectional, false, 'Bidirectional analysis is enabled after disabling');
84
+ });
85
+
86
+ test('Mutation Cliffs', async () => {
87
+ // Check default mutation cliffs parameters
88
+ expect(model.settings.maxMutations, mutationCliffsDefaultParams.maxMutations, `Max mutations mismatch: expected ` +
89
+ `${mutationCliffsDefaultParams.maxMutations}, actual ${model.settings.maxMutations}`);
90
+ expect(model.settings.minActivityDelta, mutationCliffsDefaultParams.minActivityDelta, `Min activity delta ` +
91
+ `mismatch: expected ${mutationCliffsDefaultParams.minActivityDelta}, actual ${model.settings.minActivityDelta}`);
92
+
93
+ // Check test mutation cliffs parameters
94
+ model.settings = {maxMutations: mutationCliffsTestParams.maxMutations,
95
+ minActivityDelta: mutationCliffsTestParams.minActivityDelta};
96
+ expect(model.settings.maxMutations, mutationCliffsTestParams.maxMutations, `Max mutations mismatch: expected ` +
97
+ `${mutationCliffsTestParams.maxMutations}, actual ${model.settings.maxMutations}`);
98
+ expect(model.settings.minActivityDelta, mutationCliffsTestParams.minActivityDelta, `Min activity delta ` +
99
+ `mismatch: expected ${mutationCliffsTestParams.minActivityDelta}, actual ${model.settings.minActivityDelta}`);
100
+ });
101
+
102
+ test('Include columns', async () => {
103
+ const columnName = 'rank';
104
+ const testColumns = {[columnName]: DG.AGG.AVG};
105
+
106
+ // Include column
107
+ model.settings = {columns: testColumns};
108
+
109
+ expect(Object.keys(model.settings.columns!)[0], Object.keys(testColumns)[0], 'Expected to include column ' +
110
+ `'${Object.keys(testColumns)[0]}' but '${Object.keys(model.settings.columns!)[0]}' is included instead`);
111
+ expect(model.settings.columns![columnName], testColumns[columnName], `Expected to aggregate column ` +
112
+ `'${Object.keys(testColumns)[0]}' with '${testColumns[columnName]}' but aggregated with ` +
113
+ `'${model.settings.columns![columnName]}' instead`);
114
+
115
+ expect(model.analysisView.grid.col(columnName)?.visible, true, `Expected to show column '${columnName}' but ` +
116
+ `it is hidden`);
117
+
118
+ const lstViewer = model.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable;
119
+ const aggColName = getAggregatedColName(testColumns[columnName], columnName);
120
+ expect(lstViewer.viewerGrid.col(aggColName) !== null, true, `Expected to include column '${columnName}' in ` +
121
+ `${VIEWER_TYPE.LOGO_SUMMARY_TABLE} but it is absent`);
122
+
123
+ // Remove column
124
+ model.settings = {columns: {}};
125
+ expect(Object.keys(model.settings.columns!).length, 0,
126
+ `Expected to remove all column aggregations but columns {${Object.keys(model.settings.columns!).join(' & ')}} ` +
127
+ `are still included`);
128
+
129
+ expect(model.analysisView.grid.col(columnName)?.visible, false, `Expected to hide column '${columnName}' but ` +
130
+ `it is shown`);
131
+
132
+ expect(lstViewer.viewerGrid.col(aggColName) === null, true, `Expected to remove column '${columnName}' from ` +
133
+ `${VIEWER_TYPE.LOGO_SUMMARY_TABLE} but it is still present`);
134
+ });
135
+
136
+ test('Dendrogram', async () => {
137
+ // Enable dendrogram
138
+ model.settings = {showDendrogram: true};
139
+ expect(model.settings.showDendrogram, true, 'Dendrogram is disabled after enabling');
140
+
141
+ await delay(5000);
142
+
143
+ expect(model.findViewer(VIEWER_TYPE.DENDROGRAM) !== null, true,
144
+ 'Dendrogram is not present in the view after 5s delay');
145
+
146
+ // Disable dendrogram
147
+ model.settings = {showDendrogram: false};
148
+ expect(model.settings.showDendrogram, false, 'Dendrogram is enabled after disabling');
149
+ expect(model.findViewer(VIEWER_TYPE.DENDROGRAM) === null, true,
150
+ 'Dendrogram is present in the view after disabling');
151
+ }, {skipReason: 'Need to find a way to replace _package variable to call for Bio function with tests'});
152
+ });
@@ -1,4 +1,4 @@
1
- import {/*before, after, */after, category, test} from '@datagrok-libraries/utils/src/test';
1
+ import {category, test} from '@datagrok-libraries/utils/src/test';
2
2
  import * as utils from './utils';
3
3
  import {DimensionalityReducer} from '@datagrok-libraries/ml/src/reduce-dimensionality';
4
4
  import {cleanAlignedSequencesColumn} from '../utils/peptide-similarity-space';
@@ -7,7 +7,6 @@ import {aligned1} from './test-data';
7
7
  import * as DG from 'datagrok-api/dg';
8
8
  import * as grok from 'datagrok-api/grok';
9
9
  import {StringMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
10
- import {_package} from '../package-test';
11
10
 
12
11
  let table: DG.DataFrame;
13
12
  let view: DG.TableView;
@@ -0,0 +1,158 @@
1
+ import * as DG from 'datagrok-api/dg';
2
+
3
+ import {category, test, before, expect} from '@datagrok-libraries/utils/src/test';
4
+ import {_package} from '../package-test';
5
+ import {PeptidesModel} from '../model';
6
+ import {startAnalysis} from '../widgets/peptides';
7
+ import {scaleActivity} from '../utils/misc';
8
+ import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
9
+ import {COLUMNS_NAMES, SCALING_METHODS} from '../utils/constants';
10
+
11
+ category('Table view', () => {
12
+ let df: DG.DataFrame;
13
+ let model: PeptidesModel;
14
+ let activityCol: DG.Column<number>;
15
+ let sequenceCol: DG.Column<string>;
16
+ let clusterCol: DG.Column<any>;
17
+ let scaledActivityCol: DG.Column<number>;
18
+ const scaling = 'none' as SCALING_METHODS;
19
+
20
+ const firstMonomerPair = {monomer: 'N', position: '4', count: 7};
21
+ const secondMonomerPair = {monomer: 'meI', position: '1', count: 10};
22
+ const firstCluster = {name: '0', count: 3};
23
+ const secondCluster = {name: '1', count: 3};
24
+
25
+ before(async () => {
26
+ df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
27
+ activityCol = df.getCol('activity');
28
+ sequenceCol = df.getCol('sequence');
29
+ sequenceCol.semType = DG.SEMTYPE.MACROMOLECULE;
30
+ sequenceCol.setTag(DG.TAGS.UNITS, NOTATION.HELM);
31
+ scaledActivityCol = scaleActivity(activityCol, scaling);
32
+ clusterCol = df.getCol('cluster');
33
+ const tempModel = await startAnalysis(activityCol, sequenceCol, clusterCol, df, scaledActivityCol, scaling);
34
+ if (tempModel === null)
35
+ throw new Error('Model is null');
36
+ model = tempModel;
37
+ });
38
+
39
+ test('Tooltip', async () => {
40
+ expect(model.showMonomerTooltip(firstMonomerPair.monomer, 0, 0), true,
41
+ `Couldn't structure for monomer ${firstMonomerPair.monomer}`);
42
+ }, {skipReason: 'Need to find a way to replace _package variable to call for Bio function with tests'});
43
+
44
+ test('Visible columns', async () => {
45
+ const gridCols = model.analysisView.grid.columns;
46
+ const posCols = model.splitSeqDf.columns.names();
47
+ const visibleColumns = Object.keys(model.settings.columns ?? {});
48
+ for (let colIdx = 1; colIdx < gridCols.length; colIdx++) {
49
+ const col = gridCols.byIndex(colIdx)!;
50
+ const tableColName = col.column!.name;
51
+ expect(col.visible, posCols.includes(tableColName) || (tableColName === COLUMNS_NAMES.ACTIVITY_SCALED) ||
52
+ visibleColumns.includes(tableColName));
53
+ }
54
+ });
55
+
56
+ test('Selection', async () => {
57
+ const selection = model.df.selection;
58
+
59
+ for (const [position, selectedMonomers] of Object.entries(model.monomerPositionSelection))
60
+ expect(selectedMonomers.length, 0, `Selection is not empty for position ${position} after initialization`);
61
+
62
+ // Select first monomer-position pair
63
+ model.modifyMonomerPositionSelection(firstMonomerPair.monomer, firstMonomerPair.position, false);
64
+ expect(model.monomerPositionSelection[firstMonomerPair.position].includes(firstMonomerPair.monomer), true,
65
+ `Monomer ${firstMonomerPair.monomer} is not selected at position ${firstMonomerPair.position}`);
66
+ expect(selection.trueCount, firstMonomerPair.count, `Selection count is not equal to ${firstMonomerPair.count} ` +
67
+ `for monomer ${firstMonomerPair.monomer} at position ${firstMonomerPair.position}`);
68
+
69
+ // Select second monomer-position pair
70
+ model.modifyMonomerPositionSelection(secondMonomerPair.monomer, secondMonomerPair.position, false);
71
+ expect(model.monomerPositionSelection[secondMonomerPair.position].includes(secondMonomerPair.monomer), true,
72
+ `Monomer ${secondMonomerPair.monomer} is not selected at position ${secondMonomerPair.position}`);
73
+ expect(selection.trueCount, secondMonomerPair.count, `Selection count is not equal to ${secondMonomerPair.count} ` +
74
+ `for monomer ${secondMonomerPair.monomer} at position ${secondMonomerPair.position}`);
75
+
76
+ // Deselect second monomer-position pair
77
+ model.modifyMonomerPositionSelection(secondMonomerPair.monomer, secondMonomerPair.position, false);
78
+ expect(model.monomerPositionSelection[secondMonomerPair.position].includes(secondMonomerPair.monomer), false,
79
+ `Monomer ${secondMonomerPair.monomer} is still selected at position ${secondMonomerPair.position} after ` +
80
+ `deselection`);
81
+ expect(selection.trueCount, firstMonomerPair.count, `Selection count is not equal to ${firstMonomerPair.count} ` +
82
+ `for monomer ${firstMonomerPair.monomer} at position ${firstMonomerPair.position}`);
83
+
84
+ // Clear monomer-position selection
85
+ model.initMonomerPositionSelection({cleanInit: true});
86
+ for (const [position, selectedMonomers] of Object.entries(model.monomerPositionSelection)) {
87
+ expect(selectedMonomers.length, 0, `Selection is not empty for position ${position} after clearing ` +
88
+ `monomer-position selection`);
89
+ }
90
+ expect(selection.trueCount, 0, `Selection count is not equal to 0 after clearing monomer-position selection`);
91
+
92
+ // Select first cluster
93
+ model.modifyClusterSelection(firstCluster.name);
94
+ expect(model.clusterSelection.includes(firstCluster.name), true,
95
+ `Cluster ${firstCluster.name} is not selected`);
96
+ expect(selection.trueCount, firstCluster.count, `Selection count is not equal to ${firstCluster.count} for ` +
97
+ `cluster ${firstCluster.name}`);
98
+
99
+ // Select second cluster
100
+ model.modifyClusterSelection(secondCluster.name);
101
+ expect(model.clusterSelection.includes(secondCluster.name), true,
102
+ `Cluster ${secondCluster.name} is not selected`);
103
+ expect(selection.trueCount, firstCluster.count + secondCluster.count, `Selection count is not equal to ` +
104
+ `${firstCluster.count + secondCluster.count} for cluster ${firstCluster.name} and cluster ${secondCluster.name}`);
105
+
106
+ // Deselect first cluster
107
+ model.modifyClusterSelection(firstCluster.name);
108
+ expect(model.clusterSelection.includes(firstCluster.name), false,
109
+ `Cluster ${firstCluster.name} is still selected after deselection`);
110
+ expect(selection.trueCount, secondCluster.count, `Selection count is not equal to ${secondCluster.count} for ` +
111
+ `cluster ${secondCluster.name} after deselection of cluster ${firstCluster.name}`);
112
+
113
+ // Clear selection
114
+ model.initClusterSelection();
115
+ expect(model.clusterSelection.length, 0, `Selection is not empty after clearing cluster selection`);
116
+ expect(selection.trueCount, 0, `Selection count is not equal to 0 after clearing cluster selection`);
117
+ });
118
+
119
+ test('Filtering', async () => {
120
+ const filter = model.df.filter;
121
+
122
+ for (const [position, filteredMonomers] of Object.entries(model.monomerPositionFilter))
123
+ expect(filteredMonomers.length, 0, `Filter is not empty for position ${position} after initialization`);
124
+
125
+ // Filter by second monomer-position pair
126
+ model.modifyMonomerPositionSelection(secondMonomerPair.monomer, secondMonomerPair.position, true);
127
+ expect(model.monomerPositionFilter[secondMonomerPair.position].includes(secondMonomerPair.monomer), true,
128
+ `Monomer ${secondMonomerPair.monomer} is not filtered at position ${secondMonomerPair.position}`);
129
+ expect(filter.trueCount, secondMonomerPair.count, `Filter count is not equal to ${secondMonomerPair.count} ` +
130
+ `for monomer ${secondMonomerPair.monomer} at position ${secondMonomerPair.position}`);
131
+
132
+ // Filter by first monomer-position pair
133
+ model.modifyMonomerPositionSelection(firstMonomerPair.monomer, firstMonomerPair.position, true);
134
+ expect(model.monomerPositionFilter[firstMonomerPair.position].includes(firstMonomerPair.monomer), true,
135
+ `Monomer ${firstMonomerPair.monomer} is not filtered at position ${firstMonomerPair.position}`);
136
+ expect(filter.trueCount, firstMonomerPair.count, `Filter count is not equal to ${firstMonomerPair.count} ` +
137
+ `for monomer ${firstMonomerPair.monomer} at position ${firstMonomerPair.position}`);
138
+
139
+ // Deselect filter for second monomer-position pair
140
+ model.modifyMonomerPositionSelection(secondMonomerPair.monomer, secondMonomerPair.position, true);
141
+ expect(model.monomerPositionFilter[secondMonomerPair.position].includes(secondMonomerPair.monomer), false,
142
+ `Monomer ${secondMonomerPair.monomer} is still filtered at position ${secondMonomerPair.position} after ` +
143
+ `deselection`);
144
+ expect(filter.trueCount, firstMonomerPair.count, `Filter count is not equal to ${firstMonomerPair.count} ` +
145
+ `for monomer ${firstMonomerPair.monomer} at position ${firstMonomerPair.position} after deselection of ` +
146
+ `monomer ${secondMonomerPair.monomer} at position ${secondMonomerPair.position}`);
147
+
148
+ // Clear selection
149
+ model.initMonomerPositionFilter({cleanInit: true});
150
+ expect(filter.trueCount, df.rowCount, `Filter count is not equal to ${df.rowCount} after clearing ` +
151
+ `monomer-position filter`);
152
+
153
+ for (const [position, filteredMonomers] of Object.entries(model.monomerPositionFilter)) {
154
+ expect(filteredMonomers.length, 0, `Filter is not empty for position ${position} after clearing ` +
155
+ `monomer-position filter`);
156
+ }
157
+ });
158
+ });