@datagrok/peptides 0.8.5 → 0.8.9

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.
@@ -3,8 +3,9 @@ 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
-
7
- import {model} from '../model';
6
+ import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
7
+ import {PeptidesController} from '../peptides';
8
+ // import {PeptidesModel} from '../model';
8
9
 
9
10
  /**
10
11
  * Structure-activity relationship viewer.
@@ -27,8 +28,11 @@ export class SARViewer extends DG.JsViewer {
27
28
  protected _initialBitset: DG.BitSet | null;
28
29
  protected viewerVGrid: DG.Grid | null;
29
30
  protected currentBitset: DG.BitSet | null;
30
- grouping: boolean;
31
- groupMapping: {[key: string]: string} | null;
31
+ protected grouping: boolean;
32
+ protected groupMapping: StringDictionary | null;
33
+ // model: PeptidesModel | null;
34
+ protected _name: string = 'Monomer-Positions';
35
+ protected controller: PeptidesController | null;
32
36
  // protected pValueThreshold: number;
33
37
  // protected amountOfBestAARs: number;
34
38
  // duplicatesHandingMethod: string;
@@ -50,6 +54,8 @@ export class SARViewer extends DG.JsViewer {
50
54
  this._initialBitset = null;
51
55
  this.viewGridInitialized = false;
52
56
  this.currentBitset = null;
57
+ // this.model = null;
58
+ this.controller = null;
53
59
 
54
60
  //TODO: find a way to restrict activityColumnName to accept only numerical columns (double even better)
55
61
  this.activityColumnName = this.string('activityColumnName');
@@ -57,47 +63,38 @@ export class SARViewer extends DG.JsViewer {
57
63
  this.filterMode = this.bool('filterMode', false);
58
64
  this.bidirectionalAnalysis = this.bool('bidirectionalAnalysis', false);
59
65
  this.grouping = this.bool('grouping', false);
60
- // this.pValueThreshold = this.float('pValueThreshold', 0.1);
61
- // this.amountOfBestAARs = this.int('amountOfBestAAR', 1);
62
- // this.duplicatesHandingMethod = this.string('duplicatesHandlingMethod', 'median', {choices: ['median']});
63
66
 
64
67
  this.sourceGrid = null;
65
68
  }
66
69
 
67
- /**
68
- * Initializes SARViewer.
69
- *
70
- * @memberof SARViewer
71
- */
70
+ get name() {
71
+ return this._name;
72
+ }
73
+
72
74
  init() {
73
75
  this._initialBitset = this.dataFrame!.filter.clone();
74
76
  this.currentBitset = this._initialBitset.clone();
75
77
  this.initialized = true;
76
- this.subs.push(model.statsDf$.subscribe((data) => this.statsDf = data));
77
- this.subs.push(model.viewerGrid$.subscribe((data) => {
78
+ }
79
+
80
+ async onTableAttached() {
81
+ this.sourceGrid = this.view?.grid ?? (grok.shell.v as DG.TableView).grid;
82
+ this.dataFrame?.setTag('dataType', 'peptides');
83
+ this.controller = PeptidesController.getInstance(this.dataFrame!);
84
+ // this.model = PeptidesModel.getOrInit(this.dataFrame!);
85
+ // this.model = this.controller.getOrInitModel();
86
+
87
+ this.subs.push(this.controller.onStatsDataFrameChanged.subscribe((data) => this.statsDf = data));
88
+ this.subs.push(this.controller.onSARGridChanged.subscribe((data) => {
78
89
  this.viewerGrid = data;
79
- this.render();
90
+ this.render(false);
80
91
  }));
81
- this.subs.push(model.viewerVGrid$.subscribe((data) => this.viewerVGrid = data));
82
- this.subs.push(model.groupMapping$.subscribe((data) => this.groupMapping = data));
83
- }
92
+ this.subs.push(this.controller.onSARVGridChanged.subscribe((data) => this.viewerVGrid = data));
93
+ this.subs.push(this.controller.onGroupMappingChanged.subscribe((data) => this.groupMapping = data));
84
94
 
85
- /**
86
- * Function that is executed when the table is attached.
87
- *
88
- * @memberof SARViewer
89
- */
90
- onTableAttached() {
91
- this.sourceGrid = this.view.grid;
92
- this.sourceGrid?.dataFrame?.setTag('dataType', 'peptides');
93
- this.render();
95
+ await this.render();
94
96
  }
95
97
 
96
- /**
97
- * Function that is executed when the viewer is detached from the table.
98
- *
99
- * @memberof SARViewer
100
- */
101
98
  detach() {
102
99
  this.subs.forEach((sub) => sub.unsubscribe());
103
100
  }
@@ -108,7 +105,7 @@ export class SARViewer extends DG.JsViewer {
108
105
  * @param {DG.Property} property New property.
109
106
  * @memberof SARViewer
110
107
  */
111
- onPropertyChanged(property: DG.Property) {
108
+ async onPropertyChanged(property: DG.Property) {
112
109
  super.onPropertyChanged(property);
113
110
 
114
111
  if (!this.initialized) {
@@ -129,7 +126,7 @@ export class SARViewer extends DG.JsViewer {
129
126
  }
130
127
  }
131
128
 
132
- this.render();
129
+ await this.render();
133
130
  }
134
131
 
135
132
  /**
@@ -139,45 +136,41 @@ export class SARViewer extends DG.JsViewer {
139
136
  * @memberof SARViewer
140
137
  */
141
138
  async render(computeData = true) {
142
- if (!this.initialized) {
139
+ if (!this.initialized)
143
140
  return;
144
- }
141
+
145
142
  //TODO: optimize. Don't calculate everything again if only view changes
146
- if (computeData) {
147
- if (typeof this.dataFrame !== 'undefined' && this.activityColumnName && this.sourceGrid) {
148
- await model?.updateData(
149
- this.dataFrame!,
150
- this.activityColumnName,
151
- this.scaling,
152
- this.sourceGrid,
153
- this.bidirectionalAnalysis,
154
- this._initialBitset,
155
- this.grouping,
156
- );
157
-
158
- if (this.viewerGrid !== null && this.viewerVGrid !== null) {
159
- $(this.root).empty();
160
- this.root.appendChild(this.viewerGrid.root);
161
- this.viewerGrid.dataFrame!.onCurrentCellChanged.subscribe((_) => {
162
- this.currentBitset = applyBitset(
163
- this.dataFrame!, this.viewerGrid!, this.aminoAcidResidue,
164
- this.groupMapping!, this._initialBitset!, this.filterMode,
165
- ) ?? this.currentBitset;
166
- syncGridsFunc(false, this.viewerGrid!, this.viewerVGrid!, this.aminoAcidResidue);
167
- });
168
- this.viewerVGrid.dataFrame!.onCurrentCellChanged.subscribe((_) => {
169
- syncGridsFunc(true, this.viewerGrid!, this.viewerVGrid!, this.aminoAcidResidue);
170
- });
171
- this.dataFrame!.onRowsFiltering.subscribe((_) => {
172
- sourceFilteringFunc(this.filterMode, this.dataFrame!, this.currentBitset!, this._initialBitset!);
173
- });
174
- grok.events.onAccordionConstructed.subscribe((accordion: DG.Accordion) => {
175
- accordionFunc(
176
- accordion, this.viewerGrid!, this.aminoAcidResidue,
177
- this._initialBitset!, this.activityColumnName, this.statsDf!,
178
- );
179
- });
180
- }
143
+ if (typeof this.dataFrame !== 'undefined' && this.activityColumnName && this.sourceGrid) {
144
+ if (computeData) {
145
+ await this.controller!.updateData(this.dataFrame, this.activityColumnName, this.scaling, this.sourceGrid,
146
+ this.bidirectionalAnalysis, this._initialBitset, this.grouping);
147
+ }
148
+
149
+ if (this.viewerGrid !== null && this.viewerVGrid !== null) {
150
+ $(this.root).empty();
151
+ const title = ui.h1(this._name, {style: {'align-self': 'center'}});
152
+ const gridRoot = this.viewerGrid.root;
153
+ gridRoot.style.width = 'auto';
154
+ this.root.appendChild(ui.divV([title, gridRoot]));
155
+ this.viewerGrid.dataFrame!.onCurrentCellChanged.subscribe((_) => {
156
+ this.currentBitset = applyBitset(
157
+ this.dataFrame!, this.viewerGrid!, this.aminoAcidResidue,
158
+ this.groupMapping!, this._initialBitset!, this.filterMode,
159
+ ) ?? this.currentBitset;
160
+ syncGridsFunc(false, this.viewerGrid!, this.viewerVGrid!, this.aminoAcidResidue);
161
+ });
162
+ this.viewerVGrid.dataFrame!.onCurrentCellChanged.subscribe((_) => {
163
+ syncGridsFunc(true, this.viewerGrid!, this.viewerVGrid!, this.aminoAcidResidue);
164
+ });
165
+ this.dataFrame.onRowsFiltering.subscribe((_) => {
166
+ sourceFilteringFunc(this.filterMode, this.dataFrame!, this.currentBitset!, this._initialBitset!);
167
+ });
168
+ grok.events.onAccordionConstructed.subscribe((accordion: DG.Accordion) => {
169
+ accordionFunc(
170
+ accordion, this.viewerGrid!, this.aminoAcidResidue,
171
+ this._initialBitset!, this.activityColumnName, this.statsDf!,
172
+ );
173
+ });
181
174
  }
182
175
  }
183
176
  //fixes viewers not rendering immediately after analyze.
@@ -194,27 +187,31 @@ export class SARViewer extends DG.JsViewer {
194
187
  */
195
188
  export class SARViewerVertical extends DG.JsViewer {
196
189
  viewerVGrid: DG.Grid | null;
190
+ // model: PeptidesModel | null;
191
+ protected _name = 'Sequence-Activity relationship';
192
+ controller: PeptidesController | null;
197
193
 
198
- /**
199
- * Creates an instance of SARViewerVertical.
200
- *
201
- * @memberof SARViewerVertical
202
- */
203
194
  constructor() {
204
195
  super();
205
196
 
206
197
  this.viewerVGrid = null;
207
- this.subs.push(model.viewerVGrid$.subscribe((data) => {
198
+ this.controller = null;
199
+ }
200
+
201
+ get name() {
202
+ return this._name;
203
+ }
204
+
205
+ onTableAttached(): void {
206
+ // this.model = PeptidesModel.getOrInit(this.dataFrame!);
207
+ this.controller = PeptidesController.getInstance(this.dataFrame!);
208
+
209
+ this.subs.push(this.controller.onSARVGridChanged.subscribe((data) => {
208
210
  this.viewerVGrid = data;
209
211
  this.render();
210
212
  }));
211
213
  }
212
214
 
213
- /**
214
- * Viewer render function.
215
- *
216
- * @memberof SARViewerVertical
217
- */
218
215
  render() {
219
216
  if (this.viewerVGrid) {
220
217
  $(this.root).empty();
@@ -224,19 +221,15 @@ export class SARViewerVertical extends DG.JsViewer {
224
221
  }
225
222
  }
226
223
 
227
- function syncGridsFunc(
228
- sourceVertical: boolean,
229
- viewerGrid: DG.Grid,
230
- viewerVGrid: DG.Grid,
231
- aminoAcidResidue: string,
232
- ) { //TODO: refactor, move
224
+ //TODO: refactor, move
225
+ function syncGridsFunc(sourceVertical: boolean, viewerGrid: DG.Grid, viewerVGrid: DG.Grid, aminoAcidResidue: string) {
233
226
  if (viewerGrid && viewerGrid.dataFrame && viewerVGrid && viewerVGrid.dataFrame) {
234
227
  if (sourceVertical) {
235
228
  const dfCell = viewerVGrid.dataFrame.currentCell;
236
- if (dfCell.column === null || dfCell.column.name !== 'Mean difference') {
229
+ if (dfCell.column === null || dfCell.column.name !== 'Diff')
237
230
  return;
238
- }
239
- const otherColName: string = viewerVGrid.dataFrame.get('Position', dfCell.rowIndex);
231
+
232
+ const otherColName: string = viewerVGrid.dataFrame.get('Pos', dfCell.rowIndex);
240
233
  const otherRowName: string = viewerVGrid.dataFrame.get(aminoAcidResidue, dfCell.rowIndex);
241
234
  let otherRowIndex = -1;
242
235
  for (let i = 0; i < viewerGrid.dataFrame.rowCount; i++) {
@@ -245,39 +238,33 @@ function syncGridsFunc(
245
238
  break;
246
239
  }
247
240
  }
248
- if (otherRowIndex !== -1) {
241
+ if (otherRowIndex !== -1)
249
242
  viewerGrid.dataFrame.currentCell = viewerGrid.dataFrame.cell(otherRowIndex, otherColName);
250
- }
251
243
  } else {
252
244
  const otherPos: string = viewerGrid.dataFrame.currentCol?.name;
253
- if (typeof otherPos === 'undefined' && otherPos !== aminoAcidResidue) {
245
+ if (typeof otherPos === 'undefined' && otherPos !== aminoAcidResidue)
254
246
  return;
255
- }
247
+
256
248
  const otherAAR: string =
257
249
  viewerGrid.dataFrame.get(aminoAcidResidue, viewerGrid.dataFrame.currentRowIdx);
258
250
  let otherRowIndex = -1;
259
251
  for (let i = 0; i < viewerVGrid.dataFrame.rowCount; i++) {
260
252
  if (
261
253
  viewerVGrid.dataFrame.get(aminoAcidResidue, i) === otherAAR &&
262
- viewerVGrid.dataFrame.get('Position', i) === otherPos
254
+ viewerVGrid.dataFrame.get('Pos', i) === otherPos
263
255
  ) {
264
256
  otherRowIndex = i;
265
257
  break;
266
258
  }
267
259
  }
268
- if (otherRowIndex !== -1) {
269
- viewerVGrid.dataFrame.currentCell = viewerVGrid.dataFrame.cell(otherRowIndex, 'Mean difference');
270
- }
260
+ if (otherRowIndex !== -1)
261
+ viewerVGrid.dataFrame.currentCell = viewerVGrid.dataFrame.cell(otherRowIndex, 'Diff');
271
262
  }
272
263
  }
273
264
  }
274
265
 
275
266
  function sourceFilteringFunc(
276
- filterMode: boolean,
277
- dataFrame: DG.DataFrame,
278
- currentBitset: DG.BitSet,
279
- initialBitset: DG.BitSet,
280
- ) {
267
+ filterMode: boolean, dataFrame: DG.DataFrame, currentBitset: DG.BitSet, initialBitset: DG.BitSet) {
281
268
  if (filterMode) {
282
269
  dataFrame.selection.setAll(false, false);
283
270
  dataFrame.filter.copyFrom(currentBitset);
@@ -288,13 +275,8 @@ function sourceFilteringFunc(
288
275
  }
289
276
 
290
277
  function applyBitset(
291
- dataFrame: DG.DataFrame,
292
- viewerGrid: DG.Grid,
293
- aminoAcidResidue: string,
294
- groupMapping: {[key: string]: string},
295
- initialBitset: DG.BitSet,
296
- filterMode: boolean,
297
- ) {
278
+ dataFrame: DG.DataFrame, viewerGrid: DG.Grid, aminoAcidResidue: string, groupMapping: StringDictionary,
279
+ initialBitset: DG.BitSet, filterMode: boolean) {
298
280
  let currentBitset = null;
299
281
  if (
300
282
  viewerGrid.dataFrame &&
@@ -310,9 +292,9 @@ function applyBitset(
310
292
  const aarLabel = `${currentAAR === '-' ? 'Empty' : currentAAR} - ${currentPosition}`;
311
293
 
312
294
  let splitCol = dataFrame.col(splitColName);
313
- if (!splitCol) {
295
+ if (!splitCol)
314
296
  splitCol = dataFrame.columns.addNew(splitColName, 'string');
315
- }
297
+
316
298
 
317
299
  const isChosen = (i: number) => groupMapping[dataFrame!.get(currentPosition, i)] === currentAAR;
318
300
  splitCol!.init((i) => isChosen(i) ? aarLabel : otherLabel);
@@ -331,13 +313,8 @@ function applyBitset(
331
313
  }
332
314
 
333
315
  function accordionFunc(
334
- accordion: DG.Accordion,
335
- viewerGrid: DG.Grid,
336
- aminoAcidResidue: string,
337
- initialBitset: DG.BitSet,
338
- activityColumnName: string,
339
- statsDf: DG.DataFrame,
340
- ) {
316
+ accordion: DG.Accordion, viewerGrid: DG.Grid, aminoAcidResidue: string, initialBitset: DG.BitSet,
317
+ activityColumnName: string, statsDf: DG.DataFrame) {
341
318
  if (accordion.context instanceof DG.RowGroup) {
342
319
  const originalDf: DG.DataFrame = DG.toJs(accordion.context.dataFrame);
343
320
  const viewerDf = viewerGrid.dataFrame;
@@ -363,9 +340,9 @@ function accordionFunc(
363
340
  const elements: (HTMLLabelElement | HTMLElement)[] = [currentLabel, otherLabel];
364
341
 
365
342
  const distPane = accordion.getPane('Distribution');
366
- if (distPane) {
343
+ if (distPane)
367
344
  accordion.removePane(distPane);
368
- }
345
+
369
346
  accordion.addPane('Distribution', () => {
370
347
  const hist = originalDf.clone(initialBitset).plot.histogram({
371
348
  // const hist = originalDf.plot.histogram({
@@ -380,9 +357,9 @@ function accordionFunc(
380
357
  hist.style.width = 'auto';
381
358
  elements.push(hist);
382
359
 
383
- const tableMap: {[key: string]: string} = {'Statistics:': ''};
360
+ const tableMap: StringDictionary = {'Statistics:': ''};
384
361
  for (const colName of new Set(['Count', 'pValue', 'Mean difference'])) {
385
- const query = `${aminoAcidResidue} = ${currentAAR} and Position = ${currentPosition}`;
362
+ const query = `${aminoAcidResidue} = ${currentAAR} and Pos = ${currentPosition}`;
386
363
  const textNum = statsDf.groupBy([colName]).where(query).aggregate().get(colName, 0);
387
364
  // const text = textNum === 0 ? '<0.01' : `${colName === 'Count' ? textNum : textNum.toFixed(2)}`;
388
365
  const text = colName === 'Count' ? `${textNum}` : textNum < 0.01 ? '<0.01' : textNum.toFixed(2);