@datagrok/peptides 0.8.13 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +5 -2
- package/dist/package-test.js +1268 -1766
- package/dist/package.js +1097 -1622
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +120 -62
- package/package.json +13 -17
- package/package.png +0 -0
- package/src/model.ts +504 -448
- package/src/monomer-library.ts +31 -30
- package/src/package-test.ts +5 -6
- package/src/package.ts +52 -70
- package/src/tests/core.ts +67 -0
- package/src/tests/msa-tests.ts +3 -3
- package/src/tests/peptide-space-test.ts +65 -45
- package/src/tests/utils.ts +20 -50
- package/src/utils/cell-renderer.ts +25 -151
- package/src/utils/chem-palette.ts +3 -14
- package/src/utils/constants.ts +5 -0
- package/src/utils/filtering-statistics.ts +2 -2
- package/src/utils/misc.ts +29 -0
- package/src/utils/multiple-sequence-alignment.ts +5 -18
- package/src/utils/multivariate-analysis.ts +5 -8
- package/src/utils/peptide-similarity-space.ts +12 -9
- package/src/utils/types.ts +5 -2
- package/src/viewers/peptide-space-viewer.ts +67 -39
- package/src/viewers/sar-viewer.ts +34 -37
- package/src/viewers/stacked-barchart-viewer.ts +38 -61
- package/src/widgets/analyze-peptides.ts +53 -75
- package/src/widgets/distribution.ts +34 -18
- package/src/widgets/manual-alignment.ts +8 -12
- package/src/widgets/peptide-molecule.ts +48 -25
- package/src/widgets/subst-table.ts +53 -52
- package/src/workers/dimensionality-reducer.ts +8 -13
- package/{test-Peptides-f8114def7953-4bf59d70.html → test-Peptides-69a4761f6044-40ac3a0c.html} +2 -2
- package/src/peptides.ts +0 -327
- package/src/semantics.ts +0 -5
- package/src/tests/peptides-tests.ts +0 -60
- package/src/utils/SAR-multiple-filter.ts +0 -439
- package/src/utils/SAR-multiple-selection.ts +0 -177
- package/src/viewers/logo-viewer.ts +0 -195
package/src/utils/types.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import * as DG from 'datagrok-api/dg';
|
|
2
2
|
|
|
3
|
-
export type SubstitutionCases = {[aar: string]: number[][][]};
|
|
4
|
-
export type SubstitutionTooltips = { [aar: string]: {}[][]; };
|
|
5
3
|
export type DataFrameDict = {[key: string]: DG.DataFrame};
|
|
6
4
|
|
|
7
5
|
export namespace BarChart {
|
|
8
6
|
export type BarPart = {colName : string, aaName : string};
|
|
9
7
|
export type BarStatsObject = {name: string, count: number, selectedCount: number};
|
|
10
8
|
}
|
|
9
|
+
|
|
10
|
+
export type UTypedArray = Uint8Array | Uint16Array | Uint32Array;
|
|
11
|
+
//AAR: (Position: (index: indexList))
|
|
12
|
+
export type SubstitutionsInfo = Map<string, Map<string, Map<number, number[] | UTypedArray>>>;
|
|
13
|
+
export type SelectionObject = {[postiton: string]: string[]};
|
|
@@ -6,21 +6,21 @@ import $ from 'cash-dom';
|
|
|
6
6
|
|
|
7
7
|
import {getSequenceMolecularWeight} from '../utils/molecular-measure';
|
|
8
8
|
import {AlignedSequenceEncoder} from '@datagrok-libraries/bio/src/sequence-encoder';
|
|
9
|
-
import {
|
|
10
|
-
createDimensinalityReducingWorker,
|
|
9
|
+
import {createDimensinalityReducingWorker, IReduceDimensionalityResult,
|
|
11
10
|
} from '@datagrok-libraries/ml/src/workers/dimensionality-reducing-worker-creator';
|
|
12
11
|
import {StringMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
|
|
13
|
-
import {Coordinates} from '@datagrok-libraries/utils/src/type-declarations';
|
|
14
12
|
import * as C from '../utils/constants';
|
|
15
|
-
import {
|
|
13
|
+
import {PeptidesModel} from '../model';
|
|
16
14
|
|
|
17
15
|
export class PeptideSpaceViewer extends DG.JsViewer {
|
|
18
16
|
method: string;
|
|
19
17
|
measure: string;
|
|
20
18
|
cyclesCount: number;
|
|
21
|
-
// controller: PeptidesController | null = null;
|
|
22
19
|
customProperties = new Set(['method', 'measure', 'cyclesCount']);
|
|
23
20
|
isEmbeddingCreating: boolean = false;
|
|
21
|
+
model!: PeptidesModel;
|
|
22
|
+
//FIXME: even if the property stays the same, for some reason it's still triggering prop change
|
|
23
|
+
prevProps!: {'method': string, 'measure': string, 'cyclesCount': number};
|
|
24
24
|
|
|
25
25
|
constructor() {
|
|
26
26
|
super();
|
|
@@ -30,35 +30,60 @@ export class PeptideSpaceViewer extends DG.JsViewer {
|
|
|
30
30
|
const measureChoices = ['Levenshtein', 'Jaro-Winkler'];
|
|
31
31
|
this.measure = this.addProperty('measure', DG.TYPE.STRING, 'Levenshtein', {choices: measureChoices});
|
|
32
32
|
this.cyclesCount = this.addProperty('cyclesCount', DG.TYPE.INT, 100);
|
|
33
|
+
this.updatePrevProperties();
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
updatePrevProperties(): void {
|
|
37
|
+
this.prevProps = {'method': this.method, 'measure': this.measure, 'cyclesCount': this.cyclesCount};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async onTableAttached(): Promise<void> {
|
|
41
|
+
super.onTableAttached();
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
this.model = await PeptidesModel.getInstance(this.dataFrame);
|
|
39
44
|
|
|
40
|
-
await this.render(this.dataFrame
|
|
45
|
+
await this.render(!this.dataFrame.temp[C.EMBEDDING_STATUS]);
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
async onPropertyChanged(property: DG.Property | null) {
|
|
48
|
+
async onPropertyChanged(property: DG.Property | null): Promise<void> {
|
|
44
49
|
super.onPropertyChanged(property);
|
|
50
|
+
if (this.prevProps[property?.name as 'method' | 'measure' | 'cyclesCount' ?? ''] == property?.get(this))
|
|
51
|
+
return;
|
|
45
52
|
|
|
53
|
+
if (this.model)
|
|
54
|
+
await this.render(this.customProperties.has(property?.name ?? '') || !this.dataFrame.temp[C.EMBEDDING_STATUS]);
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
this.updatePrevProperties();
|
|
48
57
|
}
|
|
49
58
|
|
|
50
|
-
async render(computeData=false) {
|
|
51
|
-
if (computeData && !this.isEmbeddingCreating) {
|
|
59
|
+
async render(computeData=false): Promise<void> {
|
|
60
|
+
if (computeData && !this.isEmbeddingCreating && !this.model.isChangingEdfBitset) {
|
|
52
61
|
this.isEmbeddingCreating = true;
|
|
53
62
|
$(this.root).empty();
|
|
54
63
|
const viewerHost = ui.waitBox(async () => {
|
|
55
|
-
await computeWeights(this.dataFrame
|
|
64
|
+
const edf = await computeWeights(this.dataFrame, this.method, this.measure, this.cyclesCount);
|
|
65
|
+
this.dataFrame.temp[C.EMBEDDING_STATUS] = true;
|
|
66
|
+
this.model.edf = edf;
|
|
67
|
+
|
|
68
|
+
if (edf === null)
|
|
69
|
+
return ui.label('Could not compute embeddings');
|
|
70
|
+
|
|
71
|
+
const edfSelection = edf.selection;
|
|
72
|
+
edfSelection.copyFrom(this.model.getBiteset());
|
|
73
|
+
edfSelection.onChanged.subscribe(() => {
|
|
74
|
+
if (!this.model.isChangingEdfBitset)
|
|
75
|
+
this.model.fireBitsetChanged(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const colorCol = this.dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
|
|
79
|
+
edf.columns.add(colorCol);
|
|
56
80
|
|
|
57
81
|
const viewerOptions = {
|
|
58
|
-
x: '~X', y: '~Y', color:
|
|
82
|
+
x: '~X', y: '~Y', color: colorCol.name ?? '~MW', size: '~MW', title: 'Peptide Space',
|
|
59
83
|
showYSelector: false, showXSelector: false, showColorSelector: false, showSizeSelector: false,
|
|
84
|
+
zoomAndFilter: 'no action', axesFollowFilter: false,
|
|
60
85
|
};
|
|
61
|
-
const viewerRoot =
|
|
86
|
+
const viewerRoot = edf.plot.scatter(viewerOptions).root;
|
|
62
87
|
viewerRoot.style.width = 'auto';
|
|
63
88
|
this.isEmbeddingCreating = false;
|
|
64
89
|
viewerHost.style.paddingLeft = 'unset';
|
|
@@ -70,22 +95,24 @@ export class PeptideSpaceViewer extends DG.JsViewer {
|
|
|
70
95
|
}
|
|
71
96
|
}
|
|
72
97
|
|
|
98
|
+
//Do not accept table, only column
|
|
73
99
|
export async function computeWeights(
|
|
74
100
|
table: DG.DataFrame, method: string, measure: string, cyclesCount: number, col?: DG.Column,
|
|
75
|
-
): Promise<
|
|
101
|
+
): Promise<DG.DataFrame | null> {
|
|
76
102
|
const pi = DG.TaskBarProgressIndicator.create('Creating embedding...');
|
|
103
|
+
let edf: DG.DataFrame | null = null;
|
|
77
104
|
try {
|
|
78
105
|
const axesNames = ['~X', '~Y', '~MW'];
|
|
79
|
-
|
|
80
|
-
|
|
106
|
+
col ??= table.columns.bySemType(C.SEM_TYPES.ALIGNED_SEQUENCE)!;
|
|
107
|
+
const columnData = col.toList().map((v) => AlignedSequenceEncoder.clean(v));
|
|
81
108
|
|
|
82
|
-
const
|
|
83
|
-
{data: columnData, metric: measure as StringMetrics}, method, cyclesCount);
|
|
109
|
+
const reduceDimRes: IReduceDimensionalityResult = await createDimensinalityReducingWorker(
|
|
110
|
+
{data: columnData, metric: measure as StringMetrics}, method, {cycles: cyclesCount});
|
|
111
|
+
const embcols = reduceDimRes.embedding;
|
|
84
112
|
|
|
85
|
-
const columns = Array.from(
|
|
86
|
-
embcols as Coordinates, (v: Float32Array, k) => DG.Column.fromFloat32Array(axesNames[k], v));
|
|
113
|
+
const columns = Array.from(embcols, (v: Float32Array, k) => DG.Column.fromFloat32Array(axesNames[k], v));
|
|
87
114
|
|
|
88
|
-
function _getMW(sequences: string[]) {
|
|
115
|
+
function _getMW(sequences: string[]): Float32Array {
|
|
89
116
|
const mw: Float32Array = new Float32Array(sequences.length);
|
|
90
117
|
|
|
91
118
|
mw.map((_, index) => getSequenceMolecularWeight(sequences[index] ?? ''));
|
|
@@ -95,27 +122,28 @@ export async function computeWeights(
|
|
|
95
122
|
|
|
96
123
|
columns.push(DG.Column.fromFloat32Array('~MW', _getMW(columnData)));
|
|
97
124
|
|
|
98
|
-
|
|
125
|
+
edf = DG.DataFrame.fromColumns(columns);
|
|
99
126
|
|
|
100
127
|
// Add new axes.
|
|
101
|
-
for (const axis of axesNames) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
128
|
+
// for (const axis of axesNames) {
|
|
129
|
+
// const col = table.col(axis);
|
|
130
|
+
// const newCol = edf.getCol(axis);
|
|
131
|
+
|
|
132
|
+
// // if (col != null) {
|
|
133
|
+
// // for (let i = 0; i < newCol.length; ++i) {
|
|
134
|
+
// // const v = newCol.get(i);
|
|
135
|
+
// // table.set(axis, i, v);
|
|
136
|
+
// // }
|
|
137
|
+
// // } else
|
|
138
|
+
// // table.columns.insert(newCol);
|
|
139
|
+
// const columnList = table.columns;
|
|
140
|
+
// col !== null ? columnList.replace(col, newCol) : columnList.insert(newCol);
|
|
141
|
+
// }
|
|
115
142
|
} catch (error) {
|
|
116
143
|
grok.shell.error('Could not compute embeddings. See console for details.');
|
|
117
144
|
console.error(error);
|
|
118
145
|
} finally {
|
|
119
146
|
pi.close();
|
|
147
|
+
return edf;
|
|
120
148
|
}
|
|
121
149
|
}
|
|
@@ -3,8 +3,8 @@ import * as ui from 'datagrok-api/ui';
|
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import $ from 'cash-dom';
|
|
6
|
-
import {PeptidesController} from '../peptides';
|
|
7
6
|
import * as C from '../utils/constants';
|
|
7
|
+
import {PeptidesModel} from '../model';
|
|
8
8
|
|
|
9
9
|
let IS_PROPERTY_CHANGING = false;
|
|
10
10
|
|
|
@@ -12,11 +12,9 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
12
12
|
tempName!: string;
|
|
13
13
|
viewerGrid!: DG.Grid;
|
|
14
14
|
sourceGrid!: DG.Grid;
|
|
15
|
-
|
|
15
|
+
model!: PeptidesModel;
|
|
16
16
|
scaling: string;
|
|
17
|
-
// filterMode: boolean;
|
|
18
17
|
bidirectionalAnalysis: boolean;
|
|
19
|
-
// grouping: boolean;
|
|
20
18
|
showSubstitution: boolean;
|
|
21
19
|
maxSubstitutions: number;
|
|
22
20
|
activityLimit: number;
|
|
@@ -28,29 +26,25 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
28
26
|
super();
|
|
29
27
|
|
|
30
28
|
this.scaling = this.string('scaling', 'none', {choices: ['none', 'lg', '-lg']});
|
|
31
|
-
// this.filterMode = this.bool('filterMode', false);
|
|
32
29
|
this.bidirectionalAnalysis = this.bool('bidirectionalAnalysis', false);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.showSubstitution = this.bool('showSubstitution', false);
|
|
30
|
+
this.showSubstitution = this.bool('showSubstitution', true);
|
|
36
31
|
this.maxSubstitutions = this.int('maxSubstitutions', 1);
|
|
37
32
|
this.activityLimit = this.float('activityLimit', 2);
|
|
38
33
|
}
|
|
39
34
|
|
|
40
|
-
async onTableAttached() {
|
|
35
|
+
async onTableAttached(): Promise<void> {
|
|
41
36
|
super.onTableAttached();
|
|
42
37
|
this.dataFrame.temp[this.tempName] ??= this;
|
|
43
38
|
this.sourceGrid = this.view?.grid ?? (grok.shell.v as DG.TableView).grid;
|
|
44
|
-
this.
|
|
45
|
-
this.
|
|
39
|
+
this.model = await PeptidesModel.getInstance(this.dataFrame);
|
|
40
|
+
// this.model.init(this.dataFrame);
|
|
46
41
|
await this.requestDataUpdate();
|
|
47
|
-
|
|
48
|
-
// this.subs.push(this.controller.onGroupMappingChanged.subscribe(() => {this.render(true);}));
|
|
42
|
+
this.helpUrl = '/help/domains/bio/peptides.md';
|
|
49
43
|
}
|
|
50
44
|
|
|
51
|
-
detach() {this.subs.forEach((sub) => sub.unsubscribe());}
|
|
45
|
+
detach(): void {this.subs.forEach((sub) => sub.unsubscribe());}
|
|
52
46
|
|
|
53
|
-
render(refreshOnly = false) {
|
|
47
|
+
render(refreshOnly = false): void {
|
|
54
48
|
if (!this.initialized)
|
|
55
49
|
return;
|
|
56
50
|
if (!refreshOnly) {
|
|
@@ -62,24 +56,26 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
62
56
|
this.viewerGrid?.invalidate();
|
|
63
57
|
}
|
|
64
58
|
|
|
65
|
-
async requestDataUpdate() {
|
|
66
|
-
await this.
|
|
59
|
+
async requestDataUpdate(): Promise<void> {
|
|
60
|
+
await this.model.updateData(this.scaling, this.sourceGrid, this.bidirectionalAnalysis,
|
|
67
61
|
this.activityLimit, this.maxSubstitutions, this.showSubstitution);
|
|
68
62
|
}
|
|
69
63
|
|
|
70
|
-
async onPropertyChanged(property: DG.Property) {
|
|
64
|
+
async onPropertyChanged(property: DG.Property): Promise<void> {
|
|
71
65
|
super.onPropertyChanged(property);
|
|
72
66
|
this.dataFrame.tags[property.name] = `${property.get(this)}`;
|
|
73
67
|
if (!this.initialized || IS_PROPERTY_CHANGING)
|
|
74
68
|
return;
|
|
75
|
-
|
|
69
|
+
|
|
76
70
|
const propName = property.name;
|
|
77
71
|
|
|
78
72
|
if (propName === 'scaling' && typeof this.dataFrame !== 'undefined') {
|
|
79
|
-
const
|
|
73
|
+
const activityCol = this.dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY)!;
|
|
74
|
+
// const minActivity = this.dataFrame.getCol(C.COLUMNS_NAMES.ACTIVITY).stats.min;
|
|
75
|
+
const minActivity = activityCol.stats.min;
|
|
80
76
|
if (minActivity && minActivity <= 0 && this.scaling !== 'none') {
|
|
81
77
|
grok.shell.warning(`Could not apply ${this.scaling}: ` +
|
|
82
|
-
`activity column ${
|
|
78
|
+
`activity column ${activityCol.name} contains zero or negative values, falling back to 'none'.`);
|
|
83
79
|
property.set(this, 'none');
|
|
84
80
|
return;
|
|
85
81
|
}
|
|
@@ -101,15 +97,16 @@ export class SARViewer extends SARViewerBase {
|
|
|
101
97
|
_name = 'Structure-Activity Relationship';
|
|
102
98
|
tempName = 'sarViewer';
|
|
103
99
|
|
|
104
|
-
constructor() {
|
|
100
|
+
constructor() {super();}
|
|
105
101
|
|
|
106
|
-
get name() {return this._name;}
|
|
102
|
+
get name(): string {return this._name;}
|
|
107
103
|
|
|
108
|
-
async onTableAttached() {
|
|
104
|
+
async onTableAttached(): Promise<void> {
|
|
109
105
|
await super.onTableAttached();
|
|
110
|
-
this.viewerGrid = this.
|
|
106
|
+
this.viewerGrid = this.model._sarGrid;
|
|
107
|
+
this.dataFrame.temp['sarViewer'] = this;
|
|
111
108
|
|
|
112
|
-
this.subs.push(this.
|
|
109
|
+
this.subs.push(this.model.onSARGridChanged.subscribe((data) => {
|
|
113
110
|
this.viewerGrid = data;
|
|
114
111
|
this.render();
|
|
115
112
|
}));
|
|
@@ -118,22 +115,21 @@ export class SARViewer extends SARViewerBase {
|
|
|
118
115
|
this.render();
|
|
119
116
|
}
|
|
120
117
|
|
|
121
|
-
isInitialized() {
|
|
118
|
+
isInitialized(): DG.Grid {return this.model?._sarGrid;}
|
|
122
119
|
|
|
120
|
+
//1. debouncing in rxjs; 2. flags?
|
|
123
121
|
async onPropertyChanged(property: DG.Property): Promise<void> {
|
|
124
122
|
if (!this.isInitialized() || IS_PROPERTY_CHANGING)
|
|
125
123
|
return;
|
|
126
124
|
|
|
127
125
|
await super.onPropertyChanged(property);
|
|
128
126
|
IS_PROPERTY_CHANGING = true;
|
|
129
|
-
this.
|
|
127
|
+
this.model.syncProperties(true);
|
|
130
128
|
IS_PROPERTY_CHANGING = false;
|
|
131
129
|
}
|
|
132
130
|
}
|
|
133
131
|
|
|
134
|
-
/**
|
|
135
|
-
* Vertical structure activity relationship viewer.
|
|
136
|
-
*/
|
|
132
|
+
/** Vertical structure activity relationship viewer. */
|
|
137
133
|
export class SARViewerVertical extends SARViewerBase {
|
|
138
134
|
_name = 'Sequence-Activity relationship';
|
|
139
135
|
_titleHost = ui.divText('Most Potent Residues', {id: 'pep-viewer-title'});
|
|
@@ -143,13 +139,14 @@ export class SARViewerVertical extends SARViewerBase {
|
|
|
143
139
|
super();
|
|
144
140
|
}
|
|
145
141
|
|
|
146
|
-
get name() {return this._name;}
|
|
142
|
+
get name(): string {return this._name;}
|
|
147
143
|
|
|
148
|
-
async onTableAttached() {
|
|
144
|
+
async onTableAttached(): Promise<void> {
|
|
149
145
|
await super.onTableAttached();
|
|
150
|
-
this.viewerGrid = this.
|
|
146
|
+
this.viewerGrid = this.model._sarVGrid;
|
|
147
|
+
this.dataFrame.temp['sarViewerVertical'] = this;
|
|
151
148
|
|
|
152
|
-
this.subs.push(this.
|
|
149
|
+
this.subs.push(this.model.onSARVGridChanged.subscribe((data) => {
|
|
153
150
|
this.viewerGrid = data;
|
|
154
151
|
this.render();
|
|
155
152
|
}));
|
|
@@ -158,7 +155,7 @@ export class SARViewerVertical extends SARViewerBase {
|
|
|
158
155
|
this.render();
|
|
159
156
|
}
|
|
160
157
|
|
|
161
|
-
isInitialized() {
|
|
158
|
+
isInitialized(): DG.Grid {return this.model?._sarVGrid;}
|
|
162
159
|
|
|
163
160
|
async onPropertyChanged(property: DG.Property): Promise<void> {
|
|
164
161
|
if (!this.isInitialized() || IS_PROPERTY_CHANGING)
|
|
@@ -166,7 +163,7 @@ export class SARViewerVertical extends SARViewerBase {
|
|
|
166
163
|
|
|
167
164
|
await super.onPropertyChanged(property);
|
|
168
165
|
IS_PROPERTY_CHANGING = true;
|
|
169
|
-
this.
|
|
166
|
+
this.model.syncProperties(false);
|
|
170
167
|
IS_PROPERTY_CHANGING = false;
|
|
171
168
|
}
|
|
172
169
|
}
|
|
@@ -2,28 +2,21 @@ import * as DG from 'datagrok-api/dg';
|
|
|
2
2
|
import * as rxjs from 'rxjs';
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
import {MonomerLibrary} from '../monomer-library';
|
|
5
|
-
import {PeptidesController} from '../peptides';
|
|
6
5
|
|
|
7
6
|
import * as C from '../utils/constants';
|
|
8
7
|
import * as type from '../utils/types';
|
|
8
|
+
import {PeptidesModel} from '../model';
|
|
9
9
|
|
|
10
|
-
export function addViewerToHeader(grid: DG.Grid, barchart: StackedBarChart) {
|
|
10
|
+
export function addViewerToHeader(grid: DG.Grid, barchart: StackedBarChart): void {
|
|
11
11
|
if (grid.temp['containsBarchart'])
|
|
12
12
|
return;
|
|
13
|
-
|
|
14
|
-
const compareBarParts = (bar1: type.BarChart.BarPart | null, bar2: type.BarChart.BarPart | null) =>
|
|
15
|
-
bar1 && bar2 && bar1.aaName === bar2.aaName && bar1.colName === bar2.colName;
|
|
16
13
|
|
|
17
|
-
const eventAction = (
|
|
18
|
-
const cell = grid.hitTest(
|
|
14
|
+
const eventAction = (ev: MouseEvent): void => {
|
|
15
|
+
const cell = grid.hitTest(ev.offsetX, ev.offsetY);
|
|
19
16
|
if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.AMINO_ACIDS) {
|
|
20
|
-
const newBarPart = barchart.findAARandPosition(cell,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
barchart.isSameBarClicked = true;
|
|
24
|
-
else
|
|
25
|
-
barchart.currentBarPart = newBarPart;
|
|
26
|
-
barchart.requestAction(mouseMove);
|
|
17
|
+
const newBarPart = barchart.findAARandPosition(cell, ev);
|
|
18
|
+
barchart._currentBarPart = newBarPart;
|
|
19
|
+
barchart.requestAction(ev, newBarPart);
|
|
27
20
|
barchart.computeData();
|
|
28
21
|
}
|
|
29
22
|
};
|
|
@@ -45,10 +38,10 @@ export function addViewerToHeader(grid: DG.Grid, barchart: StackedBarChart) {
|
|
|
45
38
|
) {
|
|
46
39
|
if (!cell.isColHeader) {
|
|
47
40
|
const monomerLib = cell.cell.dataFrame.temp[MonomerLibrary.id];
|
|
48
|
-
|
|
49
|
-
} else if (barchart.
|
|
41
|
+
PeptidesModel.chemPalette.showTooltip(cell, x, y, monomerLib);
|
|
42
|
+
} else if (barchart._currentBarPart) {
|
|
50
43
|
let elements: HTMLElement[] = [];
|
|
51
|
-
elements = elements.concat([ui.divText(barchart.
|
|
44
|
+
elements = elements.concat([ui.divText(barchart._currentBarPart.aaName)]);
|
|
52
45
|
ui.tooltip.show(ui.divV(elements), x, y);
|
|
53
46
|
}
|
|
54
47
|
}
|
|
@@ -92,22 +85,14 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
92
85
|
barStats: {[Key: string]: type.BarChart.BarStatsObject[]} = {};
|
|
93
86
|
selected: type.BarChart.BarPart[] = [];
|
|
94
87
|
aggregatedSelectedTables: type.DataFrameDict = {};
|
|
95
|
-
|
|
96
|
-
isSameBarClicked: boolean = false;
|
|
97
|
-
_previousClickedBarPart: type.BarChart.BarPart | null = null;
|
|
88
|
+
model!: PeptidesModel;
|
|
98
89
|
|
|
99
90
|
constructor() {
|
|
100
91
|
super();
|
|
101
92
|
this.dataEmptyAA = this.string('dataEmptyAA', '-');
|
|
102
93
|
}
|
|
103
94
|
|
|
104
|
-
|
|
105
|
-
set currentBarPart(barPart: type.BarChart.BarPart | null) {
|
|
106
|
-
this._currentBarPart = barPart;
|
|
107
|
-
this.isSameBarClicked = false;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
init() {
|
|
95
|
+
init(): void {
|
|
111
96
|
const groups: {[key: string]: string[]} = {
|
|
112
97
|
'yellow': ['C', 'U'],
|
|
113
98
|
'red': ['G', 'P'],
|
|
@@ -127,10 +112,10 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
127
112
|
}
|
|
128
113
|
|
|
129
114
|
// Stream subscriptions
|
|
130
|
-
async onTableAttached() {
|
|
115
|
+
async onTableAttached(): Promise<void> {
|
|
131
116
|
this.init();
|
|
132
|
-
this.
|
|
133
|
-
this.controller.init(this.dataFrame);
|
|
117
|
+
this.model = await PeptidesModel.getInstance(this.dataFrame);
|
|
118
|
+
// this.controller.init(this.dataFrame);
|
|
134
119
|
if (this.dataFrame) {
|
|
135
120
|
this.subs.push(DG.debounce(this.dataFrame.selection.onChanged, 50).subscribe((_) => this.computeData()));
|
|
136
121
|
this.subs.push(DG.debounce(this.dataFrame.filter.onChanged, 50).subscribe((_) => this.computeData()));
|
|
@@ -139,18 +124,18 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
139
124
|
}
|
|
140
125
|
|
|
141
126
|
// Cancel subscriptions when the viewer is detached
|
|
142
|
-
detach() {
|
|
127
|
+
detach(): void {
|
|
143
128
|
this.subs.forEach((sub) => sub.unsubscribe());
|
|
144
129
|
}
|
|
145
130
|
|
|
146
|
-
computeData() {
|
|
131
|
+
computeData(): void {
|
|
147
132
|
this.aminoColumnNames = [];
|
|
148
133
|
this.aminoColumnIndices = {};
|
|
149
134
|
|
|
150
|
-
this.dataFrame
|
|
151
|
-
if (this.dataFrame
|
|
152
|
-
!this.dataFrame
|
|
153
|
-
!this.dataFrame
|
|
135
|
+
this.dataFrame.columns.names().forEach((name: string) => {
|
|
136
|
+
if (this.dataFrame.getCol(name).semType === C.SEM_TYPES.AMINO_ACIDS &&
|
|
137
|
+
!this.dataFrame.getCol(name).categories.includes('COOH') &&
|
|
138
|
+
!this.dataFrame.getCol(name).categories.includes('NH2')) {
|
|
154
139
|
this.aminoColumnIndices[name] = this.aminoColumnNames.length + 1;
|
|
155
140
|
this.aminoColumnNames.push(name);
|
|
156
141
|
}
|
|
@@ -160,15 +145,15 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
160
145
|
this.aggregatedSelectedTables = {};
|
|
161
146
|
//TODO: optimize it, why store so many tables?
|
|
162
147
|
this.aminoColumnNames.forEach((name) => {
|
|
163
|
-
this.aggregatedFilterTables[name] = this.dataFrame
|
|
148
|
+
this.aggregatedFilterTables[name] = this.dataFrame
|
|
164
149
|
.groupBy([name])
|
|
165
|
-
.whereRowMask(this.dataFrame
|
|
150
|
+
.whereRowMask(this.dataFrame.filter)
|
|
166
151
|
.add('count', name, `${name}_count`)
|
|
167
152
|
.aggregate();
|
|
168
153
|
|
|
169
|
-
this.aggregatedSelectedTables[name] = this.dataFrame
|
|
154
|
+
this.aggregatedSelectedTables[name] = this.dataFrame
|
|
170
155
|
.groupBy([name])
|
|
171
|
-
.whereRowMask(this.dataFrame
|
|
156
|
+
.whereRowMask(this.dataFrame.selection)
|
|
172
157
|
.add('count', name, `${name}_count`)
|
|
173
158
|
.aggregate();
|
|
174
159
|
});
|
|
@@ -206,10 +191,10 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
206
191
|
colData.sort((o1, o2) => this.ord[o2['name']] - this.ord[o1['name']]);
|
|
207
192
|
}
|
|
208
193
|
|
|
209
|
-
this.max = this.dataFrame
|
|
194
|
+
this.max = this.dataFrame.filter.trueCount;
|
|
210
195
|
}
|
|
211
196
|
|
|
212
|
-
renderBarToCanvas(g: CanvasRenderingContext2D, cell: DG.GridCell, x: number, y: number, w: number, h: number) {
|
|
197
|
+
renderBarToCanvas(g: CanvasRenderingContext2D, cell: DG.GridCell, x: number, y: number, w: number, h: number): void {
|
|
213
198
|
const name = cell.tableColumn!.name;
|
|
214
199
|
const colNameSize = g.measureText(name).width;
|
|
215
200
|
const barData = this.barStats[name];
|
|
@@ -236,7 +221,7 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
236
221
|
const sBarHeight = h * obj['count'] / this.max;
|
|
237
222
|
const gapSize = sBarHeight * innerMargin;
|
|
238
223
|
const verticalShift = (this.max - sum) / this.max;
|
|
239
|
-
const [color, aarOuter] =
|
|
224
|
+
const [color, aarOuter] = PeptidesModel.chemPalette.getColorAAPivot(obj['name']);
|
|
240
225
|
const textSize = g.measureText(aarOuter);
|
|
241
226
|
const fontSize = 11;
|
|
242
227
|
const leftMargin = (w - (aarOuter.length > 1 ? fontSize : textSize.width - 8)) / 2;
|
|
@@ -252,7 +237,7 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
252
237
|
if (textSize.width <= subBartHeight) {
|
|
253
238
|
const origTransform = g.getTransform();
|
|
254
239
|
|
|
255
|
-
if (color !=
|
|
240
|
+
if (color != PeptidesModel.chemPalette.undefinedColor) {
|
|
256
241
|
g.fillRect(x + xStart, y + yStart, barWidth, subBartHeight);
|
|
257
242
|
g.fillStyle = 'black';
|
|
258
243
|
} else
|
|
@@ -287,7 +272,7 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
287
272
|
});
|
|
288
273
|
}
|
|
289
274
|
|
|
290
|
-
findAARandPosition(cell: DG.GridCell, mouseEvent: MouseEvent) {
|
|
275
|
+
findAARandPosition(cell: DG.GridCell, mouseEvent: MouseEvent): {colName: string, aaName: string} | null {
|
|
291
276
|
if (!cell.tableColumn?.name || !this.aminoColumnNames.includes(cell.tableColumn.name))
|
|
292
277
|
return null;
|
|
293
278
|
|
|
@@ -330,28 +315,20 @@ export class StackedBarChart extends DG.JsViewer {
|
|
|
330
315
|
return null;
|
|
331
316
|
}
|
|
332
317
|
|
|
333
|
-
unhighlight() {
|
|
318
|
+
unhighlight(): void {
|
|
334
319
|
ui.tooltip.hide();
|
|
335
320
|
this.computeData();
|
|
336
321
|
}
|
|
337
322
|
|
|
338
|
-
/**
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
* @returns
|
|
342
|
-
*/
|
|
343
|
-
requestAction(event: MouseEvent) {
|
|
344
|
-
if (!this._currentBarPart)
|
|
323
|
+
/** Requests highlight/select/filter action based on currentBarPart */
|
|
324
|
+
requestAction(event: MouseEvent, barPart: {colName: string, aaName: string} | null): void {
|
|
325
|
+
if (!barPart)
|
|
345
326
|
return;
|
|
346
|
-
|
|
347
|
-
|
|
327
|
+
const aar = barPart['aaName'];
|
|
328
|
+
const position = barPart['colName'];
|
|
348
329
|
if (event.type === 'click') {
|
|
349
|
-
|
|
350
|
-
aar
|
|
351
|
-
this.currentBarPart = null;
|
|
352
|
-
}
|
|
353
|
-
this.controller.setSARGridCellAt(aar, position);
|
|
354
|
-
this._previousClickedBarPart = this._currentBarPart;
|
|
330
|
+
event.shiftKey ? this.model.modifyCurrentSelection(aar, position) :
|
|
331
|
+
this.model.initCurrentSelection(aar, position);
|
|
355
332
|
} else {
|
|
356
333
|
ui.tooltip.showRowGroup(this.dataFrame, (i) => {
|
|
357
334
|
const currentAAR = this.dataFrame.get(position, i);
|