@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.
- package/.eslintrc.json +2 -2
- package/README.md +3 -3
- package/dist/563.js +2 -0
- package/dist/611.js +2 -0
- package/dist/802.js +2 -0
- package/dist/96.js +2 -0
- package/dist/package-test.js +2 -29778
- package/dist/package.js +2 -28285
- package/files/icons/logo-summary-viewer.svg +13 -0
- package/files/icons/peptide-sar-vertical-viewer.svg +13 -0
- package/files/icons/peptide-sar-viewer.svg +19 -0
- package/files/icons/peptide-space-viewer.svg +40 -0
- package/files/tests/HELM_small.csv +12 -0
- package/package.json +7 -8
- package/src/demo/fasta.ts +24 -0
- package/src/model.ts +381 -325
- package/src/package-test.ts +3 -0
- package/src/package.ts +54 -30
- package/src/tests/algorithms.ts +1 -1
- package/src/tests/core.ts +13 -8
- package/src/tests/model.ts +152 -0
- package/src/tests/peptide-space-test.ts +1 -2
- package/src/tests/table-view.ts +158 -0
- package/src/tests/viewers.ts +142 -4
- package/src/tests/widgets.ts +135 -0
- package/src/utils/algorithms.ts +2 -2
- package/src/utils/cell-renderer.ts +2 -2
- package/src/utils/constants.ts +8 -0
- package/src/utils/distance-matrix.worker.ts +16 -0
- package/src/utils/misc.ts +4 -4
- package/src/utils/peptide-similarity-space.ts +0 -1
- package/src/utils/statistics.ts +14 -10
- package/src/utils/types.ts +8 -4
- package/src/utils/worker-creator.ts +11 -0
- package/src/viewers/logo-summary.ts +246 -168
- package/src/viewers/peptide-space-viewer.ts +6 -6
- package/src/viewers/sar-viewer.ts +108 -110
- package/src/widgets/distribution.ts +95 -128
- package/src/widgets/mutation-cliffs.ts +2 -2
- package/src/widgets/peptides.ts +11 -3
- package/src/widgets/settings.ts +94 -24
- package/tsconfig.json +1 -1
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +0 -9077
package/src/package-test.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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:
|
|
105
|
-
//description: Peptides
|
|
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
|
|
121
|
+
export function monomerPosition(): MonomerPosition {
|
|
109
122
|
return new MonomerPosition();
|
|
110
123
|
}
|
|
111
124
|
|
|
112
|
-
//name:
|
|
113
|
-
//description: Peptides
|
|
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
|
|
130
|
+
export function mostPotentResidues(): MostPotentResiduesViewer {
|
|
117
131
|
return new MostPotentResiduesViewer();
|
|
118
132
|
}
|
|
119
133
|
|
|
120
|
-
//name:
|
|
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
|
|
124
|
-
return new
|
|
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
|
+
}
|
package/src/tests/algorithms.ts
CHANGED
|
@@ -26,7 +26,7 @@ category('Algorithms', () => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
test('MutationCliffs', async () => {
|
|
29
|
-
const substInfo: type.
|
|
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(
|
|
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.
|
|
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,
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 {
|
|
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
|
+
});
|