diff --git a/js/components/datasets/datasetMoleculeView.js b/js/components/datasets/datasetMoleculeView.js index 13a7a4e4c..6292cb222 100644 --- a/js/components/datasets/datasetMoleculeView.js +++ b/js/components/datasets/datasetMoleculeView.js @@ -32,11 +32,12 @@ import { removeMoleculeFromCompoundsOfDatasetToBuy, setCrossReferenceCompoundName, setIsOpenCrossReferenceDialog, - setInspirationFragmentList + setInspirationFragmentList, + setInspirationMoleculeDataList } from './redux/actions'; import { centerOnLigandByMoleculeID } from '../../reducers/ngl/dispatchActions'; import { ArrowDownward, ArrowUpward, MyLocation } from '@material-ui/icons'; -import { isNumber, isString } from 'lodash'; +import { isString } from 'lodash'; import { SvgTooltip } from '../common'; import { OBJECT_TYPE } from '../nglView/constants'; import { getRepresentationsByType } from '../nglView/generatingObjects'; @@ -265,6 +266,7 @@ export const DatasetMoleculeView = memo( const ref = useRef(null); const dispatch = useDispatch(); const compoundsToBuyList = useSelector(state => state.datasetsReducers.compoundsToBuyDatasetMap[datasetID]); + const allInspirations = useSelector(state => state.datasetsReducers.allInspirations); const datasets = useSelector(state => state.datasetsReducers.datasets); const filteredScoreProperties = useSelector(state => state.datasetsReducers.filteredScoreProperties); @@ -525,6 +527,16 @@ export const DatasetMoleculeView = memo( }); }; + const getInspirationsForMol = (datasetId, molId) => { + let inspirations = []; + + if (allInspirations && allInspirations.hasOwnProperty(datasetId) && allInspirations[datasetId].hasOwnProperty(molId)) { + inspirations = allInspirations[datasetId][molId]; + } + + return inspirations; + }; + const handleClickOnDownArrow = () => { const refNext = ref.current.nextSibling; scrollToElement(refNext); @@ -538,7 +550,9 @@ export const DatasetMoleculeView = memo( moveSelectedMoleculeSettings(nextItem, nextDatasetID); dispatch(moveSelectedMoleculeInspirationsSettings(data, nextItem)); - dispatch(setInspirationFragmentList(nextItem.computed_inspirations)); + const inspirations = getInspirationsForMol(datasetID, nextItem.id); + dispatch(setInspirationMoleculeDataList(inspirations)); + // dispatch(setInspirationFragmentList(nextItem.computed_inspirations)); dispatch(setCrossReferenceCompoundName(moleculeTitleNext)); if (setRef && ref.current) { setRef(refNext); @@ -560,7 +574,9 @@ export const DatasetMoleculeView = memo( moveSelectedMoleculeSettings(previousItem, previousDatasetID); dispatch(moveSelectedMoleculeInspirationsSettings(data, previousItem)); - dispatch(setInspirationFragmentList(previousItem.computed_inspirations)); + const inspirations = getInspirationsForMol(datasetID, previousItem.id); + dispatch(setInspirationMoleculeDataList(inspirations)); + // dispatch(setInspirationFragmentList(previousItem.computed_inspirations)); dispatch(setCrossReferenceCompoundName(moleculeTitlePrev)); if (setRef && ref.current) { setRef(refPrevious); @@ -752,7 +768,7 @@ export const DatasetMoleculeView = memo( clickOnInspirations({ datasetID, currentID, - computed_inspirations: data && data.computed_inspirations + computed_inspirations: getInspirationsForMol(datasetID, currentID) }) ); if (setRef) { diff --git a/js/components/datasets/inspirationDialog.js b/js/components/datasets/inspirationDialog.js index a593b6378..684774306 100644 --- a/js/components/datasets/inspirationDialog.js +++ b/js/components/datasets/inspirationDialog.js @@ -24,11 +24,10 @@ import { removeDensity, removeVector } from '../preview/molecule/redux/dispatchActions'; -import { loadInspirationMoleculesDataList } from './redux/dispatchActions'; import MoleculeView from '../preview/molecule/moleculeView'; import { moleculeProperty } from '../preview/molecule/helperConstants'; import { debounce } from 'lodash'; -import { setInspirationMoleculeDataList, setIsOpenInspirationDialog } from './redux/actions'; +import { setIsOpenInspirationDialog } from './redux/actions'; import { Button } from '../common/Inputs/Button'; import classNames from 'classnames'; // import { useDisableUserInteraction } from '../helpers/useEnableUserInteracion'; @@ -37,7 +36,6 @@ import { NglContext } from '../nglView/nglProvider'; import { VIEWS } from '../../constants/constants'; import { Panel } from '../common/Surfaces/Panel'; import { changeButtonClassname } from './helpers'; -import { getMoleculeList } from '../preview/molecule/redux/selectors'; const useStyles = makeStyles(theme => ({ paper: { @@ -143,14 +141,10 @@ export const InspirationDialog = memo( const { getNglView } = useContext(NglContext); const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; - const inspirationFragmentList = useSelector(state => state.datasetsReducers.inspirationFragmentList); - const isLoadingInspirationListOfMolecules = useSelector( state => state.datasetsReducers.isLoadingInspirationListOfMolecules ); const inspirationMoleculeDataList = useSelector(state => state.datasetsReducers.inspirationMoleculeDataList); - const allInspirationMoleculeDataList = useSelector(state => state.datasetsReducers.allInspirationMoleculeDataList); - const getJoinedMoleculeList = useSelector(state => getMoleculeList(state)); const ligandList = useSelector(state => state.selectionReducers.fragmentDisplayList); const proteinList = useSelector(state => state.selectionReducers.proteinList); @@ -162,16 +156,6 @@ export const InspirationDialog = memo( const dispatch = useDispatch(); // const disableUserInteraction = useDisableUserInteraction(); - useEffect(() => { - if (inspirationFragmentList && inspirationFragmentList.length > 0) { - dispatch(loadInspirationMoleculesDataList(inspirationFragmentList)).catch(error => { - throw new Error(error); - }); - } else { - dispatch(setInspirationMoleculeDataList([])); - } - }, [dispatch, inspirationFragmentList]); - let debouncedFn; const handleSearch = event => { @@ -224,8 +208,6 @@ export const InspirationDialog = memo( const selectMoleculeSite = moleculeGroupSite => {}; const removeOfAllSelectedTypes = () => { - let molecules = [...getJoinedMoleculeList, ...allInspirationMoleculeDataList]; - proteinList?.forEach(moleculeID => { let foundedMolecule = moleculeList?.find(mol => mol.id === moleculeID); foundedMolecule = foundedMolecule && Object.assign({ isInspiration: true }, foundedMolecule); diff --git a/js/components/datasets/redux/actions.js b/js/components/datasets/redux/actions.js index b89ee53f9..6d496a06c 100644 --- a/js/components/datasets/redux/actions.js +++ b/js/components/datasets/redux/actions.js @@ -248,6 +248,11 @@ export const setInspirationMoleculeDataList = (moleculeList = []) => ({ payload: moleculeList }); +export const setAllInspirations = (allInspirationsMap) => ({ + type: constants.SET_ALL_INSPIRATIONS, + payload: allInspirationsMap +}); + export const setAllInspirationMoleculeDataList = (moleculeList = []) => ({ type: constants.SET_ALL_INSPIRATION_MOLECULE_DATA_LIST, payload: moleculeList diff --git a/js/components/datasets/redux/constants.js b/js/components/datasets/redux/constants.js index 40eacee8c..b8e757e4a 100644 --- a/js/components/datasets/redux/constants.js +++ b/js/components/datasets/redux/constants.js @@ -29,6 +29,7 @@ export const constants = { SET_INSPIRATION_LIST: prefix + 'SET_INSPIRATION_LIST', APPEND_INSPIRATION_LIST: prefix + 'APPEND_INSPIRATION_LIST', REMOVE_FROM_INSPIRATION_LIST: prefix + 'REMOVE_FROM_INSPIRATION_LIST', + SET_ALL_INSPIRATIONS: prefix + 'SET_ALL_INSPIRATIONS', APPEND_TO_SCORE_DATASET_MAP: prefix + 'APPEND_TO_SCORE_DATASET_MAP', REMOVE_FROM_SCORE_DATASET_MAP: prefix + 'REMOVE_FROM_SCORE_DATASET_MAP', diff --git a/js/components/datasets/redux/dispatchActions.js b/js/components/datasets/redux/dispatchActions.js index 81fe66c5b..0d8cd375b 100644 --- a/js/components/datasets/redux/dispatchActions.js +++ b/js/components/datasets/redux/dispatchActions.js @@ -386,7 +386,7 @@ export const clearDatasetSettings = datasetID => dispatch => { export const clickOnInspirations = ({ datasetID, currentID, computed_inspirations = [] }) => dispatch => { dispatch(setInspirationList(datasetID, [currentID])); - dispatch(setInspirationFragmentList(computed_inspirations)); + dispatch(setInspirationMoleculeDataList(computed_inspirations)); dispatch(setIsOpenInspirationDialog(true)); }; diff --git a/js/components/datasets/redux/reducer.js b/js/components/datasets/redux/reducer.js index 28e22811b..1e7fa77c9 100644 --- a/js/components/datasets/redux/reducer.js +++ b/js/components/datasets/redux/reducer.js @@ -30,6 +30,7 @@ export const INITIAL_STATE = { isLoadingInspirationListOfMolecules: false, inspirationMoleculeDataList: [], allInspirationMoleculeDataList: [], + allInspirations: {}, // cross reference isOpenCrossReferenceDialog: false, @@ -341,6 +342,9 @@ export const datasetsReducers = (state = INITIAL_STATE, action = {}) => { } return Object.assign({}, state, { inspirationFragmentList: [...diminishedInspirationFragmentList] }); + case constants.SET_ALL_INSPIRATIONS: + return {...state, allInspirations: action.payload}; + case constants.APPEND_MOLECULE_TO_COMPOUNDS_TO_BUY_OF_DATASET: const setOfMolecules = new Set(state.compoundsToBuyDatasetMap[action.payload.datasetID]); setOfMolecules.add(action.payload.moleculeID); diff --git a/js/components/nglView/renderingObjects.js b/js/components/nglView/renderingObjects.js index b1f755bac..1ed54a329 100644 --- a/js/components/nglView/renderingObjects.js +++ b/js/components/nglView/renderingObjects.js @@ -6,6 +6,7 @@ import { defaultFocus } from './generatingObjects'; import { concatStructures, Selection, Shape, Matrix4 } from 'ngl'; +import {addToPdbCache} from '../../reducers/ngl/actions'; const showSphere = ({ stage, input_dict, object_name, representations }) => { let colour = input_dict.colour; @@ -80,18 +81,53 @@ const renderHitProtein = (ol, representations, orientationMatrix) => { return assignRepresentationArrayToComp(reprArray, comp); }; -const showHitProtein = ({ stage, input_dict, object_name, representations, orientationMatrix }) => { +const loadPdbFile = (url) => { + return fetch(url).then(response => response.text()).then(str => { + return new Blob([str], { type: 'text/plain' }) + }); +}; + +const getNameOfPdb = (url) => { + const parts = url.split('/'); + const last = parts[parts.length - 1]; + return last; +}; + +const getPdb = (url) => (dispatch, getState) => { + const state = getState(); + + const pdbCache = state.nglReducers.pdbCache; + const pdbName = getNameOfPdb(url); + if (pdbCache.hasOwnProperty(pdbName)) { + return new Promise((resolve, reject) => { + resolve(pdbCache[pdbName]) + }); + } else { + return loadPdbFile(url).then(b => { + dispatch(addToPdbCache(pdbName, b)); + return b; + }); + }; +}; + +const showHitProtein = ({ stage, input_dict, object_name, representations, orientationMatrix, dispatch }) => { let stringBlob = new Blob([input_dict.sdf_info], { type: 'text/plain' }); - return Promise.all([ - stage.loadFile(input_dict.prot_url, { ext: 'pdb', defaultAssembly: 'BU1' }), - stage.loadFile(stringBlob, { ext: 'sdf' }), - stage, - defaultFocus, - object_name, - input_dict.colour - ]).then(ol => renderHitProtein(ol, representations, orientationMatrix)); + + return dispatch(getPdb(input_dict.prot_url)).then(pdbBlob => { + return Promise.all([ + stage.loadFile(pdbBlob, { ext: 'pdb', defaultAssembly: 'BU1' }), + stage.loadFile(stringBlob, { ext: 'sdf' }), + stage, + defaultFocus, + object_name, + input_dict.colour + ]); + }).then(ol => { + renderHitProtein(ol, representations, orientationMatrix) + }); }; + const renderComplex = (ol, representations, orientationMatrix) => { let cs = concatStructures( ol[4], diff --git a/js/components/preview/Preview.js b/js/components/preview/Preview.js index 8ec554e9a..2ac0893f2 100644 --- a/js/components/preview/Preview.js +++ b/js/components/preview/Preview.js @@ -31,7 +31,7 @@ import { loadDatasetCompoundsWithScores, loadDataSets } from '../datasets/redux/ import { SelectedCompoundList } from '../datasets/selectedCompoundsList'; import { DatasetSelectorMenuButton } from '../datasets/datasetSelectorMenuButton'; import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; -import { setMoleculeListIsLoading, replaceAllMoleculeLists } from '../datasets/redux/actions'; +import { setMoleculeListIsLoading, setAllInspirations } from '../datasets/redux/actions'; const hitNavigatorWidth = 504; @@ -130,19 +130,18 @@ const Preview = memo(({ isStateLoaded, hideProjects }) => { const keys = Object.keys(moleculeLists); keys.forEach(key => { let dataset = moleculeLists[key]; - let mols = []; + let mols = {}; dataset.forEach(dsMol => { let inspirations = []; dsMol.computed_inspirations.forEach(id => { let lhsMol = allMolsMap[id]; inspirations.push(lhsMol); }); - dsMol.inspirations = inspirations; - mols.push(dsMol); + mols[dsMol.id] = inspirations; }); allDatasets[key] = mols; }); - dispatch(replaceAllMoleculeLists(allDatasets)); + dispatch(setAllInspirations(allDatasets)); } }, [all_mol_lists, moleculeLists, isLoadingMoleculeList, linearizeMoleculesLists, dispatch]); diff --git a/js/reducers/ngl/actions.js b/js/reducers/ngl/actions.js index 000f43a5c..16f25da94 100644 --- a/js/reducers/ngl/actions.js +++ b/js/reducers/ngl/actions.js @@ -1,6 +1,8 @@ /** * Created by abradley on 03/03/2018. */ +import { constant } from 'lodash'; +import { constants } from '../selection/constants'; import { CONSTANTS } from './constants'; export const loadNglObject = (target, representations) => ({ type: CONSTANTS.LOAD_OBJECT, target, representations }); @@ -85,3 +87,8 @@ export const removeMoleculeOrientation = moleculeGroupID => ({ type: CONSTANTS.REMOVE_MOLECULE_ORIENTATION, payload: moleculeGroupID }); + +export const addToPdbCache = (name, cacheItem) => ({ + type: CONSTANTS.ADD_TO_PDB_CACHE, + payload: {name: name, cacheItem: cacheItem} +}); diff --git a/js/reducers/ngl/actions.test.js b/js/reducers/ngl/actions.test.js index d862ef084..bf7e6ac9a 100644 --- a/js/reducers/ngl/actions.test.js +++ b/js/reducers/ngl/actions.test.js @@ -297,35 +297,6 @@ describe("testing ngl reducer's actions", () => { expect(result.nglOrientations).toStrictEqual(defaultScene[SCENES.defaultScene].nglOrientations); }); - /* - it('should reset current state to session scene', () => { - expect.hasAssertions(); - const sessionScene = { - objectsInView: { - molecule_1: { - properties: [{ a: 'dfg' }], - representations: [ - { - lastKnownID: 78904, - uuid: 9903, - other: 'fdjsdj' - }, - { - lastKnownID: 178904, - uuid: 19903, - other: '1fdjsdj' - } - ] - } - }, - nglOrientations: { a: [24, 566] } - }; - - let result = nglReducers(initialState, actions.setNglStateFromCurrentSnapshot(sessionScene)); - expect(result.objectsInView).toStrictEqual(sessionScene.objectsInView); - expect(result.nglOrientations).toStrictEqual(sessionScene.nglOrientations); - }); -*/ it('should remove all ngl view components', () => { expect.hasAssertions(); let result = nglReducers(initialState, actions.removeAllNglComponents()); diff --git a/js/reducers/ngl/constants.js b/js/reducers/ngl/constants.js index 78ca7400b..86a4de479 100644 --- a/js/reducers/ngl/constants.js +++ b/js/reducers/ngl/constants.js @@ -22,7 +22,9 @@ export const CONSTANTS = { SET_MOLECULE_ORIENTATIONS: prefix + 'SET_MOLECULE_ORIENTATIONS', APPEND_MOLECULE_ORIENTATION: prefix + 'SET_MOLECULE_ORIENTATION', - REMOVE_MOLECULE_ORIENTATION: prefix + 'REMOVE_MOLECULE_ORIENTATION' + REMOVE_MOLECULE_ORIENTATION: prefix + 'REMOVE_MOLECULE_ORIENTATION', + + ADD_TO_PDB_CACHE: prefix + 'ADD_TO_PDB_CACHE' }; export const SCENES = { diff --git a/js/reducers/ngl/dispatchActions.js b/js/reducers/ngl/dispatchActions.js index 770dcde77..4e0665206 100644 --- a/js/reducers/ngl/dispatchActions.js +++ b/js/reducers/ngl/dispatchActions.js @@ -45,7 +45,8 @@ export const loadObject = ({ object_name: versionFixedTarget.name, representations: previousRepresentations, orientationMatrix, - markAsRightSideLigand + markAsRightSideLigand, + dispatch }) .then(representations => { dispatch(loadNglObject(versionFixedTarget, representations)) diff --git a/js/reducers/ngl/nglReducers.js b/js/reducers/ngl/nglReducers.js index a187a370b..dd528df9d 100644 --- a/js/reducers/ngl/nglReducers.js +++ b/js/reducers/ngl/nglReducers.js @@ -1,6 +1,5 @@ import { BACKGROUND_COLOR, NGL_PARAMS } from '../../components/nglView/constants'; import { CONSTANTS } from './constants'; -import NglView from '../../components/nglView/nglView'; import { VIEWS } from '../../constants/constants'; export const INITIAL_STATE = { @@ -39,7 +38,8 @@ export const INITIAL_STATE = { [VIEWS.MAJOR_VIEW]: 0, [VIEWS.SUMMARY_VIEW]: 0 }, - moleculeOrientations: {} + moleculeOrientations: {}, + pdbCache: {} }; export default function nglReducers(state = INITIAL_STATE, action = {}) { @@ -174,6 +174,11 @@ export default function nglReducers(state = INITIAL_STATE, action = {}) { } return Object.assign({}, state, { moleculeOrientations: diminishedMoleculeOrientations }); + case CONSTANTS.ADD_TO_PDB_CACHE: + return {...state, pdbCache: { + ...state.pdbCache, [action.payload.name]: action.payload.cacheItem + }}; + default: return state; }