From b5dc390b9253fc053581de50b3422d13a9e3a32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Fri, 1 Dec 2023 15:22:16 +0100 Subject: [PATCH] fix: remap existing assignments when molecule is updated Closes: https://github.com/cheminfo/nmrium/issues/1329 --- package-lock.json | 12 +- package.json | 2 +- .../modal/MoleculeStructureEditorModal.tsx | 42 +- .../reducer/actions/MoleculeActions.ts | 21 +- .../utilities/__tests__/data/test.json | 1319 +++++++++++++++++ .../__tests__/deepReplaceDiaIDs.test.ts | 55 + .../actions/utilities/deepReplaceDiaIDs.ts | 30 + 7 files changed, 1459 insertions(+), 22 deletions(-) create mode 100644 src/component/reducer/actions/utilities/__tests__/data/test.json create mode 100644 src/component/reducer/actions/utilities/__tests__/deepReplaceDiaIDs.test.ts create mode 100644 src/component/reducer/actions/utilities/deepReplaceDiaIDs.ts diff --git a/package-lock.json b/package-lock.json index d3db69ba5..c38de6a96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "nmredata": "^0.9.7", "numeral": "^2.0.6", "openchemlib": "^8.7.1", - "openchemlib-utils": "^5.4.0", + "openchemlib-utils": "^5.6.0", "papaparse": "^5.4.1", "re-resizable": "6.9.11", "react-d3-utils": "^1.0.0", @@ -10218,20 +10218,20 @@ "integrity": "sha512-skzgPw0F5ZxLe3y0Td4uyegNi8N2P3jj9EumIdki8eGzs4QhBtpOibkejcy9lLcItsnxwrO1se5cuaJWRB5MiQ==" }, "node_modules/openchemlib-utils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/openchemlib-utils/-/openchemlib-utils-5.4.0.tgz", - "integrity": "sha512-9DSgbhYWWBA+EgGQDXFJkuMoIF5oheQoU5teiF4E4FYKKsM42C7VCoMpYTZFQkIdgHYMlmEMOqofT8LHbHRVyg==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/openchemlib-utils/-/openchemlib-utils-5.6.0.tgz", + "integrity": "sha512-YQrVpeQeh0ad5wgNlh1mE/AV9Z3vR0PUmGy8YpE2idgl/ekibqFESwQzSHbvXKeAZPRV4xBHGUQAZxF//BmZqg==", "dependencies": { "atom-sorter": "^2.0.0", "ensure-string": "^1.2.0", "get-value": "^3.0.1", "ml-floyd-warshall": "^3.0.1", - "ml-matrix": "^6.10.5", + "ml-matrix": "^6.10.8", "papaparse": "^5.4.1", "sdf-parser": "^6.0.1" }, "peerDependencies": { - "openchemlib": ">=8.5.0" + "openchemlib": ">=8.6.2" } }, "node_modules/optionator": { diff --git a/package.json b/package.json index 9beaa1f1c..94ac5123f 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "nmredata": "^0.9.7", "numeral": "^2.0.6", "openchemlib": "^8.7.1", - "openchemlib-utils": "^5.4.0", + "openchemlib-utils": "^5.6.0", "papaparse": "^5.4.1", "re-resizable": "6.9.11", "react-d3-utils": "^1.0.0", diff --git a/src/component/modal/MoleculeStructureEditorModal.tsx b/src/component/modal/MoleculeStructureEditorModal.tsx index 7022772d7..54e325928 100644 --- a/src/component/modal/MoleculeStructureEditorModal.tsx +++ b/src/component/modal/MoleculeStructureEditorModal.tsx @@ -1,5 +1,7 @@ /** @jsxImportSource @emotion/react */ -import { useState, useEffect, useCallback } from 'react'; +import { Molecule } from 'openchemlib/full'; +import { TopicMolecule } from 'openchemlib-utils'; +import { useState, useEffect, useCallback, useMemo } from 'react'; import { StructureEditor, IStructureEditorProps } from 'react-ocl/full'; import { ConfirmModal, Modal, useOnOff } from 'react-science/ui'; @@ -23,15 +25,28 @@ function MoleculeStructureEditorModal( isOpen = false, } = props; + const initialMolfile = selectedMolecule?.molfile; + const initialEnhancedMolfile = useMemo(() => { + if (!initialMolfile) { + return null; + } + // Add mapNo to molfile, so we can remap diaIDs after edition. + const molecule = Molecule.fromMolfile(initialMolfile); + const topicMolecule = new TopicMolecule(molecule); + topicMolecule.ensureMapNo(); + const newMolfile = topicMolecule.toMolfile({ version: 3 }); + return { molfile: newMolfile, topicMolecule }; + }, [initialMolfile]); + const [molfile, setMolfile] = useState(null); const dispatch = useDispatch(); useEffect(() => { - if (selectedMolecule) { - setMolfile(selectedMolecule.molfile); + if (initialEnhancedMolfile) { + setMolfile(initialEnhancedMolfile.molfile); } else { setMolfile(null); } - }, [selectedMolecule]); + }, [initialEnhancedMolfile]); const cb = useCallback>( (newMolfile, molecule) => { @@ -50,14 +65,18 @@ function MoleculeStructureEditorModal( const handleSave = useCallback(() => { if (molfile) { - if (selectedMolecule) { + if (selectedMolecule && initialEnhancedMolfile) { const { id, label } = selectedMolecule; + const editedMolecule = Molecule.fromMolfile(molfile); + const mappings = + initialEnhancedMolfile.topicMolecule.getDiaIDsMapping(editedMolecule); dispatch({ type: 'SET_MOLECULE', payload: { molfile, id, label, + mappings, }, }); onClose('replace'); @@ -72,7 +91,14 @@ function MoleculeStructureEditorModal( onClose('new'); } } - }, [molfile, selectedMolecule, dispatch, floatMoleculeOnSave, onClose]); + }, [ + molfile, + selectedMolecule, + initialEnhancedMolfile, + dispatch, + floatMoleculeOnSave, + onClose, + ]); return ( ; -type SetMoleculeAction = ActionType<'SET_MOLECULE', Required>; +type SetMoleculeAction = ActionType< + 'SET_MOLECULE', + Required & { + mappings?: ReturnType; + } +>; type DeleteMoleculeAction = ActionType< 'DELETE_MOLECULE', { id: string; assignmentData: AssignmentContext } @@ -108,8 +114,8 @@ function handleAddMolecules(draft: Draft, action: AddMoleculesAction) { } } -function setMolecule(draft: Draft, props: Required) { - const { id, label, molfile } = props; +function setMolecule(draft: Draft, props: SetMoleculeAction['payload']) { + const { id, label, molfile, mappings } = props; MoleculeManager.setMolfile(draft.molecules, { id, label, @@ -121,6 +127,10 @@ function setMolecule(draft: Draft, props: Required) { */ const index = draft.molecules.findIndex((molecule) => molecule.id === id); + if (mappings) { + deepReplaceDiaIDs(draft, mappings); + } + changeSpectraRelativeSum( draft, id, @@ -328,8 +338,7 @@ function getFloatingMoleculeInitialPosition(id: string, draft: Draft) { const rows = Math.floor(height / baseHeight); const moleculeKeys = Object.keys(molecules); - let index = 0; - + let index: number; if (!molecules[id]) { index = moleculeKeys.length - 1; } else { diff --git a/src/component/reducer/actions/utilities/__tests__/data/test.json b/src/component/reducer/actions/utilities/__tests__/data/test.json new file mode 100644 index 000000000..d047b0e14 --- /dev/null +++ b/src/component/reducer/actions/utilities/__tests__/data/test.json @@ -0,0 +1,1319 @@ +{ + "version": 6, + "data": { + "source": { + "entries": [ + { + "baseURL": "", + "relativePath": "data/ethylbenzene/1h.jdx" + } + ] + }, + "spectra": [ + { + "id": "0abwvynpah97", + "filters": [], + "display": { + "isVisible": true, + "isRealSpectrumVisible": true, + "isPeaksMarkersVisible": true, + "isVisibleInDomain": true, + "color": "#C10020" + }, + "peaks": { + "values": [], + "options": {} + }, + "signals": [], + "sourceSelector": { + "files": ["data/ethylbenzene/1h.jdx"] + }, + "integrals": { + "values": [ + { + "id": "9f983b52-d907-469e-a28c-ec8444ae2200", + "originalFrom": 7.251626451964119, + "originalTo": 7.3922548623410425, + "from": 7.251626451964119, + "to": 7.3922548623410425, + "integral": 100, + "absolute": 1841923.3254847943, + "kind": "signal" + } + ], + "options": { + "sum": 100, + "isSumConstant": true, + "sumAuto": false + } + }, + "ranges": { + "values": [ + { + "id": "0f91b17d-c29c-406b-9e54-4332dc66489f", + "integration": 2.958756448385244, + "absolute": 2583924.0581951905, + "signals": [ + { + "kind": "signal", + "id": "91b17dc2-9c00-4bde-9443-32dc66489fe0", + "peaks": [ + { + "id": "dc66489f-e072-4ff8-b78d-a67ea8d4d7ae", + "y": 189295517.1090382, + "x": 1.1714287272036807, + "width": 0.6274173134261312, + "originalX": 1.1714287272036807 + }, + { + "id": "66489fe0-727f-48f7-8da6-7ea8d4d7aefd", + "y": 330961607.15955114, + "x": 1.1907489786238723, + "width": 0.6274173134261312, + "originalX": 1.1907489786238723 + }, + { + "id": "489fe072-7ff8-478d-a67e-a8d4d7aefddc", + "y": 216938813.73184657, + "x": 1.2095314331879106, + "width": 0.6274173134261312, + "originalX": 1.2095314331879106 + } + ], + "js": [ + { + "coupling": 7.622112374458709, + "multiplicity": "t" + } + ], + "multiplicity": "t", + "delta": 1.1906055043725645, + "originalDelta": 1.1906055043725645, + "diaIDs": ["did@`@fTf[Waj@@bJ@_iB@bUP"], + "nbAtoms": 2 + }, + { + "kind": "signal", + "id": "b17dc29c-006b-4e54-8332-dc66489fe072", + "peaks": [ + { + "id": "9fe0727f-f8f7-4da6-bea8-d4d7aefddc25", + "y": 30996972.142857146, + "x": 1.1960617189419018, + "width": 0.2509669253705946, + "originalX": 1.1960617189419018 + } + ], + "js": [], + "multiplicity": "s", + "delta": 1.1960617189419018, + "originalDelta": 1.1960617189419018, + "diaIDs": ["did@`@fTeYWaj@@@GzP`HeT"], + "nbAtoms": 3 + } + ], + "from": 1.1668494915207366, + "to": 1.2143615172243925, + "kind": "signal", + "originalFrom": 1.1668494915207366, + "originalTo": 1.2143615172243925 + }, + { + "id": "3cc2420f-91b1-4dc2-9c00-6bde544332dc", + "integration": 2.0579699854280213, + "absolute": 1797254.4375165442, + "signals": [ + { + "kind": "signal", + "id": "c29c006b-de54-4332-9c66-489fe0727ff8", + "peaks": [ + { + "id": "e0727ff8-f78d-467e-a8d4-d7aefddc2550", + "y": 39317604.0651426, + "x": 2.57508409763594, + "width": 1.3803180895375597, + "originalX": 2.57508409763594 + }, + { + "id": "727ff8f7-8da6-4ea8-94d7-aefddc2550d2", + "y": 107353718.50865774, + "x": 2.594357343824602, + "width": 1.6312850149077989, + "originalX": 2.594357343824602 + }, + { + "id": "7ff8f78d-a67e-48d4-97ae-fddc2550d2f7", + "y": 109721916.10758184, + "x": 2.6133998526831492, + "width": 1.6312850149077989, + "originalX": 2.6133998526831492 + }, + { + "id": "f8f78da6-7ea8-44d7-aefd-dc2550d2f7fb", + "y": 40588899.992739655, + "x": 2.6321459178529136, + "width": 1.5058015522225017, + "originalX": 2.6321459178529136 + } + ], + "js": [ + { + "coupling": 7.6054300104933645, + "multiplicity": "q" + } + ], + "multiplicity": "q", + "delta": 2.603816237485895, + "originalDelta": 2.603816237485895 + } + ], + "from": 2.564935075686039, + "to": 2.6426973992857508, + "kind": "signal", + "originalFrom": 2.564935075686039, + "originalTo": 2.6426973992857508, + "diaIDs": ["did@`@fTfYUn`HH@GzP`HeT"], + "nbAtoms": 2 + }, + { + "id": "143cc242-0f91-417d-829c-006bde544332", + "integration": 0.20222235816393483, + "absolute": 176603.65950361599, + "signals": [ + { + "kind": "signal", + "id": "006bde54-4332-4c66-889f-e0727ff8f78d", + "peaks": [ + { + "id": "f78da67e-a8d4-47ae-bddc-2550d2f7fbcc", + "y": 60266533.8333525, + "x": 3.4274543574896454, + "width": 0.6274173134261312, + "originalX": 3.4274543574896454 + } + ], + "js": [], + "multiplicity": "s", + "delta": 3.4274543574896454, + "originalDelta": 3.4274543574896454 + } + ], + "from": 3.4227496976299325, + "to": 3.4321590173493584, + "kind": "signal", + "originalFrom": 3.4227496976299325, + "originalTo": 3.4321590173493584, + "diaIDs": ["did@`@f\\bbRaih@J@A~dHBIU@"], + "nbAtoms": 2 + }, + { + "id": "b7143cc2-420f-41b1-bdc2-9c006bde5443", + "integration": 2.794425258208007, + "absolute": 2440411.2942289133, + "signals": [ + { + "kind": "signal", + "id": "de544332-dc66-489f-a072-7ff8f78da67e", + "peaks": [ + { + "id": "8da67ea8-d4d7-4efd-9c25-50d2f7fbccb8", + "y": 20933365.93247988, + "x": 7.15947925418579, + "width": 0.3764503880558919, + "originalX": 7.15947925418579 + }, + { + "id": "a67ea8d4-d7ae-4ddc-a550-d2f7fbccb872", + "y": 36103376.22857143, + "x": 7.175998044627571, + "width": 0.3764503880551812, + "originalX": 7.175998044627571 + }, + { + "id": "7ea8d4d7-aefd-4c25-90d2-f7fbccb8725f", + "y": 51615100.63924954, + "x": 7.1775459610952534, + "width": 0.8783842387963704, + "originalX": 7.1775459610952534 + }, + { + "id": "a8d4d7ae-fddc-4550-92f7-fbccb8725fa7", + "y": 25727327.914285712, + "x": 7.181016348477932, + "width": 0.2509669253705946, + "originalX": 7.181016348477932 + }, + { + "id": "d4d7aefd-dc25-40d2-b7fb-ccb8725fa750", + "y": 25228834.346577175, + "x": 7.182229814633405, + "width": 0.3764503880558919, + "originalX": 7.182229814633405 + }, + { + "id": "d7aefddc-2550-42f7-bbcc-b8725fa75053", + "y": 18021165.42857143, + "x": 7.183525500403112, + "width": 0.2509669253705946, + "originalX": 7.183525500403112 + }, + { + "id": "aefddc25-50d2-47fb-8cb8-725fa75053ec", + "y": 37081996.42857143, + "x": 7.190739312188006, + "width": 0.2509669253698839, + "originalX": 7.190739312188006 + }, + { + "id": "fddc2550-d2f7-4bcc-b872-5fa75053ecbe", + "y": 65696679.16890142, + "x": 7.192770447185832, + "width": 0.3764503880558919, + "originalX": 7.192770447185832 + }, + { + "id": "dc2550d2-f7fb-4cb8-b25f-a75053ecbe6d", + "y": 86313014.86098947, + "x": 7.194518809842537, + "width": 0.3764503880551812, + "originalX": 7.194518809842537 + }, + { + "id": "2550d2f7-fbcc-4872-9fa7-5053ecbe6df9", + "y": 97361177.62208347, + "x": 7.19596266007075, + "width": 0.3764503880558919, + "originalX": 7.19596266007075 + }, + { + "id": "50d2f7fb-ccb8-425f-a750-53ecbe6df9a7", + "y": 85741912.64842604, + "x": 7.197439551080657, + "width": 0.2509669253705946, + "originalX": 7.197439551080657 + }, + { + "id": "d2f7fbcc-b872-4fa7-9053-ecbe6df9a7bc", + "y": 76402511.37142858, + "x": 7.198894055944841, + "width": 0.2509669253705946, + "originalX": 7.198894055944841 + }, + { + "id": "f7fbccb8-725f-4750-93ec-be6df9a7bcf0", + "y": 55272619.28571428, + "x": 7.20014863190743, + "width": 0.3764503880551812, + "originalX": 7.20014863190743 + }, + { + "id": "fbccb872-5fa7-4053-acbe-6df9a7bcf020", + "y": 22740802.257142857, + "x": 7.2061078677297346, + "width": 0.3764503880551812, + "originalX": 7.2061078677297346 + }, + { + "id": "ccb8725f-a750-43ec-be6d-f9a7bcf020c3", + "y": 31009182.227102228, + "x": 7.20765320497578, + "width": 0.3764503880558919, + "originalX": 7.20765320497578 + }, + { + "id": "b8725fa7-5053-4cbe-adf9-a7bcf020c3ea", + "y": 96268741.4, + "x": 7.212067103552037, + "width": 0.5019338507404785, + "originalX": 7.212067103552037 + }, + { + "id": "725fa750-53ec-4e6d-b9a7-bcf020c3ea8e", + "y": 114401229.78791827, + "x": 7.213782920801378, + "width": 0.3764503880558919, + "originalX": 7.213782920801378 + }, + { + "id": "5fa75053-ecbe-4df9-a7bc-f020c3ea8e94", + "y": 94841167.05714285, + "x": 7.215517187449159, + "width": 0.2509669253698839, + "originalX": 7.215517187449159 + }, + { + "id": "a75053ec-be6d-49a7-bcf0-20c3ea8e9402", + "y": 92489310.50261469, + "x": 7.216937904570003, + "width": 0.5019338507411892, + "originalX": 7.216937904570003 + }, + { + "id": "5053ecbe-6df9-47bc-b020-c3ea8e94021a", + "y": 70386284.37142858, + "x": 7.218653627355634, + "width": 0.3764503880551812, + "originalX": 7.218653627355634 + } + ], + "js": [], + "multiplicity": "m", + "delta": 7.205232989115558, + "originalDelta": 7.205232989115558 + } + ], + "from": 7.1566564582699606, + "to": 7.221476423271458, + "kind": "signal", + "originalFrom": 7.1566564582699606, + "originalTo": 7.221476423271458, + "diaIDs": ["did@`@fTfUvf`@h@GzP`HeT"], + "nbAtoms": 1 + }, + { + "id": "45b7143c-c242-4f91-b17d-c29c006bde54", + "integration": 1.9866259498147933, + "absolute": 1734948.6772264864, + "signals": [ + { + "kind": "signal", + "id": "4332dc66-489f-4072-bff8-f78da67ea8d4", + "peaks": [ + { + "id": "53ecbe6d-f9a7-4cf0-a0c3-ea8e94021a9e", + "y": 20476150.2, + "x": 7.264132005999526, + "width": 0.5019338507404785, + "originalX": 7.264132005999526 + }, + { + "id": "ecbe6df9-a7bc-4020-83ea-8e94021a9e01", + "y": 58906038.400000006, + "x": 7.266954801915354, + "width": 0.3764503880551812, + "originalX": 7.266954801915354 + }, + { + "id": "be6df9a7-bcf0-40c3-aa8e-94021a9e0119", + "y": 95689257.28903544, + "x": 7.269016611425174, + "width": 0.7529007761117839, + "originalX": 7.269016611425174 + }, + { + "id": "6df9a7bc-f020-43ea-8e94-021a9e01194b", + "y": 40355631.85427714, + "x": 7.273425534769613, + "width": 0.7529007761110731, + "originalX": 7.273425534769613 + }, + { + "id": "f9a7bcf0-20c3-4a8e-9402-1a9e01194ba0", + "y": 60406456.571428575, + "x": 7.284518865391615, + "width": 0.5019338507411892, + "originalX": 7.284518865391615 + }, + { + "id": "a7bcf020-c3ea-4e94-821a-9e01194ba0f7", + "y": 99801699.18577753, + "x": 7.287332983608531, + "width": 0.6274173134264865, + "originalX": 7.287332983608531 + }, + { + "id": "bcf020c3-ea8e-4402-9a9e-01194ba0f7dc", + "y": 97294720.53420568, + "x": 7.288466450314026, + "width": 0.5019338507404785, + "originalX": 7.288466450314026 + }, + { + "id": "f020c3ea-8e94-421a-9e01-194ba0f7dcfe", + "y": 51128703.14285715, + "x": 7.291105389195213, + "width": 0.3764503880558919, + "originalX": 7.291105389195213 + }, + { + "id": "20c3ea8e-9402-4a9e-8119-4ba0f7dcfe29", + "y": 23917408.828571428, + "x": 7.293614541120393, + "width": 0.5019338507411892, + "originalX": 7.293614541120393 + }, + { + "id": "c3ea8e94-021a-4e01-994b-a0f7dcfe29dc", + "y": 22420284.51580987, + "x": 7.301667641302832, + "width": 0.6274173134257758, + "originalX": 7.301667641302832 + }, + { + "id": "ea8e9402-1a9e-4119-8ba0-f7dcfe29dc9c", + "y": 49008065.46629754, + "x": 7.305717110780697, + "width": 0.6274173134264865, + "originalX": 7.305717110780697 + }, + { + "id": "8e94021a-9e01-494b-a0f7-dcfe29dc9c77", + "y": 35392943, + "x": 7.308042164690178, + "width": 0.5019338507411892, + "originalX": 7.308042164690178 + }, + { + "id": "94021a9e-0119-4ba0-b7dc-fe29dc9c77c2", + "y": 18818872.971428573, + "x": 7.3099240286340645, + "width": 0.2509669253698839, + "originalX": 7.3099240286340645 + } + ], + "js": [], + "multiplicity": "m", + "delta": 7.28125898101929, + "originalDelta": 7.28125898101929 + } + ], + "from": 7.260368278111759, + "to": 7.311805892577951, + "kind": "signal", + "originalFrom": 7.260368278111759, + "originalTo": 7.311805892577951 + } + ], + "options": { + "sum": 10, + "isSumConstant": true, + "sumAuto": true, + "mf": "C8H10", + "moleculeId": "fcf8f1e2-a754-4731-8f04-5166300a82e2" + } + } + } + ], + "molecules": [ + { + "molfile": "\nActelion Java MolfileCreator 2.0\n\n 0 0 0 0 0 0 0 V3000\nM V30 BEGIN CTAB\nM V30 COUNTS 10 10 0 0 0\nM V30 BEGIN ATOM\nM V30 1 C 17.1875 -16.6875 0 0\nM V30 2 C 17.1875 -18.1875 0 0\nM V30 3 C 18.4865 -18.9375 0 0\nM V30 4 C 19.7855 -18.1875 0 0\nM V30 5 C 19.7855 -16.6875 0 0\nM V30 6 C 18.4865 -15.9375 0 0\nM V30 7 C 21.0844 -15.9375 0 0\nM V30 8 C 22.3836 -16.6875 0 0\nM V30 9 H 16.538 -16.3125 0 0\nM V30 10 H 16.538 -18.5625 0 0\nM V30 END ATOM\nM V30 BEGIN BOND\nM V30 1 2 1 2\nM V30 2 1 2 3\nM V30 3 2 3 4\nM V30 4 1 4 5\nM V30 5 2 5 6\nM V30 6 1 6 1\nM V30 7 1 5 7\nM V30 8 1 7 8\nM V30 9 1 1 9\nM V30 10 1 2 10\nM V30 END BOND\nM V30 END CTAB\nM END\n", + "label": "P1", + "id": "fcf8f1e2-a754-4731-8f04-5166300a82e2" + } + ], + "correlations": { + "values": [ + { + "id": "ocbuQAyR", + "atomType": "H", + "label": { + "origin": "H1" + }, + "link": [ + { + "id": "wFcDJ2hQ", + "experimentType": "1d", + "experimentID": "0abwvynpah97", + "atomType": ["H"], + "signal": { + "kind": "signal", + "id": "91b17dc2-9c00-4bde-9443-32dc66489fe0", + "peaks": [ + { + "id": "dc66489f-e072-4ff8-b78d-a67ea8d4d7ae", + "y": 189295517.1090382, + "x": 1.1714287272036807, + "width": 0.6274173134261312, + "originalX": 1.1714287272036807 + }, + { + "id": "66489fe0-727f-48f7-8da6-7ea8d4d7aefd", + "y": 330961607.15955114, + "x": 1.1907489786238723, + "width": 0.6274173134261312, + "originalX": 1.1907489786238723 + }, + { + "id": "489fe072-7ff8-478d-a67e-a8d4d7aefddc", + "y": 216938813.73184657, + "x": 1.2095314331879106, + "width": 0.6274173134261312, + "originalX": 1.2095314331879106 + } + ], + "js": [ + { + "coupling": 7.622112374458709, + "multiplicity": "t" + } + ], + "multiplicity": "t", + "delta": 1.1906055043725645, + "originalDelta": 1.1906055043725645 + }, + "match": [], + "experimentLabel": "", + "pseudo": false, + "edited": {} + }, + { + "id": "PTbCpu4h", + "experimentType": "1d", + "experimentID": "0abwvynpah97", + "atomType": ["H"], + "signal": { + "kind": "signal", + "id": "b17dc29c-006b-4e54-8332-dc66489fe072", + "peaks": [ + { + "id": "9fe0727f-f8f7-4da6-bea8-d4d7aefddc25", + "y": 30996972.142857146, + "x": 1.1960617189419018, + "width": 0.2509669253705946, + "originalX": 1.1960617189419018 + } + ], + "js": [], + "multiplicity": "s", + "delta": 1.1960617189419018, + "originalDelta": 1.1960617189419018 + }, + "match": [], + "experimentLabel": "", + "pseudo": false, + "edited": {} + } + ], + "equivalence": 1, + "attachment": {}, + "protonsCount": [], + "hybridization": [], + "pseudo": false, + "edited": {} + }, + { + "id": "Wwy6orvj", + "atomType": "H", + "label": { + "origin": "H2" + }, + "link": [ + { + "id": "0a83Mb2I", + "experimentType": "1d", + "experimentID": "0abwvynpah97", + "atomType": ["H"], + "signal": { + "kind": "signal", + "id": "c29c006b-de54-4332-9c66-489fe0727ff8", + "peaks": [ + { + "id": "e0727ff8-f78d-467e-a8d4-d7aefddc2550", + "y": 39317604.0651426, + "x": 2.57508409763594, + "width": 1.3803180895375597, + "originalX": 2.57508409763594 + }, + { + "id": "727ff8f7-8da6-4ea8-94d7-aefddc2550d2", + "y": 107353718.50865774, + "x": 2.594357343824602, + "width": 1.6312850149077989, + "originalX": 2.594357343824602 + }, + { + "id": "7ff8f78d-a67e-48d4-97ae-fddc2550d2f7", + "y": 109721916.10758184, + "x": 2.6133998526831492, + "width": 1.6312850149077989, + "originalX": 2.6133998526831492 + }, + { + "id": "f8f78da6-7ea8-44d7-aefd-dc2550d2f7fb", + "y": 40588899.992739655, + "x": 2.6321459178529136, + "width": 1.5058015522225017, + "originalX": 2.6321459178529136 + } + ], + "js": [ + { + "coupling": 7.6054300104933645, + "multiplicity": "q" + } + ], + "multiplicity": "q", + "delta": 2.603816237485895, + "originalDelta": 2.603816237485895 + }, + "match": [], + "experimentLabel": "", + "pseudo": false, + "edited": {} + } + ], + "equivalence": 1, + "attachment": {}, + "protonsCount": [], + "hybridization": [], + "pseudo": false, + "edited": {} + }, + { + "id": "UD1RWbBE", + "atomType": "H", + "label": { + "origin": "H3" + }, + "link": [ + { + "id": "tqKL2bib", + "experimentType": "1d", + "experimentID": "0abwvynpah97", + "atomType": ["H"], + "signal": { + "kind": "signal", + "id": "006bde54-4332-4c66-889f-e0727ff8f78d", + "peaks": [ + { + "id": "f78da67e-a8d4-47ae-bddc-2550d2f7fbcc", + "y": 60266533.8333525, + "x": 3.4274543574896454, + "width": 0.6274173134261312, + "originalX": 3.4274543574896454 + } + ], + "js": [], + "multiplicity": "s", + "delta": 3.4274543574896454, + "originalDelta": 3.4274543574896454 + }, + "match": [], + "experimentLabel": "", + "pseudo": false, + "edited": {} + } + ], + "equivalence": 1, + "attachment": {}, + "protonsCount": [], + "hybridization": [], + "pseudo": false, + "edited": {} + }, + { + "id": "rCTOSzT9", + "atomType": "H", + "label": { + "origin": "H4" + }, + "link": [ + { + "id": "se100KyI", + "experimentType": "1d", + "experimentID": "0abwvynpah97", + "atomType": ["H"], + "signal": { + "kind": "signal", + "id": "de544332-dc66-489f-a072-7ff8f78da67e", + "peaks": [ + { + "id": "8da67ea8-d4d7-4efd-9c25-50d2f7fbccb8", + "y": 20933365.93247988, + "x": 7.15947925418579, + "width": 0.3764503880558919, + "originalX": 7.15947925418579 + }, + { + "id": "a67ea8d4-d7ae-4ddc-a550-d2f7fbccb872", + "y": 36103376.22857143, + "x": 7.175998044627571, + "width": 0.3764503880551812, + "originalX": 7.175998044627571 + }, + { + "id": "7ea8d4d7-aefd-4c25-90d2-f7fbccb8725f", + "y": 51615100.63924954, + "x": 7.1775459610952534, + "width": 0.8783842387963704, + "originalX": 7.1775459610952534 + }, + { + "id": "a8d4d7ae-fddc-4550-92f7-fbccb8725fa7", + "y": 25727327.914285712, + "x": 7.181016348477932, + "width": 0.2509669253705946, + "originalX": 7.181016348477932 + }, + { + "id": "d4d7aefd-dc25-40d2-b7fb-ccb8725fa750", + "y": 25228834.346577175, + "x": 7.182229814633405, + "width": 0.3764503880558919, + "originalX": 7.182229814633405 + }, + { + "id": "d7aefddc-2550-42f7-bbcc-b8725fa75053", + "y": 18021165.42857143, + "x": 7.183525500403112, + "width": 0.2509669253705946, + "originalX": 7.183525500403112 + }, + { + "id": "aefddc25-50d2-47fb-8cb8-725fa75053ec", + "y": 37081996.42857143, + "x": 7.190739312188006, + "width": 0.2509669253698839, + "originalX": 7.190739312188006 + }, + { + "id": "fddc2550-d2f7-4bcc-b872-5fa75053ecbe", + "y": 65696679.16890142, + "x": 7.192770447185832, + "width": 0.3764503880558919, + "originalX": 7.192770447185832 + }, + { + "id": "dc2550d2-f7fb-4cb8-b25f-a75053ecbe6d", + "y": 86313014.86098947, + "x": 7.194518809842537, + "width": 0.3764503880551812, + "originalX": 7.194518809842537 + }, + { + "id": "2550d2f7-fbcc-4872-9fa7-5053ecbe6df9", + "y": 97361177.62208347, + "x": 7.19596266007075, + "width": 0.3764503880558919, + "originalX": 7.19596266007075 + }, + { + "id": "50d2f7fb-ccb8-425f-a750-53ecbe6df9a7", + "y": 85741912.64842604, + "x": 7.197439551080657, + "width": 0.2509669253705946, + "originalX": 7.197439551080657 + }, + { + "id": "d2f7fbcc-b872-4fa7-9053-ecbe6df9a7bc", + "y": 76402511.37142858, + "x": 7.198894055944841, + "width": 0.2509669253705946, + "originalX": 7.198894055944841 + }, + { + "id": "f7fbccb8-725f-4750-93ec-be6df9a7bcf0", + "y": 55272619.28571428, + "x": 7.20014863190743, + "width": 0.3764503880551812, + "originalX": 7.20014863190743 + }, + { + "id": "fbccb872-5fa7-4053-acbe-6df9a7bcf020", + "y": 22740802.257142857, + "x": 7.2061078677297346, + "width": 0.3764503880551812, + "originalX": 7.2061078677297346 + }, + { + "id": "ccb8725f-a750-43ec-be6d-f9a7bcf020c3", + "y": 31009182.227102228, + "x": 7.20765320497578, + "width": 0.3764503880558919, + "originalX": 7.20765320497578 + }, + { + "id": "b8725fa7-5053-4cbe-adf9-a7bcf020c3ea", + "y": 96268741.4, + "x": 7.212067103552037, + "width": 0.5019338507404785, + "originalX": 7.212067103552037 + }, + { + "id": "725fa750-53ec-4e6d-b9a7-bcf020c3ea8e", + "y": 114401229.78791827, + "x": 7.213782920801378, + "width": 0.3764503880558919, + "originalX": 7.213782920801378 + }, + { + "id": "5fa75053-ecbe-4df9-a7bc-f020c3ea8e94", + "y": 94841167.05714285, + "x": 7.215517187449159, + "width": 0.2509669253698839, + "originalX": 7.215517187449159 + }, + { + "id": "a75053ec-be6d-49a7-bcf0-20c3ea8e9402", + "y": 92489310.50261469, + "x": 7.216937904570003, + "width": 0.5019338507411892, + "originalX": 7.216937904570003 + }, + { + "id": "5053ecbe-6df9-47bc-b020-c3ea8e94021a", + "y": 70386284.37142858, + "x": 7.218653627355634, + "width": 0.3764503880551812, + "originalX": 7.218653627355634 + } + ], + "js": [], + "multiplicity": "m", + "delta": 7.205232989115558, + "originalDelta": 7.205232989115558 + }, + "match": [], + "experimentLabel": "", + "pseudo": false, + "edited": {} + } + ], + "equivalence": 1, + "attachment": {}, + "protonsCount": [], + "hybridization": [], + "pseudo": false, + "edited": {} + }, + { + "id": "6TZOBLAv", + "atomType": "H", + "label": { + "origin": "H5" + }, + "link": [ + { + "id": "DAUnfa2q", + "experimentType": "1d", + "experimentID": "0abwvynpah97", + "atomType": ["H"], + "signal": { + "kind": "signal", + "id": "4332dc66-489f-4072-bff8-f78da67ea8d4", + "peaks": [ + { + "id": "53ecbe6d-f9a7-4cf0-a0c3-ea8e94021a9e", + "y": 20476150.2, + "x": 7.264132005999526, + "width": 0.5019338507404785, + "originalX": 7.264132005999526 + }, + { + "id": "ecbe6df9-a7bc-4020-83ea-8e94021a9e01", + "y": 58906038.400000006, + "x": 7.266954801915354, + "width": 0.3764503880551812, + "originalX": 7.266954801915354 + }, + { + "id": "be6df9a7-bcf0-40c3-aa8e-94021a9e0119", + "y": 95689257.28903544, + "x": 7.269016611425174, + "width": 0.7529007761117839, + "originalX": 7.269016611425174 + }, + { + "id": "6df9a7bc-f020-43ea-8e94-021a9e01194b", + "y": 40355631.85427714, + "x": 7.273425534769613, + "width": 0.7529007761110731, + "originalX": 7.273425534769613 + }, + { + "id": "f9a7bcf0-20c3-4a8e-9402-1a9e01194ba0", + "y": 60406456.571428575, + "x": 7.284518865391615, + "width": 0.5019338507411892, + "originalX": 7.284518865391615 + }, + { + "id": "a7bcf020-c3ea-4e94-821a-9e01194ba0f7", + "y": 99801699.18577753, + "x": 7.287332983608531, + "width": 0.6274173134264865, + "originalX": 7.287332983608531 + }, + { + "id": "bcf020c3-ea8e-4402-9a9e-01194ba0f7dc", + "y": 97294720.53420568, + "x": 7.288466450314026, + "width": 0.5019338507404785, + "originalX": 7.288466450314026 + }, + { + "id": "f020c3ea-8e94-421a-9e01-194ba0f7dcfe", + "y": 51128703.14285715, + "x": 7.291105389195213, + "width": 0.3764503880558919, + "originalX": 7.291105389195213 + }, + { + "id": "20c3ea8e-9402-4a9e-8119-4ba0f7dcfe29", + "y": 23917408.828571428, + "x": 7.293614541120393, + "width": 0.5019338507411892, + "originalX": 7.293614541120393 + }, + { + "id": "c3ea8e94-021a-4e01-994b-a0f7dcfe29dc", + "y": 22420284.51580987, + "x": 7.301667641302832, + "width": 0.6274173134257758, + "originalX": 7.301667641302832 + }, + { + "id": "ea8e9402-1a9e-4119-8ba0-f7dcfe29dc9c", + "y": 49008065.46629754, + "x": 7.305717110780697, + "width": 0.6274173134264865, + "originalX": 7.305717110780697 + }, + { + "id": "8e94021a-9e01-494b-a0f7-dcfe29dc9c77", + "y": 35392943, + "x": 7.308042164690178, + "width": 0.5019338507411892, + "originalX": 7.308042164690178 + }, + { + "id": "94021a9e-0119-4ba0-b7dc-fe29dc9c77c2", + "y": 18818872.971428573, + "x": 7.3099240286340645, + "width": 0.2509669253698839, + "originalX": 7.3099240286340645 + } + ], + "js": [], + "multiplicity": "m", + "delta": 7.28125898101929, + "originalDelta": 7.28125898101929 + }, + "match": [], + "experimentLabel": "", + "pseudo": false, + "edited": {} + } + ], + "equivalence": 1, + "attachment": {}, + "protonsCount": [], + "hybridization": [], + "pseudo": false, + "edited": {} + } + ], + "options": { + "tolerance": { + "C": 0.25, + "H": 0.02, + "N": 0.25, + "F": 0.25, + "Si": 0.25, + "P": 0.25 + } + }, + "state": { + "H": { + "current": 0, + "error": { + "notAttached": [0, 1, 2, 3, 4] + } + } + } + } + }, + "settings": { + "display": { + "general": { + "hideGeneralSettings": false, + "experimentalFeatures": { + "display": false + }, + "hidePanelOnLoad": false, + "hideLogs": false, + "hideHelp": false, + "hideMaximize": false, + "hideWorkspaces": false + }, + "panels": { + "spectraPanel": { + "display": true, + "open": true + }, + "informationPanel": { + "display": true, + "open": false + }, + "integralsPanel": { + "display": true, + "open": false + }, + "rangesPanel": { + "display": true, + "open": false + }, + "structuresPanel": { + "display": true, + "open": false + }, + "processingsPanel": { + "display": true, + "open": false + }, + "zonesPanel": { + "display": true, + "open": false + }, + "automaticAssignmentPanel": { + "display": false, + "open": false + }, + "databasePanel": { + "display": false, + "open": false + }, + "multipleSpectraAnalysisPanel": { + "display": false, + "open": false + }, + "peaksPanel": { + "display": true, + "open": false + }, + "predictionPanel": { + "display": false, + "open": false + }, + "summaryPanel": { + "display": false, + "open": false + }, + "simulationPanel": { + "display": false, + "open": false + } + }, + "toolBarButtons": { + "baselineCorrection": true, + "exclusionZones": false, + "exportAs": true, + "fft": true, + "import": true, + "integral": true, + "multipleSpectraAnalysis": false, + "phaseCorrection": true, + "rangePicking": true, + "realImaginary": true, + "slicing": true, + "spectraCenterAlignments": true, + "spectraStackAlignments": true, + "apodization": true, + "zeroFilling": true, + "zonePicking": true, + "zoomOut": true, + "zoom": true, + "peakPicking": true, + "autoRangeAndZonePicking": true, + "fftDimension1": true, + "fftDimension2": true + } + }, + "general": { + "dimmedSpectraOpacity": 0.1, + "verticalSplitterPosition": "560px", + "verticalSplitterCloseThreshold": 600, + "spectraRendering": "auto", + "loggingLevel": "info", + "invert": false + }, + "formatting": { + "nuclei": { + "1h": { + "name": "1H", + "ppm": "0.00", + "hz": "0.00" + }, + "13c": { + "name": "13C", + "ppm": "0.00", + "hz": "0.00" + }, + "15n": { + "name": "15N", + "ppm": "0.00", + "hz": "0.00" + }, + "19f": { + "name": "19F", + "ppm": "0.00", + "hz": "0.00" + }, + "29si": { + "name": "29Si", + "ppm": "0.00", + "hz": "0.00" + }, + "31p": { + "name": "31P", + "ppm": "0.00", + "hz": "0.00" + } + }, + "panels": {} + }, + "databases": { + "defaultDatabase": "", + "data": [ + { + "key": "toc", + "label": "Toc", + "url": "https://data.cheminfo.org/nmr/database/toc.json", + "enabled": true + } + ] + }, + "nmrLoaders": { + "general": { + "keep1D": true, + "keep2D": true, + "onlyReal": false, + "dataSelection": "preferFT" + }, + "bruker": { + "onlyFirstProcessedData": true + } + }, + "infoBlock": { + "visible": false, + "fields": [ + { + "label": "name", + "jpath": ["info", "name"], + "visible": true, + "format": "" + }, + { + "label": "Number Of Scan", + "jpath": ["info", "numberOfScans"], + "visible": true, + "format": "0" + }, + { + "label": "Pulse Sequence", + "jpath": ["info", "pulseSequence"], + "visible": true, + "format": "" + }, + { + "label": "Frequency", + "jpath": ["info", "originFrequency"], + "visible": true, + "format": "0" + } + ] + }, + "onLoadProcessing": { + "autoProcessing": true, + "filters": { + "1H": [ + { + "name": "digitalFilter", + "label": "Digital Filter", + "value": {}, + "flag": true + }, + { + "name": "apodization", + "label": "Apodization", + "value": {}, + "flag": false + }, + { + "name": "zeroFilling", + "label": "Zero Filling", + "value": {}, + "flag": true + }, + { + "name": "fft", + "label": "FFT", + "value": {}, + "flag": true + }, + { + "name": "phaseCorrection", + "label": "Phase correction", + "value": {}, + "flag": true + } + ], + "13C": [ + { + "name": "digitalFilter", + "label": "Digital Filter", + "value": {}, + "flag": true + }, + { + "name": "apodization", + "label": "Apodization", + "value": {}, + "flag": true + }, + { + "name": "zeroFilling", + "label": "Zero Filling", + "value": {}, + "flag": true + }, + { + "name": "fft", + "label": "FFT", + "value": {}, + "flag": true + }, + { + "name": "phaseCorrection", + "label": "Phase correction", + "value": {}, + "flag": true + } + ] + } + }, + "version": 2, + "label": "Simple NMR analysis", + "visible": true, + "source": "predefined" + }, + "view": { + "molecules": {}, + "ranges": {}, + "zones": {}, + "peaks": {}, + "integrals": {}, + "spectra": { + "activeSpectra": { + "1H": [ + { + "id": "0abwvynpah97", + "index": 0, + "selected": true + } + ] + }, + "activeTab": "1H", + "showLegend": false, + "selectReferences": {} + }, + "zoom": { + "levels": {} + }, + "verticalAlign": { + "1H": "bottom" + }, + "predictions": {}, + "currentSimulatedSpectrumKey": null + } +} diff --git a/src/component/reducer/actions/utilities/__tests__/deepReplaceDiaIDs.test.ts b/src/component/reducer/actions/utilities/__tests__/deepReplaceDiaIDs.test.ts new file mode 100644 index 000000000..ba6fb3500 --- /dev/null +++ b/src/component/reducer/actions/utilities/__tests__/deepReplaceDiaIDs.test.ts @@ -0,0 +1,55 @@ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +import { produce } from 'immer'; +import { describe, it, expect } from 'vitest'; + +import { deepReplaceDiaIDs } from '../deepReplaceDiaIDs'; + +describe('deepReplaceDiaIDs', () => { + it('basic case', () => { + const data = { + name: 'test', + diaIDs: ['1', '2'], + }; + deepReplaceDiaIDs(data, { '1': '3' }); + expect(data).toEqual({ + name: 'test', + diaIDs: ['3', '2'], + }); + }); + + it('basic case with immer', () => { + const data = { + name: 'test', + diaIDs: ['1', '2'], + }; + const newData = produce(data, (draft) => { + deepReplaceDiaIDs(draft, { '1': '3' }); + }); + expect(data).not.toEqual(newData); + expect(newData).toEqual({ + name: 'test', + diaIDs: ['3', '2'], + }); + }); + + it('deepReplaceDiaIDs', () => { + const data = JSON.parse( + readFileSync(join(__dirname, 'data/test.json'), 'utf8'), + ); + const mappings = { + 'did@`@fTeYWaj@@@GzP`HeT': 'Hello World', + }; + const newData = produce(data, (draft) => { + deepReplaceDiaIDs(draft, mappings); + }); + const lines = JSON.stringify(newData, undefined, 2).split('\n'); + const oldID = lines.filter((line) => + line.includes('did@`@fTeYWaj@@@GzP`HeT'), + ); + expect(oldID.length).toBe(0); + const newID = lines.filter((line) => line.includes('Hello World')); + expect(newID.length).toBe(1); + }); +}); diff --git a/src/component/reducer/actions/utilities/deepReplaceDiaIDs.ts b/src/component/reducer/actions/utilities/deepReplaceDiaIDs.ts new file mode 100644 index 000000000..fb736a2c7 --- /dev/null +++ b/src/component/reducer/actions/utilities/deepReplaceDiaIDs.ts @@ -0,0 +1,30 @@ +import { Draft } from 'immer'; +import { TopicMolecule } from 'openchemlib-utils'; + +export function deepReplaceDiaIDs( + data: Draft, + mappings: ReturnType, +) { + for (const key in data) { + if (key !== 'diaIDs') { + if (typeof data[key] === 'object' && !ArrayBuffer.isView(data[key])) { + deepReplaceDiaIDs(data[key], mappings); + } + continue; + } + if (!Array.isArray(data[key])) { + throw new Error('diaIDs must be an array'); + } + const diaIDs = data[key]; + for (let i = 0; i < diaIDs.length; i++) { + const diaID = diaIDs[i]; + if (typeof diaID !== 'string') { + throw new Error('diaID must be a string'); + } + const newDiaID = mappings[diaID]; + if (newDiaID) { + diaIDs[i] = newDiaID; + } + } + } +}