diff --git a/server/src/actix/mod.rs b/server/src/actix/mod.rs index f6c0021..aba4b0d 100644 --- a/server/src/actix/mod.rs +++ b/server/src/actix/mod.rs @@ -23,6 +23,9 @@ pub fn dicomweb_config(cfg: &mut actix_web::web::ServiceConfig) { .service(search_instances_study_level) .service(search_instances_series_level) .service(retrieve_instance) + .service(retrieve_instance_metadata) .service(retrieve_series) - .service(retrieve_study); + .service(retrieve_series_metadata) + .service(retrieve_study) + .service(retrieve_study_metadata); } diff --git a/server/src/actix/qido.rs b/server/src/actix/qido.rs index 816403e..d60c055 100644 --- a/server/src/actix/qido.rs +++ b/server/src/actix/qido.rs @@ -2,8 +2,9 @@ use actix_web::{get, http, web, HttpRequest, HttpResponse, Responder}; use dicom_json::DicomJson; use dicom_object::InMemDicomObject; - -use crate::{DicomWebServer, QidoInstanceQuery, QidoSeriesQuery, QidoStudyQuery}; +use crate::{ + DicomWebServer, QidoInstanceQuery, QidoSeriesQuery, QidoStudyQuery, APPLICATION_DICOM_JSON, +}; #[get("/studies")] pub async fn search_studies_all( @@ -18,6 +19,12 @@ pub async fn search_studies_all( return HttpResponse::NotAcceptable().finish(); } + // If the Accept header is present, it must be application/dicom+json + if accept_header.unwrap().to_str().unwrap() != APPLICATION_DICOM_JSON { + return HttpResponse::NotAcceptable().finish(); + } + + // Get the matching DICOM objects from the callback let result = (callbacks.search_study)(&query); match result { diff --git a/server/src/actix/wado.rs b/server/src/actix/wado.rs index 2ff5b94..e3e8327 100644 --- a/server/src/actix/wado.rs +++ b/server/src/actix/wado.rs @@ -1,6 +1,9 @@ use std::io::Write; use actix_web::{get, web, HttpResponse, Responder}; +use dicom::dictionary_std::tags; +use dicom_json::DicomJson; +use dicom_object::InMemDicomObject; use crate::{actix::MultipartWriter, DicomWebServer}; @@ -41,6 +44,32 @@ pub async fn retrieve_study( } } +#[get("/studies/{study_uid}/metadata")] +pub async fn retrieve_study_metadata( + callbacks: web::Data, + study_uid: web::Path, +) -> impl Responder { + let result = (callbacks.retrieve_study)(&study_uid); + + match result { + Ok(dcm_files) => { + // Apply the offset and the filter + let mut filtered: Vec = dcm_files + .into_iter() + .map(|dcm_file| dcm_file.into_inner()) + .collect(); + + // Remove any bulk data + for dcm in &mut filtered { + dcm.remove_element(tags::PIXEL_DATA); + } + + return HttpResponse::Ok().json(DicomJson::from(filtered)); + } + Err(e) => return HttpResponse::InternalServerError().body(e.to_string()), + } +} + #[get("/studies/{study_uid}/series/{series_uid}")] pub async fn retrieve_series( callbacks: web::Data, @@ -76,6 +105,33 @@ pub async fn retrieve_series( } } +#[get("/studies/{study_uid}/series/{series_uid}/metadata")] +pub async fn retrieve_series_metadata( + callbacks: web::Data, + path: web::Path<(String, String)>, +) -> impl Responder { + let (study_uid, series_uid) = path.into_inner(); + let result = (callbacks.retrieve_series)(&study_uid, &series_uid); + + match result { + Ok(dcm_files) => { + // Apply the offset and the filter + let mut filtered: Vec = dcm_files + .into_iter() + .map(|dcm_file| dcm_file.into_inner()) + .collect(); + + // Remove any bulk data + for dcm in &mut filtered { + dcm.remove_element(tags::PIXEL_DATA); + } + + return HttpResponse::Ok().json(DicomJson::from(filtered)); + } + Err(e) => return HttpResponse::InternalServerError().body(e.to_string()), + } +} + #[get("/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}")] pub async fn retrieve_instance( callbacks: web::Data, @@ -109,8 +165,30 @@ pub async fn retrieve_instance( } } +#[get("/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}/metadata")] +pub async fn retrieve_instance_metadata( + callbacks: web::Data, + path: web::Path<(String, String, String)>, +) -> impl Responder { + let (study_uid, series_uid, instance_uid) = path.into_inner(); + let result = (callbacks.retrieve_instance)(&study_uid, &series_uid, &instance_uid); + + match result { + Ok(mut dcm_file) => { + // Remove any bulkdata from the DICOM file + dcm_file.remove_element(tags::PIXEL_DATA); + + return HttpResponse::Ok().json(DicomJson::from(dcm_file)); + } + Err(e) => return HttpResponse::InternalServerError().body(e.to_string()), + } +} + pub fn wado_config(cfg: &mut web::ServiceConfig) { cfg.service(retrieve_study) + .service(retrieve_study_metadata) .service(retrieve_series) - .service(retrieve_instance); + .service(retrieve_series_metadata) + .service(retrieve_instance) + .service(retrieve_instance_metadata); } diff --git a/server/src/lib.rs b/server/src/lib.rs index 6ad7c86..f4323a3 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -10,6 +10,8 @@ pub use filter::study_filter; #[cfg(feature = "actix")] pub mod actix; +const APPLICATION_DICOM_JSON: &str = "application/dicom+json"; + /// QIDO-RS /// /// See https://www.dicomstandard.org/using/dicomweb/query-qido-rs for more information