diff --git a/docker-compose/Dockerfile b/docker-compose/Dockerfile index 147d41f1c3..5ed4cac68f 100644 --- a/docker-compose/Dockerfile +++ b/docker-compose/Dockerfile @@ -311,7 +311,7 @@ CMD [] ################ nginx ##################################################### -FROM ohif/viewer:v4.12.32.19362 AS nginx-viewer +FROM ohif/app:v3.9.0-beta.64 AS nginx-viewer FROM nginx as nginx diff --git a/docker-compose/nginx/viewer/app-config.js b/docker-compose/nginx/viewer/app-config.js index 52ce144b9c..9adbb6befa 100644 --- a/docker-compose/nginx/viewer/app-config.js +++ b/docker-compose/nginx/viewer/app-config.js @@ -1,35 +1,31 @@ -window.config = { - // default: '/' - routerBasename: '/', - extensions: ['cornerstone', 'dicom-p10-downloader'], - showStudyList: true, - filterQueryParam: false, - disableServersCache: false, - studyPrefetcher: { - enabled: true, - order: 'closest', - displaySetCount: 3, - preventCache: false, - prefetchDisplaySetsTimeout: 300, - displayProgress: true, - includeActiveDisplaySet: true, - }, - servers: { - dicomWeb: [ - { - name: 'SHANOIR-NG', - wadoUriRoot: 'SHANOIR_VIEWER_OHIF_URL_SCHEME://SHANOIR_VIEWER_OHIF_URL_HOST/shanoir-ng/', - qidoRoot: 'SHANOIR_VIEWER_OHIF_URL_SCHEME://SHANOIR_VIEWER_OHIF_URL_HOST/dicomweb', - wadoRoot: 'SHANOIR_VIEWER_OHIF_URL_SCHEME://SHANOIR_VIEWER_OHIF_URL_HOST/dicomweb', - qidoSupportsIncludeField: true, - imageRendering: 'wadors', - thumbnailRendering: 'wadors', - enableStudyLazyLoad: true, - supportsFuzzyMatching: true, - }, - ], - }, - oidc: [ +window.config = { + routerBasename: '/', + extensions: [], + modes: [], + showStudyList: true, + dataSources: [ + { + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'dicomweb', + configuration: { + friendlyName: 'SHANOIR-NG', + name: 'SHANOIR-NG', + wadoUriRoot: 'SHANOIR_VIEWER_OHIF_URL_SCHEME://SHANOIR_VIEWER_OHIF_URL_HOST/shanoir-ng/', + qidoRoot: 'SHANOIR_VIEWER_OHIF_URL_SCHEME://SHANOIR_VIEWER_OHIF_URL_HOST/dicomweb', + wadoRoot: 'SHANOIR_VIEWER_OHIF_URL_SCHEME://SHANOIR_VIEWER_OHIF_URL_HOST/dicomweb', + qidoSupportsIncludeField: true, + supportsReject: true, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: true, + supportsWildcard: true, + omitQuotationForMultipartRequest: false, + }, + }, + ], + defaultDataSourceName: 'dicomweb', + oidc: [ { // ~ REQUIRED // Authorization Server URL @@ -43,111 +39,5 @@ window.config = { // ~ OPTIONAL post_logout_redirect_uri: 'SHANOIR_URL_SCHEME://SHANOIR_URL_HOST' } - ], - // Extensions should be able to suggest default values for these? - // Or we can require that these be explicitly set - hotkeys: [ - // ~ Global - { - commandName: 'incrementActiveViewport', - label: 'Next Viewport', - keys: ['right'], - }, - { - commandName: 'decrementActiveViewport', - label: 'Previous Viewport', - keys: ['left'], - }, - // Supported Keys: https://craig.is/killing/mice - // ~ Cornerstone Extension - { commandName: 'rotateViewportCW', label: 'Rotate Right', keys: ['r'] }, - { commandName: 'rotateViewportCCW', label: 'Rotate Left', keys: ['l'] }, - { commandName: 'invertViewport', label: 'Invert', keys: ['i'] }, - { - commandName: 'flipViewportVertical', - label: 'Flip Horizontally', - keys: ['h'], - }, - { - commandName: 'flipViewportHorizontal', - label: 'Flip Vertically', - keys: ['v'], - }, - { commandName: 'scaleUpViewport', label: 'Zoom In', keys: ['+'] }, - { commandName: 'scaleDownViewport', label: 'Zoom Out', keys: ['-'] }, - { commandName: 'fitViewportToWindow', label: 'Zoom to Fit', keys: ['='] }, - { commandName: 'resetViewport', label: 'Reset', keys: ['space'] }, - // clearAnnotations - { commandName: 'nextImage', label: 'Next Image', keys: ['down'] }, - { commandName: 'previousImage', label: 'Previous Image', keys: ['up'] }, - // firstImage - // lastImage - { - commandName: 'previousViewportDisplaySet', - label: 'Previous Series', - keys: ['pagedown'], - }, - { - commandName: 'nextViewportDisplaySet', - label: 'Next Series', - keys: ['pageup'], - }, - // ~ Cornerstone Tools - { commandName: 'setZoomTool', label: 'Zoom', keys: ['z'] }, - // ~ Window level presets - { - commandName: 'windowLevelPreset1', - label: 'W/L Preset 1', - keys: ['1'], - }, - { - commandName: 'windowLevelPreset2', - label: 'W/L Preset 2', - keys: ['2'], - }, - { - commandName: 'windowLevelPreset3', - label: 'W/L Preset 3', - keys: ['3'], - }, - { - commandName: 'windowLevelPreset4', - label: 'W/L Preset 4', - keys: ['4'], - }, - { - commandName: 'windowLevelPreset5', - label: 'W/L Preset 5', - keys: ['5'], - }, - { - commandName: 'windowLevelPreset6', - label: 'W/L Preset 6', - keys: ['6'], - }, - { - commandName: 'windowLevelPreset7', - label: 'W/L Preset 7', - keys: ['7'], - }, - { - commandName: 'windowLevelPreset8', - label: 'W/L Preset 8', - keys: ['8'], - }, - { - commandName: 'windowLevelPreset9', - label: 'W/L Preset 9', - keys: ['9'], - }, - ], - cornerstoneExtensionConfig: {}, - // Following property limits number of simultaneous series metadata requests. - // For http/1.x-only servers, set this to 5 or less to improve - // on first meaningful display in viewer - // If the server is particularly slow to respond to series metadata - // requests as it extracts the metadata from raw files everytime, - // try setting this to even lower value - // Leave it undefined for no limit, suitable for HTTP/2 enabled servers - // maxConcurrentMetadataRequests: 5, + ] }; diff --git a/docker-compose/nginx/viewer/ohif-viewer.template.conf b/docker-compose/nginx/viewer/ohif-viewer.template.conf index e61e473eca..dfb6989cb6 100644 --- a/docker-compose/nginx/viewer/ohif-viewer.template.conf +++ b/docker-compose/nginx/viewer/ohif-viewer.template.conf @@ -26,7 +26,6 @@ location / try_files $uri $uri/ /index.html; #more_set_headers 'X-Frame-Options: SAMEORIGIN'; - add_header 'X-Frame-Options' 'SAMEORIGIN'; } # Directly root to MS-Datasets, to avoid CORS-errors from OHIF Viewer diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dicom/web/DICOMWebApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dicom/web/DICOMWebApiController.java index 298166ddc1..167c4b2e2d 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dicom/web/DICOMWebApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dicom/web/DICOMWebApiController.java @@ -1,10 +1,10 @@ package org.shanoir.ng.dicom.web; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + import org.apache.commons.lang3.StringUtils; import org.shanoir.ng.dicom.web.service.DICOMWebService; import org.shanoir.ng.examination.model.Examination; @@ -14,14 +14,19 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import java.util.Iterator; -import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import jakarta.servlet.http.HttpServletRequest; @Controller public class DICOMWebApiController implements DICOMWebApi { @@ -47,13 +52,34 @@ public ResponseEntity findPatients() throws RestServiceException { @Override public ResponseEntity findStudies(Map allParams) throws RestServiceException, JsonMappingException, JsonProcessingException { + Page examinations = null; int offset = Integer.valueOf(allParams.get("offset")); int limit = Integer.valueOf(allParams.get("limit")); Pageable pageable = PageRequest.of(offset, limit); - // Search for studies with patient name (DICOM patientID does not make sense in case of Shanoir) + // 1. Search for studies with patient name + // (DICOM patientID does not make sense in case of Shanoir) String patientName = allParams.get("PatientName"); - Page examinations = examinationService.findPage(pageable, patientName); - if (examinations.getContent().isEmpty()) { + if (patientName != null) { + examinations = examinationService.findPage(pageable, patientName); + } else { + // 2. Try StudyInstanceUIDs, in case no patient name + // Manage already multiple study instance UIDs + String studyInstanceUIDs = allParams.get("StudyInstanceUIDs"); + if (studyInstanceUIDs != null) { + List examinationList = new ArrayList<>(); + String[] studyInstanceUIDArray = studyInstanceUIDs.split(","); + for(String studyInstanceUID : studyInstanceUIDArray) { + String examinationIdString = studyInstanceUID.substring(studyInstanceUID.lastIndexOf(".") + 1, studyInstanceUID.length()); + Examination examination = examinationService.findById(Long.valueOf(examinationIdString)); + examinationList.add(examination); + } + examinations = new PageImpl<>(examinationList); + // 3. No param, return page of examinations in db + } else { + examinations = examinationService.findPage(pageable, null); + } + } + if (examinations == null || examinations.getContent().isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } StringBuffer studies = new StringBuffer(); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java index 634db30d5f..84cde218bc 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java @@ -154,8 +154,8 @@ public Page findPage(final Pageable pageable, boolean preclinical, @Override public Page findPage(final Pageable pageable, String patientName) { - if (patientName.length() > 64) { - throw new IllegalArgumentException("A patient name cannot be longer than 64 chars, it exceed the data representation limit"); + if (patientName != null && patientName.length() > 64) { + throw new IllegalArgumentException("A patient name cannot be longer than 64 chars, it exceed the data representation limit."); } if (KeycloakUtil.getTokenRoles().contains("ROLE_ADMIN")) { if (StringUtils.isNotEmpty(patientName)) { diff --git a/shanoir-ng-front/src/app/examinations/examination/examination.component.ts b/shanoir-ng-front/src/app/examinations/examination/examination.component.ts index d30a76fb5e..b8ed193432 100644 --- a/shanoir-ng-front/src/app/examinations/examination/examination.component.ts +++ b/shanoir-ng-front/src/app/examinations/examination/examination.component.ts @@ -161,7 +161,7 @@ export class ExaminationComponent extends EntityComponent { } openViewer() { - window.open(environment.viewerUrl + '/viewer/1.4.9.12.34.1.8527.' + this.entity.id, '_blank'); + window.open(environment.viewerUrl + '/viewer?StudyInstanceUIDs=1.4.9.12.34.1.8527.' + this.entity.id, '_blank'); } getCenters(): void { diff --git a/shanoir-ng-front/src/app/solr/solr.search.component.ts b/shanoir-ng-front/src/app/solr/solr.search.component.ts index 13ed803f2a..64cc29e386 100644 --- a/shanoir-ng-front/src/app/solr/solr.search.component.ts +++ b/shanoir-ng-front/src/app/solr/solr.search.component.ts @@ -459,7 +459,7 @@ export class SolrSearchComponent implements AfterViewChecked, AfterContentInit { {headerName: "OHIF Viewer", type: "button", awesome: "fa-solid fa-up-right-from-square", condition: item => (item.datasetType.includes("MR") || item.datasetType.includes("Pet") || item.datasetType.includes("Ct")), action: item => { - window.open(environment.viewerUrl + '/viewer/1.4.9.12.34.1.8527.' + item.examinationId, '_blank'); + window.open(environment.viewerUrl + '/viewer?StudyInstanceUIDs=1.4.9.12.34.1.8527.' + item.examinationId, '_blank'); } } ];