@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/CHANGELOG.md +9 -0
- package/css/monomer-manager.css +1 -0
- package/dist/package-test.js +2 -2
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/package.json +3 -3
- package/src/utils/monomer-lib/monomer-manager/monomer-manager.ts +74 -32
- package/test-console-output-1.log +1174 -351
- package/test-record-1.mp4 +0 -0
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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?.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
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:
|
|
1168
|
+
polymerType: polymerType,
|
|
1127
1169
|
monomerType: dfRow.get(MONOMER_DF_COLUMN_NAMES.MONOMER_TYPE),
|
|
1128
|
-
naturalAnalog:
|
|
1170
|
+
naturalAnalog: naturalAnalog,
|
|
1129
1171
|
id: dfRow.get(MONOMER_DF_COLUMN_NAMES.ID),
|
|
1130
|
-
rgroups:
|
|
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),
|