@datagrok/bio 2.7.1 → 2.8.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.7.1",
8
+ "version": "2.8.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",
@@ -24,14 +24,20 @@
24
24
  "propertyType": "bool",
25
25
  "defaultValue": "true",
26
26
  "nullable": false
27
+ },
28
+ {
29
+ "name": "DefaultSeparator",
30
+ "propertyType": "string",
31
+ "defaultValue": ".",
32
+ "nullable": false
27
33
  }
28
34
  ],
29
35
  "dependencies": {
30
36
  "@biowasm/aioli": "^3.1.0",
31
- "@datagrok-libraries/bio": "^5.33.2",
37
+ "@datagrok-libraries/bio": "^5.34.0",
32
38
  "@datagrok-libraries/chem-meta": "^1.0.1",
33
39
  "@datagrok-libraries/ml": "^6.3.39",
34
- "@datagrok-libraries/tutorials": "^1.3.2",
40
+ "@datagrok-libraries/tutorials": "^1.3.6",
35
41
  "@datagrok-libraries/utils": "^4.0.17",
36
42
  "cash-dom": "^8.0.0",
37
43
  "css-loader": "^6.7.3",
@@ -53,9 +59,9 @@
53
59
  "source-map-loader": "^4.0.1",
54
60
  "ts-loader": "^9.2.5",
55
61
  "typescript": "^4.8.4",
56
- "webpack": "^5.76.0",
62
+ "webpack": "^5.76.3",
57
63
  "webpack-bundle-analyzer": "latest",
58
- "webpack-cli": "^4.6.0",
64
+ "webpack-cli": "^4.9.1",
59
65
  "@datagrok/chem": "1.4.21",
60
66
  "@datagrok/helm": "2.1.7"
61
67
  },
@@ -9,6 +9,7 @@ import {ObjectPropertyBag} from 'datagrok-api/dg';
9
9
  export const enum BioPackagePropertiesNames {
10
10
  MaxMonomerLength = 'MaxMonomerLength',
11
11
  TooltipWebLogo = 'TooltipWebLogo',
12
+ DefaultSeparator = 'DefaultSeparator',
12
13
  }
13
14
 
14
15
 
