@datagrok/bio 1.7.20 → 1.7.23

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/package.json CHANGED
@@ -2,7 +2,11 @@
2
2
  "name": "@datagrok/bio",
3
3
  "beta": false,
4
4
  "friendlyName": "Bio",
5
- "version": "1.7.20",
5
+ "author": {
6
+ "name": "Leonid Stolbov",
7
+ "email": "lstolbov@datagrok.ai"
8
+ },
9
+ "version": "1.7.23",
6
10
  "description": "Bio is a [package](https://datagrok.ai/help/develop/develop#packages) for the [Datagrok](https://datagrok.ai) platform",
7
11
  "repository": {
8
12
  "type": "git",
@@ -11,7 +15,7 @@
11
15
  },
12
16
  "dependencies": {
13
17
  "@biowasm/aioli": ">=2.4.0",
14
- "@datagrok-libraries/bio": "^3.0.0",
18
+ "@datagrok-libraries/bio": "^3.0.2",
15
19
  "@datagrok-libraries/ml": "^3.0.0",
16
20
  "@datagrok-libraries/utils": "^1.4.0",
17
21
  "cash-dom": "latest",
@@ -11,6 +11,7 @@ import './tests/activity-cliffs-tests';
11
11
  import './tests/splitters-test';
12
12
  import './tests/renderers-test';
13
13
  import './tests/convert-test';
14
+ import './tests/fasta-handler-test';
14
15
  import './tests/WebLogo-positions-test';
15
16
 
16
17
  export const _package = new DG.Package();
package/src/package.ts CHANGED
@@ -5,7 +5,7 @@ import * as DG from 'datagrok-api/dg';
5
5
 
6
6
  export const _package = new DG.Package();
7
7
 
8
- import {AlignedSequenceDifferenceCellRenderer, AminoAcidsCellRenderer} from './utils/cell-renderer';
8
+ import {MacromoleculeDifferenceCellRenderer, MonomerCellRenderer} from './utils/cell-renderer';
9
9
  import {WebLogo, SeqColStats} from '@datagrok-libraries/bio/src/viewers/web-logo';
10
10
  import {VdRegionsViewer} from './viewers/vd-regions-viewer';
11
11
  import {runKalign, testMSAEnoughMemory} from './utils/multiple-sequence-alignment';
@@ -26,17 +26,18 @@ import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
26
26
  import {FastaFileHandler} from '@datagrok-libraries/bio/src/utils/fasta-handler';
27
27
 
28
28
 
29
-
30
29
  //tags: init
31
30
  export async function initBio(): Promise<void> {
32
31
  // apparently HELMWebEditor requires dojo to be initialized first
33
- if (DG.Func.find({package: 'Helm', name: 'initHelm'}) != null) {
34
- grok.functions.call('Helm:initHelp');
35
- }
32
+ const funcList: DG.Func[] = DG.Func.find({package: 'Helm', name: 'initHelm'});
33
+ console.debug(`Bio: initBio() funcList.length = ${funcList.length}`);
34
+ if (funcList.length === 1)
35
+ await grok.functions.call('Helm:initHelp');
36
+
36
37
  return new Promise((resolve, reject) => {
37
38
  // @ts-ignore
38
39
  dojo.ready(function() { resolve(null); });
39
- });
40
+ });
40
41
  }
41
42
 
42
43
  //name: Lru
@@ -240,6 +241,14 @@ export async function multipleSequenceAlignmentAny(table: DG.DataFrame, col: DG.
240
241
  return msaCol;
241
242
  }
242
243
 
244
+ //name: Bio | MSA
245
+ //tags: bio, panel
246
+ //input: column sequence { semType: Macromolecule }
247
+ //output: column result
248
+ export async function panelMSA(col: DG.Column): Promise<DG.Column | null> {
249
+ return multipleSequenceAlignmentAny(col.dataFrame, col);
250
+ }
251
+
243
252
  //name: Composition Analysis
244
253
  //top-menu: Bio | Composition Analysis
245
254
  //output: viewer result
@@ -333,20 +342,20 @@ export function convertPanel(col: DG.Column): void {
333
342
  convert(col);
334
343
  }
