Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
delphine committed Jul 27, 2023
1 parent 02cf83d commit 6532378
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 79 deletions.
14 changes: 4 additions & 10 deletions FrontEnd/src/components/AutoQuery/AutoQueryRoot.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useState } from 'react'
import { Button } from 'react-bootstrap'
import { useSelector } from 'react-redux'

import QueryRoot from './Query/QueryRoot'
import ResultsRoot from './Results/ResultsRoot'
import MyRobotRoot from './MyRobot/MyRobotRoot'
import RobotHistoryRoot from './RobotHistory/RobotHistoryRoot'
import { useSelector } from 'react-redux'
import apis from '../../services/apis'
import Spinner from '../CommonComponents/Spinner'
import { useCustomQuery } from '../../services/ReactQuery/hooks'
Expand Down Expand Up @@ -45,20 +46,13 @@ export default () => {
const TAB_ROBOT_HISTORY = 'history'

const [currentComponent, setCurrentComponent] = useState(TAB_QUERIES)
const [currentComponentLevel, setCurrentComponentLevel] = useState('studies')

const getComponentToDisplay = () => {
switch (currentComponent) {
case TAB_QUERIES:
return <QueryRoot onQueryFinished={(level) => {
console.log('onQueryFinished')
console.log(level)
setCurrentComponent(TAB_RESULTS)
setCurrentComponentLevel(level)
}}
/>
return <QueryRoot onQueryFinished={() => setCurrentComponent(TAB_RESULTS)} />
case TAB_RESULTS:
return <ResultsRoot onRobotCreated={() => setCurrentComponent(TAB_MYROBOT)} currentComponentRoot={currentComponentLevel}/>
return <ResultsRoot onRobotCreated={() => setCurrentComponent(TAB_MYROBOT)} />
case TAB_MYROBOT:
return <MyRobotWrapper />
case TAB_ROBOT_HISTORY:
Expand Down
227 changes: 191 additions & 36 deletions FrontEnd/src/components/AutoQuery/Query/QueryRoot.jsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,202 @@
import React, { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'

import { Button, Container, Modal, Row } from 'react-bootstrap'
import QueryStudies from './QueryStudies'
import QuerySeries from './QuerySeries'
import moment from 'moment'

export default ((onQueryFinished) => {
const QUERY_STUDIES = 'studies'
const QUERY_SERIES = 'series'
import Spinner from '../../CommonComponents/Spinner'
import CsvLoader from './CsvLoader'
import QueryTable from './QueryTable'

const [currentComponent, setCurrentComponent] = useState(QUERY_STUDIES)
import { addRow, emptyQueryTable, removeQuery } from '../../../actions/TableQuery'
import { useCustomQuery } from '../../../services/ReactQuery/hooks'
import apis from '../../../services/apis'
import { keys } from '../../../model/Constant'
import { exportCsv } from '../../../tools/CSVExport'
import { dissmissToast, errorMessage, infoMessage, successMessage, updateToastMessage } from '../../../tools/toastify'
import { addStudyResult } from '../../../actions/TableResult'
import EditQueries from './EditQueries'

const getComponentToDisplay = () => {
switch (currentComponent) {
case QUERY_STUDIES:
return <QueryStudies onQueryStudiesFinished={() => onQueryFinished('studies')}/>
case QUERY_SERIES:
return <QuerySeries onQuerySeriesFinished={() => onQueryFinished('series')}/>
export default ({ onQueryFinished }) => {

const [openEditModal, setOpenEditModal] = useState(false)
const [currentRow, setCurrentRow] = useState(null)
const [selectedRowsIds, setSelectedRowsIds] = useState([])

const dispatch = useDispatch()

const { data: aets, isLoading } = useCustomQuery(
[keys.AETS_KEY],
() => apis.aets.getAets(),
undefined
)
const queries = useSelector(state => state.AutoRetrieveQueryList.queries)

const onRowClick = (rowId) => {
setCurrentRow(rowId)
}

const removeRows = () => {
dispatch(removeQuery(selectedRowsIds));
}

const emptyTable = () => {
dispatch(emptyQueryTable())
}

const onSelectRowsChange = (rowIds) => {
setSelectedRowsIds(rowIds)
}

const onCSVDownload = () => {
let data = queries.map(row => {
let formattedDateFrom = typeof row.DateFrom === 'string' ? moment(new Date(row.DateFrom)).format('YYYYMMDD') : ''
let formattedDateTo = typeof row.DateTo === 'string' ? moment(new Date(row.DateTo)).format('YYYYMMDD') : ''
return {
'Patient Name': row.PatientName,
'Patient ID': row.PatientID,
'Accession Number': row.AccessionNumber,
'Date From': formattedDateFrom,
'Date To': formattedDateTo,
'Study Description': row.StudyDescription,
'Modalities': row.ModalitiesInStudy,
'AET': row.Aet
}
})
exportCsv(data, 'csv', 'queries.csv')
}

const makeDicomQuery = async (queryParams) => {

let DateFrom = queryParams.DateFrom ? queryParams.DateFrom : null
let DateTo = queryParams.DateTo ? queryParams.DateTo : null

//Prepare Date string for post data
let DateString = '';
if (DateFrom !== null && DateTo !== null) {
DateString = DateFrom + '-' + DateTo
} else if (DateFrom === null && DateTo !== null) {
DateString = '-' + DateTo
} else if (DateFrom !== null && DateTo === null) {
DateString = DateFrom + '-'
}

//Prepare POST payload for query (follow Orthanc APIs)
let queryPost = {
Level: 'Study',
Query: {
PatientName: queryParams.PatientName,
PatientID: queryParams.PatientID,
StudyDate: DateString,
ModalitiesInStudy: queryParams.ModalitiesInStudy,
StudyDescription: queryParams.StudyDescription,
AccessionNumber: queryParams.AccessionNumber,
NumberOfStudyRelatedInstances: '',
NumberOfStudyRelatedSeries: ''
}
}

//Call Orthanc API to make Query
let createQueryRessource = await apis.query.dicomQuery(queryParams.Aet, queryPost)
//Call OrthancToolsJS API to get a parsed answer of the results
return await apis.query.retrieveAnswer(createQueryRessource.ID)
}

const areAllRowsAetDefined = (data) => {
for (let i = 0; i < data.length; i++) {
if (data[i].Aet == null || data[i].Aet == "") {
errorMessage('Missing AET in row ' + i + ' fill it before querying')
return false
}
}
return true
}

const onQueryHandle = async () => {

const data = queries;

if (!areAllRowsAetDefined(data)) return

const toastId = infoMessage('Starting Studies Queries')

let i = 1
for (const query of data) {
i = i++
updateToastMessage(toastId, 'Query study ' + i + '/' + data.length)
//For each line make dicom query and return results
try {
let answeredResults = await makeDicomQuery(query)
//For each results, fill the result table through Redux
answeredResults.forEach((answer) => {
dispatch(addStudyResult(answer))
})
} catch (err) {
console.error(err)
}

}

dissmissToast(toastId)
successMessage('Queries completed')
onQueryFinished()
}

if (isLoading) return <Spinner />

return (
<Container fluid>
<Row className='mb-5'>
<nav className="otjs-navmenu container-fluid">
<div className="otjs-navmenu-nav">
<li className='col-4 text-center'>
<Button
className={currentComponent === QUERY_STUDIES ? 'otjs-navmenu-nav-link link-button-active link-button' : 'otjs-navmenu-nav-link link-button'}
onClick={() => setCurrentComponent(QUERY_STUDIES)}>Studies Level
</Button>
</li>
<li className='col-4 text-center'>
<Button
className={currentComponent === QUERY_SERIES ? 'otjs-navmenu-nav-link link-button-active link-button' : 'otjs-navmenu-nav-link link-button'}
onClick={() => setCurrentComponent(QUERY_SERIES)}>Series Level
</Button>
</li>
</div>
</nav>
</Row>
<Row>
{getComponentToDisplay()}
</Row>
</Container>
<>
<Modal size='xl' show={openEditModal} onHide={()=>setOpenEditModal(false)}>
<Modal.Header closeButton/>
<Modal.Title>Edit Queries</Modal.Title>
<Modal.Body>
<EditQueries aets={aets} selectedRowsIds={selectedRowsIds}/>
</Modal.Body>
</Modal>
<Container fluid>
<Row>
<CsvLoader />
</Row>
<Row className="m-3 d-flex justify-content-around">
<Button className="otjs-button otjs-button-blue w-10"
onClick={() => dispatch(addRow())}>
Add Query
</Button>
<Button onClick={onCSVDownload} className="otjs-button otjs-button-blue w-10">
Export CSV
</Button>
<Button className="otjs-button otjs-button-orange w-10"
onClick={() => setOpenEditModal(true)} >
Edit Selected
</Button>
<Button className="otjs-button otjs-button-orange w-10"
onClick={removeRows} >
Delete Selected
</Button>
<Button className="otjs-button otjs-button-red w-10"
onClick={emptyTable} >
Empty Table
</Button>
</Row>
<Row>
<QueryTable
queries={queries}
aets={aets}
currentRow={currentRow}
onRowClick={onRowClick}
onSelectRowsChange={onSelectRowsChange}
selectedRowIds={selectedRowsIds}
/>
</Row>
<Row className="d-flex justify-content-center mt-5">
<Button
className="otjs-button otjs-button-blue"
onClick={onQueryHandle}
>
Query
</Button>
</Row>
</Container>
</>
)
})
}
29 changes: 3 additions & 26 deletions FrontEnd/src/components/AutoQuery/Query/QuerySeries.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import EditQueries from './EditQueries'
import QueryTableSeries from './QueryTableSeries'
import { addSeriesDetails } from '../../../actions/TableResult'

export default ((onQuerySeriesFinished)=> {
export default ({onQuerySeriesFinished}) => {

const [openEditModal, setOpenEditModal] = useState(false)
const [currentRow, setCurrentRow] = useState(null)
Expand Down Expand Up @@ -48,26 +48,6 @@ export default ((onQuerySeriesFinished)=> {
setSelectedRowsIds(rowIds)
}

const onCSVDownload = () => {
let result = Object.values(data).map(row => {
return {
'Patient Name': row.PatientName,
'Patient ID': row.PatientID,
'Accession Number': row.AccessionNumber,
'Study Date': row.StudyDate,
'Study Description': row.StudyDescription ,
'Requested Procedure': row.RequestedProcedureDescription,
'Study Instance UID' : row.StudyInstanceUID,
'Series Instance UID': row.SeriesInstanceUID,
'Series Description': row.SeriesDescription,
'Modalities': row.Modality,
'Number of Instances': row.NumberOfSeriesRelatedInstances,
'AET': row.OriginAET
}
})
exportCsv(result, 'csv', 'queriesSeries.csv')
}

const makeDicomQuery = async (queryParams) => {
console.log("queryParams :", queryParams)
//Prepare POST payload for query (follow Orthanc APIs)
Expand Down Expand Up @@ -144,9 +124,6 @@ export default ((onQuerySeriesFinished)=> {
onClick={() => dispatch(addRow())}>
Add Query
</Button>
<Button onClick={onCSVDownload} className="otjs-button otjs-button-blue w-10">
Export CSV
</Button>
<Button className="otjs-button otjs-button-orange w-10"
onClick={() => setOpenEditModal(true)} >
Edit Selected
Expand Down Expand Up @@ -174,10 +151,10 @@ export default ((onQuerySeriesFinished)=> {
className="otjs-button otjs-button-blue"
onClick={onQueryHandle}
>
Query
Search
</Button>
</Row>
</Container>
</>
)
})
}
13 changes: 9 additions & 4 deletions FrontEnd/src/components/AutoQuery/Query/QueryTableSeries.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import React from 'react'
import { useDispatch } from 'react-redux'

import moment from 'moment'

import SelectModalities from "../../CommonComponents/SearchForm/SelectModalities"
import CommonTableV8 from "../../CommonComponents/RessourcesDisplay/ReactTableV8/CommonTableV8"
import { editCellQuery } from '../../../actions/TableQuery'

export default ({ queries = [], onRowClick, currentRow, onSelectRowsChange, selectedRowIds }) => {
export default ({ queries = [], aets = [], onRowClick, currentRow, onSelectRowsChange, selectedRowIds }) => {

const dispatch = useDispatch()

Expand Down Expand Up @@ -35,6 +32,14 @@ export default ({ queries = [], onRowClick, currentRow, onSelectRowsChange, sele
accessorKey: 'SeriesInstanceUID',
header: 'SeriesInstanceUID',
isEditable: true
}, {
accessorKey: 'Aet',
header: 'AET',
isEditable: true,
editionProperties: {
type: 'SELECT',
options: aets.map(aet => ({ value: aet, label: aet }))
}
}]

return (
Expand Down
4 changes: 2 additions & 2 deletions FrontEnd/src/components/AutoQuery/Results/ResultsRoot.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import ResultsSeries from './ResultsSeries'
import ResultsStudies from './ResultsStudies'
import CreateRobot from './CreateRobot'

export default ({onRobotCreated, currentComponentRoot}) => {
export default ({onRobotCreated}) => {

const RESULTS_STUDIES = 'studies'
const RESULTS_SERIES = 'series'

const [currentComponent, setCurrentComponent] = useState(currentComponentRoot)
const [currentComponent, setCurrentComponent] = useState(RESULTS_STUDIES)

const getComponentToDisplay = () => {
switch (currentComponent) {
Expand Down
Loading

0 comments on commit 6532378

Please sign in to comment.