@datagrok/sequence-translator 1.8.0 → 1.9.1

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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/sequence-translator",
3
3
  "friendlyName": "Sequence Translator",
4
- "version": "1.8.0",
4
+ "version": "1.9.1",
5
5
  "author": {
6
6
  "name": "Leonid Stolbov",
7
7
  "email": "lstolbov@datagrok.ai"
@@ -28,7 +28,7 @@
28
28
  "@datagrok-libraries/utils": "^4.3.7",
29
29
  "@types/react": "^18.0.15",
30
30
  "cash-dom": "^8.1.0",
31
- "datagrok-api": "^1.24.0",
31
+ "datagrok-api": "^1.23.0",
32
32
  "lodash": "^4.17.21",
33
33
  "object-hash": "^3.0.0",
34
34
  "openchemlib": "6.0.1",
@@ -83,7 +83,7 @@
83
83
  "build-all": "npm --prefix ./../../libraries/chem-meta run build && npm --prefix ./../../js-api run build && npm --prefix ./../../libraries/utils run build && npm --prefix ./../../libraries/bio run build && npm --prefix ./../../libraries/tutorials run build && npm run build"
84
84
  },
85
85
  "canEdit": [
86
- "Developers"
86
+ "Administrators"
87
87
  ],
88
88
  "canView": [
89
89
  "All users"
@@ -471,7 +471,7 @@ class ColumnInputsManager {
471
471
  private selectColumnIfTableNotNull(
472
472
  table: DG.DataFrame | null, columnName: string, columnLabel: REQUIRED_COLUMN_LABEL
473
473
  ): void {
474
- if (table !== null) {
474
+ if (table !== null && columnName) {
475
475
  const selectedColumn = table.getCol(columnName);
476
476
  this.eventBus.selectColumn(columnLabel, selectedColumn);
477
477
  }
@@ -34,5 +34,5 @@ export async function test(category: string, test: string, testContext: TestCont
34
34
 
35
35
  //name: initAutoTests
36
36
  export async function initAutoTests() {
37
- await initTests(_package, _package.getModule('package-test.js'));
37
+ await initTests(_package as any, _package.getModule('package-test.js'));
38
38
  }
package/src/package.ts CHANGED
@@ -27,6 +27,7 @@ import {CyclizedNotationProvider} from './utils/cyclized';
27
27
  import {getSeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
28
28
  import {PolyToolTags} from './consts';
29
29
  import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
30
+ import { getPTCombineDialog } from './polytool/pt-combine-dialog';
30
31
 
31
32
  export const _package: OligoToolkitPackage = new OligoToolkitPackage({debug: true}/**/);
32
33
 
@@ -302,6 +303,12 @@ export async function getPtChemEnumeratorDialog(cell?: DG.Cell) {
302
303
  return polyToolEnumerateChemUI(cell);
303
304
  }
304
305
 
306
+ //name: Combine Sequences
307
+ //top-menu: Bio | PolyTool | Combine Sequences...
308
+ export async function getPolyToolCombineDialog() {
309
+ getPTCombineDialog();
310
+ }
311
+
305
312
 
306
313
  //name: applyNotationProviderForHarmonizedSequence
307
314
  //input: column col
@@ -0,0 +1,131 @@
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
+ export async function getPTCombineDialog() {
6
+
7
+ const createEditor = () => {
8
+ const editorDiv = ui.divH([]);
9
+ const tableEditor = ui.input.table('Table', {value: undefined, tooltipText: 'Table with sequences'});
10
+ const columnEditor = ui.input.choice('Column', {items: [] as string[], value: undefined, tooltipText: 'Sequence column'});
11
+
12
+ tableEditor.onChanged.subscribe(async () => {
13
+ const table = tableEditor.value;
14
+ if (!table) {
15
+ columnEditor.items = [];
16
+ columnEditor.value = null;
17
+ return;
18
+ }
19
+ await table.meta.detectSemanticTypes();
20
+ await grok.data.detectSemanticTypes(table);
21
+ const stringColumns = Array.from(table.columns.categorical);
22
+ const stringColumnsNames = stringColumns.map((c) => c.name);
23
+ columnEditor.items = stringColumnsNames;
24
+ columnEditor.value = stringColumns.find((c) => c.semType === DG.SEMTYPE.MACROMOLECULE)?.name ?? stringColumnsNames.find((c) => {
25
+ const lc = c.toLowerCase();
26
+ return lc.includes('seq') || lc.includes('pep');
27
+ }) ?? stringColumnsNames[0];
28
+ });
29
+ editorDiv.appendChild(tableEditor.root);
30
+ editorDiv.appendChild(columnEditor.root);
31
+ editorDiv.style.alignItems = 'center';
32
+ return {root: editorDiv, getValue: () => ({table: tableEditor.value, column: columnEditor.value})};
33
+ }
34
+ type Editor = ReturnType<typeof createEditor>;
35
+
36
+ const editors: Editor[] = [];
37
+ const editorsDiv = ui.divV([]);
38
+
39
+ const insertNewEditor = (index: number) => {
40
+ const editorDiv = createEditor();
41
+ const removeButton = ui.icons.delete(() => {
42
+ if (!editorDiv.root.parentElement || editors.length < 2)
43
+ return;
44
+ editorDiv.root.remove();
45
+ const editorIndex = editors.indexOf(editorDiv);
46
+ if (editorIndex !== -1) {
47
+ editors.splice(editorIndex, 1);
48
+ }
49
+ }, 'Remove');
50
+ const addEditor = ui.icons.add(() => {
51
+ let editorIndex = editors.indexOf(editorDiv);
52
+ if (editorIndex === -1) {
53
+ editorIndex = editors.length;
54
+ }
55
+ insertNewEditor(editorIndex + 1);
56
+ }, 'Add');
57
+ editorDiv.root.appendChild(removeButton);
58
+ editorDiv.root.appendChild(addEditor);
59
+ removeButton.style.marginLeft = '8px';
60
+ removeButton.style.marginRight = '8px';
61
+ removeButton.style.color = 'var(--blue-1)';
62
+ addEditor.style.color = 'var(--blue-1)';
63
+
64
+
65
+ const prevEditor = editors[index];
66
+ if (prevEditor) {
67
+ editorsDiv.insertBefore(editorDiv.root, prevEditor.root);
68
+ } else {
69
+ editorsDiv.appendChild(editorDiv.root);
70
+ }
71
+ editors.splice(index, 0, editorDiv);
72
+ }
73
+ insertNewEditor(0);
74
+
75
+ function validate() {
76
+ const values = editors.map((e) => e.getValue());
77
+ const tables = values.map((v) => v.table);
78
+ const columns = values.map((v) => v.column);
79
+ return tables.every((t) => !!t) && columns.every((c) => !!c);
80
+ }
81
+
82
+ const separatorInput = ui.input.string('Separator', {value: '-', tooltipText: 'Separator for sequences', nullable: false});
83
+
84
+ ui.dialog('Combine Sequences')
85
+ .add(editorsDiv)
86
+ .add(separatorInput.root)
87
+ .onOK(async () => {
88
+ if (!validate()) {
89
+ grok.shell.error('Please fill all the fields');
90
+ return;
91
+ }
92
+ const values = editors.map((e) => e.getValue());
93
+ const tables = values.map((v) => v.table);
94
+ const columns = values.map((v) => v.column);
95
+ const separator = separatorInput.value;
96
+ const cols = columns.map((c, i) => tables[i]!.col(c!)!.toList().filter((v) => !!v));
97
+ // do a DFS to generate all combinations
98
+ let curIndex = 0;
99
+ const totalLength = cols.reduce((acc, c) => acc * c.length, 1);
100
+ if (totalLength > 10_000_000) {
101
+ grok.shell.error('Too many combinations. Maximum allowed is 10M');
102
+ return;
103
+ }
104
+ const result: string[] = new Array(totalLength).fill(null);
105
+
106
+ const dfs = (cur: string, depth: number) => {
107
+ if (depth === cols.length) {
108
+ result[curIndex++] = cur;
109
+ return;
110
+ }
111
+ const actCur = `${cur}${cur ? separator : ''}`;
112
+ const col = cols[depth];
113
+ for (let i = 0; i < cols[depth].length; i++) {
114
+ dfs(actCur + col[i], depth + 1);
115
+ }
116
+ }
117
+ dfs('', 0);
118
+ const df = DG.DataFrame.fromColumns([DG.Column.fromStrings('Combined', result)]);
119
+ df.name = 'Combined Sequences';
120
+ await df.meta.detectSemanticTypes();
121
+ await grok.data.detectSemanticTypes(df);
122
+ grok.shell.addTableView(df);
123
+ })
124
+ .show({resizable: true});
125
+
126
+
127
+
128
+
129
+
130
+
131
+ }