diff --git a/package-lock.json b/package-lock.json index 4cd4b11..93bdfac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "evs-sip", - "version": "v3.0.0", + "version": "v3.0.1", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 773a58b..6a4ec0c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "evs-sip", - "version": "v3.0.0", + "version": "v3.0.1", "private": true, "homepage": "/evssip/", "dependencies": { diff --git a/server/service/esapi/esapicontroller.js b/server/service/esapi/esapicontroller.js index 283a721..aa30bea 100644 --- a/server/service/esapi/esapicontroller.js +++ b/server/service/esapi/esapicontroller.js @@ -374,18 +374,18 @@ const getGraphicalPCDCDictionary = (project = null, node, prop) => { }; let result; - // TODO - fix result being empty after making same request for 2nd time - // if (project) { - // result = cache.getValue(`pcdc_dict_${project}`); - // } else { - // result = cache.getValue("pcdc_dict"); - // } + // Read from cache + if (project) { + result = cache.getValue(`public_pcdc_dict_${project}`); + } else { + result = cache.getValue("public_pcdc_dict"); + } if (true || result === undefined) { let jsonData = shared.readPCDCMapping(); result = generatePCDCData(jsonData, {}); //result = generatePCDCData(jsonData, {Relationships: {}}); - cache.setValue("pcdc_dict", result, config.item_ttl); + cache.setValue("public_pcdc_dict", result, config.item_ttl); } // Obtain nodes from specified project @@ -417,7 +417,13 @@ const getGraphicalPCDCDictionary = (project = null, node, prop) => { project_result[n].links.push(linkItem); } }); - cache.setValue("pcdc_dict_" + project, project_result, config.item_ttl); + + // Cache the results + if (project) { + cache.setValue(`public_pcdc_dict_${project}`, project_result, config.item_ttl); + } else { + cache.setValue("public_pcdc_dict", project_result, config.item_ttl); + } // Handle empty results if (project_result.results.length === 0 ) { @@ -442,7 +448,7 @@ const getPcdcNodes = (nodes, desiredNode, desiredProp) => { const desiredNodes = []; // Gather desired nodes into an array - for(const nodeName in nodes) { + for (const nodeName in nodes) { const node = nodes[nodeName]; const isCorrectNode = node.node_name?.toLowerCase() === desiredNode?.toLowerCase(); diff --git a/server/service/search/controller.js b/server/service/search/controller.js index 45cf227..50c4791 100644 --- a/server/service/search/controller.js +++ b/server/service/search/controller.js @@ -468,7 +468,7 @@ const getGraphicalCTDCDictionary = (req, res) => { }; const getGraphicalPCDCDictionary = (req, res) => { - let project = req.query.project == "" ? "AML" : req.query.project; + let project = (req.query.project !== undefined && req.query.project !== '') ? req.query.project : 'AML'; let jsonData = shared.getGraphicalPCDCDictionary(project); res.json(jsonData); }; @@ -1290,92 +1290,236 @@ const exportAllCompareResult = async function(req, res){ res.send(report); } -const generateProperties = async function(req, res) { - const dataset = []; - let output_file_path = path.join(__dirname, '..', '..', 'data_files', 'GDC', 'gdc_values_updated.js'); - let GDCDict = await shared.getGDCDictionaryByVersion("2.4.1"); - - for(let node in GDCDict){ - let entry = GDCDict[node]; - let uid = node + "/" + entry.category + "/gdc"; - if(entry.properties){ - let prop_dict = entry.properties; - for(let prop in prop_dict){ - let tmp = {}; - tmp.category = entry.category; - tmp.node = node; - tmp.property = prop; - let dict = prop_dict[prop]; - tmp.ncit = dict.termDef && dict.termDef.source && dict.termDef.source == "NCIt" ? (dict.termDef.term_id || dict.termDef.cde_id ) : ""; - dataset.push(tmp); - } - } - } +const generateGDCPropertiesReport = async function(req, res) { + const dataset = []; + let GDCDict = await shared.getGDCDictionaryByVersion("2.5.0"); + let prop_mapping = shared.readGDCProps(); + + for(let node in GDCDict){ + let entry = GDCDict[node]; + if(entry.properties){ + let prop_dict = entry.properties; + for(let prop in prop_dict){ + let tmp = {}; + let uid = entry.category + "." + node + "." + prop; + tmp.category = entry.category; + tmp.node = node; + tmp.property = prop; + let dict = prop_dict[prop]; + tmp.ncit = prop_mapping[uid] !== undefined ? prop_mapping[uid] : ''; + tmp.cdeid = dict.termDef && dict.termDef.source && dict.termDef.source === "caDSR" ? (dict.termDef.cde_id) : ""; + dataset.push(tmp); + } + } + } // You can define styles as json object - const styles = { + const styles = { cellPink: { fill: { - fgColor: { - rgb: 'FF00FF00' - } + fgColor: { + rgb: '1F497D' + } + }, + font:{ + color: { + rgb: 'FFFFFF' + } } } - }; + }; - //Array of objects representing heading rows (very top) - const heading = [ - ]; + //Array of objects representing heading rows (very top) + const heading = [ + ]; - //Here you specify the export structure - const specification = { - category: { // <- the key should match the actual data key - displayName: 'Category', // <- Here you specify the column header - headerStyle: styles.cellPink, // <- Header style - width: 220 // <- width in pixels - }, - node: { - displayName: 'Node', - headerStyle: styles.cellPink, - width: 220 // <- width in chars (when the number is passed as string) - }, - property: { - displayName: 'Property', - headerStyle: styles.cellPink, - width: 220 // <- width in pixels - }, - ncit: { - displayName: 'NCIt Code', - headerStyle: styles.cellPink, - width: 220 // <- width in pixels - } + //Here you specify the export structure + const specification = { + category: { // <- the key should match the actual data key + displayName: 'Category', // <- Here you specify the column header + headerStyle: styles.cellPink, // <- Header style + width: 220 // <- width in pixels + }, + node: { + displayName: 'Node', + headerStyle: styles.cellPink, + width: 220 // <- width in chars (when the number is passed as string) + }, + property: { + displayName: 'Property', + headerStyle: styles.cellPink, + width: 220 // <- width in pixels + }, + ncit: { + displayName: 'NCIt Code', + headerStyle: styles.cellPink, + width: 220 // <- width in pixels + }, + cdeid: { + displayName: 'CDE ID', + headerStyle: styles.cellPink, + width: 220 // <- width in pixels } + } - // Define an array of merges. 1-1 = A:1 - // The merges are independent of the data. - // A merge will overwrite all data _not_ in the top-left cell. - const merges = []; - - // Create the excel report. - // This function will return Buffer - const report = export_excel.buildExport( - [ // <- Notice that this is an array. Pass multiple sheets to create multi sheet report - { - name: 'Report', // <- Specify sheet name (optional) - heading: heading, // <- Raw heading array (optional) - merges: merges, // <- Merge cell ranges - specification: specification, // <- Report specification - data: dataset // <-- Report data + // Define an array of merges. 1-1 = A:1 + // The merges are independent of the data. + // A merge will overwrite all data _not_ in the top-left cell. + const merges = []; + + // Create the excel report. + // This function will return Buffer + const report = export_excel.buildExport( + [ // <- Notice that this is an array. Pass multiple sheets to create multi sheet report + { + name: 'Report', // <- Specify sheet name (optional) + heading: heading, // <- Raw heading array (optional) + merges: merges, // <- Merge cell ranges + specification: specification, // <- Report specification + data: dataset // <-- Report data + } + ] + ); + + // You can then return this straight + res.attachment('GDC_Properties_Data_Mapping.xlsx'); // This is sails.js specific (in general you need to set headers) + res.send(report); +} + + +const generateGDCValuesReport = async function(req, res) { + const dataset = []; + let GDCDict = await shared.getGDCDictionaryByVersion("2.5.0"); + let gdc_values = shared.readGDCValues(); + let syns = shared.readNCItDetails(); + + for(let node in GDCDict){ + let entry = GDCDict[node]; + if(entry.properties){ + let prop_dict = entry.properties; + for(let prop in prop_dict){ + let uid = entry.category + "." + node + "." + prop; + let prop_enum = prop_dict[prop] !== undefined && prop_dict[prop].enum !== undefined && prop_dict[prop].enum.length > 0 ? prop_dict[prop].enum : []; + let mappings = gdc_values[uid] !== undefined ? gdc_values[uid] : []; + + for(let value of prop_enum){ + let tmp = {}; + tmp.category = entry.category; + tmp.node = node; + tmp.property = prop; + tmp.value = value; + tmp.ncit = []; + tmp.icdo3 = ''; + tmp.icdoS = []; + tmp.ncitPV = []; + + let map = mappings.find(({ nm }) => nm === value); + if(map !== undefined){ + tmp.ncit = Array.isArray(map.n_c) ? map.n_c.join('|') : ''; + tmp.icdo3 = map.i_c; + tmp.icdoS = Array.isArray(map.i_c_s) ? map.i_c_s.join('|') : '' + for(let code of map.n_c){ + tmp.ncitPV.push(syns[code] !== undefined ? syns[code].label : ''); + } + tmp.ncitPV = Array.isArray(tmp.ncitPV) ? tmp.ncitPV.join('|') : ''; + } + + dataset.push(tmp); + } } - ] - ); + } + } + + // You can define styles as json object + const styles = { + cellPink: { + fill: { + fgColor: { + rgb: '1F497D' + } + }, + font:{ + color: { + rgb: 'FFFFFF' + } + } + } + }; - // You can then return this straight - res.attachment('report.xlsx'); // This is sails.js specific (in general you need to set headers) - res.send(report); + //Array of objects representing heading rows (very top) + const heading = [ + ]; + + //Here you specify the export structure + const specification = { + category: { // <- the key should match the actual data key + displayName: 'Category', // <- Here you specify the column header + headerStyle: styles.cellPink, // <- Header style + width: 220 // <- width in pixels + }, + node: { + displayName: 'Node', + headerStyle: styles.cellPink, + width: 220 // <- width in chars (when the number is passed as string) + }, + property: { + displayName: 'Property', + headerStyle: styles.cellPink, + width: 220 // <- width in pixels + }, + value: { + displayName: 'GDC Values', + headerStyle: styles.cellPink, + width: 220 // <- width in pixels + }, + ncit: { + displayName: 'NCIt Code', + headerStyle: styles.cellPink, + width: 220 // <- width in pixels + }, + ncitPV: { + displayName: 'NCIt PV', + headerStyle: styles.cellPink, + width: 220 // <- width in pixels + }, + icdo3: { + displayName: 'ICDO3 Code', + headerStyle: styles.cellPink, + width: 220 // <- width in pixels + }, + icdoS: { + displayName: 'ICDO3 String', + headerStyle: styles.cellPink, + width: 220 // <- width in pixels + } + } + + // Define an array of merges. 1-1 = A:1 + // The merges are independent of the data. + // A merge will overwrite all data _not_ in the top-left cell. + const merges = []; + + // Create the excel report. + // This function will return Buffer + const report = export_excel.buildExport( + [ // <- Notice that this is an array. Pass multiple sheets to create multi sheet report + { + name: 'Report', // <- Specify sheet name (optional) + heading: heading, // <- Raw heading array (optional) + merges: merges, // <- Merge cell ranges + specification: specification, // <- Report specification + data: dataset // <-- Report data + } + ] + ); + + // You can then return this straight + res.attachment('GDC_Values_Data_Mapping.xlsx'); // This is sails.js specific (in general you need to set headers) + res.send(report); } + const generateCompareProperties = async function(req, res) { try { const datasetnew = {}; @@ -1959,7 +2103,8 @@ module.exports = { compareAllWithGDCDictionary, exportCompareResult, exportAllCompareResult, - generateProperties, + generateGDCPropertiesReport, + generateGDCValuesReport, generateCompareProperties, generateCompareNodes, updateGDCPropertyMappings, diff --git a/server/service/search/index.js b/server/service/search/index.js index d30985a..b30a333 100644 --- a/server/service/search/index.js +++ b/server/service/search/index.js @@ -25,7 +25,8 @@ router.get("/p/local/vs", controller.getGDCData); router.get('/compareAllWithGDCDictionary', controller.compareAllWithGDCDictionary); router.get('/exportCompareResult', controller.exportCompareResult); router.get('/exportAllCompareResult', controller.exportAllCompareResult); -//router.get('/generateProperties', controller.generateProperties); +//router.get('/generateGDCPropertiesReport', controller.generateGDCPropertiesReport); +//router.get('/generateGDCValuesReport', controller.generateGDCValuesReport); //router.get('/generateCompareProperties', controller.generateCompareProperties); //router.get('/generateCompareNodes', controller.generateCompareNodes); //router.get('/updateGDCPropertyMappings', controller.updateGDCPropertyMappings); diff --git a/server/service/search/shared.js b/server/service/search/shared.js index 5fd15bc..b0e5418 100644 --- a/server/service/search/shared.js +++ b/server/service/search/shared.js @@ -1284,35 +1284,42 @@ const getCompareResult_conflict = async function(searchText, from , limit) { const getGraphicalPCDCDictionary = (project) => { let project_result = cache.getValue("pcdc_dict_" + project); - if (project_result == undefined) { - let result = cache.getValue("pcdc_dict"); - if (result == undefined) { - let jsonData = readPCDCMapping(); - result = generatePCDCData(jsonData, {}); - //result = generatePCDCData(jsonData, {Relationships: {}}); - cache.setValue("pcdc_dict", result, config.item_ttl); + + // Early return for successful cache retrieval + if (project_result !== undefined) { + return project_result; + } + + let result = cache.getValue("pcdc_dict"); + + if (result === undefined) { + let jsonData = readPCDCMapping(); + result = generatePCDCData(jsonData, {}); + //result = generatePCDCData(jsonData, {Relationships: {}}); + cache.setValue("pcdc_dict", result, config.item_ttl); + } + + project_result = result[project]; + let nodes = Object.keys(project_result); + //create fake relationship for graphical display purpose + nodes.forEach((n, i) => { + if (i - 4 >= 0) { + let linkItem = {}; + linkItem["name"] = nodes[i - 4]; + linkItem["backref"] = n; + linkItem["label"] = "of_pcdc"; + linkItem["target_type"] = nodes[i - 4]; + linkItem["required"] = false; + + project_result[n].links.push(linkItem); } + }); - project_result = result[project]; - let nodes = Object.keys(project_result); - //create fake relationship for graphical display purpose - - nodes.forEach((n, i) => { - if (i - 4 >= 0) { - let linkItem = {}; - linkItem["name"] = nodes[i - 4]; - linkItem["backref"] = n; - linkItem["label"] = "of_pcdc"; - linkItem["target_type"] = nodes[i - 4]; - linkItem["required"] = false; - - // TODO - find out why linkItem is sometimes undefined - if (linkItem) { - project_result[n].links.push(linkItem); - } - } - }); - cache.setValue("pcdc_dict_" + project, project_result, config.item_ttl); + // Cache the results + if (project) { + cache.setValue(`pcdc_dict_${project}`, project_result, config.item_ttl); + } else { + cache.setValue("pcdc_dict", project_result, config.item_ttl); } return project_result; diff --git a/src/pages/Search/SearchBox.js b/src/pages/Search/SearchBox.js index 0d69fd1..458b53a 100644 --- a/src/pages/Search/SearchBox.js +++ b/src/pages/Search/SearchBox.js @@ -1,5 +1,6 @@ import React, { useState, useRef } from 'react'; import styled from 'styled-components'; +import debounce from 'lodash.debounce'; import { apiSuggest } from '../../api'; import { InputGroup, FormControl, Button } from 'react-bootstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' @@ -321,9 +322,20 @@ const SearchBox = (props) => { setSelectIndexState(-1); }; + const suggestHandlerDebounce = useRef( + debounce((value) => { + apiSuggest(value) + .then(result => setSuggestState(result)) + .catch(error => { + console.log(error); + }); + }, 300) + ).current; + const suggestHandler = event => { - setSearchState(event.target.value); - apiSuggest(event.target.value).then(result => setSuggestState(result)); + let value = event.target.value; + setSearchState(value); + suggestHandlerDebounce(value); }; const matchOptionsHandler = event => {