@datagrok/peptides 0.8.15 → 1.0.2

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.
Files changed (38) hide show
  1. package/dist/package-test.js +8397 -4112
  2. package/dist/package.js +9609 -4868
  3. package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +306 -215
  4. package/files/aligned.csv +648 -648
  5. package/files/aligned_2.csv +541 -10275
  6. package/files/aligned_3.csv +335 -0
  7. package/helm/JSDraw/Pistoia.HELM.js +27 -0
  8. package/package.json +20 -14
  9. package/package.png +0 -0
  10. package/src/__jest__/remote.test.ts +31 -13
  11. package/src/model.ts +540 -646
  12. package/src/package-test.ts +3 -4
  13. package/src/package.ts +13 -131
  14. package/src/tests/core.ts +56 -20
  15. package/src/tests/peptide-space-test.ts +65 -45
  16. package/src/tests/utils.ts +15 -59
  17. package/src/utils/cell-renderer.ts +140 -262
  18. package/src/utils/constants.ts +8 -4
  19. package/src/utils/filtering-statistics.ts +21 -53
  20. package/src/utils/misc.ts +103 -16
  21. package/src/utils/multiple-sequence-alignment.ts +1 -1
  22. package/src/utils/peptide-similarity-space.ts +2 -2
  23. package/src/utils/types.ts +8 -7
  24. package/src/viewers/peptide-space-viewer.ts +62 -35
  25. package/src/viewers/sar-viewer.ts +33 -20
  26. package/src/widgets/analyze-peptides.ts +26 -10
  27. package/src/widgets/distribution.ts +171 -42
  28. package/src/widgets/manual-alignment.ts +4 -4
  29. package/src/widgets/subst-table.ts +40 -21
  30. package/src/workers/dimensionality-reducer.ts +1 -6
  31. package/{test-Peptides-e702a345ac13-2a8c8b59.html → test-Peptides-4f0c8bae6479-74cbfe68.html} +22 -20
  32. package/detectors.js +0 -9
  33. package/src/monomer-library.ts +0 -193
  34. package/src/tests/msa-tests.ts +0 -27
  35. package/src/utils/chem-palette.ts +0 -280
  36. package/src/viewers/stacked-barchart-viewer.ts +0 -362
  37. package/src/widgets/multiple-sequence-alignment.ts +0 -9
  38. package/src/widgets/peptide-molecule.ts +0 -82
package/src/utils/misc.ts CHANGED
@@ -1,24 +1,25 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
-
3
2
  import * as C from './constants';
3
+ import * as type from './types';
4
4
 
