@datagrok/bio 2.11.37 → 2.11.39

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.
Files changed (36) hide show
  1. package/.eslintrc.json +2 -3
  2. package/CHANGELOG.md +14 -0
  3. package/dist/package-test.js +3 -3
  4. package/dist/package-test.js.map +1 -1
  5. package/dist/package.js +3 -3
  6. package/dist/package.js.map +1 -1
  7. package/dockerfiles/Dockerfile +52 -1
  8. package/files/tests/100_3_clustests.csv +1 -1
  9. package/package.json +2 -2
  10. package/src/demo/bio01-similarity-diversity.ts +1 -1
  11. package/src/demo/bio01a-hierarchical-clustering-and-sequence-space.ts +1 -1
  12. package/src/demo/bio01b-hierarchical-clustering-and-activity-cliffs.ts +1 -1
  13. package/src/package.ts +2 -2
  14. package/src/tests/activity-cliffs-tests.ts +1 -1
  15. package/src/tests/detectors-tests.ts +2 -2
  16. package/src/tests/msa-tests.ts +1 -1
  17. package/src/tests/pepsea-tests.ts +53 -13
  18. package/src/tests/renderers-test.ts +3 -3
  19. package/src/tests/utils/test-logger.ts +29 -0
  20. package/src/tests/utils.ts +0 -8
  21. package/src/tests/viewers.ts +1 -1
  22. package/src/utils/docker.ts +39 -23
  23. package/src/utils/multiple-sequence-alignment-ui.ts +16 -5
  24. package/src/utils/pepsea.ts +59 -12
  25. package/files/samples/sample_FASTA.csv +0 -65
  26. package/files/samples/sample_HELM.csv +0 -541
  27. package/files/samples/sample_MSA.csv +0 -541
  28. /package/files/{data/sample_FASTA.fasta → samples/FASTA.fasta} +0 -0
  29. /package/files/{data/sample_FASTA_DNA.csv → samples/FASTA_DNA.csv} +0 -0
  30. /package/files/{data/sample_FASTA_PT.csv → samples/FASTA_PT.csv} +0 -0
  31. /package/files/{data/sample_FASTA_PT_activity.csv → samples/FASTA_PT_activity.csv} +0 -0
  32. /package/files/{data/sample_FASTA_RNA.csv → samples/FASTA_RNA.csv} +0 -0
  33. /package/files/{data/sample_HELM_50.csv → samples/HELM_50.csv} +0 -0
  34. /package/files/{data/sample_HELM_empty_unkn.csv → samples/HELM_empty_unkn.csv} +0 -0
  35. /package/files/{data/sample_HELM_empty_vals.csv → samples/HELM_empty_vals.csv} +0 -0
  36. /package/files/{data/sample_SEPARATOR_PT.csv → samples/SEPARATOR_PT.csv} +0 -0
@@ -55,11 +55,62 @@ def distout():\n\
55
55
  if distout_output is None:\n\
56
56
  raise FileNotFoundError("distance matrix is not found")\n\
57
57
  \n\
