Update viewer with Study List and routing to Viewer

This commit is contained in:
Erik Ziegler 2018-12-31 13:26:38 +01:00
parent 4fbc208b41
commit 5d6aad488e
15 changed files with 254 additions and 27 deletions

View File

@ -13,7 +13,7 @@
"hammerjs": "^2.0.8",
"lodash.debounce": "^4.0.8",
"moment": "^2.22.2",
"ohif-core": "^0.1.1",
"ohif-core": "^0.1.2",
"prop-types": "^15.6.2",
"query-string": "^6.2.0",
"react": "^16.4.1",
@ -23,7 +23,7 @@
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-scripts": "^2.1.1",
"react-viewerbase": "^0.1.5",
"react-viewerbase": "^0.1.6",
"redux": "^4.0.1"
},
"scripts": {

View File

@ -2,13 +2,13 @@ import React, { Component } from 'react';
import { withRouter } from 'react-router';
import { Route, Switch } from 'react-router-dom';
import ViewerRouting from "./ViewerRouting.js";
import StudyListRouting from './StudyListRouting.js';
import StandaloneRouting from './StandaloneRouting.js';
import IHEInvokeImageDisplay from './IHEInvokeImageDisplay.js';
import './App.css';
import './variables.css';
// TODO: figure out how to change themes dynamically
import './theme-tide.css';
import { StudyList } from 'react-viewerbase';
const reload = () => window.location.reload();
@ -43,14 +43,14 @@ class App extends Component {
<Route
exact
path="/studylist"
component={StudyList}
component={StudyListRouting}
/*auth={this.props.auth}*/
store={this.props.store}
/>
<Route
exact
path="/"
component={StudyList}
component={StudyListRouting}
/*auth={this.props.auth}*/
store={this.props.store}
/>

View File

@ -0,0 +1,21 @@
import { connect } from 'react-redux';
import StudyListWithData from './StudyListWithData.js';
const isActive = (a) => a.active === true;
const mapStateToProps = (state, ownProps) => {
const activeServer = state.servers.servers.find(isActive);
return {
server: activeServer,
...ownProps
};
};
const ConnectedStudyList = connect(
mapStateToProps,
null
)(StudyListWithData);
export default ConnectedStudyList;

View File

@ -0,0 +1,16 @@
import { connect } from 'react-redux';
import FlexboxLayout from './FlexboxLayout';
const mapStateToProps = state => {
return {
leftSidebarOpen: state.ui.leftSidebarOpen,
rightSidebarOpen: state.ui.rightSidebarOpen
};
};
const ConnectedFlexboxLayout = connect(
mapStateToProps,
null
)(FlexboxLayout);
export default ConnectedFlexboxLayout;

View File

@ -0,0 +1,29 @@
import { connect } from 'react-redux';
import ToolbarRow from './ToolbarRow';
import { setLeftSidebarOpen, setRightSidebarOpen } from '../redux/actions.js';
const mapStateToProps = state => {
return {
leftSidebarOpen: state.ui.leftSidebarOpen,
rightSidebarOpen: state.ui.rightSidebarOpen
};
};
const mapDispatchToProps = dispatch => {
return {
setLeftSidebarOpen: state => {
dispatch(setLeftSidebarOpen(state))
},
setRightSidebarOpen: state => {
dispatch(setRightSidebarOpen(state))
}
};
};
const ConnectedToolbarRow = connect(
mapStateToProps,
mapDispatchToProps
)(ToolbarRow);
export default ConnectedToolbarRow;

View File

@ -1,6 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import OHIF from 'ohif-core';
import cornerstone from 'cornerstone-core';
import { StudyBrowser } from 'react-viewerbase';
import ViewerMain from './ViewerMain.js';
@ -81,12 +80,12 @@ class FlexboxLayout extends Component {
}
getThumbnailsFromImageIds() {
const studyThumbnails = this.state.studiesForBrowser.forEach(function (study) {
this.state.studiesForBrowser.forEach(function (study) {
const { studyInstanceUid } = study;
study.thumbnails.forEach(function (displaySet) {
const imageId = displaySet.imageId;
const {displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames} = displaySet;
const { displaySetInstanceUid } = displaySet;
cornerstone.loadAndCacheImage(imageId).then((image) => {
loadSuccess.call(this, image, studyInstanceUid, displaySetInstanceUid);

View File

@ -5,7 +5,7 @@ import ViewerFromStudyData from "./ViewerFromStudyData.js";
function IHEInvokeImageDisplay({ match }) {
const requestType = match.params.query.requestType;
let studyInstanceUids;
let patientUids;
//let patientUids;
let displayStudyList = false;
if (requestType === "STUDY") {
@ -15,7 +15,7 @@ function IHEInvokeImageDisplay({ match }) {
const decodedData = window.atob(uids);
studyInstanceUids = decodedData.split(';');
} else if (requestType === "PATIENT") {
patientUids = this.params.query.patientID.split(';');
//patientUidspatientUids = this.params.query.patientID.split(';');
displayStudyList = true
} else {
displayStudyList = true

View File

@ -0,0 +1,24 @@
import React from "react";
import PropTypes from "prop-types";
import ConnectedStudyList from "./ConnectedStudyList";
// TODO: Move to react-viewerbase
function StudyListRouting({ match }) {
// TODO: Figure out which filters we want to pass in via a URL
//const { patientId } = match.params;
return (
<ConnectedStudyList/>
);
}
StudyListRouting.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({
patientIds: PropTypes.string,
})
})
};
export default StudyListRouting;

View File

@ -0,0 +1,120 @@
import React, {Component} from "react";
import PropTypes from "prop-types";
import OHIF from 'ohif-core';
import { withRouter } from 'react-router-dom';
import { StudyList } from "react-viewerbase";
import Header from "./Header";
class StudyListWithData extends Component {
constructor(props) {
super(props);
this.state = {
searchData: {},
studies: null,
error: null
};
this.pageSize = 5;
this.defaultSort = { field: 'patientName', order: 'desc' };
}
componentDidMount() {
// TODO: Avoid using timepoints here
//const params = { studyInstanceUids, seriesInstanceUids, timepointId, timepointsFilter={} };
this.searchForStudies();
}
searchForStudies = (searchData = {
currentPage: 0,
pageSize: 5
}) => {
const { server } = this.props;
console.log(searchData);
const filter = {
patientId: searchData.patientId,
patientName: searchData.patientName,
accessionNumber: searchData.accessionNumber,
studyDescription: searchData.studyDescription,
modalitiesInStudy: searchData.modalitiesInStudy,
limit: searchData.currentPage * searchData.pageSize + searchData.pageSize,
offset: searchData.currentPage * searchData.pageSize
};
// TODO: add sorting
const promise = OHIF.studies.searchStudies(server, filter)
// Render the viewer when the data is ready
promise.then(studies => {
if (!studies) {
studies = [];
}
// TODO: Fix casing of this property!
const fixedStudies = studies.map(study => {
const fixedStudy = study;
fixedStudy.studyInstanceUID = study.studyInstanceUid;
return fixedStudy;
})
this.setState({
studies: fixedStudies,
});
}).catch(error => {
this.setState({
error: true,
});
console.error(error);
});
}
onImport = () => {
console.log('onImport');
}
onSelectItem = (studyInstanceUID) => {
console.log('onSelectItem');
console.log('studyInstanceUID');
this.props.history.push(`/viewer/${studyInstanceUID}`);
}
onSearch = (searchData) => {
// TODO: Update search filters
this.searchForStudies(searchData);
}
render() {
if (this.state.error) {
return (<div>Error: {JSON.stringify(this.state.error)}</div>);
} else if (this.state.studies === null) {
return (<div>Loading...</div>);
}
const studyCount = this.state.studies ? this.state.studies.length : 0;
return (<>
<Header />
<StudyList studies={this.state.studies}
studyCount={studyCount}
studyListFunctionsEnabled={false}
onImport={this.onImport}
onSelectItem={this.onSelectItem}
pageSize={this.pageSize}
defaultSort={this.defaultSort}
onSearch={this.onSearch} />
</>
);
}
}
StudyListWithData.propTypes = {
patientId: PropTypes.string,
server: PropTypes.object
};
export default withRouter(StudyListWithData);

View File

@ -3,7 +3,7 @@ import PropTypes from "prop-types";
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
import OHIF from 'ohif-core';
import { CineDialog } from 'react-viewerbase';
//import { CineDialog } from 'react-viewerbase';
import Header from '../Header'
import ConnectedFlexboxLayout from '../FlexboxLayout/ConnectedFlexboxLayout.js';

View File

@ -8,7 +8,7 @@ class Dropdown extends Component {
}
renderList = () => {
const { list, link, align } = this.props
const { list, align } = this.props
if (!this.state.open) {
return null
@ -80,8 +80,6 @@ class Dropdown extends Component {
}
render() {
const { open } = this.state
return (
<div className='dd-menu' ref={node => this.node = node}>
<div className="dd-menu-toggle" onClick={this.toggleList}>

View File

@ -18,9 +18,12 @@ const servers = {
dicomWeb: [
{
"name": "DCM4CHEE",
"wadoUriRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/wado",
"qidoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs",
"wadoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs",
//"wadoUriRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/wado",
//"qidoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs",
//"wadoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs",
"wadoUriRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/wado",
"qidoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs",
"wadoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs",
"qidoSupportsIncludeField": true,
"imageRendering": "wadors",
"thumbnailRendering": "wadors",
@ -29,7 +32,8 @@ const servers = {
"logRequests": true,
"logResponses": false,
"logTiming": true,
"auth": "admin:admin"
//"auth": "admin:admin"
"auth": "cloud:healthcare"
}
}
]

View File

@ -0,0 +1,16 @@
export const setLeftSidebarOpen = state => ({
type: 'SET_LEFT_SIDEBAR_OPEN',
state
});
export const setRightSidebarOpen = state => ({
type: 'SET_RIGHT_SIDEBAR_OPEN',
state
});
const actions = {
setLeftSidebarOpen,
setRightSidebarOpen
};
export default actions;

View File

@ -1 +1 @@
export default 'c08d12cb36ad7b1ff310b4970a84bc7e6a14e6ed';
export default '4fbc208b41fcaa0f957a49409cb55e91c6397d03';

View File

@ -6832,10 +6832,10 @@ obuf@^1.0.0, obuf@^1.1.1:
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
ohif-core@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ohif-core/-/ohif-core-0.1.1.tgz#594b5a7969c11e5d5eaa3890a384f39409543d3b"
integrity sha512-Ib/lI9fioMiqt5DXmZ8iXtiSne8Z2ubQoh/ePAwgZPljXfMvSY1cUpQK+O90pZqBh444KDolYX00OrJIIbSRgg==
ohif-core@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ohif-core/-/ohif-core-0.1.2.tgz#897c63b73de7a139567d0a6cfe98426aa25e1cb9"
integrity sha512-KkiN14ctO3Dp4fJkjndSyUH5suBVcMVxetZdOVGrSlxWbnpCg6Hd2FPZpP6PcYPE4yhWNvUyV5dfP4Qhg14oxw==
dependencies:
cornerstone-math "^0.1.7"
isomorphic-base64 "^1.0.2"
@ -8288,10 +8288,10 @@ react-scripts@^2.1.1:
optionalDependencies:
fsevents "1.2.4"
react-viewerbase@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/react-viewerbase/-/react-viewerbase-0.1.5.tgz#a5ef7fd852ead91c74fb0068a7cb97c922a9c17a"
integrity sha512-196Req5szlhpvQWivxZmQzF3dMj2vCJtjnyusullRd8isTdaK+cLaCuKBRiJ5Eo6zwM69uTXj7ar8BHQARRzeQ==
react-viewerbase@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/react-viewerbase/-/react-viewerbase-0.1.6.tgz#09c775aed103d4382e7f24e5e0850c45327fa481"
integrity sha512-msFND25X0uooifaPf8y/VA/2yHrz+M/6yiJwh/rJD8HiM2Bel4JExDOJw9hJmmOS4l7dHpooxAI9ackibvwFAQ==
dependencies:
classnames "^2.2.6"