# HG changeset patch # User Sebastien Jodogne # Date 1715100293 -7200 # Node ID 81a29ad7fb4bdd2310bef03a916a68d1685cf72b # Parent a87f2a56257d76582b97c29849679dbae6b0c66b added possibility to retrieve main DICOM tags and metadata at any level diff -r a87f2a56257d -r 81a29ad7fb4b OrthancServer/Sources/Database/Compatibility/GenericFind.cpp --- a/OrthancServer/Sources/Database/Compatibility/GenericFind.cpp Tue May 07 12:53:12 2024 +0200 +++ b/OrthancServer/Sources/Database/Compatibility/GenericFind.cpp Tue May 07 18:44:53 2024 +0200 @@ -25,6 +25,8 @@ #include "../../../../OrthancFramework/Sources/DicomFormat/DicomArray.h" #include "../../../../OrthancFramework/Sources/OrthancException.h" +#include + namespace Orthanc { @@ -59,6 +61,91 @@ } + void GenericFind::RetrieveMainDicomTags(FindResponse::Resource& target, + ResourceType level, + int64_t internalId) + { + DicomMap m; + transaction_.GetMainDicomTags(m, internalId); + + DicomArray a(m); + for (size_t i = 0; i < a.GetSize(); i++) + { + const DicomElement& element = a.GetElement(i); + if (element.GetValue().IsString()) + { + target.AddStringDicomTag(level, element.GetTag().GetGroup(), + element.GetTag().GetElement(), element.GetValue().GetContent()); + } + else + { + throw OrthancException(ErrorCode_BadParameterType); + } + } + } + + + static ResourceType GetTopLevelOfInterest(const FindRequest& request) + { + switch (request.GetLevel()) + { + case ResourceType_Patient: + return ResourceType_Patient; + + case ResourceType_Study: + if (request.IsRetrieveMainDicomTags(ResourceType_Patient) || + request.IsRetrieveMetadata(ResourceType_Patient)) + { + return ResourceType_Patient; + } + else + { + return ResourceType_Study; + } + + case ResourceType_Series: + if (request.IsRetrieveMainDicomTags(ResourceType_Patient) || + request.IsRetrieveMetadata(ResourceType_Patient)) + { + return ResourceType_Patient; + } + else if (request.IsRetrieveMainDicomTags(ResourceType_Study) || + request.IsRetrieveMetadata(ResourceType_Study)) + { + return ResourceType_Study; + } + else + { + return ResourceType_Series; + } + + case ResourceType_Instance: + if (request.IsRetrieveMainDicomTags(ResourceType_Patient) || + request.IsRetrieveMetadata(ResourceType_Patient)) + { + return ResourceType_Patient; + } + else if (request.IsRetrieveMainDicomTags(ResourceType_Study) || + request.IsRetrieveMetadata(ResourceType_Study)) + { + return ResourceType_Study; + } + else if (request.IsRetrieveMainDicomTags(ResourceType_Series) || + request.IsRetrieveMetadata(ResourceType_Series)) + { + return ResourceType_Series; + } + else + { + return ResourceType_Instance; + } + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + void GenericFind::ExecuteExpand(FindResponse& response, const FindRequest& request, const std::string& identifier) @@ -110,32 +197,43 @@ resource->SetParentIdentifier(parent); } - if (request.IsRetrieveMainDicomTags()) { - DicomMap m; - transaction_.GetMainDicomTags(m, internalId); + int64_t currentId = internalId; + ResourceType currentLevel = level; + const ResourceType topLevel = GetTopLevelOfInterest(request); - DicomArray a(m); - for (size_t i = 0; i < a.GetSize(); i++) + for (;;) { - const DicomElement& element = a.GetElement(i); - if (element.GetValue().IsString()) + if (request.IsRetrieveMainDicomTags(currentLevel)) + { + RetrieveMainDicomTags(*resource, currentLevel, currentId); + } + + if (request.IsRetrieveMetadata(currentLevel)) { - resource->AddStringDicomTag(element.GetTag().GetGroup(), element.GetTag().GetElement(), - element.GetValue().GetContent()); + transaction_.GetAllMetadata(resource->GetMetadata(currentLevel), currentId); + } + + if (currentLevel == topLevel) + { + break; } else { - throw OrthancException(ErrorCode_BadParameterType); + int64_t parentId; + if (transaction_.LookupParent(parentId, currentId)) + { + currentId = parentId; + currentLevel = GetParentResourceType(currentLevel); + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } } } } - if (request.IsRetrieveMetadata()) - { - transaction_.GetAllMetadata(resource->GetMetadata(), internalId); - } - if (request.IsRetrieveLabels()) { transaction_.ListLabels(resource->GetLabels(), internalId); @@ -169,7 +267,7 @@ for (std::list::const_iterator it = children.begin(); it != children.end(); ++it) { - resource->AddChildIdentifier(GetChildResourceType(level), *it); + resource->AddChildIdentifier(*it); } } @@ -181,6 +279,50 @@ resource->AddChildrenMetadata(*it, values); } + if (!request.GetRetrieveAttachmentOfOneInstance().empty()) + { + std::set todo = request.GetRetrieveAttachmentOfOneInstance(); + std::stack< std::pair > candidates; + candidates.push(std::make_pair(level, internalId)); + + while (!todo.empty() && + !candidates.empty()) + { + std::pair top = candidates.top(); + candidates.pop(); + + if (top.first == ResourceType_Instance) + { + std::set nextTodo; + + for (std::set::const_iterator it = todo.begin(); it != todo.end(); ++it) + { + FileInfo attachment; + int64_t revision; + if (transaction_.LookupAttachment(attachment, revision, top.second, *it)) + { + resource->AddAttachmentOfOneInstance(attachment); + } + else + { + nextTodo.insert(*it); + } + } + + todo = nextTodo; + } + else + { + std::list children; + transaction_.GetChildrenInternalId(children, top.second); + for (std::list::const_iterator it = children.begin(); it != children.end(); ++it) + { + candidates.push(std::make_pair(GetChildResourceType(top.first), *it)); + } + } + } + } + response.Add(resource.release()); } } diff -r a87f2a56257d -r 81a29ad7fb4b OrthancServer/Sources/Database/Compatibility/GenericFind.h --- a/OrthancServer/Sources/Database/Compatibility/GenericFind.h Tue May 07 12:53:12 2024 +0200 +++ b/OrthancServer/Sources/Database/Compatibility/GenericFind.h Tue May 07 18:44:53 2024 +0200 @@ -33,6 +33,10 @@ private: IDatabaseWrapper::ITransaction& transaction_; + void RetrieveMainDicomTags(FindResponse::Resource& target, + ResourceType level, + int64_t internalId); + public: GenericFind(IDatabaseWrapper::ITransaction& transaction) : transaction_(transaction) diff -r a87f2a56257d -r 81a29ad7fb4b OrthancServer/Sources/Database/FindRequest.cpp --- a/OrthancServer/Sources/Database/FindRequest.cpp Tue May 07 12:53:12 2024 +0200 +++ b/OrthancServer/Sources/Database/FindRequest.cpp Tue May 07 18:44:53 2024 +0200 @@ -34,8 +34,14 @@ hasLimits_(false), limitsSince_(0), limitsCount_(0), - retrieveMainDicomTags_(false), - retrieveMetadata_(false), + retrieveMainDicomTagsPatients_(false), + retrieveMainDicomTagsStudies_(false), + retrieveMainDicomTagsSeries_(false), + retrieveMainDicomTagsInstances_(false), + retrieveMetadataPatients_(false), + retrieveMetadataStudies_(false), + retrieveMetadataSeries_(false), + retrieveMetadataInstances_(false), retrieveLabels_(false), retrieveAttachments_(false), retrieveParentIdentifier_(false), @@ -128,6 +134,124 @@ } + void FindRequest::SetRetrieveMainDicomTags(ResourceType level, + bool retrieve) + { + if (!IsResourceLevelAboveOrEqual(level, level_)) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + switch (level) + { + case ResourceType_Patient: + retrieveMainDicomTagsPatients_ = retrieve; + break; + + case ResourceType_Study: + retrieveMainDicomTagsStudies_ = retrieve; + break; + + case ResourceType_Series: + retrieveMainDicomTagsSeries_ = retrieve; + break; + + case ResourceType_Instance: + retrieveMainDicomTagsInstances_ = retrieve; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + + bool FindRequest::IsRetrieveMainDicomTags(ResourceType level) const + { + if (!IsResourceLevelAboveOrEqual(level, level_)) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + switch (level) + { + case ResourceType_Patient: + return retrieveMainDicomTagsPatients_; + + case ResourceType_Study: + return retrieveMainDicomTagsStudies_; + + case ResourceType_Series: + return retrieveMainDicomTagsSeries_; + + case ResourceType_Instance: + return retrieveMainDicomTagsInstances_; + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + + void FindRequest::SetRetrieveMetadata(ResourceType level, + bool retrieve) + { + if (!IsResourceLevelAboveOrEqual(level, level_)) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + switch (level) + { + case ResourceType_Patient: + retrieveMetadataPatients_ = retrieve; + break; + + case ResourceType_Study: + retrieveMetadataStudies_ = retrieve; + break; + + case ResourceType_Series: + retrieveMetadataSeries_ = retrieve; + break; + + case ResourceType_Instance: + retrieveMetadataInstances_ = retrieve; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + + bool FindRequest::IsRetrieveMetadata(ResourceType level) const + { + if (!IsResourceLevelAboveOrEqual(level, level_)) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + switch (level) + { + case ResourceType_Patient: + return retrieveMetadataPatients_; + + case ResourceType_Study: + return retrieveMetadataStudies_; + + case ResourceType_Series: + return retrieveMetadataSeries_; + + case ResourceType_Instance: + return retrieveMetadataInstances_; + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + void FindRequest::SetRetrieveParentIdentifier(bool retrieve) { if (level_ == ResourceType_Patient) @@ -158,11 +282,24 @@ { if (IsRetrieveChildrenMetadata(metadata)) { - throw OrthancException(ErrorCode_BadParameterType); + throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { retrieveChildrenMetadata_.insert(metadata); } } + + + void FindRequest::AddRetrieveAttachmentOfOneInstance(FileContentType type) + { + if (retrieveAttachmentOfOneInstance_.find(type) == retrieveAttachmentOfOneInstance_.end()) + { + retrieveAttachmentOfOneInstance_.insert(type); + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } } diff -r a87f2a56257d -r 81a29ad7fb4b OrthancServer/Sources/Database/FindRequest.h --- a/OrthancServer/Sources/Database/FindRequest.h Tue May 07 12:53:12 2024 +0200 +++ b/OrthancServer/Sources/Database/FindRequest.h Tue May 07 18:44:53 2024 +0200 @@ -166,13 +166,20 @@ LabelsConstraint labelsContraint_; std::deque ordering_; // The ordering criteria (note: the order is important !) - bool retrieveMainDicomTags_; - bool retrieveMetadata_; + bool retrieveMainDicomTagsPatients_; + bool retrieveMainDicomTagsStudies_; + bool retrieveMainDicomTagsSeries_; + bool retrieveMainDicomTagsInstances_; + bool retrieveMetadataPatients_; + bool retrieveMetadataStudies_; + bool retrieveMetadataSeries_; + bool retrieveMetadataInstances_; bool retrieveLabels_; bool retrieveAttachments_; bool retrieveParentIdentifier_; bool retrieveChildrenIdentifiers_; std::set retrieveChildrenMetadata_; + std::set retrieveAttachmentOfOneInstance_; public: explicit FindRequest(ResourceType level); @@ -259,25 +266,15 @@ return labelsContraint_; } - void SetRetrieveMetadata(bool retrieve) - { - retrieveMetadata_ = retrieve; - } + void SetRetrieveMainDicomTags(ResourceType level, + bool retrieve); - bool IsRetrieveMainDicomTags() const - { - return retrieveMainDicomTags_; - } + bool IsRetrieveMainDicomTags(ResourceType level) const; - void SetRetrieveMainDicomTags(bool retrieve) - { - retrieveMainDicomTags_ = retrieve; - } + void SetRetrieveMetadata(ResourceType level, + bool retrieve); - bool IsRetrieveMetadata() const - { - return retrieveMetadata_; - } + bool IsRetrieveMetadata(ResourceType level) const; void SetRetrieveLabels(bool retrieve) { @@ -324,5 +321,12 @@ { return retrieveChildrenMetadata_; } + + void AddRetrieveAttachmentOfOneInstance(FileContentType type); + + const std::set& GetRetrieveAttachmentOfOneInstance() const + { + return retrieveAttachmentOfOneInstance_; + } }; } diff -r a87f2a56257d -r 81a29ad7fb4b OrthancServer/Sources/Database/FindResponse.cpp --- a/OrthancServer/Sources/Database/FindResponse.cpp Tue May 07 12:53:12 2024 +0200 +++ b/OrthancServer/Sources/Database/FindResponse.cpp Tue May 07 18:44:53 2024 +0200 @@ -22,16 +22,17 @@ #include "FindResponse.h" -#include "../../../OrthancFramework/Sources/DicomFormat/DicomInstanceHasher.h" +#include "../../../OrthancFramework/Sources/DicomFormat/DicomArray.h" #include "../../../OrthancFramework/Sources/OrthancException.h" #include "../../../OrthancFramework/Sources/SerializationToolbox.h" +#include #include namespace Orthanc { - class FindResponse::Resource::DicomValue : public boost::noncopyable + class FindResponse::MainDicomTagsAtLevel::DicomValue : public boost::noncopyable { public: enum ValueType @@ -74,8 +75,18 @@ }; - void FindResponse::Resource::AddNullDicomTag(uint16_t group, - uint16_t element) + FindResponse::MainDicomTagsAtLevel::~MainDicomTagsAtLevel() + { + for (MainDicomTags::iterator it = mainDicomTags_.begin(); it != mainDicomTags_.end(); ++it) + { + assert(it->second != NULL); + delete it->second; + } + } + + + void FindResponse::MainDicomTagsAtLevel::AddNullDicomTag(uint16_t group, + uint16_t element) { const DicomTag tag(group, element); @@ -90,9 +101,9 @@ } - void FindResponse::Resource::AddStringDicomTag(uint16_t group, - uint16_t element, - const std::string& value) + void FindResponse::MainDicomTagsAtLevel::AddStringDicomTag(uint16_t group, + uint16_t element, + const std::string& value) { const DicomTag tag(group, element); @@ -107,7 +118,7 @@ } - void FindResponse::Resource::GetMainDicomTags(DicomMap& target) const + void FindResponse::MainDicomTagsAtLevel::Export(DicomMap& target) const { for (MainDicomTags::const_iterator it = mainDicomTags_.begin(); it != mainDicomTags_.end(); ++it) { @@ -130,11 +141,11 @@ } - void FindResponse::ChildrenAtLevel::AddIdentifier(const std::string& identifier) + void FindResponse::Resource::AddChildIdentifier(const std::string& identifier) { - if (identifiers_.find(identifier) == identifiers_.end()) + if (childrenIdentifiers_.find(identifier) == childrenIdentifiers_.end()) { - identifiers_.insert(identifier); + childrenIdentifiers_.insert(identifier); } else { @@ -143,42 +154,26 @@ } - FindResponse::ChildrenAtLevel& FindResponse::Resource::GetChildrenAtLevel(ResourceType level) + FindResponse::MainDicomTagsAtLevel& FindResponse::Resource::GetMainDicomTagsAtLevel(ResourceType level) { + if (!IsResourceLevelAboveOrEqual(level, level_)) + { + throw OrthancException(ErrorCode_BadParameterType); + } + switch (level) { + case ResourceType_Patient: + return mainDicomTagsPatient_; + case ResourceType_Study: - if (level_ == ResourceType_Patient) - { - return childrenStudies_; - } - else - { - throw OrthancException(ErrorCode_BadParameterType); - } + return mainDicomTagsStudy_; case ResourceType_Series: - if (level_ == ResourceType_Patient || - level_ == ResourceType_Study) - { - return childrenSeries_; - } - else - { - throw OrthancException(ErrorCode_BadParameterType); - } + return mainDicomTagsSeries_; case ResourceType_Instance: - if (level_ == ResourceType_Patient || - level_ == ResourceType_Study || - level_ == ResourceType_Series) - { - return childrenInstances_; - } - else - { - throw OrthancException(ErrorCode_BadParameterType); - } + return mainDicomTagsInstance_; default: throw OrthancException(ErrorCode_ParameterOutOfRange); @@ -186,26 +181,59 @@ } - void FindResponse::Resource::AddMetadata(MetadataType metadata, + void FindResponse::Resource::AddMetadata(ResourceType level, + MetadataType metadata, const std::string& value) { - if (metadata_.find(metadata) != metadata_.end()) + std::map& m = GetMetadata(level); + + if (m.find(metadata) != m.end()) { throw OrthancException(ErrorCode_BadSequenceOfCalls); // Metadata already present } else { - metadata_[metadata] = value; + m[metadata] = value; + } + } + + + std::map& FindResponse::Resource::GetMetadata(ResourceType level) + { + if (!IsResourceLevelAboveOrEqual(level, level_)) + { + throw OrthancException(ErrorCode_BadParameterType); + } + + switch (level) + { + case ResourceType_Patient: + return metadataPatient_; + + case ResourceType_Study: + return metadataStudy_; + + case ResourceType_Series: + return metadataSeries_; + + case ResourceType_Instance: + return metadataInstance_; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); } } bool FindResponse::Resource::LookupMetadata(std::string& value, + ResourceType level, MetadataType metadata) const { - std::map::const_iterator found = metadata_.find(metadata); + const std::map& m = GetMetadata(level); - if (found == metadata_.end()) + std::map::const_iterator found = m.find(metadata); + + if (found == m.end()) { return false; } @@ -217,16 +245,6 @@ } - void FindResponse::Resource::ListMetadata(std::set& target) const - { - target.clear(); - - for (std::map::const_iterator it = metadata_.begin(); it != metadata_.end(); ++it) - { - target.insert(it->first); - } - } - const std::string& FindResponse::Resource::GetParentIdentifier() const { if (level_ == ResourceType_Patient) @@ -246,12 +264,6 @@ FindResponse::Resource::~Resource() { - for (MainDicomTags::iterator it = mainDicomTags_.begin(); it != mainDicomTags_.end(); ++it) - { - assert(it->second != NULL); - delete it->second; - } - for (ChildrenMetadata::iterator it = childrenMetadata_.begin(); it != childrenMetadata_.end(); ++it) { assert(it->second != NULL); @@ -362,6 +374,36 @@ } + void FindResponse::Resource::AddAttachmentOfOneInstance(const FileInfo& info) + { + if (attachmentOfOneInstance_.find(info.GetContentType()) == attachmentOfOneInstance_.end()) + { + attachmentOfOneInstance_[info.GetContentType()] = info; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + bool FindResponse::Resource::LookupAttachmentOfOneInstance(FileInfo& target, + FileContentType type) const + { + std::map::const_iterator found = attachmentOfOneInstance_.find(type); + + if (found == attachmentOfOneInstance_.end()) + { + return false; + } + else + { + target = found->second; + return true; + } + } + + SeriesStatus FindResponse::Resource::GetSeriesStatus(uint32_t& expectedNumberOfInstances) const { if (level_ != ResourceType_Series) @@ -370,7 +412,7 @@ } std::string s; - if (!LookupMetadata(s, MetadataType_Series_ExpectedNumberOfInstances) || + if (!LookupMetadata(s, ResourceType_Series, MetadataType_Series_ExpectedNumberOfInstances) || !SerializationToolbox::ParseUnsignedInteger32(expectedNumberOfInstances, s)) { return SeriesStatus_Unknown; @@ -422,7 +464,8 @@ void FindResponse::Resource::Expand(Json::Value& target, - const FindRequest& request) const + const FindRequest& request, + bool includeAllMetadata) const { /** @@ -444,35 +487,15 @@ **/ + /** + * This method closely follows "SerializeExpandedResource()" in + * "ServerContext.cpp" from Orthanc 1.12.3. + **/ + target = Json::objectValue; target["ID"] = identifier_; target["Type"] = GetResourceTypeText(level_, false, true); - if (request.IsRetrieveMetadata()) - { - Json::Value metadata = Json::objectValue; - - for (std::map::const_iterator - it = metadata_.begin(); it != metadata_.end(); ++it) - { - metadata[EnumerationToString(it->first)] = it->second; - } - - target["Metadata"] = metadata; - } - - if (request.IsRetrieveLabels()) - { - Json::Value labels = Json::arrayValue; - - for (std::set::const_iterator it = labels_.begin(); it != labels_.end(); ++it) - { - labels.append(*it); - } - - target["Labels"] = labels; - } - if (request.IsRetrieveParentIdentifier()) { switch (level_) @@ -499,10 +522,9 @@ if (request.IsRetrieveChildrenIdentifiers()) { - Json::Value c = Json::arrayValue; + const std::set& children = GetChildrenIdentifiers(); - const std::set& children = GetChildrenAtLevel(GetChildResourceType(level_)).GetIdentifiers(); - + Json::Value c = Json::arrayValue; for (std::set::const_iterator it = children.begin(); it != children.end(); ++it) { @@ -527,6 +549,269 @@ throw OrthancException(ErrorCode_InternalError); } } + + if (request.IsRetrieveMetadata(level_)) + { + switch (level_) + { + case ResourceType_Patient: + case ResourceType_Study: + break; + + case ResourceType_Series: + { + uint32_t expectedNumberOfInstances; + SeriesStatus status = GetSeriesStatus(expectedNumberOfInstances); + + target["Status"] = EnumerationToString(status); + + if (status == SeriesStatus_Unknown) + { + target["ExpectedNumberOfInstances"] = Json::nullValue; + } + else + { + target["ExpectedNumberOfInstances"] = Json::nullValue; + } + + break; + } + + case ResourceType_Instance: + { + // TODO-FIND + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + if (request.IsRetrieveLabels()) + { + Json::Value labels = Json::arrayValue; + + for (std::set::const_iterator it = labels_.begin(); it != labels_.end(); ++it) + { + labels.append(*it); + } + + target["Labels"] = labels; + } + + if (request.IsRetrieveMetadata(level_) && + includeAllMetadata) + { + const std::map& m = GetMetadata(level_); + + Json::Value metadata = Json::objectValue; + + for (std::map::const_iterator it = m.begin(); it != m.end(); ++it) + { + metadata[EnumerationToString(it->first)] = it->second; + } + + target["Metadata"] = metadata; + } + } + + + static void DebugDicomMap(Json::Value& target, + const DicomMap& m) + { + DicomArray a(m); + for (size_t i = 0; i < a.GetSize(); i++) + { + if (a.GetElement(i).GetValue().IsNull()) + { + target[a.GetElement(i).GetTag().Format()] = Json::nullValue; + } + else if (a.GetElement(i).GetValue().IsString()) + { + target[a.GetElement(i).GetTag().Format()] = a.GetElement(i).GetValue().GetContent(); + } + else + { + throw OrthancException(ErrorCode_InternalError); + } + } + } + + + static void DebugMetadata(Json::Value& target, + const std::map& m) + { + target = Json::objectValue; + + for (std::map::const_iterator it = m.begin(); it != m.end(); ++it) + { + target[EnumerationToString(it->first)] = it->second; + } + } + + + static void DebugAddAttachment(Json::Value& target, + const FileInfo& info) + { + Json::Value u = Json::arrayValue; + u.append(info.GetUuid()); + u.append(info.GetUncompressedSize()); + target[EnumerationToString(info.GetContentType())] = u; + } + + void FindResponse::Resource::DebugExport(Json::Value& target, + const FindRequest& request) const + { + target = Json::objectValue; + + target["Level"] = EnumerationToString(GetLevel()); + target["ID"] = GetIdentifier(); + + if (request.IsRetrieveParentIdentifier()) + { + target["ParentID"] = GetParentIdentifier(); + } + + if (request.IsRetrieveMainDicomTags(ResourceType_Patient)) + { + DicomMap m; + GetMainDicomTags(m, ResourceType_Patient); + DebugDicomMap(target["Patient"]["MainDicomTags"], m); + } + + if (request.IsRetrieveMetadata(ResourceType_Patient)) + { + DebugMetadata(target["Patient"]["Metadata"], GetMetadata(ResourceType_Patient)); + } + + if (request.GetLevel() != ResourceType_Patient) + { + if (request.IsRetrieveMainDicomTags(ResourceType_Study)) + { + DicomMap m; + GetMainDicomTags(m, ResourceType_Study); + DebugDicomMap(target["Study"]["MainDicomTags"], m); + } + + if (request.IsRetrieveMetadata(ResourceType_Study)) + { + DebugMetadata(target["Study"]["Metadata"], GetMetadata(ResourceType_Study)); + } + } + + if (request.GetLevel() != ResourceType_Patient && + request.GetLevel() != ResourceType_Study) + { + if (request.IsRetrieveMainDicomTags(ResourceType_Series)) + { + DicomMap m; + GetMainDicomTags(m, ResourceType_Series); + DebugDicomMap(target["Series"]["MainDicomTags"], m); + } + + if (request.IsRetrieveMetadata(ResourceType_Series)) + { + DebugMetadata(target["Series"]["Metadata"], GetMetadata(ResourceType_Series)); + } + } + + if (request.GetLevel() != ResourceType_Patient && + request.GetLevel() != ResourceType_Study && + request.GetLevel() != ResourceType_Series) + { + if (request.IsRetrieveMainDicomTags(ResourceType_Instance)) + { + DicomMap m; + GetMainDicomTags(m, ResourceType_Instance); + DebugDicomMap(target["Instance"]["MainDicomTags"], m); + } + + if (request.IsRetrieveMetadata(ResourceType_Instance)) + { + DebugMetadata(target["Instance"]["Metadata"], GetMetadata(ResourceType_Instance)); + } + } + + if (request.IsRetrieveChildrenIdentifiers()) + { + Json::Value v = Json::arrayValue; + for (std::set::const_iterator it = childrenIdentifiers_.begin(); + it != childrenIdentifiers_.end(); ++it) + { + v.append(*it); + } + target["Children"] = v; + } + + if (request.IsRetrieveLabels()) + { + Json::Value v = Json::arrayValue; + for (std::set::const_iterator it = labels_.begin(); + it != labels_.end(); ++it) + { + v.append(*it); + } + target["Labels"] = v; + } + + if (request.IsRetrieveAttachments()) + { + Json::Value v = Json::objectValue; + for (std::map::const_iterator it = attachments_.begin(); + it != attachments_.end(); ++it) + { + if (it->first != it->second.GetContentType()) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + else + { + DebugAddAttachment(v, it->second); + } + } + target["Attachments"] = v; + } + + for (std::set::const_iterator it = request.GetRetrieveChildrenMetadata().begin(); + it != request.GetRetrieveChildrenMetadata().end(); ++it) + { + std::list l; + if (LookupChildrenMetadata(l, *it)) + { + Json::Value v = Json::arrayValue; + for (std::list::const_iterator it2 = l.begin(); it2 != l.end(); ++it2) + { + v.append(*it2); + } + target["ChildrenMetadata"][EnumerationToString(*it)] = v; + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + + for (std::set::const_iterator it = request.GetRetrieveAttachmentOfOneInstance().begin(); + it != request.GetRetrieveAttachmentOfOneInstance().end(); ++it) + { + FileInfo info; + if (LookupAttachmentOfOneInstance(info, *it)) + { + if (info.GetContentType() == *it) + { + DebugAddAttachment(target["AttachmentOfOneInstance"], info); + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } } diff -r a87f2a56257d -r 81a29ad7fb4b OrthancServer/Sources/Database/FindResponse.h --- a/OrthancServer/Sources/Database/FindResponse.h Tue May 07 12:53:12 2024 +0200 +++ b/OrthancServer/Sources/Database/FindResponse.h Tue May 07 18:44:53 2024 +0200 @@ -41,18 +41,28 @@ class FindResponse : public boost::noncopyable { private: - class ChildrenAtLevel : public boost::noncopyable + class MainDicomTagsAtLevel : public boost::noncopyable { private: - std::set identifiers_; + class DicomValue; + + typedef std::map MainDicomTags; + + MainDicomTags mainDicomTags_; public: - void AddIdentifier(const std::string& identifier); + ~MainDicomTagsAtLevel(); + + void AddStringDicomTag(uint16_t group, + uint16_t element, + const std::string& value); - const std::set& GetIdentifiers() const - { - return identifiers_; - } + // The "Null" value could be used in the future to indicate a + // value that is not available, typically a new "ExtraMainDicomTag" + void AddNullDicomTag(uint16_t group, + uint16_t element); + + void Export(DicomMap& target) const; }; @@ -60,28 +70,30 @@ class Resource : public boost::noncopyable { private: - class DicomValue; - - typedef std::map MainDicomTags; typedef std::map*> ChildrenMetadata; ResourceType level_; std::string identifier_; std::unique_ptr parentIdentifier_; - MainDicomTags mainDicomTags_; - ChildrenAtLevel childrenStudies_; - ChildrenAtLevel childrenSeries_; - ChildrenAtLevel childrenInstances_; + MainDicomTagsAtLevel mainDicomTagsPatient_; + MainDicomTagsAtLevel mainDicomTagsStudy_; + MainDicomTagsAtLevel mainDicomTagsSeries_; + MainDicomTagsAtLevel mainDicomTagsInstance_; + std::map metadataPatient_; + std::map metadataStudy_; + std::map metadataSeries_; + std::map metadataInstance_; + std::set childrenIdentifiers_; std::set labels_; - std::map metadata_; std::map attachments_; ChildrenMetadata childrenMetadata_; + std::map attachmentOfOneInstance_; - ChildrenAtLevel& GetChildrenAtLevel(ResourceType level); + MainDicomTagsAtLevel& GetMainDicomTagsAtLevel(ResourceType level); - const ChildrenAtLevel& GetChildrenAtLevel(ResourceType level) const + const MainDicomTagsAtLevel& GetMainDicomTagsAtLevel(ResourceType level) const { - return const_cast(*this).GetChildrenAtLevel(level); + return const_cast(*this).GetMainDicomTagsAtLevel(level); } public: @@ -110,26 +122,47 @@ bool HasParentIdentifier() const; - void AddStringDicomTag(uint16_t group, + void AddStringDicomTag(ResourceType level, + uint16_t group, uint16_t element, - const std::string& value); + const std::string& value) + { + GetMainDicomTagsAtLevel(level).AddStringDicomTag(group, element, value); + } - // The "Null" value could be used in the future to indicate a - // value that is not available, typically a new "ExtraMainDicomTag" - void AddNullDicomTag(uint16_t group, - uint16_t element); + void AddNullDicomTag(ResourceType level, + uint16_t group, + uint16_t element) + { + GetMainDicomTagsAtLevel(level).AddNullDicomTag(group, element); + } - void GetMainDicomTags(DicomMap& target) const; - - void AddChildIdentifier(ResourceType level, - const std::string& childId) + void GetMainDicomTags(DicomMap& target, + ResourceType level) const { - GetChildrenAtLevel(level).AddIdentifier(childId); + GetMainDicomTagsAtLevel(level).Export(target); } - const std::set& GetChildrenIdentifiers(ResourceType level) const + void AddMetadata(ResourceType level, + MetadataType metadata, + const std::string& value); + + std::map& GetMetadata(ResourceType level); + + const std::map& GetMetadata(ResourceType level) const { - return const_cast(*this).GetChildrenAtLevel(level).GetIdentifiers(); + return const_cast(*this).GetMetadata(level); + } + + bool LookupMetadata(std::string& value, + ResourceType level, + MetadataType metadata) const; + + void AddChildIdentifier(const std::string& childId); + + const std::set& GetChildrenIdentifiers() const + { + return childrenIdentifiers_; } void AddLabel(const std::string& label); @@ -144,44 +177,35 @@ return labels_; } - void AddMetadata(MetadataType metadata, - const std::string& value); - - std::map& GetMetadata() - { - return metadata_; - } - - const std::map& GetMetadata() const - { - return metadata_; - } - - bool HasMetadata(MetadataType metadata) const - { - return metadata_.find(metadata) != metadata_.end(); - } - - bool LookupMetadata(std::string& value, - MetadataType metadata) const; - - void ListMetadata(std::set& metadata) const; - void AddAttachment(const FileInfo& attachment); bool LookupAttachment(FileInfo& target, FileContentType type) const; + const std::map& GetAttachments() const + { + return attachments_; + } + void AddChildrenMetadata(MetadataType metadata, const std::list& values); bool LookupChildrenMetadata(std::list& values, MetadataType metadata) const; + void AddAttachmentOfOneInstance(const FileInfo& info); + + bool LookupAttachmentOfOneInstance(FileInfo& target, + FileContentType type) const; + SeriesStatus GetSeriesStatus(uint32_t& expecterNumberOfInstances) const; void Expand(Json::Value& target, - const FindRequest& request) const; + const FindRequest& request, + bool includeAllMetadata) const; + + void DebugExport(Json::Value& target, + const FindRequest& request) const; }; private: diff -r a87f2a56257d -r 81a29ad7fb4b OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Tue May 07 12:53:12 2024 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Tue May 07 18:44:53 2024 +0200 @@ -3928,15 +3928,15 @@ throw OrthancException(ErrorCode_InternalError); } - if (request.IsRetrieveMainDicomTags()) + if (request.IsRetrieveMainDicomTags(request.GetLevel())) { - resource.GetMainDicomTags(tags_); + resource.GetMainDicomTags(tags_, request.GetLevel()); } if (request.IsRetrieveChildrenIdentifiers()) { - const std::set& s = resource.GetChildrenIdentifiers(GetChildResourceType(request.GetLevel())); - for (std::set::const_iterator it = s.begin(); it != s.end(); ++it) + const std::set& children = resource.GetChildrenIdentifiers(); + for (std::set::const_iterator it = children.begin(); it != children.end(); ++it) { childrenIds_.push_back(*it); } @@ -3947,36 +3947,36 @@ parentId_ = resource.GetParentIdentifier(); } - if (request.IsRetrieveMetadata()) + if (request.IsRetrieveMetadata(request.GetLevel())) { - metadata_ = resource.GetMetadata(); + metadata_ = resource.GetMetadata(request.GetLevel()); std::string value; - if (resource.LookupMetadata(value, MetadataType_MainDicomTagsSignature)) + if (resource.LookupMetadata(value, request.GetLevel(), MetadataType_MainDicomTagsSignature)) { mainDicomTagsSignature_ = value; } - if (resource.LookupMetadata(value, MetadataType_AnonymizedFrom)) + if (resource.LookupMetadata(value, request.GetLevel(), MetadataType_AnonymizedFrom)) { anonymizedFrom_ = value; } - if (resource.LookupMetadata(value, MetadataType_ModifiedFrom)) + if (resource.LookupMetadata(value, request.GetLevel(), MetadataType_ModifiedFrom)) { modifiedFrom_ = value; } - if (resource.LookupMetadata(value, MetadataType_LastUpdate)) + if (resource.LookupMetadata(value, request.GetLevel(), MetadataType_LastUpdate)) { lastUpdate_ = value; } if (request.GetLevel() == ResourceType_Series) { - if (resource.LookupMetadata(value, MetadataType_Series_ExpectedNumberOfInstances)) + if (resource.LookupMetadata(value, request.GetLevel(), MetadataType_Series_ExpectedNumberOfInstances)) { expectedNumberOfInstances_ = boost::lexical_cast(value); } } if (request.GetLevel() == ResourceType_Instance) { - if (resource.LookupMetadata(value, MetadataType_Instance_IndexInSeries)) + if (resource.LookupMetadata(value, request.GetLevel(), MetadataType_Instance_IndexInSeries)) { indexInSeries_ = boost::lexical_cast(value); } diff -r a87f2a56257d -r 81a29ad7fb4b OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Tue May 07 12:53:12 2024 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Tue May 07 18:44:53 2024 +0200 @@ -245,8 +245,8 @@ if (expand) { // compatibility with default expand option - request.SetRetrieveMainDicomTags(true); - request.SetRetrieveMetadata(true); + request.SetRetrieveMainDicomTags(resourceType, true); + request.SetRetrieveMetadata(resourceType, true); request.SetRetrieveLabels(true); if (resourceType == ResourceType_Series) diff -r a87f2a56257d -r 81a29ad7fb4b OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Tue May 07 12:53:12 2024 +0200 +++ b/OrthancServer/Sources/ServerContext.cpp Tue May 07 18:44:53 2024 +0200 @@ -2732,11 +2732,11 @@ { expandFlags = static_cast(expandFlags | ExpandResourceFlags_IncludeChildren); } - if (request.IsRetrieveMetadata()) + if (request.IsRetrieveMetadata(request.GetLevel())) { expandFlags = static_cast(expandFlags | ExpandResourceFlags_IncludeAllMetadata | ExpandResourceFlags_IncludeMetadata ); } - if (request.IsRetrieveMainDicomTags()) + if (request.IsRetrieveMainDicomTags(request.GetLevel())) { expandFlags = static_cast(expandFlags | ExpandResourceFlags_IncludeMainDicomTags); }