58
- return distout_output\n' >> /opt/PepSeA-main/alignment/api.py;
58
+ return distout_output\n\
59
+ \n\
60
+ from fastapi import Request, HTTPException\n\
61
+ import sys\n\
62
+ import ujson as json\n\
63
+ from io import StringIO\n\
64
+ from subprocess import CalledProcessError\n\
65
+ \n\
66
+ @api.middleware("http")\n\
67
+ async def error_to_json_middleware(request: Request, call_next):\n\
68
+ sys.stderr = temp_stderr = StringIO()\n\
69
+ response = None\n\
70
+ try:\n\
71
+ response = await call_next(request)\n\
72
+ finally:\n\
73
+ sys.stderr = sys.__stderr__\n\
74
+ stderr_msg = temp_stderr.getvalue()\n\
75
+ if len(stderr_msg) > 0 and response and response.headers["content-type"] == "application/json":\n\
76
+ response_body = [chunk async for chunk in response.body_iterator]\n\
77
+ response_str = "".join(((s.decode() if isinstance(s, bytes) else str(s)) for s in response_body))\n\
78
+ response_obj = json.loads(response_str)\n\
79
+ response_obj.update({"pepsea-stderr": stderr_msg})\n\
80
+ headers = {}\n\
81
+ for header_name in response.headers:\n\
82
+ if header_name != "content-length" and header_name != "content-type":\n\
83
+ headers[header_name] = response.headers[header_name]\n\
84
+ json_response = JSONResponse(status_code=response.status_code, headers=headers, content=response_obj)\n\
85
+ return json_response\n\
86
+ return response\n\
87
+ \n\
88
+ \n\
89
+ def pepsea_json_response(err_msg: str):\n\
90
+ return JSONResponse(status_code=500, content={\n\
91
+ "pepsea-error": err_msg,\n\
92
+ })\n\
93
+ \n\
94
+ \n\
95
+ @api.exception_handler(CalledProcessError)\n\
96
+ async def called_process_error_handler(request: Request, ex: CalledProcessError):\n\
97
+ s = ex.stderr\n\
98
+ err_msg = s.decode() if isinstance(s, bytes) else str(ex.stderr)\n\
99
+ sys.__stderr__.write("called_process_error_handler() {0}: {1}\\n".format(type(ex).__name__, err_msg))\n\
100
+ return pepsea_json_response(err_msg)\n\
101
+ \n\
102
+ \n\
103
+ @api.exception_handler(Exception)\n\
104
+ async def base_exception_handler(request: Request, ex: Exception):\n\
105
+ err_msg = str(ex)\n\
106
+ sys.__stderr__.write("base_exception_handler() {0}: {1}\\n".format(type(ex).__name__, err_msg))\n\
107
+ return pepsea_json_response(err_msg)\n\
108
+ \n' >> /opt/PepSeA-main/alignment/api.py;
59
109
 
60
110
  # It is important to run docker container as user and not as root
61
111
  USER grok:grok
62
112
 
113
+
63
114
  # Command source does not work for Docker, cause it will apply only to one layer
64
115
  # The PATH works better for Docker
65
116
  ARG VIRTUAL_ENV=${HOME_DIR}/venv
@@ -97,4 +97,4 @@ cluster,sequence_id,sequence,activity,is_cliff
97
97
  2,c2_seq1052,NQYRREPRGCRSPVWK,-0.18,False
98
98
  2,c2_seq1053,GMGWYQHFIDCMPVME, 0.39,False
99
99
  2,c2_seq1054,HHEDYSSHGDRHPVAE, 0.34,False
100
- 2,c2_seq1055,WFYIRFWNGCMWPVDH, 0.63,False
100
+ 2,c2_seq1055,WFYIRFWNGCMWPVDH, 0.63,False
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "Leonid Stolbov",
6
6
  "email": "lstolbov@datagrok.ai"
7
7
  },
8
- "version": "2.11.37",
8
+ "version": "2.11.39",
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",
@@ -34,7 +34,7 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "@biowasm/aioli": "^3.1.0",
37
- "@datagrok-libraries/bio": "5.39.26",
37
+ "@datagrok-libraries/bio": "5.39.29",
38
38
  "@datagrok-libraries/chem-meta": "^1.2.1",
39
39
  "@datagrok-libraries/ml": "^6.4.10",
40
40
  "@datagrok-libraries/tutorials": "^1.3.11",
@@ -8,7 +8,7 @@ import {handleError} from './utils';
8
8
  import {SequenceDiversityViewer} from '../analysis/sequence-diversity-viewer';
9
9
  import {SequenceSimilarityViewer} from '../analysis/sequence-similarity-viewer';
10
10
 
11
- const dataFn: string = 'data/sample_FASTA_PT_activity.csv';
11
+ const dataFn: string = 'samples/FASTA_PT_activity.csv';
12
12
 
