@datagrok/bio 2.26.2 → 2.26.5

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.
@@ -162,10 +162,16 @@ export async function convertDo(srcCol: DG.Column, seqHelper: ISeqHelper, target
162
162
  return '';
163
163
  }
164
164
  });
165
+ newCol.semType = DG.SEMTYPE.MACROMOLECULE;
166
+ newCol.meta.units = NOTATION.CUSTOM;
167
+ newCol.setTag('separator', '-');
168
+ newCol.setTag('aligned', 'SEQ');
169
+ newCol.setTag('alphabet', 'UN');
170
+ newCol.setTag('.alphabetIsMultichar', 'true');
165
171
  srcCol.dataFrame.columns.add(newCol);
166
- const semType = await grok.functions.call('Bio:detectMacromolecule', {col: newCol});
167
- if (semType)
168
- newCol.semType = semType;
172
+ // const semType = await grok.functions.call('Bio:detectMacromolecule', {col: newCol});
173
+ // if (semType)
174
+ // newCol.semType = semType;
169
175
  await grok.data.detectSemanticTypes(srcCol.dataFrame);
170
176
  return newCol;
171
177
  } else {
@@ -3,7 +3,8 @@ import * as DG from 'datagrok-api/dg';
3
3
  import {GridCell} from 'datagrok-api/dg';
4
4
  import * as ui from 'datagrok-api/ui';
5
5
  import {ALPHABET, monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
6
- import {BioTags, GAP_SYMBOL, MONOMER_MOTIF_SPLITTER, TAGS as bioTAGS,} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
6
+ import {BioTags, GAP_SYMBOL, MONOMER_CANONICALIZER_FUNC_TAG, MONOMER_CANONICALIZER_TEMP, MONOMER_MOTIF_SPLITTER, TAGS as bioTAGS,} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
7
+ import {IMonomerCanonicalizer} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
7
8
  import {MONOMER_RENDERER_TAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer';
8
9
  import {getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
9
10
 
@@ -24,6 +25,34 @@ export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase
24
25
  super(gridCol, tableCol);
25
26
  }
26
27
 
28
+ private static readonly _canonLoadingKey = MONOMER_CANONICALIZER_TEMP + '.loading';
29
+
30
+ /** Lazily resolves and caches the IMonomerCanonicalizer from the column tag/temp.
31
+ * Uses async apply() so the plugin package doesn't need to be loaded yet.
32
+ * Returns null while loading; the resolved instance is cached for subsequent calls. */
33
+ private getCanonicalizer(col: DG.Column): IMonomerCanonicalizer | null {
34
+ const cached: IMonomerCanonicalizer | null = col.temp[MONOMER_CANONICALIZER_TEMP] ?? null;
35
+ if (cached) return cached;
36
+ // Already loading — don't fire another request
37
+ if (col.temp[MonomerCellRendererBack._canonLoadingKey]) return null;
38
+ const funcName = col.getTag(MONOMER_CANONICALIZER_FUNC_TAG);
39
+ if (!funcName) return null;
40
+ const parts = funcName.includes(':') ? funcName.split(':') : [undefined, funcName];
41
+ const funcs = DG.Func.find({name: parts[1], package: parts[0]});
42
+ if (funcs.length === 0) return null;
43
+ col.temp[MonomerCellRendererBack._canonLoadingKey] = true;
44
+ funcs[0].apply({}).then((canon: IMonomerCanonicalizer) => {
45
+ col.temp[MONOMER_CANONICALIZER_TEMP] = canon;
46
+ col.temp[MonomerCellRendererBack._canonLoadingKey] = false;
47
+ // Invalidate the grid so cells re-render with the canonicalizer
48
+ if (this.gridCol?.grid?.invalidate)
49
+ this.gridCol.grid.invalidate();
50
+ }).catch(() => {
51
+ col.temp[MonomerCellRendererBack._canonLoadingKey] = false;
52
+ });
53
+ return null;
54
+ }
55
+
27
56
  render(g: CanvasRenderingContext2D,
28
57
  x: number, y: number, w: number, h: number, gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
29
58
  ): void {
@@ -41,6 +70,10 @@ export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase
41
70
  g.textAlign = 'left';
42
71
 
43
72
  let value: string = gridCell.cell.value;
73
+ // render original value
74
+ // const canonicalizer = this.getCanonicalizer(gridCell.cell.column);
75
+ // if (canonicalizer && value)
76
+ // value = canonicalizer.canonicalize(value);
44
77
  if (!value || value === GAP_SYMBOL)
45
78
  value = DASH_GAP_SYMBOL;
46
79
  const symbols = value.split(MONOMER_MOTIF_SPLITTER).map((s) => !s || s === GAP_SYMBOL ? DASH_GAP_SYMBOL : s.trim());
@@ -88,12 +121,16 @@ export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase
88
121
  ) return false;
89
122
 
90
123
  const alphabet = tableCol.getTag(bioTAGS.alphabet) as ALPHABET;
91
- const monomerName: string = gridCell.cell.value;
124
+ const rawMonomerName: string = gridCell.cell.value;
125
+ const canonicalizer = this.getCanonicalizer(tableCol);
126
+ const monomerName = canonicalizer && rawMonomerName ? canonicalizer.canonicalize(rawMonomerName) : rawMonomerName;
92
127
  const canvasClientRect = gridCell.grid.canvas.getBoundingClientRect();
93
128
  const x1 = gridCell.bounds.right + canvasClientRect.left - 4;
94
129
  const y1 = gridCell.bounds.bottom + canvasClientRect.top - 4;
95
130
 
96
- if (!monomerName || monomerName == GAP_SYMBOL || monomerName == DASH_GAP_SYMBOL) {
131
+ const isGap = !rawMonomerName || rawMonomerName == GAP_SYMBOL || rawMonomerName == DASH_GAP_SYMBOL ||
132
+ (canonicalizer != null && canonicalizer.isGap(rawMonomerName));
133
+ if (isGap) {
97
134
  ui.tooltip.show(ui.divText('gap'), x1, y1);
98
135
  return true;
99
136
  }
@@ -44,6 +44,15 @@ export class SeqHandler implements ISeqHandler {
44
44
  private _refinerPromise: Promise<void> = Promise.resolve();
45
45
  public get refinerPromise(): Promise<void> { return this._refinerPromise; }
46
46
 
47
+ private runInnerDetector() {
48
+ if (!this._column.temp['seqHandlerDetectorRun']) {
49
+ this._column.temp['seqHandlerDetectorRun'] = true;
50
+ const detectorFunc = DG.Func.find({name: 'detectMacromolecule', meta: {role: 'semTypeDetector'}})[0];
51
+ if (detectorFunc)
52
+ detectorFunc.applySync({col: this._column});
53
+ }
54
+ }
55
+
47
56
  protected constructor(col: DG.Column<string>,
48
57
  private readonly seqHelper: SeqHelper,
49
58
  ) {
@@ -53,19 +62,20 @@ export class SeqHandler implements ISeqHandler {
53
62
  let units: string | null = this._column.meta.units;
54
63
  if (!units) {
55
64
  // it may be from layout that the macromolecule semtype is set but every other tag is missing, so we manually run detectors
56
- if (!this._column.temp['seqHandlerDetectorRun']) {
57
- this._column.temp['seqHandlerDetectorRun'] = true;
58
- const detectorFunc = DG.Func.find({name: 'detectMacromolecule', meta: {role: 'semTypeDetector'}})[0];
59
- if (detectorFunc)
60
- detectorFunc.applySync({col: this._column});
61
- units = this._column.meta.units;
62
- }
65
+ this.runInnerDetector();
66
+ units = this._column.meta.units;
63
67
  if (!units)
64
68
  throw new Error('Units are not specified in column');
65
69
  }
66
70
  this._units = units!;
67
71
 
68
72
  this._notation = this.getNotation();
73
+ if ([NOTATION.BILN, NOTATION.CUSTOM, NOTATION.SEPARATOR].includes(this._notation) && !this.column.getTag(TAGS.separator)) {
74
+ this.runInnerDetector();
75
+ if (!this.column.getTag(TAGS.separator))
76
+ throw new Error(`Separator tag '${TAGS.separator}' is not set for notation '${this._notation}'.`);
77
+ }
78
+
69
79
  if (this.isCustom() || this.isBiln()) {
70
80
  // this.column.temp[SeqTemps.notationProvider] must be set at detector stage
71
81
  this.notationProvider = this.column.temp[SeqTemps.notationProvider] ?? null;
@@ -185,7 +195,7 @@ export class SeqHandler implements ISeqHandler {
185
195
  public static setTags(uh: SeqHandler): void {
186
196
  const units = uh.column.meta.units as NOTATION;
187
197
 
188
- if ([NOTATION.FASTA, NOTATION.SEPARATOR].includes(units)) {
198
+ if ([NOTATION.FASTA, NOTATION.SEPARATOR, NOTATION.BILN].includes(units)) {
189
199
  // Empty monomer alphabet is allowed, only if alphabet tag is annotated
190
200
  if (!uh.column.getTag(TAGS.alphabet) && Object.keys(uh.stats.freq).length === 0)
191
201
  throw new Error('Alphabet is empty and not annotated.');