335
344
 
336
- //name: aminoAcidsCellRenderer
345
+ //name: monomerCellRenderer
337
346
  //tags: cellRenderer
338
- //meta.cellType: aminoAcids
347
+ //meta.cellType: Monomer
339
348
  //output: grid_cell_renderer result
340
- export function aminoAcidsCellRenderer(): AminoAcidsCellRenderer {
341
- return new AminoAcidsCellRenderer();
349
+ export function monomerCellRenderer(): MonomerCellRenderer {
350
+ return new MonomerCellRenderer();
342
351
  }
343
352
 
344
- //name: alignedSequenceDifferenceCellRenderer
353
+ //name: MacromoleculeDifferenceCellRenderer
345
354
  //tags: cellRenderer
346
- //meta.cellType: alignedSequenceDifference
355
+ //meta.cellType: MacromoleculeDifference
347
356
  //output: grid_cell_renderer result
348
- export function alignedSequenceDifferenceCellRenderer(): AlignedSequenceDifferenceCellRenderer {
349
- return new AlignedSequenceDifferenceCellRenderer();
357
+ export function macromoleculeDifferenceCellRenderer(): MacromoleculeDifferenceCellRenderer {
358
+ return new MacromoleculeDifferenceCellRenderer();
350
359
  }
351
360
 
352
361
  //name: testDetectMacromolecule
@@ -1,13 +1,14 @@
1
+
1
2
  import {after, before, category, test, expect, expectObject} from '@datagrok-libraries/utils/src/test';
2
3
 
3
4
  import * as grok from 'datagrok-api/grok';
4
5
  import * as ui from 'datagrok-api/ui';
5
6
  import * as DG from 'datagrok-api/dg';
6
7
  import {PositionInfo, PositionMonomerInfo, WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
7
-
8
8
  category('WebLogo-positions', () => {
9
9
  let tvList: DG.TableView[];
10
10
  let dfList: DG.DataFrame[];
11
+ let currentView: DG.View;
11
12
 
12
13
  const csvDf1 = `seq
13
14
  ATC-G-TTGC--
@@ -22,13 +23,14 @@ category('WebLogo-positions', () => {
22
23
  before(async () => {
23
24
  tvList = [];
24
25
  dfList = [];
26
+ currentView = grok.shell.tv;
25
27
  });
26
28
 
27
29
  after(async () => {
28
- dfList.forEach((df: DG.DataFrame) => { grok.shell.closeTable(df); });
30
+ dfList.forEach((df: DG.DataFrame) => { grok.shell.closeTable(df);});
29
31
  tvList.forEach((tv: DG.TableView) => tv.close());
32
+ currentView = grok.shell.tv;
30
33
  });
31
-
32
34
  test('allPositions', async () => {
33
35
  const df: DG.DataFrame = DG.DataFrame.fromCsv(csvDf1);
34
36
  const tv: DG.TableView = grok.shell.addTableView(df);
@@ -55,12 +57,82 @@ category('WebLogo-positions', () => {
55
57
  new PositionInfo('11', {'-': new PositionMonomerInfo(5)}),
56
58
  new PositionInfo('12', {'-': new PositionMonomerInfo(5)})
57
59
  ];
60
+ console.log(positions);
61
+ expect(positions.length,resAllDf1.length);
58
62
  // check all positions are equal resAllDf1
59
63
  for (let i = 0; i < positions.length; i++) {
60
64
  expect(positions[i].name, resAllDf1[i].name);
61
- for (const key in positions[i].freq) {
62
- expect(positions[i].freq[key].count, resAllDf1[i].freq[key].count);
63
- }
65
+ for (const key in positions[i].freq) {
66
+ expect(positions[i].freq[key].count, resAllDf1[i].freq[key].count);
67
+ }
68
+ }
69
+
70
+ });
71
+ test('positions with shrinkEmptyTail option', async () => {
72
+ const df: DG.DataFrame = DG.DataFrame.fromCsv(csvDf1);
73
+ const tv: DG.TableView = grok.shell.addTableView(df);
74
+
75
+ const wlViewer: WebLogo = await df.plot.fromType('WebLogo', {'shrinkEmptyTail': true}) as unknown as WebLogo;
76
+ tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
77
+
78
+ tvList.push(tv);
79
+ dfList.push(df);
80
+
81
+ const positions: PositionInfo[] = wlViewer['positions'];
82
+
83
+ const resAllDf1: PositionInfo[] = [
84
+ new PositionInfo('1', {'A': new PositionMonomerInfo(2), '-': new PositionMonomerInfo(3)}),
85
+ new PositionInfo('2', {'T': new PositionMonomerInfo(5)}),
86
+ new PositionInfo('3', {'C': new PositionMonomerInfo(5)}),
87
+ new PositionInfo('4', {'-': new PositionMonomerInfo(5)}),
88
+ new PositionInfo('5', {'G': new PositionMonomerInfo(5)}),
89
+ new PositionInfo('6', {'-': new PositionMonomerInfo(3), 'C': new PositionMonomerInfo(2)}),
90
+ new PositionInfo('7', {'T': new PositionMonomerInfo(5)}),
91
+ new PositionInfo('8', {'T': new PositionMonomerInfo(5)}),
92
+ new PositionInfo('9', {'G': new PositionMonomerInfo(5)}),
93
+ new PositionInfo('10', {'C': new PositionMonomerInfo(5)})
94
+ ];
95
+
96
+ console.log(positions);
97
+ for (let i = 0; i < positions.length; i++) {
98
+ expect(positions[i].name, resAllDf1[i].name);
99
+ for (const key in positions[i].freq) {
100
+ expect(positions[i].freq[key].count, resAllDf1[i].freq[key].count);
101
+ }
102
+ }
103
+
104
+ });
105
+
106
+ test('positions with skipEmptyPositions option', async () => {
107
+ const df: DG.DataFrame = DG.DataFrame.fromCsv(csvDf1);
108
+ const tv: DG.TableView = grok.shell.addTableView(df);
109
+
110
+ const wlViewer: WebLogo = await df.plot.fromType('WebLogo', {'skipEmptyPositions': false}) as unknown as WebLogo;
111
+ tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
112
+
113
+ tvList.push(tv);
114
+ dfList.push(df);
115
+
116
+ const positions: PositionInfo[] = wlViewer['positions'];
117
+
118
+ const resAllDf1: PositionInfo[] = [
119
+ new PositionInfo('1', {'A': new PositionMonomerInfo(2), '-': new PositionMonomerInfo(3)}),
120
+ new PositionInfo('2', {'T': new PositionMonomerInfo(5)}),
121
+ new PositionInfo('3', {'C': new PositionMonomerInfo(5)}),
122
+ new PositionInfo('5', {'G': new PositionMonomerInfo(5)}),
123
+ new PositionInfo('6', {'-': new PositionMonomerInfo(3), 'C': new PositionMonomerInfo(2)}),
124
+ new PositionInfo('7', {'T': new PositionMonomerInfo(5)}),
125
+ new PositionInfo('8', {'T': new PositionMonomerInfo(5)}),
126
+ new PositionInfo('9', {'G': new PositionMonomerInfo(5)}),
127
+ new PositionInfo('10', {'C': new PositionMonomerInfo(5)})
128
+ ];
129
+ console.log(positions);
130
+
131
+ for (let i = 0; i < positions.length; i++) {
132
+ expect(positions[i].name, resAllDf1[i].name);
133
+ for (const key in positions[i].freq) {
134
+ expect(positions[i].freq[key].count, resAllDf1[i].freq[key].count);
135
+ }
64
136
  }
65
137
 
66
138
  });
@@ -146,3 +146,4 @@ export async function _testPickupPaletteAA2(dfAA2: DG.DataFrame) {
146
146
 
147
147
  expect(cp instanceof AminoacidsPalettes, true);
148
148
  }
149
+
@@ -125,6 +125,7 @@ MWRSWY-CKHP
125
125
  testSpgi100 = 'testSpgi100',
126
126
  testUnichemSources = 'testUnichemSources',
127
127
  testDmvOffices = 'testDmvOffices',
128
+ testAlertCollection = 'testAlertCollection',
128
129
  }
129
130
 
130
131
  const samples: { [key: string]: string } = {
@@ -145,6 +146,7 @@ MWRSWY-CKHP
145
146
  'testSpgi100': 'System:AppData/Bio/tests/testSpgi100.csv',
146
147
  'testUnichemSources': 'System:AppData/Bio/tests/testUnichemSources.csv',
147
148
  'testDmvOffices': 'System:AppData/Bio/tests/testDmvOffices.csv',
149
+ 'testAlertCollection': 'System:AppData/Bio/tests/testAlertCollection.csv',
148
150
  };
149
151
 
150
152
  const _samplesDfs: { [key: string]: Promise<DG.DataFrame> } = {};
@@ -342,6 +344,9 @@ MWRSWY-CKHP
342
344
  test('samplesTestSpgi100NegativePrimaryScaffoldName', async () => {
343
345
  await _testNeg(readSamples(Samples.testSpgi100), 'Primary Scaffold Name');
344
346
  });
347
+ test('samplesTestSpgi100NegativeSampleName', async () => {
348
+ await _testNeg(readSamples(Samples.testSpgi100), 'Sample Name');
349
+ });
345
350
 
346
351
  test('samplesTestUnichemSourcesNegativeSrcUrl', async () => {
347
352
  await _testNeg(readSamples(Samples.testUnichemSources), 'src_url');
@@ -356,6 +361,10 @@ MWRSWY-CKHP
356
361
  test('samplesTestDmvOfficesNegativeCity', async () => {
357
362
  await _testNeg(readSamples(Samples.testDmvOffices), 'City');
358
363
  });
364
+
365
+ test('samplesTestAlertCollectionNegativeSmarts', async () => {
366
+ await _testNeg(readSamples(Samples.testAlertCollection), 'smarts');
367
+ });
359
368
  });
360
369
 
361
370
  export async function _testNeg(readDf: DfReaderFunc, colName: string) {
@@ -0,0 +1,141 @@
1
+ /* Do not change these import lines to match external modules in webpack configuration */
2
+ import * as grok from 'datagrok-api/grok';
3
+ import * as ui from 'datagrok-api/ui';
4
+ import * as DG from 'datagrok-api/dg';
5
+
6
+ import {category, expectArray, test} from '@datagrok-libraries/utils/src/test';
7
+ import {FastaFileHandler} from '@datagrok-libraries/bio/src/utils/fasta-handler';
8
+ import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
9
+
10
+
11
+ category('fastaFileHandler', () => {
12
+ const fastaNormalFormatting = `>description:1
13
+ MDYKETLLMPKTDFPMRGGLPNKEPQIQEKW
14
+
15
+ >description:2
16
+ MIEVFLFGIVLGLIPITLAGLFVTAYLQYRRGDQLDL
17
+
18
+ >description:3
19
+ MMELVLKTIIGPIVVGVVLRIVDKWLNKDK
20
+
21
+ >description:4
22
+ MDRTDEVSNHTHDKPTLTWFEEIFEEYHSPFHN
23
+ `;
24
+
25
+ const fastaExtraSpaces = `>description:1
26
+ MDYKETLLMPKTDFPMRGGLPNKEPQIQEKW
27
+
28
+ >description:2
29
+ MI EVF LFGIVLGLI PITLAGLFVTAY LQYRRGDQLDL
30
+
31
+ >description:3
32
+ M MELVLKTI IGPI VVGVVLR IVDKWLNKDK
33
+
34
+ >description:4
35
+ MDR TDEVSNHTHDKP TLTWFEEIFEEYHSPFHN
36
+ `;
37
+
38
+ const fastaExtraNewlines = `>description:1
39
+
40
+ MDYKETLLMPKTDFPMRGGLPNKEPQIQEKW
41
+
42
+ >description:2
43
+ MIEVF
44
+ LFGIVLGLI
45
+ PITLAGLFVTA
46
+ YLQYRRGDQLDL
47
+
48
+ >description:3
49
+ M
50
+ ME
51
+
52
+ LVLKTIIG
53
+
54
+ PIVVGVVLRI
55
+ VDKWLNKDK
56
+
57
+
58
+ >description:4
59
+
60
+ MDRT
61
+
62
+ DEVSNHTHDKP
63
+
64
+ TLTWFEEIFEE
65
+
66
+
67
+
68
+ YHSPFHN
69
+ `;
70
+ // a "broken" fasta file
71
+ // const fastaBroken = `
72
+
73
+ // >description:1
74
+ // MDYKETLLM
75
+ // PKTDFPMRGGLPN
76
+ // KEPQIQEKW
77
+
78
+
79
+
80
+ // >description:2
81
+ // MIEVFL FGIVLGLIPI TLAGLFVTAYLQYRRGDQLDL
82
+
83
+ // >description:3
84
+
85
+ // M
86
+ // MELVLKTIIGP
87
+ // IVVGVVLR
88
+ // IVDKWLNKD
89
+
90
+ // K
91
+
92
+ // >description:4
93
+ // MDRTDEV
94
+
95
+ // SNHTHDKP
96
+ // TLTWFEEI
97
+ // FEE
98
+
99
+ // YHSPFHN
100
+
101
+
102
+ // `;
103
+
104
+ const descriptionsArray = [
105
+ 'description:1', 'description:2', 'description:3', 'description:4',
106
+ ];
107
+ const descriptionCol = DG.Column.fromStrings('description', descriptionsArray);
108
+
109
+ const sequencesArray = [
110
+ 'MDYKETLLMPKTDFPMRGGLPNKEPQIQEKW',
111
+ 'MIEVFLFGIVLGLIPITLAGLFVTAYLQYRRGDQLDL',
112
+ 'MMELVLKTIIGPIVVGVVLRIVDKWLNKDK',
113
+ 'MDRTDEVSNHTHDKPTLTWFEEIFEEYHSPFHN',
114
+ ];
115
+ const sequencesCol = DG.Column.fromStrings('sequence', sequencesArray);
116
+ sequencesCol.semType = DG.SEMTYPE.MACROMOLECULE;
117
+ UnitsHandler.setUnitsToFastaColumn(sequencesCol);
118
+
119
+ const fastaDf = DG.DataFrame.fromColumns([descriptionCol, sequencesCol]);
120
+
121
+ function _testColumnsParser(inputFasta: string) {
122
+ const ffh = new FastaFileHandler(inputFasta);
123
+ const parsedDescriptionsArray = ffh.descriptionsArray;
124
+ const parsedSequencesArray = ffh.sequencesArray;
125
+ expectArray(
126
+ [parsedDescriptionsArray, parsedSequencesArray],
127
+ [descriptionsArray, sequencesArray]
128
+ );
129
+ }
130
+
131
+ // test parser
132
+ test('testNormalFormatting', async () => {
133
+ _testColumnsParser(fastaNormalFormatting);
134
+ });
135
+ test('testExtraSpaces', async () => {
136
+ _testColumnsParser(fastaExtraSpaces);
137
+ });
138
+ test('testExtraNewlines', async () => {
139
+ _testColumnsParser(fastaExtraNewlines);
140
+ });
141
+ });
@@ -2,7 +2,7 @@ import * as C from './constants';
2
2
  import * as DG from 'datagrok-api/dg';
3
3
  import {AminoacidsPalettes} from '@datagrok-libraries/bio/src/aminoacids';
4
4
  import {NucleotidesPalettes} from '@datagrok-libraries/bio/src/nucleotides';
5
- import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
5
+ import {UnknownSeqPalette, UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
6
6
  import {SplitterFunc, WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
7
7
  import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
8
8
  import * as ui from 'datagrok-api/ui';
@@ -44,6 +44,7 @@ export function processSequence(subParts: string[]): [string[], boolean] {
44
44
  return [text, simplified];
45
45
  }
46
46
 
47
+
47
48
  /**
48
49
  * A function that prints a string aligned to left or centered.
49
50
  *
@@ -65,33 +66,46 @@ function printLeftOrCentered(
65
66
  x: number, y: number, w: number, h: number,
66
67
  g: CanvasRenderingContext2D, s: string, color = undefinedColor,
67
68
  pivot: number = 0, left = false, transparencyRate: number = 1.0,
68
- separator: string = '', last: boolean = false): number {
69
+ separator: string = '', last: boolean = false, drawStyle: string = 'classic', maxWord:any={}, maxWordIdx:number=0, gridCell:any = {}): number {
69
70
  g.textAlign = 'start';
70
71
  const colorPart = s.substring(0);
71
- let grayPart = last ? '' : separator;
72
+ let grayPart = last ? '' : separator;
73
+ if (drawStyle === 'msa') {
74
+ grayPart = ' ';
75
+ }
72
76
 
73
- const textSize = g.measureText(colorPart + grayPart);
77
+ let textSize: any = g.measureText(colorPart + grayPart);
74
78
  const indent = 5;
75
79
 
76
- const colorTextSize = g.measureText(colorPart);
80
+ let colorTextSize = g.measureText(colorPart).width;
77
81
  const dy = (textSize.fontBoundingBoxAscent + textSize.fontBoundingBoxDescent) / 2;
82
+ textSize = textSize.width;
83
+ if (drawStyle === 'msa') {
84
+ if (colorTextSize > maxWord) {
85
+ maxWord[maxWordIdx] = colorTextSize;
86
+ gridCell.cell.column.temp = maxWord;
87
+ }
88
+ colorTextSize = maxWord[maxWordIdx];
89
+ textSize = maxWord[maxWordIdx];
90
+ }
78
91
 
79
92
  function draw(dx1: number, dx2: number): void {
80
93
  g.fillStyle = color;
81
94
  g.globalAlpha = transparencyRate;
82
95
  g.fillText(colorPart, x + dx1, y + dy);
83
- g.fillStyle = grayColor;
84
- g.fillText(grayPart, x + dx2, y + dy);
96
+ if (drawStyle === 'classic') {
97
+ g.fillStyle = grayColor;
98
+ g.fillText(grayPart, x + dx2, y + dy);
99
+ }
85
100
  }
86
101
 
87
-
88
- if (left || textSize.width > w) {
89
- draw(indent, indent + colorTextSize.width);
90
- return x + colorTextSize.width + g.measureText(grayPart).width;
102
+ if (left || textSize > w) {
103
+ draw(indent, indent + colorTextSize);
104
+ return x + colorTextSize + g.measureText(grayPart).width;
91
105
  } else {
92
- const dx = (w - textSize.width) / 2;
93
- draw(dx, dx + colorTextSize.width);
94
- return x + dx + colorTextSize.width;
106
+ const dx = (w - textSize) / 2;
107
+ draw(dx, dx + colorTextSize);
108
+ return x + dx + colorTextSize;
95
109
  }
96
110
  }
97
111
 
@@ -114,7 +128,7 @@ function findMonomers(helmString: string) {
114
128
  export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
115
129
  get name(): string { return 'macromoleculeSequence'; }
116
130
 
117
- get cellType(): string { return C.SEM_TYPES.Macro_Molecule; }
131
+ get cellType(): string { return C.SEM_TYPES.MACROMOLECULE; }
118
132
 
119
133
  get defaultHeight(): number { return 30; }
120
134
 
@@ -198,15 +212,51 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
198
212
  const separator = gridCell.cell.column.getTag('separator') ?? '';
199
213
  const splitterFunc: SplitterFunc = WebLogo.getSplitter(units, gridCell.cell.column.getTag('separator'));
200
214
 
215
+ // обработка новых елементов
216
+ const columns = gridCell.cell.column.categories;
217
+ let maxLengthWords = {};
218
+ // check if gridCell.cell.column.temp is array
219
+ if (gridCell.cell.column.getTag('.calculatedCellRender') !== 'exist') {
220
+ for (let i = 0; i < columns.length; i++) {
221
+ let subParts: string[] = splitterFunc(columns[i]);
222
+ subParts.forEach((amino, index) => {
223
+ //@ts-ignore
224
+ let textSizeWidth = g.measureText(WebLogo.monomerToText(amino) + ' ');
225
+ //@ts-ignore
226
+ if (textSizeWidth.width > (maxLengthWords[index] ?? 0)) {
227
+ //@ts-ignore
228
+ maxLengthWords[index] = textSizeWidth.width;
229
+ }
230
+ });
231
+ }
232
+ gridCell.cell.column.temp = maxLengthWords;
233
+ gridCell.cell.column.setTag('.calculatedCellRender', 'exist');
234
+ } else {
235
+ maxLengthWords = gridCell.cell.column.temp;
236
+ }
237
+
201
238
  const subParts: string[] = splitterFunc(cell.value);
202
- // console.log(subParts);
203
239
  let x1 = x;
204
240
  let color = undefinedColor;
241
+ // get max length word in subParts
242
+ let tagUnits = gridCell.cell.column.getTag(DG.TAGS.UNITS);
243
+ let maxLength = 0;
244
+ let maxWord = '';
245
+ let drawStyle = 'classic';
246
+ if (tagUnits.includes('MSA')) {
247
+ subParts.forEach(part => {
248
+ if (part.length > maxLength) {
249
+ maxLength = part.length;
250
+ maxWord = part;
251
+ drawStyle = 'msa';
252
+ }
253
+ });
254
+ }
205
255
  subParts.forEach((amino, index) => {
206
256
  color = palette.get(amino);
207
257
  g.fillStyle = undefinedColor;
208
258
  let last = index === subParts.length - 1;
209
- x1 = printLeftOrCentered(x1, y, w, h, g, amino, color, 0, true, 1.0, separator, last);
259
+ x1 = printLeftOrCentered(x1, y, w, h, g, WebLogo.monomerToText(amino), color, 0, true, 1.0, separator, last, drawStyle, maxLengthWords, index, gridCell);
210
260
  });
211
261
 
212
262
  g.restore();
@@ -215,11 +265,10 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
215
265
  }
216
266
  }
217
267
 
268
+ export class MonomerCellRenderer extends DG.GridCellRenderer {
269
+ get name(): string {return 'MonomerCR';}
218
270
 
219
- export class AminoAcidsCellRenderer extends DG.GridCellRenderer {
220
- get name(): string {return 'aminoAcidsCR';}
221
-
222
- get cellType(): string {return C.SEM_TYPES.AMINO_ACIDS;}
271
+ get cellType(): string {return C.SEM_TYPES.MONOMER;}
223
272
 
224
273
  get defaultHeight(): number {return 15;}
225
274
 
@@ -256,10 +305,10 @@ export class AminoAcidsCellRenderer extends DG.GridCellRenderer {
256
305
  }
257
306
  }
258
307
 
259
- export class AlignedSequenceDifferenceCellRenderer extends DG.GridCellRenderer {
260
- get name(): string {return 'alignedSequenceDifferenceCR';}
308
+ export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
309
+ get name(): string {return 'MacromoleculeDifferenceCR';}
261
310
 
262
- get cellType(): string {return C.SEM_TYPES.ALIGNED_SEQUENCE_DIFFERENCE;}
311
+ get cellType(): string {return C.SEM_TYPES.MACROMOLECULE_DIFFERENCE;}
263
312
 
264
313
  get defaultHeight(): number {return 30;}
265
314
 
@@ -295,23 +344,28 @@ export class AlignedSequenceDifferenceCellRenderer extends DG.GridCellRenderer {
295
344
  //TODO: can this be replaced/merged with splitSequence?
296
345
  const [s1, s2] = s.split('#');
297
346
  const separator = gridCell.tableColumn!.tags[C.TAGS.SEPARATOR];
298
- const subParts1 = s1.split(separator);
299
- const subParts2 = s2.split(separator);
347
+ const units: string = gridCell.tableColumn!.tags[DG.TAGS.UNITS];
348
+ const splitter = WebLogo.getSplitter(units, separator);
349
+ const subParts1 = splitter(s1);
350
+ const subParts2 = splitter(s2);
300
351
  const [text] = processSequence(subParts1);
301
352
  const textSize = g.measureText(text.join(''));
302
353
  let updatedX = Math.max(x, x + (w - (textSize.width + subParts1.length * 4)) / 2);
303
354
  // 28 is the height of the two substitutions on top of each other + space
304
355
  const updatedY = Math.max(y, y + (h - 28) / 2);
305
356
 
306
- const palette = getPalleteByType(gridCell.tableColumn!.tags[C.TAGS.ALPHABET]);
357
+ let palette: SeqPalette = UnknownSeqPalettes.Color;
358
+ if (units != 'HELM')
359
+ palette = getPalleteByType(units.substring(units.length - 2));
360
+
361
+ const vShift = 7;
307
362
  for (let i = 0; i < subParts1.length; i++) {
308
363
  const amino1 = subParts1[i];
309
364
  const amino2 = subParts2[i];
310
365
  const color1 = palette.get(amino1);
311
- const color2 = palette.get(amino2);
312
366
 
313
367
  if (amino1 != amino2) {
314
- const vShift = 7;
368
+ const color2 = palette.get(amino2);
315
369
  const subX0 = printLeftOrCentered(updatedX, updatedY - vShift, w, h, g, amino1, color1, 0, true);
316
370
  const subX1 = printLeftOrCentered(updatedX, updatedY + vShift, w, h, g, amino2, color2, 0, true);
317
371
  updatedX = Math.max(subX1, subX0);
@@ -23,12 +23,11 @@ export enum TAGS {
23
23
  }
24
24
 
25
25
  export enum SEM_TYPES {
26
- AMINO_ACIDS = 'aminoAcids',
27
- ALIGNED_SEQUENCE = 'alignedSequence',
28
- ALIGNED_SEQUENCE_DIFFERENCE = 'alignedSequenceDifference',
26
+ MONOMER = 'Monomer',
27
+ MACROMOLECULE_DIFFERENCE = 'MacromoleculeDifference',
29
28
  ACTIVITY = 'activity',
30
29
  ACTIVITY_SCALED = 'activityScaled',
31
- Macro_Molecule = 'Macromolecule',
30
+ MACROMOLECULE = 'Macromolecule',
32
31
  }
33
32
 
34
33
  export const STATS = 'stats';
@@ -50,6 +50,9 @@ export class VdRegionsViewer extends DG.JsViewer {
50
50
  public chains: string[];
51
51
  public sequenceColumnNamePostfix: string;
52
52
 
53
+ public skipEmptyPositions: boolean;
54
+
55
+
53
56
  public get df(): DG.DataFrame {
54
57
  return this.dataFrame;
55
58
  }
@@ -72,6 +75,8 @@ export class VdRegionsViewer extends DG.JsViewer {
72
75
  this.chains = this.stringList('chains', ['Heavy', 'Light'],
73
76
  {choices: ['Heavy', 'Light']});
74
77
  this.sequenceColumnNamePostfix = this.string('sequenceColumnNamePostfix', 'chain sequence');
78
+
79
+ this.skipEmptyPositions = this.bool('skipEmptyPositions', false);
75
80
  }
76
81
 
77
82
  public async init() {
@@ -119,6 +124,17 @@ export class VdRegionsViewer extends DG.JsViewer {
119
124
  break;
120
125
  case 'sequenceColumnNamePostfix':
121
126
  break;
127
+ case 'skipEmptyPositions':
128
+ // for (let orderI = 0; orderI < this.logos.length; orderI++) {
129
+ // for (let chainI = 0; chainI < this.chains.length; chainI++) {
130
+ // const chain: string = this.chains[chainI];
131
+ // this.logos[orderI][chain].setOptions({skipEmptyPositions: this.skipEmptyPositions});
132
+ // }
133
+ // }
134
+ // this.calcSize();
135
+ await this.destroyView();
136
+ await this.buildView();
137
+ break;
122
138
  }
123
139
  }
124
140
  }
@@ -188,6 +204,7 @@ export class VdRegionsViewer extends DG.JsViewer {
188
204
  startPositionName: region!.positionStartName,
189
205
  endPositionName: region!.positionEndName,
190
206
  fixWidth: true,
207
+ skipEmptyPositions: this.skipEmptyPositions,
191
208
  })) as unknown as WebLogo;
192
209
  }
193
210
  // WebLogo creation fires onRootSizeChanged event even before control being added to this.logos