5
- export function stringToBool(str: string): boolean {
6
- return str === 'true' ? true : false;
7
- }
5
+ import {AminoacidsPalettes} from '@datagrok-libraries/bio/src/aminoacids';
6
+ import {NucleotidesPalettes} from '@datagrok-libraries/bio/src/nucleotides';
7
+ import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
8
+ import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
9
+ import {WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
8
10
 
9
- export function getSeparator(col: DG.Column<string>): string {
10
- const separator = col.tags[C.TAGS.SEPARATOR];
11
- if (separator)
12
- return separator as string;
13
-
14
- const defaultSeparators = ['.', '-', ' '];
15
- const categories = col.categories;
16
- const catLen = categories.length;
17
- for (const potentialSeparator of defaultSeparators) {
18
- if (categories.filter((sequence) => sequence.includes(potentialSeparator)).length == catLen)
19
- return potentialSeparator;
11
+ export function getPalleteByType(paletteType: string): SeqPalette {
12
+ switch (paletteType) {
13
+ case 'PT':
14
+ return AminoacidsPalettes.GrokGroups;
15
+ case 'NT':
16
+ case 'DNA':
17
+ case 'RNA':
18
+ return NucleotidesPalettes.Chromatogram;
19
+ // other
20
+ default:
21
+ return UnknownSeqPalettes.Color;
20
22
  }
21
- return separator as string ?? '';
22
23
  }
23
24
 
24
25
  export function getTypedArrayConstructor(
@@ -27,3 +28,89 @@ export function getTypedArrayConstructor(
27
28
  maxNum < 65536 ? Uint16Array :
28
29
  Uint32Array;
29
30
  }
31
+
32
+ export function getSeparator(col: DG.Column<string>): string {
33
+ return col.getTag(C.TAGS.SEPARATOR) ?? '';
34
+ }
35
+
36
+ export function splitAlignedPeptides(peptideColumn: DG.Column<string>): DG.DataFrame {
37
+ const splitter = WebLogo.getSplitterForColumn(peptideColumn);
38
+ const colLen = peptideColumn.length;
39
+ const resultDf = DG.DataFrame.create(colLen);
40
+ let monomerList = splitter(peptideColumn.get(0)!);
41
+ const columnList: DG.Column<string>[] = [];
42
+
43
+ // create columns and fill the first row for faster values filling in the next loop
44
+ for (let i = 0; i < monomerList.length; i++) {
45
+ const col = resultDf.columns.addNewString((i + 1).toString());
46
+ col.set(0, monomerList[i] || '-', false);
47
+ columnList.push(col);
48
+ }
49
+
50
+ for (let rowIndex = 1; rowIndex < colLen; rowIndex++) {
51
+ monomerList = splitter(peptideColumn.get(rowIndex)!);
52
+ monomerList.forEach((monomer, colIndex) => columnList[colIndex].set(rowIndex, monomer || '-', false));
53
+ }
54
+
55
+ return resultDf;
56
+ }
57
+
58
+ export function scaleActivity(
59
+ activityScaling: string, df: DG.DataFrame, originalActivityName?: string, cloneBitset = false,
60
+ ): [DG.DataFrame, string] {
61
+ let currentActivityColName = originalActivityName ?? C.COLUMNS_NAMES.ACTIVITY;
62
+ const flag = df.columns.names().includes(currentActivityColName) &&
63
+ currentActivityColName === originalActivityName;
64
+ currentActivityColName = flag ? currentActivityColName : C.COLUMNS_NAMES.ACTIVITY;
65
+ const tempDf = df.clone(cloneBitset ? df.filter : null, [currentActivityColName]);
66
+
67
+ let formula = (v: number) => v;
68
+ let newColName = 'activity';
69
+ switch (activityScaling) {
70
+ case 'none':
71
+ break;
72
+ case 'lg':
73
+ formula = (v: number) => Math.log10(v);
74
+ newColName = `Log10(${newColName})`;
75
+ break;
76
+ case '-lg':
77
+ formula = (v: number) => -Math.log10(v);
78
+ newColName = `-Log10(${newColName})`;
79
+ break;
80
+ default:
81
+ throw new Error(`ScalingError: method \`${activityScaling}\` is not available.`);
82
+ }
83
+
84
+ const asCol = tempDf.columns.addNewFloat(C.COLUMNS_NAMES.ACTIVITY_SCALED);
85
+ const activityCol = df.getCol(currentActivityColName);
86
+ asCol.init((i) => formula(activityCol.get(i)));
87
+ df.tags['scaling'] = activityScaling;
88
+
89
+ return [tempDf, newColName];
90
+ }
91
+
92
+ export function calculateBarsData(columns: DG.Column<string>[], selection: DG.BitSet): type.MonomerDfStats {
93
+ const dfStats: type.MonomerDfStats = {};
94
+ const columnsLen = columns.length;
95
+
96
+ for (let colIndex = 0; colIndex < columnsLen; colIndex++) {
97
+ const col = columns[colIndex];
98
+ dfStats[col.name] = calculateBarData(col, selection);
99
+ }
100
+
101
+ return dfStats;
102
+ }
103
+
104
+ export function calculateBarData(col: DG.Column<string>, selection: DG.BitSet): type.MonomerColStats {
105
+ const colLen = col.length;
106
+ const colStats: type.MonomerColStats = {};
107
+ col.categories.forEach((monomer) => colStats[monomer] = {count: 0, selected: 0});
108
+
109
+ for (let rowIndex = 0; rowIndex < colLen; rowIndex++) {
110
+ const monomerStats = colStats[col.get(rowIndex)!];
111
+ monomerStats.count += 1;
112
+ monomerStats.selected += +selection.get(rowIndex);
113
+ }
114
+
115
+ return colStats;
116
+ }
@@ -87,7 +87,7 @@ export async function runKalign(col: DG.Column, isAligned = false) : Promise<DG.
87
87
 
88
88
  const aligned = _fastaToStrings(buf).slice(0, sequences.length);
89
89
  const alignedCol = DG.Column.fromStrings(`(${col.name})msa`, _stringsToAligned(aligned));
90
- alignedCol.semType = C.SEM_TYPES.ALIGNED_SEQUENCE;
90
+ alignedCol.semType = C.SEM_TYPES.MACROMOLECULE;
91
91
  return alignedCol;
92
92
  }
93
93
 
@@ -7,7 +7,7 @@ import {AlignedSequenceEncoder} from '@datagrok-libraries/bio/src/sequence-encod
7
7
  import {DimensionalityReducer} from '@datagrok-libraries/ml/src/reduce-dimensionality';
8
8
  import {
9
9
  createDimensinalityReducingWorker,
10
- IReduceDimensionalityResult
10
+ IReduceDimensionalityResult,
11
11
  } from '@datagrok-libraries/ml/src/workers/dimensionality-reducing-worker-creator';
12
12
  import {Measure, StringMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
13
13
  import {Coordinates} from '@datagrok-libraries/utils/src/type-declarations';
@@ -44,7 +44,7 @@ export async function createPeptideSimilaritySpaceViewer(
44
44
  const pi = DG.TaskBarProgressIndicator.create('Creating embedding...');
45
45
 
46
46
  const axesNames = ['~X', '~Y', '~MW'];
47
- col ??= table.columns.bySemType(C.SEM_TYPES.ALIGNED_SEQUENCE)!;
47
+ col ??= table.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
48
48
  const columnData = col.toList().map((v) => AlignedSequenceEncoder.clean(v));
49
49
 
50
50
  const reduceDimRes: IReduceDimensionalityResult = await createDimensinalityReducingWorker(
@@ -1,14 +1,15 @@
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
- export namespace BarChart {
8
- export type BarPart = {colName : string, aaName : string};
9
- export type BarStatsObject = {name: string, count: number, selectedCount: number};
10
- }
11
-
12
5
  export type UTypedArray = Uint8Array | Uint16Array | Uint32Array;
13
6
  //AAR: (Position: (index: indexList))
14
7
  export type SubstitutionsInfo = Map<string, Map<string, Map<number, number[] | UTypedArray>>>;
8
+ export type SelectionObject = {[postiton: string]: string[]};
9
+
10
+ export type HELMMonomer = {at: {[key: string]: string}, id: string, m: string, na: string, n: string, rs: number};
11
+
12
+ export type MonomerColStats = {[monomer: string]: {count: number, selected: number}};
13
+ export type MonomerDfStats = {[position: string]: MonomerColStats};
14
+
15
+ export type BarCoordinates = {[monomer: string]: DG.Rect};
@@ -6,19 +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 {createDimensinalityReducingWorker, IReduceDimensionalityResult
9
+ import {createDimensinalityReducingWorker, IReduceDimensionalityResult,
10
10
  } from '@datagrok-libraries/ml/src/workers/dimensionality-reducing-worker-creator';
11
11
  import {StringMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
12
- import {Coordinates} from '@datagrok-libraries/utils/src/type-declarations';
13
12
  import * as C from '../utils/constants';
13
+ import {PeptidesModel} from '../model';
14
14
 
15
15
  export class PeptideSpaceViewer extends DG.JsViewer {
16
16
  method: string;
17
17
  measure: string;
18
18
  cyclesCount: number;
19
- // controller: PeptidesController | null = null;
20
19
  customProperties = new Set(['method', 'measure', 'cyclesCount']);
21
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};
22
24
 
23
25
  constructor() {
24
26
  super();
@@ -28,34 +30,74 @@ export class PeptideSpaceViewer extends DG.JsViewer {
28
30
  const measureChoices = ['Levenshtein', 'Jaro-Winkler'];
29
31
  this.measure = this.addProperty('measure', DG.TYPE.STRING, 'Levenshtein', {choices: measureChoices});
30
32
  this.cyclesCount = this.addProperty('cyclesCount', DG.TYPE.INT, 100);
33
+ this.updatePrevProperties();
31
34
  }
32
35
 
33
- async onFrameAttached(dataFrame: DG.DataFrame): Promise<void> {
34
- super.onFrameAttached(dataFrame);
35
- await this.render(this.dataFrame.temp[C.EMBEDDING_STATUS]);
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();
42
+
43
+ this.model = await PeptidesModel.getInstance(this.dataFrame);
44
+
45
+ await this.render(!this.dataFrame.temp[C.EMBEDDING_STATUS]);
36
46
  }
37
47
 
38
48
  async onPropertyChanged(property: DG.Property | null): Promise<void> {
39
49
  super.onPropertyChanged(property);
50
+ if (this.prevProps[property?.name as 'method' | 'measure' | 'cyclesCount' ?? ''] == property?.get(this))
51
+ return;
40
52
 
53
+ if (this.model)
54
+ await this.render(this.customProperties.has(property?.name ?? '') || !this.dataFrame.temp[C.EMBEDDING_STATUS]);
41
55
 
42
- await this.render(this.customProperties.has(property?.name ?? '') || this.dataFrame.temp[C.EMBEDDING_STATUS]);
56
+ this.updatePrevProperties();
43
57
  }
44
58
 
45
59
  async render(computeData=false): Promise<void> {
46
- if (computeData && !this.isEmbeddingCreating) {
60
+ if (computeData && !this.isEmbeddingCreating && !this.model.isChangingEdfBitset) {
47
61
  this.isEmbeddingCreating = true;
48
62
  $(this.root).empty();
49
63
  const viewerHost = ui.waitBox(async () => {
50
- await computeWeights(this.dataFrame, this.method, this.measure, this.cyclesCount);
64
+ const aligendSeqCol = this.dataFrame.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
65
+ const edf = await computeWeights(this.dataFrame, this.method, this.measure, this.cyclesCount, aligendSeqCol);
66
+ this.dataFrame.temp[C.EMBEDDING_STATUS] = true;
67
+ this.model.edf = edf;
68
+
69
+ if (edf === null)
70
+ return ui.label('Could not compute embeddings');
71
+
72
+ const edfSelection = edf.selection;
73
+ edfSelection.copyFrom(this.dataFrame.selection);
74
+ edfSelection.onChanged.subscribe(() => {
75
+ if (!this.model.isChangingEdfBitset)
76
+ this.model.fireBitsetChanged(true);
77
+ });
78
+
79
+ const colorCol = this.dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
80
+ edf.columns.add(colorCol);
51
81
 
52
- const colorColName = this.dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!.name;
53
82
  const viewerOptions = {
54
- x: '~X', y: '~Y', color: colorColName ?? '~MW', size: '~MW', title: 'Peptide Space',
83
+ x: '~X', y: '~Y', color: colorCol.name ?? '~MW', size: '~MW', title: 'Peptide Space',
55
84
  showYSelector: false, showXSelector: false, showColorSelector: false, showSizeSelector: false,
56
85
  zoomAndFilter: 'no action', axesFollowFilter: false,
57
86
  };
58
- const viewerRoot = this.dataFrame.plot.scatter(viewerOptions).root;
87
+ const scatterPlot = edf.plot.scatter(viewerOptions);
88
+ const viewerRoot = scatterPlot.root;
89
+
90
+ viewerRoot.addEventListener('mousemove', (ev) => {
91
+ const idx = scatterPlot.hitTest(ev.offsetX, ev.offsetY);
92
+ if (idx != -1) {
93
+ const table = ui.tableFromMap({
94
+ 'Activity': colorCol.get(idx),
95
+ 'Sequence': aligendSeqCol.get(idx),
96
+ 'Row ID': idx,
97
+ });
98
+ ui.tooltip.show(table, ev.clientX, ev.clientY);
99
+ }
100
+ });
59
101
  viewerRoot.style.width = 'auto';
60
102
  this.isEmbeddingCreating = false;
61
103
  viewerHost.style.paddingLeft = 'unset';
@@ -67,22 +109,22 @@ export class PeptideSpaceViewer extends DG.JsViewer {
67
109
  }
68
110
  }
69
111
 
112
+ //Do not accept table, only column
70
113
  export async function computeWeights(
71
114
  table: DG.DataFrame, method: string, measure: string, cyclesCount: number, col?: DG.Column,
72
- ): Promise<void> {
115
+ ): Promise<DG.DataFrame | null> {
73
116
  const pi = DG.TaskBarProgressIndicator.create('Creating embedding...');
117
+ let edf: DG.DataFrame | null = null;
74
118
  try {
75
119
  const axesNames = ['~X', '~Y', '~MW'];
76
- col ??= table.columns.bySemType(C.SEM_TYPES.ALIGNED_SEQUENCE)!;
77
- const columnData = col.toList()
78
- .map((v) => AlignedSequenceEncoder.clean(v));
120
+ col ??= table.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
121
+ const columnData = col.toList().map((v) => AlignedSequenceEncoder.clean(v));
79
122
 
80
123
  const reduceDimRes: IReduceDimensionalityResult = await createDimensinalityReducingWorker(
81
124
  {data: columnData, metric: measure as StringMetrics}, method, {cycles: cyclesCount});
82
125
  const embcols = reduceDimRes.embedding;
83
126
 
84
- const columns = Array.from(
85
- embcols as Coordinates, (v: Float32Array, k) => DG.Column.fromFloat32Array(axesNames[k], v));
127
+ const columns = Array.from(embcols, (v: Float32Array, k) => DG.Column.fromFloat32Array(axesNames[k], v));
86
128
 
87
129
  function _getMW(sequences: string[]): Float32Array {
88
130
  const mw: Float32Array = new Float32Array(sequences.length);
@@ -94,27 +136,12 @@ export async function computeWeights(
94
136
 
95
137
  columns.push(DG.Column.fromFloat32Array('~MW', _getMW(columnData)));
96
138
 
97
- const edf = DG.DataFrame.fromColumns(columns);
98
-
99
- // Add new axes.
100
- for (const axis of axesNames) {
101
- const col = table.col(axis);
102
- const newCol = edf.getCol(axis);
103
-
104
- // if (col != null) {
105
- // for (let i = 0; i < newCol.length; ++i) {
106
- // const v = newCol.get(i);
107
- // table.set(axis, i, v);
108
- // }
109
- // } else
110
- // table.columns.insert(newCol);
111
- const columnList = table.columns;
112
- col !== null ? columnList.replace(col, newCol) : columnList.insert(newCol);
113
- }
139
+ edf = DG.DataFrame.fromColumns(columns);
114
140
  } catch (error) {
115
141
  grok.shell.error('Could not compute embeddings. See console for details.');
116
142
  console.error(error);
117
143
  } finally {
118
144
  pi.close();
145
+ return edf;
119
146
  }
120
147
  }
@@ -17,7 +17,7 @@ export class SARViewerBase extends DG.JsViewer {
17
17
  bidirectionalAnalysis: boolean;
18
18
  showSubstitution: boolean;
19
19
  maxSubstitutions: number;
20
- activityLimit: number;
20
+ minActivityDelta: number;
21
21
  _titleHost = ui.divText('SAR Viewer', {id: 'pep-viewer-title'});
22
22
  initialized = false;
23
23
  isPropertyChanging: boolean = false;
@@ -28,18 +28,27 @@ export class SARViewerBase extends DG.JsViewer {
28
28
  this.scaling = this.string('scaling', 'none', {choices: ['none', 'lg', '-lg']});
29
29
  this.bidirectionalAnalysis = this.bool('bidirectionalAnalysis', false);
30
30
  this.showSubstitution = this.bool('showSubstitution', true);
31
- this.maxSubstitutions = this.int('maxSubstitutions', 1);
32
- this.activityLimit = this.float('activityLimit', 2);
31
+ this.maxSubstitutions = this.int('maxSubstitutions', 2);
32
+ this.minActivityDelta = this.float('minActivityDelta', 1);
33
33
  }
34
34
 
35
35
  async onTableAttached(): Promise<void> {
36
36
  super.onTableAttached();
37
- this.dataFrame.temp[this.tempName] ??= this;
38
37
  this.sourceGrid = this.view?.grid ?? (grok.shell.v as DG.TableView).grid;
39
38
  this.model = await PeptidesModel.getInstance(this.dataFrame);
40
39
  // this.model.init(this.dataFrame);
41
- await this.requestDataUpdate();
42
40
  this.helpUrl = '/help/domains/bio/peptides.md';
41
+ // await this.requestDataUpdate();
42
+
43
+ this.initProperties();
44
+ }
45
+
46
+ initProperties() {
47
+ const props = this.model.usedProperties;
48
+ IS_PROPERTY_CHANGING = true;
49
+ for (const [propName, propVal] of Object.entries(props))
50
+ this.props.set(propName, propVal as any as object);
51
+ IS_PROPERTY_CHANGING = false;
43
52
  }
44
53
 
45
54
  detach(): void {this.subs.forEach((sub) => sub.unsubscribe());}
@@ -56,12 +65,12 @@ export class SARViewerBase extends DG.JsViewer {
56
65
  this.viewerGrid?.invalidate();
57
66
  }
58
67
 
59
- async requestDataUpdate(): Promise<void> {
60
- await this.model.updateData(this.scaling, this.sourceGrid, this.bidirectionalAnalysis,
61
- this.activityLimit, this.maxSubstitutions, this.showSubstitution);
62
- }
68
+ // async requestDataUpdate(): Promise<void> {
69
+ // await this.model.updateData(this.scaling, this.sourceGrid, this.bidirectionalAnalysis,
70
+ // this.minActivityDelta, this.maxSubstitutions, this.showSubstitution);
71
+ // }
63
72
 
64
- async onPropertyChanged(property: DG.Property): Promise<void> {
73
+ onPropertyChanged(property: DG.Property): void {
65
74
  super.onPropertyChanged(property);
66
75
  this.dataFrame.tags[property.name] = `${property.get(this)}`;
67
76
  if (!this.initialized || IS_PROPERTY_CHANGING)
@@ -71,7 +80,6 @@ export class SARViewerBase extends DG.JsViewer {
71
80
 
72
81
  if (propName === 'scaling' && typeof this.dataFrame !== 'undefined') {
73
82
  const activityCol = this.dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY)!;
74
- // const minActivity = this.dataFrame.getCol(C.COLUMNS_NAMES.ACTIVITY).stats.min;
75
83
  const minActivity = activityCol.stats.min;
76
84
  if (minActivity && minActivity <= 0 && this.scaling !== 'none') {
77
85
  grok.shell.warning(`Could not apply ${this.scaling}: ` +
@@ -84,7 +92,8 @@ export class SARViewerBase extends DG.JsViewer {
84
92
  if (!this.showSubstitution && ['maxSubstitutions', 'activityLimit'].includes(propName))
85
93
  return;
86
94
 
87
- await this.requestDataUpdate();
95
+ // await this.requestDataUpdate();
96
+ this.model.updateDefault();
88
97
  this.render(true);
89
98
  }
90
99
  }
@@ -95,7 +104,6 @@ export class SARViewerBase extends DG.JsViewer {
95
104
  export class SARViewer extends SARViewerBase {
96
105
  _titleHost = ui.divText('Monomer-Positions', {id: 'pep-viewer-title'});
97
106
  _name = 'Structure-Activity Relationship';
98
- tempName = 'sarViewer';
99
107
 
100
108
  constructor() {super();}
101
109
 
@@ -103,13 +111,16 @@ export class SARViewer extends SARViewerBase {
103
111
 
104
112
  async onTableAttached(): Promise<void> {
105
113
  await super.onTableAttached();
106
- this.viewerGrid = this.model._sarGrid;
114
+ this.model.sarViewer ??= this;
115
+ // this.dataFrame.temp['sarViewer'] = this;
107
116
 
108
117
  this.subs.push(this.model.onSARGridChanged.subscribe((data) => {
109
118
  this.viewerGrid = data;
110
119
  this.render();
111
120
  }));
112
121
 
122
+ this.model.updateDefault();
123
+ this.viewerGrid = this.model._sarGrid;
113
124
  this.initialized = true;
114
125
  this.render();
115
126
  }
@@ -117,11 +128,11 @@ export class SARViewer extends SARViewerBase {
117
128
  isInitialized(): DG.Grid {return this.model?._sarGrid;}
118
129
 
119
130
  //1. debouncing in rxjs; 2. flags?
120
- async onPropertyChanged(property: DG.Property): Promise<void> {
131
+ onPropertyChanged(property: DG.Property): void {
121
132
  if (!this.isInitialized() || IS_PROPERTY_CHANGING)
122
133
  return;
123
134
 
124
- await super.onPropertyChanged(property);
135
+ super.onPropertyChanged(property);
125
136
  IS_PROPERTY_CHANGING = true;
126
137
  this.model.syncProperties(true);
127
138
  IS_PROPERTY_CHANGING = false;
@@ -132,7 +143,6 @@ export class SARViewer extends SARViewerBase {
132
143
  export class SARViewerVertical extends SARViewerBase {
133
144
  _name = 'Sequence-Activity relationship';
134
145
  _titleHost = ui.divText('Most Potent Residues', {id: 'pep-viewer-title'});
135
- tempName = 'sarViewerVertical';
136
146
 
137
147
  constructor() {
138
148
  super();
@@ -142,24 +152,27 @@ export class SARViewerVertical extends SARViewerBase {
142
152
 
143
153
  async onTableAttached(): Promise<void> {
144
154
  await super.onTableAttached();
145
- this.viewerGrid = this.model._sarVGrid;
155
+ this.model.sarViewerVertical ??= this;
146
156
 
147
157
  this.subs.push(this.model.onSARVGridChanged.subscribe((data) => {
148
158
  this.viewerGrid = data;
149
159
  this.render();
150
160
  }));
151
161
 
162
+ this.model.updateDefault();
163
+ this.viewerGrid = this.model._sarVGrid;
164
+
152
165
  this.initialized = true;
153
166
  this.render();
154
167
  }
155
168
 
156
169
  isInitialized(): DG.Grid {return this.model?._sarVGrid;}
157
170
 
158
- async onPropertyChanged(property: DG.Property): Promise<void> {
171
+ onPropertyChanged(property: DG.Property): void {
159
172
  if (!this.isInitialized() || IS_PROPERTY_CHANGING)
160
173
  return;
161
174
 
162
- await super.onPropertyChanged(property);
175
+ super.onPropertyChanged(property);
163
176
  IS_PROPERTY_CHANGING = true;
164
177
  this.model.syncProperties(false);
165
178
  IS_PROPERTY_CHANGING = false;
@@ -1,11 +1,15 @@
1
1
  import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
+
5
+ import {WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
6
+
4
7
  import '../styles.css';
5
8
  import * as C from '../utils/constants';
6
- import {getSeparator} from '../utils/misc';
7
9
  import {PeptidesModel} from '../model';
8
10
  import {_package} from '../package';
11
+ import $ from 'cash-dom';
12
+ import {scaleActivity} from '../utils/misc';
9
13
 
10
14
  /** Peptide analysis widget.
11
15
  *
@@ -13,11 +17,12 @@ import {_package} from '../package';
13
17
  * @param {DG.Column} col Aligned sequence column
14
18
  * @return {Promise<DG.Widget>} Widget containing peptide analysis */
15
19
  export async function analyzePeptidesWidget(currentDf: DG.DataFrame, col: DG.Column): Promise<DG.Widget> {
20
+ const funcs = DG.Func.find({package: 'Bio', name: 'webLogoViewer'});
21
+ if (funcs.length == 0)
22
+ return new DG.Widget(ui.label('Bio package is missing or out of date. Please install the latest version.'));
16
23
  let tempCol = null;
17
24
  let scaledDf: DG.DataFrame;
18
25
  let newScaledColName: string;
19
- const separator = getSeparator(col);
20
- col.tags[C.TAGS.SEPARATOR] ??= separator;
21
26
 
22
27
  for (const column of currentDf.columns.numerical)
23
28
  tempCol = column.type === DG.TYPE.FLOAT ? column : null;
@@ -30,8 +35,7 @@ export async function analyzePeptidesWidget(currentDf: DG.DataFrame, col: DG.Col
30
35
  async (currentMethod: string): Promise<void> => {
31
36
  const currentActivityCol = activityColumnChoice.value?.name;
32
37
 
33
- [scaledDf, newScaledColName] = await PeptidesModel.scaleActivity(
34
- currentMethod, currentDf, currentActivityCol, true);
38
+ [scaledDf, newScaledColName] = scaleActivity(currentMethod, currentDf, currentActivityCol, true);
35
39
 
36
40
  const hist = scaledDf.plot.histogram({
37
41
  filteringEnabled: false,
@@ -63,12 +67,14 @@ export async function analyzePeptidesWidget(currentDf: DG.DataFrame, col: DG.Col
63
67
  });
64
68
  startBtn.style.alignSelf = 'center';
65
69
 
66
- const viewer = await currentDf.plot.fromType('WebLogo');
70
+ const viewer = await currentDf.plot.fromType('WebLogo') as WebLogo;
67
71
  viewer.root.style.setProperty('height', '130px');
72
+ const logoHost = ui.div();
73
+ $(logoHost).empty().append(viewer.root);
68
74
 
69
75
  return new DG.Widget(
70
76
  ui.divV([
71
- viewer.root,
77
+ logoHost,
72
78
  ui.splitH([
73
79
  ui.splitV([ui.inputs(inputsList), startBtn]),
74
80
  histogramHost,
@@ -95,10 +101,20 @@ export async function startAnalysis(
95
101
  activityScaledCol.semType = C.SEM_TYPES.ACTIVITY_SCALED;
96
102
  newDf.columns.add(activityScaledCol);
97
103
  newDf.name = 'Peptides analysis';
98
- newDf.temp[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newScaledColName;
99
- newDf.tags[C.PEPTIDES_ANALYSIS] = 'true';
104
+ newDf.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newScaledColName;
105
+ // newDf.tags[C.PEPTIDES_ANALYSIS] = 'true';
106
+
107
+ const alignedSeqColUnits = alignedSeqCol.getTag(DG.TAGS.UNITS);
108
+ let monomerType = 'HELM_AA';
109
+ if (alignedSeqColUnits == 'HELM') {
110
+ const sampleSeq = alignedSeqCol.get(0)!;
111
+ monomerType = sampleSeq.startsWith('PEPTIDE') ? 'HELM_AA' : 'HELM_BASE';
112
+ } else {
113
+ monomerType = alignedSeqColUnits.split(':')[2] == 'PT' ? 'HELM_AA' : 'HELM_BASE';
114
+ }
115
+ newDf.setTag('monomerType', monomerType);
100
116
 
101
- model = await PeptidesModel.getInstance(newDf, dgPackage ?? _package);
117
+ model = await PeptidesModel.getInstance(newDf);
102
118
  } else
103
119
  grok.shell.error('The activity column must be of floating point number type!');
104
120
  progress.close();