@datagrok/peptides 1.27.4 → 1.27.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/package.json +3 -3
- package/src/demo/fasta.ts +1 -2
- package/src/package.g.ts +4 -4
- package/src/package.ts +5 -5
- package/src/viewers/position-statistics-viewer.ts +131 -41
- package/src/widgets/peptides.ts +11 -10
- package/test-console-output-1.log +71 -107
- package/test-record-1.mp4 +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/peptides",
|
|
3
3
|
"friendlyName": "Peptides",
|
|
4
|
-
"version": "1.27.
|
|
4
|
+
"version": "1.27.6",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Davit Rizhinashvili",
|
|
7
7
|
"email": "drizhinashvili@datagrok.ai"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"directory": "packages/Peptides"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@datagrok-libraries/bio": "^5.
|
|
16
|
+
"@datagrok-libraries/bio": "^5.63.2",
|
|
17
17
|
"@datagrok-libraries/math": "^1.2.6",
|
|
18
18
|
"@datagrok-libraries/ml": "^6.10.7",
|
|
19
19
|
"@datagrok-libraries/statistics": "^1.2.12",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@datagrok-libraries/utils": "^4.6.9",
|
|
22
22
|
"@webgpu/types": "^0.1.40",
|
|
23
23
|
"cash-dom": "^8.1.5",
|
|
24
|
-
"datagrok-api": "^1.
|
|
24
|
+
"datagrok-api": "^1.26.0",
|
|
25
25
|
"file-loader": "^6.2.0",
|
|
26
26
|
"rxjs": "^6.5.5",
|
|
27
27
|
"uuid": "^10.0.0",
|
package/src/demo/fasta.ts
CHANGED
|
@@ -7,7 +7,6 @@ import * as C from '../utils/constants';
|
|
|
7
7
|
import {scaleActivity} from '../utils/misc';
|
|
8
8
|
import {ALIGNMENT, ALPHABET, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
9
9
|
import {PeptidesModel} from '../model';
|
|
10
|
-
import {awaitCheck, delay} from '@datagrok-libraries/test/src/test';
|
|
11
10
|
import {MCLSettings} from '../utils/types';
|
|
12
11
|
|
|
13
12
|
export async function macromoleculeSarFastaDemoUI(): Promise<void> {
|
|
@@ -19,7 +18,7 @@ export async function macromoleculeSarFastaDemoUI(): Promise<void> {
|
|
|
19
18
|
if (!simpleTable.id)
|
|
20
19
|
simpleTable.id = `Simple-peptides-analysis-table-id-${Math.random()}-${Date.now()}`;
|
|
21
20
|
const view = grok.shell.addTableView(simpleTable);
|
|
22
|
-
await delay(50);
|
|
21
|
+
await DG.delay(50);
|
|
23
22
|
const grid = view.grid;
|
|
24
23
|
await new Promise((res) => {
|
|
25
24
|
let timer: any = null;
|
package/src/package.g.ts
CHANGED
|
@@ -14,8 +14,8 @@ export function Peptides() : any {
|
|
|
14
14
|
|
|
15
15
|
//name: Bio Peptides
|
|
16
16
|
//top-menu: Bio | Analyze | SAR...
|
|
17
|
-
export function peptidesDialog() : any {
|
|
18
|
-
return PackageFunctions.peptidesDialog();
|
|
17
|
+
export async function peptidesDialog() : Promise<any> {
|
|
18
|
+
return await PackageFunctions.peptidesDialog();
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
//input: viewer v
|
|
@@ -28,8 +28,8 @@ export async function testInitFunctionPeptides(v: any) : Promise<void> {
|
|
|
28
28
|
//input: column col { semType: Macromolecule }
|
|
29
29
|
//output: widget result
|
|
30
30
|
//meta.role: widgets,panel
|
|
31
|
-
export function peptidesPanel(col: DG.Column) : any {
|
|
32
|
-
return PackageFunctions.peptidesPanel(col);
|
|
31
|
+
export async function peptidesPanel(col: DG.Column) : Promise<any> {
|
|
32
|
+
return await PackageFunctions.peptidesPanel(col);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
//name: Sequence Variability Map
|
package/src/package.ts
CHANGED
|
@@ -128,7 +128,7 @@ export class PackageFunctions {
|
|
|
128
128
|
'name': 'Bio Peptides',
|
|
129
129
|
'outputs': [],
|
|
130
130
|
})
|
|
131
|
-
static peptidesDialog(): DG.Dialog | null {
|
|
131
|
+
static async peptidesDialog(): Promise<DG.Dialog | null> {
|
|
132
132
|
if (!grok.shell.t || !grok.shell.t.columns.bySemType('Macromolecule')?.length) {
|
|
133
133
|
grok.shell.warning('SAR Analysis requires an active table with Macromolecule column');
|
|
134
134
|
return null;
|
|
@@ -139,7 +139,7 @@ export class PackageFunctions {
|
|
|
139
139
|
return null;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
const analyzeObject = analyzePeptidesUI(grok.shell.t);
|
|
142
|
+
const analyzeObject = await analyzePeptidesUI(grok.shell.t);
|
|
143
143
|
const dialog = ui.dialog('Analyze Peptides').add(analyzeObject.host).onOK(async () => {
|
|
144
144
|
const startSuccess = analyzeObject.callback();
|
|
145
145
|
if (!startSuccess)
|
|
@@ -163,11 +163,11 @@ export class PackageFunctions {
|
|
|
163
163
|
meta: {role: 'widgets'},
|
|
164
164
|
tags: ['widgets', 'panel'],
|
|
165
165
|
})
|
|
166
|
-
static peptidesPanel(
|
|
167
|
-
@grok.decorators.param({'options': {'semType': 'Macromolecule'}}) col: DG.Column): DG.Widget {
|
|
166
|
+
static async peptidesPanel(
|
|
167
|
+
@grok.decorators.param({'options': {'semType': 'Macromolecule'}}) col: DG.Column): Promise<DG.Widget> {
|
|
168
168
|
if (!col.dataFrame || !DG.Utils.firstOrNull(col.dataFrame.columns.numerical))
|
|
169
169
|
return new DG.Widget(ui.divText('SAR Analysis requires an active table with at least one numerical column for activity'));
|
|
170
|
-
const analyzeObject = analyzePeptidesUI(col.dataFrame, col);
|
|
170
|
+
const analyzeObject = await analyzePeptidesUI(col.dataFrame, col);
|
|
171
171
|
return new DG.Widget(analyzeObject.host);
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -10,23 +10,18 @@ import {TAGS as bioTAGS, MONOMER_MOTIF_SPLITTER} from '@datagrok-libraries/bio/s
|
|
|
10
10
|
export const POSITION_HIDDEN_NAME = '~sequence_position_monomers';
|
|
11
11
|
|
|
12
12
|
export class SequencePositionStatsViewer extends DG.JsViewer {
|
|
13
|
-
public
|
|
13
|
+
public positions: string;
|
|
14
14
|
public sequenceColumnName: string;
|
|
15
15
|
private _positionColumn?: DG.Column;
|
|
16
16
|
private _boxPlotViewer?: DG.Viewer;
|
|
17
17
|
public valueColumnName: string;
|
|
18
|
-
public leftMotifLength: number = 0;
|
|
19
|
-
public rightMotifLength: number = 0;
|
|
20
18
|
public showPositionInfo: boolean = true;
|
|
21
19
|
constructor() {
|
|
22
20
|
super();
|
|
23
|
-
|
|
24
|
-
this.position = this.int('position', 1, {nullable: false, showSlider: false, min: 1});
|
|
21
|
+
this.positions = this.string('positions', '1', {description: 'Comma-separated sequence positions (1-based) to analyze'});
|
|
25
22
|
this.sequenceColumnName = this.column('sequence', {semType: DG.SEMTYPE.MACROMOLECULE, nullable: false});
|
|
26
23
|
this.valueColumnName = this.column('value', {columnTypeFilter: 'numerical', nullable: false});
|
|
27
|
-
this.
|
|
28
|
-
this.rightMotifLength = this.int('rightMotifLength', 0, {nullable: false, min: 0, max: 10});
|
|
29
|
-
this.showPositionInfo = this.bool('showPositionInfo', true, {nullable: false, defaultValue: true, description: 'Show position and overhangs info in the viewer header'});
|
|
24
|
+
this.showPositionInfo = this.bool('showPositionInfo', true, {nullable: false, defaultValue: true, description: 'Show position selector in the viewer header'});
|
|
30
25
|
this.subs.push(grok.events.onContextMenu.subscribe((e) => {
|
|
31
26
|
if (e.causedBy && e.causedBy.target && this._boxPlotViewer?.root.contains(e.causedBy.target)) {
|
|
32
27
|
e.causedBy.preventDefault();
|
|
@@ -48,6 +43,19 @@ export class SequencePositionStatsViewer extends DG.JsViewer {
|
|
|
48
43
|
return Math.max(1, position);
|
|
49
44
|
}
|
|
50
45
|
|
|
46
|
+
parsePositions(): number[] {
|
|
47
|
+
if (!this.positions) return [1];
|
|
48
|
+
const result = this.positions.split(',')
|
|
49
|
+
.map((s) => parseInt(s.trim()))
|
|
50
|
+
.filter((n) => !Number.isNaN(n) && n >= 1);
|
|
51
|
+
return [...new Set(result)].sort((a, b) => a - b);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private _setPositions(positions: number[]): void {
|
|
55
|
+
const unique = [...new Set(positions)].sort((a, b) => a - b);
|
|
56
|
+
this.getProperty('positions')!.set(this, unique.join(', '));
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
onTableAttached(): void {
|
|
52
60
|
super.onTableAttached();
|
|
53
61
|
if (this.dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE) == null) {
|
|
@@ -63,18 +71,19 @@ export class SequencePositionStatsViewer extends DG.JsViewer {
|
|
|
63
71
|
|
|
64
72
|
this.getProperty('sequenceColumnName')!.set(this, this.dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE)!.name);
|
|
65
73
|
this.getProperty('valueColumnName')!.set(this, wu(this.dataFrame.columns.numerical).next().value.name);
|
|
66
|
-
this.getProperty('
|
|
74
|
+
this.getProperty('positions')!.set(this, String(this.getPositionFromColumn()));
|
|
67
75
|
|
|
68
76
|
this.subs.push(DG.debounce(this.dataFrame.onMetadataChanged, 200).subscribe((_) => {
|
|
69
77
|
const curPosition = this.getPositionFromColumn();
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
const currentPositions = this.parsePositions();
|
|
79
|
+
if (!currentPositions.includes(curPosition))
|
|
80
|
+
this._setPositions([curPosition]);
|
|
72
81
|
}));
|
|
73
82
|
}
|
|
74
83
|
|
|
75
84
|
render(): void {
|
|
76
|
-
const
|
|
77
|
-
if (this.dataFrame == null || !this.sequenceColumnName ||
|
|
85
|
+
const positions = this.parsePositions();
|
|
86
|
+
if (this.dataFrame == null || !this.sequenceColumnName || !positions.length || !this._positionColumn || !this.valueColumnName)
|
|
78
87
|
return;
|
|
79
88
|
|
|
80
89
|
$(this.root).empty();
|
|
@@ -82,12 +91,9 @@ export class SequencePositionStatsViewer extends DG.JsViewer {
|
|
|
82
91
|
const seqHelper = PeptideUtils.getSeqHelper();
|
|
83
92
|
const sequenceColumn = this.dataFrame.col(this.sequenceColumnName)!;
|
|
84
93
|
const seqHandler = seqHelper.getSeqHandler(sequenceColumn);
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
const end = rightOverhang + position0Based;
|
|
89
|
-
const canonicals = Array.from({length: end - start + 1}).fill('')
|
|
90
|
-
.map((_, i) => seqHandler.getMonomersAtPosition(start + i, true));
|
|
94
|
+
const maxPos = seqHandler.maxLength;
|
|
95
|
+
|
|
96
|
+
const canonicals = positions.map((p) => seqHandler.getMonomersAtPosition(p - 1, true));
|
|
91
97
|
this._positionColumn.init((i) => canonicals.map((c) => c[i]).join(MONOMER_MOTIF_SPLITTER));
|
|
92
98
|
|
|
93
99
|
this._boxPlotViewer = this.dataFrame.plot.box({categoryColumnNames: [this._positionColumn.name], plotStyle: 'violin',
|
|
@@ -96,30 +102,10 @@ export class SequencePositionStatsViewer extends DG.JsViewer {
|
|
|
96
102
|
autoLayout: false, labelOrientation: 'Vert',
|
|
97
103
|
});
|
|
98
104
|
|
|
99
|
-
const leftOverhangInput = ui.input.int('Left Overhang', {value: leftOverhang, min: 0, max: 10, step: 1, showSlider: false, showPlusMinus: true,
|
|
100
|
-
onValueChanged: (v) => {
|
|
101
|
-
this.getProperty('leftMotifLength')!.set(this, leftOverhangInput.value);
|
|
102
|
-
}, tooltipText: 'Left overhang motif length from the selected position',
|
|
103
|
-
});
|
|
104
|
-
const rightOverhangInput = ui.input.int('Right Overhang', {value: rightOverhang, min: 0, max: 10, step: 1, showSlider: false, showPlusMinus: true,
|
|
105
|
-
onValueChanged: (v) => {
|
|
106
|
-
this.getProperty('rightMotifLength')!.set(this, rightOverhangInput.value);
|
|
107
|
-
}, tooltipText: 'Right overhang motif length from the selected position',
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const descriptionDiv = ui.divH([
|
|
111
|
-
leftOverhangInput.root, ui.h2(`${this.sequenceColumnName}: Position ${this.position}`),
|
|
112
|
-
rightOverhangInput.root,
|
|
113
|
-
], {style: {alignItems: 'center', justifyContent: 'space-around', width: '100%'}});
|
|
114
105
|
if (this.showPositionInfo) {
|
|
115
|
-
this.
|
|
116
|
-
|
|
117
|
-
rightOverhangInput.input.style.width = '20px';
|
|
106
|
+
const selectorDiv = this._renderPositionSelector(positions, maxPos);
|
|
107
|
+
this.root.appendChild(selectorDiv);
|
|
118
108
|
}
|
|
119
|
-
// setTimeout(() => {
|
|
120
|
-
// this._boxPlotViewer!.props.title = 'Sequence Position Statistics';
|
|
121
|
-
// this._boxPlotViewer!.props.description = `${this.sequenceColumnName}: Position ${this.position + 1}`;
|
|
122
|
-
// }, 200);
|
|
123
109
|
|
|
124
110
|
this._boxPlotViewer.props.statistics = ['min', 'max', 'avg', 'med', 'count'];
|
|
125
111
|
|
|
@@ -134,6 +120,110 @@ export class SequencePositionStatsViewer extends DG.JsViewer {
|
|
|
134
120
|
}));
|
|
135
121
|
}
|
|
136
122
|
|
|
123
|
+
private _renderPositionSelector(positions: number[], maxPos: number): HTMLElement {
|
|
124
|
+
const container = ui.divH([], {style: {alignItems: 'center', justifyContent: 'center', gap: '4px', flexWrap: 'wrap', padding: '4px 8px', width: '100%'}});
|
|
125
|
+
|
|
126
|
+
const label = ui.label(`${this.sequenceColumnName}:`);
|
|
127
|
+
label.style.fontWeight = 'bold';
|
|
128
|
+
label.style.marginRight = '4px';
|
|
129
|
+
container.appendChild(label);
|
|
130
|
+
|
|
131
|
+
for (const pos of positions)
|
|
132
|
+
container.appendChild(this._createChip(pos, positions, maxPos));
|
|
133
|
+
|
|
134
|
+
container.appendChild(this._createAddButton(container, positions, maxPos));
|
|
135
|
+
return container;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private _createChip(pos: number, allPositions: number[], maxPos: number): HTMLElement {
|
|
139
|
+
const isSingle = allPositions.length === 1;
|
|
140
|
+
const posLabel = document.createElement('span');
|
|
141
|
+
posLabel.textContent = String(pos);
|
|
142
|
+
posLabel.style.marginRight = '3px';
|
|
143
|
+
|
|
144
|
+
const actionIcon = isSingle ?
|
|
145
|
+
ui.iconFA('pencil', () => this._showInlineEdit(chip, pos, maxPos), 'Edit position') :
|
|
146
|
+
ui.iconFA('times', () => this._setPositions(allPositions.filter((p) => p !== pos)), 'Remove position');
|
|
147
|
+
actionIcon.style.cursor = 'pointer';
|
|
148
|
+
actionIcon.style.fontSize = '10px';
|
|
149
|
+
actionIcon.style.opacity = '0.6';
|
|
150
|
+
|
|
151
|
+
const chip = ui.divH([posLabel, actionIcon], {style: {
|
|
152
|
+
display: 'inline-flex', alignItems: 'center',
|
|
153
|
+
background: '#f0f0f0', borderRadius: '12px', padding: '2px 8px',
|
|
154
|
+
fontSize: '12px', cursor: 'default', border: '1px solid #d0d0d0',
|
|
155
|
+
}});
|
|
156
|
+
return chip;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private _showInlineEdit(chip: HTMLElement, currentPos: number, maxPos: number): void {
|
|
160
|
+
const input = document.createElement('input');
|
|
161
|
+
input.type = 'number';
|
|
162
|
+
input.min = '1';
|
|
163
|
+
input.max = String(maxPos);
|
|
164
|
+
input.value = String(currentPos);
|
|
165
|
+
input.style.cssText = 'width:45px;height:22px;font-size:12px;text-align:center;border-radius:12px;border:1px solid #aaa;outline:none;padding:0 4px;';
|
|
166
|
+
|
|
167
|
+
let handled = false;
|
|
168
|
+
const confirm = () => {
|
|
169
|
+
if (handled) return;
|
|
170
|
+
handled = true;
|
|
171
|
+
const val = parseInt(input.value);
|
|
172
|
+
const positions = this.parsePositions();
|
|
173
|
+
if (!Number.isNaN(val) && val >= 1 && val <= maxPos && val !== currentPos)
|
|
174
|
+
this._setPositions(positions.map((p) => p === currentPos ? val : p));
|
|
175
|
+
else
|
|
176
|
+
input.replaceWith(chip);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
input.addEventListener('keydown', (e) => {
|
|
180
|
+
if (e.key === 'Enter') confirm();
|
|
181
|
+
if (e.key === 'Escape') {handled = true; chip.parentElement?.replaceChild(chip, input);}
|
|
182
|
+
});
|
|
183
|
+
input.addEventListener('blur', confirm);
|
|
184
|
+
|
|
185
|
+
chip.parentElement?.replaceChild(input, chip);
|
|
186
|
+
input.focus();
|
|
187
|
+
input.select();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private _createAddButton(container: HTMLElement, positions: number[], maxPos: number): HTMLElement {
|
|
191
|
+
const addBtn = ui.iconFA('plus-circle', () => {
|
|
192
|
+
if (container.querySelector('.position-add-input'))
|
|
193
|
+
return;
|
|
194
|
+
const input = document.createElement('input');
|
|
195
|
+
input.type = 'number';
|
|
196
|
+
input.min = '1';
|
|
197
|
+
input.max = String(maxPos);
|
|
198
|
+
input.className = 'position-add-input';
|
|
199
|
+
input.style.cssText = 'width:45px;height:22px;font-size:12px;text-align:center;border-radius:12px;border:1px solid #aaa;outline:none;padding:0 4px;';
|
|
200
|
+
|
|
201
|
+
let handled = false;
|
|
202
|
+
const confirm = () => {
|
|
203
|
+
if (handled) return;
|
|
204
|
+
handled = true;
|
|
205
|
+
const val = parseInt(input.value);
|
|
206
|
+
if (!Number.isNaN(val) && val >= 1 && val <= maxPos && !positions.includes(val))
|
|
207
|
+
this._setPositions([...positions, val]);
|
|
208
|
+
else
|
|
209
|
+
input.remove();
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
input.addEventListener('keydown', (e) => {
|
|
213
|
+
if (e.key === 'Enter') confirm();
|
|
214
|
+
if (e.key === 'Escape') {handled = true; input.remove();}
|
|
215
|
+
});
|
|
216
|
+
input.addEventListener('blur', confirm);
|
|
217
|
+
|
|
218
|
+
container.insertBefore(input, addBtn);
|
|
219
|
+
input.focus();
|
|
220
|
+
}, 'Add position');
|
|
221
|
+
addBtn.style.cursor = 'pointer';
|
|
222
|
+
addBtn.style.fontSize = '14px';
|
|
223
|
+
addBtn.style.color = '#2083d5';
|
|
224
|
+
return addBtn;
|
|
225
|
+
}
|
|
226
|
+
|
|
137
227
|
onPropertyChanged(property: DG.Property | null): void {
|
|
138
228
|
if (this.dataFrame == null || !this.sequenceColumnName)
|
|
139
229
|
return;
|
package/src/widgets/peptides.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {ALIGNMENT, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/
|
|
|
12
12
|
import {ILogoSummaryTable, LogoSummaryTable} from '../viewers/logo-summary';
|
|
13
13
|
import {MmDistanceFunctionsNames} from '@datagrok-libraries/ml/src/macromolecule-distance-functions';
|
|
14
14
|
import {MCL_INPUTS} from './settings';
|
|
15
|
+
import {createSequenceColumnInput, ISequenceColumnInput} from '@datagrok-libraries/bio/src/utils/sequence-column-input';
|
|
15
16
|
//@ts-ignore
|
|
16
17
|
import '../styles.css';
|
|
17
18
|
export type DialogParameters = { host: HTMLElement, callback: () => Promise<boolean> };
|
|
@@ -22,10 +23,10 @@ export type DialogParameters = { host: HTMLElement, callback: () => Promise<bool
|
|
|
22
23
|
* @param [col] - Peptides column
|
|
23
24
|
* @return - UI host and analysis start callback
|
|
24
25
|
*/
|
|
25
|
-
export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): DialogParameters {
|
|
26
|
+
export async function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Promise<DialogParameters> {
|
|
26
27
|
const mclOptions = new type.MCLSettings();
|
|
27
28
|
const logoHost = ui.div();
|
|
28
|
-
let
|
|
29
|
+
let sequenceColInput: ISequenceColumnInput | null = null;
|
|
29
30
|
if (typeof col === 'undefined') {
|
|
30
31
|
// Building UI for starting analysis from dialog (top menu)
|
|
31
32
|
const potentialCol = DG.Utils.firstOrNull(
|
|
@@ -36,7 +37,7 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
|
|
|
36
37
|
grok.shell.info('Sequences column contains missing values. They will be ignored during analysis');
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
sequenceColInput = await createSequenceColumnInput('Sequence', {table: df, value: potentialCol, onValueChanged: (value) => {
|
|
40
41
|
$(logoHost).empty().append(ui.wait(async () => {
|
|
41
42
|
const viewer = await df.plot.fromType('WebLogo', {sequenceColumnName: value.name});
|
|
42
43
|
viewer.root.style.setProperty('height', '130px');
|
|
@@ -45,7 +46,7 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
|
|
|
45
46
|
if (value.stats.missingValueCount !== 0)
|
|
46
47
|
grok.shell.info('Sequences column contains missing values. They will be ignored during analysis');
|
|
47
48
|
}, filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE, nullable: false});
|
|
48
|
-
|
|
49
|
+
sequenceColInput.inputBase.setTooltip('Macromolecule column in FASTA, HELM or separated format');
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
let funcs = DG.Func.find({package: 'Bio', name: 'webLogoViewer'});
|
|
@@ -117,8 +118,8 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
|
|
|
117
118
|
|
|
118
119
|
|
|
119
120
|
const inputsList = [activityColumnChoice, activityScalingMethod, clustersColumnChoice, generateClustersInput];
|
|
120
|
-
if (
|
|
121
|
-
inputsList.splice(0, 0,
|
|
121
|
+
if (sequenceColInput !== null)
|
|
122
|
+
inputsList.splice(0, 0, sequenceColInput.inputBase);
|
|
122
123
|
|
|
123
124
|
// ### MCL INPUTS ###
|
|
124
125
|
const similarityThresholdInput = ui.input.float(MCL_INPUTS.THRESHOLD, {
|
|
@@ -188,7 +189,7 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
|
|
|
188
189
|
const bitsetChanged = df.filter.onChanged.subscribe(() => activityScalingMethodState());
|
|
189
190
|
|
|
190
191
|
const startAnalysisCallback = async (): Promise<boolean> => {
|
|
191
|
-
const sequencesCol = col ??
|
|
192
|
+
const sequencesCol = col ?? sequenceColInput!.value;
|
|
192
193
|
bitsetChanged.unsubscribe();
|
|
193
194
|
if (sequencesCol) {
|
|
194
195
|
const model = await startAnalysis(activityColumnChoice.value!, sequencesCol, clustersColumnChoice.value, df,
|
|
@@ -210,7 +211,7 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
|
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
$(logoHost).empty().append(ui.wait(async () => {
|
|
213
|
-
const viewer = await df.plot.fromType('WebLogo', {sequenceColumnName: col?.name ??
|
|
214
|
+
const viewer = await df.plot.fromType('WebLogo', {sequenceColumnName: col?.name ?? sequenceColInput!.value!.name});
|
|
214
215
|
viewer.root.style.setProperty('height', '130px');
|
|
215
216
|
return viewer.root;
|
|
216
217
|
}));
|
|
@@ -218,9 +219,9 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
|
|
|
218
219
|
const mainHost = ui.divV([
|
|
219
220
|
logoHost,
|
|
220
221
|
ui.splitH([
|
|
221
|
-
ui.splitV(inputElements),
|
|
222
|
+
ui.splitV(inputElements, {style: {flex: '0 1 auto'}}),
|
|
222
223
|
histogramHost,
|
|
223
|
-
], {style: {height: bottomHeight, minWidth: '
|
|
224
|
+
], {style: {height: bottomHeight, minWidth: '600px', maxWidth: '750px'}}),
|
|
224
225
|
mclInputsHost,
|
|
225
226
|
]);
|
|
226
227
|
return {host: mainHost, callback: startAnalysisCallback};
|