@@ -18,24 +19,34 @@ export class BioPackageProperties extends Map<string, any> {
18
19
  public get onPropertyChanged(): Observable<string> { return this._onPropertyChanged; }
19
20
 
20
21
  /** Monomer name maximum length displayed in short mode. */
21
- public get maxMonomerLength(): number {
22
- return super.get(BioPackagePropertiesNames.MaxMonomerLength) as unknown as number;
22
+ public get MaxMonomerLength(): number {
23
+ return super.get(BioPackagePropertiesNames.MaxMonomerLength) as number;
23
24
  }
24
25
 
25
- public set maxMonomerLength(value: number) {
26
- super.set(BioPackagePropertiesNames.MaxMonomerLength, value as unknown as object);
26
+ public set MaxMonomerLength(value: number) {
27
+ super.set(BioPackagePropertiesNames.MaxMonomerLength, value);
27
28
  this._onPropertyChanged.next(BioPackagePropertiesNames.MaxMonomerLength);
28
29
  }
29
30
 
30
- public get tooltipWebLogo(): boolean {
31
- return super.get(BioPackagePropertiesNames.TooltipWebLogo) as unknown as boolean;
31
+ public get TooltipWebLogo(): boolean {
32
+ return super.get(BioPackagePropertiesNames.TooltipWebLogo) as boolean;
32
33
  }
33
34
 
34
- public set tooltipWebLogo(value: boolean) {
35
- super.set(BioPackagePropertiesNames.TooltipWebLogo, value as unknown as boolean);
35
+ public set TooltipWebLogo(value: boolean) {
36
+ super.set(BioPackagePropertiesNames.TooltipWebLogo, value);
36
37
  this._onPropertyChanged.next(BioPackagePropertiesNames.TooltipWebLogo);
37
38
  }
38
39
 
40
+ public get DefaultSeparator(): string {
41
+ return super.get(BioPackagePropertiesNames.DefaultSeparator) as string;
42
+ }
43
+
44
+ public set DefaultSeparator(value: string) {
45
+ if (value.length !== 1) throw new Error('The separator must be of length one.');
46
+ super.set(BioPackagePropertiesNames.DefaultSeparator, value);
47
+ this._onPropertyChanged.next(BioPackagePropertiesNames.DefaultSeparator);
48
+ }
49
+
39
50
  constructor(source: any) {
40
51
  super(Object.entries(source));
41
52
  }
package/src/package.ts CHANGED
@@ -64,6 +64,7 @@ import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
64
64
  import {PackageSettingsEditorWidget} from './widgets/package-settings-editor-widget';
65
65
  import {getCompositionAnalysisWidget} from './widgets/composition-analysis-widget';
66
66
  import {MacromoleculeColumnWidget} from './utils/macromolecule-column-widget';
67
+ import {addCopyMenuUI} from './utils/context-menu';
67
68
 
68
69
  export const _package = new BioPackage();
69
70
 
@@ -262,9 +263,9 @@ export function SeqActivityCliffsEditor(call: DG.FuncCall) {
262
263
  .show();
263
264
  }
264
265
 
265
- //top-menu: Bio | SAR | Activity Cliffs...
266
+ //top-menu: Bio | Analyze | Activity Cliffs...
266
267
  //name: Sequence Activity Cliffs
267
- //description: detect activity cliffs
268
+ //description: Detects pairs of molecules with similar structure and significant difference in any given property
268
269
  //input: dataframe table [Input data table]
269
270
  //input: column molecules {semType: Macromolecule}
270
271
  //input: column activities
@@ -356,8 +357,9 @@ export function SequenceSpaceEditor(call: DG.FuncCall) {
356
357
  .show();
357
358
  }
358
359
 
359
- //top-menu: Bio | Structure | Sequence Space...
360
+ //top-menu: Bio | Analyze | Sequence Space...
360
361
  //name: Sequence Space
362
+ //description: Creates 2D sequence space with projected sequences by pairwise distance
361
363
  //input: dataframe table
362
364
  //input: column molecules { semType: Macromolecule }
363
365
  //input: string methodName { choices:["UMAP", "t-SNE"] }
@@ -454,9 +456,9 @@ export async function sequenceSpaceTopMenu(
454
456
  } */
455
457
  };
456
458
 
457
- //top-menu: Bio | Atomic Level | To Atomic Level...
459
+ //top-menu: Bio | Convert | To Atomic Level...
458
460
  //name: To Atomic Level
459
- //description: returns molfiles for each monomer from HELM library
461
+ //description: Converts sequences to molblocks
460
462
  //input: dataframe df [Input data table]
461
463
  //input: column macroMolecule {semType: Macromolecule}
462
464
  export async function toAtomicLevel(df: DG.DataFrame, macroMolecule: DG.Column): Promise<void> {
@@ -477,8 +479,9 @@ export async function toAtomicLevel(df: DG.DataFrame, macroMolecule: DG.Column):
477
479
  grok.shell.warning(ui.list(atomicLevelRes.warnings));
478
480
  }
479
481
 
480
- //top-menu: Bio | Alignment | MSA...
481
- //name: MSA...
482
+ //top-menu: Bio | Analyze | MSA...
483
+ //name: MSA
484
+ //description: Performs multiple sequence alignment
482
485
  //tags: bio, panel
483
486
  export function multipleSequenceAlignmentDialog(): void {
484
487
  multipleSequenceAlignmentUI();
@@ -495,8 +498,9 @@ export async function alignSequences(sequenceCol: DG.Column<string> | null = nul
495
498
  return multipleSequenceAlignmentUI({col: sequenceCol, clustersCol});
496
499
  }
497
500
 
498
- //top-menu: Bio | Structure | Composition Analysis
501
+ //top-menu: Bio | Analyze | Composition
499
502
  //name: Composition Analysis
503
+ //description: Visualizes sequence composition on a WebLogo plot
500
504
  //meta.icon: files/icons/composition-analysis.svg
501
505
  //output: viewer result
502
506
  export async function compositionAnalysis(): Promise<void> {
@@ -554,7 +558,7 @@ export async function compositionAnalysis(): Promise<void> {
554
558
  await handler(col);
555
559
  }
556
560
 
557
- //top-menu: Bio | Atomic Level | SDF to JSON Library...
561
+ //top-menu: Bio | Convert | SDF to JSON Library...
558
562
  //name: SDF to JSON Library
559
563
  //input: dataframe table
560
564
  export async function sdfToJsonLib(table: DG.DataFrame) {
@@ -595,7 +599,7 @@ export function importBam(fileContent: string): DG.DataFrame [] {
595
599
  return [];
596
600
  }
597
601
 
598
- //top-menu: Bio | Convert...
602
+ //top-menu: Bio | Convert | Notation...
599
603
  //name: convertDialog
600
604
  export function convertDialog() {
601
605
  const col = getMacromoleculeColumn();
@@ -675,7 +679,7 @@ export function SplitToMonomersEditor(call: DG.FuncCall): void {
675
679
  .show();
676
680
  }
677
681
 
678
- //top-menu: Bio | Split to Monomers
682
+ //top-menu: Bio | Convert | Split to Monomers...
679
683
  //name: Split to Monomers
680
684
  //input: dataframe table
681
685
  //input: column sequence { semType: Macromolecule }
@@ -702,9 +706,9 @@ export function similaritySearchViewer(): SequenceSimilarityViewer {
702
706
  return new SequenceSimilarityViewer();
703
707
  }
704
708
 
705
- //top-menu: Bio | Search | Similarity Search
709
+ //top-menu: Bio | Search | Similarity
706
710
  //name: similaritySearch
707
- //description: finds the most similar sequence
711
+ //description: Finds similar sequences
708
712
  //output: viewer result
709
713
  export function similaritySearchTopMenu(): void {
710
714
  const view = (grok.shell.v as DG.TableView);
@@ -720,9 +724,9 @@ export function diversitySearchViewer(): SequenceDiversityViewer {
720
724
  return new SequenceDiversityViewer();
721
725
  }
722
726
 
723
- //top-menu: Bio | Search | Diversity Search
727
+ //top-menu: Bio | Search | Diversity
724
728
  //name: diversitySearch
725
- //description: finds the most diverse molecules
729
+ //description: Finds the most diverse sequences
726
730
  //output: viewer result
727
731
  export function diversitySearchTopMenu() {
728
732
  const view = (grok.shell.v as DG.TableView);
@@ -730,8 +734,9 @@ export function diversitySearchTopMenu() {
730
734
  view.dockManager.dock(viewer, 'down');
731
735
  }
732
736
 
733
- //top-menu: Bio | Structure | Substructure Search ...
737
+ //top-menu: Bio | Search | Substructure...
734
738
  //name: bioSubstructureSearch
739
+ //description: Finds sequence with the given subsequence
735
740
  export function bioSubstructureSearch(): void {
736
741
  const col = getMacromoleculeColumn();
737
742
  substructureSearchDialog(col);
@@ -768,6 +773,14 @@ export async function webLogoLargeApp(): Promise<void> {
768
773
  }
769
774
  }
770
775
 
776
+ // -- Handle context menu --
777
+
778
+ ///name: addCopyMenu
779
+ //input: object cell
780
+ //input: object menu
781
+ export function addCopyMenu(cell: DG.Cell, menu: DG.Menu): void {
782
+ addCopyMenuUI(cell, menu);
783
+ }
771
784
 
772
785
  // -- Demo --
773
786
 
@@ -105,6 +105,7 @@ export async function helmSubstructureSearch(substructure: string, col: DG.Colum
105
105
  if (col.version !== col.temp[MONOMERIC_COL_TAGS.LAST_INVALIDATED_VERSION])
106
106
  await invalidateMols(col, true);
107
107
  const substructureCol: DG.Column<string> = DG.Column.string('helm', 1).init((_i) => substructure);
108
+ substructureCol.semType = DG.SEMTYPE.MACROMOLECULE;
108
109
  substructureCol.setTag(DG.TAGS.UNITS, NOTATION.HELM);
109
110
  const substructureMolsCol =
110
111
  await getMonomericMols(substructureCol, true, col.temp[MONOMERIC_COL_TAGS.MONOMERS_DICT]);
@@ -129,7 +129,7 @@ RNA1{P.R(U)P.R(U)P.R(C)P.R(A)P.R(A)P.R(C)P.P.P}$$$$`,
129
129
  return df;
130
130
  }
131
131
 
132
- function converter(tgtNotation: NOTATION, tgtSeparator: string | null = null): ConverterFunc {
132
+ function converter(tgtNotation: NOTATION, tgtSeparator?: string): ConverterFunc {
133
133
  if (tgtNotation === NOTATION.SEPARATOR && !tgtSeparator)
134
134
  throw new Error(`Argument 'separator' is mandatory for target notation '${tgtNotation.toString()}'.`);
135
135
 
@@ -141,12 +141,12 @@ RNA1{P.R(U)P.R(U)P.R(C)P.R(A)P.R(A)P.R(C)P.P.P}$$$$`,
141
141
  };
142
142
  }
143
143
 
144
- async function _testConvert(srcKey: Samples, converter: ConverterFunc, tgtKey: Samples) {
144
+ async function _testConvert(srcKey: Samples, colConverter: ConverterFunc, tgtKey: Samples) {
145
145
  const srcDf: DG.DataFrame = await readCsv(srcKey);
146
146
  const srcCol: DG.Column = srcDf.getCol('seq');
147
147
 
148
148
  // conversion results
149
- const resCol: DG.Column = converter(srcCol);
149
+ const resCol: DG.Column = colConverter(srcCol);
150
150
 
151
151
  // The correct reference data to compare conversion results with.
152
152
  const tgtDf: DG.DataFrame = await readCsv(tgtKey);
@@ -135,7 +135,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
135
135
  const tempMonomerWidth: string | null = tableColTemp[tempTAGS.monomerWidth];
136
136
  const monomerWidth: string = (tempMonomerWidth != null) ? tempMonomerWidth : 'short';
137
137
  if (monomerWidth === 'short')
138
- maxLengthOfMonomer = tableColTemp[mmcrTemps.maxMonomerLength] ?? _package.properties.maxMonomerLength;
138
+ maxLengthOfMonomer = tableColTemp[mmcrTemps.maxMonomerLength] ?? _package.properties.MaxMonomerLength;
139
139
 
140
140
 
141
141
  let seqColTemp: MonomerPlacer = tableCol.temp[tempTAGS.bioSeqCol];
@@ -0,0 +1,30 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as DG from 'datagrok-api/dg';
3
+ import * as ui from 'datagrok-api/ui';
4
+
5
+ import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
6
+ import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
7
+ import {NotationConverter} from '@datagrok-libraries/bio/src/utils/notation-converter';
8
+
9
+ import {_package} from '../package';
10
+
11
+
12
+ export function addCopyMenuUI(cell: DG.Cell, menu: DG.Menu): void {
13
+ const uh = UnitsHandler.getOrCreate(cell.column);
14
+ const tgtNotationList: string[] = Object.values(NOTATION).filter((v) => v !== uh.units);
15
+
16
+ menu.group('Copy')
17
+ .items(tgtNotationList, (tgtNotation) => {
18
+ const nc = new NotationConverter(cell.column);
19
+ const separator = tgtNotation === NOTATION.SEPARATOR ? _package.properties.DefaultSeparator : undefined;
20
+ const converter = nc.getConverter(tgtNotation as NOTATION, separator);
21
+ const tgtSeq = converter(cell.value);
22
+
23
+ if (!navigator.clipboard) {
24
+ grok.shell.warning('The clipboard functionality requires a secure origin — either HTTPS or localhost');
25
+ } else {
26
+ navigator.clipboard.writeText(tgtSeq);
27
+ grok.shell.info(`Value of notation '${tgtNotation}' copied to clipboard`);
28
+ }
29
+ });
30
+ }
@@ -97,7 +97,7 @@ export function convert(col?: DG.Column): void {
97
97
  ]))
98
98
  .onOK(async () => {
99
99
  const targetNotation = targetNotationInput.value as NOTATION;
100
- const separator: string | null = separatorInput.value;
100
+ const separator: string | undefined = separatorInput.value ?? undefined;
101
101
 
102
102
  await convertDo(tgtCol, targetNotation, separator);
103
103
  })
@@ -116,9 +116,7 @@ export function convert(col?: DG.Column): void {
116
116
  * @param {NOTATION} targetNotation Target notation
117
117
  * @param {string | null} separator Separator for SEPARATOR notation
118
118
  */
119
- export async function convertDo(
120
- srcCol: DG.Column, targetNotation: NOTATION, separator: string | null,
121
- ): Promise<DG.Column> {
119
+ export async function convertDo(srcCol: DG.Column, targetNotation: NOTATION, separator?: string): Promise<DG.Column> {
122
120
  const converter = new NotationConverter(srcCol);
123
121
  const newColumn = converter.convert(targetNotation, separator);
124
122
  srcCol.dataFrame.columns.add(newColumn);
@@ -24,7 +24,7 @@ export class MacromoleculeColumnWidget extends DG.Widget {
24
24
 
25
25
  async init(): Promise<void> {
26
26
  const uh = UnitsHandler.getOrCreate(this.seqCol);
27
- const pkgTooltipWebLogo = _package.properties.tooltipWebLogo;
27
+ const pkgTooltipWebLogo = _package.properties.TooltipWebLogo;
28
28
  const colTooltipWebLogo = this.seqCol.getTag(wlTAGS.tooltipWebLogo);
29
29
 
30
30
  if (pkgTooltipWebLogo !== false && !['false', 'off', 'disable', 'disabled'].includes(colTooltipWebLogo)) {
@@ -316,7 +316,8 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
316
316
  this.filterSourceInput.root.style.position = 'absolute';
317
317
  this.filterSourceInput.root.style.left = '10px';
318
318
  this.filterSourceInput.root.style.top = '-3px';
319
- ui.tooltip.bind(this.filterSourceInput.root, 'Check to filter sequences for selected VRs');
319
+ //this.filterSourceInput.setTooltip('Check to filter sequences for selected VRs'); // TODO: GROK-13614
320
+ ui.tooltip.bind(this.filterSourceInput.input, 'Check to filter sequences for selected VRs');
320
321
 
321
322
  const _color: string = `#ffbb${Math.ceil(Math.random() * 255).toString(16)}`;
322
323
  this.host = ui.div([this.mainLayout, this.filterSourceInput!.root],
@@ -6,6 +6,7 @@ import {_package} from '../package';
6
6
  export class PackageSettingsEditorWidget extends DG.Widget {
7
7
  maxMonomerLengthProp: DG.Property;
8
8
  tooltipWebLogo: DG.Property;
9
+ defaultSeparator: DG.Property;
9
10
 
10
11
  constructor(propList: DG.Property[]) {
11
12
  super(ui.div([], {}));
@@ -15,24 +16,33 @@ export class PackageSettingsEditorWidget extends DG.Widget {
15
16
 
16
17
  this.maxMonomerLengthProp = props['MaxMonomerLength'];
17
18
  this.tooltipWebLogo = props['TooltipWebLogo'];
19
+ this.defaultSeparator = props['DefaultSeparator'];
18
20
  }
19
21
 
20
22
  async init(): Promise<void> {
21
23
  const maxMonomerLengthInput = ui.intInput('Max monomer length',
22
- _package.properties.maxMonomerLength,
24
+ _package.properties.MaxMonomerLength,
23
25
  (value: number) => {
24
26
  // Handle user changed value
25
- _package.properties.maxMonomerLength = value;
27
+ _package.properties.MaxMonomerLength = value;
26
28
  });
27
29
 
28
- const tooltipWebLogo = ui.boolInput('Tooltip WebLogo',
29
- _package.properties.tooltipWebLogo,
30
+ const tooltipWebLogoInput = ui.boolInput('Tooltip WebLogo',
31
+ _package.properties.TooltipWebLogo,
30
32
  (value: boolean) => {
31
- _package.properties.tooltipWebLogo = value;
33
+ _package.properties.TooltipWebLogo = value;
34
+ });
35
+
36
+ const defaultSeparatorInput = ui.choiceInput('Default Separator',
37
+ _package.properties.DefaultSeparator, ['.', '/', '-'],
38
+ (value: string) => {
39
+ _package.properties.DefaultSeparator = value;
32
40
  });
33
41
 
34
42
  this.root.appendChild(ui.form([
35
43
  maxMonomerLengthInput,
36
- tooltipWebLogo]));
44
+ tooltipWebLogoInput,
45
+ defaultSeparatorInput,
46
+ ]));
37
47
  }
38
48
  }
@@ -32,7 +32,7 @@ export function getMacromoleculeColumnPropertyPanel(col: DG.Column): DG.Widget {
32
32
  );
33
33
 
34
34
  const maxMonomerLength: DG.InputBase = ui.intInput('Max monomer length',
35
- col.temp[mmcrTemps.maxMonomerLength] ?? _package.properties.maxMonomerLength,
35
+ col.temp[mmcrTemps.maxMonomerLength] ?? _package.properties.MaxMonomerLength,
36
36
  (value: number) => {
37
37
  col.temp[mmcrTemps.maxMonomerLength] = value;
38
38
  col.setTag(mmcrTags.RendererSettingsChanged, rendererSettingsChangedState.true);
package/tsconfig.json CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  /* Basic Options */
6
6
  // "incremental": true, /* Enable incremental compilation */
7
- "target": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
7
+ "target": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
8
8
  "module": "es2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
9
9
  "lib": ["ES2022", "dom"], /* Specify library files to be included in the compilation. */
10
10
  // "allowJs": true, /* Allow javascript files to be compiled. */
@@ -66,8 +66,7 @@
66
66
 
67
67
  /* Advanced Options */
68
68
  "skipLibCheck": false, /* Skip type checking of declaration files. */
69
- "forceConsistentCasingInFileNames": true,
70
- /* Disallow inconsistently-cased references to the same file. */
69
+ "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
71
70
  //"jsx": "react"
72
71
  }
73
72
  }
package/webpack.config.js CHANGED
@@ -36,7 +36,7 @@ module.exports = {
36
36
  },
37
37
  output: {
38
38
  filename: '[name].js',
39
- library: 'bio',
39
+ library: packageName,
40
40
  libraryTarget: 'var',
41
41
  path: path.resolve(__dirname, 'dist'),
42
42
  },