@datagrok/bio 2.16.9 → 2.17.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/CHANGELOG.md +11 -0
- package/css/monomer-manager.css +4 -0
- package/dist/package-test.js +2 -2
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +2 -2
- package/dist/package.js.map +1 -1
- package/dockerfiles/container.json +6 -0
- package/files/tests/peptides_with_random_motif_1600.csv +1601 -0
- package/package.json +5 -5
- package/src/package.ts +6 -5
- package/src/tests/activity-cliffs-tests.ts +1 -1
- package/src/tests/msa-tests.ts +3 -5
- package/src/tests/pepsea-tests.ts +0 -5
- package/src/tests/renderers-test.ts +6 -1
- package/src/tests/sequence-space-test.ts +1 -1
- package/src/tests/substructure-filters-tests.ts +13 -5
- package/src/tests/utils.ts +0 -2
- package/src/utils/cell-renderer-custom.ts +3 -1
- package/src/utils/cell-renderer.ts +62 -14
- package/src/utils/monomer-lib/library-file-manager/style.css +2 -0
- package/src/utils/monomer-lib/library-file-manager/ui.ts +5 -2
- package/src/utils/monomer-lib/monomer-manager/duplicate-monomer-manager.ts +6 -6
- package/src/utils/monomer-lib/monomer-manager/monomer-manager.ts +32 -7
- package/src/utils/multiple-sequence-alignment-ui.ts +0 -3
- package/src/utils/pepsea.ts +0 -3
- package/src/utils/docker.ts +0 -52
package/package.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"name": "@datagrok/bio",
|
|
3
3
|
"friendlyName": "Bio",
|
|
4
4
|
"author": {
|
|
5
|
-
"name": "
|
|
6
|
-
"email": "
|
|
5
|
+
"name": "Leonid Stolbov",
|
|
6
|
+
"email": "lstolbov@datagrok.ai"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.
|
|
8
|
+
"version": "2.17.1",
|
|
9
9
|
"description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@biowasm/aioli": "^3.1.0",
|
|
40
|
-
"@datagrok-libraries/bio": "^5.45.
|
|
40
|
+
"@datagrok-libraries/bio": "^5.45.11",
|
|
41
41
|
"@datagrok-libraries/chem-meta": "^1.2.7",
|
|
42
42
|
"@datagrok-libraries/math": "^1.2.2",
|
|
43
43
|
"@datagrok-libraries/ml": "^6.7.4",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@datagrok-libraries/js-draw-lite": "^0.0.10",
|
|
63
63
|
"@datagrok/chem": "^1.12.3",
|
|
64
64
|
"@datagrok/dendrogram": "^1.2.33",
|
|
65
|
-
"@datagrok/helm": "^2.5.
|
|
65
|
+
"@datagrok/helm": "^2.5.10",
|
|
66
66
|
"@types/node": "^17.0.24",
|
|
67
67
|
"@types/wu": "^2.1.44",
|
|
68
68
|
"@typescript-eslint/eslint-plugin": "^8.8.1",
|
package/src/package.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable max-params */
|
|
2
|
+
/* eslint-disable max-len */
|
|
1
3
|
/* eslint max-lines: "off" */
|
|
2
4
|
import * as grok from 'datagrok-api/grok';
|
|
3
5
|
import * as ui from 'datagrok-api/ui';
|
|
@@ -9,7 +11,7 @@ import {getActivityCliffs} from '@datagrok-libraries/ml/src/viewers/activity-cli
|
|
|
9
11
|
import {MmDistanceFunctionsNames} from '@datagrok-libraries/ml/src/macromolecule-distance-functions';
|
|
10
12
|
import {BitArrayMetrics, KnownMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
|
|
11
13
|
import {NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
12
|
-
import {IMonomerLib
|
|
14
|
+
import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
|
|
13
15
|
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
14
16
|
import {FastaFileHandler} from '@datagrok-libraries/bio/src/utils/fasta-handler';
|
|
15
17
|
import {SCORE} from '@datagrok-libraries/bio/src/utils/macromolecule/scoring';
|
|
@@ -24,14 +26,13 @@ import {ITSNEOptions, IUMAPOptions} from '@datagrok-libraries/ml/src/multi-colum
|
|
|
24
26
|
import {generateLongSequence, generateLongSequence2} from '@datagrok-libraries/bio/src/utils/generator';
|
|
25
27
|
import {getUserLibSettings, setUserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
26
28
|
import {ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
|
|
27
|
-
import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
|
|
29
|
+
import {RDModule as _RDMoule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
|
|
28
30
|
import {getRdKitModule} from '@datagrok-libraries/bio/src/chem/rdkit-module';
|
|
29
31
|
import {ISeqHandler} from '@datagrok-libraries/bio/src/utils/macromolecule/seq-handler';
|
|
30
32
|
import {MmcrTemps} from '@datagrok-libraries/bio/src/utils/cell-renderer-consts';
|
|
31
33
|
|
|
32
34
|
import {getMacromoleculeColumns} from './utils/ui-utils';
|
|
33
35
|
import {MacromoleculeDifferenceCellRenderer, MacromoleculeSequenceCellRenderer,} from './utils/cell-renderer';
|
|
34
|
-
import {MacromoleculeCustomCellRenderer} from './utils/cell-renderer-custom';
|
|
35
36
|
import {VdRegionsViewer} from './viewers/vd-regions-viewer';
|
|
36
37
|
import {SequenceAlignment} from './seq_align';
|
|
37
38
|
import {getEncodedSeqSpaceCol} from './analysis/sequence-space';
|
|
@@ -318,7 +319,7 @@ export function SeqActivityCliffsEditor(call: DG.FuncCall) {
|
|
|
318
319
|
//meta.columnTags: quality=Macromolecule, units=custom
|
|
319
320
|
//output: grid_cell_renderer result
|
|
320
321
|
export function customSequenceCellRenderer(): DG.GridCellRenderer {
|
|
321
|
-
return new
|
|
322
|
+
return new MacromoleculeSequenceCellRenderer();
|
|
322
323
|
}
|
|
323
324
|
|
|
324
325
|
//name: fastaSequenceCellRenderer
|
|
@@ -764,7 +765,7 @@ export function convertDialog() {
|
|
|
764
765
|
export async function convertSeqNotation(sequence: string, targetNotation: NOTATION, separator?: string): Promise<string | undefined | null> {
|
|
765
766
|
try {
|
|
766
767
|
const col = DG.Column.fromStrings('sequence', [sequence]);
|
|
767
|
-
const
|
|
768
|
+
const _df = DG.DataFrame.fromColumns([col]);
|
|
768
769
|
const semType = await grok.functions.call('Bio:detectMacromolecule', {col: col});
|
|
769
770
|
if (semType)
|
|
770
771
|
col.semType = semType;
|
|
@@ -44,7 +44,7 @@ category('activityCliffs', async () => {
|
|
|
44
44
|
test('activityCliffsOpens', async () => {
|
|
45
45
|
const testData = !DG.Test.isInBenchmark ?
|
|
46
46
|
{fileName: 'tests/100_3_clustests.csv', tgt: {cliffCount: 3}} :
|
|
47
|
-
{fileName: 'tests/
|
|
47
|
+
{fileName: 'tests/peptides_with_random_motif_1600.csv', tgt: {cliffCount: 64}};
|
|
48
48
|
const actCliffsDf = await readDataframe(testData.fileName);
|
|
49
49
|
const actCliffsTableView = grok.shell.addTableView(actCliffsDf);
|
|
50
50
|
|
package/src/tests/msa-tests.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-lines-per-function */
|
|
1
2
|
import * as grok from 'datagrok-api/grok';
|
|
2
3
|
import * as ui from 'datagrok-api/ui';
|
|
3
4
|
import * as DG from 'datagrok-api/dg';
|
|
@@ -8,7 +9,6 @@ import {ISeqHelper, getSeqHelper} from '@datagrok-libraries/bio/src/utils/seq-he
|
|
|
8
9
|
|
|
9
10
|
import {runKalign} from '../utils/multiple-sequence-alignment';
|
|
10
11
|
import {multipleSequenceAlignmentUI} from '../utils/multiple-sequence-alignment-ui';
|
|
11
|
-
import {awaitContainerStart} from '../utils/docker';
|
|
12
12
|
|
|
13
13
|
category('MSA', async () => {
|
|
14
14
|
let seqHelper: ISeqHelper;
|
|
@@ -87,14 +87,12 @@ MWRSWYCKHPMWRSWYCKHPMWRSWYCKHPMWRSWYCKHPMWRSWYCKHPMWRSWYCKHPMWRSWYCKHPMWRSWYCKHP
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
test('isCorrectHelm', async () => {
|
|
90
|
-
await awaitContainerStart();
|
|
91
90
|
await _testMSAOnColumn(helmFromCsv, helmToCsv, NOTATION.HELM, NOTATION.SEPARATOR, undefined, 'mafft');
|
|
92
|
-
}, {timeout:
|
|
91
|
+
}, {timeout: 80000 /* docker */});
|
|
93
92
|
|
|
94
93
|
test('isCorrectHelmLong', async () => {
|
|
95
|
-
await awaitContainerStart();
|
|
96
94
|
await _testMSAOnColumn(longHelmFromCsv, longHelmToCsv, NOTATION.HELM, NOTATION.SEPARATOR, undefined, 'mafft');
|
|
97
|
-
}, {timeout:
|
|
95
|
+
}, {timeout: 80000 /* docker */});
|
|
98
96
|
|
|
99
97
|
test('isCorrectSeparator', async () => {
|
|
100
98
|
await _testMSAOnColumn(
|
|
@@ -4,7 +4,6 @@ import {before, category, expect, expectArray, test} from '@datagrok-libraries/u
|
|
|
4
4
|
import {runPepsea} from '../utils/pepsea';
|
|
5
5
|
import {TestLogger} from './utils/test-logger';
|
|
6
6
|
import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
|
|
7
|
-
import {awaitContainerStart} from '../utils/docker';
|
|
8
7
|
|
|
9
8
|
category('PepSeA', () => {
|
|
10
9
|
const testCsv = `HELM,MSA
|
|
@@ -30,10 +29,6 @@ category('PepSeA', () => {
|
|
|
30
29
|
`;
|
|
31
30
|
const pepseaErrorError: string = 'PepSeA error: The pair (*,M) couldn\'t be found in the substitution matrix';
|
|
32
31
|
|
|
33
|
-
before(async () => {
|
|
34
|
-
await awaitContainerStart();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
32
|
test('Basic alignment', async () => {
|
|
38
33
|
const df = DG.DataFrame.fromCsv(testCsv);
|
|
39
34
|
const resMsaCol = await runPepsea(df.getCol('HELM'), 'msa(HELM)');
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-lines-per-function */
|
|
1
2
|
import * as grok from 'datagrok-api/grok';
|
|
2
3
|
import * as DG from 'datagrok-api/dg';
|
|
3
4
|
import * as ui from 'datagrok-api/ui';
|
|
@@ -196,6 +197,8 @@ category('renderers', () => {
|
|
|
196
197
|
const df: DG.DataFrame = await grok.dapi.files.readCsv('System:AppData/Bio/samples/FASTA_PT.csv');
|
|
197
198
|
const view = grok.shell.addTableView(df);
|
|
198
199
|
await awaitGrid(view.grid);
|
|
200
|
+
await df.meta.detectSemanticTypes();
|
|
201
|
+
await grok.data.detectSemanticTypes(df);
|
|
199
202
|
|
|
200
203
|
const srcCol = df.getCol('sequence');
|
|
201
204
|
const sh = seqHelper.getSeqHandler(srcCol);
|
|
@@ -239,6 +242,8 @@ CTCGGCATGC,2,0
|
|
|
239
242
|
const df = DG.DataFrame.fromCsv(seqCoordsCsv);
|
|
240
243
|
df.currentRowIdx = 0;
|
|
241
244
|
const view = grok.shell.addTableView(df);
|
|
245
|
+
await df.meta.detectSemanticTypes();
|
|
246
|
+
await grok.data.detectSemanticTypes(df);
|
|
242
247
|
const sp: DG.ScatterPlotViewer = df.plot.scatter({x: 'x', y: 'y'});
|
|
243
248
|
view.dockManager.dock(sp, DG.DOCK_TYPE.RIGHT, null);
|
|
244
249
|
await Promise.all([
|
|
@@ -253,7 +258,7 @@ CTCGGCATGC,2,0
|
|
|
253
258
|
clientX: spBcr.left + wp.x, clientY: spBcr.top + wp.y
|
|
254
259
|
});
|
|
255
260
|
const spCanvas = $(sp.root).find('canvas').get()[0] as HTMLCanvasElement;
|
|
256
|
-
await testEvent(fromEvent(spCanvas, 'mousemove'), () => {
|
|
261
|
+
await testEvent(DG.debounce(fromEvent(spCanvas, 'mousemove'), 200), () => {
|
|
257
262
|
_package.logger.debug(`Test: event, currentRowIdx=${df.currentRowIdx}`);
|
|
258
263
|
expect($(ui.tooltip.root).find('div table.d4-row-tooltip-table tr td canvas').length, 1);
|
|
259
264
|
expect(sp.hitTest(wp.x, wp.y), 1);
|
|
@@ -16,7 +16,7 @@ category('sequenceSpace', async () => {
|
|
|
16
16
|
test('sequenceSpaceOpens', async () => {
|
|
17
17
|
const testData = !DG.Test.isInBenchmark ?
|
|
18
18
|
{fileName: 'tests/100_3_clustests.csv'} :
|
|
19
|
-
{fileName: 'tests/
|
|
19
|
+
{fileName: 'tests/peptides_with_random_motif_1600.csv'};
|
|
20
20
|
testFastaDf = await readDataframe(testData.fileName);
|
|
21
21
|
testFastaTableView = grok.shell.addTableView(testFastaDf);
|
|
22
22
|
await _testSequenceSpaceReturnsResult(testFastaDf, DimReductionMethods.UMAP, 'sequence');
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
2
|
+
/* eslint-disable max-lines-per-function */
|
|
1
3
|
import * as grok from 'datagrok-api/grok';
|
|
2
4
|
import * as ui from 'datagrok-api/ui';
|
|
3
5
|
import * as DG from 'datagrok-api/dg';
|
|
@@ -152,8 +154,9 @@ category('bio-substructure-filters', async () => {
|
|
|
152
154
|
test('helm-dialog', async () => {
|
|
153
155
|
const logPrefix = 'Bio tests: substructureFilters/helm-dialog';
|
|
154
156
|
const df = await readDataframe('tests/filter_HELM.csv');
|
|
155
|
-
await grok.data.detectSemanticTypes(df);
|
|
156
157
|
const view = grok.shell.addTableView(df);
|
|
158
|
+
await grok.data.detectSemanticTypes(df);
|
|
159
|
+
await df.meta.detectSemanticTypes();
|
|
157
160
|
|
|
158
161
|
_package.logger.debug(`${logPrefix}, filter attaching.`);
|
|
159
162
|
const filter = new BioSubstructureFilter(seqHelper, _package.logger);
|
|
@@ -178,7 +181,9 @@ category('bio-substructure-filters', async () => {
|
|
|
178
181
|
await testEvent(df.onRowsFiltered, () => {}, () => {
|
|
179
182
|
bf.props = new BioFilterProps('PEPTIDE1{C}$$$$V2.0');
|
|
180
183
|
}, 20000);
|
|
184
|
+
setTimeout(() => view.grid.invalidate(), 500);
|
|
181
185
|
await awaitGrid(view.grid);
|
|
186
|
+
await delay(1000);
|
|
182
187
|
_package.logger.debug(`${logPrefix}, filter 2 changed.`);
|
|
183
188
|
expect(filter.dataFrame!.filter.trueCount, 2);
|
|
184
189
|
expect(filter.dataFrame!.filter.toBinaryString(), '1001');
|
|
@@ -187,7 +192,7 @@ category('bio-substructure-filters', async () => {
|
|
|
187
192
|
}
|
|
188
193
|
await filter.awaitRendered();
|
|
189
194
|
await delay(3000); //TODO: await for grid.onLookChanged
|
|
190
|
-
});
|
|
195
|
+
}, {});
|
|
191
196
|
|
|
192
197
|
// Generates unhandled exception accessing isFiltering before bioFilter created
|
|
193
198
|
test('helm-view', async () => {
|
|
@@ -398,8 +403,9 @@ category('bio-substructure-filters', async () => {
|
|
|
398
403
|
|
|
399
404
|
test('reset-fasta', async () => {
|
|
400
405
|
const df = await readDataframe('tests/filter_FASTA.csv');
|
|
401
|
-
await grok.data.detectSemanticTypes(df);
|
|
402
406
|
const view = grok.shell.addTableView(df);
|
|
407
|
+
await grok.data.detectSemanticTypes(df);
|
|
408
|
+
await df.meta.detectSemanticTypes();
|
|
403
409
|
|
|
404
410
|
const fSeqColName: string = 'fasta';
|
|
405
411
|
const fSubStr: string = 'MD';
|
|
@@ -432,6 +438,8 @@ category('bio-substructure-filters', async () => {
|
|
|
432
438
|
test('reopen', async () => {
|
|
433
439
|
const df = await _package.files.readCsv('tests/filter_FASTA.csv');
|
|
434
440
|
const view = grok.shell.addTableView(df);
|
|
441
|
+
await grok.data.detectSemanticTypes(df);
|
|
442
|
+
await df.meta.detectSemanticTypes();
|
|
435
443
|
|
|
436
444
|
const filterList = [{type: 'Bio:bioSubstructureFilter', columnName: 'fasta'}];
|
|
437
445
|
|
|
@@ -448,7 +456,7 @@ category('bio-substructure-filters', async () => {
|
|
|
448
456
|
const fg2Dn = view.dockManager.dock(fg2, DG.DOCK_TYPE.LEFT);
|
|
449
457
|
await delay(100);
|
|
450
458
|
await awaitGrid(view.grid);
|
|
451
|
-
});
|
|
459
|
+
}, {});
|
|
452
460
|
|
|
453
461
|
async function createFilter(colName: string, df: DG.DataFrame): Promise<BioSubstructureFilter> {
|
|
454
462
|
if (!df.columns.names().includes(colName)) {
|
|
@@ -464,5 +472,5 @@ category('bio-substructure-filters', async () => {
|
|
|
464
472
|
//filter.tableName = df.name;
|
|
465
473
|
return filter;
|
|
466
474
|
};
|
|
467
|
-
});
|
|
475
|
+
}, );
|
|
468
476
|
|
package/src/tests/utils.ts
CHANGED
|
@@ -4,8 +4,6 @@ import * as grok from 'datagrok-api/grok';
|
|
|
4
4
|
import {delay, expect, testEvent} from '@datagrok-libraries/utils/src/test';
|
|
5
5
|
import {asRenderer, IRenderer, isRenderer} from '@datagrok-libraries/bio/src/types/renderer';
|
|
6
6
|
|
|
7
|
-
import {startDockerContainer} from '../utils/docker';
|
|
8
|
-
|
|
9
7
|
import {_package} from '../package-test';
|
|
10
8
|
import {CellRendererBackBase, getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
11
9
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
1
2
|
import * as grok from 'datagrok-api/grok';
|
|
2
3
|
import * as DG from 'datagrok-api/dg';
|
|
3
4
|
import * as ui from 'datagrok-api/ui';
|
|
@@ -8,6 +9,7 @@ import {ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
|
|
|
8
9
|
|
|
9
10
|
import {_package} from '../package';
|
|
10
11
|
|
|
12
|
+
/// NB! UNUSED LEGACY CODE
|
|
11
13
|
export class MacromoleculeCustomCellRenderer extends DG.GridCellRenderer {
|
|
12
14
|
private readonly seqHelper: ISeqHelper;
|
|
13
15
|
|
|
@@ -41,7 +43,7 @@ export class MacromoleculeCustomCellRenderer extends DG.GridCellRenderer {
|
|
|
41
43
|
back?.onMouseEnter(gridCell, e);
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
override onMouseLeave(
|
|
46
|
+
override onMouseLeave(_gridCell: DG.GridCell, _e: MouseEvent) {
|
|
45
47
|
// TODO: We get gridCell from another column here, so we can not get back object from the column rendered.
|
|
46
48
|
ui.tooltip.hide();
|
|
47
49
|
// const back = this.getRendererBack(gridCell);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
1
2
|
/* eslint-disable max-lines */
|
|
2
3
|
/* eslint-disable max-params */
|
|
3
4
|
/* eslint-disable max-lines-per-function */
|
|
@@ -22,7 +23,7 @@ import {getSplitter} from '@datagrok-libraries/bio/src/utils/macromolecule/utils
|
|
|
22
23
|
import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types';
|
|
23
24
|
import {GapOriginals} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
24
25
|
import {execMonomerHoverLinks} from '@datagrok-libraries/bio/src/monomer-works/monomer-hover';
|
|
25
|
-
import {getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
26
|
+
import {CellRendererBackBase, getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
26
27
|
import {HelmTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
27
28
|
import {MmcrTemps, rendererSettingsChangedState, tempTAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer-consts';
|
|
28
29
|
|
|
@@ -53,6 +54,7 @@ export function processSequence(subParts: string[]): [string[], boolean] {
|
|
|
53
54
|
return [text, simplified];
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
56
58
|
type RendererGridCellTemp = {
|
|
57
59
|
[MmcrTemps.monomerPlacer]: MonomerPlacer
|
|
58
60
|
}
|
|
@@ -73,21 +75,58 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
73
75
|
this.seqHelper = _package.seqHelper;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
getRendererBack(gridCell: DG.GridCell): CellRendererBackBase<string> | null {
|
|
79
|
+
const [gridCol, tableCol, _temp] = getGridCellColTemp<string, any>(gridCell);
|
|
80
|
+
if (tableCol.meta.units !== NOTATION.CUSTOM)
|
|
81
|
+
return _temp.rendererBack;
|
|
82
|
+
let back: CellRendererBackBase<string> | null = null;
|
|
83
|
+
|
|
84
|
+
if (this.seqHelper) {
|
|
85
|
+
const sh = this.seqHelper.getSeqHandler(tableCol);
|
|
86
|
+
back = sh.getRendererBack(gridCol, tableCol);
|
|
87
|
+
}
|
|
88
|
+
return back;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
override onClick(gridCell: DG.GridCell, _e: MouseEvent): void {
|
|
77
92
|
const colTemp: TempType = gridCell.cell.column.temp;
|
|
78
93
|
colTemp[tempTAGS.currentWord] = gridCell.cell.value;
|
|
79
94
|
gridCell.grid.invalidate();
|
|
95
|
+
if (gridCell.cell.column.meta.units === NOTATION.CUSTOM) {
|
|
96
|
+
const back = this.getRendererBack(gridCell);
|
|
97
|
+
back?.onClick(gridCell, _e);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
override onMouseEnter(gridCell: DG.GridCell, e: MouseEvent) {
|
|
102
|
+
const back = this.getRendererBack(gridCell);
|
|
103
|
+
back?.onMouseEnter(gridCell, e);
|
|
80
104
|
}
|
|
81
105
|
|
|
82
106
|
override onMouseMove(gridCell: DG.GridCell, e: MouseEvent): void {
|
|
83
|
-
const
|
|
84
|
-
|
|
107
|
+
const back = this.getRendererBack(gridCell);
|
|
108
|
+
back?.onMouseMove(gridCell, e);
|
|
85
109
|
}
|
|
86
110
|
|
|
87
|
-
override onMouseLeave(gridCell: DG.GridCell,
|
|
111
|
+
override onMouseLeave(gridCell: DG.GridCell, _e: MouseEvent) {
|
|
88
112
|
execMonomerHoverLinks(gridCell, null);
|
|
89
113
|
}
|
|
90
114
|
|
|
115
|
+
override onDoubleClick(gridCell: DG.GridCell, e: MouseEvent) {
|
|
116
|
+
const back = this.getRendererBack(gridCell);
|
|
117
|
+
back?.onDoubleClick(gridCell, e);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
override onKeyDown(gridCell: DG.GridCell, e: KeyboardEvent) {
|
|
121
|
+
const back = this.getRendererBack(gridCell);
|
|
122
|
+
back?.onKeyDown(gridCell, e);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
override onKeyPress(gridCell: DG.GridCell, e: KeyboardEvent) {
|
|
126
|
+
const back = this.getRendererBack(gridCell);
|
|
127
|
+
back?.onKeyPress(gridCell, e);
|
|
128
|
+
}
|
|
129
|
+
|
|
91
130
|
/**
|
|
92
131
|
* Cell renderer function.
|
|
93
132
|
*
|
|
@@ -99,11 +138,11 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
99
138
|
* @param {DG.GridCell} gridCell Grid cell.
|
|
100
139
|
* @param {DG.GridCellStyle} _cellStyle Cell style.
|
|
101
140
|
*/
|
|
102
|
-
|
|
141
|
+
renderInt(
|
|
103
142
|
g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
|
|
104
143
|
_cellStyle: DG.GridCellStyle
|
|
105
144
|
): void {
|
|
106
|
-
const
|
|
145
|
+
const _logPrefix: string = 'MacromoleculeSequenceCellRenderer.render()';
|
|
107
146
|
|
|
108
147
|
const [gridCol, tableCol, temp] =
|
|
109
148
|
getGridCellColTemp<string, MonomerPlacer>(gridCell);
|
|
@@ -111,7 +150,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
111
150
|
const tableColTemp: TempType = tableCol.temp;
|
|
112
151
|
const sh = this.seqHelper.getSeqHandler(tableCol);
|
|
113
152
|
|
|
114
|
-
|
|
153
|
+
const gapLength = 0;
|
|
115
154
|
const msaGapLength = 8;
|
|
116
155
|
|
|
117
156
|
// Cell renderer settings
|
|
@@ -140,6 +179,15 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
140
179
|
|
|
141
180
|
seqColTemp.render(g, x, y, w, h, gridCell, _cellStyle);
|
|
142
181
|
}
|
|
182
|
+
|
|
183
|
+
override render(g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell, cellStyle: DG.GridCellStyle): void {
|
|
184
|
+
if (gridCell.cell.column?.meta?.units === NOTATION.CUSTOM) {
|
|
185
|
+
const back = this.getRendererBack(gridCell);
|
|
186
|
+
back?.render(g, x, y, w, h, gridCell, cellStyle);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this.renderInt(g, x, y, w, h, gridCell, cellStyle);
|
|
190
|
+
}
|
|
143
191
|
}
|
|
144
192
|
|
|
145
193
|
export class MacromoleculeDifferenceCellRendererBack extends CellRendererWithMonomerLibBackBase {
|
|
@@ -151,7 +199,7 @@ export class MacromoleculeDifferenceCellRendererBack extends CellRendererWithMon
|
|
|
151
199
|
}
|
|
152
200
|
|
|
153
201
|
render(g: CanvasRenderingContext2D,
|
|
154
|
-
x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
|
|
202
|
+
x: number, y: number, w: number, h: number, gridCell: DG.GridCell, _cellStyle: DG.GridCellStyle
|
|
155
203
|
): void {
|
|
156
204
|
const dpr = window.devicePixelRatio;
|
|
157
205
|
const grid = gridCell.grid;
|
|
@@ -172,7 +220,7 @@ export class MacromoleculeDifferenceCellRendererBack extends CellRendererWithMon
|
|
|
172
220
|
drawMoleculeDifferenceOnCanvas(g, x, y, w, h, subParts1, subParts2, biotype, this.monomerLib, undefined, undefined);
|
|
173
221
|
}
|
|
174
222
|
|
|
175
|
-
async awaitRendered(timeout: number = 10000,
|
|
223
|
+
async awaitRendered(timeout: number = 10000, _reason: string = `${timeout} timeout`): Promise<void> {
|
|
176
224
|
return Promise.resolve();
|
|
177
225
|
}
|
|
178
226
|
|
|
@@ -258,15 +306,15 @@ export function drawMoleculeDifferenceOnCanvas(
|
|
|
258
306
|
const amino2 = subParts2[i];
|
|
259
307
|
|
|
260
308
|
let color1 = undefinedColor;
|
|
261
|
-
if (monomerLib)
|
|
309
|
+
if (monomerLib)
|
|
262
310
|
color1 = monomerLib.getMonomerTextColor(biotype, amino1);
|
|
263
|
-
|
|
311
|
+
|
|
264
312
|
|
|
265
313
|
if (amino1 != amino2) {
|
|
266
314
|
let color2 = undefinedColor;
|
|
267
|
-
if (monomerLib)
|
|
315
|
+
if (monomerLib)
|
|
268
316
|
color2 = monomerLib.getMonomerTextColor(biotype, amino2);
|
|
269
|
-
|
|
317
|
+
|
|
270
318
|
const subX0 = printLeftOrCentered(g, amino1, updatedX, updatedY - vShift, w, h,
|
|
271
319
|
{color: color1, pivot: 0, left: true});
|
|
272
320
|
const subX1 = printLeftOrCentered(g, amino2, updatedX, updatedY + vShift, w, h,
|
|
@@ -77,6 +77,9 @@ class MonomerLibraryManagerWidget {
|
|
|
77
77
|
private async getWidgetContent(): Promise<HTMLElement> {
|
|
78
78
|
const libControlsForm = await LibraryControlsManager.createControlsForm();
|
|
79
79
|
$(libControlsForm).addClass('monomer-lib-controls-form');
|
|
80
|
+
setTimeout(() => {
|
|
81
|
+
libControlsForm && $(libControlsForm) && $(libControlsForm).removeClass('ui-form-condensed');
|
|
82
|
+
}, 200);
|
|
80
83
|
const widgetContent = ui.divV([libControlsForm]);
|
|
81
84
|
return widgetContent;
|
|
82
85
|
}
|
|
@@ -132,7 +135,7 @@ class LibraryControlsManager {
|
|
|
132
135
|
|
|
133
136
|
private _createControlsForm(): HTMLElement {
|
|
134
137
|
const libraryControls = this.createLibraryControls();
|
|
135
|
-
const inputsForm = ui.
|
|
138
|
+
const inputsForm = ui.wideForm(libraryControls, undefined);
|
|
136
139
|
$(inputsForm).addClass('monomer-lib-controls-form');
|
|
137
140
|
|
|
138
141
|
return inputsForm;
|
|
@@ -287,7 +290,7 @@ class LibManagerView {
|
|
|
287
290
|
const rightWidth = combinedWidth - leftWidth;
|
|
288
291
|
right.style.width = `${rightWidth}px`;
|
|
289
292
|
}, 100);
|
|
290
|
-
this._view.subs.push(grok.events.onCurrentViewChanged.subscribe(
|
|
293
|
+
this._view.subs.push(grok.events.onCurrentViewChanged.subscribe(() => {
|
|
291
294
|
try {
|
|
292
295
|
const inst = LibManagerView._instance;
|
|
293
296
|
if (inst && inst._view && 'id' in grok.shell.v && grok.shell.v.id === inst._view.id)
|
|
@@ -47,12 +47,12 @@ class MonomerCard {
|
|
|
47
47
|
|
|
48
48
|
class DuplicateSymbolRow {
|
|
49
49
|
root: HTMLElement = ui.divH([],
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
{style: {
|
|
51
|
+
alignItems: 'center',
|
|
52
|
+
width: '100%',
|
|
53
|
+
overflow: 'hidden',
|
|
54
|
+
visibility: 'visible',
|
|
55
|
+
}, classes: 'duplicate-monomer-symbol-row'}
|
|
56
56
|
);
|
|
57
57
|
monomerCards: MonomerCard[];
|
|
58
58
|
constructor(
|
|
@@ -102,7 +102,7 @@ export class MonomerManager implements IMonomerManager {
|
|
|
102
102
|
async createNewMonomerLib(libName: string, _monomers: Monomer[]): Promise<void> {
|
|
103
103
|
this.tv?.grid && ui.setUpdateIndicator(this.tv.grid.root, true);
|
|
104
104
|
try {
|
|
105
|
-
const monomersString = JSON.stringify(_monomers.map((m) => ({...m, lib: undefined, wem: undefined})));
|
|
105
|
+
const monomersString = JSON.stringify(_monomers.map((m) => ({...m, lib: undefined, wem: undefined})), null, 2);
|
|
106
106
|
if (!libName.endsWith('.json'))
|
|
107
107
|
libName += '.json';
|
|
108
108
|
await (await this.monomerLibManamger.getFileManager()).addLibraryFile(monomersString, libName);
|
|
@@ -214,13 +214,23 @@ export class MonomerManager implements IMonomerManager {
|
|
|
214
214
|
return tv ?? null;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
private _skipLibInputOnchange: boolean = false;
|
|
218
|
+
|
|
217
219
|
async getViewRoot(libName?: string) {
|
|
218
220
|
const availableMonLibs = (await this.monomerLibManamger.getFileManager()).getValidLibraryPaths();
|
|
219
221
|
this._newMonomerForm.molSketcher.resize();
|
|
220
222
|
if ((this.tv = this.findActiveManagerView()) && (libName ?? this.libInput.value)) {
|
|
221
223
|
// get monomer library list
|
|
222
|
-
|
|
223
|
-
|
|
224
|
+
try {
|
|
225
|
+
this._skipLibInputOnchange = true;
|
|
226
|
+
this.libInput && ((this.libInput as DG.ChoiceInput<string>).items = availableMonLibs);
|
|
227
|
+
libName && (this.libInput.value = libName);
|
|
228
|
+
} catch (e) {
|
|
229
|
+
grok.shell.error('Error updating library list');
|
|
230
|
+
console.error(e);
|
|
231
|
+
} finally {
|
|
232
|
+
this._skipLibInputOnchange = false;
|
|
233
|
+
}
|
|
224
234
|
const df = await this.getMonomersDf(libName);
|
|
225
235
|
this.tv.dataFrame = df;
|
|
226
236
|
this.adjustColWidths();
|
|
@@ -287,6 +297,7 @@ export class MonomerManager implements IMonomerManager {
|
|
|
287
297
|
this.tv.name = MonomerManager.VIEW_NAME;
|
|
288
298
|
this.libInput = ui.input.choice('Monomer Library', {value: libName, items: availableMonLibs, nullable: false, onValueChanged: async () => {
|
|
289
299
|
try {
|
|
300
|
+
if (this._skipLibInputOnchange) return;
|
|
290
301
|
const df = await this.getMonomersDf(this.libInput.value!);
|
|
291
302
|
this.tv!.dataFrame = df;
|
|
292
303
|
this.adjustColWidths();
|
|
@@ -553,6 +564,8 @@ class MonomerForm implements INewMonomerForm {
|
|
|
553
564
|
}
|
|
554
565
|
try {
|
|
555
566
|
this.rgroupsGridRoot.style.display = 'none';
|
|
567
|
+
const rGroupsPane = this.inputsTabControl.panes.find((p) => p.name?.toLowerCase() === 'r-groups');
|
|
568
|
+
rGroupsPane && (rGroupsPane.header.style.removeProperty('background-color'));
|
|
556
569
|
let smiles = this.molSketcher.getSmiles();
|
|
557
570
|
if (!smiles) {
|
|
558
571
|
this.rgroupsGrid.items = [];
|
|
@@ -568,9 +581,21 @@ class MonomerForm implements INewMonomerForm {
|
|
|
568
581
|
this.rgroupsGrid.items = [];
|
|
569
582
|
this.rgroupsGrid.render();
|
|
570
583
|
this.saveValidationResult = 'At least one R-group is required';
|
|
584
|
+
rGroupsPane && (rGroupsPane.header.style.setProperty('background-color', '#ff000030'));
|
|
571
585
|
this.invalidateSaveButton();
|
|
572
586
|
return;
|
|
573
587
|
}
|
|
588
|
+
|
|
589
|
+
// check for duplicate r-groups
|
|
590
|
+
const rGroupsSet = new Set(rGroupMatches.map((match) => match[0]));
|
|
591
|
+
if (rGroupsSet.size !== rGroupMatches.length) {
|
|
592
|
+
this.saveValidationResult = 'Duplicate R-groups are not allowed';
|
|
593
|
+
this.rgroupsGridRoot.style.display = 'flex';
|
|
594
|
+
rGroupsPane && (rGroupsPane.header.style.setProperty('background-color', '#ff000030'));
|
|
595
|
+
this.invalidateSaveButton();
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
|
|
574
599
|
const rGroupNums = rGroupMatches.map((match) => Number.parseInt(match[0].match(/[1-9]/g)![0]));
|
|
575
600
|
const rGroupItems: RGroup[] = rGroupNums.map((num) => {
|
|
576
601
|
const existingRGroup = this.rgroupsGrid.items.find((rg) => rg[HELM_RGROUP_FIELDS.LABEL] === `R${num}`) as RGroup | undefined;
|
|
@@ -581,8 +606,8 @@ class MonomerForm implements INewMonomerForm {
|
|
|
581
606
|
[HELM_RGROUP_FIELDS.LABEL]: `R${num}`,
|
|
582
607
|
} as unknown as RGroup;
|
|
583
608
|
});
|
|
584
|
-
if (this.rgroupsGrid.items.length !== rGroupItems.length)
|
|
585
|
-
|
|
609
|
+
// if (this.rgroupsGrid.items.length !== rGroupItems.length)
|
|
610
|
+
this.rgroupsGrid.items = rGroupItems.sort((a, b) => a.label.localeCompare(b.label));
|
|
586
611
|
this.rgroupsGrid.render();
|
|
587
612
|
this.rgroupsGridRoot.style.display = 'flex';
|
|
588
613
|
const mostSimilar = await mostSimilarNaturalAnalog(capSmiles(smiles, rGroupItems), this.polymerTypeInput.value ?? '');
|
|
@@ -830,7 +855,7 @@ class MonomerForm implements INewMonomerForm {
|
|
|
830
855
|
.add(infoTables)
|
|
831
856
|
.addButton('Remove', async () => {
|
|
832
857
|
libJSON = libJSON.filter((m) => !removingMonomers.includes(m));
|
|
833
|
-
await grok.dapi.files.writeAsText(LIB_PATH + libName, JSON.stringify(libJSON));
|
|
858
|
+
await grok.dapi.files.writeAsText(LIB_PATH + libName, JSON.stringify(libJSON, null, 2));
|
|
834
859
|
await (await MonomerLibManager.getInstance()).loadLibraries(true);
|
|
835
860
|
await this.refreshTable();
|
|
836
861
|
|
|
@@ -865,7 +890,7 @@ class MonomerForm implements INewMonomerForm {
|
|
|
865
890
|
else
|
|
866
891
|
libJSON.push({...monomer, lib: undefined, wem: undefined});
|
|
867
892
|
|
|
868
|
-
await grok.dapi.files.writeAsText(LIB_PATH + libName, JSON.stringify(libJSON));
|
|
893
|
+
await grok.dapi.files.writeAsText(LIB_PATH + libName, JSON.stringify(libJSON, null, 2));
|
|
869
894
|
await (await MonomerLibManager.getInstance()).loadLibraries(true);
|
|
870
895
|
await this.refreshTable(monomer.symbol);
|
|
871
896
|
this._molChanged = false; // reset the flag
|
|
@@ -12,7 +12,6 @@ import {pepseaMethods, runPepsea} from './pepsea';
|
|
|
12
12
|
import {checkInputColumn} from './check-input-column';
|
|
13
13
|
import {MultipleSequenceAlignmentUIOptions} from './types';
|
|
14
14
|
import {kalignVersion, msaDefaultOptions} from './constants';
|
|
15
|
-
import {awaitContainerStart} from './docker';
|
|
16
15
|
|
|
17
16
|
import '../../css/msa.css';
|
|
18
17
|
import {_package} from '../package';
|
|
@@ -176,7 +175,6 @@ async function onColInputChange(
|
|
|
176
175
|
gapExtendInput.value ??= msaDefaultOptions.pepsea.gapExtend;
|
|
177
176
|
|
|
178
177
|
return async () => {
|
|
179
|
-
await awaitContainerStart();
|
|
180
178
|
return runPepsea(col, unusedName, methodInput.value!,
|
|
181
179
|
gapOpenInput.value!, gapExtendInput.value!, clustersColInput.value);
|
|
182
180
|
};
|
|
@@ -190,7 +188,6 @@ async function onColInputChange(
|
|
|
190
188
|
// convert to helm and assign alignment function to PepSea
|
|
191
189
|
|
|
192
190
|
return async () => {
|
|
193
|
-
await awaitContainerStart();
|
|
194
191
|
return runPepsea(helmCol, unusedName, methodInput.value!,
|
|
195
192
|
gapOpenInput.value!, gapExtendInput.value!, clustersColInput.value);
|
|
196
193
|
};
|
package/src/utils/pepsea.ts
CHANGED
|
@@ -49,9 +49,6 @@ export async function runPepsea(srcCol: DG.Column<string>, unUsedName: string,
|
|
|
49
49
|
clustersCol: DG.Column<string | number> | null = null, logger?: ILogger
|
|
50
50
|
): Promise<DG.Column<string>> {
|
|
51
51
|
const pepseaContainer = await Pepsea.getDockerContainer();
|
|
52
|
-
if (pepseaContainer.status !== 'started' && pepseaContainer.status !== 'checking')
|
|
53
|
-
throw new Error('PepSeA container has not started yet');
|
|
54
|
-
|
|
55
52
|
const peptideCount = srcCol.length;
|
|
56
53
|
clustersCol ??= DG.Column.int('Clusters', peptideCount).init(0);
|
|
57
54
|
if (clustersCol.type != DG.COLUMN_TYPE.STRING)
|