13
13
  export async function demoBio01UI() {
14
14
  let view: DG.TableView;
@@ -9,7 +9,7 @@ import {demoSequenceSpace, handleError} from './utils';
9
9
  import {DemoScript} from '@datagrok-libraries/tutorials/src/demo-script';
10
10
  import {getClusterMatrixWorker} from '@datagrok-libraries/math';
11
11
 
12
- const dataFn = 'data/sample_FASTA_PT_activity.csv';
12
+ const dataFn = 'samples/FASTA_PT_activity.csv';
13
13
  const seqColName = 'sequence';
14
14
 
15
15
  export async function demoBio01aUI() {
@@ -14,7 +14,7 @@ import {MmDistanceFunctionsNames} from '@datagrok-libraries/ml/src/macromolecule
14
14
  import {getClusterMatrixWorker} from '@datagrok-libraries/math';
15
15
  import {DimReductionMethods} from '@datagrok-libraries/ml/src/multi-column-dimensionality-reduction/types';
16
16
 
17
- const dataFn: string = 'data/sample_FASTA_PT_activity.csv';
17
+ const dataFn: string = 'samples/FASTA_PT_activity.csv';
18
18
 
19
19
  export async function demoBio01bUI() {
20
20
  let treeHelper: ITreeHelper;
package/src/package.ts CHANGED
@@ -912,7 +912,7 @@ export async function webLogoAggApp(): Promise<void> {
912
912
  try {
913
913
  const urlParams = new URLSearchParams(window.location.search);
914
914
  const app = new WebLogoApp(urlParams, 'webLogoAggApp');
915
- const df: DG.DataFrame = await _package.files.readCsv('data/sample_FASTA_PT_activity.csv');
915
+ const df: DG.DataFrame = await _package.files.readCsv('samples/FASTA_PT_activity.csv');
916
916
  await grok.data.detectSemanticTypes(df);
917
917
  await app.init(df);
918
918
  } finally {
@@ -937,7 +937,7 @@ export async function getRegionHelmApp(): Promise<void> {
937
937
  const pi = DG.TaskBarProgressIndicator.create('getRegion ...');
938
938
  try {
939
939
  const urlParams = new URLSearchParams(window.location.search);
940
- const df = await _package.files.readCsv('data/sample_HELM_empty_vals.csv');
940
+ const df = await _package.files.readCsv('samples/HELM_empty_vals.csv');
941
941
  const app = new GetRegionApp(urlParams, 'getRegionHelmApp');
942
942
  await app.init({df: df, colName: 'HELM'});
943
943
  } finally {
@@ -72,7 +72,7 @@ category('activityCliffs', async () => {
72
72
  });
73
73
 
74
74
  test('Helm', async () => {
75
- const df = await _package.files.readCsv('data/sample_HELM_50.csv');
75
+ const df = await _package.files.readCsv('samples/HELM_50.csv');
76
76
  const _view = grok.shell.addTableView(df);
77
77
 
78
78
  await _testActivityCliffsOpen(df, DimReductionMethods.UMAP,
@@ -155,8 +155,8 @@ MWRSWY-CKHPMWRSWY-CKHP`;
155
155
  }
156
156
 
157
157
  const samples: { [key: string]: string } = {
158
- [Samples.fastaFasta]: 'System:AppData/Bio/data/sample_FASTA.fasta',
159
- [Samples.fastaPtCsv]: 'System:AppData/Bio/data/sample_FASTA_PT.csv',
158
+ [Samples.fastaFasta]: 'System:AppData/Bio/samples/FASTA.fasta',
159
+ [Samples.fastaPtCsv]: 'System:AppData/Bio/samples/FASTA_PT.csv',
160
160
  [Samples.msaComplex]: 'System:AppData/Bio/samples/MSA.csv',
161
161
  [Samples.fastaCsv]: 'System:AppData/Bio/samples/FASTA.csv',
162
162
  [Samples.helmCsv]: 'System:AppData/Bio/samples/HELM.csv',
@@ -6,7 +6,7 @@ import {category, expect, expectArray, test} from '@datagrok-libraries/utils/src
6
6
  import {ALIGNMENT, ALPHABET, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
7
7
  import {runKalign} from '../utils/multiple-sequence-alignment';
8
8
  import {multipleSequenceAlignmentUI} from '../utils/multiple-sequence-alignment-ui';
9
- import {awaitContainerStart} from './utils';
9
+ import {awaitContainerStart} from '../utils/docker';
10
10
 
11
11
  category('MSA', async () => {
12
12
  //table = await grok.data.files.openTable('Demo:Files/bio/peptides.csv');
@@ -1,27 +1,67 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
2
 
3
- import {before, category, expect, test} from '@datagrok-libraries/utils/src/test';
3
+ import {before, category, expect, expectArray, test} from '@datagrok-libraries/utils/src/test';
4
4
  import {runPepsea} from '../utils/pepsea';
5
- import {awaitContainerStart} from './utils';
5
+ import {TestLogger} from './utils/test-logger';
6
+ import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
7
+ import {awaitContainerStart} from '../utils/docker';
6
8
 
7
9
  category('PepSeA', () => {
8
10
  const testCsv = `HELM,MSA
9
- "PEPTIDE1{F.L.R.G.W.[MeF].Y.S.N.N.C}$$$$","F.L.R.G.W.MeF.Y..S.N.N.C"
10
- "PEPTIDE1{F.L.R.G.Y.[MeF].Y.W.N.C}$$$$","F.L.R.G.Y.MeF.Y.W...N.C"
11
- "PEPTIDE1{F.G.Y.[MeF].Y.W.S.D.N.C}$$$$","F...G.Y.MeF.Y.W.S.D.N.C"
12
- "PEPTIDE1{F.L.R.G.Y.[MeF].Y.W.S.N.D.C}$$$$","F.L.R.G.Y.MeF.Y.W.S.N.D.C"
13
- "PEPTIDE1{F.V.R.G.Y.[MeF].Y.W.S.N.C}$$$$","F.V.R.G.Y.MeF.Y.W.S..N.C"`;
11
+ "PEPTIDE1{F.L.R.G.W.[MeF].Y.S.N.N.C}$$$$","F.L.R.G.W.MeF.Y..S.N.N.C"
12
+ "PEPTIDE1{F.L.R.G.Y.[MeF].Y.W.N.C}$$$$","F.L.R.G.Y.MeF.Y.W...N.C"
13
+ "PEPTIDE1{F.G.Y.[MeF].Y.W.S.D.N.C}$$$$","F...G.Y.MeF.Y.W.S.D.N.C"
14
+ "PEPTIDE1{F.L.R.G.Y.[MeF].Y.W.S.N.D.C}$$$$","F.L.R.G.Y.MeF.Y.W.S.N.D.C"
15
+ "PEPTIDE1{F.V.R.G.Y.[MeF].Y.W.S.N.C}$$$$","F.V.R.G.Y.MeF.Y.W.S..N.C"
16
+ `;
17
+
18
+ const pepseaStderrCsv: string = `HELM,MSA
19
+ "PEPTIDE1{F.L.Mis.G.W.[MeF].Y.S.N.N.C}$$$$","F.L.Mis.G.W.MeF.Y..S.N.N.C"
20
+ "PEPTIDE1{F.L.Mis.G.Y.[MeF].Y.W.N.C}$$$$","F.L.Mis.G.Y.MeF.Y...W.N.C"
21
+ "PEPTIDE1{F.G.Y.[MeF].Y.W.S.D.N.C}$$$$","F...G.Y.MeF.Y.W.S.D.N.C"
22
+ `;
23
+ const pepseaStderrWarningList: string = 'Mis not found in Monomer Map\nMeF not found in Monomer Map\n';
24
+
25
+ const pepseaErrorCsv: string = `HELM
26
+ "PEPTIDE1{[NH2].*.A.Q.T.T.Y.K.N.Y.R.R.N.L.L.*.[COOH]}$$$$"
27
+ "PEPTIDE1{[NH2].M.A.N.T.T.Y.K.N.Y.R.N.N.L.L.*.[COOH]}$$$$"
28
+ "PEPTIDE1{[NH2].*.A.N.T.T.Y.K.C.Y.R.R.N.L.L.*.[COOH]}$$$$"
29
+ "PEPTIDE1{[NH2].*.A.N.T.T.Y.K.F.Y.R.R.N.L.L.*.[COOH]}$$$$"
30
+ `;
31
+ const pepseaErrorError: string = 'PepSeA error: The pair (*,M) couldn\'t be found in the substitution matrix';
14
32
 
15
33
  before(async () => {
16
34
  await awaitContainerStart();
17
35
  });
18
36
 
19
37
  test('Basic alignment', async () => {
20
- const table = DG.DataFrame.fromCsv(testCsv);
21
- const alignedCol = await runPepsea(table.getCol('HELM'), 'msa(HELM)');
22
- expect(alignedCol !== null, true, 'PepSeA container has not started');
23
- const alignedTestCol = table.getCol('MSA');
24
- for (let i = 0; i < alignedCol!.length; ++i)
25
- expect(alignedCol!.get(i) == alignedTestCol.get(i), true);
38
+ const df = DG.DataFrame.fromCsv(testCsv);
39
+ const resMsaCol = await runPepsea(df.getCol('HELM'), 'msa(HELM)');
40
+ const tgtMsaCol = df.getCol('MSA');
41
+ for (let i = 0; i < resMsaCol!.length; ++i)
42
+ expect(resMsaCol!.get(i) == tgtMsaCol.get(i), true);
43
+ }, {timeout: 60000 /* docker */});
44
+
45
+ test('stderr', async () => {
46
+ const logger = new TestLogger();
47
+ const df = DG.DataFrame.fromCsv(pepseaStderrCsv);
48
+ const resMsaCol = await runPepsea(df.getCol('HELM'), 'msa(HELM)',
49
+ undefined, undefined, undefined, undefined, logger);
50
+ const tgtMsaCol = df.getCol('MSA');
51
+ expectArray(resMsaCol!.toList(), tgtMsaCol.toList());
52
+ expect(logger.warningList[0].message, pepseaStderrWarningList);
26
53
  }, {timeout: 60000 /* docker */});
54
+
55
+ test('error', async () => {
56
+ const logger = new TestLogger();
57
+ try {
58
+ const df = DG.DataFrame.fromCsv(pepseaErrorCsv);
59
+ const _resMsaCol = await runPepsea(df.getCol('HELM'), 'msa(HELM)',
60
+ undefined, undefined, undefined, undefined, logger);
61
+ } catch (err: any) {
62
+ const [errMsg, errStack] = errInfo(err);
63
+ logger.error(errMsg, undefined, errStack);
64
+ }
65
+ expect(logger.errorList[0].message, pepseaErrorError);
66
+ });
27
67
  });
@@ -71,7 +71,7 @@ category('renderers', () => {
71
71
  }
72
72
 
73
73
  async function _rendererMacromoleculeSeparator() {
74
- const csv: string = await grok.dapi.files.readAsText('System:AppData/Bio/data/sample_SEPARATOR_PT.csv');
74
+ const csv: string = await grok.dapi.files.readAsText('System:AppData/Bio/samples/SEPARATOR_PT.csv');
75
75
  const df: DG.DataFrame = DG.DataFrame.fromCsv(csv);
76
76
 
77
77
  const seqCol = df.getCol('sequence');
@@ -113,7 +113,7 @@ category('renderers', () => {
113
113
  }
114
114
 
115
115
  async function _testAfterMsa() {
116
- const fastaTxt: string = await grok.dapi.files.readAsText('System:AppData/Bio/data/sample_FASTA.fasta');
116
+ const fastaTxt: string = await grok.dapi.files.readAsText('System:AppData/Bio/samples/FASTA.fasta');
117
117
  const df: DG.DataFrame = importFasta(fastaTxt)[0];
118
118
 
119
119
  const srcSeqCol: DG.Column = df.getCol('sequence');
@@ -153,7 +153,7 @@ category('renderers', () => {
153
153
  }
154
154
 
155
155
  async function _testAfterConvert() {
156
- const csv: string = await grok.dapi.files.readAsText('System:AppData/Bio/data/sample_FASTA_PT.csv');
156
+ const csv: string = await grok.dapi.files.readAsText('System:AppData/Bio/samples/FASTA_PT.csv');
157
157
  const df: DG.DataFrame = DG.DataFrame.fromCsv(csv);
158
158
 
159
159
  const srcCol: DG.Column = df.col('sequence')!;
@@ -0,0 +1,29 @@
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 {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
6
+
7
+
8
+ export class TestLogger implements ILogger {
9
+ errorList: { message: any, params?: object, stackTrace?: string }[] = [];
10
+ warningList: { message: string, params?: object }[] = [];
11
+ infoList: { message: string, params?: object }[] = [];
12
+ debugList: { message: string, params?: object }[] = [];
13
+
14
+ error(message: any, params?: object, stackTrace?: string): void {
15
+ this.errorList.push({message, params, stackTrace});
16
+ }
17
+
18
+ warning(message: string, params?: object): void {
19
+ this.warningList.push({message, params});
20
+ }
21
+
22
+ info(message: string, params?: object): void {
23
+ this.infoList.push({message, params});
24
+ }
25
+
26
+ debug(message: string, params?: object): void {
27
+ this.debugList.push({message, params});
28
+ }
29
+ }
@@ -34,14 +34,6 @@ export function _testTableIsNotEmpty(table: DG.DataFrame): void {
34
34
  expect(table.columns.length > 0 && table.rowCount > 0, true);
35
35
  }
36
36
 
37
- /** Waits if container is not started
38
- * @param {number} ms - time to wait in milliseconds */
39
- export async function awaitContainerStart(ms: number = 30000): Promise<void> {
40
- const dc = await grok.dapi.docker.dockerContainers.filter('bio').first();
41
- const dcId = dc.id;
42
- await startDockerContainer(dcId, ms);
43
- }
44
-
45
37
  export async function awaitGrid(grid: DG.Grid, timeout: number = 5000): Promise<void> {
46
38
  await delay(0);
47
39
  await testEvent(grid.onAfterDrawContent, () => {},
@@ -10,7 +10,7 @@ category('viewers', () => {
10
10
  const viewers = DG.Func.find({package: 'Bio', tags: ['viewer']}).map((f) => f.friendlyName);
11
11
  for (const v of viewers) {
12
12
  test(v, async () => {
13
- const df = await readDataframe('data/sample_FASTA_DNA.csv');
13
+ const df = await readDataframe('samples/FASTA_DNA.csv');
14
14
  await testViewer(v, df, {detectSemanticTypes: true});
15
15
  }, {
16
16
  skipReason: {
@@ -1,36 +1,52 @@
1
1
  import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
+
4
5
  import {delay} from '@datagrok-libraries/utils/src/test';
5
6
 
6
- export async function startDockerContainer(dcId: string, timeout: number = 30000): Promise<void> {
7
- // TODO: Use the new dockerContainers API
8
- const res = await grok.dapi.docker.dockerContainers.run(dcId /*, true */);
7
+ import {Pepsea} from './pepsea';
8
+
9
+ /** Waits if container is not started
10
+ * @param {number} ms - time to wait in milliseconds */
11
+ export async function awaitContainerStart(ms: number = 60000): Promise<void> {
12
+ const dc = await Pepsea.getDockerContainer();
13
+ await startDockerContainer(dc, ms);
14
+ }
15
+
16
+ export async function startDockerContainer(argDc: DG.DockerContainer, timeout: number = 60000): Promise<void> {
17
+ // argDc contains current container status
18
+ let dc: DG.DockerContainer | null = argDc;
9
19
  let end: boolean = false;
10
20
  for (let i = 0; i < timeout / 200; ++i) {
11
- const dc = await grok.dapi.docker.dockerContainers.find(dcId);
21
+ if (dc === null) dc = await grok.dapi.docker.dockerContainers.find(argDc.id);
22
+
23
+ if (isStarted(dc)) {
24
+ end = true;
25
+ break;
26
+ }
27
+
12
28
  switch (dc.status) {
13
- case 'stopped': {
14
- await grok.dapi.docker.dockerContainers.run(dcId);
15
- break;
16
- }
17
- case 'pending change':
18
- case 'changing': {
19
- // skip to wait
20
- break;
21
- }
22
- case 'checking':
23
- case 'started': {
24
- end = true;
25
- break;
26
- }
27
- case 'error': {
28
- throw new Error('Docker container error state.');
29
- }
29
+ case 'stopped': {
30
+ // TODO: Use the new dockerContainers API
31
+ await grok.dapi.docker.dockerContainers.run(dc.id);
32
+ break;
33
+ }
34
+ case 'pending change':
35
+ case 'changing': {
36
+ // skip to wait
37
+ break;
38
+ }
39
+ case 'error': {
40
+ throw new Error('Docker container error state.');
30
41
  }
31
- if (end) break;
42
+ }
43
+ dc = null;
32
44
  await delay(200);
33
45
  }
34
- if (!end) throw new Error('Docker container run timeout.');
46
+ if (!end) throw new Error('Docker container start timeout.');
35
47
  // this.dc = await grok.dapi.docker.dockerContainers.find(dcId);
36
48
  }
49
+
50
+ export function isStarted(dc: DG.DockerContainer): boolean {
51
+ return dc.status === 'checking' || dc.status === 'started';
52
+ }
@@ -11,6 +11,7 @@ import {pepseaMethods, runPepsea} from './pepsea';
11
11
  import {checkInputColumnUI} from './check-input-column';
12
12
  import {multipleSequenceAlginmentUIOptions} from './types';
13
13
  import {kalignVersion, msaDefaultOptions} from './constants';
14
+ import {awaitContainerStart} from './docker';
14
15
 
15
16
  import {_package} from '../package';
16
17
 
@@ -58,6 +59,10 @@ export async function multipleSequenceAlignmentUI(
58
59
  const msaParamsDiv = ui.inputs([gapOpenInput, gapExtendInput, terminalGapInput]);
59
60
  const msaParamsButton = ui.button('Alignment parameters', () => {
60
61
  msaParamsDiv.hidden = !msaParamsDiv.hidden;
62
+ [gapOpenInput, gapExtendInput, terminalGapInput].forEach((input) => {
63
+ input.root.style.removeProperty('max-width');
64
+ input.captionLabel.style.removeProperty('max-width');
65
+ });
61
66
  }, 'Adjust alignment parameters such as penalties for opening and extending gaps');
62
67
  msaParamsButton.classList.add('msa-params-button');
63
68
  msaParamsDiv.hidden = true;
@@ -68,7 +73,7 @@ export async function multipleSequenceAlignmentUI(
68
73
  let performAlignment: (() => Promise<DG.Column<string> | null>) | undefined;
69
74
 
70
75
  //TODO: remove when the new version of datagrok-api is available
71
- //TODO: allow only macromolecule colums to be chosen
76
+ //TODO: allow only macromolecule columns to be chosen
72
77
  const colInput = ui.columnInput('Sequence', table, seqCol, async () => {
73
78
  performAlignment = await onColInputChange(
74
79
  colInput.value, table, pepseaInputRootStyles, kalignInputRootStyles,
@@ -166,8 +171,11 @@ async function onColInputChange(
166
171
  gapOpenInput.value ??= msaDefaultOptions.pepsea.gapOpen;
167
172
  gapExtendInput.value ??= msaDefaultOptions.pepsea.gapExtend;
168
173
 
169
- return async () => await runPepsea(col, unusedName, methodInput.value!,
170
- gapOpenInput.value!, gapExtendInput.value!, clustersColInput.value);
174
+ return async () => {
175
+ await awaitContainerStart();
176
+ return runPepsea(col, unusedName, methodInput.value!,
177
+ gapOpenInput.value!, gapExtendInput.value!, clustersColInput.value);
178
+ };
171
179
  } else if (checkInputColumnUI(col, col.name, [NOTATION.SEPARATOR], [ALPHABET.UN], false)) {
172
180
  //if the column is separator with unknown alphabet, it might be helm. check if it can be converted to helm
173
181
  const potentialColUH = UnitsHandler.getOrCreate(col);
@@ -177,8 +185,11 @@ async function onColInputChange(
177
185
  gapExtendInput.value ??= msaDefaultOptions.pepsea.gapExtend;
178
186
  // convert to helm and assign alignment function to PepSea
179
187
 
180
- return async () => await runPepsea(helmCol, unusedName, methodInput.value!,
181
- gapOpenInput.value!, gapExtendInput.value!, clustersColInput.value);
188
+ return async () => {
189
+ await awaitContainerStart();
190
+ return runPepsea(helmCol, unusedName, methodInput.value!,
191
+ gapOpenInput.value!, gapExtendInput.value!, clustersColInput.value);
192
+ };
182
193
  } else {
183
194
  gapOpenInput.value = null;
184
195
  gapExtendInput.value = null;
@@ -6,6 +6,7 @@ import {Subject} from 'rxjs';
6
6
 
7
7
  import {testEvent} from '@datagrok-libraries/utils/src/test';
8
8
  import {NOTATION, TAGS as bioTAGS, ALIGNMENT, ALPHABET} from '@datagrok-libraries/bio/src/utils/macromolecule';
9
+ import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
9
10
 
10
11
  import * as C from './constants';
11
12
 
@@ -38,17 +39,15 @@ type PepseaBodyUnit = { ID: string, HELM: string };
38
39
  * @param {number} gapOpen - The gap open penalty.
39
40
  * @param {number} gapExtend - The gap extension penalty.
40
41
  * @param {DG.Column} clustersCol - The column containing the clusters of the sequences.
42
+ * @param logger {ILogger} Logger
41
43
  */
42
44
  export async function runPepsea(srcCol: DG.Column<string>, unUsedName: string,
43
45
  method: typeof pepseaMethods[number] = 'ginsi', gapOpen: number = 1.53, gapExtend: number = 0.0,
44
- clustersCol: DG.Column<string | number> | null = null,
45
- ): Promise<DG.Column<string> | null> {
46
+ clustersCol: DG.Column<string | number> | null = null, logger?: ILogger
47
+ ): Promise<DG.Column<string>> {
46
48
  const pepseaContainer = await Pepsea.getDockerContainer();
47
- if (pepseaContainer.status !== 'started' && pepseaContainer.status !== 'checking') {
48
- grok.log.warning('PepSeA container has not started yet');
49
- return null;
50
- }
51
-
49
+ if (pepseaContainer.status !== 'started' && pepseaContainer.status !== 'checking')
50
+ throw new Error('PepSeA container has not started yet');
52
51
 
53
52
  const peptideCount = srcCol.length;
54
53
  clustersCol ??= DG.Column.int('Clusters', peptideCount).init(0);
@@ -72,7 +71,7 @@ export async function runPepsea(srcCol: DG.Column<string>, unUsedName: string,
72
71
 
73
72
  const alignedSequences: string[] = new Array(peptideCount);
74
73
  for (const body of bodies) { // getting aligned sequences for each cluster
75
- const alignedObject = await requestAlignedObjects(pepseaContainer.id, body, method, gapOpen, gapExtend);
74
+ const alignedObject = await requestAlignedObjects(pepseaContainer.id, body, method, gapOpen, gapExtend, logger);
76
75
  const alignments = alignedObject.Alignment;
77
76
 
78
77
  for (const alignment of alignments) { // filling alignedSequencesCol
@@ -94,14 +93,62 @@ export async function runPepsea(srcCol: DG.Column<string>, unUsedName: string,
94
93
  return alignedSequencesCol;
95
94
  }
96
95
 
97
- async function requestAlignedObjects(dockerfileId: string, body: PepseaBodyUnit[], method: string, gapOpen: number,
98
- gapExtend: number): Promise<PepseaResponse> {
96
+ async function requestAlignedObjects(
97
+ dockerfileId: string, body: PepseaBodyUnit[], method: string, gapOpen: number, gapExtend: number, logger?: ILogger
98
+ ): Promise<PepseaResponse> {
99
99
  const params = {
100
100
  method: 'POST',
101
101
  headers: {'Accept': 'application/json', 'Content-Type': 'application/json'},
102
102
  body: JSON.stringify(body),
103
103
  };
104
104
  const path = `/align?method=${method}&gap_open=${gapOpen}&gap_extend=${gapExtend}`;
105
- const response = await grok.dapi.docker.dockerContainers.request(dockerfileId, path, params);
106
- return JSON.parse(response ?? '{}');
105
+ let responseObj: any;
106
+ if ('fetchProxy' in grok.dapi.docker.dockerContainers) {
107
+ // new dockerContainers API
108
+ const t1: number = window.performance.now();
109
+ // @ts-ignore
110
+ const response: Response = await grok.dapi.docker.dockerContainers.fetchProxy(dockerfileId, path, params);
111
+ const t2: number = window.performance.now();
112
+ _package.logger.debug(`Bio: requestAlignedObjects() dockerContainers.fetchProxy(), ET: ${(t2 - t1)} ms`);
113
+ const responseContentType = response.headers.get('content-type');
114
+ const isJson: boolean = responseContentType === 'application/json';
115
+ if (!response.ok && isJson) {
116
+ const responseJson = await response.json();
117
+ const pepseaErrorMsg = responseJson['pepsea-error'];
118
+ if (!!pepseaErrorMsg)
119
+ throw new Error(`PepSeA error: ${pepseaErrorMsg}`);
120
+
121
+ const datagrokErrorMsg = responseJson['datagrok-error'];
122
+ if (!!datagrokErrorMsg)
123
+ throw new Error(`Datagrok error: ${datagrokErrorMsg}`);
124
+
125
+ throw new Error(response.statusText);
126
+ } else if (!response.ok && !isJson) {
127
+ const responseStr = await response.text();
128
+ throw new Error(`Error: ${responseStr}`);
129
+ } else if (!isJson) {
130
+ const responseStr = await response.text();
131
+ throw new Error(`Error: PepSeA expected JSON response, got '${responseStr}'.`);
132
+ }
133
+ responseObj = await response.json();
134
+ } else {
135
+ const responseStr = await grok.dapi.docker.dockerContainers.request(dockerfileId, path, params)!;
136
+ if (!responseStr)
137
+ throw new Error('Empty response');
138
+ responseObj = JSON.parse(responseStr);
139
+
140
+ const pepseaErrorMsg = responseObj['pepsea-error'];
141
+ if (!!pepseaErrorMsg)
142
+ throw new Error(`PepSeA error: ${pepseaErrorMsg}`);
143
+
144
+ const datagrokErrorMsg = responseObj['datagrok-error'];
145
+ if (!!datagrokErrorMsg)
146
+ throw new Error(`Datagrok error: ${datagrokErrorMsg}`);
147
+ }
148
+ // Check for pepsea stderr output
149
+ if ('pepsea-stderr' in responseObj) {
150
+ const pepseaStdErr: string = responseObj['pepsea-stderr'] as string;
151
+ logger?.warning(pepseaStdErr);
152
+ }
153
+ return responseObj as PepseaResponse;
107
154
  }