# HG changeset patch # User Sebastien Jodogne # Date 1624031936 -7200 # Node ID facea16b055bc4e9586ada5a524fd8f83f728ea0 # Parent d16c3c7f11efe4babcd98a4e749ce95e6f5f27c4 added the "Level" argument to "/tools/bulk-content" diff -r d16c3c7f11ef -r facea16b055b OrthancServer/Resources/Configuration.json --- a/OrthancServer/Resources/Configuration.json Fri Jun 18 16:37:12 2021 +0200 +++ b/OrthancServer/Resources/Configuration.json Fri Jun 18 17:58:56 2021 +0200 @@ -628,7 +628,7 @@ // Load a set of external DICOM dictionaries in order to replace the // default dictionaries. This option must contain a set of files in // the DCMTK format. The order of the dictionaries *is* - // important. This option can be used to turn Orhanc into a DICONDE + // important. This option can be used to turn Orthanc into a DICONDE // server. (new in Orthanc 1.9.4) /** "ExternalDictionaries" : [ diff -r d16c3c7f11ef -r facea16b055b OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp Fri Jun 18 16:37:12 2021 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp Fri Jun 18 17:58:56 2021 +0200 @@ -427,7 +427,7 @@ .SetTag("System") .SetSummary("Modify a set of resources") .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings, - "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false) + "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", true) .SetDescription("Start a job that will modify all the DICOM patients, studies, series or instances " "whose identifiers are provided in the `Resources` field.") .AddAnswerType(MimeType_Json, "The list of all the resources that have been altered by this modification"); @@ -485,7 +485,7 @@ .SetTag("System") .SetSummary("Anonymize a set of resources") .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings, - "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false) + "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", true) .SetDescription("Start a job that will anonymize all the DICOM patients, studies, series or instances " "whose identifiers are provided in the `Resources` field.") .AddAnswerType(MimeType_Json, "The list of all the resources that have been created by this anonymization"); diff -r d16c3c7f11ef -r facea16b055b OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Fri Jun 18 16:37:12 2021 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Fri Jun 18 17:58:56 2021 +0200 @@ -3092,6 +3092,27 @@ } + static void GetBulkChildren(std::set& target, + ServerIndex& index, + const std::set& source) + { + target.clear(); + + for (std::set::const_iterator + it = source.begin(); it != source.end(); ++it) + { + std::list children; + index.GetChildren(children, *it); + + for (std::list::const_iterator + child = children.begin(); child != children.end(); ++child) + { + target.insert(*child); + } + } + } + + static void BulkContent(RestApiPostCall& call) { if (call.IsDocumentation()) @@ -3102,7 +3123,11 @@ .SetTag("System") .SetSummary("Describe a set of instances") .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings, - "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false) + "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", true) + .SetRequestField("Level", RestApiCallDocumentation::Type_String, + "This optional argument specifies the level of interest (can be `Patient`, `Study`, `Series` or " + "`Instance`). Orthanc will loop over the items inside `Resources`, and explorer upward or " + "downward in the DICOM hierarchy in order to find the level of interest.", false) .SetDescription("Get the content all the DICOM patients, studies, series or instances " "whose identifiers are provided in the `Resources` field, in one single call."); return; @@ -3117,27 +3142,131 @@ } else { + static const char* const LEVEL = "Level"; + const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(request, DicomToJsonFormat_Human); ServerIndex& index = OrthancRestApi::GetIndex(call); - std::list resources; - SerializationToolbox::ReadListOfStrings(resources, request, "Resources"); - Json::Value answer = Json::arrayValue; - for (std::list::const_iterator - it = resources.begin(); it != resources.end(); ++it) + + if (request.isMember(LEVEL)) { - ResourceType type; - Json::Value item; - if (index.LookupResourceType(type, *it) && - index.ExpandResource(item, *it, type, format)) + // Complex case: Need to explore the DICOM hierarchy + ResourceType level = StringToResourceType(SerializationToolbox::ReadString(request, LEVEL).c_str()); + + std::set resources; + SerializationToolbox::ReadSetOfStrings(resources, request, "Resources"); + + std::set interest; + + assert(ResourceType_Patient < ResourceType_Study && + ResourceType_Study < ResourceType_Series && + ResourceType_Series < ResourceType_Instance); + + for (std::set::const_iterator + it = resources.begin(); it != resources.end(); ++it) { - answer.append(item); + ResourceType type; + if (index.LookupResourceType(type, *it)) + { + if (type == level) + { + // This resource is already from the level of interest + interest.insert(*it); + } + else if (type < level) + { + // Need to explore children + std::set current; + current.insert(*it); + + for (;;) + { + std::set children; + GetBulkChildren(children, index, current); + + type = GetChildResourceType(type); + if (type == level) + { + for (std::set::const_iterator + it = children.begin(); it != children.end(); ++it) + { + interest.insert(*it); + } + + break; // done + } + else + { + current.swap(children); + } + } + } + else + { + // Need to explore parents + std::string current = *it; + + for (;;) + { + std::string parent; + if (index.LookupParent(parent, current)) + { + type = GetParentResourceType(type); + if (type == level) + { + interest.insert(parent); + break; // done + } + else + { + current = parent; + } + } + else + { + break; // The resource has been deleted during the exploration + } + } + } + } + else + { + CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it; + } } - else + + for (std::set::const_iterator + it = interest.begin(); it != interest.end(); ++it) { - CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it; + Json::Value item; + if (index.ExpandResource(item, *it, level, format)) + { + answer.append(item); + } + } + } + else + { + // Simple case: We return the queried resources as such + std::list resources; + SerializationToolbox::ReadListOfStrings(resources, request, "Resources"); + + for (std::list::const_iterator + it = resources.begin(); it != resources.end(); ++it) + { + ResourceType type; + Json::Value item; + if (index.LookupResourceType(type, *it) && + index.ExpandResource(item, *it, type, format)) + { + answer.append(item); + } + else + { + CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it; + } } } @@ -3154,7 +3283,7 @@ .SetTag("System") .SetSummary("Delete a set of instances") .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings, - "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", false) + "List of the Orthanc identifiers of the patients/studies/series/instances of interest.", true) .SetDescription("Delete all the DICOM patients, studies, series or instances " "whose identifiers are provided in the `Resources` field."); return;