chore(prettier): full repo linting and code reformatting once and for all (#3627)
This commit is contained in:
1 parent
01500a2d4c
commit
9045ddaedc
656 files changed
+21737
-20628
No files matched your search
@@ -7,7 +7,6 @@ const ENTRY = {
|
||||
app: `${SRC_DIR}/index.tsx`,
|
||||
};
|
||||
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return webpackCommon(env, argv, { SRC_DIR, DIST_DIR, ENTRY });
|
||||
};
|
||||
@@ -40,13 +40,7 @@ module.exports = (env, argv) => {
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
},
|
||||
externals: [
|
||||
/\b(vtk.js)/,
|
||||
/\b(dcmjs)/,
|
||||
/\b(gl-matrix)/,
|
||||
/^@ohif/,
|
||||
/^@cornerstonejs/,
|
||||
],
|
||||
externals: [/\b(vtk.js)/, /\b(dcmjs)/, /\b(gl-matrix)/, /^@ohif/, /^@cornerstonejs/],
|
||||
plugins: [
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1,
|
||||
|
||||
@@ -16,11 +16,7 @@ async function createReportAsync(
|
||||
measurements,
|
||||
options
|
||||
) {
|
||||
const {
|
||||
displaySetService,
|
||||
uiNotificationService,
|
||||
uiDialogService,
|
||||
} = servicesManager.services;
|
||||
const { displaySetService, uiNotificationService, uiDialogService } = servicesManager.services;
|
||||
const loadingDialogId = uiDialogService.create({
|
||||
showOverlay: true,
|
||||
isDraggable: false,
|
||||
|
||||
@@ -18,13 +18,10 @@ function DataSourceConfigurationComponent({
|
||||
|
||||
const { customizationService } = servicesManager.services;
|
||||
|
||||
const [configurationAPI, setConfigurationAPI] = useState<
|
||||
Types.BaseDataSourceConfigurationAPI
|
||||
>();
|
||||
const [configurationAPI, setConfigurationAPI] = useState<Types.BaseDataSourceConfigurationAPI>();
|
||||
|
||||
const [configuredItems, setConfiguredItems] = useState<
|
||||
Array<Types.BaseDataSourceConfigurationAPIItem>
|
||||
>();
|
||||
const [configuredItems, setConfiguredItems] =
|
||||
useState<Array<Types.BaseDataSourceConfigurationAPIItem>>();
|
||||
|
||||
useEffect(() => {
|
||||
let shouldUpdate = true;
|
||||
@@ -37,9 +34,7 @@ function DataSourceConfigurationComponent({
|
||||
}
|
||||
|
||||
const { factory: configurationAPIFactory } =
|
||||
customizationService.get(
|
||||
activeDataSourceDef.configuration.configurationAPI
|
||||
) ?? {};
|
||||
customizationService.get(activeDataSourceDef.configuration.configurationAPI) ?? {};
|
||||
|
||||
if (!configurationAPIFactory) {
|
||||
return;
|
||||
@@ -95,24 +90,25 @@ function DataSourceConfigurationComponent({
|
||||
}, [configurationAPI, configuredItems, showConfigurationModal]);
|
||||
|
||||
return configuredItems ? (
|
||||
<div className="flex text-aqua-pale overflow-hidden items-center">
|
||||
<div className="text-aqua-pale flex items-center overflow-hidden">
|
||||
<Icon
|
||||
name="settings"
|
||||
className="cursor-pointer shrink-0 w-3.5 h-3.5 mr-2.5"
|
||||
className="mr-2.5 h-3.5 w-3.5 shrink-0 cursor-pointer"
|
||||
onClick={showConfigurationModal}
|
||||
></Icon>
|
||||
{configuredItems.map((item, itemIndex) => {
|
||||
return (
|
||||
<div key={itemIndex} className="flex overflow-hidden">
|
||||
<div
|
||||
key={itemIndex}
|
||||
className="flex overflow-hidden"
|
||||
>
|
||||
<div
|
||||
key={itemIndex}
|
||||
className="text-ellipsis whitespace-nowrap overflow-hidden"
|
||||
className="overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
>
|
||||
{item.name}
|
||||
</div>
|
||||
{itemIndex !== configuredItems.length - 1 && (
|
||||
<div className="px-2.5">|</div>
|
||||
)}
|
||||
{itemIndex !== configuredItems.length - 1 && <div className="px-2.5">|</div>}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -5,8 +5,7 @@ import { Icon } from '@ohif/ui';
|
||||
import { Types } from '@ohif/core';
|
||||
import ItemListComponent from './ItemListComponent';
|
||||
|
||||
const NO_WRAP_ELLIPSIS_CLASS_NAMES =
|
||||
'text-ellipsis whitespace-nowrap overflow-hidden';
|
||||
const NO_WRAP_ELLIPSIS_CLASS_NAMES = 'text-ellipsis whitespace-nowrap overflow-hidden';
|
||||
|
||||
type DataSourceConfigurationModalComponentProps = {
|
||||
configurationAPI: Types.BaseDataSourceConfigurationAPI;
|
||||
@@ -21,9 +20,7 @@ function DataSourceConfigurationModalComponent({
|
||||
}: DataSourceConfigurationModalComponentProps) {
|
||||
const { t } = useTranslation('DataSourceConfiguration');
|
||||
|
||||
const [itemList, setItemList] = useState<
|
||||
Array<Types.BaseDataSourceConfigurationAPIItem>
|
||||
>();
|
||||
const [itemList, setItemList] = useState<Array<Types.BaseDataSourceConfigurationAPIItem>>();
|
||||
|
||||
const [selectedItems, setSelectedItems] = useState(configuredItems);
|
||||
|
||||
@@ -104,9 +101,7 @@ function DataSourceConfigurationModalComponent({
|
||||
itemIndex < selectedItems.length
|
||||
? classNames(
|
||||
'bg-black/[.4]',
|
||||
itemIndex !== itemLabels.length - 1
|
||||
? 'hover:bg-transparent active:bg-secondary-dark'
|
||||
: ''
|
||||
itemIndex !== itemLabels.length - 1 ? 'hover:bg-transparent active:bg-secondary-dark' : ''
|
||||
)
|
||||
: 'bg-transparent';
|
||||
|
||||
@@ -118,17 +113,15 @@ function DataSourceConfigurationModalComponent({
|
||||
: 'border border-dashed border-secondary-light';
|
||||
|
||||
const getSelectedItemTextClasses = itemIndex =>
|
||||
itemIndex <= selectedItems.length
|
||||
? 'text-primary-light'
|
||||
: 'text-primary-active';
|
||||
itemIndex <= selectedItems.length ? 'text-primary-light' : 'text-primary-active';
|
||||
|
||||
const getErrorComponent = (): ReactElement => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 min-h-[1px] grow">
|
||||
<div className="flex min-h-[1px] grow flex-col gap-4">
|
||||
<div className="text-primary-light text-[20px]">
|
||||
{t(`Error fetching ${itemLabels[selectedItems.length]} list`)}
|
||||
</div>
|
||||
<div className="bg-black text-[14px] grow p-4">{errorMessage}</div>
|
||||
<div className="grow bg-black p-4 text-[14px]">{errorMessage}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -141,7 +134,7 @@ function DataSourceConfigurationModalComponent({
|
||||
<div
|
||||
key={itemLabel}
|
||||
className={classNames(
|
||||
'rounded-md p-3.5 flex flex-col gap-1 shrink min-w-[1px] basis-[200px]',
|
||||
'flex min-w-[1px] shrink basis-[200px] flex-col gap-1 rounded-md p-3.5',
|
||||
getSelectedItemCursorClasses(itemLabelIndex),
|
||||
getSelectedItemBackgroundClasses(itemLabelIndex),
|
||||
getSelectedItemBorderClasses(itemLabelIndex),
|
||||
@@ -152,30 +145,21 @@ function DataSourceConfigurationModalComponent({
|
||||
itemLabelIndex <= currentSelectedItemIndex
|
||||
? () => {
|
||||
setShowFullConfig(false);
|
||||
setSelectedItems(theList =>
|
||||
theList.slice(0, itemLabelIndex)
|
||||
);
|
||||
setSelectedItems(theList => theList.slice(0, itemLabelIndex));
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<div className="flex gap-2 items-center text-">
|
||||
<div className="text- flex items-center gap-2">
|
||||
{itemLabelIndex < selectedItems.length ? (
|
||||
<Icon name="status-tracked" />
|
||||
) : (
|
||||
<Icon name="status-untracked" />
|
||||
)}
|
||||
<div className={classNames(NO_WRAP_ELLIPSIS_CLASS_NAMES)}>
|
||||
{t(itemLabel)}
|
||||
</div>
|
||||
<div className={classNames(NO_WRAP_ELLIPSIS_CLASS_NAMES)}>{t(itemLabel)}</div>
|
||||
</div>
|
||||
{itemLabelIndex < selectedItems.length ? (
|
||||
<div
|
||||
className={classNames(
|
||||
'text-white text-[14px]',
|
||||
NO_WRAP_ELLIPSIS_CLASS_NAMES
|
||||
)}
|
||||
>
|
||||
<div className={classNames('text-[14px] text-white', NO_WRAP_ELLIPSIS_CLASS_NAMES)}>
|
||||
{selectedItems[itemLabelIndex].name}
|
||||
</div>
|
||||
) : (
|
||||
@@ -189,9 +173,9 @@ function DataSourceConfigurationModalComponent({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-[calc(100vh-300px)] flex flex-col pt-0.5 gap-4 select-none">
|
||||
<div className="flex h-[calc(100vh-300px)] select-none flex-col gap-4 pt-0.5">
|
||||
{getSelectedItemsComponent()}
|
||||
<div className="w-full h-0.5 shrink-0 bg-black"></div>
|
||||
<div className="h-0.5 w-full shrink-0 bg-black"></div>
|
||||
{errorMessage ? (
|
||||
getErrorComponent()
|
||||
) : (
|
||||
@@ -200,10 +184,7 @@ function DataSourceConfigurationModalComponent({
|
||||
itemList={itemList}
|
||||
onItemClicked={item => {
|
||||
setShowFullConfig(false);
|
||||
setSelectedItems(theList => [
|
||||
...theList.slice(0, currentSelectedItemIndex + 1),
|
||||
item,
|
||||
]);
|
||||
setSelectedItems(theList => [...theList.slice(0, currentSelectedItemIndex + 1), item]);
|
||||
}}
|
||||
></ItemListComponent>
|
||||
)}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import classNames from 'classnames';
|
||||
import React, { ReactElement, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
InputFilterText,
|
||||
LoadingIndicatorProgress,
|
||||
} from '@ohif/ui';
|
||||
import { Button, Icon, InputFilterText, LoadingIndicatorProgress } from '@ohif/ui';
|
||||
import { Types } from '@ohif/core';
|
||||
|
||||
type ItemListComponentProps = {
|
||||
@@ -28,37 +23,35 @@ function ItemListComponent({
|
||||
}, [itemList]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 min-h-[1px] grow">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-primary-light text-[20px]">
|
||||
{t(`Select ${itemLabel}`)}
|
||||
</div>
|
||||
<div className="flex min-h-[1px] grow flex-col gap-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-primary-light text-[20px]">{t(`Select ${itemLabel}`)}</div>
|
||||
<InputFilterText
|
||||
className="grow max-w-[40%]"
|
||||
className="max-w-[40%] grow"
|
||||
value={filterValue}
|
||||
onDebounceChange={setFilterValue}
|
||||
placeholder={t(`Search ${itemLabel} list`)}
|
||||
></InputFilterText>
|
||||
</div>
|
||||
<div className="flex flex-col relative min-h-[1px] grow text-[14px] bg-black">
|
||||
<div className="relative flex min-h-[1px] grow flex-col bg-black text-[14px]">
|
||||
{itemList == null ? (
|
||||
<LoadingIndicatorProgress className={'w-full h-full'} />
|
||||
<LoadingIndicatorProgress className={'h-full w-full'} />
|
||||
) : itemList.length === 0 ? (
|
||||
<div className="flex flex-col h-full px-6 py-4 items-center justify-center text-primary-light">
|
||||
<Icon name="magnifier" className="mb-4" />
|
||||
<div className="text-primary-light flex h-full flex-col items-center justify-center px-6 py-4">
|
||||
<Icon
|
||||
name="magnifier"
|
||||
className="mb-4"
|
||||
/>
|
||||
<span>{t(`No ${itemLabel} available`)}</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="bg-secondary-dark text-white px-3 py-1.5">
|
||||
{t(itemLabel)}
|
||||
</div>
|
||||
<div className="overflow-auto ohif-scrollbar">
|
||||
<div className="bg-secondary-dark px-3 py-1.5 text-white">{t(itemLabel)}</div>
|
||||
<div className="ohif-scrollbar overflow-auto">
|
||||
{itemList
|
||||
.filter(
|
||||
item =>
|
||||
!filterValue ||
|
||||
item.name.toLowerCase().includes(filterValue.toLowerCase())
|
||||
!filterValue || item.name.toLowerCase().includes(filterValue.toLowerCase())
|
||||
)
|
||||
.map(item => {
|
||||
const border =
|
||||
@@ -66,7 +59,7 @@ function ItemListComponent({
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'group mx-2 px-6 py-2 flex justify-between items-center hover:text-primary-light hover:bg-primary-dark',
|
||||
'hover:text-primary-light hover:bg-primary-dark group mx-2 flex items-center justify-between px-6 py-2',
|
||||
border
|
||||
)}
|
||||
key={item.id}
|
||||
|
||||
@@ -19,10 +19,7 @@ export default class ContextMenuController {
|
||||
services: Types.Services;
|
||||
menuItems: Menu[] | MenuItem[];
|
||||
|
||||
constructor(
|
||||
servicesManager: ServicesManager,
|
||||
commandsManager: CommandsManager
|
||||
) {
|
||||
constructor(servicesManager: ServicesManager, commandsManager: CommandsManager) {
|
||||
this.services = servicesManager.services as Obj;
|
||||
this.commandsManager = commandsManager;
|
||||
}
|
||||
@@ -74,8 +71,7 @@ export default class ContextMenuController {
|
||||
|
||||
// This naming is part of hte uiDialogService convention
|
||||
// Clicking outside simpy closes the dialog box.
|
||||
onClickOutside: () =>
|
||||
this.services.uiDialogService.dismiss({ id: 'context-menu' }),
|
||||
onClickOutside: () => this.services.uiDialogService.dismiss({ id: 'context-menu' }),
|
||||
|
||||
contentProps: {
|
||||
items,
|
||||
@@ -170,9 +166,7 @@ export default class ContextMenuController {
|
||||
};
|
||||
|
||||
static _isValidPosition = (source): boolean => {
|
||||
return (
|
||||
source && typeof source.x === 'number' && typeof source.y === 'number'
|
||||
);
|
||||
return source && typeof source.x === 'number' && typeof source.y === 'number';
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -180,10 +174,7 @@ export default class ContextMenuController {
|
||||
*/
|
||||
static _getDefaultPosition = (canvasPoints, eventDetail, viewerElement) => {
|
||||
function* getPositionIterator() {
|
||||
yield ContextMenuController._getCanvasPointsPosition(
|
||||
canvasPoints,
|
||||
viewerElement
|
||||
);
|
||||
yield ContextMenuController._getCanvasPointsPosition(canvasPoints, viewerElement);
|
||||
yield ContextMenuController._getEventDefaultPosition(eventDetail);
|
||||
yield ContextMenuController._getElementDefaultPosition(viewerElement);
|
||||
yield ContextMenuController.getDefaultPosition();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ContextMenuItemsBuilder from "./ContextMenuItemsBuilder";
|
||||
import ContextMenuItemsBuilder from './ContextMenuItemsBuilder';
|
||||
|
||||
const menus = [
|
||||
{
|
||||
|
||||
@@ -31,16 +31,11 @@ export function findMenuById(menus: Menu[], menuId?: string): Menu {
|
||||
* @param {*} subProps
|
||||
* @returns
|
||||
*/
|
||||
export function findMenuDefault(
|
||||
menus: Menu[],
|
||||
subProps: Record<string, unknown>
|
||||
): Menu {
|
||||
export function findMenuDefault(menus: Menu[], subProps: Record<string, unknown>): Menu {
|
||||
if (!menus) {
|
||||
return null;
|
||||
}
|
||||
return menus.find(
|
||||
menu => !menu.selector || menu.selector(subProps.selectorProps)
|
||||
);
|
||||
return menus.find(menu => !menu.selector || menu.selector(subProps.selectorProps));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,11 +48,7 @@ export function findMenuDefault(
|
||||
* @param menuIdFilter - menu id identifier (to be considered on selection)
|
||||
* This is intended to support other types of filtering in the future.
|
||||
*/
|
||||
export function findMenu(
|
||||
menus: Menu[],
|
||||
props?: Types.IProps,
|
||||
menuIdFilter?: string
|
||||
) {
|
||||
export function findMenu(menus: Menu[], props?: Types.IProps, menuIdFilter?: string) {
|
||||
const { subMenu } = props;
|
||||
|
||||
function* findMenuIterator() {
|
||||
@@ -137,10 +128,7 @@ export function getMenuItems(
|
||||
|
||||
if (!selector || selector(selectorProps)) {
|
||||
if (delegating) {
|
||||
menuItems = [
|
||||
...menuItems,
|
||||
...getMenuItems(selectorProps, event, menus, subMenu),
|
||||
];
|
||||
menuItems = [...menuItems, ...getMenuItems(selectorProps, event, menus, subMenu)];
|
||||
} else {
|
||||
const toAdd = adaptItem(item, subProps);
|
||||
menuItems.push(toAdd);
|
||||
@@ -161,10 +149,7 @@ export function getMenuItems(
|
||||
* @returns a MenuItem that is compatible with the base ContextMenu
|
||||
* This requires having a label and set of actions to be called.
|
||||
*/
|
||||
export function adaptItem(
|
||||
item: MenuItem,
|
||||
subProps: ContextMenuProps
|
||||
): ContextMenuItem {
|
||||
export function adaptItem(item: MenuItem, subProps: ContextMenuProps): ContextMenuItem {
|
||||
const newItem: ContextMenuItem = {
|
||||
...item,
|
||||
value: subProps.selectorProps?.value,
|
||||
|
||||
+7
-14
@@ -29,15 +29,15 @@ const initialUrl = 'https://cloudresourcemanager.googleapis.com/v1';
|
||||
const baseHealthcareUrl = 'https://healthcare.googleapis.com/v1';
|
||||
|
||||
class GoogleCloudDataSourceConfigurationAPIItem
|
||||
implements Types.BaseDataSourceConfigurationAPIItem {
|
||||
implements Types.BaseDataSourceConfigurationAPIItem
|
||||
{
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
itemType: ItemType;
|
||||
}
|
||||
|
||||
class GoogleCloudDataSourceConfigurationAPI
|
||||
implements Types.BaseDataSourceConfigurationAPI {
|
||||
class GoogleCloudDataSourceConfigurationAPI implements Types.BaseDataSourceConfigurationAPI {
|
||||
private _extensionManager: ExtensionManager;
|
||||
private _fetchOptions: { method: string; headers: unknown };
|
||||
private _dataSourceName: string;
|
||||
@@ -45,8 +45,7 @@ class GoogleCloudDataSourceConfigurationAPI
|
||||
constructor(dataSourceName, servicesManager, extensionManager) {
|
||||
this._dataSourceName = dataSourceName;
|
||||
this._extensionManager = extensionManager;
|
||||
const userAuthenticationService =
|
||||
servicesManager.services.userAuthenticationService;
|
||||
const userAuthenticationService = servicesManager.services.userAuthenticationService;
|
||||
this._fetchOptions = {
|
||||
method: 'GET',
|
||||
headers: userAuthenticationService.getAuthorizationHeader(),
|
||||
@@ -89,9 +88,7 @@ class GoogleCloudDataSourceConfigurationAPI
|
||||
// Last configurable item, so update the data source configuration.
|
||||
const url = `${googleCloudItem.url}/dicomWeb`;
|
||||
const dataSourceDefCopy = JSON.parse(
|
||||
JSON.stringify(
|
||||
this._extensionManager.getDataSourceDefinition(this._dataSourceName)
|
||||
)
|
||||
JSON.stringify(this._extensionManager.getDataSourceDefinition(this._dataSourceName))
|
||||
);
|
||||
dataSourceDefCopy.configuration = {
|
||||
...dataSourceDefCopy.configuration,
|
||||
@@ -136,9 +133,7 @@ class GoogleCloudDataSourceConfigurationAPI
|
||||
return subItems;
|
||||
}
|
||||
|
||||
async getConfiguredItems(): Promise<
|
||||
Array<GoogleCloudDataSourceConfigurationAPIItem>
|
||||
> {
|
||||
async getConfiguredItems(): Promise<Array<GoogleCloudDataSourceConfigurationAPIItem>> {
|
||||
const dataSourceDefinition = this._extensionManager.getDataSourceDefinition(
|
||||
this._dataSourceName
|
||||
);
|
||||
@@ -216,9 +211,7 @@ class GoogleCloudDataSourceConfigurationAPI
|
||||
fetchOptions,
|
||||
fetchSearchParams
|
||||
);
|
||||
data[ItemType[fetchItemType]] = data[ItemType[fetchItemType]].concat(
|
||||
subPageData
|
||||
);
|
||||
data[ItemType[fetchItemType]] = data[ItemType[fetchItemType]].concat(subPageData);
|
||||
}
|
||||
if (data[ItemType[fetchItemType]]) {
|
||||
return data[ItemType[fetchItemType]];
|
||||
|
||||
@@ -152,15 +152,9 @@ function createDicomJSONApi(dicomJsonConfig) {
|
||||
return getDirectURL(wadoRoot, params);
|
||||
},
|
||||
series: {
|
||||
metadata: ({
|
||||
StudyInstanceUID,
|
||||
madeInClient = false,
|
||||
customSort,
|
||||
} = {}) => {
|
||||
metadata: ({ StudyInstanceUID, madeInClient = false, customSort } = {}) => {
|
||||
if (!StudyInstanceUID) {
|
||||
throw new Error(
|
||||
'Unable to query for SeriesMetadata without StudyInstanceUID'
|
||||
);
|
||||
throw new Error('Unable to query for SeriesMetadata without StudyInstanceUID');
|
||||
}
|
||||
|
||||
const study = findStudies('StudyInstanceUID', StudyInstanceUID)[0];
|
||||
@@ -186,16 +180,10 @@ function createDicomJSONApi(dicomJsonConfig) {
|
||||
DicomMetadataStore.addInstances(naturalizedInstances, madeInClient);
|
||||
}
|
||||
|
||||
DicomMetadataStore.addSeriesMetadata(
|
||||
seriesSummaryMetadata,
|
||||
madeInClient
|
||||
);
|
||||
DicomMetadataStore.addSeriesMetadata(seriesSummaryMetadata, madeInClient);
|
||||
|
||||
function setSuccessFlag() {
|
||||
const study = DicomMetadataStore.getStudy(
|
||||
StudyInstanceUID,
|
||||
madeInClient
|
||||
);
|
||||
const study = DicomMetadataStore.getStudy(StudyInstanceUID, madeInClient);
|
||||
study.isLoaded = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,16 +127,11 @@ function createDicomLocalApi(dicomLocalConfig) {
|
||||
series: {
|
||||
metadata: async ({ StudyInstanceUID, madeInClient = false } = {}) => {
|
||||
if (!StudyInstanceUID) {
|
||||
throw new Error(
|
||||
'Unable to query for SeriesMetadata without StudyInstanceUID'
|
||||
);
|
||||
throw new Error('Unable to query for SeriesMetadata without StudyInstanceUID');
|
||||
}
|
||||
|
||||
// Instances metadata already added via local upload
|
||||
const study = DicomMetadataStore.getStudy(
|
||||
StudyInstanceUID,
|
||||
madeInClient
|
||||
);
|
||||
const study = DicomMetadataStore.getStudy(StudyInstanceUID, madeInClient);
|
||||
|
||||
// Series metadata already added via local upload
|
||||
DicomMetadataStore._broadcastEvent(EVENTS.SERIES_ADDED, {
|
||||
@@ -236,8 +231,7 @@ function createDicomLocalApi(dicomLocalConfig) {
|
||||
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
|
||||
const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
|
||||
|
||||
const StudyInstanceUIDs =
|
||||
queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
|
||||
const StudyInstanceUIDs = queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
|
||||
const StudyInstanceUIDsAsArray =
|
||||
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
|
||||
? StudyInstanceUIDs
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
}
|
||||
|
||||
.dicom-tag-browser-table-wrapper {
|
||||
/* height: 500px;*/
|
||||
/* height: 500px;*/
|
||||
/*overflow-y: scroll;*/
|
||||
overflow-x: scroll;
|
||||
}
|
||||
@@ -45,7 +45,7 @@
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
text-align: center;
|
||||
color: "#20A5D6";
|
||||
color: '#20A5D6';
|
||||
}
|
||||
|
||||
.dicom-tag-browser-table th.dicom-tag-browser-table-left {
|
||||
|
||||
@@ -21,10 +21,8 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
||||
// 3: Value
|
||||
const excludedColumnIndicesForFilter: Set<number> = new Set([1]);
|
||||
|
||||
const [
|
||||
selectedDisplaySetInstanceUID,
|
||||
setSelectedDisplaySetInstanceUID,
|
||||
] = useState(displaySetInstanceUID);
|
||||
const [selectedDisplaySetInstanceUID, setSelectedDisplaySetInstanceUID] =
|
||||
useState(displaySetInstanceUID);
|
||||
const [instanceNumber, setInstanceNumber] = useState(1);
|
||||
const [filterValue, setFilterValue] = useState('');
|
||||
|
||||
@@ -110,27 +108,31 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
||||
|
||||
return (
|
||||
<div className="dicom-tag-browser-content">
|
||||
<div className="flex flex-row mb-6 items-center pl-1">
|
||||
<div className="flex flex-row items-center w-1/2">
|
||||
<Typography variant="subtitle" className="mr-4">
|
||||
<div className="mb-6 flex flex-row items-center pl-1">
|
||||
<div className="flex w-1/2 flex-row items-center">
|
||||
<Typography
|
||||
variant="subtitle"
|
||||
className="mr-4"
|
||||
>
|
||||
Series
|
||||
</Typography>
|
||||
<div className="grow mr-8">
|
||||
<div className="mr-8 grow">
|
||||
<Select
|
||||
id="display-set-selector"
|
||||
isClearable={false}
|
||||
onChange={onSelectChange}
|
||||
options={displaySetList}
|
||||
value={displaySetList.find(
|
||||
ds => ds.value === selectedDisplaySetInstanceUID
|
||||
)}
|
||||
value={displaySetList.find(ds => ds.value === selectedDisplaySetInstanceUID)}
|
||||
className="text-white"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row items-center w-1/2">
|
||||
<div className="flex w-1/2 flex-row items-center">
|
||||
{showInstanceList && (
|
||||
<Typography variant="subtitle" className="mr-4">
|
||||
<Typography
|
||||
variant="subtitle"
|
||||
className="mr-4"
|
||||
>
|
||||
Instance Number
|
||||
</Typography>
|
||||
)}
|
||||
@@ -153,10 +155,10 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full h-1 bg-black"></div>
|
||||
<div className="flex flex-row my-3 w-1/2">
|
||||
<div className="h-1 w-full bg-black"></div>
|
||||
<div className="my-3 flex w-1/2 flex-row">
|
||||
<InputFilterText
|
||||
className="block w-full mr-8"
|
||||
className="mr-8 block w-full"
|
||||
placeholder="Search metadata..."
|
||||
onDebounceChange={setFilterValue}
|
||||
></InputFilterText>
|
||||
@@ -171,24 +173,14 @@ function getFormattedRowsFromTags(tags, metadata) {
|
||||
|
||||
tags.forEach(tagInfo => {
|
||||
if (tagInfo.vr === 'SQ') {
|
||||
rows.push([
|
||||
`${tagInfo.tagIndent}${tagInfo.tag}`,
|
||||
tagInfo.vr,
|
||||
tagInfo.keyword,
|
||||
'',
|
||||
]);
|
||||
rows.push([`${tagInfo.tagIndent}${tagInfo.tag}`, tagInfo.vr, tagInfo.keyword, '']);
|
||||
|
||||
const { values } = tagInfo;
|
||||
|
||||
values.forEach((item, index) => {
|
||||
const formatedRowsFromTags = getFormattedRowsFromTags(item, metadata);
|
||||
|
||||
rows.push([
|
||||
`${item[0].tagIndent}(FFFE,E000)`,
|
||||
'',
|
||||
`Item #${index}`,
|
||||
'',
|
||||
]);
|
||||
rows.push([`${item[0].tagIndent}(FFFE,E000)`, '', `Item #${index}`, '']);
|
||||
|
||||
rows.push(...formatedRowsFromTags);
|
||||
});
|
||||
@@ -199,17 +191,10 @@ function getFormattedRowsFromTags(tags, metadata) {
|
||||
const originalTagInfo = metadata[tag];
|
||||
tagInfo.vr = originalTagInfo.vr;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to parse value representation for tag '${tagInfo.keyword}'`
|
||||
);
|
||||
console.error(`Failed to parse value representation for tag '${tagInfo.keyword}'`);
|
||||
}
|
||||
}
|
||||
rows.push([
|
||||
`${tagInfo.tagIndent}${tagInfo.tag}`,
|
||||
tagInfo.vr,
|
||||
tagInfo.keyword,
|
||||
tagInfo.value,
|
||||
]);
|
||||
rows.push([`${tagInfo.tagIndent}${tagInfo.tag}`, tagInfo.vr, tagInfo.keyword, tagInfo.value]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -17,48 +17,40 @@ function ColumnHeaders({ tagRef, vrRef, keywordRef, valueRef }) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'flex flex-row w-full bg-secondary-dark ohif-scrollbar overflow-y-scroll'
|
||||
'bg-secondary-dark ohif-scrollbar flex w-full flex-row overflow-y-scroll'
|
||||
)}
|
||||
style={rowVerticalPaddingStyle}
|
||||
>
|
||||
<div className="px-3 w-4/24">
|
||||
<div className="w-4/24 px-3">
|
||||
<label
|
||||
ref={tagRef}
|
||||
className="flex flex-col flex-1 text-white text-lg pl-1 select-none"
|
||||
className="flex flex-1 select-none flex-col pl-1 text-lg text-white"
|
||||
>
|
||||
<span className="flex flex-row items-center focus:outline-none">
|
||||
Tag
|
||||
</span>
|
||||
<span className="flex flex-row items-center focus:outline-none">Tag</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="px-3 w-2/24">
|
||||
<div className="w-2/24 px-3">
|
||||
<label
|
||||
ref={vrRef}
|
||||
className="flex flex-col flex-1 text-white text-lg pl-1 select-none"
|
||||
className="flex flex-1 select-none flex-col pl-1 text-lg text-white"
|
||||
>
|
||||
<span className="flex flex-row items-center focus:outline-none">
|
||||
VR
|
||||
</span>
|
||||
<span className="flex flex-row items-center focus:outline-none">VR</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="px-3 w-6/24">
|
||||
<div className="w-6/24 px-3">
|
||||
<label
|
||||
ref={keywordRef}
|
||||
className="flex flex-col flex-1 text-white text-lg pl-1 select-none"
|
||||
className="flex flex-1 select-none flex-col pl-1 text-lg text-white"
|
||||
>
|
||||
<span className="flex flex-row items-center focus:outline-none">
|
||||
Keyword
|
||||
</span>
|
||||
<span className="flex flex-row items-center focus:outline-none">Keyword</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="px-3 w-5/24 grow">
|
||||
<div className="w-5/24 grow px-3">
|
||||
<label
|
||||
ref={valueRef}
|
||||
className="flex flex-col flex-1 text-white text-lg pl-1 select-none"
|
||||
className="flex flex-1 select-none flex-col pl-1 text-lg text-white"
|
||||
>
|
||||
<span className="flex flex-row items-center focus:outline-none">
|
||||
Value
|
||||
</span>
|
||||
<span className="flex flex-row items-center focus:outline-none">Value</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,10 +106,7 @@ function DicomTagTable({ rows }) {
|
||||
* When the browser window resizes, update the row virtualization (i.e. row heights)
|
||||
*/
|
||||
useEffect(() => {
|
||||
const debouncedResize = debounce(
|
||||
() => listRef.current.resetAfterIndex(0),
|
||||
100
|
||||
);
|
||||
const debouncedResize = debounce(() => listRef.current.resetAfterIndex(0), 100);
|
||||
|
||||
window.addEventListener('resize', debouncedResize);
|
||||
|
||||
@@ -135,15 +124,15 @@ function DicomTagTable({ rows }) {
|
||||
<div
|
||||
style={{ ...style, ...rowStyle }}
|
||||
className={classNames(
|
||||
'hover:bg-secondary-main transition duration-300 bg-black flex flex-row w-full border-secondary-light items-center text-base break-all',
|
||||
'hover:bg-secondary-main border-secondary-light flex w-full flex-row items-center break-all bg-black text-base transition duration-300',
|
||||
lineHeightClassName
|
||||
)}
|
||||
key={`DICOMTagRow-${index}`}
|
||||
>
|
||||
<div className="px-3 w-4/24">{row[0]}</div>
|
||||
<div className="px-3 w-2/24">{row[1]}</div>
|
||||
<div className="px-3 w-6/24">{row[2]}</div>
|
||||
<div className="px-3 w-5/24 grow">{row[3]}</div>
|
||||
<div className="w-4/24 px-3">{row[0]}</div>
|
||||
<div className="w-2/24 px-3">{row[1]}</div>
|
||||
<div className="w-6/24 px-3">{row[2]}</div>
|
||||
<div className="w-5/24 grow px-3">{row[3]}</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -154,9 +143,7 @@ function DicomTagTable({ rows }) {
|
||||
* Whenever any one of the column headers is set, then the header is rendered.
|
||||
* Here we chose the tag header.
|
||||
*/
|
||||
const isHeaderRendered = useCallback(() => tagHeaderElem !== null, [
|
||||
tagHeaderElem,
|
||||
]);
|
||||
const isHeaderRendered = useCallback(() => tagHeaderElem !== null, [tagHeaderElem]);
|
||||
|
||||
/**
|
||||
* Get the item/row size. We use the header column widths to calculate the various row heights.
|
||||
@@ -179,11 +166,7 @@ function DicomTagTable({ rows }) {
|
||||
.map((colText, index) => {
|
||||
const colOneLineWidth = context.measureText(colText).width;
|
||||
const numLines = Math.ceil(colOneLineWidth / headerWidths[index]);
|
||||
return (
|
||||
numLines * lineHeightPx +
|
||||
2 * rowVerticalPaddingPx +
|
||||
rowBottomBorderPx
|
||||
);
|
||||
return numLines * lineHeightPx + 2 * rowVerticalPaddingPx + rowBottomBorderPx;
|
||||
})
|
||||
.reduce((maxHeight, colHeight) => Math.max(maxHeight, colHeight));
|
||||
},
|
||||
@@ -204,7 +187,7 @@ function DicomTagTable({ rows }) {
|
||||
valueRef={valueRef}
|
||||
/>
|
||||
<div
|
||||
className="m-auto relative border-2 border-black bg-black"
|
||||
className="relative m-auto border-2 border-black bg-black"
|
||||
style={{ height: '32rem' }}
|
||||
>
|
||||
{isHeaderRendered() && (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default function(wadoRoot) {
|
||||
export default function (wadoRoot) {
|
||||
return {
|
||||
series: (StudyInstanceUID, SeriesInstanceUID) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -15,7 +15,7 @@ export default function(wadoRoot) {
|
||||
|
||||
console.log(xhr);
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
xhr.onreadystatechange = function () {
|
||||
//Call a function when the state changes.
|
||||
if (xhr.readyState == 4) {
|
||||
switch (xhr.status) {
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import { api } from 'dicomweb-client';
|
||||
import {
|
||||
DicomMetadataStore,
|
||||
IWebApiDataSource,
|
||||
utils,
|
||||
errorHandler,
|
||||
classes,
|
||||
} from '@ohif/core';
|
||||
import { DicomMetadataStore, IWebApiDataSource, utils, errorHandler, classes } from '@ohif/core';
|
||||
|
||||
import {
|
||||
mapParams,
|
||||
@@ -18,10 +12,7 @@ import dcm4cheeReject from './dcm4cheeReject';
|
||||
|
||||
import getImageId from './utils/getImageId';
|
||||
import dcmjs from 'dcmjs';
|
||||
import {
|
||||
retrieveStudyMetadata,
|
||||
deleteStudyMetadataPromise,
|
||||
} from './retrieveStudyMetadata.js';
|
||||
import { retrieveStudyMetadata, deleteStudyMetadataPromise } from './retrieveStudyMetadata.js';
|
||||
import StaticWadoClient from './utils/StaticWadoClient';
|
||||
import getDirectURL from '../utils/getDirectURL';
|
||||
import { fixBulkDataURI } from './utils/fixBulkDataURI';
|
||||
@@ -30,8 +21,7 @@ const { DicomMetaDictionary, DicomDict } = dcmjs.data;
|
||||
|
||||
const { naturalizeDataset, denaturalizeDataset } = DicomMetaDictionary;
|
||||
|
||||
const ImplementationClassUID =
|
||||
'2.25.270695996825855179949881587723571202391.2.0.0';
|
||||
const ImplementationClassUID = '2.25.270695996825855179949881587723571202391.2.0.0';
|
||||
const ImplementationVersionName = 'OHIF-VIEWER-2.0.0';
|
||||
const EXPLICIT_VR_LITTLE_ENDIAN = '1.2.840.10008.1.2.1';
|
||||
|
||||
@@ -61,10 +51,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
|
||||
const implementation = {
|
||||
initialize: ({ params, query }) => {
|
||||
if (
|
||||
dicomWebConfig.onConfiguration &&
|
||||
typeof dicomWebConfig.onConfiguration === 'function'
|
||||
) {
|
||||
if (dicomWebConfig.onConfiguration && typeof dicomWebConfig.onConfiguration === 'function') {
|
||||
dicomWebConfig = dicomWebConfig.onConfiguration(dicomWebConfig, {
|
||||
params,
|
||||
query,
|
||||
@@ -126,7 +113,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
query: {
|
||||
studies: {
|
||||
mapParams: mapParams.bind(),
|
||||
search: async function(origParams) {
|
||||
search: async function (origParams) {
|
||||
qidoDicomWebClient.headers = getAuthrorizationHeader();
|
||||
const { studyInstanceUid, seriesInstanceUid, ...mappedParams } =
|
||||
mapParams(origParams, {
|
||||
@@ -134,12 +121,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
supportsWildcard: dicomWebConfig.supportsWildcard,
|
||||
}) || {};
|
||||
|
||||
const results = await qidoSearch(
|
||||
qidoDicomWebClient,
|
||||
undefined,
|
||||
undefined,
|
||||
mappedParams
|
||||
);
|
||||
const results = await qidoSearch(qidoDicomWebClient, undefined, undefined, mappedParams);
|
||||
|
||||
return processResults(results);
|
||||
},
|
||||
@@ -147,12 +129,9 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
},
|
||||
series: {
|
||||
// mapParams: mapParams.bind(),
|
||||
search: async function(studyInstanceUid) {
|
||||
search: async function (studyInstanceUid) {
|
||||
qidoDicomWebClient.headers = getAuthrorizationHeader();
|
||||
const results = await seriesInStudy(
|
||||
qidoDicomWebClient,
|
||||
studyInstanceUid
|
||||
);
|
||||
const results = await seriesInStudy(qidoDicomWebClient, studyInstanceUid);
|
||||
|
||||
return processSeriesResults(results);
|
||||
},
|
||||
@@ -161,13 +140,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
instances: {
|
||||
search: (studyInstanceUid, queryParameters) => {
|
||||
qidoDicomWebClient.headers = getAuthrorizationHeader();
|
||||
qidoSearch.call(
|
||||
undefined,
|
||||
qidoDicomWebClient,
|
||||
studyInstanceUid,
|
||||
null,
|
||||
queryParameters
|
||||
);
|
||||
qidoSearch.call(undefined, qidoDicomWebClient, studyInstanceUid, null, queryParameters);
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -213,9 +186,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
madeInClient = false,
|
||||
} = {}) => {
|
||||
if (!StudyInstanceUID) {
|
||||
throw new Error(
|
||||
'Unable to query for SeriesMetadata without StudyInstanceUID'
|
||||
);
|
||||
throw new Error('Unable to query for SeriesMetadata without StudyInstanceUID');
|
||||
}
|
||||
|
||||
if (dicomWebConfig.enableStudyLazyLoad) {
|
||||
@@ -250,8 +221,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
await wadoDicomWebClient.storeInstances(options);
|
||||
} else {
|
||||
const meta = {
|
||||
FileMetaInformationVersion:
|
||||
dataset._meta.FileMetaInformationVersion.Value,
|
||||
FileMetaInformationVersion: dataset._meta.FileMetaInformationVersion.Value,
|
||||
MediaStorageSOPClassUID: dataset.SOPClassUID,
|
||||
MediaStorageSOPInstanceUID: dataset.SOPInstanceUID,
|
||||
TransferSyntaxUID: EXPLICIT_VR_LITTLE_ENDIAN,
|
||||
@@ -340,10 +310,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
DicomMetadataStore.addSeriesMetadata(seriesMetadata, madeInClient);
|
||||
|
||||
Object.keys(instancesPerSeries).forEach(seriesInstanceUID =>
|
||||
DicomMetadataStore.addInstances(
|
||||
instancesPerSeries[seriesInstanceUID],
|
||||
madeInClient
|
||||
)
|
||||
DicomMetadataStore.addInstances(instancesPerSeries[seriesInstanceUID], madeInClient)
|
||||
);
|
||||
},
|
||||
|
||||
@@ -357,17 +324,15 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
const enableStudyLazyLoad = true;
|
||||
wadoDicomWebClient.headers = generateWadoHeader();
|
||||
// Get Series
|
||||
const {
|
||||
preLoadData: seriesSummaryMetadata,
|
||||
promises: seriesPromises,
|
||||
} = await retrieveStudyMetadata(
|
||||
wadoDicomWebClient,
|
||||
StudyInstanceUID,
|
||||
enableStudyLazyLoad,
|
||||
filters,
|
||||
sortCriteria,
|
||||
sortFunction
|
||||
);
|
||||
const { preLoadData: seriesSummaryMetadata, promises: seriesPromises } =
|
||||
await retrieveStudyMetadata(
|
||||
wadoDicomWebClient,
|
||||
StudyInstanceUID,
|
||||
enableStudyLazyLoad,
|
||||
filters,
|
||||
sortCriteria,
|
||||
sortFunction
|
||||
);
|
||||
|
||||
/**
|
||||
* naturalizes the dataset, and adds a retrieve bulkdata method
|
||||
@@ -412,8 +377,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
// the bulk data and DICOM video cases where the second ArrayBuffer is
|
||||
// the bulk data. Here we play it safe and do a find.
|
||||
const ret =
|
||||
(val instanceof Array &&
|
||||
val.find(arrayBuffer => arrayBuffer?.byteLength)) ||
|
||||
(val instanceof Array && val.find(arrayBuffer => arrayBuffer?.byteLength)) ||
|
||||
undefined;
|
||||
value.Value = ret;
|
||||
return ret;
|
||||
@@ -456,10 +420,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
}
|
||||
|
||||
function setSuccessFlag() {
|
||||
const study = DicomMetadataStore.getStudy(
|
||||
StudyInstanceUID,
|
||||
madeInClient
|
||||
);
|
||||
const study = DicomMetadataStore.getStudy(StudyInstanceUID, madeInClient);
|
||||
study.isLoaded = true;
|
||||
}
|
||||
|
||||
@@ -520,13 +481,10 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
},
|
||||
getStudyInstanceUIDs({ params, query }) {
|
||||
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
|
||||
const queryStudyInstanceUIDs = utils.splitComma(
|
||||
query.getAll('StudyInstanceUIDs')
|
||||
);
|
||||
const queryStudyInstanceUIDs = utils.splitComma(query.getAll('StudyInstanceUIDs'));
|
||||
|
||||
const StudyInstanceUIDs =
|
||||
(queryStudyInstanceUIDs.length && queryStudyInstanceUIDs) ||
|
||||
paramsStudyInstanceUIDs;
|
||||
(queryStudyInstanceUIDs.length && queryStudyInstanceUIDs) || paramsStudyInstanceUIDs;
|
||||
const StudyInstanceUIDsAsArray =
|
||||
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
|
||||
? StudyInstanceUIDs
|
||||
|
||||
@@ -54,10 +54,7 @@ function processResults(qidoStudies) {
|
||||
patientName: utils.formatPN(getName(qidoStudy['00100010'])) || '',
|
||||
instances: Number(getString(qidoStudy['00201208'])) || 0, // number
|
||||
description: getString(qidoStudy['00081030']) || '',
|
||||
modalities:
|
||||
getString(
|
||||
getModalities(qidoStudy['00080060'], qidoStudy['00080061'])
|
||||
) || '',
|
||||
modalities: getString(getModalities(qidoStudy['00080060'], qidoStudy['00080061'])) || '',
|
||||
})
|
||||
);
|
||||
|
||||
@@ -105,12 +102,7 @@ export function processSeriesResults(qidoSeries) {
|
||||
* @param {string} [queryParamaters]
|
||||
* @returns {Promise<results>} - Promise that resolves results
|
||||
*/
|
||||
async function search(
|
||||
dicomWebClient,
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid,
|
||||
queryParameters
|
||||
) {
|
||||
async function search(dicomWebClient, studyInstanceUid, seriesInstanceUid, queryParameters) {
|
||||
let searchResult = await dicomWebClient.searchForStudies({
|
||||
studyInstanceUid: undefined,
|
||||
queryParams: queryParameters,
|
||||
@@ -136,10 +128,7 @@ export function seriesInStudy(dicomWebClient, studyInstanceUID) {
|
||||
}
|
||||
|
||||
export default function searchStudies(server, filter) {
|
||||
const queryParams = getQIDOQueryParams(
|
||||
filter,
|
||||
server.qidoSupportsIncludeField
|
||||
);
|
||||
const queryParams = getQIDOQueryParams(filter, server.qidoSupportsIncludeField);
|
||||
const options = {
|
||||
queryParams,
|
||||
};
|
||||
|
||||
@@ -28,14 +28,10 @@ export function retrieveStudyMetadata(
|
||||
// corresponding promise from the "StudyMetaDataPromises" map...
|
||||
|
||||
if (!dicomWebClient) {
|
||||
throw new Error(
|
||||
`${moduleName}: Required 'dicomWebClient' parameter not provided.`
|
||||
);
|
||||
throw new Error(`${moduleName}: Required 'dicomWebClient' parameter not provided.`);
|
||||
}
|
||||
if (!StudyInstanceUID) {
|
||||
throw new Error(
|
||||
`${moduleName}: Required 'StudyInstanceUID' parameter not provided.`
|
||||
);
|
||||
throw new Error(`${moduleName}: Required 'StudyInstanceUID' parameter not provided.`);
|
||||
}
|
||||
|
||||
// Already waiting on result? Return cached promise
|
||||
@@ -52,7 +48,7 @@ export function retrieveStudyMetadata(
|
||||
filters,
|
||||
sortCriteria,
|
||||
sortFunction
|
||||
).then(function(data) {
|
||||
).then(function (data) {
|
||||
resolve(data);
|
||||
}, reject);
|
||||
});
|
||||
|
||||
@@ -50,14 +50,7 @@ export default class StaticWadoClient extends api.DICOMwebClient {
|
||||
const lowerParams = this.toLowerParams(queryParams);
|
||||
const filtered = searchResult.filter(study => {
|
||||
for (const key of Object.keys(StaticWadoClient.studyFilterKeys)) {
|
||||
if (
|
||||
!this.filterItem(
|
||||
key,
|
||||
lowerParams,
|
||||
study,
|
||||
StaticWadoClient.studyFilterKeys
|
||||
)
|
||||
) {
|
||||
if (!this.filterItem(key, lowerParams, study, StaticWadoClient.studyFilterKeys)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -80,14 +73,7 @@ export default class StaticWadoClient extends api.DICOMwebClient {
|
||||
|
||||
const filtered = searchResult.filter(series => {
|
||||
for (const key of Object.keys(StaticWadoClient.seriesFilterKeys)) {
|
||||
if (
|
||||
!this.filterItem(
|
||||
key,
|
||||
lowerParams,
|
||||
series,
|
||||
StaticWadoClient.seriesFilterKeys
|
||||
)
|
||||
) {
|
||||
if (!this.filterItem(key, lowerParams, series, StaticWadoClient.seriesFilterKeys)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -132,10 +118,7 @@ export default class StaticWadoClient extends api.DICOMwebClient {
|
||||
} else if (desired[desired.length - 1] === '*') {
|
||||
return actual.indexOf(desired.substring(0, desired.length - 1)) != -1;
|
||||
} else if (desired[0] === '*') {
|
||||
return (
|
||||
actual.indexOf(desired.substring(1)) ===
|
||||
actual.length - desired.length + 1
|
||||
);
|
||||
return actual.indexOf(desired.substring(1)) === actual.length - desired.length + 1;
|
||||
}
|
||||
}
|
||||
return desired === actual;
|
||||
|
||||
@@ -20,10 +20,7 @@ function fixBulkDataURI(value, instance, dicomWebConfig) {
|
||||
// in case of the relative path, make it absolute. The current DICOM standard says
|
||||
// the bulkdataURI is relative to the series. However, there are situations where
|
||||
// it can be relative to the study too
|
||||
if (
|
||||
!value.BulkDataURI.startsWith('http') &&
|
||||
!value.BulkDataURI.startsWith('/')
|
||||
) {
|
||||
if (!value.BulkDataURI.startsWith('http') && !value.BulkDataURI.startsWith('/')) {
|
||||
if (dicomWebConfig.bulkDataURI?.relativeResolution === 'studies') {
|
||||
value.BulkDataURI = `${dicomWebConfig.wadoRoot}/studies/${instance.StudyInstanceUID}/${value.BulkDataURI}`;
|
||||
} else if (
|
||||
|
||||
@@ -24,12 +24,7 @@ function buildInstanceWadoUrl(config, instance) {
|
||||
* @param thumbnail
|
||||
* @returns {string} The imageId to be used by Cornerstone
|
||||
*/
|
||||
export default function getImageId({
|
||||
instance,
|
||||
frame,
|
||||
config,
|
||||
thumbnail = false,
|
||||
}) {
|
||||
export default function getImageId({ instance, frame, config, thumbnail = false }) {
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -20,9 +20,7 @@ async function RetrieveMetadata(
|
||||
sortFunction
|
||||
) {
|
||||
const RetrieveMetadataLoader =
|
||||
enableStudyLazyLoad !== false
|
||||
? RetrieveMetadataLoaderAsync
|
||||
: RetrieveMetadataLoaderSync;
|
||||
enableStudyLazyLoad !== false ? RetrieveMetadataLoaderAsync : RetrieveMetadataLoaderSync;
|
||||
|
||||
const retrieveMetadataLoader = new RetrieveMetadataLoader(
|
||||
dicomWebClient,
|
||||
|
||||
@@ -14,13 +14,7 @@ export default class RetrieveMetadataLoader {
|
||||
* @param {string} [filter.seriesInstanceUID] - series instance uid to filter results against
|
||||
* @param {Function} [sortSeries] - Custom sort function for series
|
||||
*/
|
||||
constructor(
|
||||
client,
|
||||
studyInstanceUID,
|
||||
filters = {},
|
||||
sortCriteria,
|
||||
sortFunction
|
||||
) {
|
||||
constructor(client, studyInstanceUID, filters = {}, sortCriteria, sortFunction) {
|
||||
this.client = client;
|
||||
this.studyInstanceUID = studyInstanceUID;
|
||||
this.filters = filters;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import dcmjs from 'dcmjs';
|
||||
import {
|
||||
sortStudySeries,
|
||||
sortingCriteria,
|
||||
} from '@ohif/core/src/utils/sortStudy';
|
||||
import { sortStudySeries, sortingCriteria } from '@ohif/core/src/utils/sortStudy';
|
||||
import RetrieveMetadataLoader from './retrieveMetadataLoader';
|
||||
|
||||
/**
|
||||
@@ -12,11 +9,7 @@ import RetrieveMetadataLoader from './retrieveMetadataLoader';
|
||||
* @param {Array} seriesInstanceUIDList A list of Series Instance UIDs
|
||||
* @returns {Object} Returns an object which supports loading of instances from each of given Series Instance UID
|
||||
*/
|
||||
function makeSeriesAsyncLoader(
|
||||
client,
|
||||
studyInstanceUID,
|
||||
seriesInstanceUIDList
|
||||
) {
|
||||
function makeSeriesAsyncLoader(client, studyInstanceUID, seriesInstanceUIDList) {
|
||||
return Object.freeze({
|
||||
hasNext() {
|
||||
return seriesInstanceUIDList.length > 0;
|
||||
@@ -43,11 +36,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
|
||||
*/
|
||||
*getPreLoaders() {
|
||||
const preLoaders = [];
|
||||
const {
|
||||
studyInstanceUID,
|
||||
filters: { seriesInstanceUID } = {},
|
||||
client,
|
||||
} = this;
|
||||
const { studyInstanceUID, filters: { seriesInstanceUID } = {}, client } = this;
|
||||
|
||||
if (seriesInstanceUID) {
|
||||
const options = {
|
||||
@@ -73,8 +62,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
|
||||
|
||||
return sortStudySeries(
|
||||
naturalized,
|
||||
sortCriteria ||
|
||||
sortingCriteria.seriesSortCriteria.seriesInfoSortingCriteria,
|
||||
sortCriteria || sortingCriteria.seriesSortCriteria.seriesInfoSortingCriteria,
|
||||
sortFunction
|
||||
);
|
||||
}
|
||||
@@ -84,11 +72,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
|
||||
|
||||
const seriesInstanceUIDs = preLoadData.map(s => s.SeriesInstanceUID);
|
||||
|
||||
const seriesAsyncLoader = makeSeriesAsyncLoader(
|
||||
client,
|
||||
studyInstanceUID,
|
||||
seriesInstanceUIDs
|
||||
);
|
||||
const seriesAsyncLoader = makeSeriesAsyncLoader(client, studyInstanceUID, seriesInstanceUIDs);
|
||||
|
||||
const promises = [];
|
||||
|
||||
|
||||
@@ -31,11 +31,7 @@ export default class RetrieveMetadataLoaderSync extends RetrieveMetadataLoader {
|
||||
*/
|
||||
*getLoaders() {
|
||||
const loaders = [];
|
||||
const {
|
||||
studyInstanceUID,
|
||||
filters: { seriesInstanceUID } = {},
|
||||
client,
|
||||
} = this;
|
||||
const { studyInstanceUID, filters: { seriesInstanceUID } = {}, client } = this;
|
||||
|
||||
if (seriesInstanceUID) {
|
||||
loaders.push(
|
||||
@@ -46,9 +42,7 @@ export default class RetrieveMetadataLoaderSync extends RetrieveMetadataLoader {
|
||||
);
|
||||
}
|
||||
|
||||
loaders.push(
|
||||
client.retrieveStudyMetadata.bind(client, { studyInstanceUID })
|
||||
);
|
||||
loaders.push(client.retrieveStudyMetadata.bind(client, { studyInstanceUID }));
|
||||
|
||||
yield* loaders;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,7 @@ import { createDicomWebApi } from '../DicomWebDataSource/index';
|
||||
* dicomWeb configuration array
|
||||
*
|
||||
*/
|
||||
function createDicomWebProxyApi(
|
||||
dicomWebProxyConfig,
|
||||
UserAuthenticationService
|
||||
) {
|
||||
function createDicomWebProxyApi(dicomWebProxyConfig, UserAuthenticationService) {
|
||||
const { name } = dicomWebProxyConfig;
|
||||
let dicomWebDelegate = undefined;
|
||||
|
||||
@@ -45,28 +42,21 @@ function createDicomWebProxyApi(
|
||||
},
|
||||
instances: {
|
||||
search: (studyInstanceUid, queryParameters) =>
|
||||
dicomWebDelegate.query.instances.search(
|
||||
studyInstanceUid,
|
||||
queryParameters
|
||||
),
|
||||
dicomWebDelegate.query.instances.search(studyInstanceUid, queryParameters),
|
||||
},
|
||||
},
|
||||
retrieve: {
|
||||
directURL: (...args) => dicomWebDelegate.retrieve.directURL(...args),
|
||||
series: {
|
||||
metadata: (...args) =>
|
||||
dicomWebDelegate.retrieve.series.metadata(...args),
|
||||
metadata: (...args) => dicomWebDelegate.retrieve.series.metadata(...args),
|
||||
},
|
||||
},
|
||||
store: {
|
||||
dicom: (...args) => dicomWebDelegate.store(...args),
|
||||
},
|
||||
deleteStudyMetadataPromise: (...args) =>
|
||||
dicomWebDelegate.deleteStudyMetadataPromise(...args),
|
||||
getImageIdsForDisplaySet: (...args) =>
|
||||
dicomWebDelegate.getImageIdsForDisplaySet(...args),
|
||||
getImageIdsForInstance: (...args) =>
|
||||
dicomWebDelegate.getImageIdsForInstance(...args),
|
||||
deleteStudyMetadataPromise: (...args) => dicomWebDelegate.deleteStudyMetadataPromise(...args),
|
||||
getImageIdsForDisplaySet: (...args) => dicomWebDelegate.getImageIdsForDisplaySet(...args),
|
||||
getImageIdsForInstance: (...args) => dicomWebDelegate.getImageIdsForInstance(...args),
|
||||
getStudyInstanceUIDs({ params, query }) {
|
||||
let studyInstanceUIDs = [];
|
||||
|
||||
|
||||
@@ -9,9 +9,15 @@ function ActionButtons({ onExportClick, onCreateReportClick }) {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ButtonGroup color="black" size="inherit">
|
||||
<ButtonGroup
|
||||
color="black"
|
||||
size="inherit"
|
||||
>
|
||||
{/* TODO Revisit design of ButtonGroup later - for now use LegacyButton for its children.*/}
|
||||
<LegacyButton className="px-2 py-2 text-base" onClick={onExportClick}>
|
||||
<LegacyButton
|
||||
className="px-2 py-2 text-base"
|
||||
onClick={onExportClick}
|
||||
>
|
||||
{t('Export CSV')}
|
||||
</LegacyButton>
|
||||
<LegacyButton
|
||||
|
||||
@@ -15,20 +15,16 @@ function DataSourceSelector() {
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<div className="h-screen w-screen flex justify-center items-center ">
|
||||
<div className="py-8 px-8 mx-auto bg-secondary-dark drop-shadow-md space-y-2 rounded-lg">
|
||||
<div className="flex h-screen w-screen items-center justify-center ">
|
||||
<div className="bg-secondary-dark mx-auto space-y-2 rounded-lg py-8 px-8 drop-shadow-md">
|
||||
<img
|
||||
className="block mx-auto h-14"
|
||||
className="mx-auto block h-14"
|
||||
src="./ohif-logo.svg"
|
||||
alt="OHIF"
|
||||
/>
|
||||
<div className="text-center space-y-2 pt-4">
|
||||
<div className="space-y-2 pt-4 text-center">
|
||||
{dsConfigs
|
||||
.filter(
|
||||
it =>
|
||||
it.sourceName !== 'dicomjson' &&
|
||||
it.sourceName !== 'dicomlocal'
|
||||
)
|
||||
.filter(it => it.sourceName !== 'dicomjson' && it.sourceName !== 'dicomlocal')
|
||||
.map(ds => (
|
||||
<div key={ds.sourceName}>
|
||||
<h1 className="text-white">
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { utils, ServicesManager } from '@ohif/core';
|
||||
import {
|
||||
MeasurementTable,
|
||||
Dialog,
|
||||
Input,
|
||||
useViewportGrid,
|
||||
ButtonEnums,
|
||||
} from '@ohif/ui';
|
||||
import { MeasurementTable, Dialog, Input, useViewportGrid, ButtonEnums } from '@ohif/ui';
|
||||
import ActionButtons from './ActionButtons';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
@@ -26,19 +20,13 @@ export default function PanelMeasurementTable({
|
||||
}): React.FunctionComponent {
|
||||
const [viewportGrid, viewportGridService] = useViewportGrid();
|
||||
const { activeViewportId, viewports } = viewportGrid;
|
||||
const {
|
||||
measurementService,
|
||||
uiDialogService,
|
||||
uiNotificationService,
|
||||
displaySetService,
|
||||
} = (servicesManager as ServicesManager).services;
|
||||
const { measurementService, uiDialogService, uiNotificationService, displaySetService } = (
|
||||
servicesManager as ServicesManager
|
||||
).services;
|
||||
const [displayMeasurements, setDisplayMeasurements] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const debouncedSetDisplayMeasurements = debounce(
|
||||
setDisplayMeasurements,
|
||||
100
|
||||
);
|
||||
const debouncedSetDisplayMeasurements = debounce(setDisplayMeasurements, 100);
|
||||
// ~~ Initial
|
||||
setDisplayMeasurements(_getMappedMeasurements(measurementService));
|
||||
|
||||
@@ -53,9 +41,7 @@ export default function PanelMeasurementTable({
|
||||
[added, addedRaw, updated, removed, cleared].forEach(evt => {
|
||||
subscriptions.push(
|
||||
measurementService.subscribe(evt, () => {
|
||||
debouncedSetDisplayMeasurements(
|
||||
_getMappedMeasurements(measurementService)
|
||||
);
|
||||
debouncedSetDisplayMeasurements(_getMappedMeasurements(measurementService));
|
||||
}).unsubscribe
|
||||
);
|
||||
});
|
||||
@@ -104,9 +90,7 @@ export default function PanelMeasurementTable({
|
||||
});
|
||||
|
||||
if (promptResult.action === CREATE_REPORT_DIALOG_RESPONSE.CREATE_REPORT) {
|
||||
const dataSources = extensionManager.getDataSources(
|
||||
promptResult.dataSourceName
|
||||
);
|
||||
const dataSources = extensionManager.getDataSources(promptResult.dataSourceName);
|
||||
const dataSource = dataSources[0];
|
||||
|
||||
const SeriesDescription =
|
||||
@@ -117,10 +101,7 @@ export default function PanelMeasurementTable({
|
||||
|
||||
// Re-use an existing series having the same series description to avoid
|
||||
// creating too many series instances.
|
||||
const options = findSRWithSameSeriesDescription(
|
||||
SeriesDescription,
|
||||
displaySetService
|
||||
);
|
||||
const options = findSRWithSameSeriesDescription(SeriesDescription, displaySetService);
|
||||
|
||||
return createReportAsync(
|
||||
servicesManager,
|
||||
@@ -186,7 +167,7 @@ export default function PanelMeasurementTable({
|
||||
labelClassName="text-white text-[14px] leading-[1.2]"
|
||||
autoFocus
|
||||
id="annotation"
|
||||
className="bg-black border-primary-main"
|
||||
className="border-primary-main bg-black"
|
||||
type="text"
|
||||
value={value.label}
|
||||
onChange={onChangeHandler}
|
||||
@@ -217,7 +198,7 @@ export default function PanelMeasurementTable({
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="overflow-x-hidden overflow-y-auto ohif-scrollbar"
|
||||
className="ohif-scrollbar overflow-y-auto overflow-x-hidden"
|
||||
data-cy={'measurements-panel'}
|
||||
>
|
||||
<MeasurementTable
|
||||
|
||||
@@ -17,21 +17,15 @@ function PanelStudyBrowser({
|
||||
requestDisplaySetCreationForStudy,
|
||||
dataSource,
|
||||
}) {
|
||||
const {
|
||||
hangingProtocolService,
|
||||
displaySetService,
|
||||
uiNotificationService,
|
||||
} = servicesManager.services;
|
||||
const { hangingProtocolService, displaySetService, uiNotificationService } =
|
||||
servicesManager.services;
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Normally you nest the components so the tree isn't so deep, and the data
|
||||
// doesn't have to have such an intense shape. This works well enough for now.
|
||||
// Tabs --> Studies --> DisplaySets --> Thumbnails
|
||||
const { StudyInstanceUIDs } = useImageViewer();
|
||||
const [
|
||||
{ activeViewportId, viewports },
|
||||
viewportGridService,
|
||||
] = useViewportGrid();
|
||||
const [{ activeViewportId, viewports }, viewportGridService] = useViewportGrid();
|
||||
const [activeTabName, setActiveTabName] = useState('primary');
|
||||
const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = useState([
|
||||
...StudyInstanceUIDs,
|
||||
@@ -53,8 +47,7 @@ function PanelStudyBrowser({
|
||||
console.warn(error);
|
||||
uiNotificationService.show({
|
||||
title: 'Thumbnail Double Click',
|
||||
message:
|
||||
'The selected display sets could not be added to the viewport.',
|
||||
message: 'The selected display sets could not be added to the viewport.',
|
||||
type: 'info',
|
||||
duration: 3000,
|
||||
});
|
||||
@@ -82,9 +75,7 @@ function PanelStudyBrowser({
|
||||
// try to fetch the prior studies based on the patientID if the
|
||||
// server can respond.
|
||||
try {
|
||||
qidoStudiesForPatient = await getStudiesForPatientByMRN(
|
||||
qidoForStudyUID
|
||||
);
|
||||
qidoStudiesForPatient = await getStudiesForPatientByMRN(qidoForStudyUID);
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
@@ -103,11 +94,7 @@ function PanelStudyBrowser({
|
||||
setStudyDisplayList(prevArray => {
|
||||
const ret = [...prevArray];
|
||||
for (const study of actuallyMappedStudies) {
|
||||
if (
|
||||
!prevArray.find(
|
||||
it => it.studyInstanceUid === study.studyInstanceUid
|
||||
)
|
||||
) {
|
||||
if (!prevArray.find(it => it.studyInstanceUid === study.studyInstanceUid)) {
|
||||
ret.push(study);
|
||||
}
|
||||
}
|
||||
@@ -124,18 +111,14 @@ function PanelStudyBrowser({
|
||||
const currentDisplaySets = displaySetService.activeDisplaySets;
|
||||
currentDisplaySets.forEach(async dSet => {
|
||||
const newImageSrcEntry = {};
|
||||
const displaySet = displaySetService.getDisplaySetByUID(
|
||||
dSet.displaySetInstanceUID
|
||||
);
|
||||
const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID);
|
||||
const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
|
||||
const imageId = imageIds[Math.floor(imageIds.length / 2)];
|
||||
|
||||
// TODO: Is it okay that imageIds are not returned here for SR displaySets?
|
||||
if (imageId && !displaySet?.unsupported) {
|
||||
// When the image arrives, render it and store the result in the thumbnailImgSrcMap
|
||||
newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(
|
||||
imageId
|
||||
);
|
||||
newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId);
|
||||
if (isMounted.current) {
|
||||
setThumbnailImageSrcMap(prevState => {
|
||||
return { ...prevState, ...newImageSrcEntry };
|
||||
@@ -153,10 +136,7 @@ function PanelStudyBrowser({
|
||||
useEffect(() => {
|
||||
// TODO: Are we sure `activeDisplaySets` will always be accurate?
|
||||
const currentDisplaySets = displaySetService.activeDisplaySets;
|
||||
const mappedDisplaySets = _mapDisplaySets(
|
||||
currentDisplaySets,
|
||||
thumbnailImageSrcMap
|
||||
);
|
||||
const mappedDisplaySets = _mapDisplaySets(currentDisplaySets, thumbnailImageSrcMap);
|
||||
sortStudyInstances(mappedDisplaySets);
|
||||
|
||||
setDisplaySets(mappedDisplaySets);
|
||||
@@ -172,9 +152,7 @@ function PanelStudyBrowser({
|
||||
const { displaySetsAdded } = data;
|
||||
displaySetsAdded.forEach(async dSet => {
|
||||
const newImageSrcEntry = {};
|
||||
const displaySet = displaySetService.getDisplaySetByUID(
|
||||
dSet.displaySetInstanceUID
|
||||
);
|
||||
const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID);
|
||||
if (!displaySet?.unsupported) {
|
||||
const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
|
||||
const imageId = imageIds[Math.floor(imageIds.length / 2)];
|
||||
@@ -202,10 +180,7 @@ function PanelStudyBrowser({
|
||||
const SubscriptionDisplaySetsChanged = displaySetService.subscribe(
|
||||
displaySetService.EVENTS.DISPLAY_SETS_CHANGED,
|
||||
changedDisplaySets => {
|
||||
const mappedDisplaySets = _mapDisplaySets(
|
||||
changedDisplaySets,
|
||||
thumbnailImageSrcMap
|
||||
);
|
||||
const mappedDisplaySets = _mapDisplaySets(changedDisplaySets, thumbnailImageSrcMap);
|
||||
setDisplaySets(mappedDisplaySets);
|
||||
}
|
||||
);
|
||||
@@ -230,40 +205,25 @@ function PanelStudyBrowser({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const tabs = _createStudyBrowserTabs(
|
||||
StudyInstanceUIDs,
|
||||
studyDisplayList,
|
||||
displaySets
|
||||
);
|
||||
const tabs = _createStudyBrowserTabs(StudyInstanceUIDs, studyDisplayList, displaySets);
|
||||
|
||||
// TODO: Should not fire this on "close"
|
||||
function _handleStudyClick(StudyInstanceUID) {
|
||||
const shouldCollapseStudy = expandedStudyInstanceUIDs.includes(
|
||||
StudyInstanceUID
|
||||
);
|
||||
const shouldCollapseStudy = expandedStudyInstanceUIDs.includes(StudyInstanceUID);
|
||||
const updatedExpandedStudyInstanceUIDs = shouldCollapseStudy
|
||||
? // eslint-disable-next-line prettier/prettier
|
||||
[
|
||||
...expandedStudyInstanceUIDs.filter(
|
||||
stdyUid => stdyUid !== StudyInstanceUID
|
||||
),
|
||||
]
|
||||
[...expandedStudyInstanceUIDs.filter(stdyUid => stdyUid !== StudyInstanceUID)]
|
||||
: [...expandedStudyInstanceUIDs, StudyInstanceUID];
|
||||
|
||||
setExpandedStudyInstanceUIDs(updatedExpandedStudyInstanceUIDs);
|
||||
|
||||
if (!shouldCollapseStudy) {
|
||||
const madeInClient = true;
|
||||
requestDisplaySetCreationForStudy(
|
||||
displaySetService,
|
||||
StudyInstanceUID,
|
||||
madeInClient
|
||||
);
|
||||
requestDisplaySetCreationForStudy(displaySetService, StudyInstanceUID, madeInClient);
|
||||
}
|
||||
}
|
||||
|
||||
const activeDisplaySetInstanceUIDs = viewports.get(activeViewportId)
|
||||
?.displaySetInstanceUIDs;
|
||||
const activeDisplaySetInstanceUIDs = viewports.get(activeViewportId)?.displaySetInstanceUIDs;
|
||||
|
||||
return (
|
||||
<StudyBrowser
|
||||
@@ -326,9 +286,7 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
|
||||
const componentType = _getComponentType(ds);
|
||||
|
||||
const array =
|
||||
componentType === 'thumbnail'
|
||||
? thumbnailDisplaySets
|
||||
: thumbnailNoImageDisplaySets;
|
||||
componentType === 'thumbnail' ? thumbnailDisplaySets : thumbnailNoImageDisplaySets;
|
||||
|
||||
array.push({
|
||||
displaySetInstanceUID: ds.displaySetInstanceUID,
|
||||
@@ -355,14 +313,7 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
|
||||
return [...thumbnailDisplaySets, ...thumbnailNoImageDisplaySets];
|
||||
}
|
||||
|
||||
const thumbnailNoImageModalities = [
|
||||
'SR',
|
||||
'SEG',
|
||||
'SM',
|
||||
'RTSTRUCT',
|
||||
'RTPLAN',
|
||||
'RTDOSE',
|
||||
];
|
||||
const thumbnailNoImageModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
|
||||
|
||||
function _getComponentType(ds) {
|
||||
if (thumbnailNoImageModalities.includes(ds.Modality) || ds?.unsupported) {
|
||||
@@ -385,11 +336,7 @@ function _getComponentType(ds) {
|
||||
* @param {object[]} displaySets
|
||||
* @returns tabs - The prop object expected by the StudyBrowser component
|
||||
*/
|
||||
function _createStudyBrowserTabs(
|
||||
primaryStudyInstanceUIDs,
|
||||
studyDisplayList,
|
||||
displaySets
|
||||
) {
|
||||
function _createStudyBrowserTabs(primaryStudyInstanceUIDs, studyDisplayList, displaySets) {
|
||||
const primaryStudies = [];
|
||||
const recentStudies = [];
|
||||
const allStudies = [];
|
||||
|
||||
@@ -13,21 +13,12 @@ import requestDisplaySetCreationForStudy from './requestDisplaySetCreationForStu
|
||||
* @param {object} commandsManager
|
||||
* @param {object} extensionManager
|
||||
*/
|
||||
function WrappedPanelStudyBrowser({
|
||||
commandsManager,
|
||||
extensionManager,
|
||||
servicesManager,
|
||||
}) {
|
||||
function WrappedPanelStudyBrowser({ commandsManager, extensionManager, servicesManager }) {
|
||||
// TODO: This should be made available a different way; route should have
|
||||
// already determined our datasource
|
||||
const dataSource = extensionManager.getDataSources()[0];
|
||||
const _getStudiesForPatientByMRN = getStudiesForPatientByMRN.bind(
|
||||
null,
|
||||
dataSource
|
||||
);
|
||||
const _getImageSrcFromImageId = _createGetImageSrcFromImageIdFn(
|
||||
extensionManager
|
||||
);
|
||||
const _getStudiesForPatientByMRN = getStudiesForPatientByMRN.bind(null, dataSource);
|
||||
const _getImageSrcFromImageId = _createGetImageSrcFromImageIdFn(extensionManager);
|
||||
const _requestDisplaySetCreationForStudy = requestDisplaySetCreationForStudy.bind(
|
||||
null,
|
||||
dataSource
|
||||
|
||||
@@ -7,11 +7,8 @@ export const CREATE_REPORT_DIALOG_RESPONSE = {
|
||||
CREATE_REPORT: 1,
|
||||
};
|
||||
|
||||
export default function createReportDialogPrompt(
|
||||
uiDialogService,
|
||||
{ extensionManager }
|
||||
) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
export default function createReportDialogPrompt(uiDialogService, { extensionManager }) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let dialogId = undefined;
|
||||
|
||||
const _handleClose = () => {
|
||||
@@ -52,10 +49,8 @@ export default function createReportDialogPrompt(
|
||||
|
||||
const dataSourcesOpts = Object.keys(extensionManager.dataSourceMap)
|
||||
.filter(ds => {
|
||||
const configuration =
|
||||
extensionManager.dataSourceDefs[ds]?.configuration;
|
||||
const supportsStow =
|
||||
configuration?.supportsStow ?? configuration?.wadoRoot;
|
||||
const configuration = extensionManager.dataSourceDefs[ds]?.configuration;
|
||||
const supportsStow = configuration?.supportsStow ?? configuration?.wadoRoot;
|
||||
return supportsStow;
|
||||
})
|
||||
.map(ds => {
|
||||
@@ -105,12 +100,11 @@ export default function createReportDialogPrompt(
|
||||
{dataSourcesOpts.length > 1 && (
|
||||
<Select
|
||||
closeMenuOnSelect={true}
|
||||
className="mr-2 bg-black border-primary-main"
|
||||
className="border-primary-main mr-2 bg-black"
|
||||
options={dataSourcesOpts}
|
||||
placeholder={
|
||||
dataSourcesOpts.find(
|
||||
option => option.value === value.dataSourceName
|
||||
).placeHolder
|
||||
dataSourcesOpts.find(option => option.value === value.dataSourceName)
|
||||
.placeHolder
|
||||
}
|
||||
value={value.dataSourceName}
|
||||
onChange={evt => {
|
||||
@@ -123,7 +117,7 @@ export default function createReportDialogPrompt(
|
||||
autoFocus
|
||||
label="Enter the report name"
|
||||
labelClassName="text-white text-[14px] leading-[1.2]"
|
||||
className="bg-black border-primary-main"
|
||||
className="border-primary-main bg-black"
|
||||
type="text"
|
||||
value={value.label}
|
||||
onChange={onChangeHandler}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
// leading edge, instead of the trailing.
|
||||
function debounce(func, wait, immediate) {
|
||||
var timeout;
|
||||
return function() {
|
||||
return function () {
|
||||
var context = this,
|
||||
args = arguments;
|
||||
var later = function() {
|
||||
var later = function () {
|
||||
timeout = null;
|
||||
if (!immediate) {
|
||||
func.apply(context, args);
|
||||
|
||||
@@ -49,7 +49,10 @@ export default function Toolbar({ servicesManager }) {
|
||||
return (
|
||||
// The margin for separating the tools on the toolbar should go here and NOT in each individual component (button) item.
|
||||
// This allows for the individual items to be included in other UI components where perhaps alternative margins are desired.
|
||||
<div key={id} className={classnames('mr-1')}>
|
||||
<div
|
||||
key={id}
|
||||
className={classnames('mr-1')}
|
||||
>
|
||||
<Component
|
||||
id={id}
|
||||
{...componentProps}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function ToolbarDivider() {
|
||||
return (
|
||||
<span className="self-center w-4 h-8 mx-2 border-l border-common-dark" />
|
||||
);
|
||||
return <span className="border-common-dark mx-2 h-8 w-4 self-center border-l" />;
|
||||
}
|
||||
@@ -4,19 +4,10 @@ import { LayoutSelector as OHIFLayoutSelector, ToolbarButton } from '@ohif/ui';
|
||||
|
||||
import { ServicesManager } from '@ohif/core';
|
||||
|
||||
function LayoutSelector({
|
||||
rows,
|
||||
columns,
|
||||
className,
|
||||
servicesManager,
|
||||
...rest
|
||||
}) {
|
||||
function LayoutSelector({ rows, columns, className, servicesManager, ...rest }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const {
|
||||
hangingProtocolService,
|
||||
toolbarService,
|
||||
} = (servicesManager as ServicesManager).services;
|
||||
const { hangingProtocolService, toolbarService } = (servicesManager as ServicesManager).services;
|
||||
|
||||
const closeOnOutsideClick = () => {
|
||||
if (isOpen) {
|
||||
|
||||
@@ -14,12 +14,7 @@ import {
|
||||
LoadingIndicatorProgress,
|
||||
} from '@ohif/ui';
|
||||
import i18n from '@ohif/i18n';
|
||||
import {
|
||||
ServicesManager,
|
||||
HangingProtocolService,
|
||||
hotkeys,
|
||||
CommandsManager,
|
||||
} from '@ohif/core';
|
||||
import { ServicesManager, HangingProtocolService, hotkeys, CommandsManager } from '@ohif/core';
|
||||
import { useAppConfig } from '@state';
|
||||
import Toolbar from '../Toolbar/Toolbar';
|
||||
|
||||
@@ -76,9 +71,7 @@ function ViewerLayout({
|
||||
const { t } = useTranslation();
|
||||
const { show, hide } = useModal();
|
||||
|
||||
const [showLoadingIndicator, setShowLoadingIndicator] = useState(
|
||||
appConfig.showLoadingIndicator
|
||||
);
|
||||
const [showLoadingIndicator, setShowLoadingIndicator] = useState(appConfig.showLoadingIndicator);
|
||||
|
||||
const { hangingProtocolService } = servicesManager.services;
|
||||
|
||||
@@ -105,9 +98,7 @@ function ViewerLayout({
|
||||
title: t('UserPreferencesModal:User Preferences'),
|
||||
content: UserPreferences,
|
||||
contentProps: {
|
||||
hotkeyDefaults: hotkeysManager.getValidHotkeyDefinitions(
|
||||
hotkeyDefaults
|
||||
),
|
||||
hotkeyDefaults: hotkeysManager.getValidHotkeyDefinitions(hotkeyDefaults),
|
||||
hotkeyDefinitions,
|
||||
currentLanguage: currentLanguage(),
|
||||
availableLanguages,
|
||||
@@ -134,9 +125,7 @@ function ViewerLayout({
|
||||
title: t('Header:Logout'),
|
||||
icon: 'power-off',
|
||||
onClick: async () => {
|
||||
navigate(
|
||||
`/logout?redirect_uri=${encodeURIComponent(window.location.href)}`
|
||||
);
|
||||
navigate(`/logout?redirect_uri=${encodeURIComponent(window.location.href)}`);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -234,13 +223,11 @@ function ViewerLayout({
|
||||
</ErrorBoundary>
|
||||
</Header>
|
||||
<div
|
||||
className="bg-black flex flex-row items-stretch w-full overflow-hidden flex-nowrap relative"
|
||||
className="relative flex w-full flex-row flex-nowrap items-stretch overflow-hidden bg-black"
|
||||
style={{ height: 'calc(100vh - 52px' }}
|
||||
>
|
||||
<React.Fragment>
|
||||
{showLoadingIndicator && (
|
||||
<LoadingIndicatorProgress className="h-full w-full bg-black" />
|
||||
)}
|
||||
{showLoadingIndicator && <LoadingIndicatorProgress className="h-full w-full bg-black" />}
|
||||
{/* LEFT SIDEPANELS */}
|
||||
{leftPanelComponents.length ? (
|
||||
<ErrorBoundary context="Left Panel">
|
||||
@@ -253,8 +240,8 @@ function ViewerLayout({
|
||||
</ErrorBoundary>
|
||||
) : null}
|
||||
{/* TOOLBAR + GRID */}
|
||||
<div className="flex flex-col flex-1 h-full">
|
||||
<div className="flex items-center justify-center flex-1 h-full overflow-hidden bg-black relative">
|
||||
<div className="flex h-full flex-1 flex-col">
|
||||
<div className="relative flex h-full flex-1 items-center justify-center overflow-hidden bg-black">
|
||||
<ErrorBoundary context="Grid">
|
||||
<ViewportGridComp
|
||||
servicesManager={servicesManager}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { ServicesManager, utils, Types } from '@ohif/core';
|
||||
|
||||
import {
|
||||
ContextMenuController,
|
||||
defaultContextMenu,
|
||||
} from './CustomizableContextMenu';
|
||||
import { ContextMenuController, defaultContextMenu } from './CustomizableContextMenu';
|
||||
import DicomTagBrowser from './DicomTagBrowser/DicomTagBrowser';
|
||||
import reuseCachedLayouts from './utils/reuseCachedLayouts';
|
||||
import findViewportsByPosition, {
|
||||
@@ -35,8 +32,7 @@ export type UpdateViewportDisplaySetParams = {
|
||||
*/
|
||||
const isHangingProtocolCommand = command =>
|
||||
command &&
|
||||
(command.commandName === 'setHangingProtocol' ||
|
||||
command.commandName === 'toggleHangingProtocol');
|
||||
(command.commandName === 'setHangingProtocol' || command.commandName === 'toggleHangingProtocol');
|
||||
|
||||
const commandsModule = ({
|
||||
servicesManager,
|
||||
@@ -54,10 +50,7 @@ const commandsModule = ({
|
||||
} = (servicesManager as ServicesManager).services;
|
||||
|
||||
// Define a context menu controller for use with any context menus
|
||||
const contextMenuController = new ContextMenuController(
|
||||
servicesManager,
|
||||
commandsManager
|
||||
);
|
||||
const contextMenuController = new ContextMenuController(servicesManager, commandsManager);
|
||||
|
||||
const actions = {
|
||||
/**
|
||||
@@ -95,11 +88,7 @@ const commandsModule = ({
|
||||
...selectorProps,
|
||||
};
|
||||
|
||||
contextMenuController.showContextMenu(
|
||||
optionsToUse,
|
||||
element,
|
||||
defaultPointsPosition
|
||||
);
|
||||
contextMenuController.showContextMenu(optionsToUse, element, defaultPointsPosition);
|
||||
},
|
||||
|
||||
/** Close a context menu currently displayed */
|
||||
@@ -187,19 +176,10 @@ const commandsModule = ({
|
||||
// the activeViewportId
|
||||
const state = viewportGridService.getState();
|
||||
const hpInfo = hangingProtocolService.getState();
|
||||
const {
|
||||
protocol: oldProtocol,
|
||||
} = hangingProtocolService.getActiveProtocol();
|
||||
const stateSyncReduce = reuseCachedLayouts(
|
||||
state,
|
||||
hangingProtocolService,
|
||||
stateSyncService
|
||||
);
|
||||
const {
|
||||
hangingProtocolStageIndexMap,
|
||||
viewportGridStore,
|
||||
displaySetSelectorMap,
|
||||
} = stateSyncReduce;
|
||||
const { protocol: oldProtocol } = hangingProtocolService.getActiveProtocol();
|
||||
const stateSyncReduce = reuseCachedLayouts(state, hangingProtocolService, stateSyncService);
|
||||
const { hangingProtocolStageIndexMap, viewportGridStore, displaySetSelectorMap } =
|
||||
stateSyncReduce;
|
||||
|
||||
if (!protocolId) {
|
||||
// Re-use the previous protocol id, and optionally stage
|
||||
@@ -209,8 +189,7 @@ const commandsModule = ({
|
||||
}
|
||||
} else if (stageIndex === undefined && stageId === undefined) {
|
||||
// Re-set the same stage as was previously used
|
||||
const hangingId = `${activeStudyUID ||
|
||||
hpInfo.activeStudyUID}:${protocolId}`;
|
||||
const hangingId = `${activeStudyUID || hpInfo.activeStudyUID}:${protocolId}`;
|
||||
stageIndex = hangingProtocolStageIndexMap[hangingId]?.stageIndex;
|
||||
}
|
||||
|
||||
@@ -225,9 +204,9 @@ const commandsModule = ({
|
||||
hangingProtocolService.setActiveStudyUID(activeStudyUID);
|
||||
}
|
||||
|
||||
const storedHanging = `${
|
||||
hangingProtocolService.getState().activeStudyUID
|
||||
}:${protocolId}:${useStageIdx || 0}`;
|
||||
const storedHanging = `${hangingProtocolService.getState().activeStudyUID}:${protocolId}:${
|
||||
useStageIdx || 0
|
||||
}`;
|
||||
|
||||
const restoreProtocol = !reset && viewportGridStore[storedHanging];
|
||||
|
||||
@@ -285,19 +264,14 @@ const commandsModule = ({
|
||||
}
|
||||
},
|
||||
|
||||
toggleHangingProtocol: ({
|
||||
protocolId,
|
||||
stageIndex,
|
||||
}: HangingProtocolParams): boolean => {
|
||||
toggleHangingProtocol: ({ protocolId, stageIndex }: HangingProtocolParams): boolean => {
|
||||
const {
|
||||
protocol,
|
||||
stageIndex: desiredStageIndex,
|
||||
activeStudy,
|
||||
} = hangingProtocolService.getActiveProtocol();
|
||||
const { toggleHangingProtocol } = stateSyncService.getState();
|
||||
const storedHanging = `${
|
||||
activeStudy.StudyInstanceUID
|
||||
}:${protocolId}:${stageIndex | 0}`;
|
||||
const storedHanging = `${activeStudy.StudyInstanceUID}:${protocolId}:${stageIndex | 0}`;
|
||||
if (
|
||||
protocol.id === protocolId &&
|
||||
(stageIndex === undefined || stageIndex === desiredStageIndex)
|
||||
@@ -326,10 +300,7 @@ const commandsModule = ({
|
||||
},
|
||||
|
||||
deltaStage: ({ direction }) => {
|
||||
const {
|
||||
protocolId,
|
||||
stageIndex: oldStageIndex,
|
||||
} = hangingProtocolService.getState();
|
||||
const { protocolId, stageIndex: oldStageIndex } = hangingProtocolService.getState();
|
||||
const { protocol } = hangingProtocolService.getActiveProtocol();
|
||||
for (
|
||||
let stageIndex = oldStageIndex + direction;
|
||||
@@ -358,23 +329,14 @@ const commandsModule = ({
|
||||
const { protocol } = hangingProtocolService.getActiveProtocol();
|
||||
const onLayoutChange = protocol.callbacks?.onLayoutChange;
|
||||
if (commandsManager.run(onLayoutChange, { numRows, numCols }) === false) {
|
||||
console.log(
|
||||
'setViewportGridLayout running',
|
||||
onLayoutChange,
|
||||
numRows,
|
||||
numCols
|
||||
);
|
||||
console.log('setViewportGridLayout running', onLayoutChange, numRows, numCols);
|
||||
// Don't apply the layout if the run command returns false
|
||||
return;
|
||||
}
|
||||
|
||||
const completeLayout = () => {
|
||||
const state = viewportGridService.getState();
|
||||
const stateReduce = findViewportsByPosition(
|
||||
state,
|
||||
{ numRows, numCols },
|
||||
stateSyncService
|
||||
);
|
||||
const stateReduce = findViewportsByPosition(state, { numRows, numCols }, stateSyncService);
|
||||
const findOrCreateViewport = layoutFindOrCreate.bind(
|
||||
null,
|
||||
hangingProtocolService,
|
||||
@@ -395,11 +357,8 @@ const commandsModule = ({
|
||||
toggleOneUp() {
|
||||
const viewportGridState = viewportGridService.getState();
|
||||
const { activeViewportId, viewports, layout } = viewportGridState;
|
||||
const {
|
||||
displaySetInstanceUIDs,
|
||||
displaySetOptions,
|
||||
viewportOptions,
|
||||
} = viewports.get(activeViewportId);
|
||||
const { displaySetInstanceUIDs, displaySetOptions, viewportOptions } =
|
||||
viewports.get(activeViewportId);
|
||||
|
||||
if (layout.numCols === 1 && layout.numRows === 1) {
|
||||
// The viewer is in one-up. Check if there is a state to restore/toggle back to.
|
||||
@@ -410,8 +369,7 @@ const commandsModule = ({
|
||||
}
|
||||
// There is a state to toggle back to. The viewport that was
|
||||
// originally toggled to one up was the former active viewport.
|
||||
const viewportIdToUpdate =
|
||||
toggleOneUpViewportGridStore.activeViewportId;
|
||||
const viewportIdToUpdate = toggleOneUpViewportGridStore.activeViewportId;
|
||||
|
||||
const updatedViewportsViaHP =
|
||||
displaySetInstanceUIDs.length > 1
|
||||
@@ -436,23 +394,22 @@ const commandsModule = ({
|
||||
// the current one up viewport might have a new displaySet dragged and dropped on it
|
||||
// so we should prioritize the current one in the old grid store layout viewports
|
||||
|
||||
const newViewports = Array.from(
|
||||
toggleOneUpViewportGridStore.viewports.values()
|
||||
).map(viewport => {
|
||||
if (viewport.viewportId === currentOneUpViewport.viewportId) {
|
||||
return {
|
||||
...currentOneUpViewport,
|
||||
};
|
||||
}
|
||||
const newViewports = Array.from(toggleOneUpViewportGridStore.viewports.values()).map(
|
||||
viewport => {
|
||||
if (viewport.viewportId === currentOneUpViewport.viewportId) {
|
||||
return {
|
||||
...currentOneUpViewport,
|
||||
};
|
||||
}
|
||||
|
||||
return viewport;
|
||||
});
|
||||
return viewport;
|
||||
}
|
||||
);
|
||||
|
||||
// However, we also need to take into account that the current one up viewport
|
||||
// might have been part of a bigger hanging protocol layout, so going back
|
||||
// from one up we should apply those viewports as well.
|
||||
return updatedViewportsViaHP.length > 1 &&
|
||||
updatedViewportsViaHP[position]
|
||||
return updatedViewportsViaHP.length > 1 && updatedViewportsViaHP[position]
|
||||
? {
|
||||
viewportOptions,
|
||||
displaySetOptions,
|
||||
@@ -509,10 +466,7 @@ const commandsModule = ({
|
||||
});
|
||||
};
|
||||
|
||||
subscribeToNextViewportGridChange(
|
||||
viewportGridService,
|
||||
clearToggleOneUpViewportGridStore
|
||||
);
|
||||
subscribeToNextViewportGridChange(viewportGridService, clearToggleOneUpViewportGridStore);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -574,8 +528,7 @@ const commandsModule = ({
|
||||
const { activeViewportId, viewports } = viewportGridService.getState();
|
||||
|
||||
const activeViewport = viewports.get(activeViewportId);
|
||||
const activeDisplaySetInstanceUID =
|
||||
activeViewport.displaySetInstanceUIDs[0];
|
||||
const activeDisplaySetInstanceUID = activeViewport.displaySetInstanceUIDs[0];
|
||||
|
||||
const thumbnailList = document.querySelector('#ohif-thumbnail-list');
|
||||
|
||||
@@ -585,9 +538,7 @@ const commandsModule = ({
|
||||
|
||||
const thumbnailListBounds = thumbnailList.getBoundingClientRect();
|
||||
|
||||
const thumbnail = document.querySelector(
|
||||
`#thumbnail-${activeDisplaySetInstanceUID}`
|
||||
);
|
||||
const thumbnail = document.querySelector(`#thumbnail-${activeDisplaySetInstanceUID}`);
|
||||
|
||||
if (!thumbnail) {
|
||||
return;
|
||||
@@ -610,14 +561,7 @@ const commandsModule = ({
|
||||
direction,
|
||||
excludeNonImageModalities,
|
||||
}: UpdateViewportDisplaySetParams) => {
|
||||
const nonImageModalities = [
|
||||
'SR',
|
||||
'SEG',
|
||||
'SM',
|
||||
'RTSTRUCT',
|
||||
'RTPLAN',
|
||||
'RTDOSE',
|
||||
];
|
||||
const nonImageModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
|
||||
|
||||
// Sort the display sets as per the hanging protocol service viewport/display set scoring system.
|
||||
// The thumbnail list uses the same sorting.
|
||||
@@ -638,30 +582,22 @@ const commandsModule = ({
|
||||
|
||||
for (
|
||||
displaySetIndexToShow = activeDisplaySetIndex + direction;
|
||||
displaySetIndexToShow > -1 &&
|
||||
displaySetIndexToShow < currentDisplaySets.length;
|
||||
displaySetIndexToShow > -1 && displaySetIndexToShow < currentDisplaySets.length;
|
||||
displaySetIndexToShow += direction
|
||||
) {
|
||||
if (
|
||||
!excludeNonImageModalities ||
|
||||
!nonImageModalities.includes(
|
||||
currentDisplaySets[displaySetIndexToShow].Modality
|
||||
)
|
||||
!nonImageModalities.includes(currentDisplaySets[displaySetIndexToShow].Modality)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
displaySetIndexToShow < 0 ||
|
||||
displaySetIndexToShow >= currentDisplaySets.length
|
||||
) {
|
||||
if (displaySetIndexToShow < 0 || displaySetIndexToShow >= currentDisplaySets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { displaySetInstanceUID } = currentDisplaySets[
|
||||
displaySetIndexToShow
|
||||
];
|
||||
const { displaySetInstanceUID } = currentDisplaySets[displaySetIndexToShow];
|
||||
|
||||
let updatedViewports = [];
|
||||
|
||||
|
||||
@@ -32,21 +32,13 @@ export const findOrCreateViewport = (
|
||||
options.inDisplay = [...viewportsByPosition.initialInDisplay];
|
||||
}
|
||||
// See if there is a default viewport for new views.
|
||||
const missing = hangingProtocolService.getMissingViewport(
|
||||
protocolId,
|
||||
stageIndex,
|
||||
options
|
||||
);
|
||||
const missing = hangingProtocolService.getMissingViewport(protocolId, stageIndex, options);
|
||||
if (missing) {
|
||||
const displaySetInstanceUIDs = missing.displaySetsInfo.map(
|
||||
it => it.displaySetInstanceUID
|
||||
);
|
||||
const displaySetInstanceUIDs = missing.displaySetsInfo.map(it => it.displaySetInstanceUID);
|
||||
options.inDisplay.push(...displaySetInstanceUIDs);
|
||||
return {
|
||||
displaySetInstanceUIDs,
|
||||
displaySetOptions: missing.displaySetsInfo.map(
|
||||
it => it.displaySetOptions
|
||||
),
|
||||
displaySetOptions: missing.displaySetsInfo.map(it => it.displaySetOptions),
|
||||
viewportOptions: {
|
||||
...missing.viewportOptions,
|
||||
},
|
||||
|
||||
@@ -13,10 +13,7 @@ import { GoogleCloudDataSourceConfigurationAPI } from './DataSourceConfiguration
|
||||
* custom page for the user to view their profile, or to add a custom
|
||||
* page for login etc.
|
||||
*/
|
||||
export default function getCustomizationModule({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
}) {
|
||||
export default function getCustomizationModule({ servicesManager, extensionManager }) {
|
||||
return [
|
||||
{
|
||||
name: 'helloPage',
|
||||
@@ -25,9 +22,7 @@ export default function getCustomizationModule({
|
||||
routes: [
|
||||
{
|
||||
path: '/custom',
|
||||
children: () => (
|
||||
<h1 style={{ color: 'white' }}>Hello Custom Route</h1>
|
||||
),
|
||||
children: () => <h1 style={{ color: 'white' }}>Hello Custom Route</h1>,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -88,7 +83,7 @@ export default function getCustomizationModule({
|
||||
*/
|
||||
{
|
||||
id: 'ohif.overlayItem',
|
||||
content: function(props) {
|
||||
content: function (props) {
|
||||
if (this.condition && !this.condition(props)) {
|
||||
return null;
|
||||
}
|
||||
@@ -110,9 +105,7 @@ export default function getCustomizationModule({
|
||||
style={{ color: this.color || undefined }}
|
||||
title={this.title || ''}
|
||||
>
|
||||
{this.label && (
|
||||
<span className="mr-1 shrink-0">{this.label}</span>
|
||||
)}
|
||||
{this.label && <span className="mr-1 shrink-0">{this.label}</span>}
|
||||
<span className="font-light">{value}</span>
|
||||
</span>
|
||||
);
|
||||
@@ -126,7 +119,7 @@ export default function getCustomizationModule({
|
||||
* This function clones the object and child objects to prevent
|
||||
* changes to the original customization object.
|
||||
*/
|
||||
transform: function(customizationService: CustomizationService) {
|
||||
transform: function (customizationService: CustomizationService) {
|
||||
// Don't modify the children, as those are copied by reference
|
||||
const clonedObject = { ...this };
|
||||
clonedObject.menus = this.menus.map(menu => ({ ...menu }));
|
||||
|
||||
@@ -30,10 +30,7 @@ export default function getDisplaySetMessages(
|
||||
|
||||
const isMultiframe = firstInstance.NumberOfFrames > 1;
|
||||
// Can't reconstruct if all instances don't have the ImagePositionPatient.
|
||||
if (
|
||||
!isMultiframe &&
|
||||
!instances.every(instance => instance.ImagePositionPatient)
|
||||
) {
|
||||
if (!isMultiframe && !instances.every(instance => instance.ImagePositionPatient)) {
|
||||
messages.addMessage(DisplaySetMessage.CODES.NO_POSITION_INFORMATION);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,7 @@ import ViewerLayout from './ViewerLayout';
|
||||
- Init layout based on the displaySets and the objects.
|
||||
*/
|
||||
|
||||
export default function({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
commandsManager,
|
||||
hotkeysManager,
|
||||
}) {
|
||||
export default function ({ servicesManager, extensionManager, commandsManager, hotkeysManager }) {
|
||||
function ViewerLayoutWithServices(props) {
|
||||
return ViewerLayout({
|
||||
servicesManager,
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import OHIF from '@ohif/core';
|
||||
|
||||
import {
|
||||
InstanceMetadata,
|
||||
PhilipsPETPrivateGroup,
|
||||
} from '@cornerstonejs/calculate-suv/src/types';
|
||||
import { InstanceMetadata, PhilipsPETPrivateGroup } from '@cornerstonejs/calculate-suv/src/types';
|
||||
|
||||
const metadataProvider = OHIF.classes.MetadataProvider;
|
||||
|
||||
export default function getPTImageIdInstanceMetadata(
|
||||
imageId: string
|
||||
): InstanceMetadata {
|
||||
export default function getPTImageIdInstanceMetadata(imageId: string): InstanceMetadata {
|
||||
const dicomMetaData = metadataProvider.get('instance', imageId);
|
||||
|
||||
if (!dicomMetaData) {
|
||||
@@ -22,17 +17,15 @@ export default function getPTImageIdInstanceMetadata(
|
||||
dicomMetaData.CorrectedImage === undefined ||
|
||||
dicomMetaData.Units === undefined ||
|
||||
!dicomMetaData.RadiopharmaceuticalInformationSequence ||
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
||||
.RadionuclideHalfLife === undefined ||
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
||||
.RadionuclideTotalDose === undefined ||
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideHalfLife === undefined ||
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideTotalDose === undefined ||
|
||||
dicomMetaData.DecayCorrection === undefined ||
|
||||
dicomMetaData.AcquisitionDate === undefined ||
|
||||
dicomMetaData.AcquisitionTime === undefined ||
|
||||
(dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
||||
.RadiopharmaceuticalStartDateTime === undefined &&
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
||||
.RadiopharmaceuticalStartTime === undefined)
|
||||
(dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartDateTime ===
|
||||
undefined &&
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartTime ===
|
||||
undefined)
|
||||
) {
|
||||
throw new Error('required metadata are missing');
|
||||
}
|
||||
@@ -45,17 +38,13 @@ export default function getPTImageIdInstanceMetadata(
|
||||
CorrectedImage: dicomMetaData.CorrectedImage,
|
||||
Units: dicomMetaData.Units,
|
||||
RadionuclideHalfLife:
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
||||
.RadionuclideHalfLife,
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideHalfLife,
|
||||
RadionuclideTotalDose:
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
||||
.RadionuclideTotalDose,
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideTotalDose,
|
||||
RadiopharmaceuticalStartDateTime:
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
||||
.RadiopharmaceuticalStartDateTime,
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartDateTime,
|
||||
RadiopharmaceuticalStartTime:
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
|
||||
.RadiopharmaceuticalStartTime,
|
||||
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartTime,
|
||||
DecayCorrection: dicomMetaData.DecayCorrection,
|
||||
PatientWeight: dicomMetaData.PatientWeight,
|
||||
SeriesDate: dicomMetaData.SeriesDate,
|
||||
@@ -81,17 +70,11 @@ export default function getPTImageIdInstanceMetadata(
|
||||
instanceMetadata.GEPrivatePostInjectionDateTime = dicomMetaData['0009100d'];
|
||||
}
|
||||
|
||||
if (
|
||||
dicomMetaData.FrameReferenceTime &&
|
||||
dicomMetaData.FrameReferenceTime !== undefined
|
||||
) {
|
||||
if (dicomMetaData.FrameReferenceTime && dicomMetaData.FrameReferenceTime !== undefined) {
|
||||
instanceMetadata.FrameReferenceTime = dicomMetaData.FrameReferenceTime;
|
||||
}
|
||||
|
||||
if (
|
||||
dicomMetaData.ActualFrameDuration &&
|
||||
dicomMetaData.ActualFrameDuration !== undefined
|
||||
) {
|
||||
if (dicomMetaData.ActualFrameDuration && dicomMetaData.ActualFrameDuration !== undefined) {
|
||||
instanceMetadata.ActualFrameDuration = dicomMetaData.ActualFrameDuration;
|
||||
}
|
||||
|
||||
@@ -111,10 +94,7 @@ function convertInterfaceTimeToString(time): string {
|
||||
const minutes = `${time.minutes || '00'}`.padStart(2, '0');
|
||||
const seconds = `${time.seconds || '00'}`.padStart(2, '0');
|
||||
|
||||
const fractionalSeconds = `${time.fractionalSeconds || '000000'}`.padEnd(
|
||||
6,
|
||||
'0'
|
||||
);
|
||||
const fractionalSeconds = `${time.fractionalSeconds || '000000'}`.padEnd(6, '0');
|
||||
|
||||
const timeString = `${hours}${minutes}${seconds}.${fractionalSeconds}`;
|
||||
return timeString;
|
||||
|
||||
@@ -6,11 +6,7 @@ import { WrappedPanelStudyBrowser, PanelMeasurementTable } from './Panels';
|
||||
// - cancel promises when component is destroyed
|
||||
// - show errors in UI for thumbnails if promise fails
|
||||
|
||||
function getPanelModule({
|
||||
commandsManager,
|
||||
extensionManager,
|
||||
servicesManager,
|
||||
}) {
|
||||
function getPanelModule({ commandsManager, extensionManager, servicesManager }) {
|
||||
const wrappedMeasurementPanel = () => {
|
||||
return (
|
||||
<PanelMeasurementTable
|
||||
|
||||
@@ -16,10 +16,8 @@ const makeDisplaySet = instances => {
|
||||
const instance = instances[0];
|
||||
const imageSet = new ImageSet(instances);
|
||||
|
||||
const {
|
||||
value: isReconstructable,
|
||||
averageSpacingBetweenFrames,
|
||||
} = isDisplaySetReconstructable(instances);
|
||||
const { value: isReconstructable, averageSpacingBetweenFrames } =
|
||||
isDisplaySetReconstructable(instances);
|
||||
// set appropriate attributes to image set...
|
||||
const messages = getDisplaySetMessages(instances, isReconstructable);
|
||||
|
||||
@@ -48,9 +46,7 @@ const makeDisplaySet = instances => {
|
||||
if (shallSort) {
|
||||
imageSet.sortBy((a, b) => {
|
||||
// Sort by InstanceNumber (0020,0013)
|
||||
return (
|
||||
(parseInt(a.InstanceNumber) || 0) - (parseInt(b.InstanceNumber) || 0)
|
||||
);
|
||||
return (parseInt(a.InstanceNumber) || 0) - (parseInt(b.InstanceNumber) || 0);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -163,12 +163,7 @@ const hpMNCompare: Types.HangingProtocol.Protocol = {
|
||||
columns: 2,
|
||||
},
|
||||
},
|
||||
viewports: [
|
||||
currentViewport0,
|
||||
priorViewport0,
|
||||
currentViewport1,
|
||||
priorViewport1,
|
||||
],
|
||||
viewports: [currentViewport0, priorViewport0, currentViewport1, priorViewport1],
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
@@ -11,10 +11,7 @@ import getStudiesForPatientByMRN from './Panels/getStudiesForPatientByMRN';
|
||||
import getCustomizationModule from './getCustomizationModule';
|
||||
import { id } from './id.js';
|
||||
import preRegistration from './init';
|
||||
import {
|
||||
ContextMenuController,
|
||||
CustomizableContextMenuTypes,
|
||||
} from './CustomizableContextMenu';
|
||||
import { ContextMenuController, CustomizableContextMenuTypes } from './CustomizableContextMenu';
|
||||
import * as dicomWebUtils from './DicomWebDataSource/utils';
|
||||
|
||||
const defaultExtension: Types.Extensions.Extension = {
|
||||
|
||||
@@ -13,17 +13,11 @@ const metadataProvider = classes.MetadataProvider;
|
||||
export default function init({ servicesManager, configuration = {} }): void {
|
||||
const { stateSyncService } = servicesManager.services;
|
||||
// Add
|
||||
DicomMetadataStore.subscribe(
|
||||
DicomMetadataStore.EVENTS.INSTANCES_ADDED,
|
||||
handlePETImageMetadata
|
||||
);
|
||||
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.INSTANCES_ADDED, handlePETImageMetadata);
|
||||
|
||||
// If the metadata for PET has changed by the user (e.g. manually changing the PatientWeight)
|
||||
// we need to recalculate the SUV Scaling Factors
|
||||
DicomMetadataStore.subscribe(
|
||||
DicomMetadataStore.EVENTS.SERIES_UPDATED,
|
||||
handlePETImageMetadata
|
||||
);
|
||||
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.SERIES_UPDATED, handlePETImageMetadata);
|
||||
|
||||
// viewportGridStore is a sync state which stores the entire
|
||||
// ViewportGridService getState, by the keys `<activeStudyUID>:<protocolId>:<stageIndex>`
|
||||
@@ -55,10 +49,7 @@ export default function init({ servicesManager, configuration = {} }): void {
|
||||
}
|
||||
|
||||
const handlePETImageMetadata = ({ SeriesInstanceUID, StudyInstanceUID }) => {
|
||||
const { instances } = DicomMetadataStore.getSeries(
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID
|
||||
);
|
||||
const { instances } = DicomMetadataStore.getSeries(StudyInstanceUID, SeriesInstanceUID);
|
||||
|
||||
const modality = instances[0].Modality;
|
||||
if (modality !== 'PT') {
|
||||
@@ -90,10 +81,6 @@ const handlePETImageMetadata = ({ SeriesInstanceUID, StudyInstanceUID }) => {
|
||||
}
|
||||
|
||||
instanceMetadataArray.forEach((instanceMetadata, index) => {
|
||||
metadataProvider.addCustomMetadata(
|
||||
imageIds[index],
|
||||
'scalingModule',
|
||||
suvScalingFactors[index]
|
||||
);
|
||||
metadataProvider.addCustomMetadata(imageIds[index], 'scalingModule', suvScalingFactors[index]);
|
||||
});
|
||||
};
|
||||
@@ -19,20 +19,12 @@ export default function findSRWithSameSeriesDescription(
|
||||
): Types.SeriesMetadata {
|
||||
const activeDisplaySets = displaySetService.getActiveDisplaySets();
|
||||
const srDisplaySets = activeDisplaySets.filter(ds => ds.Modality === 'SR');
|
||||
const sameSeries = srDisplaySets.find(
|
||||
ds => ds.SeriesDescription === SeriesDescription
|
||||
);
|
||||
const sameSeries = srDisplaySets.find(ds => ds.SeriesDescription === SeriesDescription);
|
||||
if (sameSeries) {
|
||||
console.log('Storing to same series', sameSeries);
|
||||
const { instance } = sameSeries;
|
||||
const {
|
||||
SeriesInstanceUID,
|
||||
SeriesDescription,
|
||||
SeriesDate,
|
||||
SeriesTime,
|
||||
SeriesNumber,
|
||||
Modality,
|
||||
} = instance;
|
||||
const { SeriesInstanceUID, SeriesDescription, SeriesDate, SeriesTime, SeriesNumber, Modality } =
|
||||
instance;
|
||||
return {
|
||||
SeriesInstanceUID,
|
||||
SeriesDescription,
|
||||
|
||||
@@ -35,15 +35,10 @@ const getDirectURL = (config, params) => {
|
||||
value.DirectRetrieveURL = URL.createObjectURL(blob);
|
||||
return value.DirectRetrieveURL;
|
||||
}
|
||||
if (
|
||||
!singlepart ||
|
||||
(singlepart !== true && singlepart.indexOf(fetchPart) === -1)
|
||||
) {
|
||||
if (!singlepart || (singlepart !== true && singlepart.indexOf(fetchPart) === -1)) {
|
||||
if (value.retrieveBulkData) {
|
||||
return value.retrieveBulkData().then(arr => {
|
||||
value.DirectRetrieveURL = URL.createObjectURL(
|
||||
new Blob([arr], { type: defaultType })
|
||||
);
|
||||
value.DirectRetrieveURL = URL.createObjectURL(new Blob([arr], { type: defaultType }));
|
||||
return value.DirectRetrieveURL;
|
||||
});
|
||||
}
|
||||
@@ -58,8 +53,7 @@ const getDirectURL = (config, params) => {
|
||||
const hasQuery = BulkDataURI.indexOf('?') !== -1;
|
||||
const hasAccept = BulkDataURI.indexOf('accept=') !== -1;
|
||||
const acceptUri =
|
||||
BulkDataURI +
|
||||
(hasAccept ? '' : (hasQuery ? '&' : '?') + `accept=${defaultType}`);
|
||||
BulkDataURI + (hasAccept ? '' : (hasQuery ? '&' : '?') + `accept=${defaultType}`);
|
||||
|
||||
if (tag === 'PixelData' || tag === 'EncapsulatedDocument') {
|
||||
return `${wadoRoot}/studies/${StudyInstanceUID}/series/${SeriesInstanceUID}/instances/${SOPInstanceUID}/rendered`;
|
||||
|
||||
@@ -57,14 +57,13 @@ const reuseCachedLayout = (
|
||||
continue;
|
||||
}
|
||||
if (viewportId === activeViewportId && i === 0) {
|
||||
displaySetSelectorMap[
|
||||
`${activeStudyUID}:activeDisplaySet:0`
|
||||
] = displaySetUID;
|
||||
displaySetSelectorMap[`${activeStudyUID}:activeDisplaySet:0`] = displaySetUID;
|
||||
}
|
||||
if (displaySetOptions[i]?.id) {
|
||||
displaySetSelectorMap[
|
||||
`${activeStudyUID}:${displaySetOptions[i].id}:${displaySetOptions[i]
|
||||
.matchedDisplaySetsIndex || 0}`
|
||||
`${activeStudyUID}:${displaySetOptions[i].id}:${
|
||||
displaySetOptions[i].matchedDisplaySetsIndex || 0
|
||||
}`
|
||||
] = displaySetUID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@ import toNumber from '@ohif/core/src/utils/toNumber';
|
||||
* @param {*} instances
|
||||
* @returns
|
||||
*/
|
||||
export default function areAllImageComponentsEqual(
|
||||
instances: Array<any>
|
||||
): boolean {
|
||||
export default function areAllImageComponentsEqual(instances: Array<any>): boolean {
|
||||
if (!instances?.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@ import toNumber from '@ohif/core/src/utils/toNumber';
|
||||
* @param {*} instances
|
||||
* @returns
|
||||
*/
|
||||
export default function areAllImageDimensionsEqual(
|
||||
instances: Array<any>
|
||||
): boolean {
|
||||
export default function areAllImageDimensionsEqual(instances: Array<any>): boolean {
|
||||
if (!instances?.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,24 +6,18 @@ import { _isSameOrientation } from '@ohif/core/src/utils/isDisplaySetReconstruct
|
||||
* @param {*} instances
|
||||
* @returns
|
||||
*/
|
||||
export default function areAllImageOrientationsEqual(
|
||||
instances: Array<any>
|
||||
): boolean {
|
||||
export default function areAllImageOrientationsEqual(instances: Array<any>): boolean {
|
||||
if (!instances?.length) {
|
||||
return false;
|
||||
}
|
||||
const firstImage = instances[0];
|
||||
const firstImageOrientationPatient = toNumber(
|
||||
firstImage.ImageOrientationPatient
|
||||
);
|
||||
const firstImageOrientationPatient = toNumber(firstImage.ImageOrientationPatient);
|
||||
|
||||
for (let i = 1; i < instances.length; i++) {
|
||||
const instance = instances[i];
|
||||
const imageOrientationPatient = toNumber(instance.ImageOrientationPatient);
|
||||
|
||||
if (
|
||||
!_isSameOrientation(imageOrientationPatient, firstImageOrientationPatient)
|
||||
) {
|
||||
if (!_isSameOrientation(imageOrientationPatient, firstImageOrientationPatient)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,7 @@ function _checkSeriesPositionShift(
|
||||
scanAxisNormal,
|
||||
averageSpacingBetweenFrames
|
||||
);
|
||||
return (
|
||||
vec3.distance(actualPosition, predictedPosition) >
|
||||
averageSpacingBetweenFrames
|
||||
);
|
||||
return vec3.distance(actualPosition, predictedPosition) > averageSpacingBetweenFrames;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,24 +33,17 @@ function _checkSeriesPositionShift(
|
||||
* @param {*} instances
|
||||
* @returns
|
||||
*/
|
||||
export default function areAllImagePositionsEqual(
|
||||
instances: Array<any>
|
||||
): boolean {
|
||||
export default function areAllImagePositionsEqual(instances: Array<any>): boolean {
|
||||
if (!instances?.length) {
|
||||
return false;
|
||||
}
|
||||
const firstImageOrientationPatient = toNumber(
|
||||
instances[0].ImageOrientationPatient
|
||||
);
|
||||
const firstImageOrientationPatient = toNumber(instances[0].ImageOrientationPatient);
|
||||
const scanAxisNormal = calculateScanAxisNormal(firstImageOrientationPatient);
|
||||
const firstImagePositionPatient = toNumber(instances[0].ImagePositionPatient);
|
||||
const lastIpp = toNumber(
|
||||
instances[instances.length - 1].ImagePositionPatient
|
||||
);
|
||||
const lastIpp = toNumber(instances[instances.length - 1].ImagePositionPatient);
|
||||
|
||||
const averageSpacingBetweenFrames =
|
||||
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
|
||||
(instances.length - 1);
|
||||
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) / (instances.length - 1);
|
||||
|
||||
let previousImagePositionPatient = firstImagePositionPatient;
|
||||
for (let i = 1; i < instances.length; i++) {
|
||||
|
||||
@@ -20,13 +20,10 @@ export default function areAllImageSpacingEqual(
|
||||
return;
|
||||
}
|
||||
const firstImagePositionPatient = toNumber(instances[0].ImagePositionPatient);
|
||||
const lastIpp = toNumber(
|
||||
instances[instances.length - 1].ImagePositionPatient
|
||||
);
|
||||
const lastIpp = toNumber(instances[instances.length - 1].ImagePositionPatient);
|
||||
|
||||
const averageSpacingBetweenFrames =
|
||||
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
|
||||
(instances.length - 1);
|
||||
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) / (instances.length - 1);
|
||||
|
||||
let previousImagePositionPatient = firstImagePositionPatient;
|
||||
|
||||
@@ -40,10 +37,7 @@ export default function areAllImageSpacingEqual(
|
||||
previousImagePositionPatient
|
||||
);
|
||||
|
||||
const spacingIssue = _getSpacingIssue(
|
||||
spacingBetweenFrames,
|
||||
averageSpacingBetweenFrames
|
||||
);
|
||||
const spacingIssue = _getSpacingIssue(spacingBetweenFrames, averageSpacingBetweenFrames);
|
||||
|
||||
if (spacingIssue) {
|
||||
const issue = spacingIssue.issue;
|
||||
|
||||
@@ -10,14 +10,9 @@ import { DisplaySetMessage, DisplaySetMessageList } from '@ohif/core';
|
||||
* @param {*} multiFrameInstance
|
||||
* @param {*} warnings
|
||||
*/
|
||||
export default function checkMultiFrame(
|
||||
multiFrameInstance,
|
||||
messages: DisplaySetMessageList
|
||||
): void {
|
||||
export default function checkMultiFrame(multiFrameInstance, messages: DisplaySetMessageList): void {
|
||||
if (!hasPixelMeasurements(multiFrameInstance)) {
|
||||
messages.addMessage(
|
||||
DisplaySetMessage.CODES.MULTIFRAME_NO_PIXEL_MEASUREMENTS
|
||||
);
|
||||
messages.addMessage(DisplaySetMessage.CODES.MULTIFRAME_NO_PIXEL_MEASUREMENTS);
|
||||
}
|
||||
|
||||
if (!hasOrientation(multiFrameInstance)) {
|
||||
@@ -25,8 +20,6 @@ export default function checkMultiFrame(
|
||||
}
|
||||
|
||||
if (!hasPosition(multiFrameInstance)) {
|
||||
messages.addMessage(
|
||||
DisplaySetMessage.CODES.MULTIFRAME_NO_POSITION_INFORMATION
|
||||
);
|
||||
messages.addMessage(DisplaySetMessage.CODES.MULTIFRAME_NO_POSITION_INFORMATION);
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,7 @@ export default function checkSingleFrames(
|
||||
}
|
||||
|
||||
if (!areAllImagePositionsEqual(instances)) {
|
||||
messages.addMessage(
|
||||
DisplaySetMessage.CODES.INCONSISTENT_POSITION_INFORMATION
|
||||
);
|
||||
messages.addMessage(DisplaySetMessage.CODES.INCONSISTENT_POSITION_INFORMATION);
|
||||
}
|
||||
areAllImageSpacingEqual(instances, messages);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user