From 04e9444746d3ba8ddcc96d0fd16f1c02adce1389 Mon Sep 17 00:00:00 2001 From: Terry Truong Date: Tue, 26 Apr 2022 13:53:46 +1000 Subject: Have tol data in sqlite db, and add server script that accesses it Adapt otol-data-converting script to generate otol.db, add server.py script that provides access to that db, and adapt the app to query the server for tol data when needed. --- src/App.vue | 110 +++++++++++++++++++++++++++++------------ src/components/SearchModal.vue | 37 +++++++++----- 2 files changed, 105 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/App.vue b/src/App.vue index cb2b1f4..f1e6e2a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -39,9 +39,8 @@ function getReverseAction(action: Action): Action | null { } // Get tree-of-life data -import data from './tolData.json'; -let tolMap: TolMap = data; -const rootName = "[Elaeocarpus williamsianus + Brunellia mexicana]"; +const rootName = "cellular organisms"; +const tolMap: TolMap = {[rootName]: new TolNode()}; // Configurable options const defaultLytOpts: LayoutOptions = { @@ -117,6 +116,8 @@ export default defineComponent({ height: document.documentElement.clientHeight, resizeThrottled: false, resizeDelay: 50, //ms (increasing to 100 seems to cause resize-skipping when opening browser mobile-view) + // Other + excessTolNodeThreshold: 1000, // Threshold where excess tolMap entries are removed (done on tile collapse) }; }, computed: { @@ -173,15 +174,32 @@ export default defineComponent({ methods: { // For tile expand/collapse events onLeafClick(layoutNode: LayoutNode){ - let success = tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts, { - allowCollapse: false, - chg: {type: 'expand', node: layoutNode, tolMap: this.tolMap}, - layoutMap: this.layoutMap - }); - if (!success){ - layoutNode.failFlag = !layoutNode.failFlag; // Triggers failure animation + let doExpansion = () => { + let success = tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts, { + allowCollapse: false, + chg: {type: 'expand', node: layoutNode, tolMap: this.tolMap}, + layoutMap: this.layoutMap + }); + if (!success){ + layoutNode.failFlag = !layoutNode.failFlag; // Triggers failure animation + } + return success; + }; + // Check if data for node-to-expand exists, getting from server if needed + let tolNode = this.tolMap[layoutNode.name]; + if (tolNode.children[0] in this.tolMap == false){ + return fetch('/tolnode/' + layoutNode.name + '?type=children') + .then(response => response.json()) + .then(obj => { + Object.getOwnPropertyNames(obj).forEach(key => {this.tolMap[key] = obj[key]}); + doExpansion(); + }) + .catch(error => { + console.log('ERROR loading tolnode data', error); + }); + } else { + return new Promise((resolve, reject) => resolve(doExpansion())); } - return success; }, onNonleafClick(layoutNode: LayoutNode){ let success = tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts, { @@ -191,6 +209,19 @@ export default defineComponent({ }); if (!success){ layoutNode.failFlag = !layoutNode.failFlag; // Triggers failure animation + } else { + // Clear out excess nodes when a threshold is reached + let tolNodeNames = Object.getOwnPropertyNames(this.tolMap) + let extraNodes = tolNodeNames.length - this.layoutMap.size; + if (extraNodes > this.excessTolNodeThreshold){ + for (let n of tolNodeNames){ + if (!this.layoutMap.has(n)){ + delete this.tolMap[n]; + } + } + let numRemovedNodes = tolNodeNames.length - Object.getOwnPropertyNames(this.tolMap).length; + console.log(`Cleaned up tolMap (removed ${numRemovedNodes} out of ${tolNodeNames.length})`); + } } return success; }, @@ -271,26 +302,27 @@ export default defineComponent({ return; } // Attempt tile-expand - let success = this.onLeafClick(layoutNode); - if (success){ - setTimeout(() => this.expandToNode(name), this.uiOpts.tileChgDuration); - return; - } - // Attempt expand-to-view on ancestor just below activeRoot - if (layoutNode == this.activeRoot){ - console.log('Unable to complete search (not enough room to expand active root)'); - // Note: Only happens if screen is significantly small or node has significantly many children - this.modeRunning = false; - return; - } - while (true){ - if (layoutNode.parent! == this.activeRoot){ - break; + this.onLeafClick(layoutNode).then(success => { + if (success){ + setTimeout(() => this.expandToNode(name), this.uiOpts.tileChgDuration); + return; } - layoutNode = layoutNode.parent!; - } - this.onNonleafClickHeld(layoutNode); - setTimeout(() => this.expandToNode(name), this.uiOpts.tileChgDuration); + // Attempt expand-to-view on ancestor just below activeRoot + if (layoutNode == this.activeRoot){ + console.log('Unable to complete search (not enough room to expand active root)'); + // Note: Only happens if screen is significantly small or node has significantly many children + this.modeRunning = false; + return; + } + while (true){ + if (layoutNode.parent! == this.activeRoot){ + break; + } + layoutNode = layoutNode.parent!; + } + this.onNonleafClickHeld(layoutNode); + setTimeout(() => this.expandToNode(name), this.uiOpts.tileChgDuration); + }); }, // For auto-mode events onPlayIconClick(){ @@ -385,7 +417,9 @@ export default defineComponent({ this.setLastFocused(node.parent!); break; case 'expand': - this.autoPrevActionFail = !this.onLeafClick(node); + this.onLeafClick(node) + .then(success => this.autoPrevActionFail = !success) + .catch(error => this.autoPrevActionFail = true); break; case 'collapse': this.autoPrevActionFail = !this.onNonleafClick(node); @@ -457,6 +491,20 @@ export default defineComponent({ window.addEventListener('keyup', this.onKeyUp); tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts, {allowCollapse: true, layoutMap: this.layoutMap}); + // Get initial tol node data + fetch('/tolnode/' + rootName) + .then(response => response.json()) + .then(obj => { + Object.getOwnPropertyNames(obj).forEach(key => {this.tolMap[key] = obj[key]}); + this.layoutTree = initLayoutTree(this.tolMap, this.layoutTree.name, 0); + this.activeRoot = this.layoutTree; + this.layoutMap = initLayoutMap(this.layoutTree); + tryLayout(this.activeRoot, this.tileAreaPos, this.tileAreaDims, this.lytOpts, + {allowCollapse: true, layoutMap: this.layoutMap}); + }) + .catch(error => { + console.log('ERROR loading initial tolnode data', error); + }); }, unmounted(){ window.removeEventListener('resize', this.onResize); diff --git a/src/components/SearchModal.vue b/src/components/SearchModal.vue index 22b6896..5accd62 100644 --- a/src/components/SearchModal.vue +++ b/src/components/SearchModal.vue @@ -12,21 +12,36 @@ export default defineComponent({ }, methods: { onCloseClick(evt: Event){ - if (evt.target == this.$el || (this.$refs.searchInput as typeof SearchIcon).$el.contains(evt.target)){ + if (evt.target == this.$el || (this.$refs.searchIcon as typeof SearchIcon).$el.contains(evt.target)){ this.$emit('search-close'); } }, onSearchEnter(){ let input = this.$refs.searchInput as HTMLInputElement; - if (this.tolMap.hasOwnProperty(input.value)){ - this.$emit('search-node', input.value); - } else { - input.value = ''; - // Trigger failure animation - input.classList.remove('animate-red-then-fade'); - input.offsetWidth; // Triggers reflow - input.classList.add('animate-red-then-fade'); - } + // Query server + let url = new URL(window.location.href); + url.pathname = '/tolnode/' + input.value; + fetch(url) + .then(response => { + // Search successful. Get nodes in parent-chain, add to tolMap, then emit event. + url.search = '?type=chain'; + fetch(url) + .then(response => response.json()) + .then(obj => { + Object.getOwnPropertyNames(obj).forEach(key => {this.tolMap[key] = obj[key]}); + this.$emit('search-node', input.value); + }) + .catch(error => { + console.log('ERROR loading tolnode chain', error); + }); + }) + .catch(error => { + input.value = ''; + // Trigger failure animation + input.classList.remove('animate-red-then-fade'); + input.offsetWidth; // Triggers reflow + input.classList.add('animate-red-then-fade'); + }); }, focusInput(){ (this.$refs.searchInput as HTMLInputElement).focus(); @@ -46,7 +61,7 @@ export default defineComponent({ bg-stone-50 rounded-md shadow shadow-black flex gap-1"> - -- cgit v1.2.3