@datagrok/bio 2.6.1 → 2.7.0

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
@@ -5,7 +5,7 @@
5
5
  "name": "Leonid Stolbov",
6
6
  "email": "lstolbov@datagrok.ai"
7
7
  },
8
- "version": "2.6.1",
8
+ "version": "2.7.0",
9
9
  "description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
10
10
  "repository": {
11
11
  "type": "git",
@@ -18,6 +18,12 @@
18
18
  "propertyType": "int",
19
19
  "defaultValue": 3,
20
20
  "nullable": false
21
+ },
22
+ {
23
+ "name": "TooltipWebLogo",
24
+ "propertyType": "bool",
25
+ "defaultValue": "true",
26
+ "nullable": false
21
27
  }
22
28
  ],
23
29
  "dependencies": {
@@ -8,6 +8,7 @@ import {ObjectPropertyBag} from 'datagrok-api/dg';
8
8
  /** Names of package properties/settings declared in properties section of {@link './package.json'} */
9
9
  export const enum BioPackagePropertiesNames {
10
10
  MaxMonomerLength = 'MaxMonomerLength',
11
+ TooltipWebLogo = 'TooltipWebLogo',
11
12
  }
12
13
 
13
14
 
@@ -26,6 +27,15 @@ export class BioPackageProperties extends Map<string, any> {
26
27
  this._onPropertyChanged.next(BioPackagePropertiesNames.MaxMonomerLength);
27
28
  }
28
29
 
30
+ public get tooltipWebLogo(): boolean {
31
+ return super.get(BioPackagePropertiesNames.TooltipWebLogo) as unknown as boolean;
32
+ }
33
+
34
+ public set tooltipWebLogo(value: boolean) {
35
+ super.set(BioPackagePropertiesNames.TooltipWebLogo, value as unknown as boolean);
36
+ this._onPropertyChanged.next(BioPackagePropertiesNames.TooltipWebLogo);
37
+ }
38
+
29
39
  constructor(source: any) {
30
40
  super(Object.entries(source));
31
41
  }
package/src/package.ts CHANGED
@@ -33,7 +33,13 @@ import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
33
33
  import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
34
34
  import {WebLogoViewer} from './viewers/web-logo-viewer';
35
35
  import {createJsonMonomerLibFromSdf, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
36
- import {MonomerLibHelper, getUserLibSettings, setUserLibSetting, getLibFileNameList} from './utils/monomer-lib';
36
+ import {
37
+ MonomerLibHelper,
38
+ getUserLibSettings,
39
+ setUserLibSetting,
40
+ getLibFileNameList,
41
+ getLibraryPanelUI
42
+ } from './utils/monomer-lib';
37
43
  import {getMacromoleculeColumn} from './utils/ui-utils';
38
44
  import {DimReductionMethods, ITSNEOptions, IUMAPOptions} from '@datagrok-libraries/ml/src/reduce-dimensionality';
39
45
  import {SequenceSpaceFunctionEditor} from '@datagrok-libraries/ml/src/functionEditors/seq-space-editor';
@@ -55,9 +61,9 @@ import {splitToMonomersUI} from './utils/split-to-monomers';
55
61
  import {MonomerCellRenderer} from './utils/monomer-cell-renderer';
56
62
  import {BioPackage, BioPackageProperties} from './package-types';
57
63
  import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
58
- import {ObjectPropertyBag} from 'datagrok-api/dg';
59
64
  import {PackageSettingsEditorWidget} from './widgets/package-settings-editor-widget';
60
65
  import {getCompositionAnalysisWidget} from './widgets/composition-analysis-widget';
66
+ import {MacromoleculeColumnWidget} from './utils/macromolecule-column-widget';
61
67
 
62
68
  export const _package = new BioPackage();
63
69
 
@@ -126,17 +132,14 @@ export async function initBio() {
126
132
  //tags: tooltip
127
133
  //input: column col {semType: Macromolecule}
128
134
  //output: widget result
129
- export async function sequenceTooltip(col: DG.Column): Promise<DG.Widget<any>> {
130
- const tv = grok.shell.tv;
131
- const viewer = await tv.dataFrame.plot.fromType('WebLogo', {
132
- sequenceColumnName: col.name,
133
- backgroundColor: 0xFFfdffe5,
134
- fitArea: false,
135
- positionHeight: 'Entropy',
136
- fixWidth: true,
137
- });
138
- viewer.root.style.height = '50px';
139
- return viewer;
135
+ export function sequenceTooltip(col: DG.Column): DG.Widget<any> {
136
+ const resWidget = new MacromoleculeColumnWidget(col);
137
+ const _resPromise = resWidget.init().then(() => { })
138
+ .catch((err: any) => {
139
+ const errMsg = err instanceof Error ? err.message : err.toString();
140
+ grok.shell.error(errMsg);
141
+ });
142
+ return resWidget;
140
143
  }
141
144
 
142
145
  //name: getBioLib
@@ -145,46 +148,12 @@ export function getBioLib(): IMonomerLib {
145
148
  return MonomerLibHelper.instance.getBioLib();
146
149
  }
147
150
 
148
- //name: manageFiles
149
- export async function manageFiles() {
150
- const a = ui.dialog({title: 'Manage files'})
151
- //@ts-ignore
152
- .add(ui.fileBrowser({path: 'System:AppData/Bio/libraries'}).root)
153
- .addButton('OK', () => a.close())
154
- .show();
155
- }
156
-
157
151
  //name: Manage Libraries
158
152
  //input: column seqColumn {semType: Macromolecule}
159
153
  //tags: panel, exclude-actions-panel
160
154
  //output: widget result
161
155
  export async function libraryPanel(_seqColumn: DG.Column): Promise<DG.Widget> {
162
- //@ts-ignore
163
- const filesButton: HTMLButtonElement = ui.button('Manage', manageFiles);
164
- const inputsForm: HTMLDivElement = ui.inputs([]);
165
- const libFileNameList: string[] = await getLibFileNameList();
166
-
167
- let userStoragePromise: Promise<void> = Promise.resolve();
168
- for (const libFileName of libFileNameList) {
169
- const settings = await getUserLibSettings();
170
- const libInput: DG.InputBase<boolean | null> = ui.boolInput(libFileName, !settings.exclude.includes(libFileName),
171
- () => {
172
- userStoragePromise = userStoragePromise.then(async () => {
173
- if (libInput.value == true) {
174
- // Checked library remove from excluded list
175
- settings.exclude = settings.exclude.filter((l) => l != libFileName);
176
- } else {
177
- // Unchecked library add to excluded list
178
- if (!settings.exclude.includes(libFileName)) settings.exclude.push(libFileName);
179
- }
180
- await setUserLibSetting(settings);
181
- await MonomerLibHelper.instance.loadLibraries(true); // from libraryPanel()
182
- grok.shell.info('Monomer library user settings saved.');
183
- });
184
- });
185
- inputsForm.append(libInput.root);
186
- }
187
- return new DG.Widget(ui.divV([inputsForm, ui.div(filesButton)]));
156
+ return getLibraryPanelUI();
188
157
  }
189
158
 
190
159
  // -- Package settings editor --
@@ -614,6 +583,7 @@ export function importFasta(fileContent: string): DG.DataFrame [] {
614
583
  const ffh = new FastaFileHandler(fileContent);
615
584
  return ffh.importFasta();
616
585
  }
586
+
617
587
  //name: importBam
618
588
  //description: Opens Bam file
619
589
  //tags: file-handler
@@ -0,0 +1,47 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as ui from 'datagrok-api/ui';
3
+ import * as DG from 'datagrok-api/dg';
4
+
5
+ import {TAGS as wlTAGS} from '@datagrok-libraries/bio/src/viewers/web-logo';
6
+ import {WebLogoViewer} from '../viewers/web-logo-viewer';
7
+
8
+ import {_package} from '../package';
9
+ import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
10
+
11
+ /** Used in Macromolecule column tooltip */
12
+ export class MacromoleculeColumnWidget extends DG.Widget {
13
+ private viewed: boolean = false;
14
+
15
+ private seqCol: DG.Column<string>;
16
+
17
+ private wlViewer: WebLogoViewer;
18
+
19
+ constructor(seqCol: DG.Column<string>) {
20
+ super(ui.divV([]));
21
+
22
+ this.seqCol = seqCol;
23
+ }
24
+
25
+ async init(): Promise<void> {
26
+ const uh = UnitsHandler.getOrCreate(this.seqCol);
27
+ const pkgTooltipWebLogo = _package.properties.tooltipWebLogo;
28
+ const colTooltipWebLogo = this.seqCol.getTag(wlTAGS.tooltipWebLogo);
29
+
30
+ if (pkgTooltipWebLogo !== false && !['false', 'off', 'disable', 'disabled'].includes(colTooltipWebLogo)) {
31
+ this.wlViewer = await this.seqCol.dataFrame.plot.fromType('WebLogo', {
32
+ sequenceColumnName: this.seqCol.name,
33
+ backgroundColor: 0x00000000,
34
+ positionHeight: 'Entropy',
35
+ positionWidth: (uh.getAlphabetIsMultichar() ? 24 : 16),
36
+ fixWidth: true,
37
+ fitArea: false,
38
+ // maxHeight: 100,
39
+ // minHeight: 25,
40
+ }) as unknown as WebLogoViewer;
41
+ this.wlViewer.root.style.height = `50px`;
42
+
43
+ this.root.appendChild(this.wlViewer.root);
44
+ this.root.style.width = '100%';
45
+ }
46
+ }
47
+ }
@@ -1,14 +1,16 @@
1
- // import * as ui from 'datagrok-api/ui';
2
- import * as DG from 'datagrok-api/dg';
3
1
  import * as grok from 'datagrok-api/grok';
2
+ import * as ui from 'datagrok-api/ui';
3
+ import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {Observable, Subject} from 'rxjs';
6
+
6
7
  import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types/index';
7
8
  import {
8
9
  createJsonMonomerLibFromSdf,
9
10
  IMonomerLibHelper,
10
11
  } from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
11
12
  import {HELM_REQUIRED_FIELDS as REQ, HELM_OPTIONAL_FIELDS as OPT} from '@datagrok-libraries/bio/src/utils/const';
13
+
12
14
  import {_package} from '../package';
13
15
 
14
16
  const _HELM_REQUIRED_FIELDS_ARRAY = [
@@ -51,6 +53,43 @@ export async function setUserLibSetting(value: LibSettings): Promise<void> {
51
53
  await grok.dapi.userDataStorage.postValue(LIB_STORAGE_NAME, 'Settings', JSON.stringify(value), true);
52
54
  }
53
55
 
56
+ export async function manageFiles() {
57
+ const a = ui.dialog({title: 'Manage files'})
58
+ //@ts-ignore
59
+ .add(ui.fileBrowser({path: 'System:AppData/Bio/libraries'}).root)
60
+ .addButton('OK', () => a.close())
61
+ .show();
62
+ }
63
+
64
+ export async function getLibraryPanelUI(): Promise<DG.Widget> {
65
+ //@ts-ignore
66
+ const filesButton: HTMLButtonElement = ui.button('Manage', manageFiles);
67
+ const inputsForm: HTMLDivElement = ui.inputs([]);
68
+ const libFileNameList: string[] = await getLibFileNameList();
69
+
70
+ let userStoragePromise: Promise<void> = Promise.resolve();
71
+ for (const libFileName of libFileNameList) {
72
+ const settings = await getUserLibSettings();
73
+ const libInput: DG.InputBase<boolean | null> = ui.boolInput(libFileName, !settings.exclude.includes(libFileName),
74
+ () => {
75
+ userStoragePromise = userStoragePromise.then(async () => {
76
+ if (libInput.value == true) {
77
+ // Checked library remove from excluded list
78
+ settings.exclude = settings.exclude.filter((l) => l != libFileName);
79
+ } else {
80
+ // Unchecked library add to excluded list
81
+ if (!settings.exclude.includes(libFileName)) settings.exclude.push(libFileName);
82
+ }
83
+ await setUserLibSetting(settings);
84
+ await MonomerLibHelper.instance.loadLibraries(true); // from libraryPanel()
85
+ grok.shell.info('Monomer library user settings saved.');
86
+ });
87
+ });
88
+ inputsForm.append(libInput.root);
89
+ }
90
+ return new DG.Widget(ui.divV([inputsForm, ui.div(filesButton)]));
91
+ }
92
+
54
93
  export class MonomerLib implements IMonomerLib {
55
94
  public readonly error: string | undefined;
56
95
 
@@ -26,6 +26,7 @@ import {
26
26
  WebLogoPropsDefault,
27
27
  } from '@datagrok-libraries/bio/src/viewers/web-logo';
28
28
  import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
29
+ import {intToHtmlA} from '@datagrok-libraries/utils/src/color';
29
30
 
30
31
  import {_package} from '../package';
31
32
 
@@ -155,6 +156,7 @@ export class PositionInfo {
155
156
  jPos: number, absoluteMaxHeight: number, heightMode: PositionHeight, alphabetSizeLog: number,
156
157
  positionWidthWithMargin: number, positionWidth: number, r: number, axisHeight: number
157
158
  ): void {
159
+ const dpr = window.devicePixelRatio;
158
160
  // const rowCount = this.positions[jPos].rowCount;
159
161
  // const alphabetSize = this.getAlphabetSize();
160
162
  // if ((this.positionHeight == PositionHeight.Entropy) && (alphabetSize == null))
@@ -207,7 +209,7 @@ export class PositionInfo {
207
209
  // const m: string = entry[0];
208
210
  const h: number = maxHeight * pmInfo.count / this.rowCount;
209
211
 
210
- pmInfo.bounds = new DG.Rect(jPos * positionWidthWithMargin, y, positionWidth, h);
212
+ pmInfo.bounds = new DG.Rect(jPos * dpr * positionWidthWithMargin, y, positionWidth * dpr, h);
211
213
  y += h;
212
214
  }
213
215
  }
@@ -755,9 +757,9 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
755
757
 
756
758
  // -- Routines --
757
759
 
758
- getMonomer(p: DG.Point): [number, string | null, PositionMonomerInfo | null] {
759
- const calculatedX = p.x + this.firstVisibleIndex * this.positionWidthWithMargin;
760
- const jPos = Math.floor(p.x / this.positionWidthWithMargin + this.firstVisibleIndex);
760
+ getMonomer(p: DG.Point, dpr: number): [number, string | null, PositionMonomerInfo | null] {
761
+ const calculatedX = p.x + this.firstVisibleIndex * this.positionWidthWithMargin * dpr;
762
+ const jPos = Math.floor(p.x / (this.positionWidthWithMargin * dpr) + this.firstVisibleIndex);
761
763
  const position: PositionInfo = this.positions[jPos];
762
764
 
763
765
  if (position === undefined)
@@ -887,7 +889,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
887
889
 
888
890
  const length: number = this.Length;
889
891
  g.resetTransform();
890
- g.fillStyle = DG.Color.toHtml(this.backgroundColor);
892
+ g.fillStyle = intToHtmlA(this.backgroundColor);
891
893
  g.fillRect(0, 0, this.canvas.width, this.canvas.height);
892
894
  g.textBaseline = this.textBaseline;
893
895
 
@@ -902,14 +904,15 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
902
904
  g.textAlign = 'center';
903
905
  g.font = `${positionFontSize.toFixed(1)}px Roboto, Roboto Local, sans-serif`;
904
906
  const posNameMaxWidth = Math.max(...this.positions.map((pos) => g.measureText(pos.name).width));
905
- const hScale = posNameMaxWidth < (this._positionWidth - 2) ? 1 : (this._positionWidth - 2) / posNameMaxWidth;
907
+ const hScale = posNameMaxWidth < (this._positionWidth * dpr - 2) ? 1 :
908
+ (this._positionWidth * dpr - 2) / posNameMaxWidth;
906
909
 
907
910
  for (let jPos = this.firstVisibleIndex; jPos < lastVisibleIndex; jPos++) {
908
911
  const pos: PositionInfo = this.positions[jPos];
909
912
  g.resetTransform();
910
913
  g.setTransform(
911
914
  hScale, 0, 0, 1,
912
- jPos * this.positionWidthWithMargin + this._positionWidth / 2 -
915
+ jPos * this.positionWidthWithMargin * dpr + this._positionWidth * dpr / 2 -
913
916
  this.positionWidthWithMargin * firstVisibleIndex, 0);
914
917
  g.fillText(pos.label, 0, 0);
915
918
  }
@@ -1097,12 +1100,12 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
1097
1100
  }
1098
1101
 
1099
1102
  private canvasOnMouseMove(e: MouseEvent) {
1103
+ const dpr = window.devicePixelRatio;
1100
1104
  try {
1101
1105
  const args = e as MouseEvent;
1102
1106
 
1103
- const dpr: number = window.devicePixelRatio;
1104
1107
  const cursorP: DG.Point = this.canvas.getCursorPosition(args, dpr);
1105
- const [jPos, monomer] = this.getMonomer(cursorP);
1108
+ const [jPos, monomer] = this.getMonomer(cursorP, dpr);
1106
1109
  // if (jPos != undefined && monomer == undefined) {
1107
1110
  // const preEl = ui.element('pre');
1108
1111
  // preEl.innerHTML = jPos < this.positions.length ?
@@ -1133,8 +1136,8 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
1133
1136
  private canvasOnMouseDown(e: MouseEvent): void {
1134
1137
  try {
1135
1138
  const args = e as MouseEvent;
1136
- const r: number = window.devicePixelRatio;
1137
- const [jPos, monomer] = this.getMonomer(this.canvas.getCursorPosition(args, r));
1139
+ const dpr: number = window.devicePixelRatio;
1140
+ const [jPos, monomer] = this.getMonomer(this.canvas.getCursorPosition(args, dpr), dpr);
1138
1141
 
1139
1142
  // prevents deselect all rows if we miss monomer bounds
1140
1143
  if (this.dataFrame && this.seqCol && this.unitsHandler && monomer) {
@@ -5,6 +5,7 @@ import {_package} from '../package';
5
5
 
6
6
  export class PackageSettingsEditorWidget extends DG.Widget {
7
7
  maxMonomerLengthProp: DG.Property;
8
+ tooltipWebLogo: DG.Property;
8
9
 
9
10
  constructor(propList: DG.Property[]) {
10
11
  super(ui.div([], {}));
@@ -13,6 +14,7 @@ export class PackageSettingsEditorWidget extends DG.Widget {
13
14
  Object.assign({}, ...propList.map((p) => ({[p.name]: p})));
14
15
 
15
16
  this.maxMonomerLengthProp = props['MaxMonomerLength'];
17
+ this.tooltipWebLogo = props['TooltipWebLogo'];
16
18
  }
17
19
 
18
20
  async init(): Promise<void> {
@@ -23,6 +25,14 @@ export class PackageSettingsEditorWidget extends DG.Widget {
23
25
  _package.properties.maxMonomerLength = value;
24
26
  });
25
27
 
26
- this.root.appendChild(ui.form([maxMonomerLengthInput,]));
28
+ const tooltipWebLogo = ui.boolInput('Tooltip WebLogo',
29
+ _package.properties.tooltipWebLogo,
30
+ (value: boolean) => {
31
+ _package.properties.tooltipWebLogo = value;
32
+ });
33
+
34
+ this.root.appendChild(ui.form([
35
+ maxMonomerLengthInput,
36
+ tooltipWebLogo]));
27
37
  }
28
38
  }