@datagrok/bio 2.21.1 → 2.21.2

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.21.1",
8
+ "version": "2.21.2",
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",
@@ -44,12 +44,12 @@
44
44
  ],
45
45
  "dependencies": {
46
46
  "@biowasm/aioli": "^3.1.0",
47
- "@datagrok-libraries/bio": "^5.51.1",
47
+ "@datagrok-libraries/bio": "^5.51.2",
48
48
  "@datagrok-libraries/chem-meta": "^1.2.7",
49
49
  "@datagrok-libraries/math": "^1.2.4",
50
50
  "@datagrok-libraries/ml": "^6.10.0",
51
51
  "@datagrok-libraries/tutorials": "^1.6.1",
52
- "@datagrok-libraries/utils": "^4.5.0",
52
+ "@datagrok-libraries/utils": "^4.5.7",
53
53
  "datagrok-api": "^1.25.0",
54
54
  "@webgpu/types": "^0.1.40",
55
55
  "ajv": "^8.12.0",
@@ -191,23 +191,23 @@ export class MonomerManager implements IMonomerManager {
191
191
  args.context.tableView.id !== (this.tv!.id ?? '') || !args.item || !args.item.isTableCell || (args.item.tableRowIndex ?? -1) < 0)
192
192
  return;
193
193
  const rowIdx = args.item.tableRowIndex;
194
- args.menu.item('Edit Monomer', () => {
195
- this.cloneMonomer(this.tv!.dataFrame.rows.get(rowIdx));
194
+ args.menu.item('Edit Monomer', async () => {
195
+ await this.editMonomer(this.tv!.dataFrame.rows.get(rowIdx));
196
196
  });
197
197
  if (this.tv!.dataFrame.selection.trueCount > 0) {
198
- args.menu.item('Remove Selected Monomers', () => {
199
- const monomers = Array.from(this.tv!.dataFrame.selection.getSelectedIndexes())
200
- .map((r) => monomerFromDfRow(this.tv!.dataFrame.rows.get(r)));
198
+ args.menu.item('Remove Selected Monomers', async () => {
199
+ const monomers = await Promise.all(Array.from(this.tv!.dataFrame.selection.getSelectedIndexes())
200
+ .map((r) => monomerFromDfRow(this.tv!.dataFrame.rows.get(r))));
201
201
  this._newMonomerForm.removeMonomers(monomers, this.libInput.value!);
202
202
  });
203
- args.menu.item('Selection To New Library', () => {
204
- const monomers = Array.from(this.tv!.dataFrame.selection.getSelectedIndexes())
205
- .map((r) => monomerFromDfRow(this.tv!.dataFrame.rows.get(r)));
203
+ args.menu.item('Selection To New Library', async () => {
204
+ const monomers = await Promise.all(Array.from(this.tv!.dataFrame.selection.getSelectedIndexes())
205
+ .map((r) => monomerFromDfRow(this.tv!.dataFrame.rows.get(r))));
206
206
  this.createNewLibDialog(monomers);
207
207
  });
208
208
  } else {
209
- args.menu.item('Remove Monomer', () => {
210
- const monomer = monomerFromDfRow(this.tv!.dataFrame.rows.get(rowIdx));
209
+ args.menu.item('Remove Monomer', async () => {
210
+ const monomer = await monomerFromDfRow(this.tv!.dataFrame.rows.get(rowIdx));
211
211
  this._newMonomerForm.removeMonomers([monomer], this.libInput.value!);
212
212
  });
213
213
  }
@@ -271,9 +271,9 @@ export class MonomerManager implements IMonomerManager {
271
271
  this._newMonomerForm.setEmptyMonomer();
272
272
  }, 'Add New Monomer');
273
273
 
274
- const editButton = ui.icons.edit(() => {
274
+ const editButton = ui.icons.edit(async () => {
275
275
  if ((this.tv?.dataFrame?.currentRowIdx ?? -1) < 0) return;
276
- this.cloneMonomer(this.tv!.dataFrame.rows.get(this.tv!.dataFrame.currentRowIdx));
276
+ await this.editMonomer(this.tv!.dataFrame.rows.get(this.tv!.dataFrame.currentRowIdx));
277
277
  }, 'Edit Monomer');
278
278
 
279
279
  const deleteButton = ui.icons.delete(async () => {
@@ -282,16 +282,16 @@ export class MonomerManager implements IMonomerManager {
282
282
  if (currentRowIdx < 0 && selectedRows.length === 0) return;
283
283
 
284
284
  if (selectedRows.length > 0) {
285
- const monomers = selectedRows.map((r) => monomerFromDfRow(this.tv!.dataFrame.rows.get(r)));
285
+ const monomers = await Promise.all(selectedRows.map((r) => monomerFromDfRow(this.tv!.dataFrame.rows.get(r))));
286
286
  await this._newMonomerForm.removeMonomers(monomers, this.libInput.value!);
287
287
  return;
288
288
  }
289
- const monomer = monomerFromDfRow(this.tv!.dataFrame.rows.get(currentRowIdx));
289
+ const monomer = await monomerFromDfRow(this.tv!.dataFrame.rows.get(currentRowIdx));
290
290
  await this._newMonomerForm.removeMonomers([monomer], this.libInput.value!);
291
291
  });
292
292
 
293
293
  ui.tooltip.bind(deleteButton, () =>
294
- `${(this.tv?.dataFrame?.selection?.getSelectedIndexes() ?? []).length > 0 ? 'Delete selected monomers' : 'Delete monomer'}`);
294
+ `${(this.tv?.dataFrame?.selection?.trueCount ?? 0) > 0 ? 'Delete selected monomers' : 'Delete monomer'}`);
295
295
 
296
296
  const downloadButton = ui.iconFA('arrow-to-bottom', async () => {
297
297
  const libName = this.libInput.value;
@@ -333,8 +333,8 @@ export class MonomerManager implements IMonomerManager {
333
333
  return this.tv;
334
334
  }
335
335
 
336
- cloneMonomer(dfRow: DG.Row): Monomer {
337
- this._newMonomer = monomerFromDfRow(dfRow);
336
+ async editMonomer(dfRow: DG.Row): Promise<Monomer> {
337
+ this._newMonomer = await monomerFromDfRow(dfRow);
338
338
  this._newMonomerForm.setMonomer(this._newMonomer);
339
339
  return this._newMonomer;
340
340
  }
@@ -384,6 +384,17 @@ export class MonomerManager implements IMonomerManager {
384
384
  const rgroup = monomers[i].rgroups.find((rg) => rg.label === rgName);
385
385
  return rgroup ? getCaseInvariantValue(rgroup, HELM_RGROUP_FIELDS.CAP_GROUP_SMILES) : '';
386
386
  });
387
+ let date: number | null = null;
388
+
389
+ if (monomers[i].createDate) {
390
+ try {
391
+ date = Date.parse(monomers[i].createDate!);
392
+ } catch (e) {
393
+ console.error(`Error parsing date ${monomers[i].createDate}`);
394
+ }
395
+ }
396
+
397
+
387
398
  df.rows.setValues(i, [
388
399
  molSmiles,
389
400
  monomers[i].symbol,
@@ -394,23 +405,30 @@ export class MonomerManager implements IMonomerManager {
394
405
  monomers[i].polymerType,
395
406
  monomers[i].naturalAnalog,
396
407
  monomers[i].author,
397
- monomers[i].createDate,
408
+ date,
398
409
  monomers[i].id,
399
410
  JSON.stringify(monomers[i].meta ?? {}),
400
411
  monomers[i].lib?.source ?? '',
401
412
  ]);
413
+ // something is wrong with setting dates, so setting it manually for now
414
+ try {
415
+ if (date)
416
+ df.col(MONOMER_DF_COLUMN_NAMES.CREATE_DATE)?.set(i, date, false);
417
+ } catch (e) {
418
+ console.error(`Error setting date ${monomers[i].createDate}`);
419
+ }
402
420
  }
403
421
  df.col(MONOMER_DF_COLUMN_NAMES.MONOMER)!.semType = DG.SEMTYPE.MOLECULE;
404
422
  uniqueRgroupNames.forEach((rgName) => {
405
423
  df.col(rgName)!.semType = DG.SEMTYPE.MOLECULE;
406
424
  });
407
425
  df.currentRowIdx = -1;
408
- // eslint-disable-next-line rxjs/no-ignored-subscription
409
- df.onCurrentRowChanged.subscribe((_) => {
426
+ // eslint-disable-next-line rxjs/no-ignored-subscription, rxjs/no-async-subscribe
427
+ df.onCurrentRowChanged.subscribe(async (_) => {
410
428
  try {
411
429
  if (df.currentRowIdx === -1 || this._newMonomerForm.molChanged)
412
430
  return;
413
- this.cloneMonomer(df.rows.get(df.currentRowIdx));
431
+ await this.editMonomer(df.rows.get(df.currentRowIdx));
414
432
  } catch (e) {
415
433
  console.error(e);
416
434
  }
@@ -753,7 +771,7 @@ class MonomerForm implements INewMonomerForm {
753
771
  this.metaGrid.render();
754
772
  this.rgroupsGridRoot.style.display = 'flex';
755
773
  this.onMonomerInputChanged();
756
- if (!monomer.naturalAnalog) {
774
+ if (!monomer.naturalAnalog && monomer.polymerType) {
757
775
  mostSimilarNaturalAnalog(capSmiles(monomer.smiles, this.rgroupsGrid.items as RGroup[]), monomer.polymerType).then((mostSimilar) => {
758
776
  if (mostSimilar)
759
777
  this.monomerNaturalAnalogInput.value = mostSimilar;
@@ -824,7 +842,7 @@ class MonomerForm implements INewMonomerForm {
824
842
  this.molSketcher.root,
825
843
  this.inputsTabControl.root,
826
844
  saveB,
827
- ], {classes: 'ui-form', style: {paddingLeft: '10px', overflow: 'scroll'}});
845
+ ], {classes: 'ui-form', style: {paddingLeft: '10px', overflow: 'scroll', maxWidth: 'unset'}});
828
846
  }
829
847
 
830
848
  get fieldInputs() {
@@ -933,7 +951,7 @@ class MonomerForm implements INewMonomerForm {
933
951
  infoTable = this.getMonomerInfoTable(libJSON[existingMonomerIdx]);
934
952
  promptMessage = `Monomer with symbol '${monomer.symbol}' already exists in library ${libName}.\nAre you sure you want to overwrite it?`;
935
953
  } else if ((existingStructureIdx ?? -1) >= 0) {
936
- const m = monomerFromDfRow(this.getMonomersDataFrame()!.rows.get(existingStructureIdx!));
954
+ const m = await monomerFromDfRow(this.getMonomersDataFrame()!.rows.get(existingStructureIdx!));
937
955
  infoTable = this.getMonomerInfoTable(m);
938
956
  promptMessage = `Monomer with the same structure already exists in library ${libName} with different symbol (${m.symbol}).\nAre you sure you want to duplicate it?`;
939
957
  }
@@ -1098,12 +1116,22 @@ function capSmiles(smiles: string, rgroups: RGroup[]) {
1098
1116
  return newSmiles;
1099
1117
  }
1100
1118
 
1101
- function monomerFromDfRow(dfRow: DG.Row): Monomer {
1119
+ async function monomerFromDfRow(dfRow: DG.Row): Promise<Monomer> {
1102
1120
  // hacky way for now, but meta object for now only supports key value pairs and not nested objects
1103
- const metaJSON = JSON.parse(dfRow.get(MONOMER_DF_COLUMN_NAMES.META) ?? '{}');
1104
- for (const key in metaJSON) {
1105
- if (typeof metaJSON[key] === 'object')
1106
- metaJSON[key] = JSON.stringify(metaJSON[key]);
1121
+ let metaJSON: any;
1122
+ try {
1123
+ metaJSON = JSON.parse(dfRow.get(MONOMER_DF_COLUMN_NAMES.META) ?? '{}');
1124
+ for (const key in metaJSON) {
1125
+ if (typeof metaJSON[key] === 'object')
1126
+ metaJSON[key] = JSON.stringify(metaJSON[key]);
1127
+ // post-fix for detecting null values in meta object
1128
+ if ((typeof metaJSON[key] === 'string' && (metaJSON[key] === DG.INT_NULL.toString() || metaJSON[key] === DG.FLOAT_NULL.toString())) ||
1129
+ (typeof metaJSON[key] === 'number' && (metaJSON[key] === DG.INT_NULL || metaJSON[key] === DG.FLOAT_NULL))
1130
+ )
1131
+ metaJSON[key] = null;
1132
+ }
1133
+ } catch (e) {
1134
+ console.error(e);
1107
1135
  }
1108
1136
  const smiles = dfRow.get(MONOMER_DF_COLUMN_NAMES.MONOMER);
1109
1137
  if (!smiles)
@@ -1118,16 +1146,30 @@ function monomerFromDfRow(dfRow: DG.Row): Monomer {
1118
1146
  console.error(e);
1119
1147
  }
1120
1148
 
1149
+ let naturalAnalog = dfRow.get(MONOMER_DF_COLUMN_NAMES.NATURAL_ANALOG);
1150
+ const polymerType = dfRow.get(MONOMER_DF_COLUMN_NAMES.POLYMER_TYPE);
1151
+ let rGroups: RGroup[] = [];
1152
+ try {
1153
+ rGroups = JSON.parse(dfRow.get(MONOMER_DF_COLUMN_NAMES.R_GROUPS) ?? '[]');
1154
+ if (!naturalAnalog && polymerType) {
1155
+ const mostSimilar = await mostSimilarNaturalAnalog(capSmiles(smiles, rGroups), polymerType);
1156
+ if (mostSimilar)
1157
+ naturalAnalog = mostSimilar;
1158
+ }
1159
+ } catch (_) {
1160
+ rGroups ??= [];
1161
+ }
1162
+
1121
1163
  return {
1122
1164
  symbol: dfRow.get(MONOMER_DF_COLUMN_NAMES.SYMBOL),
1123
1165
  name: dfRow.get(MONOMER_DF_COLUMN_NAMES.NAME),
1124
1166
  molfile: molfile,
1125
1167
  smiles: smiles,
1126
- polymerType: dfRow.get(MONOMER_DF_COLUMN_NAMES.POLYMER_TYPE),
1168
+ polymerType: polymerType,
1127
1169
  monomerType: dfRow.get(MONOMER_DF_COLUMN_NAMES.MONOMER_TYPE),
1128
- naturalAnalog: dfRow.get(MONOMER_DF_COLUMN_NAMES.NATURAL_ANALOG),
1170
+ naturalAnalog: naturalAnalog,
1129
1171
  id: dfRow.get(MONOMER_DF_COLUMN_NAMES.ID),
1130
- rgroups: JSON.parse(dfRow.get(MONOMER_DF_COLUMN_NAMES.R_GROUPS) ?? '[]'),
1172
+ rgroups: rGroups,
1131
1173
  meta: metaJSON,
1132
1174
  author: dfRow.get(MONOMER_DF_COLUMN_NAMES.AUTHOR),
1133
1175
  createDate: dfRow.get(MONOMER_DF_COLUMN_NAMES.CREATE_DATE),