From 8638830d1595f532a475004da4d69dfbe39281b8 Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Thu, 20 Aug 2020 00:12:10 -0300 Subject: [PATCH] OHIF-329: Increase fetch limit when paginating more than 101 studies (#1985) * ohif-329: increase fetch limit when paginating more than 101 studies * ohif-329 cr update: use offset approach * ohif-329: remove logs * ohif-329: add comment * ohif-329: use cachemap approach * ohif-329: update total calculation * fix: settings/about menu should appear over study list filters * fix: about menu positioning for viewer template Co-authored-by: Danny Brown --- .../default/src/DicomWebDataSource/index.js | 4 +- platform/ui/src/components/NavBar/NavBar.jsx | 6 +- .../viewer/src/routes/DataSourceWrapper.jsx | 63 ++++++++++++++++--- .../viewer/src/routes/WorkList/WorkList.jsx | 20 +++--- 4 files changed, 69 insertions(+), 24 deletions(-) diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js index fa8cdfda0..43678c69e 100644 --- a/extensions/default/src/DicomWebDataSource/index.js +++ b/extensions/default/src/DicomWebDataSource/index.js @@ -66,7 +66,7 @@ function createDicomWebApi(dicomWebConfig) { query: { studies: { mapParams: mapParams.bind(), - search: async function(origParams) { + search: async function (origParams) { const { studyInstanceUid, seriesInstanceUid, ...mappedParams } = mapParams(origParams, { supportsFuzzyMatching, @@ -86,7 +86,7 @@ function createDicomWebApi(dicomWebConfig) { }, series: { // mapParams: mapParams.bind(), - search: async function(studyInstanceUid) { + search: async function (studyInstanceUid) { const results = await seriesInStudy( qidoDicomWebClient, studyInstanceUid diff --git a/platform/ui/src/components/NavBar/NavBar.jsx b/platform/ui/src/components/NavBar/NavBar.jsx index 8adc0ce3b..4492ebc6c 100644 --- a/platform/ui/src/components/NavBar/NavBar.jsx +++ b/platform/ui/src/components/NavBar/NavBar.jsx @@ -2,14 +2,16 @@ import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -const stickyClasses = 'sticky top-0 z-10'; +const stickyClasses = 'sticky top-0'; +const notStickyClasses = 'relative'; const NavBar = ({ className, children, isSticky }) => { return (
diff --git a/platform/viewer/src/routes/DataSourceWrapper.jsx b/platform/viewer/src/routes/DataSourceWrapper.jsx index 1c9b30907..35e8b7298 100644 --- a/platform/viewer/src/routes/DataSourceWrapper.jsx +++ b/platform/viewer/src/routes/DataSourceWrapper.jsx @@ -6,6 +6,9 @@ import { MODULE_TYPES } from '@ohif/core'; import { useAppConfig } from '@state'; import { extensionManager } from '../App.jsx'; +let cacheMap = {}; +let total = {}; + /** * Uses route properties to determine the data source that should be passed * to the child layout template. In some instances, initiates requests and @@ -17,8 +20,6 @@ import { extensionManager } from '../App.jsx'; function DataSourceWrapper(props) { const [appConfig] = useAppConfig(); const { children: LayoutTemplate, history, ...rest } = props; - const queryFilterValues = _getQueryFilterValues(history.location.search); - // TODO: Fetch by type, name, etc? const dataSourceModules = extensionManager.modules[MODULE_TYPES.DATA_SOURCE]; // TODO: Good usecase for flatmap? @@ -46,17 +47,61 @@ function DataSourceWrapper(props) { // studies.processResults --> // But only for LayoutTemplate type of 'list'? // Or no data fetching here, and just hand down my source - const [data, setData] = useState([]); + const STUDIES_LIMIT = 101; + const [data, setData] = useState({ studies: [], total: 0 }); const [isLoading, setIsLoading] = useState(false); + useEffect(() => { // 204: no content async function getData() { setIsLoading(true); - const searchResults = await dataSource.query.studies.search( - queryFilterValues - ); - setData(searchResults); + + const limit = STUDIES_LIMIT - 1; + const queryFilterValues = _getQueryFilterValues(history.location.search); + const { resultsPerPage = 25, pageNumber = 1 } = queryFilterValues; + const reachedLimits = parseInt((resultsPerPage * pageNumber) / STUDIES_LIMIT); + const cacheKey = `${pageNumber}-${resultsPerPage}`; + + const getFromCache = async ({ cacheKey, pageNumber, resultsPerPage, limit, options }) => { + const pagesAmount = limit / resultsPerPage; + const pageToRequest = parseInt((resultsPerPage * pageNumber) / STUDIES_LIMIT); + + let length = 0; + if (!cacheMap[cacheKey]) { + length = pageToRequest > 0 ? (pageToRequest * STUDIES_LIMIT) : 1; + const studiesPromise = dataSource.query.studies.search(options); + + for (let pageNum = 0; pageNum < pagesAmount; pageNum++) { + const currentPageNumber = (pageNum + 1) + (pageToRequest * pagesAmount); + cacheMap[`${currentPageNumber}-${resultsPerPage}`] = studiesPromise.then(function (results) { + const slicedResult = results.slice((pageNum * resultsPerPage), ((pageNum + 1) * resultsPerPage)); + length += slicedResult.length; + return slicedResult; + }); + } + } + + const cache = await cacheMap[cacheKey]; + return { cache, length, index: pageToRequest }; + }; + + const { cache: studies, index, length } = await getFromCache({ + cacheKey, + pageNumber, + resultsPerPage, + limit, + options: { ...queryFilterValues, ...{ offset: reachedLimits * limit } } + }); + + const totalKey = `${resultsPerPage}-${index}`; + total[totalKey] = total[totalKey] ? total[totalKey] + length : length; + const totals = Object.keys(total).map(key => total[key]); + const biggestIndex = totals.indexOf(Math.max(...totals)); + const biggestKey = Object.keys(total)[biggestIndex]; + const biggestTotal = total[biggestKey]; + setIsLoading(false); + setData({ studies, total: biggestTotal }); } try { @@ -73,7 +118,8 @@ function DataSourceWrapper(props) { @@ -106,6 +152,7 @@ function _getQueryFilterValues(query) { startDate: query.get('startDate'), endDate: query.get('endDate'), page: _tryParseInt(query.get('page'), undefined), + pageNumber: _tryParseInt(query.get('pageNumber'), undefined), resultsPerPage: _tryParseInt(query.get('resultsPerPage'), undefined), // Rarely supported server-side sortBy: query.get('sortBy'), diff --git a/platform/viewer/src/routes/WorkList/WorkList.jsx b/platform/viewer/src/routes/WorkList/WorkList.jsx index 9a703beae..08e99bc0d 100644 --- a/platform/viewer/src/routes/WorkList/WorkList.jsx +++ b/platform/viewer/src/routes/WorkList/WorkList.jsx @@ -33,15 +33,15 @@ const seriesInStudiesMap = new Map(); * TODO: * - debounce `setFilterValues` (150ms?) */ -function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysManager }) { +function WorkList({ history, data: studies, dataTotal: studiesTotal, isLoadingData, dataSource, hotkeysManager }) { const { hotkeyDefinitions, hotkeyDefaults } = hotkeysManager; const { show, hide } = useModal(); const { t } = useTranslation(); - // ~ Modes const [appConfig] = useAppConfig(); // ~ Filters const query = useQuery(); + const STUDIES_LIMIT = 101; const queryFilterValues = _getQueryFilterValues(query); const [filterValues, _setFilterValues] = useState({ ...defaultFilterValues, @@ -64,12 +64,9 @@ function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysMa const noSortApplied = sortBy === '' || !sortBy; const sortModifier = sortDirection === 'descending' ? 1 : -1; - if (noSortApplied && studies.length < 101) { + if (noSortApplied && studiesTotal < STUDIES_LIMIT) { const ascendingSortModifier = -1; - defaultSortValues = { - sortBy: 'studyDate', - sortDirection: 'ascending', - }; + defaultSortValues = { sortBy: 'studyDate', sortDirection: 'ascending' }; return _sortStringDates(s1, s2, ascendingSortModifier); } else if (noSortApplied) { return 0; @@ -96,7 +93,7 @@ function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysMa // ~ Rows & Studies const [expandedRows, setExpandedRows] = useState([]); const [studiesWithSeriesData, setStudiesWithSeriesData] = useState([]); - const numOfStudies = studies.length; + const numOfStudies = studiesTotal; const totalPages = Math.floor(numOfStudies / resultsPerPage) + 1; const setFilterValues = val => { @@ -111,6 +108,7 @@ function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysMa if (newPageNumber > totalPages) { return; } + setFilterValues({ ...filterValues, pageNumber: newPageNumber }); }; @@ -135,6 +133,7 @@ function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysMa if (!debouncedFilterValues) { return; } + const queryString = {}; Object.keys(defaultFilterValues).forEach(key => { const defaultValue = defaultFilterValues[key]; @@ -389,10 +388,7 @@ function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysMa {hasStudies ? ( <>