From 6dbd47ec4c0f38a5b2efb791ba4c009212b419fb Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Mon, 23 Sep 2024 17:19:53 +0200 Subject: [PATCH 01/24] Optimized loading of most-recent studies when the Orthanc DB supports ExtendedChanges --- WebApplication/src/components/StudyList.vue | 34 ++++++++++++++----- WebApplication/src/orthancApi.js | 8 +++++ .../src/store/modules/configuration.js | 15 ++++++++ release-notes.md | 6 ++++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/WebApplication/src/components/StudyList.vue b/WebApplication/src/components/StudyList.vue index 504d14c..a5c8a46 100644 --- a/WebApplication/src/components/StudyList.vue +++ b/WebApplication/src/components/StudyList.vue @@ -92,10 +92,11 @@ export default { studiesIds: state => state.studies.studiesIds, selectedStudiesIds: state => state.studies.selectedStudiesIds, isSearching: state => state.studies.isSearching, - statistics: state => state.studies.statistics + statistics: state => state.studies.statistics, }), ...mapGetters([ - 'studies/isFilterEmpty', // -> this['studies/isFilterEmpty'] + 'studies/isFilterEmpty', // -> this['studies/isFilterEmpty'] + 'configuration/hasExtendedChanges', // -> this['configuration/hasExtendedChanges'] ]), notShowingAllResults() { if (this.sourceType == SourceType.LOCAL_ORTHANC) { @@ -663,7 +664,7 @@ export default { this.isLoadingLatestStudies = true; this.isDisplayingLatestStudies = false; - this.loadStudiesFromChange(Math.max(0, lastChangeId - 1000), 1000); + this.loadStudiesFromChange(lastChangeId, 1000); } } else { this.shouldStopLoadingLatestStudies = true; @@ -673,9 +674,18 @@ export default { await this.$store.dispatch('studies/reloadFilteredStudies'); } }, - async loadStudiesFromChange(fromChangeId, limit) { - let changes = await api.getChanges(fromChangeId, limit); - for (let change of changes["Changes"].reverse()) { + async loadStudiesFromChange(toChangeId, limit) { + let changes; + let changesResponse; + if (this['configuration/hasExtendedChanges']) { + changesResponse = await api.getChangesExtended(toChangeId, limit, ["NewStudy", "StableStudy"]); + changes = changesResponse["Changes"]; + } else { + changesResponse = await api.getChanges(toChangeId - limit, limit); + changes = changesResponse["Changes"].reverse(); + } + + for (let change of changes) { // Take the first event we find -> we see last uploaded data immediately (NewStudy but no StableStudy). // An updated study that has received a new series is visible as well (its NewStudy might be too old but the StableStudy brings it back on top of the list) if ((change["ChangeType"] == "NewStudy" || change["ChangeType"] == "StableStudy") && !this.latestStudiesIds.has(change["ID"])) { @@ -701,8 +711,16 @@ export default { } } if (!this.shouldStopLoadingLatestStudies) { - if (fromChangeId > 0 && this.latestStudiesIds.size < this.statistics.CountStudies) { - setTimeout(() => {this.loadStudiesFromChange(Math.max(0, Math.max(0, fromChangeId - 1000)), 1000)}, 1); + if (this.latestStudiesIds.size < this.statistics.CountStudies) { + if (this['configuration/hasExtendedChanges']) { + if (!changesResponse["Done"]) { + setTimeout(() => {this.loadStudiesFromChange(changesResponse["First"], 1000)}, 1); + } + } else { + if (toChangeId != changesResponse["First"]) { + setTimeout(() => {this.loadStudiesFromChange(Math.max(0, toChangeId-1000), 1000)}, 1); + } + } } else { this.isLoadingLatestStudies = false; this.isDisplayingLatestStudies = true; diff --git a/WebApplication/src/orthancApi.js b/WebApplication/src/orthancApi.js index bff1018..22b260b 100644 --- a/WebApplication/src/orthancApi.js +++ b/WebApplication/src/orthancApi.js @@ -103,6 +103,14 @@ export default { const response = (await axios.get(orthancApiUrl + "changes?since=" + since + "&limit=" + limit)); return response.data; }, + async getChangesExtended(to, limit, filter = []) { + let url = orthancApiUrl + "changes?to=" + to + "&limit=" + limit; + if (filter.length > 0) { + url += "&type=" + filter.join(";") + } + const response = (await axios.get(url)); + return response.data; + }, async getSamePatientStudies(patientTags, tags) { if (!tags || tags.length == 0) { console.error("Unable to getSamePatientStudies if 'tags' is not defined or empty"); diff --git a/WebApplication/src/store/modules/configuration.js b/WebApplication/src/store/modules/configuration.js index 01c89e9..69feb46 100644 --- a/WebApplication/src/store/modules/configuration.js +++ b/WebApplication/src/store/modules/configuration.js @@ -24,6 +24,21 @@ const state = () => ({ ///////////////////////////// GETTERS const getters = { + hasExtendedFind: (state) => { + if ("Capabilities" in state.system){ + return state.system.Capabilities.HasExtendedFind; + } else { + return false; + } + }, + hasExtendedChanges: (state) => { + if ("Capabilities" in state.system){ + return state.system.Capabilities.HasExtendedChanges; + } else { + return false; + } + + } } ///////////////////////////// MUTATIONS diff --git a/release-notes.md b/release-notes.md index 8389751..5ceabab 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,3 +1,9 @@ +Pending changes in the mainline +=============================== + +Changes: + - Optimized loading of "most-recent" studies when the Orthanc DB supports "ExtendedChanges" + 1.6.2 (2024-09-23) ================== From f64c1f83fa9cd7e6ce7e341d921da4705ca4d599 Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Tue, 1 Oct 2024 10:22:56 +0200 Subject: [PATCH 02/24] Disable UI components on ReadOnly systems --- Plugin/Plugin.cpp | 15 +++++++++++++++ WebApplication/src/components/Settings.vue | 4 ++++ WebApplication/src/locales/en.json | 1 + WebApplication/src/locales/fr.json | 1 + release-notes.md | 2 ++ 5 files changed, 23 insertions(+) diff --git a/Plugin/Plugin.cpp b/Plugin/Plugin.cpp index 513466e..e0e4e1f 100644 --- a/Plugin/Plugin.cpp +++ b/Plugin/Plugin.cpp @@ -45,6 +45,7 @@ Json::Value pluginsConfiguration_; bool hasUserProfile_ = false; bool openInOhifV3IsExplicitelyDisabled = false; bool enableShares_ = false; +bool isReadOnly_ = false; std::string customCssPath_; std::string theme_ = "light"; std::string customLogoPath_; @@ -343,6 +344,8 @@ void ReadConfiguration() } enableShares_ = pluginJsonConfiguration_["UiOptions"]["EnableShares"].asBool(); // we are sure that the value exists since it is in the default configuration file + + isReadOnly_ = orthancFullConfiguration_->GetBooleanValue("ReadOnly", false); } bool GetPluginConfiguration(Json::Value& jsonPluginConfiguration, const std::string& sectionName) @@ -690,6 +693,18 @@ void GetOE2Configuration(OrthancPluginRestOutput* output, } + // disable operations on read only systems + if (isReadOnly_) + { + uiOptions["EnableUpload"] = false; + uiOptions["EnableAddSeries"] = false; + uiOptions["EnableDeleteResources"] = false; + uiOptions["EnableModification"] = false; + uiOptions["EnableAnonymization"] = false; + uiOptions["EnableEditLabels"] = false; + uiOptions["EnablePermissionsEdition"] = false; + } + oe2Configuration["Keycloak"] = GetKeycloakConfiguration(); std::string answer = oe2Configuration.toStyledString(); diff --git a/WebApplication/src/components/Settings.vue b/WebApplication/src/components/Settings.vue index 7b50cd3..e886ec2 100644 --- a/WebApplication/src/components/Settings.vue +++ b/WebApplication/src/components/Settings.vue @@ -134,6 +134,10 @@ export default { {{ $t('settings.storage_compression') }} {{ system.StorageCompression }} + + {{ $t('settings.read_only') }} + {{ system.ReadOnly }} + diff --git a/WebApplication/src/locales/en.json b/WebApplication/src/locales/en.json index 9705453..6bce95f 100644 --- a/WebApplication/src/locales/en.json +++ b/WebApplication/src/locales/en.json @@ -210,6 +210,7 @@ "permissions_clear_selection": "Clear selection", "permissions_global_instructions_html": "Edit the permissions below and click Save to apply your modifications.
For each role, select the labels whose access is allowed and the allowed permissions.", "permissions_instructions": "The permissions are statically defined in the authorization-plugin configuration.", + "read_only": "Read only system", "select_all_label_permissions": "Select all label permissions", "select_all_global_permissions": "Select all global permissions", "plugins_not_enabled": "Plugins that are loaded but not enabled or not configured correctly are ", diff --git a/WebApplication/src/locales/fr.json b/WebApplication/src/locales/fr.json index 6a94548..c421fc4 100644 --- a/WebApplication/src/locales/fr.json +++ b/WebApplication/src/locales/fr.json @@ -211,6 +211,7 @@ "permissions_global_instructions_html": "Éditer les permissions ci-dessous et cliquez sur Enregistrer pour appliquer les modifications.
Pour chaque rôle, sélectionnez les étiquettes qui seront accessibles et les actions autorisées sur ces examens.", "permissions_instructions": "La liste de permission ne peut être modifiée; elle est définie dans le plugin authorization.", "plugins_not_enabled": "Les plugins qui sont chargés mais non-activés ou configurés correctement sont ", + "read_only": "Système en lecture seule", "roles_title": "Rôles", "roles_description_html": "Les rôles sont créés et modifiés dans Keycloak", "select_all_label_permissions": "Sélectionner toutes les permissions basées sur les étiquettes", diff --git a/release-notes.md b/release-notes.md index 5ceabab..d881001 100644 --- a/release-notes.md +++ b/release-notes.md @@ -3,6 +3,8 @@ Pending changes in the mainline Changes: - Optimized loading of "most-recent" studies when the Orthanc DB supports "ExtendedChanges" + - Disable UI components on ReadOnly systems. + 1.6.2 (2024-09-23) ================== From 776fb2ec8ed3ed3eb050f4eaa6a559968b6fd224 Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Wed, 16 Oct 2024 15:23:17 +0200 Subject: [PATCH 03/24] labels count --- Plugin/DefaultConfiguration.json | 3 ++- WebApplication/src/components/SideBar.vue | 28 +++++++++++++++++++---- WebApplication/src/orthancApi.js | 9 ++++++++ release-notes.md | 4 +++- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/Plugin/DefaultConfiguration.json b/Plugin/DefaultConfiguration.json index 34a728f..07253f1 100644 --- a/Plugin/DefaultConfiguration.json +++ b/Plugin/DefaultConfiguration.json @@ -49,11 +49,12 @@ "EnableLinkToLegacyUi": true, // Enables a link to the legacy Orthanc UI "EnableChangePassword": true, // Enables the 'change password' button in the side bar. Only applicable if Keycloak is enabled "EnableViewerQuickButton": true, // Enables a button in the study list to directly open a viewer - "EnableReportQuickButton": false, // Enables a button in the study list to directly open a PDF report if available in the study + "EnableReportQuickButton": false, // Enables a button in the study list to directly open a PDF report if available in the study "EnableEditLabels": true, // Enables labels management (create/delete/assign/unassign) "AvailableLabels": [], // If not empty, this list prevents the creation of new labels and only allows add/remove of the listed labels. // This configuration may be overriden when you use Keycloak and an auth-service that implements roles/permissions API. + "EnableLabelsCount": true, // Enables display of study count next to the each label (this might slow down the UI) "EnableShares": false, // Enables sharing studies. See "Tokens" section below. "DefaultShareDuration": 0, // [in days]. 0 means no expiration date, diff --git a/WebApplication/src/components/SideBar.vue b/WebApplication/src/components/SideBar.vue index 1024108..35cb770 100644 --- a/WebApplication/src/components/SideBar.vue +++ b/WebApplication/src/components/SideBar.vue @@ -16,7 +16,8 @@ export default { return { // selectedModality: null, selectedLabel: null, - modalitiesEchoStatus: {} + modalitiesEchoStatus: {}, + labelsStudyCount: {}, }; }, computed: { @@ -95,9 +96,6 @@ export default { isSelectedLabel(label) { return this.labelsFilter.includes(label); }, - onAllLabelsChanged() { - this.$store.dispatch('labels/refresh'); - }, logout(event) { event.preventDefault(); let logoutOptions = { @@ -116,10 +114,29 @@ export default { }).catch((error) => { console.error("login for password change failed", error); }) + }, + async loadLabelsCount() { + if (Object.entries(this.labelsStudyCount).length == 0) { + for (const label of this.allLabels) { + this.labelsStudyCount[label] = null; + } + } + if (this.uiOptions.EnableLabelsCount) { + for (const [k, v] of Object.entries(this.labelsStudyCount)) { + if (v == null) { + this.labelsStudyCount[k] = await api.getLabelStudyCount(k); + } + } + } + } + }, + watch: { + allLabels(newValue, oldValue) { + this.loadLabelsCount(); } - }, mounted() { + this.loadLabelsCount(); this.$refs['modalities-collapsible'].addEventListener('show.bs.collapse', (e) => { for (const modality of this.queryableDicomModalities) { this.modalitiesEchoStatus[modality] = null; @@ -168,6 +185,7 @@ export default { v-bind:class="{ 'active': isSelectedLabel(label) }" @click="selectLabel(label)"> {{ label }} + {{ labelsStudyCount[label] }} diff --git a/WebApplication/src/orthancApi.js b/WebApplication/src/orthancApi.js index 22b260b..8ef2cb0 100644 --- a/WebApplication/src/orthancApi.js +++ b/WebApplication/src/orthancApi.js @@ -494,6 +494,15 @@ export default { return response.data; }, + async getLabelStudyCount(label) { + const response = (await axios.post(orthancApiUrl + "tools/count-resources", { + "Level": "Study", + "Query": {}, + "Labels": [label], + "LabelConstraint" : "All" + })); + return response.data["Count"]; + }, ////////////////////////////////////////// HELPERS getOsimisViewerUrl(level, resourceOrthancId) { diff --git a/release-notes.md b/release-notes.md index 24fc920..bf612aa 100644 --- a/release-notes.md +++ b/release-notes.md @@ -4,7 +4,9 @@ Pending changes in the mainline Changes: - Optimized loading of "most-recent" studies when the Orthanc DB supports "ExtendedChanges" - Disable UI components on ReadOnly systems. - + - new configuration "EnableLabelsCount" to enable/disable the display of the number of + studies with each label. + Fixes: - When modifying studies, dates selected from the DatePicker were not always taken into account. From c7bac83056bd8ab1381ab47c1661d26ffbf795f7 Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Wed, 16 Oct 2024 16:28:14 +0200 Subject: [PATCH 04/24] use tools/find to display most recent studies --- WebApplication/src/components/StudyList.vue | 7 ++++++ WebApplication/src/orthancApi.js | 26 +++++++++++++++++++++ release-notes.md | 2 +- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/WebApplication/src/components/StudyList.vue b/WebApplication/src/components/StudyList.vue index 9cc12bc..61cc0ea 100644 --- a/WebApplication/src/components/StudyList.vue +++ b/WebApplication/src/components/StudyList.vue @@ -648,7 +648,14 @@ export default { await this.$store.dispatch('studies/clearStudies'); if (this.uiOptions.StudyListContentIfNoSearch == "empty") { return; + } else if (this.uiOptions.StudyListContentIfNoSearch == "most-recents" && this['configuration/hasExtendedChanges']) { + const studies = await api.getMostRecentStudies((this.filterLabels.length > 0 ? this.filterLabels[0] : null)); + for (const study of studies) { + this.$store.dispatch('studies/addStudy', { studyId: study["ID"], study: study, reloadStats: false }); + } } else if (this.uiOptions.StudyListContentIfNoSearch == "most-recents") { + // legacy code + if (this.isLoadingLatestStudies) { // if currently loading, stop it this.shouldStopLoadingLatestStudies = true; diff --git a/WebApplication/src/orthancApi.js b/WebApplication/src/orthancApi.js index 8ef2cb0..4b068e2 100644 --- a/WebApplication/src/orthancApi.js +++ b/WebApplication/src/orthancApi.js @@ -95,6 +95,32 @@ export default { signal: window.axiosFindStudiesAbortController.signal })).data; }, + async getMostRecentStudies(label) { + await this.cancelFindStudies(); + window.axiosFindStudiesAbortController = new AbortController(); + + let payload = { + "Level": "Study", + "Limit": store.state.configuration.uiOptions.MaxStudiesDisplayed, + "Query": {}, + "RequestedTags": store.state.configuration.requestedTagsForStudyList, + "OrderBy" : [{ + 'Type': 'Metadata', + 'Key': 'LastUpdate', + 'Direction': 'DESC' + }], + "Expand": true + }; + if (label) { + payload["Labels"] = [label]; + payload["LabelsConstraint"] = "All"; + } + + return (await axios.post(orthancApiUrl + "tools/find", payload, + { + signal: window.axiosFindStudiesAbortController.signal + })).data; + }, async getLastChangeId() { const response = (await axios.get(orthancApiUrl + "changes?last")); return response.data["Last"]; diff --git a/release-notes.md b/release-notes.md index bf612aa..eff127d 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,7 @@ Pending changes in the mainline =============================== Changes: - - Optimized loading of "most-recent" studies when the Orthanc DB supports "ExtendedChanges" + - Optimized loading of "most-recent" studies when the Orthanc DB supports "ExtendedFind" - Disable UI components on ReadOnly systems. - new configuration "EnableLabelsCount" to enable/disable the display of the number of studies with each label. From 5c42af7fe6cac7a0b1ec861583c68ff6ce8f2efd Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Fri, 8 Nov 2024 12:41:10 +0100 Subject: [PATCH 05/24] Fixed the criteria to display the OHIF Segmentation viewer --- .../src/components/ResourceButtonGroup.vue | 33 ++++++++----------- release-notes.md | 2 +- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/WebApplication/src/components/ResourceButtonGroup.vue b/WebApplication/src/components/ResourceButtonGroup.vue index 8f930e7..490a6c4 100644 --- a/WebApplication/src/components/ResourceButtonGroup.vue +++ b/WebApplication/src/components/ResourceButtonGroup.vue @@ -420,26 +420,21 @@ export default { return false; } - // // from isValidMode() in OHIF code - // // disable if it only contains modalities that are not supported by this mode: - // let modalities = this.modalitiesList; - // modalities = modalities.filter(x => !['SM', 'US', 'MG', 'OT', 'DOC', 'CR'].includes(x)); // since Set.difference is not supported on Firefox as of March 2024 - // if (modalities.length == 0) { - // return false; - // } - - // disable if it is not reconstructible - if (!(this.modalitiesList.includes("CT") && this.modalitiesList.includes("PT") && !this.modalitiesList.includes("SM")) - && this.resourceLevel != "bulk") // we can not check the modalities list for bulk mode) - { - return false; - } - - if (this.uiOptions.EnableOpenInOhifViewer3) { - return this.hasOhifViewer && (this.resourceLevel == 'study' || (this.resourceLevel == 'bulk' && this.ohifDataSource == 'dicom-web')); - } else { - return false; + if (this.uiOptions.EnableOpenInOhifViewer3 && this.hasOhifViewer) { + if (this.resourceLevel == "bulk" && this.ohifDataSource == 'dicom-web') { + return true; // unable to check the list of modalities in this case -> allow it + } else if (this.resourceLevel == "study") { + // // from isValidMode() in OHIF code + // Don't show the mode if the selected studies have only one modality that is not supported by the mode + if (this.modalitiesList.length == 1 && ['SM', 'ECG', 'OT', 'DOC'].includes(this.modalitiesList[0])) { + return false; + } else { + return true; + } + } } + + return false; }, hasOhifViewerButtonMicroscopy() { if (!this.uiOptions.ViewersOrdering.includes("ohif-micro") || this.studiesSourceType != SourceType.LOCAL_ORTHANC) { diff --git a/release-notes.md b/release-notes.md index eff127d..cdac2b5 100644 --- a/release-notes.md +++ b/release-notes.md @@ -9,7 +9,7 @@ Changes: Fixes: - When modifying studies, dates selected from the DatePicker were not always taken into account. - + - Fixed the criteria to display the OHIF Segmentation viewer. 1.6.4 (2024-10-10) ================== From 346718044dce06b7c39200e54fcc0185feac7c00 Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Thu, 14 Nov 2024 09:04:59 +0100 Subject: [PATCH 06/24] fix for DB that do not support ExtendedFind --- TODO | 19 +++++++------------ WebApplication/src/components/SideBar.vue | 15 ++++++++++----- WebApplication/src/components/StudyList.vue | 3 ++- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/TODO b/TODO index 1e0b719..3b4f37e 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,9 @@ +- implement pagination to fetch studies 100 -> 200 when we scroll to the end of the study-list +- implement pagination to fetch instances 100 -> 200 when we scroll to the end of the instance-list (displaying 2000 instances in one go sometimes takes 10 seconds !) + + - add a "reset" button (on Windows, to reload the config after you have changed it without going to the Services -> Orthanc -> Restart) - show ohif-vr and ohif-tmtv buttons only when relevant (analyse the content of the study) -- handle the EnableLabels "permission" - -- use DatePicker in remote modality study list - predefined filters in config file to display below Studies: - "Today": {"StudyDate": "$today"} @@ -12,7 +13,8 @@ UI improvements: - Admin theme including responsive tables (including multiline): https://github.com/lekoala/admini - tags: https://github.com/lekoala/bootstrap5-tags - +- when opening the series view I would prefer to see an image and it’s labels over a list of instance numbers and paired SOPInstanceUIDs: + https://discourse.orthanc-server.org/t/beginner-questions-from-horos-user/5322 - modification: - configuration to hide DICOM UID options and select the right one directly for a given setup. @@ -20,8 +22,6 @@ UI improvements: - show attachments -- support dicom-web plugin (QIDO-RS UI) - - support neuro plugin (download nifti) - TagsTree: allow click on "null" tags to open /instances/../content/group,element in new window @@ -53,12 +53,7 @@ UI improvements: } - configure other viewers url (ex: radiant://?n=pstv&v=0020000D&v=%22StudyInstanceUID%22 or osirix or horos ...) -- make table sortable and faster search by maintaining a cache DB (SQLite in each Orthanc instance handled by the oe2 plugin) - - only for studies. with a few indexed tags (the ones from the UI table) - - plugin monitors /changes route to see changes from all orthancs (instead of reacting to change events) - - to monitor deleted studies, react to change event + call other oe2 plugin API ? (need to list all orthanc urls somewhere) - - from OHIF doc for their study list: When the Study List is opened, the application queries the PACS for 101 studies by default. If there are greater than 100 studies - returned, the default sort for the study list is dictated by the image archive that hosts these studies for the viewer and study list sorting will be disabled. If there are less than or equal to 100 studies returned, they will be sorted by study date (most recent to oldest) and study list sorting will be enabled. Whenever a query returns greater than 100 studies, use filters to narrow results below 100 studies to enable Study List sorting. +- make table sortable - orthanc-share should generate QR code with publication links diff --git a/WebApplication/src/components/SideBar.vue b/WebApplication/src/components/SideBar.vue index 35cb770..00d6c5b 100644 --- a/WebApplication/src/components/SideBar.vue +++ b/WebApplication/src/components/SideBar.vue @@ -3,7 +3,7 @@ import UploadHandler from "./UploadHandler.vue" import JobsList from "./JobsList.vue"; import LanguagePicker from "./LanguagePicker.vue"; -import { mapState } from "vuex" +import { mapState, mapGetters } from "vuex" import { orthancApiUrl, oe2ApiUrl } from "../globalConfigurations"; import api from "../orthancApi" import SourceType from "../helpers/source-type"; @@ -37,6 +37,9 @@ export default { studiesSourceType: state => state.studies.sourceType, studiesRemoteSource: state => state.studies.remoteSource, }), + ...mapGetters([ + 'configuration/hasExtendedFind', // -> this['configuration/hasExtendedFind'] + ]), customLogoUrl() { if (this.hasCustomLogo && this.configuration.customLogoUrl) { return this.customLogoUrl; @@ -121,10 +124,12 @@ export default { this.labelsStudyCount[label] = null; } } - if (this.uiOptions.EnableLabelsCount) { - for (const [k, v] of Object.entries(this.labelsStudyCount)) { - if (v == null) { - this.labelsStudyCount[k] = await api.getLabelStudyCount(k); + if (this['configuration/hasExtendedFind']) { + if (this.uiOptions.EnableLabelsCount) { + for (const [k, v] of Object.entries(this.labelsStudyCount)) { + if (v == null) { + this.labelsStudyCount[k] = await api.getLabelStudyCount(k); + } } } } diff --git a/WebApplication/src/components/StudyList.vue b/WebApplication/src/components/StudyList.vue index 61cc0ea..4e27292 100644 --- a/WebApplication/src/components/StudyList.vue +++ b/WebApplication/src/components/StudyList.vue @@ -96,6 +96,7 @@ export default { }), ...mapGetters([ 'studies/isFilterEmpty', // -> this['studies/isFilterEmpty'] + 'configuration/hasExtendedFind', // -> this['configuration/hasExtendedFind'] 'configuration/hasExtendedChanges', // -> this['configuration/hasExtendedChanges'] ]), notShowingAllResults() { @@ -648,7 +649,7 @@ export default { await this.$store.dispatch('studies/clearStudies'); if (this.uiOptions.StudyListContentIfNoSearch == "empty") { return; - } else if (this.uiOptions.StudyListContentIfNoSearch == "most-recents" && this['configuration/hasExtendedChanges']) { + } else if (this.uiOptions.StudyListContentIfNoSearch == "most-recents" && this['configuration/hasExtendedFind']) { const studies = await api.getMostRecentStudies((this.filterLabels.length > 0 ? this.filterLabels[0] : null)); for (const study of studies) { this.$store.dispatch('studies/addStudy', { studyId: study["ID"], study: study, reloadStats: false }); From 782a09ae480fbf80c6361a1a1ecc3312ca9f26b7 Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Mon, 18 Nov 2024 09:37:10 +0100 Subject: [PATCH 07/24] fixed display of invalid dates like 00000000 --- WebApplication/src/helpers/date-helpers.js | 10 +++++++--- release-notes.md | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/WebApplication/src/helpers/date-helpers.js b/WebApplication/src/helpers/date-helpers.js index 0616243..c61b3fd 100644 --- a/WebApplication/src/helpers/date-helpers.js +++ b/WebApplication/src/helpers/date-helpers.js @@ -73,10 +73,14 @@ export default { formatDateForDisplay(dicomDate, dateFormat) { if (dicomDate && dicomDate.length == 8) { let d = parse(dicomDate, "yyyyMMdd", new Date()); - return format(d, dateFormat); - } else { - return ""; + if (!isNaN(d.getDate())) { + return format(d, dateFormat); + } + } + if (dicomDate.length > 0) { + return dicomDate; } + return ""; }, isDateTag(tagName) { return ["StudyDate", "PatientBirthDate", "SeriesDate", "AcquisitionDate", "ContentDate"].indexOf(tagName) != -1; diff --git a/release-notes.md b/release-notes.md index cdac2b5..d983b70 100644 --- a/release-notes.md +++ b/release-notes.md @@ -10,6 +10,8 @@ Changes: Fixes: - When modifying studies, dates selected from the DatePicker were not always taken into account. - Fixed the criteria to display the OHIF Segmentation viewer. + - Fixed display of invalid dates like 00000000. + 1.6.4 (2024-10-10) ================== From bed15cc4f5d8d9c17775dfefac95cebb2365ae62 Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Wed, 20 Nov 2024 16:03:33 +0100 Subject: [PATCH 08/24] todo --- TODO | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO b/TODO index 3b4f37e..c672168 100644 --- a/TODO +++ b/TODO @@ -20,6 +20,10 @@ UI improvements: - configuration to hide DICOM UID options and select the right one directly for a given setup. - add a Series Description renamer (one dialog to edit SeriesNumber + SeriesDescription of a study) +- settings: + - show the list of /tools/accepted-transfer-syntaxes + - show the list of /tools/accepted-sop-classes (in 1.12.6) + - show attachments - support neuro plugin (download nifti) From 1bcb8b3edfc0c0aea881130d068f9ff3eb97e845 Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Fri, 6 Dec 2024 14:47:02 +0100 Subject: [PATCH 09/24] Fixed compatibility with OHIF 1.4 if OHIF.DataSource is not defined --- Plugin/Plugin.cpp | 6 +++--- release-notes.md | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Plugin/Plugin.cpp b/Plugin/Plugin.cpp index 5129025..a09613b 100644 --- a/Plugin/Plugin.cpp +++ b/Plugin/Plugin.cpp @@ -463,12 +463,12 @@ Json::Value GetPluginsConfiguration(bool& hasUserProfile) else if (pluginName == "ohif") { pluginsConfiguration[pluginName]["Enabled"] = true; - std::string ohifDataSource = "dicom-json"; + std::string ohifDataSource = "dicom-web"; if (GetPluginConfiguration(pluginConfiguration, "OHIF")) { - if (pluginConfiguration.isMember("DataSource") && pluginConfiguration["DataSource"].asString() == "dicom-web") + if (pluginConfiguration.isMember("DataSource") && pluginConfiguration["DataSource"].asString() == "dicom-json") { - ohifDataSource = "dicom-web"; + ohifDataSource = "dicom-json"; } } pluginsConfiguration[pluginName]["DataSource"] = ohifDataSource; diff --git a/release-notes.md b/release-notes.md index 4e7bb98..4c2a1aa 100644 --- a/release-notes.md +++ b/release-notes.md @@ -4,6 +4,7 @@ Pending changes Fixes: - When modifying studies, dates selected from the DatePicker were not always taken into account. - Fixed the criteria to display the OHIF Segmentation viewer. + - Fixed compatibility with OHIF 1.4 if OHIF.DataSource is not defined. 1.6.4 (2024-10-10) ================== From 19792003d856d1111d4fba103250e346eb60f88e Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Tue, 10 Dec 2024 16:32:01 +0100 Subject: [PATCH 10/24] cleanup --- release-notes.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/release-notes.md b/release-notes.md index f9ed5e4..057f97a 100644 --- a/release-notes.md +++ b/release-notes.md @@ -3,8 +3,8 @@ Pending changes in the mainline Changes: - Optimized loading of "most-recent" studies when the Orthanc DB supports "ExtendedFind" - - Disable UI components on ReadOnly systems. - - new configuration "EnableLabelsCount" to enable/disable the display of the number of + - Disable some UI components on ReadOnly systems. + - New configuration "EnableLabelsCount" to enable/disable the display of the number of studies with each label. Fixes: @@ -13,6 +13,7 @@ Fixes: - Fixed display of invalid dates like 00000000. - Fixed compatibility with OHIF 1.4 if OHIF.DataSource is not defined. + 1.6.4 (2024-10-10) ================== From 5f20a15ae1ef3487a697bcc1900154e819f81afb Mon Sep 17 00:00:00 2001 From: Alain Mazy Date: Tue, 10 Dec 2024 16:32:35 +0100 Subject: [PATCH 11/24] re-organize table header --- WebApplication/src/components/StudyList.vue | 133 ++++++++++---------- 1 file changed, 68 insertions(+), 65 deletions(-) diff --git a/WebApplication/src/components/StudyList.vue b/WebApplication/src/components/StudyList.vue index 4e27292..9cf1e2c 100644 --- a/WebApplication/src/components/StudyList.vue +++ b/WebApplication/src/components/StudyList.vue @@ -748,16 +748,16 @@ export default {